diff --git a/DEPS b/DEPS
index 984e9c71..4c61d50 100644
--- a/DEPS
+++ b/DEPS
@@ -305,19 +305,19 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': '9044ebbb524768005838a1dc2a8b0e7114e575eb',
+  'src_internal_revision': '86a4a8afd51091e4227435859e44c9be53d99e77',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'b54e1064ccae404b7a7bff410585fef1c5381241',
+  'skia_revision': '2b871d6b99caae5e3990351b91606e9a957f196f',
   # 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': '4cfbba3684e7d04a5d46c9f9a2117f84e02ef5ca',
+  'v8_revision': 'ca4e6e3b342f30687ca4d36fda90be00be782331',
   # 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': '1f40285c90cd45ae6238c57f30d0ec8c8d6e2e4b',
+  'angle_revision': '5e51d8e71c08a84ad81080036c6b25cc36b53b1b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -397,7 +397,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '2d6e3ee0f3197f192fe4d759e8eb6934dea46069',
+  'devtools_frontend_revision': '0ef5b0631522c714639536c36dabf98a1802bf1f',
   # 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.
@@ -1190,7 +1190,7 @@
       'packages': [
           {
               'package': 'chromium/chrome/android/orderfiles/arm',
-              'version': 'T2mzadHQPsuVG1HhjRt3-c58amiQisG8Fp-qPRoY_FgC',
+              'version': 'uz_stHaQNtsXhHznyeslC8HUgqFiaJ3qxmcGsJTArqQC',
           },
       ],
       'condition': 'checkout_android',
@@ -1201,7 +1201,7 @@
       'packages': [
           {
               'package': 'chromium/chrome/android/orderfiles/arm64',
-              'version': '0afF61bx4wGehx0eczze3V2EfbcEDoo1G_6b1afMm5cC',
+              'version': 'oA9esWVQ9jlXhVe0_wczPWxQMQFzZH52279s63QufugC',
           },
       ],
       'condition': 'checkout_android',
@@ -1212,7 +1212,7 @@
       'packages': [
           {
               'package': 'chromium/android_webview/tools/orderfiles/arm64',
-              'version': 'd7DpavunvU4AgJmf14dK0lOQJPu7lzAykqvsHXqXfvMC',
+              'version': 'm7Cc-GTz68mEl6sQSSDI2yEKOzzoJZoNkzrR_YYn1dkC',
           },
       ],
       'condition': 'checkout_android',
@@ -1384,7 +1384,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/updater/chromium_linux64',
-          'version': 'version:2@1515005',
+          'version': 'version:2@1516003',
         },
       ],
   },
@@ -1439,7 +1439,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/updater/chromium_win_x86_64',
-          'version': 'version:2@1515009',
+          'version': 'version:2@1516030',
         },
       ],
   },
@@ -1517,7 +1517,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/enterprise_companion/chromium_linux64',
-          'version': 'pbnDd5wNxdibEG5vIXB4bluuqXvFWB_UyHpiVSW4HjQC',
+          'version': 'tmcsHXibYG2BNsfWFLEnyI60NwRtLCo43oTy_6_ns00C',
         },
       ],
   },
@@ -1528,7 +1528,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/enterprise_companion/chromium_mac_amd64',
-          'version': 'j1I0YjRznWAyAoJqyoAHAOSQr9gM-jJ-Waa1oArGyKYC',
+          'version': 'J134yskB9yHRr9omSDGqeGm6CkfCwm8bbSf0R1CwricC',
         },
       ],
   },
@@ -1561,7 +1561,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/enterprise_companion/chromium_win_x86_64',
-          'version': 'KwN21d5HdHtmiYmwVBT-QyKBMjiOdATbd3OGUNa1AvkC',
+          'version': 'jY8nhCbLbm_u1NrABE_YXAcDzu1tHRns_wev2f9rlsYC',
         },
       ],
   },
@@ -1708,7 +1708,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'XJzoXfyWG0qIm_nytRL9cXq3o5XVhOVLLhKUqyhiAMgC',
+          'version': 'NXMMjktEtvspOzqqwtr0vBTB2fC-K4RjyzljCw8bD_gC',
       },
     ],
     'condition': 'checkout_android and non_git_source',
@@ -2589,7 +2589,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '95317c91aadc41a24abc2a671b211baec3d6d749',
+    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + 'c65beb1b174079f116a0d31729c43e6cbfacfc85',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2908,7 +2908,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@2458bba6f0d087f6ad588af68bcb4075c1d0701b',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@5bc0a41ae522a65e91c77b043bce45637a1a2fde',
   'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@a57276bf558f5cf94d3a9854ebdf5a2236849a5a',
   'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3',
   'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@01e0577914a75a2569c846778c2f93aa8e6feddd',
@@ -2917,7 +2917,7 @@
   'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@6ec5a0d91c253c6374701e142a95a0d3215bf5a7',
   'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@e3ecc39da57a5ed15ec8de67f136273ee54b78be',
   'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@23081f01cdee214226bfed5aedd37f092fa55dd9',
-  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@102c30690b1e84e58ad659a249dbb819dcc7f926',
+  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@c352f86877474dbc7294125d31523e8c74918c76',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'cb0597213b0fcb999caa9ed08c2f88dc45eb7d50',
@@ -2960,7 +2960,7 @@
     Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '96075773302f4a4b7265052c37a793486355aef9',
+    Var('webrtc_git') + '/src.git' + '@' + 'ba391e6893da350a48a7895a0bff8f033369a729',
 
   # 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.
@@ -3093,7 +3093,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/boca_app/app',
-        'version': 'ZV9oYBlrFysUJa8weZCtyL-JNzGnYtCjdOaWvYXZ3ZwC',
+        'version': 'Mz5dXx73Yd3-srzT2ipOxc7qC9_ynsB0a5OcvaIQdQUC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3148,7 +3148,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'NjPtn9txqM5h1NzimFeMsKVu2Hqp5KOW5RO7ZELtfPsC',
+        'version': '1_DjXQ2Rs1l_YNHCq7MI4n5qXa1YgJbjpp_hpQcQhcMC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3741,7 +3741,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        'c3935b3d5c33aad75608bd4ce39545d47b8d5b24',
+        '695ab1904c1b36f5a5e9864c0304ac499a0007ff',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index d7ee961..ecbfcd1 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -1154,7 +1154,10 @@
         Flag.baseFeature(
                 BaseFeatures.USE_IS_UNBOUND_CHECK,
                 "Use ChildServiceConnectionController.isUnbound() instead of isConnected() to check"
-                    + " the connection state in ChildProcessConnection."),
+                        + " the connection state in ChildProcessConnection."),
+        Flag.baseFeature(
+                "ProbeStylusWritingInBackground",
+                "Offload probing of stylus writing support to a background thread."),
         // Add new commandline switches and features above. The final entry should have a
         // trailing comma for cleaner diffs.
     };
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
index bf15aa8d..244e71da 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
@@ -122,7 +122,7 @@
                 Features.HYPERLINK_CONTEXT_MENU_ITEMS + Features.DEV_SUFFIX,
                 Features.ASYNC_WEBVIEW_STARTUP_ASYNC_STARTUP_LOCATIONS + Features.DEV_SUFFIX,
                 Features.PAGE_IS_PRERENDERING,
-                Features.CUSTOM_REQUEST_HEADERS + Features.DEV_SUFFIX,
+                Features.CUSTOM_REQUEST_HEADERS,
                 // Add new features above. New features must include `+ Features.DEV_SUFFIX`
                 // when they're initially added (this can be removed in a future CL). The final
                 // feature should have a trailing comma for cleaner diffs.
diff --git a/ash/app_list/app_list_presenter_unittest.cc b/ash/app_list/app_list_presenter_unittest.cc
index b282225..910fadd3 100644
--- a/ash/app_list/app_list_presenter_unittest.cc
+++ b/ash/app_list/app_list_presenter_unittest.cc
@@ -3126,7 +3126,7 @@
   aura::Window* applist_container = Shell::GetContainer(
       Shell::GetPrimaryRootWindow(), kShellWindowId_AppListContainer);
   std::unique_ptr<aura::Window> window = aura::test::CreateTestWindow(
-      {.parent = applist_container, .window_id = 0});
+      {.parent = applist_container, .bounds = {100, 100}, .window_id = 0});
   wm::ActivateWindow(window.get());
   GetAppListTestHelper()->WaitUntilIdle();
 
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index 260a72b..d2ae3af 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -1744,6 +1744,10 @@
 // Whether the user is allowed to disconnect and configure VPN connections.
 inline constexpr char kVpnConfigAllowed[] = "vpn_config_allowed";
 
+// A boolean pref that indicates whether silent printing is enabled.
+inline constexpr char kSilentPrintingEnabled[] =
+    "ash.printing.silent_printing_enabled";
+
 // A boolean pref that indicates whether power peak shift is enabled.
 // Ignored unless powerd is configured to honor charging-related prefs.
 inline constexpr char kPowerPeakShiftEnabled[] = "ash.power.peak_shift_enabled";
diff --git a/base/allocator/partition_alloc_features.cc b/base/allocator/partition_alloc_features.cc
index 67240d6..acb0af3 100644
--- a/base/allocator/partition_alloc_features.cc
+++ b/base/allocator/partition_alloc_features.cc
@@ -140,14 +140,6 @@
 BASE_FEATURE(kPartitionAllocEventuallyZeroFreedMemory,
              FEATURE_DISABLED_BY_DEFAULT);
 
-// Evaluated and positive stability and peformance-wise on Linux-based systems,
-// disabled elsewhere (for now). Does not apply to Windows.
-BASE_FEATURE(kPartitionAllocFewerMemoryRegions,
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)
-             FEATURE_ENABLED_BY_DEFAULT);
-#else
-             FEATURE_DISABLED_BY_DEFAULT);
-#endif
 #endif  // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 
 BASE_FEATURE(kPartitionAllocBackupRefPtr,
@@ -216,7 +208,7 @@
     {MemtagMode::kSync, "sync"},
     {MemtagMode::kAsync, "async"}};
 
-// Note: Do not use the prepared macro as of no need for a local cache.
+// Note: Do not use the prepared muacro as of no need for a local cache.
 constinit const FeatureParam<MemtagMode> kMemtagModeParam{
     &kPartitionAllocMemoryTagging, "memtag-mode",
 #if PA_BUILDFLAG(USE_FULL_MTE)
diff --git a/base/allocator/partition_alloc_features.h b/base/allocator/partition_alloc_features.h
index 8b4827d..fab50d9 100644
--- a/base/allocator/partition_alloc_features.h
+++ b/base/allocator/partition_alloc_features.h
@@ -106,10 +106,6 @@
 // fragmented super pages.
 BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocEventuallyZeroFreedMemory);
 
-// Whether to make PartitionAlloc use fewer memory regions. This matters on
-// Linux-based systems, where there is a per-process limit that we hit in some
-// cases. See the comment in PartitionBucket::SlotSpanCOmmitedSize() for detail.
-BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocFewerMemoryRegions);
 #endif  // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 
 using BackupRefPtrEnabledProcesses = internal::PAFeatureEnabledProcesses;
diff --git a/base/allocator/partition_alloc_support.cc b/base/allocator/partition_alloc_support.cc
index b82382e7..7c89a9be 100644
--- a/base/allocator/partition_alloc_support.cc
+++ b/base/allocator/partition_alloc_support.cc
@@ -1097,8 +1097,6 @@
 
   const bool eventually_zero_freed_memory = base::FeatureList::IsEnabled(
       base::features::kPartitionAllocEventuallyZeroFreedMemory);
-  const bool fewer_memory_regions = base::FeatureList::IsEnabled(
-      base::features::kPartitionAllocFewerMemoryRegions);
 
   bool enable_memory_tagging = false;
   partition_alloc::TagViolationReportingMode memory_tagging_reporting_mode =
@@ -1196,8 +1194,7 @@
       scheduler_loop_quarantine_global_config,
       scheduler_loop_quarantine_thread_local_config,
       scheduler_loop_quarantine_for_advanced_memory_safety_checks_config,
-      allocator_shim::EventuallyZeroFreedMemory(eventually_zero_freed_memory),
-      allocator_shim::FewerMemoryRegions(fewer_memory_regions));
+      allocator_shim::EventuallyZeroFreedMemory(eventually_zero_freed_memory));
 
   const uint32_t extras_size = allocator_shim::GetMainPartitionRootExtrasSize();
   // As per description, extras are optional and are expected not to
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_config.h b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_config.h
index 0756517..823ab415 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_config.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_config.h
@@ -165,6 +165,18 @@
 constexpr bool kUseLazyCommit = false;
 #endif
 
+// See the comment in PartitionBucket::SlotSpanCommittedSize(). This should not
+// be enabled on Windows (because it increases committed memory, which is a
+// limited system-wide resource on this platform). It has been evaluated on
+// macOS, where it yielded no beenefit (nor any real downside).
+constexpr bool kUseFewerMemoryRegions =
+#if PA_BUILDFLAG(IS_LINUX) || PA_BUILDFLAG(IS_ANDROID) || \
+    PA_BUILDFLAG(IS_CHROMEOS)
+    true;
+#else
+    false;
+#endif
+
 // On these platforms, lock all the partitions before fork(), and unlock after.
 // This may be required on more platforms in the future.
 #define PA_CONFIG_HAS_ATFORK_HANDLER()                 \
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_unittest.cc b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_unittest.cc
index 4306a28d..f6a2f70 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_unittest.cc
@@ -390,7 +390,6 @@
   PartitionOptions GetCommonPartitionOptions() {
     PartitionOptions opts;
     opts.eventually_zero_freed_memory = PartitionOptions::kEnabled;
-    opts.fewer_memory_regions = PartitionOptions::kDisabled;
     opts.scheduler_loop_quarantine_global_config = {
         .branch_capacity_in_bytes = std::numeric_limits<size_t>::max(),
         .leak_on_destruction = true,
@@ -3933,9 +3932,13 @@
     PA_BUILDFLAG(IS_CHROMEOS)
 
 TEST_P(PartitionAllocTest, InaccessibleRegionAfterSlotSpans) {
-  auto* root = allocator.root();
-  ASSERT_FALSE(root->settings.fewer_memory_regions);
+  // There is inaccessible space only when this setting is not enabled,
+  // otherwise we extend the region to use fewer memory regions.
+  if (kUseFewerMemoryRegions) {
+    GTEST_SKIP();
+  }
 
+  auto* root = allocator.root();
   // Look for an allocation size that matches a bucket which doesn't fill its
   // last PartitionPage.  Scan through allocation sizes rather than buckets, as
   // depending on the bucket distribution, some buckets may not be active.
@@ -3987,9 +3990,8 @@
 }
 
 TEST_P(PartitionAllocTest, FewerMemoryRegions) {
+  static_assert(kUseFewerMemoryRegions);
   auto* root = allocator.root();
-  ASSERT_FALSE(root->settings.fewer_memory_regions);
-  root->settings.fewer_memory_regions = true;
 
   // Look for an allocation size that matches a bucket which doesn't fill its
   // last PartitionPage.  Scan through allocation sizes rather than buckets, as
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_bucket.cc b/base/allocator/partition_allocator/src/partition_alloc/partition_bucket.cc
index 31702ead..7905183 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_bucket.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_bucket.cc
@@ -1449,7 +1449,7 @@
 size_t PartitionBucket::SlotSpanCommittedSize(PartitionRoot* root) const {
   // With lazy commit, we certainly don't want to commit more than
   // necessary. This is not reached, but keep the CHECK() as documentation.
-  PA_CHECK(!kUseLazyCommit);
+  static_assert(!(kUseLazyCommit && kUseFewerMemoryRegions));
 
   // Memory is reserved in units of PartitionPage, but a given slot span may be
   // smaller than the reserved area. For instance (assuming 4k pages), for a
@@ -1473,7 +1473,9 @@
   // less than 2^16, and Chromium sometimes hits the limit (see
   // /proc/sys/vm/max_map_count for the current limit), largely because of
   // PartitionAlloc contributing thousands of regions. Locally, on a Linux
-  // system, this reduces the number of PartitionAlloc regions by up to ~4x.
+  // system, this reduces the number of PartitionAlloc regions by up to
+  // ~4x. This has been shown to meaningfully reduce crash rate on Linux-based
+  // platforms.
   //
   // Why is it safe?
   // The extra memory is not used by anything, so committing it doesn't make a
@@ -1488,19 +1490,13 @@
   // the size of the VMA red-black tree in the kernel), it might increase
   // slightly the cases where we bump into the sandbox memory limit.
   //
-  // Is it safe to do while running?
-  // Since this is decided through root settings, the value changes at runtime,
-  // so we may decommit memory that was never committed. This is safe onLinux,
-  // since decommitting is just changing permissions back to PROT_NONE, which
-  // the tail end would already have.
-  //
   // Can we do better?
   // For simplicity, we do not "fix" the regions that were committed before the
   // settings are changed (after feature list initialization). This means that
   // we end up with more regions that we could. The intent is to run a field
   // experiment, then change the default value, at which point we get the full
   // impact, so this is only temporary.
-  return root->settings.fewer_memory_regions
+  return kUseFewerMemoryRegions
              ? (get_pages_per_slot_span() << PartitionPageShift())
              : get_bytes_per_span();
 }
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_root.cc b/base/allocator/partition_allocator/src/partition_alloc/partition_root.cc
index 9834d60d..e82d9cb 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_root.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_root.cc
@@ -1042,8 +1042,6 @@
 #endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
     settings.eventually_zero_freed_memory =
         opts.eventually_zero_freed_memory == PartitionOptions::kEnabled;
-    settings.fewer_memory_regions =
-        opts.fewer_memory_regions == PartitionOptions::kEnabled;
 
     scheduler_loop_quarantine.Configure(
         scheduler_loop_quarantine_root,
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_root.h b/base/allocator/partition_allocator/src/partition_alloc/partition_root.h
index 8345065..b6459123 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_root.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_root.h
@@ -196,17 +196,6 @@
   // compression ratio of freed memory inside partially allocated pages (due to
   // fragmentation).
   EnableToggle eventually_zero_freed_memory = kDisabled;
-  // Linux-based systems have a limited per-process VMA limit, be more
-  // conservative there. This matches the feature setting in
-  // partition_alloc_features.cc, but not all clients use Chromium's feature
-  // system to configure PartitionAlloc.
-  EnableToggle fewer_memory_regions =
-#if PA_BUILDFLAG(IS_LINUX) || PA_BUILDFLAG(IS_ANDROID) || \
-    PA_BUILDFLAG(IS_CHROMEOS)
-      kEnabled;
-#else
-      kDisabled;
-#endif
 
   struct {
     EnableToggle enabled = kDisabled;
@@ -279,7 +268,6 @@
     bool eventually_zero_freed_memory = false;
     internal::SchedulerLoopQuarantineConfig
         scheduler_loop_quarantine_thread_local_config;
-    bool fewer_memory_regions = false;
 #if PA_BUILDFLAG(HAS_MEMORY_TAGGING)
     bool memory_tagging_enabled_ = false;
     bool use_random_memory_tagging_ = false;
diff --git a/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim.h b/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim.h
index a35afc8..05f9e40c 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim.h
@@ -156,9 +156,6 @@
 enum class BucketDistribution : uint8_t { kNeutral, kDenser };
 using EventuallyZeroFreedMemory = partition_alloc::internal::base::
     StrongAlias<class EventuallyZeroFreedMemoryTag, bool>;
-using FewerMemoryRegions =
-    partition_alloc::internal::base::StrongAlias<class FewerMemoryRegionsTag,
-                                                 bool>;
 // If |thread_cache_on_non_quarantinable_partition| is specified, the
 // thread-cache will be enabled on the non-quarantinable partition. The
 // thread-cache on the main (malloc) partition will be disabled.
@@ -175,8 +172,7 @@
         scheduler_loop_quarantine_thread_local_config,
     partition_alloc::internal::SchedulerLoopQuarantineConfig
         scheduler_loop_quarantine_for_advanced_memory_safety_checks_config,
-    EventuallyZeroFreedMemory eventually_zero_freed_memory,
-    FewerMemoryRegions fewer_memory_regions);
+    EventuallyZeroFreedMemory eventually_zero_freed_memory);
 
 PA_COMPONENT_EXPORT(ALLOCATOR_SHIM) uint32_t GetMainPartitionRootExtrasSize();
 
diff --git a/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.cc b/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.cc
index 804ca665..db241ee 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.cc
@@ -659,8 +659,7 @@
         scheduler_loop_quarantine_thread_local_config,
     partition_alloc::internal::SchedulerLoopQuarantineConfig
         scheduler_loop_quarantine_for_advanced_memory_safety_checks_config,
-    EventuallyZeroFreedMemory eventually_zero_freed_memory,
-    FewerMemoryRegions fewer_memory_regions) {
+    EventuallyZeroFreedMemory eventually_zero_freed_memory) {
   // Calling Get() is actually important, even if the return value isn't
   // used, because it has a side effect of initializing the variable, if it
   // wasn't already.
@@ -688,9 +687,6 @@
             eventually_zero_freed_memory
                 ? partition_alloc::PartitionOptions::kEnabled
                 : partition_alloc::PartitionOptions::kDisabled;
-        opts.fewer_memory_regions =
-            fewer_memory_regions ? partition_alloc::PartitionOptions::kEnabled
-                                 : partition_alloc::PartitionOptions::kDisabled;
         opts.scheduler_loop_quarantine_global_config =
             scheduler_loop_quarantine_global_config;
         opts.scheduler_loop_quarantine_thread_local_config =
diff --git a/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.h b/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.h
index da97f12b..1bce541 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.h
@@ -198,7 +198,6 @@
       partition_alloc::internal::SchedulerLoopQuarantineConfig();
 
   auto eventually_zero_freed_memory = EventuallyZeroFreedMemory(false);
-  auto fewer_memory_regions = FewerMemoryRegions(false);
 
   ConfigurePartitions(
       enable_brp, brp_extra_extras_size, enable_memory_tagging,
@@ -206,7 +205,7 @@
       scheduler_loop_quarantine_global_config,
       scheduler_loop_quarantine_thread_local_config,
       scheduler_loop_quarantine_for_advanced_memory_safety_checks_config,
-      eventually_zero_freed_memory, fewer_memory_regions);
+      eventually_zero_freed_memory);
 }
 #endif  // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc
index b63b0cb..4da14ed 100644
--- a/base/trace_event/malloc_dump_provider.cc
+++ b/base/trace_event/malloc_dump_provider.cc
@@ -608,10 +608,10 @@
   auto total_active_bytes = memory_stats->total_active_bytes;
   size_t wasted = 0;
   // This should always be true, but only if our accounting of committed bytes
-  // is consistent, which it isn't. Indeed, with
-  // PartitionAllocFewerMemoryRegions, we may allocate a slot span before the
-  // feature state is known, in which case we commit less, then decommit it
-  // after, in which case we subtract the new commit unit, which is larger.
+  // is consistent, which it isn't. Indeed, with kUseFewerMemoryRegions, we may
+  // allocate a slot span before the feature state is known, in which case we
+  // commit less, then decommit it after, in which case we subtract the new
+  // commit unit, which is larger.
   //
   // Properly handling this would require remembering how much was committed,
   // which complicates bookkeeping, especially as metadata space is
diff --git a/build_overrides/partition_alloc.gni b/build_overrides/partition_alloc.gni
index 2c92eeb..8d824e1 100644
--- a/build_overrides/partition_alloc.gni
+++ b/build_overrides/partition_alloc.gni
@@ -99,6 +99,12 @@
   use_allocator_shim_default = false
 }
 
+# When Cronet builds within Android this is not supported. See
+# https://crrev.com/c/6179216.
+if (is_cronet_for_aosp_build) {
+  use_allocator_shim_default = false
+}
+
 shim_supports_sized_dealloc_default = use_sized_deallocation
 
 use_partition_alloc_as_malloc_default =
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index 9f59da2..c94cead5 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -47,8 +47,6 @@
 #include "gpu/config/gpu_finch_features.h"
 #include "gpu/config/gpu_info.h"
 #include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkColorFilter.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkData.h"
 #include "third_party/skia/include/core/SkImage.h"
@@ -57,15 +55,7 @@
 #include "third_party/skia/include/core/SkRect.h"
 #include "third_party/skia/include/core/SkSamplingOptions.h"
 #include "third_party/skia/include/core/SkSize.h"
-#include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/core/SkYUVAPixmaps.h"
-#include "third_party/skia/include/gpu/GpuTypes.h"
-#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
-#include "third_party/skia/include/gpu/ganesh/GrYUVABackendTextures.h"
-#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
-#include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
-#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/skia_conversions.h"
@@ -891,31 +881,19 @@
 // GpuImageDecodeCache::UploadedImageData
 
 GpuImageDecodeCache::UploadedImageData::UploadedImageData() = default;
-GpuImageDecodeCache::UploadedImageData::~UploadedImageData() {
-  DCHECK(!image());
-  DCHECK(!image_yuv_planes_);
-  DCHECK(!gl_plane_ids_);
-}
+GpuImageDecodeCache::UploadedImageData::~UploadedImageData() = default;
 
 void GpuImageDecodeCache::UploadedImageData::SetTransferCacheId(uint32_t id) {
-  DCHECK(mode_ == Mode::kNone);
-  DCHECK(!image_);
   DCHECK(!transfer_cache_id_);
 
-  mode_ = Mode::kTransferCache;
   transfer_cache_id_ = id;
   OnSetLockedData(false /* out_of_raster */);
 }
 
 void GpuImageDecodeCache::UploadedImageData::Reset() {
-  if (mode_ != Mode::kNone)
+  if (transfer_cache_id_) {
     ReportUsageStats();
-  mode_ = Mode::kNone;
-  image_ = nullptr;
-  image_yuv_planes_.reset();
-  gl_plane_ids_.reset();
-  gl_id_ = 0;
-  is_alpha_ = false;
+  }
   transfer_cache_id_.reset();
   OnResetData();
 }
@@ -1595,66 +1573,6 @@
   return max_working_set_bytes_;
 }
 
-void GpuImageDecodeCache::AddTextureDump(
-    base::trace_event::ProcessMemoryDump* pmd,
-    const std::string& texture_dump_name,
-    const size_t bytes,
-    const GrGLuint gl_id,
-    const size_t locked_size) const {
-  using base::trace_event::MemoryAllocatorDump;
-  using base::trace_event::MemoryAllocatorDumpGuid;
-
-  MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(texture_dump_name);
-  dump->AddScalar(MemoryAllocatorDump::kNameSize,
-                  MemoryAllocatorDump::kUnitsBytes, bytes);
-
-  // Dump the "locked_size" as an additional column.
-  dump->AddScalar("locked_size", MemoryAllocatorDump::kUnitsBytes, locked_size);
-
-  MemoryAllocatorDumpGuid guid;
-  guid = gl::GetGLTextureClientGUIDForTracing(
-      context_->ContextSupport()->ShareGroupTracingGUID(), gl_id);
-  pmd->CreateSharedGlobalAllocatorDump(guid);
-  // Importance of 3 gives this dump priority over the dump made by Skia
-  // (importance 2), attributing memory here.
-  const int kImportance = 3;
-  pmd->AddOwnershipEdge(dump->guid(), guid, kImportance);
-}
-
-void GpuImageDecodeCache::MemoryDumpYUVImage(
-    base::trace_event::ProcessMemoryDump* pmd,
-    const ImageData* image_data,
-    const std::string& dump_base_name,
-    size_t locked_size) const {
-  using base::trace_event::MemoryAllocatorDump;
-  DCHECK(image_data->info.yuva.has_value());
-  DCHECK(image_data->upload.has_yuv_planes());
-
-  struct PlaneMemoryDumpInfo {
-    size_t byte_size;
-    GrGLuint gl_id;
-  };
-  std::vector<PlaneMemoryDumpInfo> plane_dump_infos;
-  // TODO(crbug.com/40604431): Also include alpha plane if applicable.
-  plane_dump_infos.push_back({image_data->upload.y_image()->textureSize(),
-                              image_data->upload.gl_y_id()});
-  plane_dump_infos.push_back({image_data->upload.u_image()->textureSize(),
-                              image_data->upload.gl_u_id()});
-  plane_dump_infos.push_back({image_data->upload.v_image()->textureSize(),
-                              image_data->upload.gl_v_id()});
-
-  for (size_t i = 0u; i < plane_dump_infos.size(); ++i) {
-    auto plane_dump_info = plane_dump_infos.at(i);
-    // If the image is currently locked, we dump the locked size per plane.
-    AddTextureDump(
-        pmd,
-        dump_base_name +
-            base::StringPrintf("/plane_%0u", base::checked_cast<uint32_t>(i)),
-        plane_dump_info.byte_size, plane_dump_info.gl_id,
-        locked_size ? plane_dump_info.byte_size : 0u);
-  }
-}
-
 bool GpuImageDecodeCache::OnMemoryDump(
     const base::trace_event::MemoryDumpArgs& args,
     base::trace_event::ProcessMemoryDump* pmd) {
@@ -2912,30 +2830,6 @@
   return image_data->decode.ImageForTesting();
 }
 
-// Used for in-process-raster YUV decoding tests, where we often need the
-// SkImages for each underlying plane because asserting or requesting fields for
-// the YUV SkImage may flatten it to RGB or not be possible to request.
-sk_sp<SkImage> GpuImageDecodeCache::GetUploadedPlaneForTesting(
-    const DrawImage& draw_image,
-    YUVIndex index) {
-  base::AutoLock lock(lock_);
-  ImageData* image_data = GetImageDataForDrawImage(
-      draw_image, InUseCacheKeyFromDrawImage(draw_image));
-  if (!image_data->info.yuva.has_value()) {
-    return nullptr;
-  }
-  switch (index) {
-    case YUVIndex::kY:
-      return image_data->upload.y_image();
-    case YUVIndex::kU:
-      return image_data->upload.u_image();
-    case YUVIndex::kV:
-      return image_data->upload.v_image();
-    default:
-      return nullptr;
-  }
-}
-
 size_t GpuImageDecodeCache::GetDarkModeImageCacheSizeForTesting(
     const DrawImage& draw_image) {
   base::AutoLock lock(lock_);
diff --git a/cc/tiles/gpu_image_decode_cache.h b/cc/tiles/gpu_image_decode_cache.h
index 6446c7f..0ff2115c 100644
--- a/cc/tiles/gpu_image_decode_cache.h
+++ b/cc/tiles/gpu_image_decode_cache.h
@@ -30,10 +30,6 @@
 #include "cc/cc_export.h"
 #include "cc/paint/image_transfer_cache_entry.h"
 #include "cc/tiles/image_decode_cache.h"
-#include "third_party/skia/include/core/SkImage.h"
-#include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/core/SkYUVAInfo.h"
-#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
 
 namespace viz {
 class RasterContextProvider;
@@ -226,8 +222,6 @@
   bool IsInInUseCacheForTesting(const DrawImage& image) const;
   bool IsInPersistentCacheForTesting(const DrawImage& image) const;
   sk_sp<SkImage> GetSWImageDecodeForTesting(const DrawImage& image);
-  sk_sp<SkImage> GetUploadedPlaneForTesting(const DrawImage& draw_image,
-                                            YUVIndex index);
   size_t GetDarkModeImageCacheSizeForTesting(const DrawImage& draw_image);
   size_t paint_image_entries_count_for_testing() const {
     base::AutoLock locker(lock_);
@@ -423,94 +417,13 @@
     void SetTransferCacheId(uint32_t id);
     void Reset();
 
-    // If in image mode.
-    const sk_sp<SkImage>& image() const {
-      DCHECK(mode_ == Mode::kSkImage || mode_ == Mode::kNone);
-      return image_;
-    }
-    const sk_sp<SkImage>& y_image() const {
-      return plane_image_internal(YUVIndex::kY);
-    }
-    const sk_sp<SkImage>& u_image() const {
-      return plane_image_internal(YUVIndex::kU);
-    }
-    const sk_sp<SkImage>& v_image() const {
-      return plane_image_internal(YUVIndex::kV);
-    }
-    GrGLuint gl_id() const {
-      DCHECK(mode_ == Mode::kSkImage || mode_ == Mode::kNone);
-      return gl_id_;
-    }
-
-    GrGLuint gl_y_id() const { return gl_plane_id_internal(YUVIndex::kY); }
-    GrGLuint gl_u_id() const { return gl_plane_id_internal(YUVIndex::kU); }
-    GrGLuint gl_v_id() const { return gl_plane_id_internal(YUVIndex::kV); }
-
-    // We consider an image to be valid YUV if all planes are non-null.
-    bool has_yuv_planes() const {
-      if (!image_yuv_planes_) {
-        return false;
-      }
-      auto yuv_planes_rstart = image_yuv_planes_->crbegin() + !is_alpha_;
-      auto yuv_planes_rend = image_yuv_planes_->crend();
-      // Iterates from end to beginning, skipping alpha plane (verified to be
-      // last) if the image is not alpha.
-      bool has_existing_planes = std::any_of(yuv_planes_rstart, yuv_planes_rend,
-                                             [](auto& it) { return it; });
-      bool has_null_planes = std::any_of(yuv_planes_rstart, yuv_planes_rend,
-                                         [](auto& it) { return !it; });
-      if (has_existing_planes && has_null_planes) {
-        DLOG(ERROR) << "Image has a mix of null and decoded planes";
-      }
-      return has_existing_planes && !has_null_planes;
-    }
-
-    // If in transfer cache mode.
     std::optional<uint32_t> transfer_cache_id() const {
-      DCHECK(mode_ == Mode::kTransferCache || mode_ == Mode::kNone);
       return transfer_cache_id_;
     }
 
    private:
-    // Used for internal DCHECKs only.
-    enum class Mode {
-      kNone,
-      kSkImage,
-      kTransferCache,
-    };
-
     void ReportUsageStats() const;
 
-    const sk_sp<SkImage>& plane_image_internal(const YUVIndex yuv_index) const {
-      DCHECK(mode_ == Mode::kSkImage || mode_ == Mode::kNone);
-      DCHECK(image_yuv_planes_);
-      const size_t index = static_cast<size_t>(yuv_index);
-      DCHECK_GT(image_yuv_planes_->size(), index)
-          << "Requested reference to a plane_id that is not set";
-      return image_yuv_planes_->at(index);
-    }
-
-    GrGLuint gl_plane_id_internal(const YUVIndex yuv_index) const {
-      DCHECK(mode_ == Mode::kSkImage || mode_ == Mode::kNone);
-      DCHECK(gl_plane_ids_);
-      const size_t index = static_cast<size_t>(yuv_index);
-      DCHECK_GT(gl_plane_ids_->size(), index)
-          << "Requested GL id for a plane texture that is not uploaded";
-      return gl_plane_ids_->at(index);
-    }
-
-    Mode mode_ = Mode::kNone;
-
-    // Used if |mode_| == kSkImage.
-    // May be null if image not yet uploaded / prepared.
-    sk_sp<SkImage> image_;
-    std::optional<YUVSkImages> image_yuv_planes_;
-    // TODO(crbug.com/40604431): Change after alpha support.
-    bool is_alpha_ = false;
-    GrGLuint gl_id_ = 0;
-    std::optional<std::array<GrGLuint, kNumYUVPlanes>> gl_plane_ids_;
-
-    // Used if |mode_| == kTransferCache.
     std::optional<uint32_t> transfer_cache_id_;
   };
 
@@ -782,28 +695,6 @@
 
   sk_sp<SkColorSpace> ColorSpaceForImageDecode(const DrawImage& image) const;
 
-  // Helper function to add a memory dump to |pmd| for a single texture
-  // identified by |gl_id| with size |bytes| and |locked_size| equal to either
-  // |bytes| or 0 depending on whether the texture is currently locked.
-  void AddTextureDump(base::trace_event::ProcessMemoryDump* pmd,
-                      const std::string& texture_dump_name,
-                      const size_t bytes,
-                      const GrGLuint gl_id,
-                      const size_t locked_size) const
-      EXCLUSIVE_LOCKS_REQUIRED(lock_);
-
-  // Alias each texture of the YUV image entry to its Skia texture counterpart,
-  // taking ownership of the memory and preventing double counting.
-  //
-  // Given |dump_base_name| as the location where single RGB image textures are
-  // dumped, this method creates dumps under |pmd| for the planar textures
-  // backing |image_data| as subcategories plane_0, plane_1, etc.
-  void MemoryDumpYUVImage(base::trace_event::ProcessMemoryDump* pmd,
-                          const ImageData* image_data,
-                          const std::string& dump_base_name,
-                          size_t locked_size) const
-      EXCLUSIVE_LOCKS_REQUIRED(lock_);
-
   // |persistent_cache_| represents the long-lived cache, keeping a certain
   // budget of ImageDatas alive even when their ref count reaches zero.
   using PersistentCache = base::HashingLRUCache<PaintImage::FrameKey,
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 87c40042..8321c32 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1184,6 +1184,7 @@
       "//chrome/browser/ui/messages/android:junit",
       "//chrome/browser/ui/plus_addresses/android:junit",
       "//chrome/browser/uid/android:junit",
+      "//chrome/browser/url_constants/android:junit",
       "//chrome/browser/usb/android:junit",
       "//chrome/browser/user_education:junit",
       "//chrome/browser/util:junit_tests",
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryChipViewRenderTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryChipViewRenderTest.java
index ba8867c..776e299 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryChipViewRenderTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryChipViewRenderTest.java
@@ -97,7 +97,7 @@
     public final RenderTestRule mRenderTestRule =
             RenderTestRule.Builder.withPublicCorpus()
                     .setBugComponent(Component.UI_BROWSER_AUTOFILL)
-                    .setRevision(2)
+                    .setRevision(4)
                     .build();
 
     @Mock private KeyboardAccessoryView mKeyboardAccessoryView;
diff --git a/chrome/android/java/res/layout/sync_enter_passphrase.xml b/chrome/android/java/res/layout/sync_enter_passphrase.xml
index 433fc1e..734eda7e 100644
--- a/chrome/android/java/res/layout/sync_enter_passphrase.xml
+++ b/chrome/android/java/res/layout/sync_enter_passphrase.xml
@@ -25,7 +25,8 @@
             android:hint="@string/sync_enter_custom_passphrase_hint"
             android:inputType="textPassword"
             android:singleLine="true"
-            android:imeOptions="actionNext" />
+            android:imeOptions="actionNext"
+            android:textAlignment="viewStart" />
         <!--
         Sets this TextView as a polite accessibility live region. Changes to
         its text, such as "Verifying..." or "Incorrect passphrase", will be
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
index 14ea79e..c4c8311 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
@@ -577,6 +577,15 @@
             @JniType("base::Token") Token tabGroupId, Activity activity, int newIndex);
 
     @Override
+    public int getPinnedTabsCount() {
+        // The index of the first non-pinned tab is equivalent to the number of pinned tabs.
+        // For example, if there are 3 pinned tabs at indices 0, 1, and 2, the first non-pinned
+        // tab will be at index 3. If all tabs are pinned, this will return getCount(). If no
+        // tabs are pinned, this will return 0.
+        return findFirstNonPinnedTabIndex();
+    }
+
+    @Override
     public void setMuteSetting(List<Tab> tabs, boolean mute) {
         TabModelJniBridgeJni.get().setMuteSetting(mNativeTabModelJniBridge, tabs, mute);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
index 091ec43..db44fe3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
@@ -1598,6 +1598,7 @@
 
             recordLegacyTabCountMetrics();
             recordTabCountMetrics();
+            recordPinnedTabCountMetrics();
             recordRestoreDuration();
             recordUniqueTabUrlMetrics();
             cleanUpPersistentData();
@@ -1651,6 +1652,15 @@
                 mTabModelSelector.getModel(true).getCount());
     }
 
+    private void recordPinnedTabCountMetrics() {
+        RecordHistogram.recordCount1MHistogram(
+                "Tabs.Startup.PinnedTabCount." + mClientTag + ".Regular",
+                mTabModelSelector.getModel(false).getPinnedTabsCount());
+        RecordHistogram.recordCount1MHistogram(
+                "Tabs.Startup.PinnedTabCount." + mClientTag + ".Incognito",
+                mTabModelSelector.getModel(true).getPinnedTabsCount());
+    }
+
     private void recordRestoreDuration() {
         if (mTabRestoreStartTime == INVALID_TIME) return;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinatorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinatorTest.java
index 580baa4..5b228022 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinatorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinatorTest.java
@@ -34,6 +34,7 @@
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.Restriction;
@@ -144,6 +145,8 @@
     @UiThreadTest
     @EnableFeatures(ChromeFeatureList.ANDROID_BOOKMARK_BAR)
     @Restriction({DeviceFormFactor.PHONE})
+    @DisabledTest
+    // TODO(crbug.com/447525636): Re-enable tests.
     public void testTopControlsHeightWithBookmarkBarWhenFlagIsEnabledOnPhone() {
         testTopControlsHeightWithBookmarkBar(/* expectBookmarkBar= */ false);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelImplTest.java
index 2ba74254..5eaebf9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelImplTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelImplTest.java
@@ -1735,6 +1735,35 @@
                 });
     }
 
+    @Test
+    @SmallTest
+    @EnableFeatures(ChromeFeatureList.ANDROID_PINNED_TABS)
+    public void testGetPinnedTabsCount() {
+        createTabs(3);
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    TabModel tabModel =
+                            mActivityTestRule.getActivity().getTabModelSelector().getModel(false);
+                    Tab tab1 = tabModel.getTabAt(1);
+                    Tab tab2 = tabModel.getTabAt(2);
+
+                    assertEquals(0, tabModel.getPinnedTabsCount());
+
+                    tabModel.pinTab(tab1.getId());
+                    assertEquals(1, tabModel.getPinnedTabsCount());
+
+                    tabModel.pinTab(tab2.getId());
+                    assertEquals(2, tabModel.getPinnedTabsCount());
+
+                    tabModel.unpinTab(tab1.getId());
+                    assertEquals(1, tabModel.getPinnedTabsCount());
+
+                    // Cleanup.
+                    tabModel.unpinTab(tab2.getId());
+                });
+    }
+
     private void assertMoveTabToIndex(
             int oldIndex, int newIndex, int expectedIndex, boolean movingInsideGroup) {
         Tab oldIndexTab = mTabModelJni.getTabAt(oldIndex);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarTest.java
index 4b1e43f..7545384 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarTest.java
@@ -181,6 +181,8 @@
     @UiThreadTest
     @EnableFeatures(ChromeFeatureList.ANDROID_BOOKMARK_BAR)
     @Restriction({DeviceFormFactor.PHONE})
+    @DisabledTest
+    // TODO(crbug.com/447525636): Re-enable tests.
     public void testControlContainerTopMarginWhenBookmarkBarIsEnabledOnPhone() {
         testControlContainerTopMargin(/* expectBookmarkBar= */ false);
     }
diff --git a/chrome/android/junit/BUILD.gn b/chrome/android/junit/BUILD.gn
index 6d03fadb..cb9916c 100644
--- a/chrome/android/junit/BUILD.gn
+++ b/chrome/android/junit/BUILD.gn
@@ -215,6 +215,7 @@
       "//chrome/browser/ui/android/toolbar:java",
       "//chrome/browser/ui/messages/android:java",
       "//chrome/browser/uid/android:java",
+      "//chrome/browser/url_constants/android:java",
       "//chrome/browser/user_education:java",
       "//chrome/browser/util:java",
       "//chrome/browser/version:java",
diff --git a/chrome/browser/ash/app_list/app_sync_ui_state.cc b/chrome/browser/ash/app_list/app_sync_ui_state.cc
index 41c2a97d..41a352e7 100644
--- a/chrome/browser/ash/app_list/app_sync_ui_state.cc
+++ b/chrome/browser/ash/app_list/app_sync_ui_state.cc
@@ -149,6 +149,11 @@
   CheckAppSync();
 }
 
+void AppSyncUIState::OnSyncShutdown(syncer::SyncService* sync) {
+  // Unreachable, since this service is Shutdown() before the SyncService.
+  NOTREACHED();
+}
+
 void AppSyncUIState::OnExtensionLoaded(content::BrowserContext* browser_context,
                                        const extensions::Extension* extension) {
   CheckAppSync();
diff --git a/chrome/browser/ash/app_list/app_sync_ui_state.h b/chrome/browser/ash/app_list/app_sync_ui_state.h
index e5ee1260..30b6fd8 100644
--- a/chrome/browser/ash/app_list/app_sync_ui_state.h
+++ b/chrome/browser/ash/app_list/app_sync_ui_state.h
@@ -76,6 +76,7 @@
 
   // syncer::SyncServiceObserver overrides:
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
   // extensions::ExtensionRegistryObserver overrides:
   void OnExtensionLoaded(content::BrowserContext* browser_context,
diff --git a/chrome/browser/ash/crostini/crostini_features.cc b/chrome/browser/ash/crostini/crostini_features.cc
index 722dae4..bb7f214 100644
--- a/chrome/browser/ash/crostini/crostini_features.cc
+++ b/chrome/browser/ash/crostini/crostini_features.cc
@@ -180,7 +180,8 @@
 
 CrostiniFeatures::~CrostiniFeatures() = default;
 
-bool CrostiniFeatures::CouldBeAllowed(Profile* profile, std::string* reason) {
+bool CrostiniFeatures::CouldBeAllowed(Profile* profile,
+                                      std::string* reason) const {
   if (!base::FeatureList::IsEnabled(features::kCrostini)) {
     VLOG(1) << "Crostini is not enabled in feature list.";
     // Prior to M105, the /dev/kvm check used the same reason string.
@@ -217,12 +218,13 @@
   return true;
 }
 
-bool CrostiniFeatures::CouldBeAllowed(Profile* profile) {
+bool CrostiniFeatures::CouldBeAllowed(Profile* profile) const {
   std::string reason;
   return CouldBeAllowed(profile, &reason);
 }
 
-bool CrostiniFeatures::IsAllowedNow(Profile* profile, std::string* reason) {
+bool CrostiniFeatures::IsAllowedNow(Profile* profile,
+                                    std::string* reason) const {
   if (!CouldBeAllowed(profile, reason)) {
     return false;
   }
@@ -259,23 +261,23 @@
   return true;
 }
 
-bool CrostiniFeatures::IsAllowedNow(Profile* profile) {
+bool CrostiniFeatures::IsAllowedNow(Profile* profile) const {
   std::string reason;
   return IsAllowedNow(profile, &reason);
 }
 
-bool CrostiniFeatures::IsEnabled(Profile* profile) {
+bool CrostiniFeatures::IsEnabled(Profile* profile) const {
   return g_crostini_features->IsAllowedNow(profile) &&
          profile->GetPrefs()->GetBoolean(crostini::prefs::kCrostiniEnabled);
 }
 
-bool CrostiniFeatures::IsExportImportUIAllowed(Profile* profile) {
+bool CrostiniFeatures::IsExportImportUIAllowed(Profile* profile) const {
   return g_crostini_features->IsAllowedNow(profile) &&
          profile->GetPrefs()->GetBoolean(
              crostini::prefs::kUserCrostiniExportImportUIAllowedByPolicy);
 }
 
-bool CrostiniFeatures::IsRootAccessAllowed(Profile* profile) {
+bool CrostiniFeatures::IsRootAccessAllowed(Profile* profile) const {
   if (base::FeatureList::IsEnabled(features::kCrostiniAdvancedAccessControls)) {
     return profile->GetPrefs()->GetBoolean(
         crostini::prefs::kUserCrostiniRootAccessAllowedByPolicy);
@@ -283,13 +285,13 @@
   return true;
 }
 
-bool CrostiniFeatures::IsContainerUpgradeUIAllowed(Profile* profile) {
+bool CrostiniFeatures::IsContainerUpgradeUIAllowed(Profile* profile) const {
   return g_crostini_features->IsAllowedNow(profile);
 }
 
 void CrostiniFeatures::CanChangeAdbSideloading(
     Profile* profile,
-    CanChangeAdbSideloadingCallback callback) {
+    CanChangeAdbSideloadingCallback callback) const {
   // First rule out a child account as it is a special case - a child can be an
   // owner, but ADB sideloading is currently not supported for this case
   if (profile->IsChild()) {
@@ -331,7 +333,7 @@
   std::move(callback).Run(true);
 }
 
-bool CrostiniFeatures::IsPortForwardingAllowed(Profile* profile) {
+bool CrostiniFeatures::IsPortForwardingAllowed(Profile* profile) const {
   if (!profile->GetPrefs()->GetBoolean(
           crostini::prefs::kCrostiniPortForwardingAllowedByPolicy)) {
     VLOG(1) << "kCrostiniPortForwardingAllowedByPolicy preference is false.";
@@ -358,7 +360,7 @@
   return is_baguette;
 }
 
-bool CrostiniFeatures::IsMultiContainerAllowed(Profile* profile) {
+bool CrostiniFeatures::IsMultiContainerAllowed(Profile* profile) const {
   return g_crostini_features->IsAllowedNow(profile) &&
          base::FeatureList::IsEnabled(ash::features::kCrostiniMultiContainer);
 }
diff --git a/chrome/browser/ash/crostini/crostini_features.h b/chrome/browser/ash/crostini/crostini_features.h
index 55afb18..3ae68ac 100644
--- a/chrome/browser/ash/crostini/crostini_features.h
+++ b/chrome/browser/ash/crostini/crostini_features.h
@@ -29,38 +29,38 @@
   // lifetime of the process. Also provides the |reason| if crostini is
   // disallowed. The |reason| string is to only be used in crosh/vmc error
   // messages.
-  virtual bool CouldBeAllowed(Profile* profile, std::string* reason);
+  virtual bool CouldBeAllowed(Profile* profile, std::string* reason) const;
 
   // Returns false if this |profile| will never be allowed to run crostini for
   // the lifetime of this process, otherwise returns true. The return value of
   // this method is guaranteed not to change for a given |profile| within the
   // lifetime of the process.
-  virtual bool CouldBeAllowed(Profile* profile);
+  virtual bool CouldBeAllowed(Profile* profile) const;
 
   // Returns true if |profile| is allowed to run crostini at this moment. This
   // method will never return true if CouldBeAllowed returns false for the same
   // profile, but otherwise may change return value at any time. Also provides
   // the reason if crostini is disallowed.
-  virtual bool IsAllowedNow(Profile* profile, std::string* reason);
+  virtual bool IsAllowedNow(Profile* profile, std::string* reason) const;
 
   // Returns true if |profile| is allowed to run crostini at this moment. This
   // method will never return true if CouldBeAllowed returns false for the same
   // profile, but otherwise may change return value at any time.
-  virtual bool IsAllowedNow(Profile* profile);
+  virtual bool IsAllowedNow(Profile* profile) const;
 
   // Returns whether if Crostini has been enabled, i.e. the user has launched it
   // at least once and not deleted it.
-  virtual bool IsEnabled(Profile* profile);
+  virtual bool IsEnabled(Profile* profile) const;
 
   // Returns true if policy allows export import UI.
-  virtual bool IsExportImportUIAllowed(Profile*);
+  virtual bool IsExportImportUIAllowed(Profile*) const;
 
   // Returns whether user is allowed root access to Crostini. Always returns
   // true when advanced access controls feature flag is disabled.
-  virtual bool IsRootAccessAllowed(Profile*);
+  virtual bool IsRootAccessAllowed(Profile*) const;
 
   // Returns true if container upgrade ui is allowed by flag.
-  virtual bool IsContainerUpgradeUIAllowed(Profile*);
+  virtual bool IsContainerUpgradeUIAllowed(Profile*) const;
 
   using CanChangeAdbSideloadingCallback =
       base::OnceCallback<void(bool can_change_adb_sideloading)>;
@@ -72,19 +72,19 @@
   // whether changes to ADB sideloading are allowed.
   virtual void CanChangeAdbSideloading(
       Profile* profile,
-      CanChangeAdbSideloadingCallback callback);
+      CanChangeAdbSideloadingCallback callback) const;
 
   // Returns whether the user is allowed to configure port forwarding into
   // Crostini. If the user is not managed or if the policy is unset or true,
   // then this returns true, else if the policy is set to false, this returns
   // false.
-  virtual bool IsPortForwardingAllowed(Profile* profile);
+  virtual bool IsPortForwardingAllowed(Profile* profile) const;
 
   // Returns whether we are running a baguette (containerless) crostini.
   virtual bool IsBaguette(Profile* profile) const;
 
   // Returns true if user is allowed to use multiple (non-default) containers.
-  virtual bool IsMultiContainerAllowed(Profile*);
+  virtual bool IsMultiContainerAllowed(Profile*) const;
 
   // TODO(crbug.com/40647881): Move other functions from crostini_util to here.
 
diff --git a/chrome/browser/ash/crostini/fake_crostini_features.cc b/chrome/browser/ash/crostini/fake_crostini_features.cc
index 1ff02dd..904cf6f1 100644
--- a/chrome/browser/ash/crostini/fake_crostini_features.cc
+++ b/chrome/browser/ash/crostini/fake_crostini_features.cc
@@ -40,7 +40,7 @@
 }
 
 bool FakeCrostiniFeatures::CouldBeAllowed(Profile* profile,
-                                          std::string* reason) {
+                                          std::string* reason) const {
   if (could_be_allowed_.has_value()) {
     *reason = "some reason";
     return *could_be_allowed_;
@@ -48,7 +48,8 @@
   return original_features_->CouldBeAllowed(profile, reason);
 }
 
-bool FakeCrostiniFeatures::IsAllowedNow(Profile* profile, std::string* reason) {
+bool FakeCrostiniFeatures::IsAllowedNow(Profile* profile,
+                                        std::string* reason) const {
   if (could_be_allowed_.has_value() && !could_be_allowed_) {
     *reason = "some reason";
     return false;
@@ -60,28 +61,28 @@
   return original_features_->IsAllowedNow(profile, reason);
 }
 
-bool FakeCrostiniFeatures::IsEnabled(Profile* profile) {
+bool FakeCrostiniFeatures::IsEnabled(Profile* profile) const {
   if (enabled_.has_value()) {
     return *enabled_;
   }
   return original_features_->IsEnabled(profile);
 }
 
-bool FakeCrostiniFeatures::IsExportImportUIAllowed(Profile* profile) {
+bool FakeCrostiniFeatures::IsExportImportUIAllowed(Profile* profile) const {
   if (export_import_ui_allowed_.has_value()) {
     return *export_import_ui_allowed_;
   }
   return original_features_->IsExportImportUIAllowed(profile);
 }
 
-bool FakeCrostiniFeatures::IsRootAccessAllowed(Profile* profile) {
+bool FakeCrostiniFeatures::IsRootAccessAllowed(Profile* profile) const {
   if (root_access_allowed_.has_value()) {
     return *root_access_allowed_;
   }
   return original_features_->IsRootAccessAllowed(profile);
 }
 
-bool FakeCrostiniFeatures::IsContainerUpgradeUIAllowed(Profile* profile) {
+bool FakeCrostiniFeatures::IsContainerUpgradeUIAllowed(Profile* profile) const {
   if (container_upgrade_ui_allowed_.has_value()) {
     return *container_upgrade_ui_allowed_;
   }
@@ -90,7 +91,7 @@
 
 void FakeCrostiniFeatures::CanChangeAdbSideloading(
     Profile* profile,
-    CanChangeAdbSideloadingCallback callback) {
+    CanChangeAdbSideloadingCallback callback) const {
   if (can_change_adb_sideloading_.has_value()) {
     std::move(callback).Run(*can_change_adb_sideloading_);
     return;
@@ -98,14 +99,14 @@
   original_features_->CanChangeAdbSideloading(profile, std::move(callback));
 }
 
-bool FakeCrostiniFeatures::IsPortForwardingAllowed(Profile* profile) {
+bool FakeCrostiniFeatures::IsPortForwardingAllowed(Profile* profile) const {
   if (port_forwarding_allowed_.has_value()) {
     return *port_forwarding_allowed_;
   }
   return original_features_->IsPortForwardingAllowed(profile);
 }
 
-bool FakeCrostiniFeatures::IsMultiContainerAllowed(Profile* profile) {
+bool FakeCrostiniFeatures::IsMultiContainerAllowed(Profile* profile) const {
   return multi_container_allowed_.value_or(
       original_features_->IsMultiContainerAllowed(profile));
 }
diff --git a/chrome/browser/ash/crostini/fake_crostini_features.h b/chrome/browser/ash/crostini/fake_crostini_features.h
index 99c88cb..2df655c4 100644
--- a/chrome/browser/ash/crostini/fake_crostini_features.h
+++ b/chrome/browser/ash/crostini/fake_crostini_features.h
@@ -24,17 +24,17 @@
   ~FakeCrostiniFeatures() override;
 
   // CrostiniFeatures:
-  bool CouldBeAllowed(Profile* profile, std::string* reason) override;
-  bool IsAllowedNow(Profile* profile, std::string* reason) override;
-  bool IsEnabled(Profile* profile) override;
-  bool IsExportImportUIAllowed(Profile* profile) override;
-  bool IsRootAccessAllowed(Profile* profile) override;
-  bool IsContainerUpgradeUIAllowed(Profile* profile) override;
+  bool CouldBeAllowed(Profile* profile, std::string* reason) const override;
+  bool IsAllowedNow(Profile* profile, std::string* reason) const override;
+  bool IsEnabled(Profile* profile) const override;
+  bool IsExportImportUIAllowed(Profile* profile) const override;
+  bool IsRootAccessAllowed(Profile* profile) const override;
+  bool IsContainerUpgradeUIAllowed(Profile* profile) const override;
   void CanChangeAdbSideloading(
       Profile* profile,
-      CanChangeAdbSideloadingCallback callback) override;
-  bool IsPortForwardingAllowed(Profile* profile) override;
-  bool IsMultiContainerAllowed(Profile* profile) override;
+      CanChangeAdbSideloadingCallback callback) const override;
+  bool IsPortForwardingAllowed(Profile* profile) const override;
+  bool IsMultiContainerAllowed(Profile* profile) const override;
 
   void SetAll(bool flag);
   void ClearAll();
diff --git a/chrome/browser/ash/login/signin/auth_error_observer.cc b/chrome/browser/ash/login/signin/auth_error_observer.cc
index 13627077..51c9a07 100644
--- a/chrome/browser/ash/login/signin/auth_error_observer.cc
+++ b/chrome/browser/ash/login/signin/auth_error_observer.cc
@@ -63,6 +63,11 @@
   HandleAuthError(sync->GetAuthError());
 }
 
+void AuthErrorObserver::OnSyncShutdown(syncer::SyncService* sync) {
+  // Unreachable, since this service is Shutdown() before the SyncService.
+  NOTREACHED();
+}
+
 void AuthErrorObserver::OnErrorChanged() {
   // This notification could have come for any account but we are only
   // interested in errors for the Primary Account.
diff --git a/chrome/browser/ash/login/signin/auth_error_observer.h b/chrome/browser/ash/login/signin/auth_error_observer.h
index 9bd6783..5c34dd1 100644
--- a/chrome/browser/ash/login/signin/auth_error_observer.h
+++ b/chrome/browser/ash/login/signin/auth_error_observer.h
@@ -43,6 +43,7 @@
 
   // syncer::SyncServiceObserver implementation.
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
   // SigninErrorController::Observer implementation.
   void OnErrorChanged() override;
diff --git a/chrome/browser/ash/preferences/preferences.cc b/chrome/browser/ash/preferences/preferences.cc
index 88751aa..84fc340 100644
--- a/chrome/browser/ash/preferences/preferences.cc
+++ b/chrome/browser/ash/preferences/preferences.cc
@@ -170,6 +170,7 @@
                              base::Value::List());
   registry->RegisterListPref(prefs::kDnsOverHttpsIncludedDomains,
                              base::Value::List());
+  registry->RegisterBooleanPref(prefs::kSilentPrintingEnabled, false);
 
   RegisterLocalStatePrefs(registry);
 }
diff --git a/chrome/browser/auxiliary_search/javatests/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchDonorTest.java b/chrome/browser/auxiliary_search/javatests/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchDonorTest.java
index 496b956..1e612fb6 100644
--- a/chrome/browser/auxiliary_search/javatests/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchDonorTest.java
+++ b/chrome/browser/auxiliary_search/javatests/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchDonorTest.java
@@ -93,16 +93,6 @@
         testDonateTabsImpl();
     }
 
-    @Test
-    @MediumTest
-    @EnableFeatures({
-        "AndroidAppIntegrationMultiDataSource:multi_data_source_skip_schema_check/true"
-    })
-    @DisableIf.Build(sdk_is_less_than = VERSION_CODES.S, message = "The donation API is for S+.")
-    public void testDonateTabs_MultiDataSource() {
-        testDonateTabsImpl();
-    }
-
     private void testDonateTabsImpl() {
         ThreadUtils.runOnUiThreadBlocking(() -> mAuxiliarySearchDonor.createSessionAndInit());
         CriteriaHelper.pollUiThread(() -> mAuxiliarySearchDonor.getIsSchemaSetForTesting());
diff --git a/chrome/browser/enterprise/reporting/extension_request/extension_request_notification_unittest.cc b/chrome/browser/enterprise/reporting/extension_request/extension_request_notification_browsertest.cc
similarity index 80%
rename from chrome/browser/enterprise/reporting/extension_request/extension_request_notification_unittest.cc
rename to chrome/browser/enterprise/reporting/extension_request/extension_request_notification_browsertest.cc
index aa8fbfc3f..8757b88 100644
--- a/chrome/browser/enterprise/reporting/extension_request/extension_request_notification_unittest.cc
+++ b/chrome/browser/enterprise/reporting/extension_request/extension_request_notification_browsertest.cc
@@ -10,11 +10,13 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/browser/notifications/notification_handler.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/grit/generated_resources.h"
-#include "chrome/test/base/browser_with_test_window_test.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_profile_manager.h"
-#include "content/public/test/browser_task_environment.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -49,17 +51,22 @@
 }
 
 class ExtensionRequestNotificationTest
-    : public BrowserWithTestWindowTest,
+    : public InProcessBrowserTest,
       public testing::WithParamInterface<
           ExtensionRequestNotification::NotifyType> {
  public:
   ExtensionRequestNotificationTest() = default;
   ~ExtensionRequestNotificationTest() override = default;
 
-  void SetUp() override {
-    BrowserWithTestWindowTest::SetUp();
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
     display_service_tester_ =
-        std::make_unique<NotificationDisplayServiceTester>(profile());
+        std::make_unique<NotificationDisplayServiceTester>(GetProfile());
+  }
+
+  void TearDownOnMainThread() override {
+    display_service_tester_.reset();
+    InProcessBrowserTest::TearDownOnMainThread();
   }
 
   ExtensionRequestNotification::NotifyType GetNotifyType() {
@@ -81,9 +88,10 @@
                       ExtensionRequestNotification::kRejected,
                       ExtensionRequestNotification::kForceInstalled));
 
-TEST_P(ExtensionRequestNotificationTest, HasExtensionAndClickedByUser) {
+IN_PROC_BROWSER_TEST_P(ExtensionRequestNotificationTest,
+                       HasExtensionAndClickedByUser) {
   ExtensionRequestNotification request_notification(
-      profile(), GetNotifyType(),
+      GetProfile(), GetNotifyType(),
       ExtensionRequestNotification::ExtensionIds({kFakeExtensionId}));
   base::RunLoop show_run_loop;
   display_service_tester_->SetNotificationAddedClosure(
@@ -110,13 +118,15 @@
   EXPECT_FALSE(GetNotification().has_value());
   std::string expected_url =
       std::string(kChromeWebstoreUrl) + std::string(kFakeExtensionId);
-  EXPECT_EQ(GURL(expected_url),
-            browser()->tab_strip_model()->GetWebContentsAt(0)->GetVisibleURL());
+  EXPECT_EQ(
+      GURL(expected_url),
+      browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL());
 }
 
-TEST_P(ExtensionRequestNotificationTest, HasExtensionAndClosedByBrowser) {
+IN_PROC_BROWSER_TEST_P(ExtensionRequestNotificationTest,
+                       HasExtensionAndClosedByBrowser) {
   ExtensionRequestNotification request_notification(
-      profile(), GetNotifyType(),
+      GetProfile(), GetNotifyType(),
       ExtensionRequestNotification::ExtensionIds({kFakeExtensionId}));
   base::RunLoop show_run_loop;
   display_service_tester_->SetNotificationAddedClosure(
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_ai_util.cc b/chrome/browser/extensions/api/autofill_private/autofill_ai_util.cc
index 595adb7..6253b7c3 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_ai_util.cc
+++ b/chrome/browser/extensions/api/autofill_private/autofill_ai_util.cc
@@ -17,6 +17,7 @@
 #include "base/types/cxx23_to_underlying.h"
 #include "base/values.h"
 #include "chrome/common/extensions/api/autofill_private.h"
+#include "components/autofill/core/browser/autofill_field.h"
 #include "components/autofill/core/browser/data_model/addresses/autofill_structured_address_component.h"
 #include "components/autofill/core/browser/data_model/autofill_ai/entity_instance.h"
 #include "components/autofill/core/browser/data_model/autofill_ai/entity_type.h"
@@ -35,6 +36,7 @@
 using autofill::AttributeInstance;
 using autofill::AttributeType;
 using autofill::AttributeTypeName;
+using autofill::AutofillFormatString;
 using autofill::EntityInstance;
 using autofill::EntityType;
 using autofill::EntityTypeName;
@@ -210,16 +212,21 @@
         return std::nullopt;
       }
 
-      attribute_instance.SetInfo(attribute_instance.type().field_type(),
-                                 base::UTF8ToUTF16(date->month), app_locale,
-                                 u"M",
-                                 autofill::VerificationStatus::kUserVerified);
-      attribute_instance.SetInfo(attribute_instance.type().field_type(),
-                                 base::UTF8ToUTF16(date->day), app_locale, u"D",
-                                 autofill::VerificationStatus::kUserVerified);
+      attribute_instance.SetInfo(
+          attribute_instance.type().field_type(),
+          base::UTF8ToUTF16(date->month), app_locale,
+          AutofillFormatString(u"M", autofill::FormatString_Type_DATE),
+          autofill::VerificationStatus::kUserVerified);
+      attribute_instance.SetInfo(
+          attribute_instance.type().field_type(), base::UTF8ToUTF16(date->day),
+          app_locale,
+          AutofillFormatString(u"D", autofill::FormatString_Type_DATE),
+          autofill::VerificationStatus::kUserVerified);
       attribute_instance.SetInfo(
           attribute_instance.type().field_type(), base::UTF8ToUTF16(date->year),
-          app_locale, u"YYYY", autofill::VerificationStatus::kUserVerified);
+          app_locale,
+          AutofillFormatString(u"YYYY", autofill::FormatString_Type_DATE),
+          autofill::VerificationStatus::kUserVerified);
     } else {
       if (!private_api_attribute_instance.value.as_string.has_value()) {
         return std::nullopt;
@@ -273,14 +280,20 @@
       autofill::FieldType field_type = attribute_instance.type().field_type();
       base::DictValue date_value;
       date_value.SetByDottedPath(
-          "month", base::UTF16ToUTF8(attribute_instance.GetInfo(
-                       field_type, app_locale, std::u16string(u"M"))));
+          "month",
+          base::UTF16ToUTF8(attribute_instance.GetInfo(
+              field_type, app_locale,
+              AutofillFormatString(u"M", autofill::FormatString_Type_DATE))));
       date_value.SetByDottedPath(
-          "day", base::UTF16ToUTF8(attribute_instance.GetInfo(
-                     field_type, app_locale, std::u16string(u"D"))));
+          "day",
+          base::UTF16ToUTF8(attribute_instance.GetInfo(
+              field_type, app_locale,
+              AutofillFormatString(u"D", autofill::FormatString_Type_DATE))));
       date_value.SetByDottedPath(
           "year", base::UTF16ToUTF8(attribute_instance.GetInfo(
-                      field_type, app_locale, std::u16string(u"YYYY"))));
+                      field_type, app_locale,
+                      AutofillFormatString(u"YYYY",
+                                           autofill::FormatString_Type_DATE))));
       autofill_private::AttributeInstance::Value::Populate(
           base::Value(std::move(date_value)),
           private_api_attribute_instances.back().value);
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 75bd7be4..a16b009 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -6490,6 +6490,11 @@
     "expiry_milestone": 140
   },
   {
+    "name": "lens-overlay-custom-bottom-sheet",
+    "owners": ["radunitescu@google.com"],
+    "expiry_milestone": 152
+  },
+  {
     "name": "lens-overlay-disable-iph-pan-gesture",
     "owners": [ "stkhapugin@chromium.org", "christianxu@chromium.org", "lens-chrome@google.com" ],
     "expiry_milestone": 140
diff --git a/chrome/browser/keyboard_accessory/android/internal/java/res/values/dimens.xml b/chrome/browser/keyboard_accessory/android/internal/java/res/values/dimens.xml
index 68402398..64af1ebc 100644
--- a/chrome/browser/keyboard_accessory/android/internal/java/res/values/dimens.xml
+++ b/chrome/browser/keyboard_accessory/android/internal/java/res/values/dimens.xml
@@ -12,7 +12,7 @@
     <dimen name="keyboard_accessory_height">48dp</dimen>
     <dimen name="keyboard_accessory_height_redesign">64dp</dimen>
     <dimen name="keyboard_accessory_chip_min_height_redesign">64dp</dimen>
-    <dimen name="keyboard_accessory_chip_corner_radius_redesign">16dp</dimen>
+    <dimen name="keyboard_accessory_chip_corner_radius_redesign">8dp</dimen>
     <dimen name="keyboard_accessory_chip_home_and_work_start_padding">12dp</dimen>
     <dimen name="keyboard_accessory_top_inset_overlap">4dp</dimen>
     <dimen name="keyboard_accessory_dismiss_button_end_padding">10dp</dimen>
@@ -26,6 +26,9 @@
     <dimen name="keyboard_accessory_sheet_top_margin">16dp</dimen>
     <dimen name="keyboard_accessory_sheet_bottom_margin">8dp</dimen>
     <dimen name="keyboard_accessory_suggestion_padding">16dp</dimen>
+    <dimen name="keyboard_accessory_chip_start_padding_redesign">6dp</dimen>
+    <dimen name="keyboard_accessory_chip_end_padding_redesign">12dp</dimen>
+    <dimen name="keyboard_accessory_text_start_padding_redesign">6dp</dimen>
     <dimen name="keyboard_accessory_suggestion_offset">12dp</dimen>
     <dimen name="keyboard_accessory_suggestion_height">48dp</dimen>
     <dimen name="keyboard_accessory_scroll_shadow_width">80dp</dimen>
diff --git a/chrome/browser/keyboard_accessory/android/internal/java/res/values/styles.xml b/chrome/browser/keyboard_accessory/android/internal/java/res/values/styles.xml
index 6fede38..5f27131 100644
--- a/chrome/browser/keyboard_accessory/android/internal/java/res/values/styles.xml
+++ b/chrome/browser/keyboard_accessory/android/internal/java/res/values/styles.xml
@@ -47,11 +47,13 @@
         <item name="cornerRadius">
             @dimen/keyboard_accessory_chip_corner_radius_redesign
         </item>
-        <item name="solidColorChip">true</item>
+        <item name="chipStartPadding">@dimen/keyboard_accessory_chip_start_padding_redesign</item>
+        <item name="chipEndPadding">@dimen/keyboard_accessory_chip_end_padding_redesign</item>
         <item name="textArrangement">vertical</item>
         <item name="textAlignStart">true</item>
         <item name="primaryTextAppearance">@style/TextAppearance.TextAccentMediumThick.Primary</item>
-        <item name="secondaryTextAppearance">@style/TextAppearance.TextSmall.Secondary</item>
+        <item name="primaryTextStartPadding">@dimen/keyboard_accessory_text_start_padding_redesign</item>
+        <item name="secondaryTextAppearance">@style/TextAppearance.TextSmall.Primary</item>
         <item name="iconWidth">
             @dimen/keyboard_accessory_bar_item_icon_width
         </item>
@@ -62,22 +64,18 @@
     <style name="KeyboardAccessoryLoyaltyCardTwoLineChip" parent="KeyboardAccessoryTwoLineChip">
         <item name="iconWidth">@dimen/keyboard_accessory_suggestion_icon_size</item>
         <item name="iconHeight">@dimen/keyboard_accessory_suggestion_icon_size</item>
-        <item name="useRoundedIcon">true</item>
     </style>
     <style name="KeyboardAccessoryLoyaltyCardLargeTwoLineChip" parent="KeyboardAccessoryLargeTwoLineChip">
         <item name="iconWidth">@dimen/keyboard_accessory_suggestion_icon_size</item>
         <item name="iconHeight">@dimen/keyboard_accessory_suggestion_icon_size</item>
-        <item name="useRoundedIcon">true</item>
     </style>
-    <!-- TODO: crbug.com/444403583 - Figure out correcr padding for Home&Word suggestions. -->
+    <!-- TODO: crbug.com/444403583 - Figure out correct padding for Home&Word suggestions. -->
     <style name="KeyboardAccessoryHomeAndWorkTwoLineChip" parent="KeyboardAccessoryTwoLineChip">
         <item name="iconWidth">@dimen/keyboard_accessory_suggestion_icon_size</item>
         <item name="iconHeight">@dimen/keyboard_accessory_suggestion_icon_size</item>
-        <item name="chipStartPadding">@dimen/keyboard_accessory_chip_home_and_work_start_padding</item>
     </style>
     <style name="KeyboardAccessoryHomeAndWorkLargeTwoLineChip" parent="KeyboardAccessoryLargeTwoLineChip">
         <item name="iconWidth">@dimen/keyboard_accessory_suggestion_icon_size</item>
         <item name="iconHeight">@dimen/keyboard_accessory_suggestion_icon_size</item>
-        <item name="chipStartPadding">@dimen/keyboard_accessory_chip_home_and_work_start_padding</item>
     </style>
 </resources>
diff --git a/chrome/browser/password_manager/actor_login/internal/actor_login_delegate_impl.cc b/chrome/browser/password_manager/actor_login/internal/actor_login_delegate_impl.cc
index 38e616b5..2a8c24d 100644
--- a/chrome/browser/password_manager/actor_login/internal/actor_login_delegate_impl.cc
+++ b/chrome/browser/password_manager/actor_login/internal/actor_login_delegate_impl.cc
@@ -164,5 +164,4 @@
   std::move(pending_attempt_login_callback_).Run(std::move(result));
 }
 
-
 }  // namespace actor_login
diff --git a/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.cc b/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.cc
index c6a2791..4ec5899 100644
--- a/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.cc
+++ b/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.cc
@@ -163,6 +163,13 @@
       account, PasswordManagerSetting::kAutoSignIn, false);
 }
 
+void PasswordManagerSettingsServiceAndroidImpl::Shutdown() {
+  if (sync_service_) {
+    sync_service_->RemoveObserver(this);
+    sync_service_ = nullptr;
+  }
+}
+
 void PasswordManagerSettingsServiceAndroidImpl::Init() {
   bridge_helper_->SetConsumer(weak_ptr_factory_.GetWeakPtr());
   lifecycle_helper_->RegisterObserver(base::BindRepeating(
@@ -245,3 +252,9 @@
   // Chrome's cache.
   RequestSettingsFromBackend();
 }
+
+void PasswordManagerSettingsServiceAndroidImpl::OnSyncShutdown(
+    syncer::SyncService* sync) {
+  // Unreachable, since this service is Shutdown() before the SyncService.
+  NOTREACHED();
+}
diff --git a/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.h b/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.h
index 12537d4..e86fc2ff1 100644
--- a/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.h
+++ b/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.h
@@ -57,6 +57,9 @@
   void RequestSettingsFromBackend() override;
   void TurnOffAutoSignIn() override;
 
+  // KeyedService implementation
+  void Shutdown() override;
+
  private:
   // Does actions that need to be done on startup (e.g. attaches services
   // observers and migrates and requests settings if needed).
@@ -84,6 +87,7 @@
 
   // syncer::SyncServiceObserver implementation
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
   // Pref service used to read and write password manager user prefs.
   raw_ptr<PrefService> pref_service_ = nullptr;
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_browsertest.cc b/chrome/browser/policy/cloud/user_policy_signin_service_browsertest.cc
index 85b3ae0b..0a089c1 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service_browsertest.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service_browsertest.cc
@@ -39,6 +39,7 @@
 #include "components/signin/public/identity_manager/identity_test_utils.h"
 #include "components/signin/public/identity_manager/primary_account_mutator.h"
 #include "components/sync/base/command_line_switches.h"
+#include "components/sync/base/features.h"
 #include "content/public/test/browser_test.h"
 #include "google_apis/gaia/fake_gaia.h"
 #include "google_apis/gaia/gaia_switches.h"
@@ -422,41 +423,6 @@
   EXPECT_TRUE(enterprise_util::ProfileCanBeManaged(profile()));
 }
 
-// Regression test for https://crbug.com/1061459
-// Start a new signing flow while the existing one is hanging on a policy
-// request.
-IN_PROC_BROWSER_TEST_F(UserPolicySigninServiceTest, ConcurrentSignin) {
-  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kShowHomeButton));
-
-  set_policy_hanging(true);
-  CreateTurnSyncOnHelper();
-  WaitForPolicyHanging();
-
-  // Policy hanging, policy is not applied.
-  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kShowHomeButton));
-  EXPECT_TRUE(signin_client()->IsClearPrimaryAccountAllowed());
-
-  // Restart a new signin flow and allow it to complete.
-  CreateTurnSyncOnHelper();
-  set_policy_hanging(false);
-  WaitForSyncConfirmation();
-
-  // Policies are applied right before the sync confirmation is shown.
-  EXPECT_EQ(signin::ConsentLevel::kSignin,
-            signin::GetPrimaryAccountConsentLevel(identity_manager()));
-  EXPECT_TRUE(profile()->GetPrefs()->GetBoolean(prefs::kShowHomeButton));
-  EXPECT_FALSE(signin_client()->IsClearPrimaryAccountAllowed());
-
-  // Confirm the signin.
-  ConfirmSync(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS);
-  // Policy is still applied.
-  EXPECT_TRUE(profile()->GetPrefs()->GetBoolean(prefs::kShowHomeButton));
-  EXPECT_EQ(signin::ConsentLevel::kSync,
-            signin::GetPrimaryAccountConsentLevel(identity_manager()));
-  EXPECT_FALSE(signin_client()->IsClearPrimaryAccountAllowed());
-  EXPECT_TRUE(signin_client()->IsRevokeSyncConsentAllowed());
-}
-
 // Disabled for Win11 arm64 flakes: https://crbug.com/340623286
 IN_PROC_BROWSER_TEST_F(UserPolicySigninServiceTest,
                        DISABLED_AcceptManagementDeclineSync) {
@@ -493,3 +459,52 @@
   EXPECT_TRUE(enterprise_util::ProfileCanBeManaged(profile()));
   TurnSyncOnHelper::SetShowSyncEnabledUiForTesting(false);
 }
+
+class UserPolicySigninServiceTestWithReplaceSyncPromosWithSignInPromosDisabled
+    : public UserPolicySigninServiceTest {
+ public:
+  UserPolicySigninServiceTestWithReplaceSyncPromosWithSignInPromosDisabled() {
+    scoped_feature_list_.InitAndDisableFeature(
+        syncer::kReplaceSyncPromosWithSignInPromos);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Regression test for https://crbug.com/1061459
+// Start a new signing flow while the existing one is hanging on a policy
+// request.
+IN_PROC_BROWSER_TEST_F(
+    UserPolicySigninServiceTestWithReplaceSyncPromosWithSignInPromosDisabled,
+    ConcurrentSignin) {
+  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kShowHomeButton));
+
+  set_policy_hanging(true);
+  CreateTurnSyncOnHelper();
+  WaitForPolicyHanging();
+
+  // Policy hanging, policy is not applied.
+  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kShowHomeButton));
+  EXPECT_TRUE(signin_client()->IsClearPrimaryAccountAllowed());
+
+  // Restart a new signin flow and allow it to complete.
+  CreateTurnSyncOnHelper();
+  set_policy_hanging(false);
+  WaitForSyncConfirmation();
+
+  // Policies are applied right before the sync confirmation is shown.
+  EXPECT_EQ(signin::ConsentLevel::kSignin,
+            signin::GetPrimaryAccountConsentLevel(identity_manager()));
+  EXPECT_TRUE(profile()->GetPrefs()->GetBoolean(prefs::kShowHomeButton));
+  EXPECT_FALSE(signin_client()->IsClearPrimaryAccountAllowed());
+
+  // Confirm the signin.
+  ConfirmSync(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS);
+  // Policy is still applied.
+  EXPECT_TRUE(profile()->GetPrefs()->GetBoolean(prefs::kShowHomeButton));
+  EXPECT_EQ(signin::ConsentLevel::kSync,
+            signin::GetPrimaryAccountConsentLevel(identity_manager()));
+  EXPECT_FALSE(signin_client()->IsClearPrimaryAccountAllowed());
+  EXPECT_TRUE(signin_client()->IsRevokeSyncConsentAllowed());
+}
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_mobile.cc b/chrome/browser/policy/cloud/user_policy_signin_service_mobile.cc
index c1070d87..28275b8 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service_mobile.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service_mobile.cc
@@ -16,6 +16,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/enterprise/client_certificates/certificate_provisioning_service_factory.h"
 #include "chrome/browser/enterprise/remote_commands/user_remote_commands_service.h"
 #include "chrome/browser/enterprise/remote_commands/user_remote_commands_service_factory.h"
 #include "chrome/browser/enterprise/util/managed_browser_utils.h"
@@ -27,6 +28,7 @@
 #include "chrome/browser/signin/account_id_from_account_info.h"
 #include "chrome/common/chrome_content_client.h"
 #include "chrome/common/pref_names.h"
+#include "components/enterprise/client_certificates/core/certificate_provisioning_service.h"
 #include "components/policy/core/browser/cloud/user_policy_signin_service_util.h"
 #include "components/policy/core/common/cloud/cloud_policy_client_registration_helper.h"
 #include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
@@ -97,6 +99,21 @@
     // `ProfileManager` may be null in tests.
     UpdateProfileAttributesWhenSignout(profile_, profile_manager);
   }
+
+  client_certificates::CertificateProvisioningService* provisioning_service =
+      client_certificates::CertificateProvisioningServiceFactory::GetForProfile(
+          profile_);
+
+  if (provisioning_service) {
+    // Delete the managed identities (permanent and temporary).
+    provisioning_service->DeleteManagedIdentities(
+        base::BindOnce([](bool success) {
+          if (!success) {
+            LOG(ERROR) << "Failed to delete managed identities on sign-out.";
+          }
+        }));
+  }
+
   ShutdownCloudPolicyManager();
 }
 
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 9ff3f8d..cbeea4c2 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -1850,6 +1850,9 @@
   { key::kGenAIInlineImageSettings,
     ash::prefs::kLobsterEnterprisePolicySettings,
     base::Value::Type::INTEGER},
+  { key::kSilentPrintingEnabled,
+    ash::prefs::kSilentPrintingEnabled,
+    base::Value::Type::BOOLEAN },
 #endif // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_LINUX)
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/other_google_data_dialog.ts b/chrome/browser/resources/settings/clear_browsing_data_dialog/other_google_data_dialog.ts
index 3813eb98..cacb868 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/other_google_data_dialog.ts
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/other_google_data_dialog.ts
@@ -142,6 +142,9 @@
   private onGeminiAppsActivityClick_() {
     OpenWindowProxyImpl.getInstance().openUrl(
         loadTimeData.getString('myActivityGeminiAppsUrl'));
+
+    this.metricsBrowserProxy_.recordAction(
+        'Settings.DeleteBrowsingData.GeminiAppsActivityLinkClick');
   }
 
   private onGeminiPersonalContextClick_() {
diff --git a/chrome/browser/search_engine_choice/search_engine_choice_service_factory.cc b/chrome/browser/search_engine_choice/search_engine_choice_service_factory.cc
index d1cc344b..56c9a6b 100644
--- a/chrome/browser/search_engine_choice/search_engine_choice_service_factory.cc
+++ b/chrome/browser/search_engine_choice/search_engine_choice_service_factory.cc
@@ -33,7 +33,7 @@
       CHECK_DEREF(
           TemplateURLPrepopulateData::ResolverFactory::GetForProfile(&profile)),
       CHECK_DEREF(IdentityManagerFactory::GetForProfile(&profile)),
-      CHECK_DEREF(policy::ManagementServiceFactory::GetForProfile(&profile)));
+      CHECK_DEREF(policy::ManagementServiceFactory::GetForPlatform()));
   service->Init();
   return service;
 }
diff --git a/chrome/browser/signin/dice_browsertest.cc b/chrome/browser/signin/dice_browsertest.cc
index ee3734f..02b2aebb 100644
--- a/chrome/browser/signin/dice_browsertest.cc
+++ b/chrome/browser/signin/dice_browsertest.cc
@@ -1018,203 +1018,6 @@
   WaitForReconcilorUnblockedCount(0);
 }
 
-// Tests that Sync is enabled if the ENABLE_SYNC response is received after the
-// refresh token.
-IN_PROC_BROWSER_TEST_F(DiceBrowserTest, EnableSyncAfterToken) {
-  base::HistogramTester histogram_tester;
-  EXPECT_EQ(0, reconcilor_started_count_);
-
-  // Signin using the Chrome Sync endpoint.
-  signin_metrics::AccessPoint access_point =
-      signin_metrics::AccessPoint::kSettings;
-  browser()->GetFeatures().signin_view_controller()->ShowDiceEnableSyncTab(
-      access_point,
-      signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT,
-      /*email_hint=*/std::string());
-
-  // Receive token.
-  EXPECT_FALSE(
-      GetIdentityManager()->HasAccountWithRefreshToken(GetMainAccountID()));
-  SendRefreshTokenResponse();
-  EXPECT_TRUE(
-      GetIdentityManager()->HasAccountWithRefreshToken(GetMainAccountID()));
-
-  // Receive ENABLE_SYNC.
-  SendEnableSyncResponse();
-
-  // Check that the Dice request header was sent, with signout confirmation.
-  std::string client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
-  EXPECT_EQ(base::StringPrintf("version=%s,client_id=%s,device_id=%s,"
-                               "signin_mode=all_accounts,"
-                               "signout_mode=show_confirmation",
-                               signin::kDiceProtocolVersion, client_id.c_str(),
-                               GetDeviceId().c_str()),
-            dice_request_header_);
-
-  content::WebContents* tab_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  base::RunLoop ntp_run_loop;
-  content::DidFinishNavigationObserver ntp_url_observer(
-      tab_contents,
-      base::BindLambdaForTesting(
-          [&ntp_run_loop](content::NavigationHandle* navigation_handle) {
-            const GURL& url = navigation_handle->GetURL();
-            // Some test flags (e.g. ForceWebRequestProxyForTest) can change
-            // whether the reported NTP URL is chrome://newtab or
-            // chrome://new-tab-page.
-            if (url == GURL(chrome::kChromeUINewTabPageURL) ||
-                url == GURL(chrome::kChromeUINewTabURL)) {
-              ntp_run_loop.Quit();
-            }
-          }));
-
-  WaitForSigninSucceeded();
-  EXPECT_EQ(GetMainAccountID(), GetIdentityManager()->GetPrimaryAccountId(
-                                    signin::ConsentLevel::kSignin));
-  histogram_tester.ExpectUniqueSample("Signin.SignIn.Completed", access_point,
-                                      1);
-
-  EXPECT_EQ(1, reconcilor_blocked_count_);
-  WaitForReconcilorUnblockedCount(1);
-  EXPECT_EQ(1, reconcilor_started_count_);
-
-  // Check that the tab was navigated to the NTP.
-  ntp_run_loop.Run();
-
-  // Dismiss the Sync confirmation UI.
-  EXPECT_TRUE(login_ui_test_utils::ConfirmSyncConfirmationDialog(browser()));
-
-  // Expect that metrics related to the browser signin stage are recorded.
-  histogram_tester.ExpectBucketCount(
-      "Signin.SigninManager.SetPrimaryAccountSigninInStage",
-      PrimaryAccountSettingGaiaIntegrationState::kOnTokenExchangeSuccess,
-      /*expected_count=*/1);
-  histogram_tester.ExpectBucketCount(
-      "Signin.SigninManager.SetPrimaryAccountSigninInStage",
-      PrimaryAccountSettingGaiaIntegrationState::kOnSyncHeaderReceived,
-      /*expected_count=*/
-      base::FeatureList::IsEnabled(
-          switches::kBrowserSigninInSyncHeaderOnGaiaIntegration)
-          ? 1
-          : 0);
-  // The interception bubble should not have been shown.
-  histogram_tester.ExpectBucketCount(
-      "Signin.Intercept.HeuristicOutcome",
-      SigninInterceptionHeuristicOutcome::kInterceptChromeSignin, 0);
-  // A Sync header on time event has been recorded.
-  histogram_tester.ExpectUniqueSample("Signin.SigninManager.SyncHeaderTimeout",
-                                      false, 1);
-
-  // Both LST and Sync Header are received so their time difference must be
-  // recorded.
-  histogram_tester.ExpectTotalCount(
-      "Signin.SigninManager.SyncHeaderArrivalTimeWindowAfterLst", 1);
-}
-
-// Tests that the account is signed in if the ENABLE_SYNC response is received
-// before the refresh token, and the Sync opt-in is offered.
-// https://crbug.com/1082858
-IN_PROC_BROWSER_TEST_F(DiceBrowserTest, EnableSyncBeforeToken) {
-  base::HistogramTester histogram_tester;
-  EXPECT_EQ(0, reconcilor_started_count_);
-
-  ui_test_utils::UrlLoadObserver enable_sync_url_observer(
-      https_server_.GetURL(kEnableSyncURL));
-
-  // Signin using the Chrome Sync endpoint.
-  browser()->GetFeatures().signin_view_controller()->ShowSignin(
-      signin_metrics::AccessPoint::kSettings);
-
-  // Receive ENABLE_SYNC.
-  SendEnableSyncResponse();
-  // Wait for the page to be fully loaded.
-  enable_sync_url_observer.Wait();
-
-  // Receive token.
-  EXPECT_FALSE(
-      GetIdentityManager()->HasPrimaryAccount(signin::ConsentLevel::kSignin));
-  EXPECT_FALSE(
-      GetIdentityManager()->HasAccountWithRefreshToken(GetMainAccountID()));
-  SendRefreshTokenResponse();
-
-  ui_test_utils::UrlLoadObserver ntp_url_observer(
-      (GURL(chrome::kChromeUINewTabURL)));
-
-  EXPECT_EQ(1, reconcilor_blocked_count_);
-  WaitForReconcilorUnblockedCount(1);
-  EXPECT_EQ(1, reconcilor_started_count_);
-
-  // Check that the tab was navigated to the NTP.
-  ntp_url_observer.Wait();
-
-  EXPECT_TRUE(
-      GetIdentityManager()->HasAccountWithRefreshToken(GetMainAccountID()));
-  EXPECT_EQ(GetMainAccountID(), GetIdentityManager()->GetPrimaryAccountId(
-                                    signin::ConsentLevel::kSignin));
-
-  AddCanShowHistorySyncOptInsWithoutMinorModeCapability(GetIdentityManager());
-
-  // Check that the Dice request header was sent, with signout confirmation.
-  std::string client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
-  EXPECT_EQ(base::StringPrintf("version=%s,client_id=%s,device_id=%s,"
-                               "signin_mode=all_accounts,"
-                               "signout_mode=show_confirmation",
-                               signin::kDiceProtocolVersion, client_id.c_str(),
-                               GetDeviceId().c_str()),
-            dice_request_header_);
-
-  // Wait for the Sync confirmation UI and click through.
-  EXPECT_TRUE(login_ui_test_utils::ConfirmSyncConfirmationDialog(browser()));
-
-  EXPECT_EQ(signin::ConsentLevel::kSync,
-            signin::GetPrimaryAccountConsentLevel(GetIdentityManager()));
-
-  // The interception bubble should not have been shown.
-  histogram_tester.ExpectBucketCount(
-      "Signin.Intercept.HeuristicOutcome",
-      SigninInterceptionHeuristicOutcome::kInterceptChromeSignin, 0);
-  // A Sync header on time event has been recorded.
-  histogram_tester.ExpectUniqueSample("Signin.SigninManager.SyncHeaderTimeout",
-                                      false, 1);
-  // Both LST and Sync Header are received so their time difference must be
-  // recorded.
-  histogram_tester.ExpectTotalCount(
-      "Signin.SigninManager.SyncHeaderArrivalTimeWindowAfterLst", 1);
-}
-
-// Verifies that Chrome doesn't crash on browser window close when the sync
-// confirmation dialog is waiting for its size.
-// Regression test for https://crbug.com/1304055.
-IN_PROC_BROWSER_TEST_F(DiceBrowserTest,
-                       CloseBrowserWhileInitializingSyncConfirmation) {
-  content::TestNavigationObserver sync_confirmation_url_observer(
-      GURL("chrome://sync-confirmation?style=0&is_sync_promo=true"));
-  sync_confirmation_url_observer.StartWatchingNewWebContents();
-
-  // Signin using the Chrome Sync endpoint.
-  browser()->GetFeatures().signin_view_controller()->ShowDiceEnableSyncTab(
-      signin_metrics::AccessPoint::kAvatarBubbleSignInWithSyncPromo,
-      signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT,
-      /*email_hint=*/std::string());
-
-  // Receive token.
-  SendRefreshTokenResponse();
-  // Receive ENABLE_SYNC.
-  SendEnableSyncResponse();
-
-  WaitForSigninSucceeded();
-  EXPECT_EQ(GetMainAccountID(), GetIdentityManager()->GetPrimaryAccountId(
-                                    signin::ConsentLevel::kSignin));
-
-  // Wait until the sync confirmation webUI is created but not fully loaded
-  // yet. The native dialog is not displayed yet since it waits until the webUI
-  // passes the dialog height back to native.
-  sync_confirmation_url_observer.WaitForNavigationFinished();
-
-  // This should not crash.
-  CloseBrowser();
-}
-
 // Tests that turning off Dice via preferences works when singed out.
 IN_PROC_BROWSER_TEST_F(DiceBrowserTest, PRE_TurnOffDice_SignedOut) {
   ASSERT_FALSE(
@@ -1342,6 +1145,229 @@
       incognito_browser->profile()));
 }
 
+// Tests that Sync is enabled if the ENABLE_SYNC response is received after the
+// refresh token.
+IN_PROC_BROWSER_TEST_F(DiceBrowserTest, EnableSyncAfterToken) {
+  base::HistogramTester histogram_tester;
+  EXPECT_EQ(0, reconcilor_started_count_);
+
+  // Signin using the Chrome Sync endpoint.
+  signin_metrics::AccessPoint access_point =
+      signin_metrics::AccessPoint::kSettings;
+  browser()->GetFeatures().signin_view_controller()->ShowDiceEnableSyncTab(
+      access_point,
+      signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT,
+      /*email_hint=*/std::string());
+
+  // Receive token.
+  EXPECT_FALSE(
+      GetIdentityManager()->HasAccountWithRefreshToken(GetMainAccountID()));
+  SendRefreshTokenResponse();
+  EXPECT_TRUE(
+      GetIdentityManager()->HasAccountWithRefreshToken(GetMainAccountID()));
+
+  // Receive ENABLE_SYNC.
+  SendEnableSyncResponse();
+
+  // Check that the Dice request header was sent, with signout confirmation.
+  std::string client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
+  EXPECT_EQ(base::StringPrintf("version=%s,client_id=%s,device_id=%s,"
+                               "signin_mode=all_accounts,"
+                               "signout_mode=show_confirmation",
+                               signin::kDiceProtocolVersion, client_id.c_str(),
+                               GetDeviceId().c_str()),
+            dice_request_header_);
+
+  content::WebContents* tab_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  base::RunLoop ntp_run_loop;
+  content::DidFinishNavigationObserver ntp_url_observer(
+      tab_contents,
+      base::BindLambdaForTesting(
+          [&ntp_run_loop](content::NavigationHandle* navigation_handle) {
+            const GURL& url = navigation_handle->GetURL();
+            // Some test flags (e.g. ForceWebRequestProxyForTest) can change
+            // whether the reported NTP URL is chrome://newtab or
+            // chrome://new-tab-page.
+            if (url == GURL(chrome::kChromeUINewTabPageURL) ||
+                url == GURL(chrome::kChromeUINewTabURL)) {
+              ntp_run_loop.Quit();
+            }
+          }));
+
+  WaitForSigninSucceeded();
+  EXPECT_EQ(GetMainAccountID(), GetIdentityManager()->GetPrimaryAccountId(
+                                    signin::ConsentLevel::kSignin));
+  histogram_tester.ExpectUniqueSample("Signin.SignIn.Completed", access_point,
+                                      1);
+
+  EXPECT_EQ(1, reconcilor_blocked_count_);
+  WaitForReconcilorUnblockedCount(1);
+  EXPECT_EQ(1, reconcilor_started_count_);
+
+  // Check that the tab was navigated to the NTP.
+  ntp_run_loop.Run();
+
+  // Wait for the Sync confirmation UI and click through. This is only needed
+  // when `syncer::kReplaceSyncPromosWithSignInPromos` is disabled, because
+  // otherwise it is a sign-in flow without involving the Sync confirmation
+  // dialog.
+  if (!base::FeatureList::IsEnabled(
+          syncer::kReplaceSyncPromosWithSignInPromos)) {
+    EXPECT_TRUE(login_ui_test_utils::ConfirmSyncConfirmationDialog(browser()));
+  }
+
+  // Expect that metrics related to the browser signin stage are recorded.
+  histogram_tester.ExpectBucketCount(
+      "Signin.SigninManager.SetPrimaryAccountSigninInStage",
+      PrimaryAccountSettingGaiaIntegrationState::kOnTokenExchangeSuccess,
+      /*expected_count=*/1);
+  histogram_tester.ExpectBucketCount(
+      "Signin.SigninManager.SetPrimaryAccountSigninInStage",
+      PrimaryAccountSettingGaiaIntegrationState::kOnSyncHeaderReceived,
+      /*expected_count=*/
+      base::FeatureList::IsEnabled(
+          switches::kBrowserSigninInSyncHeaderOnGaiaIntegration)
+          ? 1
+          : 0);
+  // The interception bubble should not have been shown.
+  histogram_tester.ExpectBucketCount(
+      "Signin.Intercept.HeuristicOutcome",
+      SigninInterceptionHeuristicOutcome::kInterceptChromeSignin, 0);
+  // A Sync header on time event has been recorded.
+  histogram_tester.ExpectUniqueSample("Signin.SigninManager.SyncHeaderTimeout",
+                                      false, 1);
+
+  // Both LST and Sync Header are received so their time difference must be
+  // recorded.
+  histogram_tester.ExpectTotalCount(
+      "Signin.SigninManager.SyncHeaderArrivalTimeWindowAfterLst", 1);
+}
+
+// Tests that the account is signed in if the ENABLE_SYNC response is received
+// before the refresh token, and the Sync opt-in is offered.
+// https://crbug.com/1082858
+IN_PROC_BROWSER_TEST_F(DiceBrowserTest, EnableSyncBeforeToken) {
+  base::HistogramTester histogram_tester;
+  EXPECT_EQ(0, reconcilor_started_count_);
+
+  ui_test_utils::UrlLoadObserver enable_sync_url_observer(
+      https_server_.GetURL(kEnableSyncURL));
+
+  // Signin using the Chrome Sync endpoint.
+  browser()->GetFeatures().signin_view_controller()->ShowSignin(
+      signin_metrics::AccessPoint::kSettings);
+
+  // Receive ENABLE_SYNC.
+  SendEnableSyncResponse();
+  // Wait for the page to be fully loaded.
+  enable_sync_url_observer.Wait();
+
+  // Receive token.
+  EXPECT_FALSE(
+      GetIdentityManager()->HasPrimaryAccount(signin::ConsentLevel::kSignin));
+  EXPECT_FALSE(
+      GetIdentityManager()->HasAccountWithRefreshToken(GetMainAccountID()));
+  SendRefreshTokenResponse();
+
+  ui_test_utils::UrlLoadObserver ntp_url_observer(
+      (GURL(chrome::kChromeUINewTabURL)));
+
+  EXPECT_EQ(1, reconcilor_blocked_count_);
+  WaitForReconcilorUnblockedCount(1);
+  EXPECT_EQ(1, reconcilor_started_count_);
+
+  // Check that the tab was navigated to the NTP.
+  ntp_url_observer.Wait();
+
+  EXPECT_TRUE(
+      GetIdentityManager()->HasAccountWithRefreshToken(GetMainAccountID()));
+  EXPECT_EQ(GetMainAccountID(), GetIdentityManager()->GetPrimaryAccountId(
+                                    signin::ConsentLevel::kSignin));
+
+  AddCanShowHistorySyncOptInsWithoutMinorModeCapability(GetIdentityManager());
+
+  // Check that the Dice request header was sent, with signout confirmation.
+  std::string client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
+  EXPECT_EQ(base::StringPrintf("version=%s,client_id=%s,device_id=%s,"
+                               "signin_mode=all_accounts,"
+                               "signout_mode=show_confirmation",
+                               signin::kDiceProtocolVersion, client_id.c_str(),
+                               GetDeviceId().c_str()),
+            dice_request_header_);
+
+  // Wait for the Sync confirmation UI and click through. This is only needed
+  // when `syncer::kReplaceSyncPromosWithSignInPromos` is disabled, because
+  // otherwise it is a sign-in flow without involving the Sync confirmation
+  // dialog.
+  if (base::FeatureList::IsEnabled(
+          syncer::kReplaceSyncPromosWithSignInPromos)) {
+    EXPECT_EQ(signin::ConsentLevel::kSignin,
+              signin::GetPrimaryAccountConsentLevel(GetIdentityManager()));
+  } else {
+    EXPECT_TRUE(login_ui_test_utils::ConfirmSyncConfirmationDialog(browser()));
+    EXPECT_EQ(signin::ConsentLevel::kSync,
+              signin::GetPrimaryAccountConsentLevel(GetIdentityManager()));
+  }
+
+  // The interception bubble should not have been shown.
+  histogram_tester.ExpectBucketCount(
+      "Signin.Intercept.HeuristicOutcome",
+      SigninInterceptionHeuristicOutcome::kInterceptChromeSignin, 0);
+  // A Sync header on time event has been recorded.
+  histogram_tester.ExpectUniqueSample("Signin.SigninManager.SyncHeaderTimeout",
+                                      false, 1);
+  // Both LST and Sync Header are received so their time difference must be
+  // recorded.
+  histogram_tester.ExpectTotalCount(
+      "Signin.SigninManager.SyncHeaderArrivalTimeWindowAfterLst", 1);
+}
+
+class DiceBrowserTestWithoutReplaceSyncPromosWithSignInPromos
+    : public DiceBrowserTest {
+ public:
+  DiceBrowserTestWithoutReplaceSyncPromosWithSignInPromos() {
+    feature_list_.InitAndDisableFeature(
+        syncer::kReplaceSyncPromosWithSignInPromos);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Verifies that Chrome doesn't crash on browser window close when the sync
+// confirmation dialog is waiting for its size.
+// Regression test for https://crbug.com/1304055.
+IN_PROC_BROWSER_TEST_F(DiceBrowserTestWithoutReplaceSyncPromosWithSignInPromos,
+                       CloseBrowserWhileInitializingSyncConfirmation) {
+  content::TestNavigationObserver sync_confirmation_url_observer(
+      GURL("chrome://sync-confirmation?style=0&is_sync_promo=true"));
+  sync_confirmation_url_observer.StartWatchingNewWebContents();
+
+  // Signin using the Chrome Sync endpoint.
+  browser()->GetFeatures().signin_view_controller()->ShowDiceEnableSyncTab(
+      signin_metrics::AccessPoint::kAvatarBubbleSignInWithSyncPromo,
+      signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT,
+      /*email_hint=*/std::string());
+
+  // Receive token.
+  SendRefreshTokenResponse();
+  // Receive ENABLE_SYNC.
+  SendEnableSyncResponse();
+
+  WaitForSigninSucceeded();
+  EXPECT_EQ(GetMainAccountID(), GetIdentityManager()->GetPrimaryAccountId(
+                                    signin::ConsentLevel::kSignin));
+
+  // Wait until the sync confirmation webUI is created but not fully loaded
+  // yet. The native dialog is not displayed yet since it waits until the webUI
+  // passes the dialog height back to native.
+  sync_confirmation_url_observer.WaitForNavigationFinished();
+
+  // This should not crash.
+  CloseBrowser();
+}
+
 class DiceBrowserSiginInInterceptionInteractiveTest
     : public InteractiveBrowserTestT<DiceBrowserTest> {
  public:
@@ -1580,65 +1606,6 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(DiceExplicitSigninBrowserTest, PRE_Migration) {
-  signin::AccountAvailabilityOptionsBuilder builder;
-  signin::MakeAccountAvailable(
-      GetIdentityManager(),
-      builder
-          .AsPrimary(signin::ConsentLevel::kSignin)
-          // `kWebSignin` is not explicit before the migration.
-          .WithAccessPoint(signin_metrics::AccessPoint::kWebSignin)
-          .Build(kMainGmailEmail));
-  ASSERT_EQ(signin::GetPrimaryAccountConsentLevel(GetIdentityManager()),
-            signin::ConsentLevel::kSignin);
-
-  ASSERT_FALSE(browser()->profile()->GetPrefs()->GetBoolean(
-      prefs::kExplicitBrowserSignin));
-
-  AccountStorageStatus account_storage_status = GetAccountStorageStatus();
-  EXPECT_FALSE(account_storage_status.autofill_sync_toggle_available);
-  EXPECT_FALSE(account_storage_status.user_selectable_type_set.HasAny(
-      {syncer::UserSelectableType::kAutofill,
-       syncer::UserSelectableType::kPasswords}));
-}
-
-// Checks that a user who signed in with Dice before UNO was enabled does not
-// get the account storage enabled silently. Account storage is enabled after
-// the user signs out and signs in again through an explicit flow.
-IN_PROC_BROWSER_TEST_F(DiceExplicitSigninBrowserTest, Migration) {
-  Profile* profile = browser()->profile();
-  // The user is still signed in implicitly.
-  ASSERT_EQ(signin::GetPrimaryAccountConsentLevel(GetIdentityManager()),
-            signin::ConsentLevel::kSignin);
-  ASSERT_TRUE(gaia::AreEmailsSame(
-      GetIdentityManager()
-          ->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)
-          .email,
-      kMainGmailEmail));
-  ASSERT_FALSE(profile->GetPrefs()->GetBoolean(prefs::kExplicitBrowserSignin));
-  // Account storage was not enabled yet.
-  AccountStorageStatus account_storage_status = GetAccountStorageStatus();
-  EXPECT_FALSE(account_storage_status.user_selectable_type_set.HasAny(
-      {syncer::UserSelectableType::kAutofill,
-       syncer::UserSelectableType::kPasswords}));
-
-  // Signout, and then signin again explicitly.
-  signin::ClearPrimaryAccount(GetIdentityManager());
-  AccountInfo primary_account_info = signin::MakePrimaryAccountAvailable(
-      GetIdentityManager(), kMainGmailEmail, signin::ConsentLevel::kSignin);
-  EXPECT_TRUE(profile->GetPrefs()->GetBoolean(prefs::kExplicitBrowserSignin));
-
-  // Account storage is now enabled.
-  account_storage_status = GetAccountStorageStatus();
-  EXPECT_TRUE(account_storage_status.user_selectable_type_set.HasAll(
-      {syncer::UserSelectableType::kAutofill,
-       syncer::UserSelectableType::kPasswords}));
-
-  // Cookie migration is done.
-  EXPECT_TRUE(profile->GetPrefs()->GetBoolean(
-      prefs::kCookieClearOnExitMigrationNoticeComplete));
-}
-
 // Checks that migration handles Cookie clear on exit and sync toggles.
 IN_PROC_BROWSER_TEST_F(DiceExplicitSigninBrowserTest,
                        PRE_MigrationWithSettings) {
@@ -1697,9 +1664,84 @@
       prefs::kCookieClearOnExitMigrationNoticeComplete));
 }
 
+class DiceExplicitSigninBrowserTestWithForcedDiceMigrationDisabled
+    : public DiceExplicitSigninBrowserTest {
+ public:
+  DiceExplicitSigninBrowserTestWithForcedDiceMigrationDisabled() {
+    scoped_feature_list_.InitAndDisableFeature(switches::kForcedDiceMigration);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(
+    DiceExplicitSigninBrowserTestWithForcedDiceMigrationDisabled,
+    PRE_Migration) {
+  signin::AccountAvailabilityOptionsBuilder builder;
+  signin::MakeAccountAvailable(
+      GetIdentityManager(),
+      builder
+          .AsPrimary(signin::ConsentLevel::kSignin)
+          // `kWebSignin` is not explicit before the migration.
+          .WithAccessPoint(signin_metrics::AccessPoint::kWebSignin)
+          .Build(kMainGmailEmail));
+  ASSERT_EQ(signin::GetPrimaryAccountConsentLevel(GetIdentityManager()),
+            signin::ConsentLevel::kSignin);
+
+  ASSERT_FALSE(browser()->profile()->GetPrefs()->GetBoolean(
+      prefs::kExplicitBrowserSignin));
+
+  AccountStorageStatus account_storage_status = GetAccountStorageStatus();
+  EXPECT_FALSE(account_storage_status.autofill_sync_toggle_available);
+  EXPECT_FALSE(account_storage_status.user_selectable_type_set.HasAny(
+      {syncer::UserSelectableType::kAutofill,
+       syncer::UserSelectableType::kPasswords}));
+}
+
+// Checks that a user who signed in with Dice before UNO was enabled does not
+// get the account storage enabled silently. Account storage is enabled after
+// the user signs out and signs in again through an explicit flow.
+IN_PROC_BROWSER_TEST_F(
+    DiceExplicitSigninBrowserTestWithForcedDiceMigrationDisabled,
+    Migration) {
+  Profile* profile = browser()->profile();
+  // The user is still signed in implicitly.
+  ASSERT_EQ(signin::GetPrimaryAccountConsentLevel(GetIdentityManager()),
+            signin::ConsentLevel::kSignin);
+  ASSERT_TRUE(gaia::AreEmailsSame(
+      GetIdentityManager()
+          ->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)
+          .email,
+      kMainGmailEmail));
+  ASSERT_FALSE(profile->GetPrefs()->GetBoolean(prefs::kExplicitBrowserSignin));
+  // Account storage was not enabled yet.
+  AccountStorageStatus account_storage_status = GetAccountStorageStatus();
+  EXPECT_FALSE(account_storage_status.user_selectable_type_set.HasAny(
+      {syncer::UserSelectableType::kAutofill,
+       syncer::UserSelectableType::kPasswords}));
+
+  // Signout, and then signin again explicitly.
+  signin::ClearPrimaryAccount(GetIdentityManager());
+  AccountInfo primary_account_info = signin::MakePrimaryAccountAvailable(
+      GetIdentityManager(), kMainGmailEmail, signin::ConsentLevel::kSignin);
+  EXPECT_TRUE(profile->GetPrefs()->GetBoolean(prefs::kExplicitBrowserSignin));
+
+  // Account storage is now enabled.
+  account_storage_status = GetAccountStorageStatus();
+  EXPECT_TRUE(account_storage_status.user_selectable_type_set.HasAll(
+      {syncer::UserSelectableType::kAutofill,
+       syncer::UserSelectableType::kPasswords}));
+
+  // Cookie migration is done.
+  EXPECT_TRUE(profile->GetPrefs()->GetBoolean(
+      prefs::kCookieClearOnExitMigrationNoticeComplete));
+}
+
 // Signin implicitlty, Dice Signin.
-IN_PROC_BROWSER_TEST_F(DiceExplicitSigninBrowserTest,
-                       PRE_DiceUserMigratedClearsCookie) {
+IN_PROC_BROWSER_TEST_F(
+    DiceExplicitSigninBrowserTestWithForcedDiceMigrationDisabled,
+    PRE_DiceUserMigratedClearsCookie) {
   signin::MakeAccountAvailable(
       GetIdentityManager(),
       signin::AccountAvailabilityOptionsBuilder()
@@ -1720,8 +1762,9 @@
 }
 
 // Dice Signin with UNO enabled.
-IN_PROC_BROWSER_TEST_F(DiceExplicitSigninBrowserTest,
-                       DiceUserMigratedClearsCookie) {
+IN_PROC_BROWSER_TEST_F(
+    DiceExplicitSigninBrowserTestWithForcedDiceMigrationDisabled,
+    DiceUserMigratedClearsCookie) {
   Profile* profile = browser()->profile();
   // The user is still signed in implicitly.
   ASSERT_TRUE(
diff --git a/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc b/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
index 3b40876..1694af7 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
@@ -1673,10 +1673,15 @@
 class DiceWebSigninInterceptorWithUnoEnabledAndPREDisabledBrowserTest
     : public DiceWebSigninInterceptorWithChromeSigninHelpersBrowserTest {
  public:
-  DiceWebSigninInterceptorWithUnoEnabledAndPREDisabledBrowserTest() = default;
+  DiceWebSigninInterceptorWithUnoEnabledAndPREDisabledBrowserTest() {
+    // With kForcedDiceMigration enabled, am implicit signed-in user is signed
+    // out leaving the test a no-op.
+    feature_list_.InitAndDisableFeature(switches::kForcedDiceMigration);
+  }
 
  protected:
   const std::string email_ = "alice@example.com";
+  base::test::ScopedFeatureList feature_list_;
 };
 
 // Signing in to Chrome while explicit signin is disabled, to simulate a signed
diff --git a/chrome/browser/sync/test/integration/password_manager_sync_test.cc b/chrome/browser/sync/test/integration/password_manager_sync_test.cc
index ee1e9e5d..d8982a92 100644
--- a/chrome/browser/sync/test/integration/password_manager_sync_test.cc
+++ b/chrome/browser/sync/test/integration/password_manager_sync_test.cc
@@ -941,69 +941,6 @@
       GetSyncService(0)));
 }
 
-// TODO(b/327118794): Delete this test once implicit signin no longer exists.
-IN_PROC_BROWSER_TEST_F(PasswordManagerSyncTest,
-                       PRE_ClearAccountStoreOnStartup) {
-  ASSERT_TRUE(SetupClients());
-
-  // Add a credential to the server.
-  AddCredentialToFakeServer(
-      CreateTestPasswordForm("accountuser", "accountpass"));
-
-  SetupSyncTransportWithPasswordAccountStorage(/*explicit_signin=*/false);
-
-  // Also add a credential to the profile store.
-  AddLocalCredential("localuser", "localpass");
-
-  ASSERT_THAT(GetAllLoginsFromProfilePasswordStore(),
-              ElementsAre(MatchesLogin("localuser", "localpass")));
-  ASSERT_THAT(GetAllLoginsFromAccountPasswordStore(),
-              ElementsAre(MatchesLogin("accountuser", "accountpass")));
-}
-
-// TODO(b/327118794): Delete this test once implicit signin no longer exists.
-IN_PROC_BROWSER_TEST_F(PasswordManagerSyncTest, ClearAccountStoreOnStartup) {
-  // Before setting up the client (aka profile), manually set account storage to
-  // off in the profile's prefs file. This simulates the case where the user
-  // disabled account storage, but the account store was not cleared correctly,
-  // e.g. due to a poorly-timed crash.
-  base::FilePath user_data_dir;
-  base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
-  base::FilePath profile_path = user_data_dir.Append(GetProfileBaseName(0));
-  base::FilePath json_path = profile_path.Append(chrome::kPreferencesFilename);
-  {
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    std::string json;
-    ASSERT_TRUE(base::ReadFileToString(json_path, &json));
-    std::optional<base::Value> prefs = base::JSONReader::Read(json);
-    ASSERT_TRUE(prefs.has_value());
-    ASSERT_TRUE(prefs->is_dict());
-    ASSERT_TRUE(prefs->GetDict().RemoveByDottedPath(
-        syncer::prefs::internal::kSelectedTypesPerAccount));
-    std::optional<std::string> new_json = base::WriteJson(prefs.value());
-    ASSERT_TRUE(new_json.has_value());
-    ASSERT_TRUE(base::WriteFile(json_path, new_json.value()));
-  }
-
-  ASSERT_TRUE(SetupClients());
-
-  ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
-
-  // Since we mangled the prefs file, account storage should be disabled.
-  ASSERT_FALSE(password_manager::features_util::IsAccountStorageEnabled(
-      GetSyncService(0)));
-  ASSERT_FALSE(GetSyncService(0)->GetActiveDataTypes().Has(syncer::PASSWORDS));
-
-  // The account-scoped store should have been cleared during startup, and the
-  // credential added by the PRE_ test should be gone.
-  EXPECT_THAT(GetAllLoginsFromAccountPasswordStore(), IsEmpty());
-
-  // Just as a sanity check: The credential in the profile-scoped store should
-  // still be there.
-  ASSERT_THAT(GetAllLoginsFromProfilePasswordStore(),
-              ElementsAre(MatchesLogin("localuser", "localpass")));
-}
-
 #endif  // !BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(PasswordManagerSyncTest, SyncUtilApis) {
@@ -1111,6 +1048,82 @@
     waiter.Wait();
   }
 }
+
+class PasswordManagerSyncTestWithForcedDiceMigrationDisabled
+    : public PasswordManagerSyncTest {
+ public:
+  PasswordManagerSyncTestWithForcedDiceMigrationDisabled() {
+    feature_list_.InitAndDisableFeature(switches::kForcedDiceMigration);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// TODO(b/327118794): Delete this test once implicit signin no longer exists.
+IN_PROC_BROWSER_TEST_F(PasswordManagerSyncTestWithForcedDiceMigrationDisabled,
+                       PRE_ClearAccountStoreOnStartup) {
+  ASSERT_TRUE(SetupClients());
+
+  // Add a credential to the server.
+  AddCredentialToFakeServer(
+      CreateTestPasswordForm("accountuser", "accountpass"));
+
+  SetupSyncTransportWithPasswordAccountStorage(/*explicit_signin=*/false);
+
+  // Also add a credential to the profile store.
+  AddLocalCredential("localuser", "localpass");
+
+  ASSERT_THAT(GetAllLoginsFromProfilePasswordStore(),
+              ElementsAre(MatchesLogin("localuser", "localpass")));
+  ASSERT_THAT(GetAllLoginsFromAccountPasswordStore(),
+              ElementsAre(MatchesLogin("accountuser", "accountpass")));
+}
+
+// TODO(b/327118794): Delete this test once implicit signin no longer exists.
+IN_PROC_BROWSER_TEST_F(PasswordManagerSyncTestWithForcedDiceMigrationDisabled,
+                       ClearAccountStoreOnStartup) {
+  // Before setting up the client (aka profile), manually set account storage to
+  // off in the profile's prefs file. This simulates the case where the user
+  // disabled account storage, but the account store was not cleared correctly,
+  // e.g. due to a poorly-timed crash.
+  base::FilePath user_data_dir;
+  base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+  base::FilePath profile_path = user_data_dir.Append(GetProfileBaseName(0));
+  base::FilePath json_path = profile_path.Append(chrome::kPreferencesFilename);
+  {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    std::string json;
+    ASSERT_TRUE(base::ReadFileToString(json_path, &json));
+    std::optional<base::Value> prefs = base::JSONReader::Read(json);
+    ASSERT_TRUE(prefs.has_value());
+    ASSERT_TRUE(prefs->is_dict());
+    ASSERT_TRUE(prefs->GetDict().RemoveByDottedPath(
+        syncer::prefs::internal::kSelectedTypesPerAccount));
+    std::optional<std::string> new_json = base::WriteJson(prefs.value());
+    ASSERT_TRUE(new_json.has_value());
+    ASSERT_TRUE(base::WriteFile(json_path, new_json.value()));
+  }
+
+  ASSERT_TRUE(SetupClients());
+
+  ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
+
+  // Since we mangled the prefs file, account storage should be disabled.
+  ASSERT_FALSE(password_manager::features_util::IsAccountStorageEnabled(
+      GetSyncService(0)));
+  ASSERT_FALSE(GetSyncService(0)->GetActiveDataTypes().Has(syncer::PASSWORDS));
+
+  // The account-scoped store should have been cleared during startup, and the
+  // credential added by the PRE_ test should be gone.
+  EXPECT_THAT(GetAllLoginsFromAccountPasswordStore(), IsEmpty());
+
+  // Just as a sanity check: The credential in the profile-scoped store should
+  // still be there.
+  ASSERT_THAT(GetAllLoginsFromProfilePasswordStore(),
+              ElementsAre(MatchesLogin("localuser", "localpass")));
+}
+
 #endif  // !BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
index f5ba559b..b679af01 100644
--- a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
@@ -31,6 +31,7 @@
 #include "components/autofill/core/browser/webdata/payments/payments_sync_bridge_util.h"
 #include "components/autofill/core/common/autofill_switches.h"
 #include "components/prefs/pref_service.h"
+#include "components/signin/public/base/signin_switches.h"
 #include "components/signin/public/identity_manager/identity_test_utils.h"
 #include "components/sync/base/data_type.h"
 #include "components/sync/base/features.h"
@@ -254,81 +255,6 @@
   EXPECT_EQ(0U, GetServerCards(account_data).size());
 }
 
-// PRE_ test used to ensure the user is signed in at the time the browser starts
-// up, which is more realistic for the implicit signed-in state.
-IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest,
-                       PRE_DownloadAccountStorageWithImplicitSignIn_Card) {
-  ASSERT_TRUE(SetupClients());
-
-  secondary_account_helper::ImplicitSignInUnconsentedAccount(
-      GetProfile(0), &test_url_loader_factory_, "user@email.com");
-  ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
-  ASSERT_TRUE(AwaitQuiescence());
-  ASSERT_EQ(syncer::SyncService::TransportState::ACTIVE,
-            GetSyncService(0)->GetTransportState());
-  ASSERT_TRUE(GetSyncService(0)->GetActiveDataTypes().Has(
-      syncer::AUTOFILL_WALLET_DATA));
-}
-
-IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest,
-                       DownloadAccountStorageWithImplicitSignIn_Card) {
-  ASSERT_TRUE(SetupClients());
-  GetFakeServer()->SetWalletData(
-      {CreateDefaultSyncWalletCard(), CreateDefaultSyncPaymentsCustomerData()});
-
-  ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
-  ASSERT_TRUE(AwaitQuiescence());
-  ASSERT_EQ(syncer::SyncService::TransportState::ACTIVE,
-            GetSyncService(0)->GetTransportState());
-  ASSERT_TRUE(GetSyncService(0)->GetActiveDataTypes().Has(
-      syncer::AUTOFILL_WALLET_DATA));
-
-  scoped_refptr<autofill::AutofillWebDataService> profile_data =
-      GetProfileWebDataService(0);
-  ASSERT_NE(nullptr, profile_data);
-  scoped_refptr<autofill::AutofillWebDataService> account_data =
-      GetAccountWebDataService(0);
-  ASSERT_NE(nullptr, account_data);
-
-  // Check that no data is stored in the profile storage.
-  EXPECT_EQ(0U, GetServerCards(profile_data).size());
-
-  // Check that one card is stored in the account storage.
-  EXPECT_EQ(1U, GetServerCards(account_data).size());
-
-  // Check whether cards are stored in-memory (which is always the case for
-  // implicit sign-ins). The corresponding metric is recorded twice as an
-  // artifact of the test setup: SyncTest creates a new profile for
-  // single-client tests, disregarding the existing profile that browser tests
-  // already have.
-  EXPECT_TRUE(GetAccountWebDataService(0)->UsesInMemoryDatabaseForTesting());
-  histogram_tester_.ExpectBucketCount(
-      "WebDatabase.AutofillAccountStorage",
-      /*sample=*/1,  // kInMemory_SignedInImplicitly.
-      /*expected_bucket_count=*/1);
-
-  PaymentsDataManager* paydm = GetPaymentsDataManager(0);
-  ASSERT_NE(nullptr, paydm);
-  std::vector<const CreditCard*> cards = paydm->GetCreditCards();
-  ASSERT_EQ(1uL, cards.size());
-
-  ExpectDefaultCreditCardValues(*cards[0]);
-
-  GetClient(0)->SignOutPrimaryAccount();
-
-  // Verify that sync is stopped.
-  ASSERT_EQ(syncer::SyncService::TransportState::DISABLED,
-            GetSyncService(0)->GetTransportState());
-  ASSERT_FALSE(GetSyncService(0)->GetActiveDataTypes().Has(
-      syncer::AUTOFILL_WALLET_DATA));
-
-  // Wait for paydm to receive the data change with no cards.
-  WaitForNumberOfCards(0, paydm);
-
-  // Check directly in the DB that the account storage is now cleared.
-  EXPECT_EQ(0U, GetServerCards(account_data).size());
-}
-
 // Wallet data should get cleared from the database when the user signs out and
 // different data should get downstreamed when the user signs in with a
 // different account.
@@ -1122,4 +1048,93 @@
   EXPECT_EQ(0U, GetCreditCardCloudTokenData(account_data).size());
   EXPECT_EQ(1U, GetCreditCardCloudTokenData(profile_data).size());
 }
+
+class SingleClientWalletSyncTestWithForcedDiceMigrationDisabled
+    : public SingleClientWalletSyncTest {
+ public:
+  SingleClientWalletSyncTestWithForcedDiceMigrationDisabled() {
+    feature_list_.InitAndDisableFeature(switches::kForcedDiceMigration);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// PRE_ test used to ensure the user is signed in at the time the browser starts
+// up, which is more realistic for the implicit signed-in state.
+IN_PROC_BROWSER_TEST_F(
+    SingleClientWalletSyncTestWithForcedDiceMigrationDisabled,
+    PRE_DownloadAccountStorageWithImplicitSignIn_Card) {
+  ASSERT_TRUE(SetupClients());
+
+  secondary_account_helper::ImplicitSignInUnconsentedAccount(
+      GetProfile(0), &test_url_loader_factory_, "user@email.com");
+  ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
+  ASSERT_TRUE(AwaitQuiescence());
+  ASSERT_EQ(syncer::SyncService::TransportState::ACTIVE,
+            GetSyncService(0)->GetTransportState());
+  ASSERT_TRUE(GetSyncService(0)->GetActiveDataTypes().Has(
+      syncer::AUTOFILL_WALLET_DATA));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    SingleClientWalletSyncTestWithForcedDiceMigrationDisabled,
+    DownloadAccountStorageWithImplicitSignIn_Card) {
+  ASSERT_TRUE(SetupClients());
+  GetFakeServer()->SetWalletData(
+      {CreateDefaultSyncWalletCard(), CreateDefaultSyncPaymentsCustomerData()});
+
+  ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
+  ASSERT_TRUE(AwaitQuiescence());
+  ASSERT_EQ(syncer::SyncService::TransportState::ACTIVE,
+            GetSyncService(0)->GetTransportState());
+  ASSERT_TRUE(GetSyncService(0)->GetActiveDataTypes().Has(
+      syncer::AUTOFILL_WALLET_DATA));
+
+  scoped_refptr<autofill::AutofillWebDataService> profile_data =
+      GetProfileWebDataService(0);
+  ASSERT_NE(nullptr, profile_data);
+  scoped_refptr<autofill::AutofillWebDataService> account_data =
+      GetAccountWebDataService(0);
+  ASSERT_NE(nullptr, account_data);
+
+  // Check that no data is stored in the profile storage.
+  EXPECT_EQ(0U, GetServerCards(profile_data).size());
+
+  // Check that one card is stored in the account storage.
+  EXPECT_EQ(1U, GetServerCards(account_data).size());
+
+  // Check whether cards are stored in-memory (which is always the case for
+  // implicit sign-ins). The corresponding metric is recorded twice as an
+  // artifact of the test setup: SyncTest creates a new profile for
+  // single-client tests, disregarding the existing profile that browser tests
+  // already have.
+  EXPECT_TRUE(GetAccountWebDataService(0)->UsesInMemoryDatabaseForTesting());
+  histogram_tester_.ExpectBucketCount(
+      "WebDatabase.AutofillAccountStorage",
+      /*sample=*/1,  // kInMemory_SignedInImplicitly.
+      /*expected_bucket_count=*/1);
+
+  PaymentsDataManager* paydm = GetPaymentsDataManager(0);
+  ASSERT_NE(nullptr, paydm);
+  std::vector<const CreditCard*> cards = paydm->GetCreditCards();
+  ASSERT_EQ(1uL, cards.size());
+
+  ExpectDefaultCreditCardValues(*cards[0]);
+
+  GetClient(0)->SignOutPrimaryAccount();
+
+  // Verify that sync is stopped.
+  ASSERT_EQ(syncer::SyncService::TransportState::DISABLED,
+            GetSyncService(0)->GetTransportState());
+  ASSERT_FALSE(GetSyncService(0)->GetActiveDataTypes().Has(
+      syncer::AUTOFILL_WALLET_DATA));
+
+  // Wait for paydm to receive the data change with no cards.
+  WaitForNumberOfCards(0, paydm);
+
+  // Check directly in the DB that the account storage is now cleared.
+  EXPECT_EQ(0U, GetServerCards(account_data).size());
+}
+
 #endif  // !BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModel.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModel.java
index c08b9a8..7444a8ab1 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModel.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModel.java
@@ -258,6 +258,11 @@
     }
 
     @Override
+    public int getPinnedTabsCount() {
+        return 0;
+    }
+
+    @Override
     public OptionalInt getNativeSessionIdForTesting() {
         return OptionalInt.empty();
     }
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImpl.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImpl.java
index cbd8381..7011d96f 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImpl.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImpl.java
@@ -421,6 +421,11 @@
     }
 
     @Override
+    public int getPinnedTabsCount() {
+        return mDelegateModel.getPinnedTabsCount();
+    }
+
+    @Override
     public OptionalInt getNativeSessionIdForTesting() {
         return mDelegateModel.getNativeSessionIdForTesting();
     }
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModel.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModel.java
index d80727d6..128219b 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModel.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModel.java
@@ -206,6 +206,11 @@
      */
     int findFirstNonPinnedTabIndex();
 
+    /**
+     * @return The number of pinned tabs in this model.
+     */
+    int getPinnedTabsCount();
+
     /** Returns the native {@code SessionID} as returned by {@code tab_model.h:GetSessionId()}. */
     OptionalInt getNativeSessionIdForTesting();
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 6241fb89..5a753e3 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1918,6 +1918,10 @@
       "views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.cc",
       "views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.h",
       "views/web_apps/isolated_web_apps/pref_observer_ash.cc",
+      "views/web_apps/protocol_handler_picker_coordinator.cc",
+      "views/web_apps/protocol_handler_picker_coordinator.h",
+      "views/web_apps/protocol_handler_picker_dialog.cc",
+      "views/web_apps/protocol_handler_picker_dialog.h",
       "webui/ash/dlp_internals/dlp_internals_page_handler.cc",
       "webui/ash/dlp_internals/dlp_internals_page_handler.h",
       "webui/ash/dlp_internals/dlp_internals_ui.cc",
diff --git a/chrome/browser/ui/autofill/bubble_manager.h b/chrome/browser/ui/autofill/bubble_manager.h
index 7a39983d..c74ebc8aa 100644
--- a/chrome/browser/ui/autofill/bubble_manager.h
+++ b/chrome/browser/ui/autofill/bubble_manager.h
@@ -38,21 +38,19 @@
 //    - Show B immediately.
 //
 // 2. If a bubble (A) is already showing:
-//    - The manager determines if the new bubble's controller (B)
-//      should preempt the active bubble (A).  If the request is to force
-//      show, the active bubble is preempted irrespective of the priorities of
-//      the bubbles and hovering state. The default preemption policy is based
-//      on priority (i.e., priority(B) > priority(A)), but custom rules can be
-//      defined in ShouldAlwaysPreemptSameType(e.g., to always preempt another
-//      bubble of the same type).
-//    - HOWEVER, preemption is blocked if the user is currently hovering their
-//      mouse over bubble A.
+//    - Preemption of an active bubble (A) by a new bubble (B) is determined by
+//      the following rules, in order:
+//      - Force Show: If `force_show` is true, B always preempts A.
+//      - Mouse Hover: If the user is hovering over A, B never preempts A.
+//      - Same Type: If B and A have the same type, B preempts A only if that
+//        type has a "preempt same type" policy.
+//      - Priority: Otherwise, B preempts A if priority(B) > priority(A).
 //
 //    - If B preempts A:
 //      - Hide A and add it to the pending queue.
 //      - Show B.
 //
-//    - If B does not preempt A (due to its policy or A being hovered):
+//    - If B does not preempt A:
 //      - Add B to the pending queue. A remains visible.
 //
 // === Queue and Hiding Logic ===
@@ -79,7 +77,9 @@
   static std::unique_ptr<BubbleManager> Create(tabs::TabInterface* tab);
   static BubbleManager* GetForWebContents(content::WebContents* web_contents);
 
-  // Called by the bubbles once they are ready to be shown.
+  // Requests the bubble for `controller_to_show` to be displayed.
+  // If `force_show` is true, this bubble will preempt any active bubble,
+  // regardless of priority or hover state.
   virtual void RequestShowController(BubbleControllerBase& controller_to_show,
                                      bool force_show) = 0;
 
@@ -90,9 +90,11 @@
       BubbleControllerBase& controller_to_hide,
       bool show_next_bubble) = 0;
 
-  // Returns true if there is a pending bubble that has not timed out, of the
-  // specified `bubble_type` in the queue. Always returns false for the bubble
-  // types that preempt themselves (eg. Password manager bubbles).
+  // Returns true if a bubble of the specified `bubble_type` is already
+  // pending in the queue and has not timed out.
+  // Note: This will always return false for bubble types that preempt
+  // themselves (e.g., password bubbles), as they replace existing requests
+  // instead of waiting in the queue.
   [[nodiscard]] virtual bool HasPendingBubbleOfSameType(
       const BubbleType bubble_type) const = 0;
 };
diff --git a/chrome/browser/ui/autofill/bubble_manager_impl.cc b/chrome/browser/ui/autofill/bubble_manager_impl.cc
index 45d241c..c821702 100644
--- a/chrome/browser/ui/autofill/bubble_manager_impl.cc
+++ b/chrome/browser/ui/autofill/bubble_manager_impl.cc
@@ -344,8 +344,7 @@
 
 void BubbleManagerImpl::TabWillEnterBackground(
     tabs::TabInterface* tab_interface) {
-  if (active_bubble_controller_ &&
-      active_bubble_controller_->IsShowingBubble()) {
+  if (active_bubble_controller_) {
     AddToPendingQueue(active_bubble_controller_);
     active_bubble_controller_->HideBubble(/*show_next_bubble=*/false);
     active_bubble_controller_ = nullptr;
@@ -354,7 +353,12 @@
 
 void BubbleManagerImpl::TabDidEnterForeground(
     tabs::TabInterface* tab_interface) {
-  ProcessPendingBubbles();
+  if (!active_bubble_controller_) {
+    ProcessPendingBubbles();
+  } else if (!active_bubble_controller_->IsShowingBubble()) {
+    // This can happen if a tab created in background becomes visible.
+    active_bubble_controller_->ShowBubble();
+  }
 }
 
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/bubble_manager_impl_unittest.cc b/chrome/browser/ui/autofill/bubble_manager_impl_unittest.cc
index eb95023..56ac068 100644
--- a/chrome/browser/ui/autofill/bubble_manager_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/bubble_manager_impl_unittest.cc
@@ -651,4 +651,102 @@
   tab_interface()->Deactivate();
 }
 
+// Test that `force_show` preempts an active bubble regardless of priority or
+// hover state.
+TEST_F(BubbleManagerImplTest,
+       RequestShow_ForceShow_PreemptsRegardlessOfPriority) {
+  std::unique_ptr<MockBubbleController> card_controller =
+      CreateController(BubbleType::kSaveUpdateCard);
+  std::unique_ptr<MockBubbleController> address_controller =
+      CreateController(BubbleType::kSaveUpdateAddress);
+
+  // Show the higher-priority card bubble.
+  EXPECT_CALL(*card_controller, ShowBubble());
+  bubble_manager().RequestShowController(*card_controller,
+                                         /*force_show=*/false);
+  ASSERT_TRUE(card_controller->IsShowingBubble());
+
+  // Simulate hovering to ensure `force_show` bypasses it.
+  ON_CALL(*card_controller, IsMouseHovered).WillByDefault(Return(true));
+  {
+    InSequence sequence;
+    EXPECT_CALL(*card_controller, HideBubble(/*show_next_bubble=*/false));
+    EXPECT_CALL(*address_controller, ShowBubble());
+  }
+
+  // Force show the lower-priority address bubble.
+  bubble_manager().RequestShowController(*address_controller,
+                                         /*force_show=*/true);
+  EXPECT_FALSE(card_controller->IsShowingBubble());
+  EXPECT_TRUE(address_controller->IsShowingBubble());
+}
+
+// Test that the active bubble is hidden and queued when the tab is deactivated.
+TEST_F(BubbleManagerImplTest, TabDeactivated_ActiveBubbleIsQueuedAndHidden) {
+  std::unique_ptr<MockBubbleController> address_controller =
+      CreateController(BubbleType::kSaveUpdateAddress);
+  EXPECT_CALL(*address_controller, ShowBubble());
+  bubble_manager().RequestShowController(*address_controller,
+                                         /*force_show=*/false);
+  ASSERT_TRUE(address_controller->IsShowingBubble());
+
+  // Deactivating the tab should hide the bubble.
+  EXPECT_CALL(*address_controller, HideBubble(/*show_next_bubble=*/false));
+  tab_interface()->Deactivate();
+  EXPECT_FALSE(address_controller->IsShowingBubble());
+
+  // When the tab is reactivated, the bubble should be shown again from the
+  // queue.
+  EXPECT_CALL(*address_controller, ShowBubble());
+  tab_interface()->Activate();
+  EXPECT_TRUE(address_controller->IsShowingBubble());
+}
+
+// Test that `ProcessPendingBubbles` cleans up stale pointers from the queue.
+TEST_F(BubbleManagerImplTest, ProcessPendingBubbles_CleansUpStaleControllers) {
+  auto card_controller = CreateController(BubbleType::kSaveUpdateCard);
+  auto address_controller = CreateController(BubbleType::kSaveUpdateAddress);
+
+  EXPECT_CALL(*card_controller, ShowBubble());
+  bubble_manager().RequestShowController(*card_controller,
+                                         /*force_show=*/false);
+
+  // Queue the address bubble.
+  bubble_manager().RequestShowController(*address_controller,
+                                         /*force_show=*/false);
+  EXPECT_TRUE(card_controller->IsShowingBubble());
+  EXPECT_FALSE(address_controller->IsShowingBubble());
+
+  // Destroy the address controller, invalidating its weak pointer.
+  address_controller.reset();
+  bubble_manager().OnBubbleHiddenByController(*card_controller,
+                                              /*show_next_bubble=*/true);
+}
+
+// Test that the time a bubble spends in the queue is logged correctly.
+TEST_F(BubbleManagerImplTest, HideActiveBubble_LogsTimeInQueue) {
+  std::unique_ptr<MockBubbleController> address_controller =
+      CreateController(BubbleType::kSaveUpdateAddress);
+  std::unique_ptr<MockBubbleController> card_controller =
+      CreateController(BubbleType::kSaveUpdateCard);
+
+  // Show card bubble, then queue address bubble.
+  EXPECT_CALL(*card_controller, ShowBubble());
+  bubble_manager().RequestShowController(*card_controller,
+                                         /*force_show=*/false);
+  bubble_manager().RequestShowController(*address_controller,
+                                         /*force_show=*/false);
+
+  // Advance time by 5 seconds.
+  FastForwardBy(base::Seconds(5));
+
+  // Hide the active bubble, which should trigger the queued bubble to show.
+  EXPECT_CALL(*address_controller, ShowBubble());
+  bubble_manager().OnBubbleHiddenByController(*card_controller,
+                                              /*show_next_bubble=*/true);
+  histogram_tester_.ExpectUniqueTimeSample(
+      "Autofill.Bubble.Queue.TimeInQueue.SaveUpdateAddress", base::Seconds(5),
+      1);
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index 3bd8225..61155b3 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -990,6 +990,7 @@
 
   // Accept the navigation so we end up on a page without a beforeunload hook.
   alert->view()->AcceptAppModalDialog();
+  EXPECT_TRUE(content::WaitForLoadStop(contents));
 }
 
 IN_PROC_BROWSER_TEST_F(BrowserTest, NotifiesBrowserDidClose) {
diff --git a/chrome/browser/ui/profiles/signin_intercept_first_run_experience_dialog_browsertest.cc b/chrome/browser/ui/profiles/signin_intercept_first_run_experience_dialog_browsertest.cc
index e0a1257ad..dfdc9d8 100644
--- a/chrome/browser/ui/profiles/signin_intercept_first_run_experience_dialog_browsertest.cc
+++ b/chrome/browser/ui/profiles/signin_intercept_first_run_experience_dialog_browsertest.cc
@@ -39,6 +39,7 @@
 #include "components/policy/policy_constants.h"
 #include "components/signin/public/base/signin_metrics.h"
 #include "components/signin/public/base/signin_switches.h"
+#include "components/sync/base/features.h"
 #include "components/sync/test/test_sync_service.h"
 #include "components/user_education/common/feature_promo/feature_promo_controller.h"
 #include "content/public/browser/web_contents.h"
@@ -120,7 +121,12 @@
                  /*use_main_profile=*/true),
         scoped_iph_delay_(
             AvatarToolbarButton::SetScopedIPHMinDelayAfterCreationForTesting(
-                base::Seconds(0))) {}
+                base::Seconds(0))) {
+    // TODO(crbug.com/447155093): Fix the tests to work with
+    // `kReplaceSyncPromosWithSignInPromos` enabled.
+    scoped_feature_list_.InitAndDisableFeature(
+        syncer::kReplaceSyncPromosWithSignInPromos);
+  }
 
   ~SigninInterceptFirstRunExperienceDialogBrowserTest() override = default;
 
@@ -249,6 +255,7 @@
   const GURL kSyncSettingsUrl = GURL("chrome://settings/syncSetup");
 
  private:
+  base::test::ScopedFeatureList scoped_feature_list_;
   testing::NiceMock<policy::MockConfigurationPolicyProvider> policy_provider_;
   // Needed for profile switch IPH testing.
   base::AutoReset<base::TimeDelta> scoped_iph_delay_;
diff --git a/chrome/browser/ui/signin/dice_migration_service_browsertest.cc b/chrome/browser/ui/signin/dice_migration_service_browsertest.cc
index fbab126..5ed3d133 100644
--- a/chrome/browser/ui/signin/dice_migration_service_browsertest.cc
+++ b/chrome/browser/ui/signin/dice_migration_service_browsertest.cc
@@ -84,6 +84,14 @@
 
 class DiceMigrationServiceBrowserTest : public InProcessBrowserTest {
  public:
+  DiceMigrationServiceBrowserTest() {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{switches::kOfferMigrationToDiceUsers},
+        // DICe migration dialog is not shown when forced migration flag is
+        // enabled.
+        /*disabled_features=*/{switches::kForcedDiceMigration});
+  }
+
   void SetUpOnMainThread() override {
     InProcessBrowserTest::SetUpOnMainThread();
     disclaimer_service_resetter_ =
@@ -147,8 +155,7 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_{
-      switches::kOfferMigrationToDiceUsers};
+  base::test::ScopedFeatureList scoped_feature_list_;
   base::ScopedClosureRunner disclaimer_service_resetter_;
   base::HistogramTester histogram_tester_;
 };
@@ -841,7 +848,11 @@
 
 class DiceMigrationServiceSyncTest : public SyncTest {
  public:
-  DiceMigrationServiceSyncTest() : SyncTest(SINGLE_CLIENT) {}
+  DiceMigrationServiceSyncTest() : SyncTest(SINGLE_CLIENT) {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{switches::kOfferMigrationToDiceUsers},
+        /*disabled_features=*/{switches::kForcedDiceMigration});
+  }
 
   signin::IdentityManager* GetIdentityManager() {
     return IdentityManagerFactory::GetForProfile(GetProfile(0));
@@ -873,8 +884,7 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_{
-      switches::kOfferMigrationToDiceUsers};
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(DiceMigrationServiceSyncTest, PRE_MigrateUser) {
diff --git a/chrome/browser/ui/signin/dice_migration_service_interactive_uitest.cc b/chrome/browser/ui/signin/dice_migration_service_interactive_uitest.cc
index b5828d24..79de456 100644
--- a/chrome/browser/ui/signin/dice_migration_service_interactive_uitest.cc
+++ b/chrome/browser/ui/signin/dice_migration_service_interactive_uitest.cc
@@ -55,6 +55,12 @@
 
 class DiceMigrationServiceInteractiveUiTest : public InteractiveBrowserTest {
  public:
+  DiceMigrationServiceInteractiveUiTest() {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{switches::kOfferMigrationToDiceUsers},
+        /*disabled_features=*/{switches::kForcedDiceMigration});
+  }
+
   Profile* GetProfile() { return browser()->profile(); }
 
   DiceMigrationService* GetDiceMigrationService() {
@@ -106,8 +112,7 @@
   }
 
  protected:
-  const base::test::ScopedFeatureList scoped_feature_list_{
-      switches::kOfferMigrationToDiceUsers};
+  base::test::ScopedFeatureList scoped_feature_list_;
   base::HistogramTester histogram_tester_;
   base::UserActionTester user_action_tester_;
 };
diff --git a/chrome/browser/ui/signin/dice_migration_service_pixel_browsertest.cc b/chrome/browser/ui/signin/dice_migration_service_pixel_browsertest.cc
index 04df3f4..dd17e4d 100644
--- a/chrome/browser/ui/signin/dice_migration_service_pixel_browsertest.cc
+++ b/chrome/browser/ui/signin/dice_migration_service_pixel_browsertest.cc
@@ -34,6 +34,12 @@
 
 class DiceMigrationServicePixelBrowserTest : public InteractiveBrowserTest {
  public:
+  DiceMigrationServicePixelBrowserTest() {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{switches::kOfferMigrationToDiceUsers},
+        /*disabled_features=*/{switches::kForcedDiceMigration});
+  }
+
   DiceMigrationService* GetDiceMigrationService() {
     return DiceMigrationServiceFactory::GetForProfile(GetProfile());
   }
@@ -58,8 +64,7 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_{
-      switches::kOfferMigrationToDiceUsers};
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // This dialog is shown during all but the final time the migration is offered.
diff --git a/chrome/browser/ui/views/autofill/payments/offer_notification_bubble_views_interactive_uitest.cc b/chrome/browser/ui/views/autofill/payments/offer_notification_bubble_views_interactive_uitest.cc
index 24cfa86..1d90165b 100644
--- a/chrome/browser/ui/views/autofill/payments/offer_notification_bubble_views_interactive_uitest.cc
+++ b/chrome/browser/ui/views/autofill/payments/offer_notification_bubble_views_interactive_uitest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <string_view>
+#include <vector>
 
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -28,6 +29,7 @@
 #include "components/autofill/core/browser/payments/offer_notification_handler.h"
 #include "components/autofill/core/browser/test_utils/test_autofill_clock.h"
 #include "components/autofill/core/common/autofill_clock.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
 #include "components/search/ntp_features.h"
 #include "components/strings/grit/components_strings.h"
@@ -59,22 +61,46 @@
 
 std::string GetTestName(
     const ::testing::TestParamInfo<
-        OfferNotificationBubbleViewsInteractiveUiTestData>& info) {
-  return info.param.name;
+        std::tuple<OfferNotificationBubbleViewsInteractiveUiTestData, bool>>&
+        info) {
+  const auto& params = std::get<0>(info.param);
+  bool bubble_manager_enabled = std::get<1>(info.param);
+  return params.name + (bubble_manager_enabled ? "WithBubbleManagerEnabled"
+                                               : "WithBubbleManagerDisabled");
 }
 
 class OfferNotificationBubbleViewsInteractiveUiTest
     : public OfferNotificationBubbleViewsTestBase,
       public testing::WithParamInterface<
-          OfferNotificationBubbleViewsInteractiveUiTestData> {
+          std::tuple<OfferNotificationBubbleViewsInteractiveUiTestData, bool>> {
  public:
   OfferNotificationBubbleViewsInteractiveUiTest()
-      : test_offer_type_(GetParam().offer_type) {
-    if (GetParam().is_page_actions_migration_enabled) {
-      feature_list_.InitAndEnableFeatureWithParameters(
-          ::features::kPageActionsMigration,
-          {{::features::kPageActionsMigrationOfferNotification.name, "true"}});
+      : test_offer_type_(std::get<0>(GetParam()).offer_type) {
+    const auto& params = std::get<0>(GetParam());
+    bool bubble_manager_enabled = std::get<1>(GetParam());
+
+    std::vector<base::test::FeatureRefAndParams> enabled_features;
+    std::vector<base::test::FeatureRef> disabled_features;
+
+    if (bubble_manager_enabled) {
+      enabled_features.push_back(
+          {features::kAutofillShowBubblesBasedOnPriorities, {}});
+    } else {
+      disabled_features.push_back(
+          features::kAutofillShowBubblesBasedOnPriorities);
     }
+
+    if (params.is_page_actions_migration_enabled) {
+      enabled_features.push_back(
+          {::features::kPageActionsMigration,
+           {{::features::kPageActionsMigrationOfferNotification.name,
+             "true"}}});
+    } else {
+      disabled_features.push_back(::features::kPageActionsMigration);
+    }
+
+    feature_list_.InitWithFeaturesAndParameters(enabled_features,
+                                                disabled_features);
   }
 
   ~OfferNotificationBubbleViewsInteractiveUiTest() override = default;
@@ -178,10 +204,13 @@
 INSTANTIATE_TEST_SUITE_P(
     MAYBE_GPayCardLinked,
     OfferNotificationBubbleViewsInteractiveUiTest,
-    testing::Values(OfferNotificationBubbleViewsInteractiveUiTestData{
-        "GPayCardLinked",
-        AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER,
-    }));
+    testing::Combine(
+        testing::Values(OfferNotificationBubbleViewsInteractiveUiTestData{
+            "GPayCardLinked",
+            AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER,
+        }),
+        testing::Bool()),
+    &GetTestName);
 
 // TODO(crbug.com/416010106): Flaky failures.
 #if BUILDFLAG(IS_MAC)
@@ -193,11 +222,14 @@
 INSTANTIATE_TEST_SUITE_P(
     MAYBE_GPayCardLinkedWithNewPageAction,
     OfferNotificationBubbleViewsInteractiveUiTest,
-    testing::Values(OfferNotificationBubbleViewsInteractiveUiTestData{
-        "GPayCardLinked",
-        AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER,
-        /*is_page_actions_migration_enabled=*/true,
-    }));
+    testing::Combine(
+        testing::Values(OfferNotificationBubbleViewsInteractiveUiTestData{
+            "GPayCardLinkedWithNewPageAction",
+            AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER,
+            /*is_page_actions_migration_enabled=*/true,
+        }),
+        testing::Bool()),
+    &GetTestName);
 
 // TODO(crbug.com/416010106): Flaky failures.
 #if BUILDFLAG(IS_MAC)
@@ -208,8 +240,12 @@
 INSTANTIATE_TEST_SUITE_P(
     MAYBE_GPayPromoCode,
     OfferNotificationBubbleViewsInteractiveUiTest,
-    testing::Values(OfferNotificationBubbleViewsInteractiveUiTestData{
-        "GPayPromoCode", AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER}));
+    testing::Combine(
+        testing::Values(OfferNotificationBubbleViewsInteractiveUiTestData{
+            "GPayPromoCode",
+            AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER}),
+        testing::Bool()),
+    &GetTestName);
 
 // TODO(crbug.com/416010106): Flaky failures.
 #if BUILDFLAG(IS_MAC)
@@ -221,9 +257,13 @@
 INSTANTIATE_TEST_SUITE_P(
     MAYBE_GPayPromoCodeWithNewPageAction,
     OfferNotificationBubbleViewsInteractiveUiTest,
-    testing::Values(OfferNotificationBubbleViewsInteractiveUiTestData{
-        "GPayPromoCode", AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER,
-        /*is_page_actions_migration_enabled=*/true}));
+    testing::Combine(
+        testing::Values(OfferNotificationBubbleViewsInteractiveUiTestData{
+            "GPayPromoCodeWithNewPageAction",
+            AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER,
+            /*is_page_actions_migration_enabled=*/true}),
+        testing::Bool()),
+    &GetTestName);
 
 // TODO(crbug.com/40285326): This fails with the field trial testing config.
 class OfferNotificationBubbleViewsInteractiveUiTestNoTestingConfig
@@ -245,8 +285,12 @@
 INSTANTIATE_TEST_SUITE_P(
     MAYBE_GPayPromoCode,
     OfferNotificationBubbleViewsInteractiveUiTestNoTestingConfig,
-    testing::Values(OfferNotificationBubbleViewsInteractiveUiTestData{
-        "GPayPromoCode", AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER}));
+    testing::Combine(
+        testing::Values(OfferNotificationBubbleViewsInteractiveUiTestData{
+            "GPayPromoCode",
+            AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER}),
+        testing::Bool()),
+    &GetTestName);
 
 // TODO(crbug.com/416010106): Flaky failures.
 #if BUILDFLAG(IS_MAC)
@@ -258,9 +302,13 @@
 INSTANTIATE_TEST_SUITE_P(
     MAYBE_GPayPromoCodeWithNewPageAction,
     OfferNotificationBubbleViewsInteractiveUiTestNoTestingConfig,
-    testing::Values(OfferNotificationBubbleViewsInteractiveUiTestData{
-        "GPayPromoCode", AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER,
-        /*is_page_actions_migration_enabled=*/true}));
+    testing::Combine(
+        testing::Values(OfferNotificationBubbleViewsInteractiveUiTestData{
+            "GPayPromoCodeWithNewPageAction",
+            AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER,
+            /*is_page_actions_migration_enabled=*/true}),
+        testing::Bool()),
+    &GetTestName);
 
 // TODO(crbug.com/40817360): Flaky failures.
 #if BUILDFLAG(IS_LINUX)
diff --git a/chrome/browser/ui/views/external_protocol_handler_ash.cc b/chrome/browser/ui/views/external_protocol_handler_ash.cc
index 04be034..68b12e6b 100644
--- a/chrome/browser/ui/views/external_protocol_handler_ash.cc
+++ b/chrome/browser/ui/views/external_protocol_handler_ash.cc
@@ -9,14 +9,13 @@
 #include "base/memory/raw_ptr.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ash/arc/intent_helper/arc_intent_helper_mojo_ash.h"
 #include "chrome/browser/ash/guest_os/guest_os_external_protocol_handler.h"
 #include "chrome/browser/chromeos/arc/arc_external_protocol_dialog.h"
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/views/external_protocol_dialog.h"
+#include "chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.h"
 #include "chrome/browser/web_applications/app_service/publisher_helper.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -125,49 +124,6 @@
   }
 }
 
-std::string GetAppName(apps::AppServiceProxy* proxy,
-                       const std::string& app_id) {
-  std::optional<std::string> app_name;
-  proxy->AppRegistryCache().ForOneApp(
-      app_id,
-      [&](const apps::AppUpdate& update) { app_name = update.ShortName(); });
-  CHECK(app_name);
-  return *app_name;
-}
-
-void HandleWebAppManifestProtocolHandler(
-    Profile* profile,
-    WebContents* web_contents,
-    const GURL& url,
-    const std::vector<std::string>& app_ids,
-    const std::optional<url::Origin>& initiating_origin,
-    content::WeakDocumentPtr initiator_document) {
-  CHECK(!app_ids.empty());
-  CHECK(apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile));
-  auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
-  if (app_ids.size() > 1) {
-    // TODO(crbug.com/422422887): Figure out how to disambiguate conflicting
-    // protocol handlers; for now, pick the first one in the list.
-    static constexpr std::string_view kConfictingProtocolHandlersWarning =
-        "There's more than one web application handling %s links : [%s]; "
-        "ChromeOS currently doesn't support disambiguating multiple handlers.";
-    if (content::RenderFrameHost* rfh =
-            initiator_document.AsRenderFrameHostIfValid()) {
-      std::vector<std::string> app_names = base::ToVector(
-          app_ids,
-          [&](const auto& app_id) { return GetAppName(proxy, app_id); });
-      rfh->AddMessageToConsole(
-          blink::mojom::ConsoleMessageLevel::kWarning,
-          base::StringPrintf(kConfictingProtocolHandlersWarning, url.scheme(),
-                             base::JoinString(app_names, ",")));
-    }
-  }
-
-  new ExternalProtocolDialog(web_contents, url,
-                             base::UTF8ToUTF16(GetAppName(proxy, app_ids[0])),
-                             initiating_origin, std::move(initiator_document));
-}
-
 }  // namespace
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -194,8 +150,8 @@
   if (std::vector<std::string> app_ids =
           web_app::GetWebAppIdsForProtocolUrl(profile, url);
       !app_ids.empty()) {
-    HandleWebAppManifestProtocolHandler(profile, web_contents, url, app_ids,
-                                        initiating_origin, initiator_document);
+    web_app::LaunchProtocolUrlInPreferredApp(web_contents, url, app_ids,
+                                             initiating_origin);
     return;
   }
 
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
index 21b72b3d..7cefb79 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
@@ -394,7 +394,6 @@
     delay_resets_.push_back(
         signin_ui_util::
             CreateZeroOverrideDelayForCrossWindowAnimationReplayForTesting());
-    ClearSyncOptinPromoIfEnabled(avatar);
     return account_info;
   }
 
@@ -934,20 +933,6 @@
   EXPECT_EQ(avatar_button->GetText(), std::u16string());
 }
 
-IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest, SigninWithSyncError) {
-  AvatarToolbarButton* avatar_button = GetAvatarToolbarButton(browser());
-  // Normal state.
-  ASSERT_TRUE(avatar_button->GetText().empty());
-
-  SigninWithImageAndClearGreetingAndSyncPromo(avatar_button, u"test@gmail.com");
-  SimulateSyncError();
-  EXPECT_EQ(avatar_button->GetText(),
-            l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SIGNIN_PAUSED));
-
-  ClearSyncError();
-  EXPECT_EQ(avatar_button->GetText(), std::u16string());
-}
-
 IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonWithSyncBrowserTest,
                        SyncPausedThenExplicitText) {
   AvatarToolbarButton* avatar_button = GetAvatarToolbarButton(browser());
@@ -970,32 +955,6 @@
   ExpectSyncPaused(avatar_button);
 }
 
-#if !BUILDFLAG(IS_CHROMEOS)
-IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest,
-                       SigninPendingThenExplicitText) {
-  AvatarToolbarButton* avatar_button = GetAvatarToolbarButton(browser());
-  // Normal state.
-  ASSERT_TRUE(avatar_button->GetText().empty());
-
-  SigninWithImageAndClearGreetingAndSyncPromo(avatar_button, u"test@gmail.com");
-  SimulateSigninPending(/*web_sign_out=*/false);
-  ASSERT_EQ(avatar_button->GetText(),
-            l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SIGNIN_PAUSED));
-
-  const std::u16string profile_switch_text(u"Profile Switch?");
-  base::ScopedClosureRunner hide_callback =
-      avatar_button->SetExplicitButtonState(
-          profile_switch_text, /*accessibility_label=*/std::nullopt,
-          /*explicit_action=*/std::nullopt);
-  ASSERT_EQ(avatar_button->GetText(), profile_switch_text);
-
-  // Clearing explicit text should go back to Signin Pending.
-  hide_callback.RunAndReset();
-  EXPECT_EQ(avatar_button->GetText(),
-            l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SIGNIN_PAUSED));
-}
-#endif
-
 // Explicit text over sync paused/error.
 IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonWithSyncBrowserTest,
                        ExplicitTextThenSyncPaused) {
@@ -1020,33 +979,6 @@
   ExpectSyncPaused(avatar_button);
 }
 
-#if !BUILDFLAG(IS_CHROMEOS)
-// Explicit text over signin pending.
-IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest,
-                       ExplicitTextThenSigninPending) {
-  AvatarToolbarButton* avatar_button = GetAvatarToolbarButton(browser());
-  // Normal state.
-  ASSERT_TRUE(avatar_button->GetText().empty());
-
-  SigninWithImageAndClearGreetingAndSyncPromo(avatar_button, u"test@gmail.com");
-  const std::u16string profile_switch_text(u"Profile Switch?");
-  base::ScopedClosureRunner hide_callback =
-      avatar_button->SetExplicitButtonState(
-          profile_switch_text, /*accessibility_label=*/std::nullopt,
-          /*explicit_action=*/std::nullopt);
-  ASSERT_EQ(avatar_button->GetText(), profile_switch_text);
-
-  SimulateSigninPending(/*web_sign_out=*/false);
-  // Explicit text should still be shown even if Signin Pending.
-  ASSERT_EQ(avatar_button->GetText(), profile_switch_text);
-
-  // Clearing explicit text should go back to Signin Pending.
-  hide_callback.RunAndReset();
-  EXPECT_EQ(avatar_button->GetText(),
-            l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SIGNIN_PAUSED));
-}
-#endif
-
 IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest,
                        ShowExplicitTextAndHide) {
   AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser());
@@ -1202,6 +1134,151 @@
 
 #endif
 
+class AvatarToolbarButtonBrowserTestWithoutReplaceSyncWithSigninPromos
+    : public AvatarToolbarButtonBrowserTest {
+ public:
+  AvatarToolbarButtonBrowserTestWithoutReplaceSyncWithSigninPromos() {
+    // TODO(crbug.com/444617947): Fix the tests to work with the feature enabled
+    // and move them back to AvatarToolbarButtonBrowserTest.
+    feature_list_.InitAndDisableFeature(
+        syncer::kReplaceSyncPromosWithSignInPromos);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(
+    AvatarToolbarButtonBrowserTestWithoutReplaceSyncWithSigninPromos,
+    SigninWithSyncError) {
+  AvatarToolbarButton* avatar_button = GetAvatarToolbarButton(browser());
+  // Normal state.
+  ASSERT_TRUE(avatar_button->GetText().empty());
+
+  SigninWithImageAndClearGreetingAndSyncPromo(avatar_button, u"test@gmail.com");
+  SimulateSyncError();
+  EXPECT_EQ(avatar_button->GetText(),
+            l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SIGNIN_PAUSED));
+
+  ClearSyncError();
+  EXPECT_EQ(avatar_button->GetText(), std::u16string());
+}
+
+#if !BUILDFLAG(IS_CHROMEOS)
+IN_PROC_BROWSER_TEST_F(
+    AvatarToolbarButtonBrowserTestWithoutReplaceSyncWithSigninPromos,
+    SigninPendingThenExplicitText) {
+  AvatarToolbarButton* avatar_button = GetAvatarToolbarButton(browser());
+  // Normal state.
+  ASSERT_TRUE(avatar_button->GetText().empty());
+
+  SigninWithImageAndClearGreetingAndSyncPromo(avatar_button, u"test@gmail.com");
+  SimulateSigninPending(/*web_sign_out=*/false);
+  ASSERT_EQ(avatar_button->GetText(),
+            l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SIGNIN_PAUSED));
+
+  const std::u16string profile_switch_text(u"Profile Switch?");
+  base::ScopedClosureRunner hide_callback =
+      avatar_button->SetExplicitButtonState(
+          profile_switch_text, /*accessibility_label=*/std::nullopt,
+          /*explicit_action=*/std::nullopt);
+  ASSERT_EQ(avatar_button->GetText(), profile_switch_text);
+
+  // Clearing explicit text should go back to Signin Pending.
+  hide_callback.RunAndReset();
+  EXPECT_EQ(avatar_button->GetText(),
+            l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SIGNIN_PAUSED));
+}
+#endif
+
+#if !BUILDFLAG(IS_CHROMEOS)
+// Explicit text over signin pending.
+IN_PROC_BROWSER_TEST_F(
+    AvatarToolbarButtonBrowserTestWithoutReplaceSyncWithSigninPromos,
+    ExplicitTextThenSigninPending) {
+  AvatarToolbarButton* avatar_button = GetAvatarToolbarButton(browser());
+  // Normal state.
+  ASSERT_TRUE(avatar_button->GetText().empty());
+
+  SigninWithImageAndClearGreetingAndSyncPromo(avatar_button, u"test@gmail.com");
+  const std::u16string profile_switch_text(u"Profile Switch?");
+  base::ScopedClosureRunner hide_callback =
+      avatar_button->SetExplicitButtonState(
+          profile_switch_text, /*accessibility_label=*/std::nullopt,
+          /*explicit_action=*/std::nullopt);
+  ASSERT_EQ(avatar_button->GetText(), profile_switch_text);
+
+  SimulateSigninPending(/*web_sign_out=*/false);
+  // Explicit text should still be shown even if Signin Pending.
+  ASSERT_EQ(avatar_button->GetText(), profile_switch_text);
+
+  // Clearing explicit text should go back to Signin Pending.
+  hide_callback.RunAndReset();
+  EXPECT_EQ(avatar_button->GetText(),
+            l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SIGNIN_PAUSED));
+}
+#endif
+
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+
+IN_PROC_BROWSER_TEST_F(
+    AvatarToolbarButtonBrowserTestWithoutReplaceSyncWithSigninPromos,
+    PRE_SigninPendingFromWebSignoutThenRestartChrome) {
+  AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser());
+  SigninWithImageAndClearGreetingAndSyncPromo(avatar, test_email(),
+                                              test_given_name());
+
+  SimulateSigninPending(/*web_sign_out=*/true);
+  ASSERT_EQ(avatar->GetText(), std::u16string());
+}
+
+IN_PROC_BROWSER_TEST_F(
+    AvatarToolbarButtonBrowserTestWithoutReplaceSyncWithSigninPromos,
+    SigninPendingFromWebSignoutThenRestartChrome) {
+  AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser());
+  // The greetings are shown after the restart.
+  EXPECT_EQ(avatar->GetText(),
+            l10n_util::GetStringFUTF16(IDS_AVATAR_BUTTON_GREETING,
+                                       test_given_name()));
+
+  avatar->ClearActiveStateForTesting();
+  // The error text is expected to be shown even if the error delay has not
+  // reached yet.
+  EXPECT_EQ(avatar->GetText(),
+            l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SIGNIN_PAUSED));
+}
+
+// Regression test for https://crbug.com/348587566
+IN_PROC_BROWSER_TEST_F(
+    AvatarToolbarButtonBrowserTestWithoutReplaceSyncWithSigninPromos,
+    SigninPendingDelayEndedNoBrowser) {
+  ASSERT_EQ(1u, chrome::GetTotalBrowserCount());
+  AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser());
+
+  SigninWithImageAndClearGreetingAndSyncPromo(avatar, u"test@gmail.com",
+                                              u"TestName");
+  SimulateSigninPending(/*web_sign_out=*/true);
+  ASSERT_TRUE(avatar->GetText().empty());
+  Profile* profile = browser()->profile();
+
+  // Close the browser before the delay ends, but keep the profile and Chrome
+  // alive by opening an incognito browser.
+  CreateIncognitoBrowser(profile);
+  CloseBrowserSynchronously(browser());
+
+  // This simulates the delay expiry for the next browser. Instead of advancing
+  // time, we set the expected delay to 0, making the elapsed time greater than
+  // the delay for sure - simulating the delay expiry.
+  SetZeroAvatarDelayForSigninPendingText();
+
+  // Open a new browser, this should not crash.
+  Browser* new_browser = CreateBrowser(profile);
+  EXPECT_EQ(GetAvatarToolbarButton(new_browser)->GetText(),
+            l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SIGNIN_PAUSED));
+}
+
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
+
 class AvatarToolbarButtonWithInteractiveFeaturePromoBrowserTest
     : public InteractiveFeaturePromoTest,
       public AvatarToolbarButtonBaseBrowserTest {
@@ -2897,59 +2974,6 @@
   EXPECT_EQ(new_browser_avatar_button->GetText(), std::u16string());
 }
 
-IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest,
-                       PRE_SigninPendingFromWebSignoutThenRestartChrome) {
-  AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser());
-  SigninWithImageAndClearGreetingAndSyncPromo(avatar, test_email(),
-                                              test_given_name());
-
-  SimulateSigninPending(/*web_sign_out=*/true);
-  ASSERT_EQ(avatar->GetText(), std::u16string());
-}
-
-IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest,
-                       SigninPendingFromWebSignoutThenRestartChrome) {
-  AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser());
-  // The greetings are shown after the restart.
-  EXPECT_EQ(avatar->GetText(),
-            l10n_util::GetStringFUTF16(IDS_AVATAR_BUTTON_GREETING,
-                                       test_given_name()));
-
-  avatar->ClearActiveStateForTesting();
-  // The error text is expected to be shown even if the error delay has not
-  // reached yet.
-  EXPECT_EQ(avatar->GetText(),
-            l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SIGNIN_PAUSED));
-}
-
-// Regression test for https://crbug.com/348587566
-IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest,
-                       SigninPendingDelayEndedNoBrowser) {
-  ASSERT_EQ(1u, chrome::GetTotalBrowserCount());
-  AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser());
-
-  SigninWithImageAndClearGreetingAndSyncPromo(avatar, u"test@gmail.com",
-                                              u"TestName");
-  SimulateSigninPending(/*web_sign_out=*/true);
-  ASSERT_TRUE(avatar->GetText().empty());
-  Profile* profile = browser()->profile();
-
-  // Close the browser before the delay ends, but keep the profile and Chrome
-  // alive by opening an incognito browser.
-  CreateIncognitoBrowser(profile);
-  CloseBrowserSynchronously(browser());
-
-  // This simulates the delay expiry for the next browser. Instead of advancing
-  // time, we set the expected delay to 0, making the elapsed time greater than
-  // the delay for sure - simulating the delay expiry.
-  SetZeroAvatarDelayForSigninPendingText();
-
-  // Open a new browser, this should not crash.
-  Browser* new_browser = CreateBrowser(profile);
-  EXPECT_EQ(GetAvatarToolbarButton(new_browser)->GetText(),
-            l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SIGNIN_PAUSED));
-}
-
 // TODO(b/331746545): Check flaky test issue on windows.
 #if BUILDFLAG(IS_WIN)
 #define MAYBE_SigninPendingThenSignout DISABLED_SigninPendingThenSignout
diff --git a/chrome/browser/ui/views/profiles/first_run_interactive_uitest.cc b/chrome/browser/ui/views/profiles/first_run_interactive_uitest.cc
index 5549a3b..680a088 100644
--- a/chrome/browser/ui/views/profiles/first_run_interactive_uitest.cc
+++ b/chrome/browser/ui/views/profiles/first_run_interactive_uitest.cc
@@ -52,6 +52,7 @@
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/identity_test_utils.h"
+#include "components/sync/base/features.h"
 #include "components/user_education/views/help_bubble_view.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -209,7 +210,12 @@
       : InteractiveFeaturePromoTestT<FirstRunServiceBrowserTestBase>(
             UseDefaultTrackerAllowingPromos(
                 {feature_engagement::kIPHSupervisedUserProfileSigninFeature})),
-        params_(params) {}
+        params_(params) {
+    // TODO(crbug.com/447151253): Fix the tests to work with the feature
+    // enabled.
+    scoped_feature_list_.InitAndDisableFeature(
+        syncer::kReplaceSyncPromosWithSignInPromos);
+  }
   ~FirstRunInteractiveUiTest() override = default;
 
  protected:
@@ -433,6 +439,7 @@
 
  private:
   TestParam params_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 
   ChromeSigninClientWithURLLoaderHelper url_loader_factory_helper_;
   base::HistogramTester histogram_tester_;
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
index be6eede2..0e362ad 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
@@ -2969,7 +2969,12 @@
       public testing::WithParamInterface<
           LoginUIService::SyncConfirmationUIClosedResult> {
  public:
-  SupervisedUserProfileIPHTest() = default;
+  SupervisedUserProfileIPHTest() {
+    // TODO(crbug.com/447099373): Fix the tests to work with the feature
+    // enabled.
+    scoped_feature_list_.InitAndDisableFeature(
+        syncer::kReplaceSyncPromosWithSignInPromos);
+  }
 
  protected:
   LoginUIService::SyncConfirmationUIClosedResult GetSyncConfirmationResult() {
@@ -2981,6 +2986,8 @@
     return HasPromoBeenShown(
         browser, feature_engagement::kIPHSupervisedUserProfileSigninFeature);
   }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 std::string SyncConfirmationResultToString(
@@ -3750,8 +3757,11 @@
                        SyncConfirmationExitChromeTest) {
   // Simulate a successful sign-in and wait for the sign-in to propagate to the
   // flow, resulting in sync confirmation screen getting displayed.
-  SignInForNewProfile(GetSyncConfirmationURL(), "joe.consumer@gmail.com",
-                      "Joe");
+  GURL target_url =
+      base::FeatureList::IsEnabled(syncer::kReplaceSyncPromosWithSignInPromos)
+          ? GetHistorySyncOptinURL()
+          : GetSyncConfirmationURL();
+  SignInForNewProfile(target_url, "joe.consumer@gmail.com", "Joe");
   EXPECT_TRUE(ProfilePicker::IsOpen());
 
   // Exit the sync confirmation view (Cmd-Q).
diff --git a/chrome/browser/ui/views/web_apps/OWNERS b/chrome/browser/ui/views/web_apps/OWNERS
index 1255a2c..bc96c62 100644
--- a/chrome/browser/ui/views/web_apps/OWNERS
+++ b/chrome/browser/ui/views/web_apps/OWNERS
@@ -1 +1,3 @@
 file://chrome/browser/web_applications/OWNERS
+
+per-file protocol_handler_picker*=greengrape@google.com
diff --git a/chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.cc b/chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.cc
new file mode 100644
index 0000000..d1f4d46
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.cc
@@ -0,0 +1,169 @@
+// 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/web_apps/protocol_handler_picker_coordinator.h"
+
+#include <string>
+#include <vector>
+
+#include "base/barrier_callback.h"
+#include "base/containers/contains.h"
+#include "base/functional/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.h"
+#include "components/services/app_service/public/cpp/app_update.h"
+#include "components/services/app_service/public/cpp/icon_types.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/models/image_model.h"
+#include "url/gurl.h"
+
+namespace {
+
+constexpr int kIconSizeInDip = 32;
+
+void LaunchApp(apps::AppServiceProxy* proxy,
+               const std::string& app_id,
+               const GURL& protocol_url) {
+  apps::AppLaunchParams params(app_id,
+                               apps::LaunchContainer::kLaunchContainerWindow,
+                               WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                               apps::LaunchSource::kFromProtocolHandler);
+  params.protocol_handler_launch_url = protocol_url;
+  proxy->LaunchAppWithParams(std::move(params));
+}
+
+void MaybeCloseWebContentsAsync(content::WebContents* web_contents) {
+  tabs::TabInterface* tab =
+      tabs::TabInterface::MaybeGetFromContents(web_contents);
+  if (!tab) {
+    return;
+  }
+  TabStripModel* tab_strip_model =
+      tab->GetBrowserWindowInterface()->GetTabStripModel();
+  // If there's more than one tab in the browser corresponding to the current
+  // tab and the current tab still in the initial navigation state, it's
+  // expected to be closed.
+  if (tab_strip_model->GetIndexOfTab(tab) != TabStripModel::kNoTab &&
+      tab_strip_model->count() > 1 &&
+      web_contents->GetController().IsInitialNavigation()) {
+    content::GetUIThreadTaskRunner({})->PostTask(
+        FROM_HERE, base::BindOnce(&content::WebContents::Close,
+                                  web_contents->GetWeakPtr()));
+  }
+}
+
+}  // namespace
+
+namespace web_app {
+
+void ProtocolHandlerPickerCoordinator::ShowPicker(
+    const GURL& protocol_url,
+    const std::vector<std::string>& app_ids,
+    const std::optional<url::Origin>& initiator_origin) {
+  GatherAppData(protocol_url, app_ids, initiator_origin);
+}
+
+ProtocolHandlerPickerCoordinator::ProtocolHandlerPickerCoordinator(
+    content::WebContents* web_contents)
+    : content::WebContentsUserData<ProtocolHandlerPickerCoordinator>(
+          *web_contents),
+      proxy_(apps::AppServiceProxyFactory::GetForProfile(
+          Profile::FromBrowserContext(web_contents->GetBrowserContext()))) {
+  CHECK(proxy_) << " AppServiceProxy must exist for this instance of "
+                   "WebContents; the caller is responsible for ensuring this.";
+}
+
+ProtocolHandlerPickerCoordinator::~ProtocolHandlerPickerCoordinator() = default;
+
+void ProtocolHandlerPickerCoordinator::GatherAppData(
+    const GURL& protocol_url,
+    const std::vector<std::string>& app_ids,
+    const std::optional<url::Origin>& initiator_origin) {
+  CHECK(!app_ids.empty());
+  auto all_apps_collected_callback =
+      base::BarrierCallback<ProtocolHandlerPickerDialogEntry>(
+          app_ids.size(),
+          base::BindOnce(
+              &ProtocolHandlerPickerCoordinator::ShowPickerWithEntries,
+              weak_factory_.GetWeakPtr(), protocol_url, initiator_origin));
+
+  for (const std::string& app_id : app_ids) {
+    std::u16string app_name;
+    proxy_->AppRegistryCache().ForOneApp(
+        app_id, [&app_name](const apps::AppUpdate& update) {
+          app_name = base::UTF8ToUTF16(update.Name());
+        });
+
+    proxy_->LoadIcon(
+        app_id, apps::IconType::kStandard, kIconSizeInDip,
+        /*allow_placeholder_icon=*/false,
+        base::BindOnce(
+            [](const std::string& app_id, const std::u16string& app_name,
+               apps::IconValuePtr icon_value)
+                -> ProtocolHandlerPickerDialogEntry {
+              return {app_id, app_name,
+                      ui::ImageModel::FromImageSkia(icon_value->uncompressed)};
+            },
+            app_id, app_name)
+            .Then(all_apps_collected_callback));
+  }
+}
+
+void ProtocolHandlerPickerCoordinator::ShowPickerWithEntries(
+    const GURL& protocol_url,
+    const std::optional<url::Origin>& initiator_origin,
+    ProtocolHandlerPickerDialogEntries app_entries) {
+  auto dialog_model = CreateProtocolHandlerPickerDialog(
+      protocol_url, app_entries, initiator_origin,
+      base::BindOnce(&ProtocolHandlerPickerCoordinator::OnPickerClosed,
+                     weak_factory_.GetWeakPtr(), protocol_url));
+  // TODO(cbug.com/422422887): Attach `dialog_model` to `tab_dialog_manager()`.
+}
+
+void ProtocolHandlerPickerCoordinator::OnPickerClosed(
+    const GURL& protocol_url,
+    std::optional<ProtocolHandlerPickerDialogResult> result) {
+  if (result) {
+    const auto& app_id = result->selected_app_id;
+    if (result->remember_choice) {
+      // TODO(cbug.com/422422887): Store the user pref in PreferredAppsList.
+    }
+    LaunchApp(proxy_, app_id, protocol_url);
+  }
+
+  MaybeCloseWebContentsAsync(&GetWebContents());
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(ProtocolHandlerPickerCoordinator);
+
+void LaunchProtocolUrlInPreferredApp(
+    content::WebContents* web_contents,
+    const GURL& protocol_url,
+    const std::vector<std::string>& app_ids,
+    const std::optional<url::Origin>& initiator_origin) {
+  auto* proxy = apps::AppServiceProxyFactory::GetForProfile(
+      Profile::FromBrowserContext(web_contents->GetBrowserContext()));
+  if (std::optional<std::string> app_id =
+          proxy->PreferredAppsList().FindPreferredAppForUrl(protocol_url)) {
+    if (base::Contains(app_ids, *app_id)) {
+      LaunchApp(proxy, *app_id, protocol_url);
+      MaybeCloseWebContentsAsync(web_contents);
+      return;
+    }
+  }
+
+  ProtocolHandlerPickerCoordinator::GetOrCreateForWebContents(web_contents)
+      ->ShowPicker(protocol_url, app_ids, initiator_origin);
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.h b/chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.h
new file mode 100644
index 0000000..91499c36
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.h
@@ -0,0 +1,71 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_WEB_APPS_PROTOCOL_HANDLER_PICKER_COORDINATOR_H_
+#define CHROME_BROWSER_UI_VIEWS_WEB_APPS_PROTOCOL_HANDLER_PICKER_COORDINATOR_H_
+
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_forward.h"
+#include "chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "ui/views/widget/widget.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace web_app {
+
+// This class manages the UI flow for picking a web app to handle a protocol
+// request. Its lifetime is tied to the WebContents it's attached to.
+class ProtocolHandlerPickerCoordinator
+    : public content::WebContentsUserData<ProtocolHandlerPickerCoordinator> {
+ public:
+  ~ProtocolHandlerPickerCoordinator() override;
+
+  void ShowPicker(const GURL& protocol_url,
+                  const std::vector<std::string>& app_ids,
+                  const std::optional<url::Origin>& initiating_origin);
+
+ private:
+  friend class content::WebContentsUserData<ProtocolHandlerPickerCoordinator>;
+
+  explicit ProtocolHandlerPickerCoordinator(content::WebContents* web_contents);
+
+  void GatherAppData(const GURL& protocol_url,
+                     const std::vector<std::string>& app_ids,
+                     const std::optional<url::Origin>& initiating_origin);
+  void ShowPickerWithEntries(
+      const GURL& protocol_url,
+      const std::optional<url::Origin>& initiating_origin,
+      ProtocolHandlerPickerDialogEntries app_entries);
+  void OnPickerClosed(const GURL& protocol_url,
+                      std::optional<ProtocolHandlerPickerDialogResult> result);
+  bool HasOpenDialogWidget() const;
+
+  const raw_ptr<apps::AppServiceProxy> proxy_;
+
+  // Owns the currently opened dialog for this tab.
+  std::unique_ptr<views::Widget> dialog_;
+
+  base::WeakPtrFactory<ProtocolHandlerPickerCoordinator> weak_factory_{this};
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+// If there's a preferred app among `app_ids` for handling `protocol_url`,
+// launches this app directly; otherwise shows the protocol handler picker
+// dialog.
+void LaunchProtocolUrlInPreferredApp(
+    content::WebContents* web_contents,
+    const GURL& protocol_url,
+    const std::vector<std::string>& app_ids,
+    const std::optional<url::Origin>& initiator_origin);
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_UI_VIEWS_WEB_APPS_PROTOCOL_HANDLER_PICKER_COORDINATOR_H_
diff --git a/chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.cc b/chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.cc
new file mode 100644
index 0000000..b17be8b
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.cc
@@ -0,0 +1,21 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.h"
+
+#include <optional>
+
+namespace web_app {
+
+std::unique_ptr<ui::DialogModel> CreateProtocolHandlerPickerDialog(
+    const GURL& protocol_url,
+    const ProtocolHandlerPickerDialogEntries& apps,
+    const std::optional<url::Origin>& initiator_origin,
+    OnPickerClosedCallback callback) {
+  // TODO(crbug.com/422422887): Implement the dialog.
+  std::move(callback).Run({{.selected_app_id = apps[0].app_id}});
+  return nullptr;
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.h b/chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.h
new file mode 100644
index 0000000..a1d62d9
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.h
@@ -0,0 +1,50 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_WEB_APPS_PROTOCOL_HANDLER_PICKER_DIALOG_H_
+#define CHROME_BROWSER_UI_VIEWS_WEB_APPS_PROTOCOL_HANDLER_PICKER_DIALOG_H_
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "base/functional/callback_forward.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/models/dialog_model.h"
+#include "ui/base/models/image_model.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace web_app {
+
+struct ProtocolHandlerPickerDialogEntry {
+  std::string app_id;
+  std::u16string app_name;
+  ui::ImageModel icon;
+};
+
+using ProtocolHandlerPickerDialogEntries =
+    std::vector<ProtocolHandlerPickerDialogEntry>;
+
+struct ProtocolHandlerPickerDialogResult {
+  std::string selected_app_id;
+  bool remember_choice;
+};
+
+// The callback to be run when the dialog is closed for any reason.
+// The optional will be empty (std::nullopt) if the user canceled or the dialog
+// was destroyed for some reason.
+using OnPickerClosedCallback =
+    base::OnceCallback<void(std::optional<ProtocolHandlerPickerDialogResult>)>;
+
+std::unique_ptr<ui::DialogModel> CreateProtocolHandlerPickerDialog(
+    const GURL& protocol_url,
+    const ProtocolHandlerPickerDialogEntries& apps,
+    const std::optional<url::Origin>& initiator_origin,
+    OnPickerClosedCallback callback);
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_UI_VIEWS_WEB_APPS_PROTOCOL_HANDLER_PICKER_DIALOG_H_
diff --git a/chrome/browser/url_constants/android/BUILD.gn b/chrome/browser/url_constants/android/BUILD.gn
index a24ac20..7ae0af8 100644
--- a/chrome/browser/url_constants/android/BUILD.gn
+++ b/chrome/browser/url_constants/android/BUILD.gn
@@ -18,6 +18,26 @@
     "//chrome/browser/preferences:java",
     "//chrome/browser/profiles/android:java",
     "//components/embedder_support/android:util_java",
+    "//third_party/androidx:androidx_annotation_annotation_java",
     "//url:gurl_java",
   ]
 }
+
+robolectric_library("junit") {
+  sources = [
+    "java/src/org/chromium/chrome/browser/url_constants/UrlConstantResolverFactoryUnitTest.java",
+    "java/src/org/chromium/chrome/browser/url_constants/UrlConstantResolverUnitTest.java",
+  ]
+  deps = [
+    ":java",
+    "//base:base_java",
+    "//base:base_java_test_support",
+    "//base:base_junit_test_support",
+    "//chrome/browser/flags:java",
+    "//chrome/browser/preferences:java",
+    "//chrome/browser/profiles/android:java",
+    "//components/embedder_support/android:util_java",
+    "//third_party/junit",
+    "//third_party/mockito:mockito_java",
+  ]
+}
diff --git a/chrome/browser/url_constants/android/java/src/org/chromium/chrome/browser/url_constants/UrlConstantResolverFactory.java b/chrome/browser/url_constants/android/java/src/org/chromium/chrome/browser/url_constants/UrlConstantResolverFactory.java
index 8035bb6..5d122442 100644
--- a/chrome/browser/url_constants/android/java/src/org/chromium/chrome/browser/url_constants/UrlConstantResolverFactory.java
+++ b/chrome/browser/url_constants/android/java/src/org/chromium/chrome/browser/url_constants/UrlConstantResolverFactory.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.url_constants;
 
+import androidx.annotation.VisibleForTesting;
+
 import org.chromium.base.ResettersForTesting;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
@@ -24,7 +26,7 @@
     // Prevent instantiation.
     private UrlConstantResolverFactory() {}
 
-    public UrlConstantResolver getForProfile(@Nullable Profile profile) {
+    public static UrlConstantResolver getForProfile(@Nullable Profile profile) {
         if (sResolverForTesting != null) {
             return sResolverForTesting;
         }
@@ -98,4 +100,10 @@
         sResolverForTesting = resolver;
         ResettersForTesting.register(() -> sResolverForTesting = null);
     }
+
+    @VisibleForTesting
+    public static void resetResolvers() {
+        sOriginalResolver = null;
+        sIncognitoResolver = null;
+    }
 }
diff --git a/chrome/browser/url_constants/android/java/src/org/chromium/chrome/browser/url_constants/UrlConstantResolverFactoryUnitTest.java b/chrome/browser/url_constants/android/java/src/org/chromium/chrome/browser/url_constants/UrlConstantResolverFactoryUnitTest.java
new file mode 100644
index 0000000..84cec14
--- /dev/null
+++ b/chrome/browser/url_constants/android/java/src/org/chromium/chrome/browser/url_constants/UrlConstantResolverFactoryUnitTest.java
@@ -0,0 +1,164 @@
+// 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.url_constants;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.when;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features.DisableFeatures;
+import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.components.embedder_support.util.UrlConstants;
+
+/** Unit tests for {@link UrlConstantResolverFactory}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@EnableFeatures({ChromeFeatureList.CHROME_NATIVE_URL_OVERRIDING})
+public class UrlConstantResolverFactoryUnitTest {
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock private Profile mProfile;
+
+    @After
+    public void tearDown() throws Exception {
+        ExtensionsUrlOverrideRegistry.setNtpOverrideEnabled(false);
+        ExtensionsUrlOverrideRegistry.setIncognitoNtpOverrideEnabled(false);
+        ExtensionsUrlOverrideRegistry.setHistoryPageOverrideEnabled(false);
+        ExtensionsUrlOverrideRegistry.setBookmarksPageOverrideEnabled(false);
+        ExtensionsUrlOverrideRegistry.setIncognitoBookmarksPageOverrideEnabled(false);
+        UrlConstantResolverFactory.resetResolvers();
+    }
+
+    @Test
+    public void testGetForProfile_NullProfile() {
+        UrlConstantResolver resolver = UrlConstantResolverFactory.getForProfile(null);
+        assertNotNull(resolver);
+    }
+
+    @Test
+    public void testGetForProfile_RegularProfile() {
+        UrlConstantResolver originalResolver = UrlConstantResolverFactory.getForProfile(null);
+        UrlConstantResolver profileResolver = UrlConstantResolverFactory.getForProfile(mProfile);
+        assertEquals(originalResolver, profileResolver);
+    }
+
+    @Test
+    public void testGetForProfile_IncognitoProfile() {
+        UrlConstantResolver originalResolver = UrlConstantResolverFactory.getForProfile(mProfile);
+
+        when(mProfile.isIncognitoBranded()).thenReturn(true);
+        UrlConstantResolver incognitoResolver = UrlConstantResolverFactory.getForProfile(mProfile);
+
+        assertNotEquals(originalResolver, incognitoResolver);
+    }
+
+    @Test
+    public void testSetForTesting() {
+        UrlConstantResolver testResolver = new UrlConstantResolver();
+        UrlConstantResolverFactory.setForTesting(testResolver);
+
+        assertEquals(testResolver, UrlConstantResolverFactory.getForProfile(null));
+        assertEquals(testResolver, UrlConstantResolverFactory.getForProfile(mProfile));
+
+        when(mProfile.isIncognitoBranded()).thenReturn(true);
+        assertEquals(testResolver, UrlConstantResolverFactory.getForProfile(mProfile));
+    }
+
+    @Test
+    @DisableFeatures({ChromeFeatureList.CHROME_NATIVE_URL_OVERRIDING})
+    public void testOriginalResolver_FeatureDisabled() {
+        UrlConstantResolver resolver = UrlConstantResolverFactory.getForProfile(mProfile);
+        assertEquals(UrlConstants.NTP_URL, resolver.getNtpUrl());
+        assertEquals(UrlConstants.BOOKMARKS_NATIVE_URL, resolver.getBookmarksNativeUrl());
+        assertEquals(UrlConstants.NATIVE_HISTORY_URL, resolver.getNativeHistoryUrl());
+    }
+
+    @Test
+    public void testOriginalResolver_NtpOverride() {
+        UrlConstantResolver resolver = UrlConstantResolverFactory.getForProfile(mProfile);
+
+        ExtensionsUrlOverrideRegistry.setNtpOverrideEnabled(true);
+        assertEquals(UrlConstants.NTP_NON_NATIVE_URL, resolver.getNtpUrl());
+
+        ExtensionsUrlOverrideRegistry.setNtpOverrideEnabled(false);
+        assertEquals(UrlConstants.NTP_URL, resolver.getNtpUrl());
+    }
+
+    @Test
+    public void testOriginalResolver_BookmarksOverride() {
+        UrlConstantResolver resolver = UrlConstantResolverFactory.getForProfile(mProfile);
+
+        ExtensionsUrlOverrideRegistry.setBookmarksPageOverrideEnabled(true);
+        assertEquals(UrlConstants.BOOKMARKS_URL, resolver.getBookmarksNativeUrl());
+
+        ExtensionsUrlOverrideRegistry.setBookmarksPageOverrideEnabled(false);
+        assertEquals(UrlConstants.BOOKMARKS_NATIVE_URL, resolver.getBookmarksNativeUrl());
+    }
+
+    @Test
+    public void testOriginalResolver_HistoryOverride() {
+        UrlConstantResolver resolver = UrlConstantResolverFactory.getForProfile(mProfile);
+
+        ExtensionsUrlOverrideRegistry.setHistoryPageOverrideEnabled(true);
+        assertEquals(UrlConstants.HISTORY_URL, resolver.getNativeHistoryUrl());
+
+        ExtensionsUrlOverrideRegistry.setHistoryPageOverrideEnabled(false);
+        assertEquals(UrlConstants.NATIVE_HISTORY_URL, resolver.getNativeHistoryUrl());
+    }
+
+    @Test
+    @DisableFeatures({ChromeFeatureList.CHROME_NATIVE_URL_OVERRIDING})
+    public void testIncognitoResolver_FeatureDisabled() {
+        when(mProfile.isIncognitoBranded()).thenReturn(true);
+        UrlConstantResolver resolver = UrlConstantResolverFactory.getForProfile(mProfile);
+        assertEquals(UrlConstants.NTP_URL, resolver.getNtpUrl());
+        assertEquals(UrlConstants.BOOKMARKS_NATIVE_URL, resolver.getBookmarksNativeUrl());
+        assertEquals(UrlConstants.NATIVE_HISTORY_URL, resolver.getNativeHistoryUrl());
+    }
+
+    @Test
+    public void testIncognitoResolver_NtpOverride() {
+        when(mProfile.isIncognitoBranded()).thenReturn(true);
+        UrlConstantResolver resolver = UrlConstantResolverFactory.getForProfile(mProfile);
+
+        ExtensionsUrlOverrideRegistry.setIncognitoNtpOverrideEnabled(true);
+        assertEquals(UrlConstants.NTP_NON_NATIVE_URL, resolver.getNtpUrl());
+
+        ExtensionsUrlOverrideRegistry.setIncognitoNtpOverrideEnabled(false);
+        assertEquals(UrlConstants.NTP_URL, resolver.getNtpUrl());
+    }
+
+    @Test
+    public void testIncognitoResolver_BookmarksOverride() {
+        when(mProfile.isIncognitoBranded()).thenReturn(true);
+        UrlConstantResolver resolver = UrlConstantResolverFactory.getForProfile(mProfile);
+
+        ExtensionsUrlOverrideRegistry.setIncognitoBookmarksPageOverrideEnabled(true);
+        assertEquals(UrlConstants.BOOKMARKS_URL, resolver.getBookmarksNativeUrl());
+
+        ExtensionsUrlOverrideRegistry.setIncognitoBookmarksPageOverrideEnabled(false);
+        assertEquals(UrlConstants.BOOKMARKS_NATIVE_URL, resolver.getBookmarksNativeUrl());
+    }
+
+    @Test
+    public void testIncognitoResolver_NoHistoryOverride() {
+        when(mProfile.isIncognitoBranded()).thenReturn(true);
+        UrlConstantResolver resolver = UrlConstantResolverFactory.getForProfile(mProfile);
+
+        ExtensionsUrlOverrideRegistry.setHistoryPageOverrideEnabled(true);
+        assertEquals(UrlConstants.NATIVE_HISTORY_URL, resolver.getNativeHistoryUrl());
+    }
+}
diff --git a/chrome/browser/url_constants/android/java/src/org/chromium/chrome/browser/url_constants/UrlConstantResolverUnitTest.java b/chrome/browser/url_constants/android/java/src/org/chromium/chrome/browser/url_constants/UrlConstantResolverUnitTest.java
new file mode 100644
index 0000000..4c98fd8
--- /dev/null
+++ b/chrome/browser/url_constants/android/java/src/org/chromium/chrome/browser/url_constants/UrlConstantResolverUnitTest.java
@@ -0,0 +1,77 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.url_constants;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.components.embedder_support.util.UrlConstants;
+
+/** Unit tests for {@link UrlConstantResolver}. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class UrlConstantResolverUnitTest {
+    private static final String OVERRIDE_URL = "OVERRIDE";
+    private UrlConstantResolver mResolver;
+
+    @Before
+    public void setUp() {
+        mResolver = new UrlConstantResolver();
+    }
+
+    @Test
+    public void testGetNtpUrl_NoOverride() {
+        assertEquals(UrlConstants.NTP_URL, mResolver.getNtpUrl());
+    }
+
+    @Test
+    public void testGetBookmarksNativeUrl_NoOverride() {
+        assertEquals(UrlConstants.BOOKMARKS_NATIVE_URL, mResolver.getBookmarksNativeUrl());
+    }
+
+    @Test
+    public void testGetNativeHistoryUrl_NoOverride() {
+        assertEquals(UrlConstants.NATIVE_HISTORY_URL, mResolver.getNativeHistoryUrl());
+    }
+
+    @Test
+    public void testGetNtpUrl_WithOverrideEnabled() {
+        mResolver.registerOverride(UrlConstants.NTP_URL, () -> OVERRIDE_URL);
+        assertEquals(OVERRIDE_URL, mResolver.getNtpUrl());
+    }
+
+    @Test
+    public void testGetNtpUrl_WithOverrideDisabled() {
+        mResolver.registerOverride(UrlConstants.NTP_URL, () -> null);
+        assertEquals(UrlConstants.NTP_URL, mResolver.getNtpUrl());
+    }
+
+    @Test
+    public void testGetBookmarksNativeUrl_WithOverrideEnabled() {
+        mResolver.registerOverride(UrlConstants.BOOKMARKS_NATIVE_URL, () -> OVERRIDE_URL);
+        assertEquals(OVERRIDE_URL, mResolver.getBookmarksNativeUrl());
+    }
+
+    @Test
+    public void testGetBookmarksNativeUrl_WithOverrideDisabled() {
+        mResolver.registerOverride(UrlConstants.BOOKMARKS_NATIVE_URL, () -> null);
+        assertEquals(UrlConstants.BOOKMARKS_NATIVE_URL, mResolver.getBookmarksNativeUrl());
+    }
+
+    @Test
+    public void testGetNativeHistoryUrl_WithOverrideEnabled() {
+        mResolver.registerOverride(UrlConstants.NATIVE_HISTORY_URL, () -> OVERRIDE_URL);
+        assertEquals(OVERRIDE_URL, mResolver.getNativeHistoryUrl());
+    }
+
+    @Test
+    public void testGetNativeHistoryUrl_WithOverrideDisabled() {
+        mResolver.registerOverride(UrlConstants.NATIVE_HISTORY_URL, () -> null);
+        assertEquals(UrlConstants.NATIVE_HISTORY_URL, mResolver.getNativeHistoryUrl());
+    }
+}
diff --git a/chrome/browser/web_applications/isolated_web_apps/browser_navigator_iwa_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/browser_navigator_iwa_browsertest.cc
index 80af07ba..ecde73d 100644
--- a/chrome/browser/web_applications/isolated_web_apps/browser_navigator_iwa_browsertest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/browser_navigator_iwa_browsertest.cc
@@ -225,9 +225,6 @@
   {
     // Eliminate all prompts/guards along the way.
     ExternalProtocolHandler::PermitLaunchUrl();
-    ExternalProtocolHandler::SetBlockState("meow", url_info2_->origin(),
-                                           ExternalProtocolHandler::DONT_BLOCK,
-                                           profile());
     base::test::TestFuture<void> future;
     web_app::WebAppProvider::GetForWebApps(profile())
         ->scheduler()
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 19df498..fb5d62e8 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1758866082-8a235d1b8b489bc36d6266e9b9e820074eac66d6-f6e9c60194c98e19bf0d98a25f997e346965b892.profdata
+chrome-android32-main-1758887821-8eff871eb517cf601ff9146ee154e4c494fe9ee7-8f33ed4a83774b89e76819368c55ce9c1830b42f.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 4718ccd8..2b8c4a1f 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1758849749-5e944251df227dd9fd2a9f28e4b7849ac21ff72a-40536ec79d7b29117543aac3a29c3919a54660bc.profdata
+chrome-android64-main-1758878852-4a739e060ae2052cc127c24dfcbbe625ccf6d3d4-4cb457723ca1adc9e010a69e18f3142cd85f3ec8.profdata
diff --git a/chrome/build/android-desktop-x64.pgo.txt b/chrome/build/android-desktop-x64.pgo.txt
index 88e656c..936a164 100644
--- a/chrome/build/android-desktop-x64.pgo.txt
+++ b/chrome/build/android-desktop-x64.pgo.txt
@@ -1 +1 @@
-chrome-android-desktop-x64-main-1758866082-16e3d2d42334928e6d7f22ff9d9bd3f8842ece15-f6e9c60194c98e19bf0d98a25f997e346965b892.profdata
+chrome-android-desktop-x64-main-1758887821-febfc698c9d7c2f08f00a5346944f9a307ecd5b0-8f33ed4a83774b89e76819368c55ce9c1830b42f.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index f81c925..4ee2012 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1758823188-05d75ff3bbf3ae7154dd2521ff9cef50fe4046d7-4390848015957f42af7b44583fb3546d877dd0fe.profdata
+chrome-linux-main-1758887821-86d46083f409d45f51fb0e3a1f4e5ff85b211d2d-8f33ed4a83774b89e76819368c55ce9c1830b42f.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 1332894..ad961c53 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1758866082-cd64dd224d20a46943591037dbc0ffbd76f8790e-f6e9c60194c98e19bf0d98a25f997e346965b892.profdata
+chrome-mac-arm-main-1758887821-62ae5e470342e33475155b7843d41e05a8d9a411-8f33ed4a83774b89e76819368c55ce9c1830b42f.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 1cfcd9f8..f68a875a 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1758844681-67386715ca4f45612a51d15eca7f405057965522-cb37e57bbc308979e611a16e766617becb7d88ee.profdata
+chrome-mac-main-1758866082-6a501898643195544ae9f5e867c8287fe7ae0a0f-f6e9c60194c98e19bf0d98a25f997e346965b892.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 2c5315a..8917a862 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1758844681-0022566591801490467fae70fc26dafecc01d7c8-cb37e57bbc308979e611a16e766617becb7d88ee.profdata
+chrome-win-arm64-main-1758887821-20c09da6739ec79a43fb97f1cf557715eb35244b-8f33ed4a83774b89e76819368c55ce9c1830b42f.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 2d4663f..e7393460 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1758855354-6ac655027b571e5cad18939a1c7ffd16babdfa02-ec56c443b711d9d2ece93b672095b6607c54fec5.profdata
+chrome-win32-main-1758866082-30f8627a468a722377fd2e8965656d47ee1cbf0a-f6e9c60194c98e19bf0d98a25f997e346965b892.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 5ab926b..42ce2ee 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1758833983-68b3e696cecebcdcb767539b3830aec861198962-7499615ba103a5665f1df5180f134e252f987363.profdata
+chrome-win64-main-1758866082-c76bdda257d628a8121f0b7a289b8a11ef7b1aa1-f6e9c60194c98e19bf0d98a25f997e346965b892.profdata
diff --git a/chrome/renderer/accessibility/phrase_segmentation/dependency_parser_op_resolver.cc b/chrome/renderer/accessibility/phrase_segmentation/dependency_parser_op_resolver.cc
index 623dd12..89ce23d7 100644
--- a/chrome/renderer/accessibility/phrase_segmentation/dependency_parser_op_resolver.cc
+++ b/chrome/renderer/accessibility/phrase_segmentation/dependency_parser_op_resolver.cc
@@ -43,12 +43,10 @@
       seq_flow_lite::ops::custom::Register_SEQUENCE_STRING_PROJECTION_V2());
 
 #if BUILDFLAG(BUILD_TFLITE_WITH_XNNPACK)
-  if (optimization_guide::features::TFLiteXNNPACKDelegateEnabled()) {
-    delegate_creators_.push_back([](TfLiteContext* context) {
-      return tflite::MaybeCreateXNNPACKDelegate(
-          context, tflite::XNNPackQS8Options::default_value);
-    });
-  }
+  delegate_creators_.push_back([](TfLiteContext* context) {
+    return tflite::MaybeCreateXNNPACKDelegate(
+        context, tflite::XNNPackQS8Options::default_value);
+  });
 #endif
 }
 DependencyParserOpResolver::~DependencyParserOpResolver() = default;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 33fe0a10..cc0564bf 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3274,6 +3274,7 @@
       "../browser/engagement/site_engagement_helper_browsertest.cc",
       "../browser/enterprise/browser_management/management_service_browsertest.cc",
       "../browser/enterprise/incognito/incognito_navigation_throttle_browsertest.cc",
+      "../browser/enterprise/reporting/extension_request/extension_request_notification_browsertest.cc",
       "../browser/enterprise/signals/profile_signals_collector_browsertest.cc",
       "../browser/enterprise/util/managed_browser_utils_browsertest.cc",
       "../browser/enterprise/watermark/watermark_browsertest.cc",
@@ -8058,7 +8059,6 @@
       "../browser/download/download_ui_enterprise_util_unittest.cc",
       "../browser/download/download_warning_desktop_hats_utils_unittest.cc",
       "../browser/enterprise/reporting/extension_info_unittest.cc",
-      "../browser/enterprise/reporting/extension_request/extension_request_notification_unittest.cc",
       "../browser/enterprise/reporting/extension_request/extension_request_observer_factory_unittest.cc",
       "../browser/enterprise/reporting/extension_request/extension_request_observer_unittest.cc",
       "../browser/enterprise/reporting/extension_request/extension_request_report_generator_unittest.cc",
diff --git a/chrome/test/data/webui/history/history_ui_browsertest.cc b/chrome/test/data/webui/history/history_ui_browsertest.cc
index 627a5408..c37b858 100644
--- a/chrome/test/data/webui/history/history_ui_browsertest.cc
+++ b/chrome/test/data/webui/history/history_ui_browsertest.cc
@@ -7,6 +7,7 @@
 #include "chrome/test/base/web_ui_mocha_browser_test.h"
 #include "components/history_clusters/core/features.h"
 #include "components/history_embeddings/history_embeddings_features.h"
+#include "components/sync/base/features.h"
 #include "content/public/test/browser_test.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
@@ -50,10 +51,6 @@
   RunTest("history/history_routing_with_query_param_test.js", "mocha.run()");
 }
 
-IN_PROC_BROWSER_TEST_F(HistoryTest, SyncedTabs) {
-  RunTest("history/history_synced_tabs_test.js", "mocha.run()");
-}
-
 IN_PROC_BROWSER_TEST_F(HistoryTest, Toolbar) {
   RunTest("history/history_toolbar_test.js", "mocha.run()");
 }
@@ -70,6 +67,24 @@
   RunTest("history/history_side_bar_footer_test.js", "mocha.run()");
 }
 
+// TODO(crbug.com/444617758): Fix the test to work with the feature enabled and
+// move it back to HistoryTest.
+class HistoryTestWithoutReplaceSyncPromosWithSignInPromos : public HistoryTest {
+ public:
+  HistoryTestWithoutReplaceSyncPromosWithSignInPromos() {
+    scoped_feature_list_.InitAndDisableFeature(
+        syncer::kReplaceSyncPromosWithSignInPromos);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(HistoryTestWithoutReplaceSyncPromosWithSignInPromos,
+                       SyncedTabs) {
+  RunTest("history/history_synced_tabs_test.js", "mocha.run()");
+}
+
 class HistoryListTest : public HistoryUIBrowserTest {
  protected:
   void RunTestCase(const std::string& testCase) {
diff --git a/chrome/test/data/webui/settings/other_google_data_dialog_test.ts b/chrome/test/data/webui/settings/other_google_data_dialog_test.ts
index 81d07b8..891dcfdb 100644
--- a/chrome/test/data/webui/settings/other_google_data_dialog_test.ts
+++ b/chrome/test/data/webui/settings/other_google_data_dialog_test.ts
@@ -233,6 +233,10 @@
 
     const url = await testOpenWindowProxy.whenCalled('openUrl');
     assertEquals(loadTimeData.getString('myActivityGeminiAppsUrl'), url);
+
+    assertEquals(
+        'Settings.DeleteBrowsingData.GeminiAppsActivityLinkClick',
+        await testMetricsBrowserProxy.whenCalled('recordAction'));
   });
 
   test('GeminiAppsActivityVisibility', async function() {
diff --git a/chromeos/ash/components/sync_wifi/wifi_configuration_bridge.cc b/chromeos/ash/components/sync_wifi/wifi_configuration_bridge.cc
index aeffddd..a4f3677c9 100644
--- a/chromeos/ash/components/sync_wifi/wifi_configuration_bridge.cc
+++ b/chromeos/ash/components/sync_wifi/wifi_configuration_bridge.cc
@@ -297,6 +297,26 @@
       .SerializeToString();
 }
 
+bool WifiConfigurationBridge::IsEntityDataValid(
+    const syncer::EntityData& entity_data) const {
+  const sync_pb::WifiConfigurationSpecifics& specifics =
+      entity_data.specifics.wifi_configuration();
+  if (specifics.hex_ssid().empty()) {
+    return false;
+  }
+  // Only security types PSK and WEP are supported, see
+  // sync_wifi::SecurityTypeStringFromProto().
+  switch (specifics.security_type()) {
+    case sync_pb::WifiConfigurationSpecifics::SECURITY_TYPE_UNSPECIFIED:
+    case sync_pb::WifiConfigurationSpecifics::SECURITY_TYPE_NONE:
+      return false;
+    case sync_pb::WifiConfigurationSpecifics::SECURITY_TYPE_PSK:
+    case sync_pb::WifiConfigurationSpecifics::SECURITY_TYPE_WEP:
+      return true;
+  }
+  NOTREACHED();
+}
+
 void WifiConfigurationBridge::ApplyDisableSyncChanges(
     std::unique_ptr<syncer::MetadataChangeList> delete_metadata_change_list) {
   // Since bridge and DataTypeStore state represents the synced networks state,
diff --git a/chromeos/ash/components/sync_wifi/wifi_configuration_bridge.h b/chromeos/ash/components/sync_wifi/wifi_configuration_bridge.h
index d47e1b9..eba3885 100644
--- a/chromeos/ash/components/sync_wifi/wifi_configuration_bridge.h
+++ b/chromeos/ash/components/sync_wifi/wifi_configuration_bridge.h
@@ -85,6 +85,7 @@
       const syncer::EntityData& entity_data) const override;
   std::string GetStorageKey(
       const syncer::EntityData& entity_data) const override;
+  bool IsEntityDataValid(const syncer::EntityData& entity_data) const override;
   void ApplyDisableSyncChanges(std::unique_ptr<syncer::MetadataChangeList>
                                    delete_metadata_change_list) override;
 
diff --git a/components/BUILD.gn b/components/BUILD.gn
index fda67ed..16f2d6f5 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -733,7 +733,6 @@
     deps += [
       "//components/keep_alive_registry:unit_tests",
       "//components/manta:unit_tests",
-      "//components/password_manager/core/browser/actor_login/internal:unit_tests",
       "//components/trusted_vault:unit_tests",
     ]
   }
@@ -745,6 +744,7 @@
       "//components/headless/console_message_logger:unit_tests",
       "//components/headless/screen_info:unit_tests",
       "//components/live_caption:unit_tests",
+      "//components/password_manager/core/browser/actor_login/internal:unit_tests",
       "//components/soda:unit_tests",
       "//components/storage_monitor:unit_tests",
       "//components/web_modal:unit_tests",
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index a7eca848..e188eba 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -679,6 +679,8 @@
     "suggestions/passkeys/hybrid_passkey_availability.h",
     "suggestions/passkeys/passkey_autofill_suggestion_generator.cc",
     "suggestions/passkeys/passkey_autofill_suggestion_generator.h",
+    "suggestions/payments/credit_card_suggestion_generator.cc",
+    "suggestions/payments/credit_card_suggestion_generator.h",
     "suggestions/payments/iban_suggestion_generator.cc",
     "suggestions/payments/iban_suggestion_generator.h",
     "suggestions/payments/merchant_promo_code_suggestion_generator.cc",
diff --git a/components/autofill/core/browser/autofill_field.cc b/components/autofill/core/browser/autofill_field.cc
index 64490a8..a108c098 100644
--- a/components/autofill/core/browser/autofill_field.cc
+++ b/components/autofill/core/browser/autofill_field.cc
@@ -21,6 +21,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/types/cxx23_to_underlying.h"
+#include "components/autofill/core/browser/data_model/data_model_utils.h"
 #include "components/autofill/core/browser/field_type_utils.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/heuristic_source.h"
@@ -415,6 +416,27 @@
   return os << section.ToString();
 }
 
+AutofillFormatString::AutofillFormatString() = default;
+
+AutofillFormatString::AutofillFormatString(std::u16string v,
+                                           FormatString_Type type)
+    : value(std::move(v)), type(type) {
+  // TODO(crbug.com/446883719): Add a DCHECK that the format is valid.
+}
+
+AutofillFormatString::AutofillFormatString(const AutofillFormatString&) =
+    default;
+
+AutofillFormatString& AutofillFormatString::operator=(
+    const AutofillFormatString&) = default;
+
+AutofillFormatString::AutofillFormatString(AutofillFormatString&&) = default;
+
+AutofillFormatString& AutofillFormatString::operator=(AutofillFormatString&&) =
+    default;
+
+AutofillFormatString::~AutofillFormatString() = default;
+
 AutofillField::AutofillField() {
   local_type_predictions_.fill(NO_SERVER_DATA);
 }
@@ -771,16 +793,19 @@
   password_requirements_ = std::move(spec);
 }
 
-base::optional_ref<const std::u16string> AutofillField::format_string() const {
+base::optional_ref<const AutofillFormatString> AutofillField::format_string()
+    const {
   if (form_control_type() == FormControlType::kInputDate) {
-    static const base::NoDestructor<std::u16string> kFormat(u"YYYY-MM-DD");
+    static const base::NoDestructor<AutofillFormatString> kFormat(
+        AutofillFormatString(u"YYYY-MM-DD", FormatString_Type_DATE));
     return *kFormat;
   }
   if (form_control_type() == FormControlType::kInputMonth) {
-    static const base::NoDestructor<std::u16string> kFormat(u"YYYY-MM");
+    static const base::NoDestructor<AutofillFormatString> kFormat(
+        AutofillFormatString(u"YYYY-MM", FormatString_Type_DATE));
     return *kFormat;
   }
-  if (format_string_source_ == FormatStringSource::kUnset) {
+  if (format_string_source_ == AutofillFormatStringSource::kUnset) {
     return std::nullopt;
   }
   return format_string_;
diff --git a/components/autofill/core/browser/autofill_field.h b/components/autofill/core/browser/autofill_field.h
index 8b7b3e1..5f480d6 100644
--- a/components/autofill/core/browser/autofill_field.h
+++ b/components/autofill/core/browser/autofill_field.h
@@ -121,6 +121,35 @@
 LogBuffer& operator<<(LogBuffer& buffer, const Section& section);
 std::ostream& operator<<(std::ostream& os, const Section& section);
 
+// Describes formatting information for a field. Currently used only for
+// filling Autofill AI data.
+struct AutofillFormatString final {
+  AutofillFormatString();
+  AutofillFormatString(std::u16string value, FormatString_Type type);
+
+  AutofillFormatString(const AutofillFormatString&);
+  AutofillFormatString& operator=(const AutofillFormatString&);
+  AutofillFormatString(AutofillFormatString&&);
+  AutofillFormatString& operator=(AutofillFormatString&&);
+  ~AutofillFormatString();
+
+  // The actual format string.
+  std::u16string value;
+
+  // Format strings can have different types: They can specify a date
+  // format, an affix format, etc. See `FormatString_Type` for allowed values.
+  FormatString_Type type{};
+};
+
+// The ordering matters: higher values overrule lower values (e.g., kServer
+// overrules kHeuristics).
+enum class AutofillFormatStringSource {
+  kUnset = 0,        // No format string set.
+  kHeuristics = 1,   // Set by local heuristics.
+  kModelResult = 2,  // Set by a direct model response
+  kServer = 3,       // Set by an (Autofill) server response.
+};
+
 class AutofillField : public FormFieldData {
  public:
   using FieldLogEventType = std::variant<std::monostate,
@@ -328,30 +357,23 @@
     return password_requirements_;
   }
 
-  // The ordering ordering matters: higher values overrule lower values (e.g.,
-  // kServer overrules kHeuristics).
-  enum class FormatStringSource {
-    kUnset = 0,        // No format string set.
-    kHeuristics = 1,   // Set by local heuristics.
-    kModelResult = 2,  // Set by a direct model response
-    kServer = 3,       // Set by an (Autofill) server response.
-  };
-
   // The format of the value expected by the web document. Currently, the
   // following kinds of format stings are supported:
   // - Affix format strings (see data_util::IsValidAffixFormat()).
   // - Date format strings (data_util::IsValidDateFormat()).
+  // - Flight number format strings (data_util::IsValidFlightNumberFormat()).
   //
   // Only one format string is stored at a time: the one with the
-  // highest-ranking `FormatStringSource`.
-  base::optional_ref<const std::u16string> format_string() const LIFETIME_BOUND;
+  // highest-ranking `AutofillFormatString::Source`.
+  base::optional_ref<const AutofillFormatString> format_string() const
+      LIFETIME_BOUND;
 
-  FormatStringSource format_string_source() const {
+  AutofillFormatStringSource format_string_source() const {
     return format_string_source_;
   }
 
-  void set_format_string_unless_overruled(std::u16string format_string,
-                                          FormatStringSource source) {
+  void set_format_string_unless_overruled(AutofillFormatString format_string,
+                                          AutofillFormatStringSource source) {
     if (format_string_source_ <= source) {
       format_string_ = std::move(format_string);
       format_string_source_ = source;
@@ -491,8 +513,9 @@
   // Corresponds to the requirements determined by the Autofill server.
   std::optional<PasswordRequirementsSpec> password_requirements_;
 
-  std::u16string format_string_;
-  FormatStringSource format_string_source_ = FormatStringSource::kUnset;
+  AutofillFormatString format_string_;
+  AutofillFormatStringSource format_string_source_ =
+      AutofillFormatStringSource::kUnset;
 
   // Predictions which where calculated on the client. This is initialized to
   // `NO_SERVER_DATA`, which means "NO_DATA", i.e. no classification was
diff --git a/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.cc b/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.cc
index e13b806e..1f6b3af93 100644
--- a/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.cc
+++ b/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.cc
@@ -1058,9 +1058,11 @@
           case FormatString_Type_DATE:
           case FormatString_Type_FLIGHT_NUMBER:
             field->set_format_string_unless_overruled(
-                base::UTF8ToUTF16(
-                    field_suggestion->format_string().format_string()),
-                AutofillField::FormatStringSource::kServer);
+                AutofillFormatString(
+                    base::UTF8ToUTF16(
+                        field_suggestion->format_string().format_string()),
+                    field_suggestion->format_string().type()),
+                AutofillFormatStringSource::kServer);
             break;
         }
       }
diff --git a/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.cc b/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.cc
index c86a6d6..1211d5f 100644
--- a/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.cc
+++ b/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.cc
@@ -13,6 +13,7 @@
 #include "base/containers/flat_set.h"
 #include "base/containers/span.h"
 #include "base/containers/to_vector.h"
+#include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
@@ -280,7 +281,8 @@
     for (const AttributeInstance& attribute : entity.attributes()) {
       for (const FieldType field_type : attribute.type().field_subtypes()) {
         const std::u16string& value_on_file =
-            attribute.GetInfo(field_type, app_locale, std::nullopt);
+            normalization::NormalizeForComparison(
+                attribute.GetInfo(field_type, app_locale, std::nullopt));
 
         // Test if `value_in_field` and `value_on_file` match.
         bool full_match =
@@ -292,6 +294,11 @@
                   features::kAutofillAiVoteForFormatStringsForAffixes)) {
             pt.formats.emplace(FormatString_Type_AFFIX, u"0");
           }
+          if (field_type == FLIGHT_RESERVATION_FLIGHT_NUMBER &&
+              base::FeatureList::IsEnabled(
+                  features::kAutofillAiVoteForFormatStringsForFlightNumbers)) {
+            pt.formats.emplace(FormatString_Type_FLIGHT_NUMBER, u"F");
+          }
         }
 
         // Test if `value_in_field` is an affix of `value_on_file`.
@@ -316,6 +323,20 @@
                     -1 * static_cast<int>(value_in_field.size())));
           }
         }
+
+        if (field_type == FLIGHT_RESERVATION_FLIGHT_NUMBER &&
+            base::FeatureList::IsEnabled(
+                features::kAutofillAiVoteForFormatStringsForFlightNumbers)) {
+          if (value_in_field.size() == 2 &&
+              value_on_file.starts_with(value_in_field)) {
+            pt.types.insert(field_type);
+            pt.formats.emplace(FormatString_Type_FLIGHT_NUMBER, u"A");
+          } else if (value_on_file.size() > 3 &&
+                     value_on_file.substr(2) == value_in_field) {
+            pt.types.insert(field_type);
+            pt.formats.emplace(FormatString_Type_FLIGHT_NUMBER, u"N");
+          }
+        }
       }
     }
   }
diff --git a/components/autofill/core/browser/crowdsourcing/determine_possible_field_types_unittest.cc b/components/autofill/core/browser/crowdsourcing/determine_possible_field_types_unittest.cc
index 402a17f..4b3275f 100644
--- a/components/autofill/core/browser/crowdsourcing/determine_possible_field_types_unittest.cc
+++ b/components/autofill/core/browser/crowdsourcing/determine_possible_field_types_unittest.cc
@@ -23,6 +23,7 @@
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_test_utils.h"
 #include "components/autofill/core/common/form_data_test_api.h"
+#include "components/autofill/core/common/form_field_data.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -89,21 +90,26 @@
 // Matcher for `PossibleTypes::formats`.
 template <typename... Ts>
   requires(std::convertible_to<Ts, const char*> && ...)
-Matcher<const PossibleTypes&> HasAffixFormats(Ts&&... formats) {
+Matcher<const PossibleTypes&> HasFormats(FormatString_Type type,
+                                         Ts&&... formats) {
   return Field("PossibleTypes::formats", &PossibleTypes::formats,
-               UnorderedElementsAre(
-                   Pair(FormatString_Type_AFFIX,
-                        base::UTF8ToUTF16(std::string_view(formats)))...));
+               UnorderedElementsAre(Pair(
+                   type, base::UTF8ToUTF16(std::string_view(formats)))...));
 }
 
-// Matcher for `PossibleTypes::formats`.
 template <typename... Ts>
-  requires(std::convertible_to<Ts, const char*> && ...)
+Matcher<const PossibleTypes&> HasAffixFormats(Ts&&... formats) {
+  return HasFormats(FormatString_Type_AFFIX, formats...);
+}
+
+template <typename... Ts>
 Matcher<const PossibleTypes&> HasDateFormats(Ts&&... formats) {
-  return Field("PossibleTypes::formats", &PossibleTypes::formats,
-               UnorderedElementsAre(
-                   Pair(FormatString_Type_DATE,
-                        base::UTF8ToUTF16(std::string_view(formats)))...));
+  return HasFormats(FormatString_Type_DATE, formats...);
+}
+
+template <typename... Ts>
+Matcher<const PossibleTypes&> HasFlightNumberFormats(Ts&&... formats) {
+  return HasFormats(FormatString_Type_FLIGHT_NUMBER, formats...);
 }
 
 // Fakes that a `form` has been seen (without its field value) and parsed and
@@ -414,6 +420,7 @@
     scoped_feature_list_.InitWithFeatures(
         {features::kAutofillAiWithDataSchema,
          features::kAutofillAiVoteForFormatStringsForAffixes,
+         features::kAutofillAiVoteForFormatStringsForFlightNumbers,
          features::kAutofillEnableLoyaltyCardsFilling},
         {});
   }
@@ -807,6 +814,13 @@
                           FormControlType::kInputText),
       CreateTestFormField("issue", "issue-year", "2010",
                           FormControlType::kInputText),
+      // Flight number.
+      CreateTestFormField("airline", "airline", "LH",
+                          FormControlType::kInputText),
+      CreateTestFormField("number", "number", "93",
+                          FormControlType::kInputText),
+      CreateTestFormField("flight number", "flight number", "LH93",
+                          FormControlType::kInputText),
       // No format string.
       CreateTestFormField("wrong-country", "wrong-country", "Finland",
                           FormControlType::kInputText),
@@ -814,7 +828,7 @@
   std::unique_ptr<FormStructure> form_structure =
       ConstructFormStructureFromFormData(form);
 
-  EntityInstance entity = test::GetPassportEntityInstance({
+  const EntityInstance passport_entity = test::GetPassportEntityInstance({
       .name = u"Pippi Longstocking",
       .number = u"0123456789",
       .country = u"Sweden",
@@ -822,10 +836,13 @@
       .issue_date = u"2010-09-01",
   });
 
+  const EntityInstance flight_entity =
+      test::GetFlightReservationEntityInstance({.flight_number = u"LH93"});
+
   EXPECT_THAT(
       DeterminePossibleFieldTypesForUpload(
           std::vector<AutofillProfile>(), std::vector<CreditCard>(),
-          base::span_from_ref(entity), std::vector<LoyaltyCard>(),
+          {passport_entity, flight_entity}, std::vector<LoyaltyCard>(),
           /*fields_that_match_state=*/{},
           /*last_unlocked_credit_card_cvc=*/u"", "en-US",
           form_structure->fields()),
@@ -843,6 +860,12 @@
           AllOf(HasTypes(PASSPORT_ISSUE_DATE), HasDateFormats("DD", "MM")),
           AllOf(HasTypes(PASSPORT_ISSUE_DATE), HasDateFormats("DD", "MM")),
           AllOf(HasTypes(PASSPORT_ISSUE_DATE), HasDateFormats("YYYY")),
+          AllOf(HasTypes(FLIGHT_RESERVATION_FLIGHT_NUMBER),
+                HasFlightNumberFormats("A")),
+          AllOf(HasTypes(FLIGHT_RESERVATION_FLIGHT_NUMBER),
+                HasFlightNumberFormats("N")),
+          AllOf(HasTypes(FLIGHT_RESERVATION_FLIGHT_NUMBER),
+                HasFlightNumberFormats("F")),
           AllOf(HasTypes(UNKNOWN_TYPE), HasNoFormats())));
 }
 
diff --git a/components/autofill/core/browser/data_manager/addresses/account_name_email_store.cc b/components/autofill/core/browser/data_manager/addresses/account_name_email_store.cc
index 022a5fbb..5a2ece26 100644
--- a/components/autofill/core/browser/data_manager/addresses/account_name_email_store.cc
+++ b/components/autofill/core/browser/data_manager/addresses/account_name_email_store.cc
@@ -14,7 +14,9 @@
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/sync/base/user_selectable_type.h"
 #include "components/sync/service/sync_service.h"
+#include "components/sync/service/sync_user_settings.h"
 
 namespace autofill {
 
@@ -55,7 +57,7 @@
       (!primary_info->IsEmpty() && info.gaia != primary_info->gaia)) {
     return;
   }
-  UpdateOrCreateAccountNameEmail(info);
+  MaybeUpdateOrCreateAccountNameEmail();
 }
 
 void AccountNameEmailStore::OnSyncShutdown(syncer::SyncService*) {
@@ -64,6 +66,48 @@
   NOTREACHED();
 }
 
+void AccountNameEmailStore::OnStateChanged(syncer::SyncService* sync_service) {
+  // Only autofill syncing users are eligible for the kAccountNameEmail
+  // profile. Having all relevant data loaded is crucial for correct execution.
+  // If the user doesn't have the autofill sync toggle enabled, try to remove
+  // the kAccountNameEmail profile.
+  std::optional<ProfileUpdateBlockReason> reason =
+      GetBlockAccountNameEmailUpdateReason();
+  if (!reason.has_value()) {
+    MaybeUpdateOrCreateAccountNameEmail();
+    return;
+  }
+  switch (reason.value()) {
+    case ProfileUpdateBlockReason::kSyncOff:
+      RemoveAccountNameEmail();
+      return;
+    case ProfileUpdateBlockReason::kDataNotLoaded:
+      // Defer call. When data is loaded, `OnStateChanged` will be called again,
+      // reattempting to create the profile.
+      return;
+  }
+}
+
+void AccountNameEmailStore::MaybeUpdateOrCreateAccountNameEmail() {
+  if (GetBlockAccountNameEmailUpdateReason().has_value()) {
+    return;
+  }
+
+  const std::optional<CoreAccountInfo>& core_info =
+      identity_manager_->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin);
+  if (!core_info.has_value()) {
+    return;
+  }
+
+  const std::optional<AccountInfo>& extended_info =
+      identity_manager_->FindExtendedAccountInfo(core_info.value());
+  if (!extended_info.has_value()) {
+    return;
+  }
+
+  UpdateOrCreateAccountNameEmail(extended_info.value());
+}
+
 void AccountNameEmailStore::OnAddressDataChanged() {
   if (pref_service_->GetInteger(
           prefs::kAutofillNameAndEmailProfileNotSelectedCounter) >
@@ -89,11 +133,23 @@
   }
 }
 
+void AccountNameEmailStore::RemoveAccountNameEmail() {
+  const std::vector<const AutofillProfile*> account_name_email_profiles =
+      address_data_manager_->GetProfilesByRecordType(
+          AutofillProfile::RecordType::kAccountNameEmail);
+  if (account_name_email_profiles.empty()) {
+    return;
+  }
+  CHECK_EQ(1u, account_name_email_profiles.size());
+
+  address_data_manager_->RemoveProfile(account_name_email_profiles[0]->guid());
+}
+
 void AccountNameEmailStore::UpdateOrCreateAccountNameEmail(
     const AccountInfo& info) {
   // During signin the `OnExtendedAccountInfoUpdated` method might call this
   // method with an empty `info.full_name` since not all data arrives all at
-  // once and `AccountiInfo` is updated multiple times. The `kAccountNameEmail`
+  // once and `AccountInfo` is updated multiple times. The `kAccountNameEmail`
   // profile and hash signature require non-empty `full_name` value.
   if (info.IsEmpty() || info.full_name.empty()) {
     return;
@@ -132,22 +188,33 @@
       prefs::kAutofillNameAndEmailProfileNotSelectedCounter, 0);
 }
 
-void AccountNameEmailStore::RemoveAccountNameEmail() {
-  const std::vector<const AutofillProfile*> account_name_email_profiles =
-      address_data_manager_->GetProfilesByRecordType(
-          AutofillProfile::RecordType::kAccountNameEmail);
-  if (account_name_email_profiles.empty()) {
-    return;
-  }
-  CHECK_EQ(1u, account_name_email_profiles.size());
-
-  address_data_manager_->RemoveProfile(account_name_email_profiles[0]->guid());
-}
-
 std::string AccountNameEmailStore::HashAccountInfo(
     const AccountInfo& info) const {
   return base::NumberToString(base::PersistentHash(
       base::StrCat({info.full_name, kSeparator, info.email})));
 }
 
+std::optional<AccountNameEmailStore::ProfileUpdateBlockReason>
+AccountNameEmailStore::GetBlockAccountNameEmailUpdateReason() {
+  if (!sync_service_->GetUserSettings()->GetSelectedTypes().Has(
+          syncer::UserSelectableType::kAutofill)) {
+    return ProfileUpdateBlockReason::kSyncOff;
+  }
+
+  if (address_data_manager_->IsAwaitingPendingAddressChanges()) {
+    return ProfileUpdateBlockReason::kDataNotLoaded;
+  }
+
+  switch (sync_service_->GetDownloadStatusFor(
+      syncer::DataType::PRIORITY_PREFERENCES)) {
+    case syncer::SyncService::DataTypeDownloadStatus::kWaitingForUpdates:
+      return ProfileUpdateBlockReason::kDataNotLoaded;
+    case syncer::SyncService::DataTypeDownloadStatus::kUpToDate:
+    // If the download status is kError, it will likely not become
+    // available anytime soon.
+    case syncer::SyncService::DataTypeDownloadStatus::kError:
+      return std::nullopt;
+  }
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/data_manager/addresses/account_name_email_store.h b/components/autofill/core/browser/data_manager/addresses/account_name_email_store.h
index 5b22518..1a0c4c29 100644
--- a/components/autofill/core/browser/data_manager/addresses/account_name_email_store.h
+++ b/components/autofill/core/browser/data_manager/addresses/account_name_email_store.h
@@ -17,19 +17,22 @@
 namespace autofill {
 
 // The kAccountNameEmail autofill profile is an un-syncable, locally stored,
-// profile generated automatically for every signed in user, unless they deleted
-// it or didn't use it. This profile is composed of 2 pieces of data:
+// profile generated automatically for the every signed in user with the
+// Autofill sync toggle enabled, unless they have deleted it or have not used
+// it.
+//
+// This profile is composed of 2 pieces of data:
 // - full name
 // - email
+// `kLegacyHierarchyCountryCode` is used to not reveal the country of this
+// profile.
+//
 // Keeping kAccountNameEmail profile's state up to date between the devices is
-// handled through syncable prefs. `AccountNameEmailStore` is a class
+// handled through priority prefs. `AccountNameEmailStore` is a class
 // responsible for accessing and modifying those prefs, as well as managing
 // (create, update, remove) kAccountNameEmail profile. In code
 // `AccountNameEmailStore` is owned by and has the same lifetime as
 // `AddressDataManager`.
-// TODO(441657422): `AccountNameEmailStrikeManager` should notify this class if
-// a suggestion with kAccountNameEmail profile was showed and whether it was
-// accepted.
 class AccountNameEmailStore : public signin::IdentityManager::Observer,
                               public AddressDataManager::Observer,
                               public syncer::SyncServiceObserver {
@@ -49,6 +52,12 @@
 
   // syncer::SyncServiceObserver:
   void OnSyncShutdown(syncer::SyncService* sync) override;
+  void OnStateChanged(syncer::SyncService* sync_service) override;
+
+  // Checks that the necessary data is available and that the user has enabled
+  // autofill sync before updating/creating the kAccountNameEmail profile.
+  // This prevents premature update/create without all of the relevant data.
+  void MaybeUpdateOrCreateAccountNameEmail();
 
   // AddressDataManager::Observer:
   // Called when the address data of the `AddressDataManager` changes. If
@@ -57,20 +66,32 @@
   // `kAutofillNameAndEmailProfileNotSelectedThreshold` + 1.
   void OnAddressDataChanged() override;
 
-  // Updates the `kAccountNameEmail` autofill profile with the newest signed-in
-  // account `info`. If the `kAccountNameEmail` profile doesn't exist, it is
-  // created.
-  void UpdateOrCreateAccountNameEmail(const AccountInfo& info);
-
   // Removes the `kAccountNameEmail` autofill profile if it exists.
   void RemoveAccountNameEmail();
 
  private:
   friend class AccountNameEmailStoreTestApi;
 
+  enum class ProfileUpdateBlockReason {
+    kSyncOff = 0,
+    kDataNotLoaded = 1,
+  };
+
+  // Updates the kAccountNameEmail autofill profile with the account `info`. If
+  // the kAccountNameEmail profile doesn't exist, it is created.
+  void UpdateOrCreateAccountNameEmail(const AccountInfo& info);
+
   // Hashes concatenated full_name and email_address delimited by |.
   std::string HashAccountInfo(const AccountInfo& info) const;
 
+  // Determines if the conditions for creating or updating the kAccountNameEmail
+  // profile are not met. The operation should be blocked if sync is disabled
+  // for Autofill or if sync is enabled but the necessary data has not yet been
+  // downloaded.
+  // Returns a blocking reason or nullopt if the operation shouldn't be blocked.
+  std::optional<ProfileUpdateBlockReason>
+  GetBlockAccountNameEmailUpdateReason();
+
   const raw_ref<AddressDataManager> address_data_manager_;
   const raw_ref<signin::IdentityManager> identity_manager_;
   const raw_ref<syncer::SyncService> sync_service_;
diff --git a/components/autofill/core/browser/data_manager/addresses/account_name_email_store_unittest.cc b/components/autofill/core/browser/data_manager/addresses/account_name_email_store_unittest.cc
index b1bc134a..5a1be1b 100644
--- a/components/autofill/core/browser/data_manager/addresses/account_name_email_store_unittest.cc
+++ b/components/autofill/core/browser/data_manager/addresses/account_name_email_store_unittest.cc
@@ -85,6 +85,7 @@
   signin::IdentityTestEnvironment& identity_test_env() {
     return identity_test_env_;
   }
+  syncer::TestSyncService& sync_service() { return sync_service_; }
 
  private:
   base::test::ScopedFeatureList feature_{
@@ -109,51 +110,80 @@
 }
 
 // Tests that a new `kAccountNameEmail` profile isn't created when an empty
-// `AccountInfo` is passed into the `UpdateOrCreateAccountNameEmail` method.
+// `AccountInfo` is passed into the `MaybeUpdateOrCreateAccountNameEmail`
+// method.
 TEST_F(AccountNameEmailStoreTest, EmptyAccountInfoCreation) {
-  account_name_email_store().UpdateOrCreateAccountNameEmail({});
+  account_name_email_store().MaybeUpdateOrCreateAccountNameEmail();
   EXPECT_THAT(address_data_manager().GetProfiles(), IsEmpty());
 }
 
-// Tests that a new `kAccountNameEmail` profile isn't created when the
-// `full_name` field of passed in `AccountInfo` is empty.
+// Tests that a new kAccountNameEmail profile isn't created when the account
+// info misses the full_name.
 TEST_F(AccountNameEmailStoreTest, EmptyAccountNameCreation) {
-  AccountInfo info;
-  info.email = kTestEmailAddress1;
-  account_name_email_store().UpdateOrCreateAccountNameEmail(info);
+  CreatePrimaryAccount(std::string(), kTestEmailAddress1);
   EXPECT_THAT(address_data_manager().GetProfiles(), IsEmpty());
 }
 
-// Tests that the `UpdateOrCreateAccountNameEmail` method creates / updates
-// `kAccountNameEmail` with the correct info.
+// Tests that a new kAccountNameEmail profile isn't created when autofill is not
+// synced.
+TEST_F(AccountNameEmailStoreTest, AutofillNotSynced) {
+  sync_service().SetSignedOut();
+  CreatePrimaryAccount(kTestName1, kTestEmailAddress1);
+  EXPECT_THAT(address_data_manager().GetProfiles(), IsEmpty());
+}
+
+// Tests that a new kAccountNameEmail profile is removed if user disabled
+// autofill sync toggle.
+TEST_F(AccountNameEmailStoreTest, ProfileRemovedAfterSyncOff) {
+  CreatePrimaryAccount(kTestName1, kTestEmailAddress1);
+
+  ASSERT_THAT(address_data_manager().GetProfiles(),
+              ElementsAre(IsCorrectAccountNameEmail(
+                  base::UTF8ToUTF16(kTestName1),
+                  base::UTF8ToUTF16(kTestEmailAddress1))));
+
+  syncer::UserSelectableTypeSet selected_sync_types =
+      sync_service().GetUserSettings()->GetSelectedTypes();
+  selected_sync_types.Remove(syncer::UserSelectableType::kAutofill);
+  sync_service().GetUserSettings()->SetSelectedTypes(
+      /*sync_everything=*/false, selected_sync_types);
+  sync_service().FireStateChanged();
+
+  EXPECT_THAT(address_data_manager().GetProfiles(), IsEmpty());
+}
+
+TEST_F(AccountNameEmailStoreTest, ProfileNotCreatedBeforeDataLoaded) {
+  sync_service().SetDownloadStatusFor(
+      {syncer::DataType::PRIORITY_PREFERENCES},
+      syncer::SyncService::DataTypeDownloadStatus::kWaitingForUpdates);
+
+  CreatePrimaryAccount(kTestName1, kTestEmailAddress1);
+  EXPECT_THAT(address_data_manager().GetProfiles(), IsEmpty());
+}
+
+// Tests that the created profile holds the correct data.
 TEST_F(AccountNameEmailStoreTest, SpecificInfoCreationUpdate) {
-  AccountInfo info;
-  info.full_name = kTestName1;
-  info.email = kTestEmailAddress1;
-
-  account_name_email_store().UpdateOrCreateAccountNameEmail(info);
-
+  CreatePrimaryAccount(kTestName1, kTestEmailAddress1);
   EXPECT_THAT(address_data_manager().GetProfiles(),
               ElementsAre(IsCorrectAccountNameEmail(
                   base::UTF8ToUTF16(kTestName1),
                   base::UTF8ToUTF16(kTestEmailAddress1))));
 }
 
-// Tests that the `UpdateOrCreateAccountNameEmail` correctly updates the
+// Tests that the creating the profile correctly updates the
 // `kAutofillNameAndEmailProfileSignature` pref.
 TEST_F(AccountNameEmailStoreTest, HashPrefSaving) {
-  AccountInfo info;
-  info.full_name = kTestName1;
-  info.email = kTestEmailAddress1;
-  account_name_email_store().UpdateOrCreateAccountNameEmail(info);
+  CreatePrimaryAccount(kTestName1, kTestEmailAddress1);
 
   EXPECT_EQ(
       pref_service().GetString(prefs::kAutofillNameAndEmailProfileSignature),
-      test_api(&account_name_email_store()).HashAccountInfo(info));
+      test_api(&account_name_email_store())
+          .HashAccountInfo(identity_manager().FindExtendedAccountInfo(
+              identity_manager().GetPrimaryAccountInfo(
+                  signin::ConsentLevel::kSignin))));
 }
 
-// Tests that the `UpdateOrCreateAccountNameEmail` returns early (does nothing)
-// when account info with the same hash as the stored one in pref was passed in.
+// Tests that no new profile is created if hashes match.
 TEST_F(AccountNameEmailStoreTest, EarlyReturnWhenHashesAreEqual) {
   AccountInfo info;
   info.full_name = kTestName1;
@@ -163,31 +193,23 @@
       test_api(&account_name_email_store()).HashAccountInfo(info);
 
   pref_service().SetString(prefs::kAutofillNameAndEmailProfileSignature, hash);
-  account_name_email_store().UpdateOrCreateAccountNameEmail(info);
+  // Start profile creation after the hash is already set to the right value.
+  CreatePrimaryAccount(kTestName1, kTestEmailAddress1);
 
   EXPECT_EQ(hash, pref_service().GetString(
                       prefs::kAutofillNameAndEmailProfileSignature));
+  EXPECT_THAT(address_data_manager().GetProfiles(), IsEmpty());
 }
 
-// Tests that the `UpdateOrCreateAccountNameEmail` removes the Account Name
-// Email profile when updating.
+// Tests that old profile is removed when new primary account is used.
 TEST_F(AccountNameEmailStoreTest, RemovingProfile) {
-  AccountInfo info1;
-  info1.full_name = kTestName1;
-  info1.email = kTestEmailAddress1;
+  CreatePrimaryAccount(kTestName1, kTestEmailAddress1);
+  CreatePrimaryAccount(kTestName2, kTestEmailAddress2);
 
-  AccountInfo info2;
-  info2.full_name = kTestName2;
-  info2.email = kTestEmailAddress2;
-
-  account_name_email_store().UpdateOrCreateAccountNameEmail(info1);
-  account_name_email_store().UpdateOrCreateAccountNameEmail(info2);
-
-  EXPECT_THAT(
-      address_data_manager().GetProfiles(),
-      Contains(IsCorrectAccountNameEmail(base::UTF8ToUTF16(kTestName1),
-                                         base::UTF8ToUTF16(kTestEmailAddress1)))
-          .Times(0));
+  EXPECT_THAT(address_data_manager().GetProfiles(),
+              ElementsAre(IsCorrectAccountNameEmail(
+                  base::UTF8ToUTF16(kTestName2),
+                  base::UTF8ToUTF16(kTestEmailAddress2))));
 }
 
 // Tests that the `OnExtendedAccountInfoUpdated` method will create the
diff --git a/components/autofill/core/browser/data_manager/addresses/account_name_email_strike_manager.cc b/components/autofill/core/browser/data_manager/addresses/account_name_email_strike_manager.cc
index 61adadc..5910ddd4 100644
--- a/components/autofill/core/browser/data_manager/addresses/account_name_email_strike_manager.cc
+++ b/components/autofill/core/browser/data_manager/addresses/account_name_email_strike_manager.cc
@@ -6,10 +6,13 @@
 
 #include <algorithm>
 
+#include "base/feature_list.h"
 #include "components/autofill/core/browser/data_manager/personal_data_manager.h"
 #include "components/autofill/core/browser/data_model/addresses/autofill_profile.h"
 #include "components/autofill/core/browser/suggestions/addresses/address_suggestion_generator.h"
 #include "components/autofill/core/browser/suggestions/suggestion.h"
+#include "components/autofill/core/common/autofill_prefs.h"
+#include "components/prefs/pref_service.h"
 
 namespace autofill {
 
@@ -20,8 +23,26 @@
 }
 
 AccountNameEmailStrikeManager::~AccountNameEmailStrikeManager() {
-  // TODO(crbug.com/356845298): Use the recorded members to set prefs correctly
-  // in `AccountNameEmailStore`.
+  PrefService* pref_service = client_->GetPrefs();
+  if (!pref_service || !was_name_email_profile_suggestion_shown_) {
+    return;
+  }
+  if (pref_service->GetBoolean(prefs::kAutofillWasNameAndEmailProfileUsed)) {
+    return;
+  }
+
+  if (was_name_email_profile_filled_) {
+    pref_service->SetBoolean(prefs::kAutofillWasNameAndEmailProfileUsed, true);
+    return;
+  }
+
+  // TODO(crbug.com/356845298): React to the pref changes in
+  // `AccountNameEmailStore` and possibly delete the profile.
+  pref_service->SetInteger(
+      prefs::kAutofillNameAndEmailProfileNotSelectedCounter,
+      pref_service->GetInteger(
+          prefs::kAutofillNameAndEmailProfileNotSelectedCounter) +
+          1);
 }
 
 void AccountNameEmailStrikeManager::OnSuggestionsShown(
diff --git a/components/autofill/core/browser/data_manager/addresses/account_name_email_strike_manager.h b/components/autofill/core/browser/data_manager/addresses/account_name_email_strike_manager.h
index a991374..a6333fa 100644
--- a/components/autofill/core/browser/data_manager/addresses/account_name_email_strike_manager.h
+++ b/components/autofill/core/browser/data_manager/addresses/account_name_email_strike_manager.h
@@ -12,9 +12,10 @@
 
 // This class is responsible for reacting to suggestions that are offered and
 // filled with `kAccountNameEmail` profiles. On destruction (i.e. navigation)
-// it may notify the `AccountNameEmailStore`, if the suggestion with
-// kAccountNameEmail profile was showed and whether it was filled.
-// Owned by the `BrowserAutofillManger`, destroyed and recreated on navigation.
+// it will record prefs related to the ussage of those suggestions (if the
+// suggestion with kAccountNameEmail profile was showed and whether it was
+// filled). Owned by the `BrowserAutofillManger`, destroyed and recreated on
+// navigation.
 class AccountNameEmailStrikeManager : AutofillManager::Observer {
  public:
   explicit AccountNameEmailStrikeManager(AutofillManager& autofill_manager);
@@ -37,7 +38,7 @@
  private:
   friend class AccountNameEmailStrikeManagerTestApi;
 
-  base::raw_ref<const AutofillClient> client_;
+  base::raw_ref<AutofillClient> client_;
 
   bool was_name_email_profile_suggestion_shown_ = false;
   bool was_name_email_profile_filled_ = false;
diff --git a/components/autofill/core/browser/data_manager/addresses/account_name_email_strike_manager_unittests.cc b/components/autofill/core/browser/data_manager/addresses/account_name_email_strike_manager_unittests.cc
index 911ba52..3ba0992d 100644
--- a/components/autofill/core/browser/data_manager/addresses/account_name_email_strike_manager_unittests.cc
+++ b/components/autofill/core/browser/data_manager/addresses/account_name_email_strike_manager_unittests.cc
@@ -78,6 +78,15 @@
       FormData(), FieldGlobalId(), base::DoNothing());
   EXPECT_FALSE(test_api(GetAccountNameEmailStrikeManager())
                    .was_name_email_profile_suggestion_shown());
+  EXPECT_FALSE(test_api(GetAccountNameEmailStrikeManager())
+                   .was_name_email_profile_filled());
+
+  autofill_manager().Reset();
+  EXPECT_EQ(autofill_client().GetPrefs()->GetInteger(
+                prefs::kAutofillNameAndEmailProfileNotSelectedCounter),
+            0);
+  EXPECT_FALSE(autofill_client().GetPrefs()->GetBoolean(
+      prefs::kAutofillWasNameAndEmailProfileUsed));
 }
 
 TEST_F(AccountNameEmailStrikeManagerTest,
@@ -90,6 +99,13 @@
                   .was_name_email_profile_suggestion_shown());
   EXPECT_FALSE(test_api(GetAccountNameEmailStrikeManager())
                    .was_name_email_profile_filled());
+
+  autofill_manager().Reset();
+  EXPECT_EQ(autofill_client().GetPrefs()->GetInteger(
+                prefs::kAutofillNameAndEmailProfileNotSelectedCounter),
+            1);
+  EXPECT_FALSE(autofill_client().GetPrefs()->GetBoolean(
+      prefs::kAutofillWasNameAndEmailProfileUsed));
 }
 
 TEST_F(AccountNameEmailStrikeManagerTest,
@@ -108,6 +124,13 @@
                   .was_name_email_profile_suggestion_shown());
   EXPECT_FALSE(test_api(GetAccountNameEmailStrikeManager())
                    .was_name_email_profile_filled());
+
+  autofill_manager().Reset();
+  EXPECT_EQ(autofill_client().GetPrefs()->GetInteger(
+                prefs::kAutofillNameAndEmailProfileNotSelectedCounter),
+            1);
+  EXPECT_FALSE(autofill_client().GetPrefs()->GetBoolean(
+      prefs::kAutofillWasNameAndEmailProfileUsed));
 }
 
 TEST_F(AccountNameEmailStrikeManagerTest,
@@ -126,6 +149,13 @@
                   .was_name_email_profile_suggestion_shown());
   EXPECT_TRUE(test_api(GetAccountNameEmailStrikeManager())
                   .was_name_email_profile_filled());
+
+  autofill_manager().Reset();
+  EXPECT_EQ(autofill_client().GetPrefs()->GetInteger(
+                prefs::kAutofillNameAndEmailProfileNotSelectedCounter),
+            0);
+  EXPECT_TRUE(autofill_client().GetPrefs()->GetBoolean(
+      prefs::kAutofillWasNameAndEmailProfileUsed));
 }
 
 TEST_F(AccountNameEmailStrikeManagerTest,
@@ -148,6 +178,13 @@
                   .was_name_email_profile_suggestion_shown());
   EXPECT_FALSE(test_api(GetAccountNameEmailStrikeManager())
                    .was_name_email_profile_filled());
+
+  autofill_manager().Reset();
+  EXPECT_EQ(autofill_client().GetPrefs()->GetInteger(
+                prefs::kAutofillNameAndEmailProfileNotSelectedCounter),
+            1);
+  EXPECT_FALSE(autofill_client().GetPrefs()->GetBoolean(
+      prefs::kAutofillWasNameAndEmailProfileUsed));
 }
 
 }  // namespace
diff --git a/components/autofill/core/browser/data_manager/addresses/address_data_manager.cc b/components/autofill/core/browser/data_manager/addresses/address_data_manager.cc
index c2e38f5..4eea3d33 100644
--- a/components/autofill/core/browser/data_manager/addresses/address_data_manager.cc
+++ b/components/autofill/core/browser/data_manager/addresses/address_data_manager.cc
@@ -9,6 +9,7 @@
 #include <memory>
 
 #include "base/check_deref.h"
+#include "base/check_is_test.h"
 #include "base/check_op.h"
 #include "base/containers/contains.h"
 #include "base/containers/to_vector.h"
@@ -178,21 +179,17 @@
 
   if (!has_initial_load_finished_) {
     has_initial_load_finished_ = true;
-    // `UpdateOrCreateAccountNameEmail()` is responsible for creating or
-    // updating the `kAccountNameEmail` profile. This requires profiles from the
-    // database to be loaded, so any old `kAccountNameEmail` profile can be
-    // accessed. Updates to the account info are generally caught by an identity
-    // observer. But if the account info becomes available before the initial
-    // load has finished, the additional call here is necessary to apply these
-    // updates.
+    // `AccountNameEmailStore::MaybeUpdateOrCreateAccountNameEmail()` is
+    // responsible for creating or updating the `kAccountNameEmail` profile.
+    // This requires profiles from the database to be loaded, so any old
+    // `kAccountNameEmail` profile can be accessed. Updates to the account info
+    // are generally caught by an identity observer. But if the account info
+    // becomes available before the initial load has finished, the additional
+    // call here is necessary to apply these updates.
     // TODO(crbug.com/356845298): Clean up after launch.
     if (base::FeatureList::IsEnabled(
             features::kAutofillEnableSupportForNameAndEmail)) {
-      const std::optional<CoreAccountInfo>& core_info = GetPrimaryAccountInfo();
-      if (core_info.has_value()) {
-        account_name_email_store_->UpdateOrCreateAccountNameEmail(
-            identity_manager_->FindExtendedAccountInfo(core_info.value()));
-      }
+      account_name_email_store_->MaybeUpdateOrCreateAccountNameEmail();
     } else {
       // In case the feature got disabled the profile should be cleaned up.
       if (!GetProfilesByRecordType(
@@ -237,7 +234,47 @@
   if (!IsAutofillProfileEnabled()) {
     return {};
   }
-  return GetProfiles(ProfileOrder::kHighestFrecencyDesc);
+
+  std::vector<const AutofillProfile*> profiles =
+      GetProfiles(ProfileOrder::kHighestFrecencyDesc);
+
+  // If the `pref_service_` doesn't exits the special logic which depends on
+  // prefs shouldn't run.
+  if (!pref_service_) {
+    CHECK_IS_TEST();
+    return profiles;
+  }
+
+  if (!base::FeatureList::IsEnabled(
+          features::kAutofillEnableSupportForNameAndEmail)) {
+    return profiles;
+  }
+
+  // `prefs::kAutofillNameAndEmailProfileNotSelectedCounter` counts how many
+  // times the suggestion for kAccountNameEmail profile was shown and wasn't
+  // accepted.
+  // TODO(crbug.com/441657423): If the profile was accepted, the counter will
+  // remain 0, yet the profile should be moved to the end. This will be fixed
+  // once a pref indicating the acceptance is added.
+  const bool name_email_profile_suggestion_was_shown_before =
+      pref_service_->GetInteger(
+          prefs::kAutofillNameAndEmailProfileNotSelectedCounter) == 0;
+  // Move the kAccountNameEmail profile to the front (or back) depending on
+  // the `name_email_profile_suggestion_was_shown_before`.
+  std::ranges::stable_partition(
+      profiles, [name_email_profile_suggestion_was_shown_before](
+                    const AutofillProfile* p) {
+        bool is_name_email_profile =
+            p->record_type() == AutofillProfile::RecordType::kAccountNameEmail;
+        // stable_partition() moves all elements where the predicate returns
+        // true to the front. The name/email profile should be in front when
+        // it hasn't been used before and in the back otherwise.
+        return name_email_profile_suggestion_was_shown_before
+                   ? is_name_email_profile
+                   : !is_name_email_profile;
+      });
+
+  return profiles;
 }
 
 std::vector<const AutofillProfile*> AddressDataManager::GetProfilesForSettings()
diff --git a/components/autofill/core/browser/data_manager/addresses/address_data_manager.h b/components/autofill/core/browser/data_manager/addresses/address_data_manager.h
index db97455..0679cdf 100644
--- a/components/autofill/core/browser/data_manager/addresses/address_data_manager.h
+++ b/components/autofill/core/browser/data_manager/addresses/address_data_manager.h
@@ -131,6 +131,8 @@
 
   // Returns the profiles to suggest to the user for filling, ordered by
   // frecency.
+  // If kAccountNameEmail is present in `profiles_`, it is always moved to the
+  // end of the vector unless this is the first time it is suggested.
   std::vector<const AutofillProfile*> GetProfilesToSuggest() const;
 
   // Returns all `GetProfiles()` in the order that the should be shown in the
diff --git a/components/autofill/core/browser/data_manager/addresses/address_data_manager_unittest.cc b/components/autofill/core/browser/data_manager/addresses/address_data_manager_unittest.cc
index 8537bf6..3cfae880 100644
--- a/components/autofill/core/browser/data_manager/addresses/address_data_manager_unittest.cc
+++ b/components/autofill/core/browser/data_manager/addresses/address_data_manager_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
+#include "base/time/time.h"
 #include "base/uuid.h"
 #include "build/buildflag.h"
 #include "components/autofill/core/browser/data_manager/personal_data_manager_test_utils.h"
@@ -21,6 +22,7 @@
 #include "components/autofill/core/browser/data_model/addresses/autofill_profile_test_api.h"
 #include "components/autofill/core/browser/data_quality/addresses/profile_token_quality_test_api.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
+#include "components/autofill/core/browser/test_utils/test_profiles.h"
 #include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
 #include "components/autofill/core/browser/webdata/autofill_change.h"
 #include "components/autofill/core/common/autofill_clock.h"
@@ -303,6 +305,49 @@
                                    Pointee(profile3)));
 }
 
+TEST_F(AddressDataManagerTest, GetProfilesToSuggest_NameEmailOrder) {
+  base::test::ScopedFeatureList scoped_feature_list{
+      features::kAutofillEnableSupportForNameAndEmail};
+  base::Time now = base::Time::Now();
+  AutofillProfile profile1 = test::GetFullProfile();
+  profile1.usage_history().set_use_date(now - base::Hours(2));
+  profile1.usage_history().set_use_count(1);
+  AutofillProfile profile2 = test::GetFullProfile2();
+  profile2.usage_history().set_use_date(now);
+  profile2.usage_history().set_use_count(1234);
+  AutofillProfile profile_name_email = test::AccountNameEmailProfile();
+  profile_name_email.usage_history().set_use_date(now - base::Hours(1));
+  profile_name_email.usage_history().set_use_count(1);
+
+  AddProfileToAddressDataManager(profile1);
+  AddProfileToAddressDataManager(profile2);
+  AddProfileToAddressDataManager(profile_name_email);
+
+  // Based solely on the default suggestion order, the `profile_name_email`
+  // should be placed in the middle.
+  ASSERT_THAT(
+      address_data_manager().GetProfiles(
+          AddressDataManager::ProfileOrder::kHighestFrecencyDesc),
+      testing::ElementsAre(Pointee(profile2), Pointee(profile_name_email),
+                           Pointee(profile1)));
+  ASSERT_EQ(
+      prefs_->GetInteger(prefs::kAutofillNameAndEmailProfileNotSelectedCounter),
+      0);
+  // Verify that calling the GetProfilesToSuggest() checks the counter, which
+  // will make the `profile_name_email` to be suggested first, because
+  // `prefs::kAutofillNameAndEmailProfileNotSelectedCounter` is 0.
+  EXPECT_THAT(address_data_manager().GetProfilesToSuggest(),
+              testing::ElementsAre(Pointee(profile_name_email),
+                                   Pointee(profile2), Pointee(profile1)));
+
+  prefs_->SetInteger(prefs::kAutofillNameAndEmailProfileNotSelectedCounter, 1);
+  // After the pref changes to a non zero value the kAccountNameEmail should be
+  // last.
+  EXPECT_THAT(address_data_manager().GetProfilesToSuggest(),
+              testing::ElementsAre(Pointee(profile2), Pointee(profile1),
+                                   Pointee(profile_name_email)));
+}
+
 // Test that profiles are not shown if |kAutofillProfileEnabled| is set to
 // |false|.
 TEST_F(AddressDataManagerTest, GetProfilesToSuggest_ProfileAutofillDisabled) {
diff --git a/components/autofill/core/browser/data_model/addresses/autofill_structured_address.cc b/components/autofill/core/browser/data_model/addresses/autofill_structured_address.cc
index fb9a682..d2b26dc 100644
--- a/components/autofill/core/browser/data_model/addresses/autofill_structured_address.cc
+++ b/components/autofill/core/browser/data_model/addresses/autofill_structured_address.cc
@@ -15,6 +15,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/data_model/addresses/autofill_i18n_api.h"
+#include "components/autofill/core/browser/data_model/addresses/autofill_normalization_utils.h"
 #include "components/autofill/core/browser/data_model/addresses/autofill_structured_address_component.h"
 #include "components/autofill/core/browser/data_model/addresses/autofill_structured_address_regex_provider.h"
 #include "components/autofill/core/browser/data_model/addresses/autofill_structured_address_utils.h"
@@ -47,6 +48,18 @@
 
 HouseNumberNode::~HouseNumberNode() = default;
 
+std::u16string HouseNumberNode::GetValueForComparison(
+    const std::u16string& value,
+    const AddressCountryCode& common_country_code) const {
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillAddressDiscardWhitespaceInHouseNumber)) {
+    return normalization::NormalizeForComparison(
+        value, normalization::WhitespaceSpec::kDiscard, common_country_code);
+  } else {
+    return AddressComponent::GetValueForComparison(value, common_country_code);
+  }
+}
+
 FloorNode::FloorNode(SubcomponentsList children)
     : AddressComponent(ADDRESS_HOME_FLOOR,
                        std::move(children),
diff --git a/components/autofill/core/browser/data_model/addresses/autofill_structured_address.h b/components/autofill/core/browser/data_model/addresses/autofill_structured_address.h
index 7065e14..b30f893 100644
--- a/components/autofill/core/browser/data_model/addresses/autofill_structured_address.h
+++ b/components/autofill/core/browser/data_model/addresses/autofill_structured_address.h
@@ -27,6 +27,10 @@
  public:
   explicit HouseNumberNode(SubcomponentsList children);
   ~HouseNumberNode() override;
+
+  std::u16string GetValueForComparison(
+      const std::u16string& value,
+      const AddressCountryCode& common_country_code) const override;
 };
 
 // Contains the specific location in the street (e.g. street name and house
diff --git a/components/autofill/core/browser/data_model/addresses/autofill_structured_address_unittest.cc b/components/autofill/core/browser/data_model/addresses/autofill_structured_address_unittest.cc
index 3286424e..8a4988e 100644
--- a/components/autofill/core/browser/data_model/addresses/autofill_structured_address_unittest.cc
+++ b/components/autofill/core/browser/data_model/addresses/autofill_structured_address_unittest.cc
@@ -3128,6 +3128,56 @@
   }
 }
 
+struct HouseNumberTestCase {
+  std::u16string input;
+  std::u16string expected;
+};
+
+class AutofillStructuredAddressHouseNumberTest
+    : public AutofillStructuredAddress,
+      public testing::WithParamInterface<HouseNumberTestCase> {
+ private:
+  base::test::ScopedFeatureList features_{
+      features::kAutofillAddressDiscardWhitespaceInHouseNumber};
+};
+
+TEST_P(AutofillStructuredAddressHouseNumberTest,
+       DiscardWhitespaceWhenNormalizingHouseNumber) {
+  const HouseNumberTestCase& test_case = GetParam();
+
+  AddressComponentsStore address =
+      i18n_model_definition::CreateAddressComponentModel();
+  AddressComponent* house_number_node =
+      test_api(*address.Root()).GetNodeForType(ADDRESS_HOME_HOUSE_NUMBER);
+
+  ASSERT_TRUE(house_number_node->SetValueForType(
+      ADDRESS_HOME_HOUSE_NUMBER, test_case.input,
+      VerificationStatus::kObserved));
+  ASSERT_EQ(house_number_node->GetValue(), test_case.input);
+  EXPECT_EQ(house_number_node->GetValueForComparisonForType(
+                ADDRESS_HOME_HOUSE_NUMBER, house_number_node->GetCountryCode()),
+            test_case.expected);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    AutofillStructuredAddressHouseNumberTest,
+    ::testing::ValuesIn<HouseNumberTestCase>(
+        {{u" 123 ", u"123"},
+         {u"123 A", u"123a"},
+         {u"123   a", u"123a"},
+         {u" 45 B ", u"45b"},
+         {u"123", u"123"},
+         {u"   ", u""},
+         {u"apt. 123", u"apt123"},
+         // Test cases below are considered equal because normalization removes
+         // character such as "/", "-" and whitespaces. The risks of merging
+         // profiles with "12/3" and "123" are considered acceptable as it
+         // requires other profile fields to match as well. Such cases are
+         // expected to be rare compared to the "123 A" and "123a".
+         {u"12/3", u"123"},
+         {u"12-3", u"123"}}));
+
 }  // namespace
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/data_model/autofill_ai/entity_instance.cc b/components/autofill/core/browser/data_model/autofill_ai/entity_instance.cc
index 2f3f2de8..b2db283 100644
--- a/components/autofill/core/browser/data_model/autofill_ai/entity_instance.cc
+++ b/components/autofill/core/browser/data_model/autofill_ai/entity_instance.cc
@@ -11,6 +11,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_field.h"
 #include "components/autofill/core/browser/data_model/addresses/autofill_i18n_api.h"
 #include "components/autofill/core/browser/data_model/addresses/autofill_normalization_utils.h"
 #include "components/autofill/core/browser/data_model/addresses/autofill_structured_address_component.h"
@@ -33,18 +34,14 @@
       attribute.GetRawInfo(attribute.type().field_type()));
 }
 
-std::u16string Format(std::u16string s,
-                      base::optional_ref<const std::u16string> format_string) {
-  if (!format_string) {
-    return s;
-  }
-
+// Returns `s` in the demanded `format`. See `data_util::IsValidDateFormat` for
+// the valid `format` values.
+std::u16string FormatAffix(std::u16string s, std::u16string_view format) {
   // We parse the leading minus here rather than using `base::StringToInt()` to
   // avoid mixing signed and unsigned integers as this easily leads to
   // undefined behavior.
-  std::u16string_view format = *format_string;
   bool suffix = false;
-  if (format_string->starts_with(u"-")) {
+  if (format.starts_with(u"-")) {
     format = format.substr(1);
     suffix = true;
   }
@@ -62,6 +59,44 @@
   return s;
 }
 
+// Returns `s` in the demanded `format`. See
+// `data_util::IsValidFlightNumberFormat` for the valid `format` values.
+std::u16string FormatFlightNumber(std::u16string s,
+                                  std::u16string_view format) {
+  // Invalid flight number - do not attempt to format.
+  if (s.size() < 3) {
+    return s;
+  }
+
+  // The airline designator corresponds to the first two characters.
+  if (format == u"A") {
+    return s.substr(0, 2);
+  }
+  // The number is the remainder of the string.
+  if (format == u"N") {
+    return s.substr(2);
+  }
+  return s;
+}
+
+std::u16string Format(
+    std::u16string s,
+    base::optional_ref<const AutofillFormatString> format_string) {
+  if (!format_string) {
+    return s;
+  }
+
+  switch (format_string->type) {
+    case FormatString_Type_AFFIX:
+      return FormatAffix(std::move(s), format_string->value);
+    case FormatString_Type_FLIGHT_NUMBER:
+      return FormatFlightNumber(std::move(s), format_string->value);
+    case FormatString_Type_DATE:
+      break;
+  }
+  return s;
+}
+
 }  // namespace
 
 AttributeInstance::AttributeInstance(AttributeType type)
@@ -90,7 +125,7 @@
 std::u16string AttributeInstance::GetInfo(
     FieldType field_type,
     const std::string& app_locale,
-    base::optional_ref<const std::u16string> format_string) const {
+    base::optional_ref<const AutofillFormatString> format_string) const {
   field_type = GetNormalizedFieldType(field_type);
   return std::visit(
       absl::Overload{[&](const CountryInfo& country) {
@@ -100,7 +135,7 @@
                        // TODO(crbug.com/396325496): Consider falling back
                        // to a locale-specific format by relying on
                        // `app_locale`.
-                       return date.GetDate(format_string ? *format_string
+                       return date.GetDate(format_string ? format_string->value
                                                          : u"YYYY-MM-DD");
                      },
                      [&](const NameInfo&) { return GetRawInfo(field_type); },
@@ -148,11 +183,12 @@
       info_);
 }
 
-void AttributeInstance::SetInfo(FieldType field_type,
-                                const std::u16string& value,
-                                const std::string& app_locale,
-                                std::u16string_view format_string,
-                                VerificationStatus status) {
+void AttributeInstance::SetInfo(
+    FieldType field_type,
+    const std::u16string& value,
+    const std::string& app_locale,
+    base::optional_ref<const AutofillFormatString> format_string,
+    VerificationStatus status) {
   field_type = GetNormalizedFieldType(field_type);
   std::visit(
       absl::Overload{
@@ -167,7 +203,12 @@
               country = CountryInfo();
             }
           },
-          [&](DateInfo& date) { date.SetDate(value, format_string); },
+          [&](DateInfo& date) {
+            date.SetDate(value, format_string && format_string->type ==
+                                                     FormatString_Type_DATE
+                                    ? format_string->value
+                                    : u"");
+          },
           [&](NameInfo& name) {
             if (!name.GetSupportedTypes().contains(field_type)) {
               return;
diff --git a/components/autofill/core/browser/data_model/autofill_ai/entity_instance.h b/components/autofill/core/browser/data_model/autofill_ai/entity_instance.h
index b883224..6b3ce93e 100644
--- a/components/autofill/core/browser/data_model/autofill_ai/entity_instance.h
+++ b/components/autofill/core/browser/data_model/autofill_ai/entity_instance.h
@@ -39,6 +39,7 @@
 // Entity instances are loaded from a webdata table and exposed through
 // EntityDataManager.
 class AttributeInstance;
+struct AutofillFormatString;
 class EntityInstance;
 class EntityTable;
 
@@ -94,7 +95,7 @@
   std::u16string GetInfo(
       FieldType field_type,
       const std::string& app_locale,
-      base::optional_ref<const std::u16string> format_string) const;
+      base::optional_ref<const AutofillFormatString> format_string) const;
 
   // Same as `GetInfo` but returns the value as stored with no formatting
   // whatsoever.
@@ -114,15 +115,19 @@
   // See GetInfo() for the meaning of `field_type`.
   //
   // Currently, the `format_string` only matters for dates. Dates are updated
-  // incrementally, e.g., SetInfo(..., u"16", ..., u"DD", ...) only changes the
-  // day and does not reset the month or year. If `value` doesn't fully match
-  // the `format_string`, the function is a no-op, e.g.,
-  // SetInfo(..., u"16/12/2022", ..., u"DD", ...) is a no-op.
+  // incrementally, e.g.,
+  //   SetInfo(..., u"16", ...,
+  //           AutofillFormatString::FromDateFormat(u"DD"), ...);
+  // only changes the day and does not reset the month or year. If `value`
+  // doesn't fully match the `format_string`, e.g.
+  //   SetInfo(..., u"16/12/2022", ...,
+  //           AutofillFormatString::FromDateFormat(u"DD"), ...);
+  // the function is a no-op.
   // See AutofillField::format_string() for the grammar of format strings.
   void SetInfo(FieldType field_type,
                const std::u16string& value,
                const std::string& app_locale,
-               std::u16string_view format_string,
+               base::optional_ref<const AutofillFormatString> format_string,
                VerificationStatus status);
 
   // Similar to SetInfo() but without canonicalization: It does not accept
diff --git a/components/autofill/core/browser/data_model/autofill_ai/entity_instance_unittest.cc b/components/autofill/core/browser/data_model/autofill_ai/entity_instance_unittest.cc
index 88ed5b7..97ac60d 100644
--- a/components/autofill/core/browser/data_model/autofill_ai/entity_instance_unittest.cc
+++ b/components/autofill/core/browser/data_model/autofill_ai/entity_instance_unittest.cc
@@ -8,6 +8,8 @@
 #include "base/time/time.h"
 #include "base/types/optional_ref.h"
 #include "base/uuid.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/data_model/addresses/autofill_structured_address_component.h"
 #include "components/autofill/core/browser/data_model/autofill_ai/entity_type.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
@@ -24,16 +26,13 @@
 
 struct GetInfoParams {
   std::string app_locale = "";
-  const char16_t* format_string = nullptr;
+  std::optional<AutofillFormatString> format_string;
 };
 
 std::u16string GetInfo(const AttributeInstance& a,
                        FieldType field_type,
                        GetInfoParams params = {}) {
-  return a.GetInfo(field_type, params.app_locale,
-                   params.format_string
-                       ? std::optional(std::u16string(params.format_string))
-                       : std::nullopt);
+  return a.GetInfo(field_type, params.app_locale, params.format_string);
 }
 
 TEST(AutofillEntityInstanceTest, Attributes) {
@@ -58,13 +57,13 @@
 TEST(AutofillEntityInstanceTest, Attributes_NormalizedType) {
   AttributeInstance passport_name((AttributeType(kPassportName)));
   passport_name.SetInfo(NAME_FULL, u"John Doe",
-                        /*app_locale=*/"", /*format_string=*/u"",
+                        /*app_locale=*/"", /*format_string=*/std::nullopt,
                         VerificationStatus::kObserved);
   passport_name.FinalizeInfo();
 
   AttributeInstance passport_number((AttributeType(kPassportNumber)));
   passport_number.SetInfo(PASSPORT_NUMBER, u"LR0123456",
-                          /*app_locale=*/"", /*format_string=*/u"",
+                          /*app_locale=*/"", /*format_string=*/std::nullopt,
                           VerificationStatus::kObserved);
 
   EXPECT_EQ(GetInfo(passport_name, NAME_FULL), u"John Doe");
@@ -82,7 +81,7 @@
 TEST(AutofillEntityInstanceTest, Attributes_CountryLocalization) {
   AttributeInstance passport_country((AttributeType(kPassportCountry)));
   passport_country.SetInfo(PASSPORT_ISSUING_COUNTRY, u"SE",
-                           /*app_locale=*/"", /*format_string=*/u"",
+                           /*app_locale=*/"", /*format_string=*/std::nullopt,
                            VerificationStatus::kObserved);
 
   EXPECT_EQ(GetInfo(passport_country, PASSPORT_ISSUING_COUNTRY,
@@ -109,7 +108,7 @@
 TEST(AutofillEntityInstanceTest, Attributes_StructuredName) {
   AttributeInstance passport_name((AttributeType(kPassportName)));
   passport_name.SetInfo(NAME_FULL, u"Some Name",
-                        /*app_locale=*/"", /*format_string=*/u"",
+                        /*app_locale=*/"", /*format_string=*/std::nullopt,
                         VerificationStatus::kObserved);
   passport_name.FinalizeInfo();
 
@@ -121,37 +120,98 @@
 
 // Tests that AttributeInstance honors the affix formats.
 TEST(AutofillEntityInstanceTest, Attributes_IdentificationNumbers) {
+  auto from_affix = [](std::u16string fs) {
+    return AutofillFormatString(std::move(fs), FormatString_Type_AFFIX);
+  };
+
   AttributeInstance passport_number((AttributeType(kPassportNumber)));
   passport_number.SetInfo(PASSPORT_NUMBER, u"LR0123456",
-                          /*app_locale=*/"", /*format_string=*/u"",
+                          /*app_locale=*/"", /*format_string=*/std::nullopt,
                           VerificationStatus::kObserved);
   EXPECT_EQ(GetInfo(passport_number, PASSPORT_NUMBER), u"LR0123456");
-  EXPECT_EQ(GetInfo(passport_number, PASSPORT_NUMBER, {.format_string = u"0"}),
+  EXPECT_EQ(GetInfo(passport_number, PASSPORT_NUMBER,
+                    {.format_string = from_affix(u"0")}),
             u"LR0123456");
-  EXPECT_EQ(GetInfo(passport_number, PASSPORT_NUMBER, {.format_string = u"4"}),
+  EXPECT_EQ(GetInfo(passport_number, PASSPORT_NUMBER,
+                    {.format_string = from_affix(u"4")}),
             u"LR01");
-  EXPECT_EQ(GetInfo(passport_number, PASSPORT_NUMBER, {.format_string = u"-4"}),
+  EXPECT_EQ(GetInfo(passport_number, PASSPORT_NUMBER,
+                    {.format_string = from_affix(u"-4")}),
             u"3456");
   EXPECT_EQ(GetInfo(passport_number, PASSPORT_NUMBER,
-                    {.format_string =
-                         base::NumberToString16(std::numeric_limits<int>::min())
-                             .c_str()}),
+                    {.format_string = from_affix(base::NumberToString16(
+                         std::numeric_limits<int>::min()))}),
             u"LR0123456");
 }
 
 // Tests that AttributeInstance appropriately manages dates.
 TEST(AutofillEntityInstanceTest, Attributes_Date) {
+  auto from_date = [](std::u16string fs) {
+    return AutofillFormatString(std::move(fs), FormatString_Type_DATE);
+  };
+
   AttributeInstance passport_name((AttributeType(kPassportIssueDate)));
   passport_name.SetInfo(PASSPORT_ISSUE_DATE, u"2001-02-03",
-                        /*app_locale=*/"", /*format_string=*/u"YYYY-MM-DD",
+                        /*app_locale=*/"", from_date(u"YYYY-MM-DD"),
                         VerificationStatus::kNoStatus);
 
   EXPECT_EQ(GetInfo(passport_name, PASSPORT_ISSUE_DATE), u"2001-02-03");
   EXPECT_EQ(GetInfo(passport_name, PASSPORT_ISSUE_DATE,
-                    {.format_string = u"DD/MM/YYYY"}),
+                    {.format_string = from_date(u"DD/MM/YYYY")}),
             u"03/02/2001");
 }
 
+// Tests that formatting flight numbers works correctly.
+TEST(AutofillEntityInstanceTest, AttributesFlightFormat) {
+  auto from_flight_number = [](std::u16string fs) {
+    return AutofillFormatString(std::move(fs), FormatString_Type_FLIGHT_NUMBER);
+  };
+
+  {
+    AttributeInstance flight_number(
+        (AttributeType(kFlightReservationFlightNumber)));
+    flight_number.SetInfo(
+        FLIGHT_RESERVATION_FLIGHT_NUMBER, u"LH89", /*app_locale*/ "",
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
+    EXPECT_EQ(GetInfo(flight_number, FLIGHT_RESERVATION_FLIGHT_NUMBER),
+              u"LH89");
+    EXPECT_EQ(GetInfo(flight_number, FLIGHT_RESERVATION_FLIGHT_NUMBER,
+                      {.format_string = from_flight_number(u"A")}),
+              u"LH");
+    EXPECT_EQ(GetInfo(flight_number, FLIGHT_RESERVATION_FLIGHT_NUMBER,
+                      {.format_string = from_flight_number(u"N")}),
+              u"89");
+    EXPECT_EQ(GetInfo(flight_number, FLIGHT_RESERVATION_FLIGHT_NUMBER,
+                      {.format_string = from_flight_number(u"F")}),
+              u"LH89");
+    EXPECT_EQ(GetInfo(flight_number, FLIGHT_RESERVATION_FLIGHT_NUMBER,
+                      {.format_string = from_flight_number(u"")}),
+              u"LH89");
+  }
+
+  {
+    // A mal-formed flight number.
+    AttributeInstance flight_number(
+        (AttributeType(kFlightReservationFlightNumber)));
+    flight_number.SetInfo(
+        FLIGHT_RESERVATION_FLIGHT_NUMBER, u"AA", /*app_locale*/ "",
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
+    EXPECT_EQ(GetInfo(flight_number, FLIGHT_RESERVATION_FLIGHT_NUMBER), u"AA");
+    EXPECT_EQ(GetInfo(flight_number, FLIGHT_RESERVATION_FLIGHT_NUMBER,
+                      {.format_string = from_flight_number(u"A")}),
+              u"AA");
+    EXPECT_EQ(GetInfo(flight_number, FLIGHT_RESERVATION_FLIGHT_NUMBER,
+                      {.format_string = from_flight_number(u"N")}),
+              u"AA");
+    EXPECT_EQ(GetInfo(flight_number, FLIGHT_RESERVATION_FLIGHT_NUMBER,
+                      {.format_string = from_flight_number(u"F")}),
+              u"AA");
+    EXPECT_EQ(GetInfo(flight_number, FLIGHT_RESERVATION_FLIGHT_NUMBER,
+                      {.format_string = from_flight_number(u"F")}),
+              u"AA");
+  }
+}
+
 TEST(AutofillEntityInstanceTest,
      GetEntityMergeability_IdentiticalEntities_NoMergeableAttribute_IsASubset) {
   EntityInstance::EntityMergeability result =
diff --git a/components/autofill/core/browser/filling/autofill_ai/field_filling_entity_util.cc b/components/autofill/core/browser/filling/autofill_ai/field_filling_entity_util.cc
index 62c0d4c..955aeed 100644
--- a/components/autofill/core/browser/filling/autofill_ai/field_filling_entity_util.cc
+++ b/components/autofill/core/browser/filling/autofill_ai/field_filling_entity_util.cc
@@ -90,10 +90,12 @@
     return std::nullopt;
   }
 
-  auto get_part = [&](const std::u16string& format_string, uint32_t min = 0,
+  auto get_part = [&](std::u16string format_string, uint32_t min = 0,
                       uint32_t max =
                           std::numeric_limits<uint32_t>::max()) -> uint32_t {
-    std::u16string s = attribute.GetInfo(type, app_locale, format_string);
+    std::u16string s = attribute.GetInfo(
+        type, app_locale,
+        AutofillFormatString(std::move(format_string), FormatString_Type_DATE));
     unsigned int i = 0;
     return base::StringToUint(s, &i) && min <= i && i <= max
                ? i
diff --git a/components/autofill/core/browser/filling/autofill_ai/field_filling_entity_util_unittest.cc b/components/autofill/core/browser/filling/autofill_ai/field_filling_entity_util_unittest.cc
index 4206450..d3cddb7 100644
--- a/components/autofill/core/browser/filling/autofill_ai/field_filling_entity_util_unittest.cc
+++ b/components/autofill/core/browser/filling/autofill_ai/field_filling_entity_util_unittest.cc
@@ -565,7 +565,8 @@
 TEST_F(GetFillValueForEntityTest_Date, FillingDateValueIntoTextInput) {
   auto field = CreateInput(FormControlType::kInputText);
   field->set_format_string_unless_overruled(
-      u"DD/MM/YYYY", AutofillField::FormatStringSource::kServer);
+      AutofillFormatString(u"DD/MM/YYYY", FormatString_Type_DATE),
+      AutofillFormatStringSource::kServer);
   EXPECT_EQ(
       GetFillValueForEntity(passport(), field, mojom::ActionPersistence::kFill,
                             /*app_locale=*/"",
diff --git a/components/autofill/core/browser/filling/form_filler_unittest.cc b/components/autofill/core/browser/filling/form_filler_unittest.cc
index 864412d..120ee272 100644
--- a/components/autofill/core/browser/filling/form_filler_unittest.cc
+++ b/components/autofill/core/browser/filling/form_filler_unittest.cc
@@ -1588,8 +1588,9 @@
   auto set_format_string = [&](size_t field_index,
                                std::string_view format_string) {
     form_structure->fields()[field_index]->set_format_string_unless_overruled(
-        base::UTF8ToUTF16(format_string),
-        AutofillField::FormatStringSource::kServer);
+        AutofillFormatString(base::UTF8ToUTF16(format_string),
+                             FormatString_Type_DATE),
+        AutofillFormatStringSource::kServer);
   };
   set_server_type(0, PASSPORT_NUMBER);
   set_server_type(1, NAME_FIRST);
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index 5526467..eacb567 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -268,9 +268,9 @@
         it != field_to_attribute_types.end()) {
       annotated_field.attribute_types = AttributeTypesToString(it->second);
     }
-    if (base::optional_ref<const std::u16string> format_string =
+    if (base::optional_ref<const AutofillFormatString> format_string =
             field->format_string()) {
-      annotated_field.format_string = base::UTF16ToUTF8(*format_string);
+      annotated_field.format_string = base::UTF16ToUTF8(format_string->value);
     }
     annotated_field.html_type = FieldTypeToStringView(field->html_type());
     annotated_field.overall_type = [&] {
@@ -436,7 +436,7 @@
     field->set_previously_autofilled(cached_field->previously_autofilled());
     field->set_did_trigger_suggestions(cached_field->did_trigger_suggestions());
     field->set_was_focused(cached_field->was_focused());
-    if (base::optional_ref<const std::u16string> format_string =
+    if (base::optional_ref<const AutofillFormatString> format_string =
             cached_field->format_string()) {
       field->set_format_string_unless_overruled(
           *format_string, cached_field->format_string_source());
@@ -829,25 +829,25 @@
       buffer << Tr{} << "Autofill AI AttributeTypes:"
              << AttributeTypesToString(it->second);
     }
-    if (base::optional_ref<const std::u16string> format_string =
+    if (base::optional_ref<const AutofillFormatString> format_string =
             field->format_string()) {
       std::string_view source;
       switch (field->format_string_source()) {
-        case AutofillField::FormatStringSource::kUnset:
+        case AutofillFormatStringSource::kUnset:
           source = "unset";
           break;
-        case AutofillField::FormatStringSource::kHeuristics:
+        case AutofillFormatStringSource::kHeuristics:
           source = "heuristic";
           break;
-        case AutofillField::FormatStringSource::kModelResult:
+        case AutofillFormatStringSource::kModelResult:
           source = "model";
           break;
-        case AutofillField::FormatStringSource::kServer:
+        case AutofillFormatStringSource::kServer:
           source = "server";
           break;
       }
       buffer << Tr{} << "Format string:"
-             << base::StrCat({"\"", base::UTF16ToUTF8(*format_string),
+             << base::StrCat({"\"", base::UTF16ToUTF8(format_string->value),
                               "\" from ", source});
     }
     buffer << Tr{} << "Section:" << field->section();
diff --git a/components/autofill/core/browser/form_structure_rationalizer.cc b/components/autofill/core/browser/form_structure_rationalizer.cc
index b58e0e74..36a42191 100644
--- a/components/autofill/core/browser/form_structure_rationalizer.cc
+++ b/components/autofill/core/browser/form_structure_rationalizer.cc
@@ -710,8 +710,9 @@
                         << "Set format string of " << field.global_id()
                         << " to " << format_string;
     field.set_format_string_unless_overruled(
-        std::move(format_string),
-        AutofillField::FormatStringSource::kHeuristics);
+        AutofillFormatString(std::move(format_string),
+                             FormatString_Type::FormatString_Type_DATE),
+        AutofillFormatStringSource::kHeuristics);
   };
 
   auto get_autofill_ai_date_types = [](const AutofillField& field) {
@@ -732,11 +733,11 @@
       continue;
     }
     switch (field.format_string_source()) {
-      case AutofillField::FormatStringSource::kUnset:
-      case AutofillField::FormatStringSource::kHeuristics:
+      case AutofillFormatStringSource::kUnset:
+      case AutofillFormatStringSource::kHeuristics:
         break;  // Breaks the switch, not the loop.
-      case AutofillField::FormatStringSource::kModelResult:
-      case AutofillField::FormatStringSource::kServer:
+      case AutofillFormatStringSource::kModelResult:
+      case AutofillFormatStringSource::kServer:
         continue;
     }
 
diff --git a/components/autofill/core/browser/form_structure_rationalizer_unittest.cc b/components/autofill/core/browser/form_structure_rationalizer_unittest.cc
index 6dbb05e..c1afd6b 100644
--- a/components/autofill/core/browser/form_structure_rationalizer_unittest.cc
+++ b/components/autofill/core/browser/form_structure_rationalizer_unittest.cc
@@ -167,14 +167,15 @@
   return ElementsAre(FieldTypeSet{types}...);
 }
 
-std::vector<std::optional<std::string>> GetFormatStrings(
+std::vector<std::optional<std::string>> GetDateFormatStrings(
     const FormStructure& form_structure) {
   std::vector<std::optional<std::string>> format_strings;
   format_strings.reserve(form_structure.field_count());
   for (size_t i = 0; i < form_structure.field_count(); ++i) {
-    if (std::optional<std::u16string> format_string =
-            form_structure.field(i)->format_string().CopyAsOptional()) {
-      format_strings.emplace_back(base::UTF16ToUTF8(*format_string));
+    if (std::optional<AutofillFormatString> format_string =
+            form_structure.field(i)->format_string().CopyAsOptional();
+        format_string && format_string->type == FormatString_Type_DATE) {
+      format_strings.emplace_back(base::UTF16ToUTF8(format_string->value));
     } else {
       format_strings.push_back(std::nullopt);
     }
@@ -1236,8 +1237,9 @@
        {.field_type = PASSPORT_EXPIRATION_DATE, .placeholder = "DD/MM/YYYY"}},
       /*run_heuristics=*/false);
   form_structure->fields()[1]->set_format_string_unless_overruled(
-      u"YYYY-MM-DD", AutofillField::FormatStringSource::kServer);
-  EXPECT_THAT(GetFormatStrings(*form_structure),
+      AutofillFormatString(u"YYYY-MM-DD", FormatString_Type_DATE),
+      AutofillFormatStringSource::kServer);
+  EXPECT_THAT(GetDateFormatStrings(*form_structure),
               ElementsAre(std::nullopt, "YYYY-MM-DD", "DD/MM/YYYY"));
 }
 
@@ -1247,9 +1249,10 @@
       {{.field_type = PASSPORT_EXPIRATION_DATE, .placeholder = "DD/MM/YYYY"},
        {.field_type = PASSPORT_EXPIRATION_DATE, .placeholder = "DD/MM/YYYY"}},
       /*run_heuristics=*/false);
-  form_structure->fields().back()->set_format_string_unless_overruled(
-      u"YYYY-MM-DD", AutofillField::FormatStringSource::kServer);
-  EXPECT_THAT(GetFormatStrings(*form_structure),
+  form_structure->fields()[1]->set_format_string_unless_overruled(
+      AutofillFormatString(u"YYYY-MM-DD", FormatString_Type_DATE),
+      AutofillFormatStringSource::kServer);
+  EXPECT_THAT(GetDateFormatStrings(*form_structure),
               ElementsAre("DD/MM/YYYY", "YYYY-MM-DD"));
 }
 
@@ -1266,7 +1269,7 @@
         .value = "YYYY-MM-DD"}},
       /*run_heuristics=*/false);
   EXPECT_THAT(
-      GetFormatStrings(*form_structure),
+      GetDateFormatStrings(*form_structure),
       ElementsAre("YYYY-MM-DD", "DD", "YYYY-MM", "DD/MM/YY", "DD/MM/YY"));
 }
 
@@ -1287,7 +1290,7 @@
         .value = "YYYY-MM-DD"}},
       /*run_heuristics=*/false);
   EXPECT_THAT(
-      GetFormatStrings(*form_structure),
+      GetDateFormatStrings(*form_structure),
       ElementsAre("YYYY-MM-DD", "DD", "YYYY-MM", "DD/MM/YY", "DD/MM/YY"));
 }
 
@@ -1300,7 +1303,7 @@
                           {.label = "Until which D/M/YY is the thing valid?",
                            .field_type = PASSPORT_EXPIRATION_DATE}},
                          /*run_heuristics=*/false);
-  EXPECT_THAT(GetFormatStrings(*form_structure),
+  EXPECT_THAT(GetDateFormatStrings(*form_structure),
               ElementsAre("YYYY-MM-DD", "D/M/YY"));
 }
 
@@ -1317,7 +1320,7 @@
                           {.label = "Until which D/M/YY is the thing valid?",
                            .field_type = PASSPORT_EXPIRATION_DATE}},
                          /*run_heuristics=*/false);
-  EXPECT_THAT(GetFormatStrings(*form_structure),
+  EXPECT_THAT(GetDateFormatStrings(*form_structure),
               ElementsAre("YYYY", "MM", "DD", "D/M/YY"));
 }
 
@@ -1332,7 +1335,7 @@
                           {.label = "Until which D/M/YY is the thing valid?",
                            .field_type = PASSPORT_ISSUE_DATE}},
                          /*run_heuristics=*/false);
-  EXPECT_THAT(GetFormatStrings(*form_structure),
+  EXPECT_THAT(GetDateFormatStrings(*form_structure),
               ElementsAre("DD", "MM", "YYYY", "D/M/YY"));
 }
 
@@ -1347,7 +1350,7 @@
                           {.label = "Until which D/M/YY is the thing valid?",
                            .field_type = PASSPORT_EXPIRATION_DATE}},
                          /*run_heuristics=*/false);
-  EXPECT_THAT(GetFormatStrings(*form_structure),
+  EXPECT_THAT(GetDateFormatStrings(*form_structure),
               ElementsAre("YYYY", "MM", "D/M/YY"));
 }
 
@@ -1360,7 +1363,7 @@
                           {.label = "When did you pick it up? YYYY-MM-DD",
                            .field_type = PASSPORT_ISSUE_DATE}},
                          /*run_heuristics=*/false);
-  EXPECT_THAT(GetFormatStrings(*form_structure),
+  EXPECT_THAT(GetDateFormatStrings(*form_structure),
               ElementsAre("YYYY-MM-DD", "YYYY-MM-DD"));
 }
 
@@ -1378,7 +1381,7 @@
                            .field_type = PASSPORT_ISSUE_DATE}},
                          /*run_heuristics=*/false);
   // There is no particular motivation for this assignment.
-  EXPECT_THAT(GetFormatStrings(*form_structure),
+  EXPECT_THAT(GetDateFormatStrings(*form_structure),
               ElementsAre("YYYY", "MM", "DD", "YYYY-MM-DD"));
 }
 
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager.cc b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
index d63c62e2..4dd940c 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
@@ -726,9 +726,12 @@
       field->MaybeAddServerPrediction(std::move(server_prediction));
     }
     if (!prediction.format_string.empty()) {
+      // TODO(crbug.com/389625753): Either implement format strings properly
+      // in the model or remove this here.
       field->set_format_string_unless_overruled(
-          prediction.format_string,
-          AutofillField::FormatStringSource::kModelResult);
+          AutofillFormatString(std::move(prediction.format_string),
+                               FormatString_Type_DATE),
+          AutofillFormatStringSource::kModelResult);
     }
   }
 }
@@ -1354,6 +1357,8 @@
   // autofill. The warning is shown even if there are no autofill suggestions
   // available.
   if (IsFormMixedContent(client(), form) &&
+      client().GetPrefs()->FindPreference(
+          ::prefs::kMixedFormsWarningsEnabled) &&
       client().GetPrefs()->GetBoolean(::prefs::kMixedFormsWarningsEnabled)) {
     LOG_AF(log_manager()) << LoggingScope::kFilling
                           << LogMessage::kSuggestionSuppressed
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
index 8ca356e1..8ac5314 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
@@ -7873,9 +7873,9 @@
   EXPECT_THAT(fs->field(3)->Type().GetAutofillAiTypes(),
               ElementsAre(PASSPORT_ISSUE_DATE));
   ASSERT_TRUE(fs->field(3)->format_string().has_value());
-  EXPECT_EQ(fs->field(3)->format_string().value(), u"D.M.YYYY");
+  EXPECT_EQ(fs->field(3)->format_string()->value, u"D.M.YYYY");
   EXPECT_EQ(fs->field(3)->format_string_source(),
-            AutofillField::FormatStringSource::kModelResult);
+            AutofillFormatStringSource::kModelResult);
 }
 
 // Tests that if the form has at least one existing AutofillAI prediction, then
diff --git a/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_import_utils.cc b/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_import_utils.cc
index 6b318a9..fc866ca 100644
--- a/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_import_utils.cc
+++ b/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_import_utils.cc
@@ -48,7 +48,7 @@
 
 struct ValueAndFormatString {
   std::u16string value;
-  std::u16string format_string;
+  AutofillFormatString format_string;
 };
 
 // Returns the value and format string of `field` for import by Autofill AI.
@@ -58,9 +58,9 @@
       !field.IsSelectElement()) {
     std::u16string value = field.value_for_import();
     base::TrimWhitespace(value, base::TRIM_ALL, &value);
-    return {
-        .value = std::move(value),
-        .format_string = field.format_string() ? *field.format_string() : u""};
+    return {.value = std::move(value),
+            .format_string = field.format_string() ? *field.format_string()
+                                                   : AutofillFormatString()};
   }
 
   auto get_value = [&](DatePartRange range) {
@@ -77,13 +77,19 @@
     }
     return std::u16string();
   };
+
+  auto make_date_format = [](std::u16string fs) {
+    return AutofillFormatString(std::move(fs), FormatString_Type_DATE);
+  };
+
   std::u16string value;
   if (!(value = get_value(GetYearRange(field.options()))).empty()) {
-    return {.value = std::move(value), .format_string = u"YYYY"};
+    return {.value = std::move(value),
+            .format_string = make_date_format(u"YYYY")};
   } else if (!(value = get_value(GetMonthRange(field.options()))).empty()) {
-    return {.value = std::move(value), .format_string = u"M"};
+    return {.value = std::move(value), .format_string = make_date_format(u"M")};
   } else if (!(value = get_value(GetDayRange(field.options()))).empty()) {
-    return {.value = std::move(value), .format_string = u"D"};
+    return {.value = std::move(value), .format_string = make_date_format(u"D")};
   }
   return {};
 }
@@ -139,7 +145,8 @@
         // Do not import entities that have an attribute whose value is a proper
         // prefix or suffix.
         if (IsAffixFormatStringEnabledForType(field_type) &&
-            data_util::IsValidAffixFormat(value.format_string,
+            value.format_string.type == FormatString_Type_AFFIX &&
+            data_util::IsValidAffixFormat(value.format_string.value,
                                           /*exclude_full_value=*/true)) {
           if (auto it = section_to_entity_types_attributes.find(section);
               it != section_to_entity_types_attributes.end()) {
@@ -207,7 +214,10 @@
     int part = 0;
     // The app_locale is irrelevant for dates.
     bool success = base::StringToInt(
-        attribute.GetInfo(field_type, /*app_locale=*/"", format), &part);
+        attribute.GetInfo(
+            field_type, /*app_locale=*/"",
+            AutofillFormatString(std::move(format), FormatString_Type_DATE)),
+        &part);
     return success ? part : 0;
   };
   base::Time time;
diff --git a/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_import_utils_unittest.cc b/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_import_utils_unittest.cc
index bac6e9dff..85b702f7 100644
--- a/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_import_utils_unittest.cc
+++ b/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_import_utils_unittest.cc
@@ -84,7 +84,7 @@
     FormControlType form_control_type,
     const std::vector<FieldType>& field_types,
     std::string_view value,
-    std::string_view format_string = "",
+    std::optional<AutofillFormatString> format_string = std::nullopt,
     std::string_view initial_value = "") {
   auto field = std::make_unique<AutofillField>(test::CreateTestFormField(
       /*label=*/"",
@@ -93,10 +93,9 @@
   // Explicitly set the value here to ensure that it differs from the initial
   // value.
   field->set_value(base::UTF8ToUTF16(value));
-  if (!format_string.empty()) {
+  if (format_string) {
     field->set_format_string_unless_overruled(
-        base::UTF8ToUTF16(format_string),
-        AutofillField::FormatStringSource::kServer);
+        std::move(*format_string), AutofillFormatStringSource::kServer);
   }
   AddPrediction(*field, field_types);
   return field;
@@ -106,10 +105,10 @@
     FormControlType form_control_type,
     FieldType field_type,
     std::string_view value,
-    std::string_view format_string = "",
+    std::optional<AutofillFormatString> format_string = std::nullopt,
     std::string_view initial_value = "") {
   return CreateInput(form_control_type, std::vector<FieldType>{field_type},
-                     value, format_string, initial_value);
+                     value, std::move(format_string), initial_value);
 }
 
 std::unique_ptr<AutofillField> CreateSelect(
@@ -160,13 +159,16 @@
   // are ignored during import.
   fields.push_back(CreateInput(FormControlType::kInputText,
                                FieldType::PASSPORT_ISSUING_COUNTRY, "Sweden",
-                               /*format_string=*/"", "Sweden"));
-  fields.push_back(CreateInput(FormControlType::kInputText,
-                               FieldType::PASSPORT_ISSUE_DATE, "24", "DD"));
-  fields.push_back(CreateInput(FormControlType::kInputText,
-                               FieldType::PASSPORT_ISSUE_DATE, "12", "MM"));
-  fields.push_back(CreateInput(FormControlType::kInputText,
-                               FieldType::PASSPORT_ISSUE_DATE, "2025", "YYYY"));
+                               /*format_string=*/std::nullopt, "Sweden"));
+  fields.push_back(
+      CreateInput(FormControlType::kInputText, FieldType::PASSPORT_ISSUE_DATE,
+                  "24", AutofillFormatString(u"DD", FormatString_Type_DATE)));
+  fields.push_back(
+      CreateInput(FormControlType::kInputText, FieldType::PASSPORT_ISSUE_DATE,
+                  "12", AutofillFormatString(u"MM", FormatString_Type_DATE)));
+  fields.push_back(CreateInput(
+      FormControlType::kInputText, FieldType::PASSPORT_ISSUE_DATE, "2025",
+      AutofillFormatString(u"YYYY", FormatString_Type_DATE)));
 
   EXPECT_THAT(GetPossibleEntitiesFromSubmittedForm(fields, "en-US",
                                                    GeoIpCountryCode("US")),
@@ -253,10 +255,13 @@
                        CreateAttribute(kDriversLicenseName,
                                        "Karlsson on the Roof")))));
 
+  auto from_affix = [](std::u16string fs) {
+    return AutofillFormatString(std::move(fs), FormatString_Type_AFFIX);
+  };
   fields[1]->set_format_string_unless_overruled(
-      u"3", AutofillField::FormatStringSource::kServer);
+      from_affix(u"3"), AutofillFormatStringSource::kServer);
   fields[2]->set_format_string_unless_overruled(
-      u"0", AutofillField::FormatStringSource::kServer);
+      from_affix(u"0"), AutofillFormatStringSource::kServer);
   EXPECT_THAT(
       GetPossibleEntitiesFromSubmittedForm(fields, "en-US",
                                            GeoIpCountryCode("US")),
diff --git a/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_manager_unittest.cc b/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_manager_unittest.cc
index 0779bd53..f8f0f444 100644
--- a/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_manager_unittest.cc
+++ b/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_manager_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/time/time.h"
+#include "components/autofill/core/browser/autofill_field.h"
 #include "components/autofill/core/browser/data_manager/autofill_ai/entity_data_manager.h"
 #include "components/autofill/core/browser/data_model/addresses/autofill_structured_address_component.h"
 #include "components/autofill/core/browser/data_model/autofill_ai/entity_instance.h"
@@ -864,7 +865,8 @@
       existing_entity_without_expiry_dates, AttributeType(kPassportNumber)));
   form->field(1)->set_value(u"01/02/20");
   form->field(1)->set_format_string_unless_overruled(
-      u"DD/MM/YY", AutofillField::FormatStringSource::kServer);
+      AutofillFormatString(u"DD/MM/YY", FormatString_Type_DATE),
+      AutofillFormatStringSource::kServer);
 
   std::optional<EntityInstance> new_entity;
   std::optional<EntityInstance> old_entity;
@@ -911,7 +913,8 @@
   // Set a new expiry date value, different from the one currently stored.
   form->field(1)->set_value(u"01/02/20");
   form->field(1)->set_format_string_unless_overruled(
-      u"DD/MM/YY", AutofillField::FormatStringSource::kServer);
+      AutofillFormatString(u"DD/MM/YY", FormatString_Type_DATE),
+      AutofillFormatStringSource::kServer);
 
   std::optional<EntityInstance> new_entity;
   std::optional<EntityInstance> old_entity;
@@ -969,7 +972,8 @@
   // Issue date.
   form->field(2)->set_value(u"01/02/16");
   form->field(2)->set_format_string_unless_overruled(
-      u"DD/MM/YY", AutofillField::FormatStringSource::kServer);
+      AutofillFormatString(u"DD/MM/YY", FormatString_Type_DATE),
+      AutofillFormatStringSource::kServer);
 
   // Since the current entity instance is read only, no prompt should be shown.
   EXPECT_CALL(autofill_client(), ShowEntitySaveOrUpdateBubble).Times(0);
diff --git a/components/autofill/core/browser/integrators/autofill_ai/metrics/autofill_ai_logger_unittest.cc b/components/autofill/core/browser/integrators/autofill_ai/metrics/autofill_ai_logger_unittest.cc
index 4b434f1..7cc2c6a 100644
--- a/components/autofill/core/browser/integrators/autofill_ai/metrics/autofill_ai_logger_unittest.cc
+++ b/components/autofill/core/browser/integrators/autofill_ai/metrics/autofill_ai_logger_unittest.cc
@@ -54,6 +54,7 @@
 using ::testing::NiceMock;
 using ::testing::Return;
 using ::testing::ReturnRef;
+using ::testing::UnorderedElementsAreArray;
 
 constexpr auto kVehicle = EntityType(EntityTypeName::kVehicle);
 constexpr auto kDriversLicense = EntityType(EntityTypeName::kDriversLicense);
@@ -810,6 +811,10 @@
                                                   : UNKNOWN_TYPE;
               }()))
         << event;
+    EXPECT_THAT(mqls_field_event.field_types(),
+                UnorderedElementsAreArray(field.Type().GetTypes()));
+    EXPECT_THAT(mqls_field_event.ai_field_types(),
+                UnorderedElementsAreArray(field.Type().GetAutofillAiTypes()));
     EXPECT_EQ(base::to_underlying(mqls_field_event.format_string_source()),
               base::to_underlying(field.format_string_source()))
         << event;
diff --git a/components/autofill/core/browser/integrators/autofill_ai/metrics/autofill_ai_ukm_logger.cc b/components/autofill/core/browser/integrators/autofill_ai/metrics/autofill_ai_ukm_logger.cc
index 87779210..4eb25f7 100644
--- a/components/autofill/core/browser/integrators/autofill_ai/metrics/autofill_ai_ukm_logger.cc
+++ b/components/autofill/core/browser/integrators/autofill_ai/metrics/autofill_ai_ukm_logger.cc
@@ -55,15 +55,15 @@
 }
 
 optimization_guide::proto::FormatStringSource GetFormatStringSource(
-    AutofillField::FormatStringSource format_string_source) {
+    AutofillFormatStringSource format_string_source) {
   switch (format_string_source) {
-    case AutofillField::FormatStringSource::kUnset:
+    case AutofillFormatStringSource::kUnset:
       return optimization_guide::proto::FORMAT_STRING_SOURCE_UNSET;
-    case AutofillField::FormatStringSource::kHeuristics:
+    case AutofillFormatStringSource::kHeuristics:
       return optimization_guide::proto::FORMAT_STRING_SOURCE_HEURISTICS;
-    case AutofillField::FormatStringSource::kModelResult:
+    case AutofillFormatStringSource::kModelResult:
       return optimization_guide::proto::FORMAT_STRING_SOURCE_ML_MODEL;
-    case AutofillField::FormatStringSource::kServer:
+    case AutofillFormatStringSource::kServer:
       return optimization_guide::proto::FORMAT_STRING_SOURCE_SERVER;
   }
   NOTREACHED();
@@ -316,13 +316,6 @@
   const FieldTypeSet field_types = field.Type().GetTypes();
   const FieldTypeSet ai_field_types = field.Type().GetAutofillAiTypes();
 
-  // TODO(crbug.com/432645177): Emit multiple `field_types` and
-  // `ai_field_types`.
-  const auto field_type = base::to_underlying(
-      !field_types.empty() ? *field_types.begin() : UNKNOWN_TYPE);
-  const auto ai_field_type = base::to_underlying(
-      !ai_field_types.empty() ? *ai_field_types.begin() : UNKNOWN_TYPE);
-
   if (optimization_guide::ModelQualityLogsUploaderService* uploader_ =
           client_->GetMqlsUploadService();
       uploader_ &&
@@ -352,8 +345,16 @@
     mqls_field_event->set_field_rank(field.rank());
     mqls_field_event->set_field_rank_in_signature_group(
         field.rank_in_signature_group());
-    mqls_field_event->set_field_type(field_type);
-    mqls_field_event->set_ai_field_type(ai_field_type);
+    mqls_field_event->set_field_type(base::to_underlying(
+        !field_types.empty() ? *field_types.begin() : UNKNOWN_TYPE));
+    mqls_field_event->set_ai_field_type(base::to_underlying(
+        !ai_field_types.empty() ? *ai_field_types.begin() : UNKNOWN_TYPE));
+    for (FieldType field_type : field_types) {
+      mqls_field_event->add_field_types(field_type);
+    }
+    for (FieldType ai_field_type : ai_field_types) {
+      mqls_field_event->add_ai_field_types(ai_field_type);
+    }
     mqls_field_event->set_format_string_source(
         GetFormatStringSource(field.format_string_source()));
     mqls_field_event->set_form_control_type(
diff --git a/components/autofill/core/browser/payments/autofill_wallet_data_type_controller.cc b/components/autofill/core/browser/payments/autofill_wallet_data_type_controller.cc
index ba95605..e1f2b9f 100644
--- a/components/autofill/core/browser/payments/autofill_wallet_data_type_controller.cc
+++ b/components/autofill/core/browser/payments/autofill_wallet_data_type_controller.cc
@@ -79,4 +79,9 @@
   sync_service_->DataTypePreconditionChanged(type());
 }
 
+void AutofillWalletDataTypeController::OnSyncShutdown(
+    syncer::SyncService* sync) {
+  // Nothing to be done, `this` will be destructed imminently.
+}
+
 }  // namespace browser_sync
diff --git a/components/autofill/core/browser/payments/autofill_wallet_data_type_controller.h b/components/autofill/core/browser/payments/autofill_wallet_data_type_controller.h
index c12ddfb..66380d5d 100644
--- a/components/autofill/core/browser/payments/autofill_wallet_data_type_controller.h
+++ b/components/autofill/core/browser/payments/autofill_wallet_data_type_controller.h
@@ -47,6 +47,7 @@
 
   // syncer::SyncServiceObserver implementation.
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
  private:
   // Callback for changes to the autofill pref.
diff --git a/components/autofill/core/browser/suggestions/payments/credit_card_suggestion_generator.cc b/components/autofill/core/browser/suggestions/payments/credit_card_suggestion_generator.cc
new file mode 100644
index 0000000..4f0f14d
--- /dev/null
+++ b/components/autofill/core/browser/suggestions/payments/credit_card_suggestion_generator.cc
@@ -0,0 +1,94 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/suggestions/payments/credit_card_suggestion_generator.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/functional/callback.h"
+#include "base/functional/function_ref.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/data_manager/payments/payments_data_manager.h"
+#include "components/autofill/core/browser/data_manager/personal_data_manager.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/foundations/autofill_client.h"
+#include "components/autofill/core/browser/suggestions/suggestion.h"
+#include "components/autofill/core/browser/suggestions/suggestion_generator.h"
+#include "components/autofill/core/common/form_data.h"
+
+namespace autofill {
+
+using SuggestionDataSource = SuggestionGenerator::SuggestionDataSource;
+
+CreditCardSuggestionGenerator::CreditCardSuggestionGenerator(
+    AutofillClient* client,
+    const std::vector<std::string>& four_digit_combinations_in_dom)
+    : client_(client),
+      four_digit_combinations_in_dom_(four_digit_combinations_in_dom) {}
+
+CreditCardSuggestionGenerator::~CreditCardSuggestionGenerator() = default;
+
+void CreditCardSuggestionGenerator::FetchSuggestionData(
+    const FormData& form_data,
+    const FormFieldData& field_data,
+    const FormStructure* form,
+    const AutofillField* field,
+    const AutofillClient& client,
+    base::OnceCallback<
+        void(std::pair<SuggestionDataSource,
+                       std::vector<SuggestionGenerator::SuggestionData>>)>
+        callback) {
+  FetchSuggestionData(
+      form_data, field_data, form, field, client,
+      [&callback](std::pair<SuggestionDataSource,
+                            std::vector<SuggestionGenerator::SuggestionData>>
+                      suggestion_data) {
+        std::move(callback).Run(std::move(suggestion_data));
+      });
+}
+
+void CreditCardSuggestionGenerator::GenerateSuggestions(
+    const FormData& form_data,
+    const FormFieldData& field_data,
+    const FormStructure* form,
+    const AutofillField* field,
+    const std::vector<
+        std::pair<SuggestionDataSource, std::vector<SuggestionData>>>&
+        all_suggestion_data,
+    base::OnceCallback<void(ReturnedSuggestions)> callback) {
+  GenerateSuggestions(
+      form_data, field_data, form, field, all_suggestion_data,
+      [&callback](ReturnedSuggestions returned_suggestions) {
+        std::move(callback).Run(std::move(returned_suggestions));
+      });
+}
+
+void CreditCardSuggestionGenerator::FetchSuggestionData(
+    const FormData& form_data,
+    const FormFieldData& field_data,
+    const FormStructure* form,
+    const AutofillField* field,
+    const AutofillClient& client,
+    base::FunctionRef<
+        void(std::pair<SuggestionDataSource,
+                       std::vector<SuggestionGenerator::SuggestionData>>)>
+        callback) {
+  // TODO(crbug.com/409962888): will appear in the next CL chain links.
+}
+
+void CreditCardSuggestionGenerator::GenerateSuggestions(
+    const FormData& form_data,
+    const FormFieldData& field_data,
+    const FormStructure* form,
+    const AutofillField* field,
+    const std::vector<
+        std::pair<SuggestionDataSource, std::vector<SuggestionData>>>&
+        all_suggestion_data,
+    base::FunctionRef<void(ReturnedSuggestions)> callback) {
+  // TODO(crbug.com/409962888): will appear in the next CL chain links.
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/suggestions/payments/credit_card_suggestion_generator.h b/components/autofill/core/browser/suggestions/payments/credit_card_suggestion_generator.h
new file mode 100644
index 0000000..fcfcdae6
--- /dev/null
+++ b/components/autofill/core/browser/suggestions/payments/credit_card_suggestion_generator.h
@@ -0,0 +1,137 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTIONS_PAYMENTS_CREDIT_CARD_SUGGESTION_GENERATOR_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTIONS_PAYMENTS_CREDIT_CARD_SUGGESTION_GENERATOR_H_
+
+#include "base/functional/function_ref.h"
+#include "components/autofill/core/browser/data_manager/payments/payments_data_manager.h"
+#include "components/autofill/core/browser/data_manager/personal_data_manager.h"
+#include "components/autofill/core/browser/data_model/payments/autofill_wallet_usage_data.h"
+#include "components/autofill/core/browser/data_model/payments/credit_card.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/foundations/autofill_client.h"
+#include "components/autofill/core/browser/logging/log_manager.h"
+#include "components/autofill/core/browser/metrics/payments/card_metadata_metrics.h"
+#include "components/autofill/core/browser/metrics/suggestions_list_metrics.h"
+#include "components/autofill/core/browser/payments/payments_autofill_client.h"
+#include "components/autofill/core/browser/payments/save_and_fill_manager.h"
+#include "components/autofill/core/browser/suggestions/suggestion.h"
+#include "components/autofill/core/browser/suggestions/suggestion_generator.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/prefs/pref_service.h"
+#include "components/sync/service/sync_service.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+
+class AutofillClient;
+class PaymentsDataManager;
+class FormFieldData;
+
+// A `SuggestionGenerator` for `FillingProduct::kCreditCard`.
+//
+// This class encapsulates logic used exclusively for generating credit card
+// suggestions. Free functions, that are also used in TouchToFill feature,
+// are still shared in payments_suggestion_generator.h file.
+class CreditCardSuggestionGenerator : public SuggestionGenerator {
+ public:
+  explicit CreditCardSuggestionGenerator(
+      AutofillClient* client,
+      const std::vector<std::string>& four_digit_combinations_in_dom);
+  ~CreditCardSuggestionGenerator() override;
+
+  void FetchSuggestionData(
+      const FormData& form_data,
+      const FormFieldData& field_data,
+      const FormStructure* form,
+      const AutofillField* field,
+      const AutofillClient& client,
+      base::OnceCallback<
+          void(std::pair<SuggestionDataSource,
+                         std::vector<SuggestionGenerator::SuggestionData>>)>
+          callback) override;
+
+  void GenerateSuggestions(
+      const FormData& form_data,
+      const FormFieldData& field_data,
+      const FormStructure* form,
+      const AutofillField* field,
+      const std::vector<
+          std::pair<SuggestionDataSource, std::vector<SuggestionData>>>&
+          all_suggestion_data,
+      base::OnceCallback<void(ReturnedSuggestions)> callback) override;
+
+  // Like SuggestionGenerator override, but takes a base::FunctionRef instead of
+  // a base::OnceCallback. Calls that callback exactly once.
+  // TODO(crbug.com/409962888): Clean up after launch.
+  void FetchSuggestionData(
+      const FormData& form_data,
+      const FormFieldData& field_data,
+      const FormStructure* form,
+      const AutofillField* field,
+      const AutofillClient& client,
+      base::FunctionRef<void(std::pair<SuggestionDataSource,
+                                       std::vector<SuggestionData>>)> callback);
+
+  // Like SuggestionGenerator override, but takes a base::FunctionRef instead of
+  // a base::OnceCallback. Calls that callback exactly once.
+  // TODO(crbug.com/409962888): Clean up after launch.
+  void GenerateSuggestions(
+      const FormData& form_data,
+      const FormFieldData& field_data,
+      const FormStructure* form,
+      const AutofillField* field,
+      const std::vector<
+          std::pair<SuggestionDataSource, std::vector<SuggestionData>>>&
+          all_suggestion_data,
+      base::FunctionRef<void(ReturnedSuggestions)> callback);
+
+  void SetCreditCardUploadStatusForTest(bool value) {
+    credit_card_upload_enabled_for_test_ = value;
+  }
+
+  void SetConsiderFromAsSecureForTest(bool value) {
+    consider_form_as_secure_for_testing_ = value;
+  }
+
+ private:
+  PaymentsDataManager* payments_data_manager() const {
+    return &client_->GetPersonalDataManager().payments_data_manager();
+  }
+
+  payments::SaveAndFillManager* save_and_fill_manager() const {
+    return client_->GetPaymentsAutofillClient()->GetSaveAndFillManager();
+  }
+
+  PrefService* pref_service() const { return client_->GetPrefs(); }
+
+  syncer::SyncService* sync_service() const {
+    return client_->GetSyncService();
+  }
+
+  LogManager* log_manager() const { return client_->GetCurrentLogManager(); }
+
+  payments::PaymentsAutofillClient* payments_autofill_client() const {
+    return client_->GetPaymentsAutofillClient();
+  }
+
+  raw_ptr<AutofillClient> client_;
+
+  const std::vector<std::string> four_digit_combinations_in_dom_;
+
+  // TODO(crbug.com/409962888): Make naming consistent after moving all logic.
+  std::optional<bool> credit_card_upload_enabled_for_test_;
+  std::optional<bool> consider_form_as_secure_for_testing_;
+
+  base::flat_map<std::string, VirtualCardUsageData::VirtualCardLastFour>
+      virtual_card_guid_to_last_four_map_;
+
+  base::WeakPtrFactory<CreditCardSuggestionGenerator> weak_ptr_factory_{this};
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTIONS_PAYMENTS_CREDIT_CARD_SUGGESTION_GENERATOR_H_
diff --git a/components/autofill/core/browser/test_utils/autofill_test_utils.cc b/components/autofill/core/browser/test_utils/autofill_test_utils.cc
index 18259f0c..858080e 100644
--- a/components/autofill/core/browser/test_utils/autofill_test_utils.cc
+++ b/components/autofill/core/browser/test_utils/autofill_test_utils.cc
@@ -913,35 +913,37 @@
     attributes.emplace_back(AttributeType(kPassportNumber));
     attributes.back().SetInfo(
         PASSPORT_NUMBER, options.number, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   if (options.name) {
     attributes.emplace_back(AttributeType(kPassportName));
     attributes.back().SetInfo(
         NAME_FULL, options.name, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
     attributes.back().FinalizeInfo();
   }
   if (options.country) {
     attributes.emplace_back(AttributeType(kPassportCountry));
     attributes.back().SetInfo(PASSPORT_ISSUING_COUNTRY, options.country,
                               std::string(options.app_locale),
-                              /*format_string=*/u"",
+                              /*format_string=*/std::nullopt,
                               VerificationStatus::kNoStatus);
   }
   if (options.expiry_date) {
     attributes.emplace_back(AttributeType(kPassportExpirationDate));
-    attributes.back().SetInfo(PASSPORT_EXPIRATION_DATE, options.expiry_date,
-                              std::string(options.app_locale),
-                              /*format_string=*/u"YYYY-MM-DD",
-                              VerificationStatus::kNoStatus);
+    attributes.back().SetInfo(
+        PASSPORT_EXPIRATION_DATE, options.expiry_date,
+        std::string(options.app_locale),
+        AutofillFormatString(u"YYYY-MM-DD", FormatString_Type_DATE),
+        VerificationStatus::kNoStatus);
   }
   if (options.issue_date) {
     attributes.emplace_back(AttributeType(kPassportIssueDate));
-    attributes.back().SetInfo(PASSPORT_ISSUE_DATE, options.issue_date,
-                              std::string(options.app_locale),
-                              /*format_string=*/u"YYYY-MM-DD",
-                              VerificationStatus::kNoStatus);
+    attributes.back().SetInfo(
+        PASSPORT_ISSUE_DATE, options.issue_date,
+        std::string(options.app_locale),
+        AutofillFormatString(u"YYYY-MM-DD", FormatString_Type_DATE),
+        VerificationStatus::kNoStatus);
   }
   return EntityInstance(
       EntityType(EntityTypeName::kPassport), std::move(attributes),
@@ -966,34 +968,36 @@
     attributes.emplace_back(AttributeType(kDriversLicenseName));
     attributes.back().SetInfo(
         NAME_FULL, options.name, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
     attributes.back().FinalizeInfo();
   }
   if (options.region) {
     attributes.emplace_back(AttributeType(kDriversLicenseState));
     attributes.back().SetInfo(
         DRIVERS_LICENSE_REGION, options.region, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   if (options.number) {
     attributes.emplace_back(AttributeType(kDriversLicenseNumber));
     attributes.back().SetInfo(
         DRIVERS_LICENSE_NUMBER, options.number, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   if (options.expiration_date) {
     attributes.emplace_back(AttributeType(kDriversLicenseExpirationDate));
     attributes.back().SetInfo(
         DRIVERS_LICENSE_EXPIRATION_DATE, options.expiration_date,
-        std::string(options.app_locale), /*format_string=*/u"YYYY-MM-DD",
+        std::string(options.app_locale),
+        AutofillFormatString(u"YYYY-MM-DD", FormatString_Type_DATE),
         VerificationStatus::kNoStatus);
   }
   if (options.issue_date) {
     attributes.emplace_back(AttributeType(kDriversLicenseIssueDate));
-    attributes.back().SetInfo(DRIVERS_LICENSE_ISSUE_DATE, options.issue_date,
-                              std::string(options.app_locale),
-                              /*format_string=*/u"YYYY-MM-DD",
-                              VerificationStatus::kNoStatus);
+    attributes.back().SetInfo(
+        DRIVERS_LICENSE_ISSUE_DATE, options.issue_date,
+        std::string(options.app_locale),
+        AutofillFormatString(u"YYYY-MM-DD", FormatString_Type_DATE),
+        VerificationStatus::kNoStatus);
   }
   return EntityInstance(
       EntityType(EntityTypeName::kDriversLicense), std::move(attributes),
@@ -1019,13 +1023,14 @@
     attributes.emplace_back(AttributeType(kKnownTravelerNumberNumber));
     attributes.back().SetInfo(
         KNOWN_TRAVELER_NUMBER, options.number, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   if (options.expiration_date) {
     attributes.emplace_back(AttributeType(kKnownTravelerNumberNumber));
     attributes.back().SetInfo(
         DRIVERS_LICENSE_EXPIRATION_DATE, options.expiration_date,
-        std::string(options.app_locale), /*format_string=*/u"YYYY-MM-DD",
+        std::string(options.app_locale),
+        AutofillFormatString(u"YYYY-MM-DD", FormatString_Type_DATE),
         VerificationStatus::kNoStatus);
   }
   return EntityInstance(
@@ -1044,7 +1049,7 @@
     attributes.emplace_back(AttributeType(kRedressNumberNumber));
     attributes.back().SetInfo(
         REDRESS_NUMBER, options.number, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
 
   return EntityInstance(
@@ -1063,44 +1068,44 @@
     attributes.emplace_back(AttributeType(kVehicleOwner));
     attributes.back().SetInfo(
         NAME_FULL, options.name, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
     attributes.back().FinalizeInfo();
   }
   if (options.plate) {
     attributes.emplace_back(AttributeType(kVehiclePlateNumber));
     attributes.back().SetInfo(
         VEHICLE_LICENSE_PLATE, options.plate, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   if (options.number) {
     attributes.emplace_back(AttributeType(kVehicleVin));
     attributes.back().SetInfo(
         VEHICLE_VIN, options.number, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   if (options.make) {
     attributes.emplace_back(AttributeType(kVehicleMake));
     attributes.back().SetInfo(
         VEHICLE_MAKE, options.make, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   if (options.model) {
     attributes.emplace_back(AttributeType(kVehicleModel));
     attributes.back().SetInfo(
         VEHICLE_MODEL, options.model, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   if (options.year) {
     attributes.emplace_back(AttributeType(kVehicleYear));
     attributes.back().SetInfo(
         VEHICLE_YEAR, options.year, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   if (options.state) {
     attributes.emplace_back(AttributeType(kVehiclePlateState));
     attributes.back().SetInfo(
         VEHICLE_PLATE_STATE, options.state, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   return EntityInstance(
       EntityType(EntityTypeName::kVehicle), std::move(attributes),
@@ -1124,21 +1129,21 @@
     attributes.emplace_back(AttributeType(kNationalIdCardNumber));
     attributes.back().SetInfo(NATIONAL_ID_CARD_NUMBER, options.number,
                               std::string(options.app_locale),
-                              /*format_string=*/u"",
+                              /*format_string=*/std::nullopt,
                               VerificationStatus::kNoStatus);
   }
   if (options.country) {
     attributes.emplace_back(AttributeType(kNationalIdCardCountry));
     attributes.back().SetInfo(NATIONAL_ID_CARD_ISSUING_COUNTRY, options.country,
                               std::string(options.app_locale),
-                              /*format_string=*/u"",
+                              /*format_string=*/std::nullopt,
                               VerificationStatus::kNoStatus);
   }
   if (options.issue_date) {
     attributes.emplace_back(AttributeType(kNationalIdCardIssueDate));
     attributes.back().SetInfo(NATIONAL_ID_CARD_ISSUE_DATE, options.issue_date,
                               std::string(options.app_locale),
-                              /*format_string=*/u"",
+                              /*format_string=*/std::nullopt,
                               VerificationStatus::kNoStatus);
   }
   if (options.expiry_date) {
@@ -1146,7 +1151,7 @@
     attributes.back().SetInfo(
         NATIONAL_ID_CARD_EXPIRATION_DATE, options.expiry_date,
         std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   return EntityInstance(
       EntityType(EntityTypeName::kNationalIdCard), std::move(attributes),
@@ -1165,7 +1170,7 @@
     attributes.emplace_back(AttributeType(kFlightReservationPassengerName));
     attributes.back().SetInfo(
         NAME_FULL, options.name, std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
     attributes.back().FinalizeInfo();
   }
   if (options.flight_number) {
@@ -1173,35 +1178,35 @@
     attributes.back().SetInfo(
         FLIGHT_RESERVATION_FLIGHT_NUMBER, options.flight_number,
         std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   if (options.ticket_number) {
     attributes.emplace_back(AttributeType(kFlightReservationTicketNumber));
     attributes.back().SetInfo(
         FLIGHT_RESERVATION_TICKET_NUMBER, options.ticket_number,
         std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   if (options.confirmation_code) {
     attributes.emplace_back(AttributeType(kFlightReservationConfirmationCode));
     attributes.back().SetInfo(
         FLIGHT_RESERVATION_CONFIRMATION_CODE, options.confirmation_code,
         std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   if (options.departure_airport) {
     attributes.emplace_back(AttributeType(kFlightReservationDepartureAirport));
     attributes.back().SetInfo(
         FLIGHT_RESERVATION_DEPARTURE_AIRPORT, options.departure_airport,
         std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
   if (options.arrival_airport) {
     attributes.emplace_back(AttributeType(kFlightReservationArrivalAirport));
     attributes.back().SetInfo(
         FLIGHT_RESERVATION_ARRIVAL_AIRPORT, options.arrival_airport,
         std::string(options.app_locale),
-        /*format_string=*/u"", VerificationStatus::kNoStatus);
+        /*format_string=*/std::nullopt, VerificationStatus::kNoStatus);
   }
 
   return EntityInstance(
diff --git a/components/autofill/core/browser/test_utils/autofill_test_utils.h b/components/autofill/core/browser/test_utils/autofill_test_utils.h
index fc0313d..b7c5d7a 100644
--- a/components/autofill/core/browser/test_utils/autofill_test_utils.h
+++ b/components/autofill/core/browser/test_utils/autofill_test_utils.h
@@ -466,9 +466,9 @@
 
 template <typename = void>
 struct FlightReservationOptionsT {
-  const char16_t* flight_number = u"987654321";
+  const char16_t* flight_number = u"AA123";
   const char16_t* ticket_number = u"123123456";
-  const char16_t* confirmation_code = u"0123";
+  const char16_t* confirmation_code = u"AB4KW5";
   const char16_t* name = u"John Doe";
   const char16_t* departure_airport = u"MUC";
   const char16_t* arrival_airport = u"BEY";
diff --git a/components/autofill/core/browser/webdata/autofill_ai/entity_table.cc b/components/autofill/core/browser/webdata/autofill_ai/entity_table.cc
index 0b3b274..d82eab4 100644
--- a/components/autofill/core/browser/webdata/autofill_ai/entity_table.cc
+++ b/components/autofill/core/browser/webdata/autofill_ai/entity_table.cc
@@ -96,10 +96,13 @@
                                std::u16string value) -> AttributeInstance {
       auto type = AttributeType(type_name);
       auto instance = AttributeInstance(AttributeType(type));
-      instance.SetInfo(instance.type().field_type(), value, /*app_locale=*/"",
-                       /*format_string=*/
-                       IsDateFieldType(type.field_type()) ? u"YYYY-MM-DD" : u"",
-                       VerificationStatus::kNoStatus);
+      instance.SetInfo(
+          instance.type().field_type(), value, /*app_locale=*/"",
+          /*format_string=*/
+          IsDateFieldType(type.field_type())
+              ? AutofillFormatString(u"YYYY-MM-DD", FormatString_Type_DATE)
+              : base::optional_ref<const AutofillFormatString>(),
+          VerificationStatus::kNoStatus);
       return instance;
     };
 
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index dad161a..a6d8101 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -33,6 +33,12 @@
 BASE_FEATURE(kAutofillAcrossIframesIosTriggerFormExtraction,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// If enabled, whitespace is discarded during normalization of a house
+// number field.
+// TODO(crbug.com/447111009): Remove when launched.
+BASE_FEATURE(kAutofillAddressDiscardWhitespaceInHouseNumber,
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Feature flag to control displaying of Autofill suggestions on
 // unclassified fields based on prefix matching. These suggestions are displayed
 // after the user typed a certain number of characters that match some data
@@ -198,6 +204,12 @@
 BASE_FEATURE(kAutofillAiVoteForFormatStringsForAffixes,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// If enabled, votes for the format of flight number fields are uploaded. For
+// example, if there is a flight number "LH89" on file, a submitted value of
+// "89" on a field with type `FLIGHT_RESERVATION_FLIGHT_NUMBER` uploads "N".
+BASE_FEATURE(kAutofillAiVoteForFormatStringsForFlightNumbers,
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Enables the second iteration AutofillAI.
 BASE_FEATURE(kAutofillAiWithDataSchema,
              IS_AUTOFILL_AI_PLATFORM ? base::FEATURE_ENABLED_BY_DEFAULT
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index 3ddc252..6aa1803a 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -20,6 +20,8 @@
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillAcrossIframesIosTriggerFormExtraction);
 COMPONENT_EXPORT(AUTOFILL)
+BASE_DECLARE_FEATURE(kAutofillAddressDiscardWhitespaceInHouseNumber);
+COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillAddressSuggestionsOnTyping);
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillAddressUserDeclinedSaveSurvey);
@@ -75,6 +77,8 @@
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillAiVoteForFormatStringsForAffixes);
 COMPONENT_EXPORT(AUTOFILL)
+BASE_DECLARE_FEATURE(kAutofillAiVoteForFormatStringsForFlightNumbers);
+COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillAiWithDataSchema);
 COMPONENT_EXPORT(AUTOFILL)
 extern const base::FeatureParam<int>
diff --git a/components/autofill/core/common/autofill_prefs.cc b/components/autofill/core/common/autofill_prefs.cc
index dfb51ae..f7b33941 100644
--- a/components/autofill/core/common/autofill_prefs.cc
+++ b/components/autofill/core/common/autofill_prefs.cc
@@ -58,6 +58,9 @@
   registry->RegisterIntegerPref(
       kAutofillNameAndEmailProfileNotSelectedCounter, 0,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
+  registry->RegisterBooleanPref(
+      kAutofillWasNameAndEmailProfileUsed, false,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
 
   // Non-synced prefs. Used for per-device choices, e.g., signin promo.
   registry->RegisterDictionaryPref(kAutofillAiOptInStatus);
diff --git a/components/autofill/core/common/autofill_prefs.h b/components/autofill/core/common/autofill_prefs.h
index 87ac3fc..6118d4f 100644
--- a/components/autofill/core/common/autofill_prefs.h
+++ b/components/autofill/core/common/autofill_prefs.h
@@ -118,6 +118,12 @@
 // accept `kAccountNameEmail` profile suggestion.
 inline constexpr char kAutofillNameAndEmailProfileNotSelectedCounter[] =
     "autofill.name_and_email_profile_not_selected_counter";
+// Boolean responsible for storing if kAccountNameEmail profile suggestion was
+// filled and submitted. The use_count (and other types of `AutofillProfile`
+// metadata) are not synced for kAccountNameEmail profile, thus making the
+// tracking of the usage pref based.
+inline constexpr char kAutofillWasNameAndEmailProfileUsed[] =
+    "autofill.was_name_and_email_profile_used";
 // Integer that is set to the last major version where the Autocomplete
 // retention policy was run.
 inline constexpr char kAutocompleteLastVersionRetentionPolicy[] =
diff --git a/components/autofill/core/common/save_password_progress_logger.cc b/components/autofill/core/common/save_password_progress_logger.cc
index 09d66ae1..6b215ea 100644
--- a/components/autofill/core/common/save_password_progress_logger.cc
+++ b/components/autofill/core/common/save_password_progress_logger.cc
@@ -588,7 +588,7 @@
       return "Actor login: the form to fill went away";
     case STRING_ACTOR_LOGIN_NO_USERNAME_FIELD:
       return "Actor login: no username field";
-    case STRING_ACTOR_LOGIN_NO_PASWORD_FIELD:
+    case STRING_ACTOR_LOGIN_NO_PASSWORD_FIELD:
       return "Actor login: no password field";
     case STRING_ACTOR_LOGIN_FILLING_FIELD_WITH_ID:
       return "Actor login: filling field with id";
diff --git a/components/autofill/core/common/save_password_progress_logger.h b/components/autofill/core/common/save_password_progress_logger.h
index f58a3ac..c1923f5 100644
--- a/components/autofill/core/common/save_password_progress_logger.h
+++ b/components/autofill/core/common/save_password_progress_logger.h
@@ -223,7 +223,7 @@
     STRING_ACTOR_LOGIN_FRAME_CHANGED,
     STRING_ACTOR_LOGIN_FORM_WENT_AWAY,
     STRING_ACTOR_LOGIN_NO_USERNAME_FIELD,
-    STRING_ACTOR_LOGIN_NO_PASWORD_FIELD,
+    STRING_ACTOR_LOGIN_NO_PASSWORD_FIELD,
     STRING_ACTOR_LOGIN_FILLING_FIELD_WITH_ID,
     STRING_ACTOR_LOGIN_USERNAME_FILL_SUCCESS,
     STRING_ACTOR_LOGIN_PASSWORD_FILL_SUCCESS,
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/chips/ChipView.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/chips/ChipView.java
index b92e671..2e5c2b9f 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/chips/ChipView.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/chips/ChipView.java
@@ -70,6 +70,7 @@
     private final ChromeImageView mStartIcon;
     private final boolean mUseRoundedStartIcon;
     private final LoadingView mLoadingView;
+    private final @Px int mTextStartPadding;
     private final @StyleRes int mSecondaryTextAppearanceId;
     private final boolean mTextAlignStart;
     private final int mEndIconWidth;
@@ -191,8 +192,7 @@
                                 .getDimensionPixelSize(
                                         R.dimen.chip_text_multiline_vertical_padding));
         mTextAlignStart = a.getBoolean(R.styleable.ChipView_textAlignStart, false);
-        @Px
-        int textStartPadding =
+        mTextStartPadding =
                 a.getDimensionPixelSize(
                         R.styleable.ChipView_primaryTextStartPadding,
                         getResources()
@@ -234,6 +234,8 @@
                 new AppCompatTextView(new ContextThemeWrapper(getContext(), R.style.ChipTextView));
         mPrimaryText.setId(R.id.chip_view_primary_text);
         mPrimaryText.setTextAppearance(primaryTextAppearance);
+        // Reduce font padding if the text is aligned vertically.
+        mPrimaryText.setIncludeFontPadding(!alignTextVertically);
 
         // If false fall back to single line defined in XML styles.
         if (allowMultipleLines) {
@@ -251,7 +253,7 @@
             mPrimaryText.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
         }
         mPrimaryText.setPaddingRelative(
-                textStartPadding,
+                mTextStartPadding,
                 mPrimaryText.getPaddingTop(),
                 mPrimaryText.getPaddingEnd(),
                 mPrimaryText.getPaddingBottom());
@@ -456,6 +458,8 @@
                             new ContextThemeWrapper(getContext(), R.style.ChipTextView));
             mSecondaryText.setId(R.id.chip_view_secondary_text);
             mSecondaryText.setTextAppearance(mSecondaryTextAppearanceId);
+            // Reduce font padding if the text is aligned vertically.
+            mSecondaryText.setIncludeFontPadding(isSingleLineChip());
             // Ensure that basic state changes are aligned with the ChipView. They update
             // automatically once the view is part of the hierarchy.
             mSecondaryText.setSelected(isSelected());
@@ -464,6 +468,13 @@
                 if (mTextAlignStart) {
                     mSecondaryText.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
                 }
+                // Align secondary text view with the primary text view if they are stacked
+                // vertically.
+                mSecondaryText.setPaddingRelative(
+                        mTextStartPadding,
+                        mSecondaryText.getPaddingTop(),
+                        mSecondaryText.getPaddingEnd(),
+                        mSecondaryText.getPaddingBottom());
                 mTextViewsWrapper.addView(mSecondaryText);
             } else {
                 addView(mSecondaryText);
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/chips/ChipViewRenderTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/chips/ChipViewRenderTest.java
index 8bbb9f4..c621c0c 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/chips/ChipViewRenderTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/chips/ChipViewRenderTest.java
@@ -64,6 +64,7 @@
     public final RenderTestRule mRenderTestRule =
             RenderTestRule.Builder.withPublicCorpus()
                     .setBugComponent(Component.UI_BROWSER_MOBILE)
+                    .setRevision(2)
                     .build();
 
     private ViewGroup mContentView;
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index 1442a3c..9b811380 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -28,9 +28,12 @@
 _gn_path = "//buildtools/linux64/gn"
 _ninja_path = "//third_party/ninja/ninja"
 
-assert(!is_component_build)
 assert(is_cronet_build)
 
+assert(!is_component_build)
+assert(use_platform_icu_alternatives,
+       "Cronet only supports being built with ICU alternatives")
+
 generate_jni("cronet_jni_headers") {
   sources = [
     "java/src/org/chromium/net/impl/CompletionOnceCallback.java",
@@ -198,7 +201,6 @@
     "//net",
     "//third_party/zlib",
     "//url",
-    "//url:buildflags",
   ]
   if (!is_cronet_for_aosp_build) {
     deps += [ "//components/cronet/native:cronet_native_impl" ]
@@ -240,10 +242,6 @@
     "log",
   ]
 
-  if (!use_platform_icu_alternatives) {
-    deps += [ "//base:i18n" ]
-  }
-
   # TODO(crbug.com/40031409): Fix code that adds exit-time destructors and
   # enable the diagnostic by removing this line.
   configs += [ "//build/config/compiler:no_exit_time_destructors" ]
@@ -1190,7 +1188,6 @@
     ":cronet_test_apk_jni",
     ":cronet_tests_jni_headers",
     "//base",
-    "//base:i18n",
     "//base/test:test_support",
     "//components/cronet:cronet_common",
     "//components/cronet:cronet_version_header",
@@ -2146,11 +2143,9 @@
 
 group("cronet_non_test_package") {
   # Target containing all of cronet's non-test targets.
-  # Enforce building with ICU alternatives, crbug.com/611621.
   # Enforce that arm_use_neon==false when building for ARMv7 by
   # not including any deps in cronet_package target otherwise.
-  if (use_platform_icu_alternatives &&
-      (!(target_cpu == "arm" && arm_version == 7) || !arm_use_neon)) {
+  if (!(target_cpu == "arm" && arm_version == 7) || !arm_use_neon) {
     deps = [
       ":cronet_package_copy",
       ":cronet_package_copy_native_headers",
diff --git a/components/cronet/android/cronet_library_loader.cc b/components/cronet/android/cronet_library_loader.cc
index aff9e209..881d77c 100644
--- a/components/cronet/android/cronet_library_loader.cc
+++ b/components/cronet/android/cronet_library_loader.cc
@@ -51,11 +51,6 @@
 #include "net/proxy_resolution/proxy_config_service_android.h"
 #include "third_party/perfetto/include/perfetto/tracing/tracing.h"
 #include "third_party/zlib/zlib.h"
-#include "url/buildflags.h"
-
-#if !BUILDFLAG(USE_PLATFORM_ICU_ALTERNATIVES)
-#include "base/i18n/icu_util.h"  // nogncheck
-#endif
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
 #include "components/cronet/android/cronet_jni_headers/CronetLibraryLoader_jni.h"
@@ -166,10 +161,6 @@
                                         jboolean initializePerfetto) {
   logging::InitLogging(logging::LoggingSettings());
 
-#if !BUILDFLAG(USE_PLATFORM_ICU_ALTERNATIVES)
-  base::i18n::InitializeICU();
-#endif
-
   if (!base::ThreadPoolInstance::Get()) {
     base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Cronet");
   }
diff --git a/components/cronet/tools/utils.py b/components/cronet/tools/utils.py
index 708a35f..90d326d2 100755
--- a/components/cronet/tools/utils.py
+++ b/components/cronet/tools/utils.py
@@ -22,11 +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')
+# Cronet in Android is distributed via Mainline
+# (https://source.android.com/docs/core/ota/modular-system) to devices back to
+# Android R (API 30).
 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',
-                   f'default_min_sdk_version={MIN_SDK_VERSION_FOR_AOSP}')
 _GN_ARG_MATCHER = re.compile("^.*=.*$")
 
 
@@ -157,13 +157,34 @@
   all_deps.remove('')
   return all_deps
 
-
 def get_gn_args_for_aosp(arch: str) -> List[str]:
-  default_args = filter_gn_args(get_android_gn_args(True, arch),
-                                ["use_remoteexec", "default_min_sdk_version"])
-  default_args.extend(AOSP_EXTRA_ARGS)
-  return default_args
-
+  # This is the source of truth for GN args for Cronet in Android.
+  # Note: for readability and discoverability, prefer to make the default value
+  # of the GN arg depend on `is_cronet_for_aosp_build` GN arg whenever possible,
+  # instead of setting the value here.
+  return (
+      # TODO: https://crbug.com/446652679 - It might be possible to drop this.
+      'dcheck_always_on = false',
+      # TODO: https://crbug.com/446652679 - It might be possible to drop this.
+      'debuggable_apks = false',
+      # Override here, instead of modifying `default_min_sdk_version`'s
+      # declaration to avoid hardcoding this value twice (there is no easy way
+      # to share a constant between GN and python files).
+      f'default_min_sdk_version={MIN_SDK_VERSION_FOR_AOSP}',
+      # TODO: https://crbug.com/446652679 - It might be possible to drop this.
+      'is_debug = false',
+      'is_cronet_for_aosp_build=true',
+      # TODO: https://crbug.com/446652679 - It might be possible to drop this.
+      'is_component_build = false',
+      # TODO: https://crbug.com/446652193 - It might be possible to drop this.
+      'is_official_build = true',
+      # TODO: https://crbug.com/446652679 - It might be possible to drop this.
+      'strip_debug_info = true',
+      # TODO: https://crbug.com/446652679 - It might be possible to drop this.
+      'symbol_level = 1',
+      f'target_cpu = "{arch}"',
+      'target_os = "android"',
+  )
 
 def android_gn_gen(is_release, target_cpu, out_dir):
   """Runs `gn gen` using Cronet's android gn_args.
diff --git a/components/enterprise/client_certificates/core/certificate_provisioning_service.cc b/components/enterprise/client_certificates/core/certificate_provisioning_service.cc
index 95ddcab7..4357f918 100644
--- a/components/enterprise/client_certificates/core/certificate_provisioning_service.cc
+++ b/components/enterprise/client_certificates/core/certificate_provisioning_service.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/check.h"
+#include "base/containers/contains.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
@@ -64,6 +65,8 @@
 
   // CertificateProvisioningService:
   void GetManagedIdentity(GetManagedIdentityCallback callback) override;
+  void DeleteManagedIdentities(
+      base::OnceCallback<void(bool)> callback) override;
   Status GetCurrentStatus() const override;
 
  private:
@@ -92,6 +95,10 @@
                               scoped_refptr<net::X509Certificate> certificate,
                               std::optional<StoreError> commit_error);
 
+  void OnIdentitiesDeleted(const std::vector<std::string>& identity_names,
+                           base::OnceCallback<void(bool)> callback,
+                           std::optional<StoreError> error);
+
   void OnProvisioningError(
       ProvisioningError error,
       std::optional<StoreError> store_error = std::nullopt);
@@ -191,6 +198,21 @@
   }
 }
 
+void CertificateProvisioningServiceImpl::DeleteManagedIdentities(
+    base::OnceCallback<void(bool)> callback) {
+  if (IsProvisioning()) {
+    std::move(callback).Run(false);
+    return;
+  }
+  std::vector<std::string> identity_names = {identity_name(),
+                                             temporary_identity_name()};
+  certificate_store_->DeleteIdentities(
+      identity_names,
+      base::BindOnce(&CertificateProvisioningServiceImpl::OnIdentitiesDeleted,
+                     weak_factory_.GetWeakPtr(), identity_names,
+                     std::move(callback)));
+}
+
 CertificateProvisioningService::Status
 CertificateProvisioningServiceImpl::GetCurrentStatus() const {
   Status status(IsProvisioning());
@@ -461,6 +483,33 @@
   OnFinishedProvisioning(/*success=*/true);
 }
 
+void CertificateProvisioningServiceImpl::OnIdentitiesDeleted(
+    const std::vector<std::string>& identity_names,
+    base::OnceCallback<void(bool)> callback,
+    std::optional<StoreError> error) {
+  if (error.has_value()) {
+    LOG_POLICY(ERROR, DEVICE_TRUST)
+        << "Failed to delete identities from store: "
+        << StoreErrorToString(error.value());
+    std::move(callback).Run(false);
+    return;
+  }
+
+  LOG_POLICY(INFO, DEVICE_TRUST)
+      << "Identities successfully deleted from store.";
+
+  if (cached_identity_ &&
+      base::Contains(identity_names, cached_identity_->name)) {
+    if (cached_identity_->certificate) {
+      context_delegate_->OnClientCertificateDeleted(
+          cached_identity_->certificate);
+    }
+    cached_identity_ = std::nullopt;
+  }
+
+  std::move(callback).Run(true);
+}
+
 void CertificateProvisioningServiceImpl::OnProvisioningError(
     ProvisioningError provisioning_error,
     std::optional<StoreError> store_error) {
diff --git a/components/enterprise/client_certificates/core/certificate_provisioning_service.h b/components/enterprise/client_certificates/core/certificate_provisioning_service.h
index e99217703..fd5f789 100644
--- a/components/enterprise/client_certificates/core/certificate_provisioning_service.h
+++ b/components/enterprise/client_certificates/core/certificate_provisioning_service.h
@@ -65,6 +65,14 @@
   // some reason, subsequent calls will retry loading it.
   virtual void GetManagedIdentity(GetManagedIdentityCallback callback) = 0;
 
+  // Deletes the managed identities (permanent and temporary). `callback` will
+  // be invoked with true if the identities were deleted successfully, and false
+  // otherwise. In case of an unexpected failure, the identities might still
+  // exist in the store but not be usable anymore currently we just log an
+  // error.
+  virtual void DeleteManagedIdentities(
+      base::OnceCallback<void(bool)> callback) = 0;
+
   // Returns metadata about the current status of the service, mainly for
   // debugging purposes.
   virtual Status GetCurrentStatus() const = 0;
diff --git a/components/enterprise/client_certificates/core/certificate_provisioning_service_unittest.cc b/components/enterprise/client_certificates/core/certificate_provisioning_service_unittest.cc
index 5ecd809..5d5c592 100644
--- a/components/enterprise/client_certificates/core/certificate_provisioning_service_unittest.cc
+++ b/components/enterprise/client_certificates/core/certificate_provisioning_service_unittest.cc
@@ -840,4 +840,81 @@
   VerifyIdleWithCache(mocked_private_key, fake_cert, kSuccessUploadCode);
 }
 
+TEST_F(CertificateProvisioningServiceTest, DeleteManagedIdentities_Success) {
+  SetPolicyPref(true);
+
+  auto mocked_private_key = base::MakeRefCounted<StrictMock<MockPrivateKey>>();
+  auto test_cert = LoadTestCert();
+  ClientIdentity existing_identity(kIdentityName, mocked_private_key,
+                                   test_cert);
+
+  auto mock_context_delegate = CreateContextDelegate();
+  auto* mock_context_delegate_ptr = mock_context_delegate.get();
+
+  // This will be called on service creation since policy is enabled.
+  EXPECT_CALL(mock_store_, GetIdentity(kIdentityName, _))
+      .WillOnce(RunOnceCallback<1>(existing_identity));
+
+  CreateProvisioningService(
+      std::move(mock_context_delegate),
+      std::make_unique<StrictMock<MockKeyUploadClient>>());
+  ASSERT_TRUE(service_);
+
+  // Let the service initialize.
+  task_environment_.RunUntilIdle();
+
+  // Verify that the identity is cached.
+  auto status_before_delete = service_->GetCurrentStatus();
+  ASSERT_TRUE(status_before_delete.identity.has_value());
+  EXPECT_TRUE(test_cert->EqualsIncludingChain(
+      status_before_delete.identity->certificate.get()));
+
+  // Set up expectations for deletion.
+  EXPECT_CALL(mock_store_,
+              DeleteIdentities(
+                  testing::ElementsAre(kIdentityName, kTempIdentityName), _))
+      .WillOnce(RunOnceCallback<1>(std::nullopt));
+  EXPECT_CALL(*mock_context_delegate_ptr,
+              OnClientCertificateDeleted(test_cert));
+
+  base::test::TestFuture<bool> delete_future;
+  service_->DeleteManagedIdentities(delete_future.GetCallback());
+  EXPECT_TRUE(delete_future.Get());
+
+  // Verify the cache is cleared.
+  auto status_after_delete = service_->GetCurrentStatus();
+  EXPECT_FALSE(status_after_delete.identity.has_value());
+}
+
+TEST_F(CertificateProvisioningServiceTest, DeleteManagedIdentities_NoIdentity) {
+  CreateProvisioningService(
+      CreateContextDelegate(),
+      std::make_unique<StrictMock<MockKeyUploadClient>>());
+
+  EXPECT_CALL(mock_store_,
+              DeleteIdentities(
+                  testing::ElementsAre(kIdentityName, kTempIdentityName), _))
+      .WillOnce(RunOnceCallback<1>(std::nullopt));
+
+  base::test::TestFuture<bool> delete_future;
+  service_->DeleteManagedIdentities(delete_future.GetCallback());
+  EXPECT_TRUE(delete_future.Get());
+}
+
+TEST_F(CertificateProvisioningServiceTest,
+       DeleteManagedIdentities_StoreDeleteFailure) {
+  CreateProvisioningService(
+      CreateContextDelegate(),
+      std::make_unique<StrictMock<MockKeyUploadClient>>());
+
+  EXPECT_CALL(mock_store_,
+              DeleteIdentities(
+                  testing::ElementsAre(kIdentityName, kTempIdentityName), _))
+      .WillOnce(RunOnceCallback<1>(StoreError::kDeleteIdentityFailed));
+
+  base::test::TestFuture<bool> delete_future;
+  service_->DeleteManagedIdentities(delete_future.GetCallback());
+  EXPECT_FALSE(delete_future.Get());
+}
+
 }  // namespace client_certificates
diff --git a/components/enterprise/client_certificates/core/certificate_store.h b/components/enterprise/client_certificates/core/certificate_store.h
index c901af7..e5d37f8d 100644
--- a/components/enterprise/client_certificates/core/certificate_store.h
+++ b/components/enterprise/client_certificates/core/certificate_store.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <vector>
 
 #include "base/functional/callback_forward.h"
 #include "base/memory/scoped_refptr.h"
@@ -72,6 +73,13 @@
       const std::string& identity_name,
       base::OnceCallback<void(StoreErrorOr<std::optional<ClientIdentity>>)>
           callback) = 0;
+
+  // Will delete the identities with the given `identity_names`. Will invoke
+  // `callback` with std::nullopt on success, or with an error if something
+  // went wrong.
+  virtual void DeleteIdentities(
+      const std::vector<std::string>& identity_names,
+      base::OnceCallback<void(std::optional<StoreError>)> callback) = 0;
 };
 
 }  // namespace client_certificates
diff --git a/components/enterprise/client_certificates/core/leveldb_certificate_store.cc b/components/enterprise/client_certificates/core/leveldb_certificate_store.cc
index 2370731..0faf0dba 100644
--- a/components/enterprise/client_certificates/core/leveldb_certificate_store.cc
+++ b/components/enterprise/client_certificates/core/leveldb_certificate_store.cc
@@ -246,6 +246,42 @@
                       base::BindOnce(OnIdentityFetched, std::move(callback)));
 }
 
+void LevelDbCertificateStore::DeleteIdentities(
+    const std::vector<std::string>& identity_names,
+    base::OnceCallback<void(std::optional<StoreError>)> callback) {
+  for (const auto& identity_name : identity_names) {
+    if (identity_name.empty()) {
+      std::move(callback).Run(StoreError::kInvalidIdentityName);
+      return;
+    }
+  }
+
+  if (database_state_ != DatabaseState::kInitialized) {
+    pending_operations_.push_back(base::BindOnce(
+        &LevelDbCertificateStore::DeleteIdentities, weak_factory_.GetWeakPtr(),
+        identity_names, std::move(callback)));
+    InitializeDatabase();
+    return;
+  }
+
+  auto mod_keys = std::make_unique<std::vector<std::string>>(identity_names);
+
+  database_->UpdateEntries(
+      std::make_unique<leveldb_proto::ProtoDatabase<
+          client_certificates_pb::ClientIdentity>::KeyEntryVector>(),
+      std::move(mod_keys),
+      base::BindOnce(
+          [](base::OnceCallback<void(std::optional<StoreError>)> callback,
+             bool success) {
+            if (!success) {
+              std::move(callback).Run(StoreError::kDeleteIdentityFailed);
+              return;
+            }
+            std::move(callback).Run(std::nullopt);
+          },
+          std::move(callback)));
+}
+
 void LevelDbCertificateStore::CreatePrivateKeyInner(
     const std::string& identity_name,
     base::OnceCallback<void(StoreErrorOr<scoped_refptr<PrivateKey>>)> callback,
diff --git a/components/enterprise/client_certificates/core/leveldb_certificate_store.h b/components/enterprise/client_certificates/core/leveldb_certificate_store.h
index 0b2a2e1b..7b66c09 100644
--- a/components/enterprise/client_certificates/core/leveldb_certificate_store.h
+++ b/components/enterprise/client_certificates/core/leveldb_certificate_store.h
@@ -79,6 +79,9 @@
       const std::string& identity_name,
       base::OnceCallback<void(StoreErrorOr<std::optional<ClientIdentity>>)>
           callback) override;
+  void DeleteIdentities(
+      const std::vector<std::string>& identity_names,
+      base::OnceCallback<void(std::optional<StoreError>)> callback) override;
 
  private:
   enum class DatabaseState {
diff --git a/components/enterprise/client_certificates/core/leveldb_certificate_store_unittest.cc b/components/enterprise/client_certificates/core/leveldb_certificate_store_unittest.cc
index 279da2cd..5fba9a97 100644
--- a/components/enterprise/client_certificates/core/leveldb_certificate_store_unittest.cc
+++ b/components/enterprise/client_certificates/core/leveldb_certificate_store_unittest.cc
@@ -748,4 +748,39 @@
   EXPECT_THAT(test_future.Get(), ErrorIs(StoreError::kLoadKeyFailed));
 }
 
+TEST_F(LevelDbCertificateStoreTest, DeleteIdentities_Success) {
+  client_certificates_pb::ClientIdentity proto_identity;
+  *proto_identity.mutable_private_key() = CreateFakeProtoKey();
+  AddDatabaseEntry(kTestIdentityName, proto_identity);
+  AddDatabaseEntry(kOtherTestIdentityName, proto_identity);
+
+  base::test::TestFuture<std::optional<StoreError>> test_future;
+  store_->DeleteIdentities({kTestIdentityName, kOtherTestIdentityName},
+                           test_future.GetCallback());
+
+  fake_db_->InitStatusCallback(InitStatus::kOK);
+  fake_db_->UpdateCallback(/*success=*/true);
+
+  EXPECT_EQ(test_future.Get(), std::nullopt);
+  ExpectNoDatabaseEntry(kTestIdentityName);
+  ExpectNoDatabaseEntry(kOtherTestIdentityName);
+}
+
+TEST_F(LevelDbCertificateStoreTest, DeleteIdentities_NotFound) {
+  base::test::TestFuture<std::optional<StoreError>> test_future;
+  store_->DeleteIdentities({kTestIdentityName}, test_future.GetCallback());
+
+  fake_db_->InitStatusCallback(InitStatus::kOK);
+  fake_db_->UpdateCallback(/*success=*/true);
+
+  EXPECT_EQ(test_future.Get(), std::nullopt);
+}
+
+TEST_F(LevelDbCertificateStoreTest, DeleteIdentities_InvalidName) {
+  base::test::TestFuture<std::optional<StoreError>> test_future;
+  store_->DeleteIdentities({kTestIdentityName, ""}, test_future.GetCallback());
+
+  EXPECT_EQ(test_future.Get(), StoreError::kInvalidIdentityName);
+}
+
 }  // namespace client_certificates
diff --git a/components/enterprise/client_certificates/core/mock_certificate_provisioning_service.h b/components/enterprise/client_certificates/core/mock_certificate_provisioning_service.h
index 8ee2f30d..e2f9619 100644
--- a/components/enterprise/client_certificates/core/mock_certificate_provisioning_service.h
+++ b/components/enterprise/client_certificates/core/mock_certificate_provisioning_service.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_ENTERPRISE_CLIENT_CERTIFICATES_CORE_MOCK_CERTIFICATE_PROVISIONING_SERVICE_H_
 #define COMPONENTS_ENTERPRISE_CLIENT_CERTIFICATES_CORE_MOCK_CERTIFICATE_PROVISIONING_SERVICE_H_
 
+#include <memory>
+
 #include "base/functional/callback.h"
 #include "components/enterprise/client_certificates/core/certificate_provisioning_service.h"
 #include "components/enterprise/client_certificates/core/certificate_store.h"
@@ -23,7 +25,12 @@
               GetManagedIdentity,
               (GetManagedIdentityCallback),
               (override));
+  MOCK_METHOD(void,
+              DeleteManagedIdentities,
+              (base::OnceCallback<void(bool)> callback),
+              (override));
   MOCK_METHOD(Status, GetCurrentStatus, (), (const, override));
+
 };
 
 }  // namespace client_certificates
diff --git a/components/enterprise/client_certificates/core/mock_certificate_store.h b/components/enterprise/client_certificates/core/mock_certificate_store.h
index 515de6a..d66b7209 100644
--- a/components/enterprise/client_certificates/core/mock_certificate_store.h
+++ b/components/enterprise/client_certificates/core/mock_certificate_store.h
@@ -47,6 +47,11 @@
       (const std::string&,
        base::OnceCallback<void(StoreErrorOr<std::optional<ClientIdentity>>)>),
       (override));
+  MOCK_METHOD(void,
+              DeleteIdentities,
+              (const std::vector<std::string>&,
+               base::OnceCallback<void(std::optional<StoreError>)>),
+              (override));
 };
 
 }  // namespace client_certificates
diff --git a/components/enterprise/client_certificates/core/prefs_certificate_store.cc b/components/enterprise/client_certificates/core/prefs_certificate_store.cc
index 8286121..05f75b1 100644
--- a/components/enterprise/client_certificates/core/prefs_certificate_store.cc
+++ b/components/enterprise/client_certificates/core/prefs_certificate_store.cc
@@ -203,4 +203,23 @@
   std::move(callback).Run(private_key);
 }
 
+void PrefsCertificateStore::DeleteIdentities(
+    const std::vector<std::string>& identity_names,
+    base::OnceCallback<void(std::optional<StoreError>)> callback) {
+  // Check that all identity names are non-empty.
+  for (const auto& identity_name : identity_names) {
+    if (identity_name.empty()) {
+      std::move(callback).Run(StoreError::kInvalidIdentityName);
+      return;
+    }
+  }
+
+  // Clear all identities from the prefs.
+  for (const auto& identity_name : identity_names) {
+    pref_service_->ClearPref(identity_name);
+  }
+
+  std::move(callback).Run(std::nullopt);
+}
+
 }  // namespace client_certificates
diff --git a/components/enterprise/client_certificates/core/prefs_certificate_store.h b/components/enterprise/client_certificates/core/prefs_certificate_store.h
index 9a16cab..78e05d08 100644
--- a/components/enterprise/client_certificates/core/prefs_certificate_store.h
+++ b/components/enterprise/client_certificates/core/prefs_certificate_store.h
@@ -57,6 +57,9 @@
       const std::string& identity_name,
       base::OnceCallback<void(StoreErrorOr<std::optional<ClientIdentity>>)>
           callback) override;
+  void DeleteIdentities(
+      const std::vector<std::string>& identity_names,
+      base::OnceCallback<void(std::optional<StoreError>)> callback) override;
 
  private:
   // Called when a private key was created from `CreatePrivateKeyInner`.
diff --git a/components/enterprise/client_certificates/core/prefs_certificate_store_unittest.cc b/components/enterprise/client_certificates/core/prefs_certificate_store_unittest.cc
index 418e729..523118f 100644
--- a/components/enterprise/client_certificates/core/prefs_certificate_store_unittest.cc
+++ b/components/enterprise/client_certificates/core/prefs_certificate_store_unittest.cc
@@ -375,4 +375,38 @@
   EXPECT_THAT(test_future.Get(), ErrorIs(StoreError::kLoadKeyFailed));
 }
 
+TEST_F(PrefsCertificateStoreTest, DeleteIdentities_Success) {
+  base::Value::Dict identity;
+  identity.Set(kCertificate, "some_cert");
+  prefs_.SetDict(kTestIdentityName, identity.Clone());
+  prefs_.SetDict(kOtherTestIdentityName, identity.Clone());
+  ASSERT_TRUE(prefs_.HasPrefPath(kTestIdentityName));
+  ASSERT_TRUE(prefs_.HasPrefPath(kOtherTestIdentityName));
+
+  base::test::TestFuture<std::optional<StoreError>> test_future;
+  store_->DeleteIdentities({kTestIdentityName, kOtherTestIdentityName},
+                           test_future.GetCallback());
+
+  EXPECT_EQ(test_future.Get(), std::nullopt);
+  EXPECT_FALSE(prefs_.HasPrefPath(kTestIdentityName));
+  EXPECT_FALSE(prefs_.HasPrefPath(kOtherTestIdentityName));
+}
+
+TEST_F(PrefsCertificateStoreTest, DeleteIdentities_NotFound) {
+  ASSERT_FALSE(prefs_.HasPrefPath(kTestIdentityName));
+
+  base::test::TestFuture<std::optional<StoreError>> test_future;
+  store_->DeleteIdentities({kTestIdentityName}, test_future.GetCallback());
+
+  EXPECT_EQ(test_future.Get(), std::nullopt);
+  EXPECT_FALSE(prefs_.HasPrefPath(kTestIdentityName));
+}
+
+TEST_F(PrefsCertificateStoreTest, DeleteIdentities_InvalidName) {
+  base::test::TestFuture<std::optional<StoreError>> test_future;
+  store_->DeleteIdentities({kTestIdentityName, ""}, test_future.GetCallback());
+
+  EXPECT_EQ(test_future.Get(), StoreError::kInvalidIdentityName);
+}
+
 }  // namespace client_certificates
diff --git a/components/enterprise/client_certificates/core/store_error.cc b/components/enterprise/client_certificates/core/store_error.cc
index 2d5d4e59..3f96256 100644
--- a/components/enterprise/client_certificates/core/store_error.cc
+++ b/components/enterprise/client_certificates/core/store_error.cc
@@ -32,6 +32,8 @@
       return "InvalidFinalIdentityName";
     case StoreError::kIdentityNotFound:
       return "IdentityNotFound";
+    case StoreError::kDeleteIdentityFailed:
+      return "DeleteIdentityFailed";
   }
 }
 
diff --git a/components/enterprise/client_certificates/core/store_error.h b/components/enterprise/client_certificates/core/store_error.h
index 9004984..805ad9bf 100644
--- a/components/enterprise/client_certificates/core/store_error.h
+++ b/components/enterprise/client_certificates/core/store_error.h
@@ -27,7 +27,8 @@
   kLoadKeyFailed = 9,
   kInvalidFinalIdentityName = 10,
   kIdentityNotFound = 11,
-  kMaxValue = kIdentityNotFound
+  kDeleteIdentityFailed = 12,
+  kMaxValue = kDeleteIdentityFailed
 };
 
 template <class T>
diff --git a/components/gcm_driver/crypto/message_payload_parser_unittest.cc b/components/gcm_driver/crypto/message_payload_parser_unittest.cc
index eebb733..a096946 100644
--- a/components/gcm_driver/crypto/message_payload_parser_unittest.cc
+++ b/components/gcm_driver/crypto/message_payload_parser_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
 #include "base/numerics/byte_conversions.h"
+#include "base/strings/string_view_util.h"
 #include "components/gcm_driver/crypto/gcm_decryption_result.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -47,13 +48,11 @@
 
 // Creates an std::string for the |kValidMessage| constant.
 std::string CreateMessageString() {
-  std::string result(kValidMessage.size(), 0x0);
-  base::as_writable_byte_span(result).copy_from_nonoverlapping(kValidMessage);
-  return result;
+  return std::string(base::as_string_view(kValidMessage));
 }
 
 TEST(MessagePayloadParserTest, ValidMessage) {
-  MessagePayloadParser parser(CreateMessageString());
+  MessagePayloadParser parser(base::as_string_view(kValidMessage));
   ASSERT_TRUE(parser.IsValid());
 
   base::span<const uint8_t> salt = base::span(kValidMessage).first(kSaltSize);
@@ -82,10 +81,8 @@
 }
 
 TEST(MessagePayloadParserTest, MinimumMessageSize) {
-  std::string message = CreateMessageString();
-  message.resize(std::size(kValidMessage) / 2);
-
-  MessagePayloadParser parser(message);
+  MessagePayloadParser parser(
+      base::as_string_view(kValidMessage).substr(0u, kValidMessage.size() / 2));
   EXPECT_FALSE(parser.IsValid());
   EXPECT_EQ(parser.GetFailureReason(),
             GCMDecryptionResult::INVALID_BINARY_HEADER_PAYLOAD_LENGTH);
diff --git a/components/history/core/browser/sync/history_data_type_controller.cc b/components/history/core/browser/sync/history_data_type_controller.cc
index a04027e..a7e11dad 100644
--- a/components/history/core/browser/sync/history_data_type_controller.cc
+++ b/components/history/core/browser/sync/history_data_type_controller.cc
@@ -164,6 +164,10 @@
   helper_.sync_service()->DataTypePreconditionChanged(type());
 }
 
+void HistoryDataTypeController::OnSyncShutdown(syncer::SyncService* sync) {
+  // Nothing to be done, `this` will be destructed imminently.
+}
+
 void HistoryDataTypeController::AccountTypeDetermined() {
   helper_.sync_service()->DataTypePreconditionChanged(type());
 }
diff --git a/components/history/core/browser/sync/history_data_type_controller.h b/components/history/core/browser/sync/history_data_type_controller.h
index db3118d8..3bcdfba4 100644
--- a/components/history/core/browser/sync/history_data_type_controller.h
+++ b/components/history/core/browser/sync/history_data_type_controller.h
@@ -43,6 +43,7 @@
 
   // syncer::SyncServiceObserver implementation.
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
  private:
   void AccountTypeDetermined();
diff --git a/components/history/core/browser/sync/history_delete_directives_data_type_controller.cc b/components/history/core/browser/sync/history_delete_directives_data_type_controller.cc
index 2084944..a345a83 100644
--- a/components/history/core/browser/sync/history_delete_directives_data_type_controller.cc
+++ b/components/history/core/browser/sync/history_delete_directives_data_type_controller.cc
@@ -96,4 +96,9 @@
   helper_.sync_service()->DataTypePreconditionChanged(type());
 }
 
+void HistoryDeleteDirectivesDataTypeController::OnSyncShutdown(
+    syncer::SyncService* sync) {
+  // Nothing to be done, `this` will be destructed imminently.
+}
+
 }  // namespace history
diff --git a/components/history/core/browser/sync/history_delete_directives_data_type_controller.h b/components/history/core/browser/sync/history_delete_directives_data_type_controller.h
index b5db1ed2..b8c433cb 100644
--- a/components/history/core/browser/sync/history_delete_directives_data_type_controller.h
+++ b/components/history/core/browser/sync/history_delete_directives_data_type_controller.h
@@ -51,6 +51,7 @@
 
   // syncer::SyncServiceObserver implementation.
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
  private:
   history::HistoryDataTypeControllerHelper helper_;
diff --git a/components/ip_protection/common/ip_protection_data_types.h b/components/ip_protection/common/ip_protection_data_types.h
index 04cd63294..0a27fd2 100644
--- a/components/ip_protection/common/ip_protection_data_types.h
+++ b/components/ip_protection/common/ip_protection_data_types.h
@@ -183,7 +183,7 @@
   kInvalidEpochIdSize = 15,
   kMaxValue = kInvalidEpochIdSize,
 };
-// LINT.ThenChange(//tools/metrics/histograms/metadata/network/enums.xml:ProbabilisticRevealTokensResult)
+// LINT.ThenChange(//tools/metrics/histograms/metadata/ip_protection/enums.xml:ProbabilisticRevealTokensResult)
 
 // Stores return status of TryGetProbabilisticRevealTokens() together with
 // NetError() returned by url loader.
diff --git a/components/ip_protection/common/ip_protection_telemetry.h b/components/ip_protection/common/ip_protection_telemetry.h
index 27ac307..bb68641 100644
--- a/components/ip_protection/common/ip_protection_telemetry.h
+++ b/components/ip_protection/common/ip_protection_telemetry.h
@@ -81,7 +81,7 @@
   kAvailableForOtherCachedGeo = 3,
   kMaxValue = kAvailableForOtherCachedGeo,
 };
-// LINT.ThenChange(//tools/metrics/histograms/metadata/network/enums.xml:IpProtectionGetAuthTokenResultForGeo)
+// LINT.ThenChange(//tools/metrics/histograms/metadata/ip_protection/enums.xml:IpProtectionGetAuthTokenResultForGeo)
 
 // An enumeration of events affecting the token count. These values are
 // persisted to logs. Entries should not be renumbered and numeric values should
@@ -103,7 +103,7 @@
   kAuthAndSign,
   kUnblindTokens,
 };
-// LINT.ThenChange(//tools/metrics/histograms/metadata/network/histograms.xml:BlindSignAuthPhase)
+// LINT.ThenChange(//tools/metrics/histograms/metadata/ip_protection/histograms.xml:BlindSignAuthPhase)
 
 // An abstract interface for all of the telemetry associated with IP Protection.
 //
diff --git a/components/one_time_tokens/OWNERS b/components/one_time_tokens/OWNERS
index 50580fd0..7df1e838 100644
--- a/components/one_time_tokens/OWNERS
+++ b/components/one_time_tokens/OWNERS
@@ -1,2 +1,3 @@
 battre@chromium.org
 koerber@google.com
+luchenpeng@google.com
diff --git a/components/optimization_guide/core/optimization_guide_features.cc b/components/optimization_guide/core/optimization_guide_features.cc
index a10593d9..0cc913c 100644
--- a/components/optimization_guide/core/optimization_guide_features.cc
+++ b/components/optimization_guide/core/optimization_guide_features.cc
@@ -75,9 +75,6 @@
 BASE_FEATURE(kOverrideNumThreadsForModelExecution,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kOptGuideEnableXNNPACKDelegateWithTFLite,
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Killswitch for fetching on search results from a remote Optimization Guide
 // Service.
 BASE_FEATURE(kOptimizationGuideFetchingForSRP,
@@ -395,10 +392,6 @@
   return std::min(num_threads, base::SysInfo::NumberOfProcessors());
 }
 
-bool TFLiteXNNPACKDelegateEnabled() {
-  return base::FeatureList::IsEnabled(kOptGuideEnableXNNPACKDelegateWithTFLite);
-}
-
 std::map<proto::OptimizationTarget, std::set<int64_t>>
 GetPredictionModelVersionsInKillSwitch() {
   if (!base::FeatureList::IsEnabled(
diff --git a/components/optimization_guide/core/optimization_guide_features.h b/components/optimization_guide/core/optimization_guide_features.h
index f56f69e..dfe69db8 100644
--- a/components/optimization_guide/core/optimization_guide_features.h
+++ b/components/optimization_guide/core/optimization_guide_features.h
@@ -49,8 +49,6 @@
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 BASE_DECLARE_FEATURE(kOverrideNumThreadsForModelExecution);
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
-BASE_DECLARE_FEATURE(kOptGuideEnableXNNPACKDelegateWithTFLite);
-COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 BASE_DECLARE_FEATURE(kOptimizationGuidePersonalizedFetching);
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 BASE_DECLARE_FEATURE(kOptimizationGuidePredictionModelKillswitch);
@@ -245,11 +243,6 @@
 std::optional<int> OverrideNumThreadsForOptTarget(
     proto::OptimizationTarget opt_target);
 
-// Whether XNNPACK should be used with TFLite, on platforms where it is
-// supported. This is a no-op on unsupported platforms.
-COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
-bool TFLiteXNNPACKDelegateEnabled();
-
 // Whether logging of model quality is enabled.
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 bool IsModelQualityLoggingEnabled();
diff --git a/components/optimization_guide/core/tflite_op_resolver.cc b/components/optimization_guide/core/tflite_op_resolver.cc
index 3f7de3e5..f46c802b 100644
--- a/components/optimization_guide/core/tflite_op_resolver.cc
+++ b/components/optimization_guide/core/tflite_op_resolver.cc
@@ -387,12 +387,10 @@
              tflite::ops::builtin::Register_RANDOM_UNIFORM());
 
 #if BUILDFLAG(BUILD_TFLITE_WITH_XNNPACK)
-  if (features::TFLiteXNNPACKDelegateEnabled()) {
-    delegate_creators_.push_back([](TfLiteContext* context) {
-      return tflite::MaybeCreateXNNPACKDelegate(
-          context, tflite::XNNPackQS8Options::default_value);
-    });
-  }
+  delegate_creators_.push_back([](TfLiteContext* context) {
+    return tflite::MaybeCreateXNNPACKDelegate(
+        context, tflite::XNNPackQS8Options::default_value);
+  });
 #endif
 }
 
diff --git a/components/page_load_metrics/renderer/features.cc b/components/page_load_metrics/renderer/features.cc
index b892bff..5ba3282 100644
--- a/components/page_load_metrics/renderer/features.cc
+++ b/components/page_load_metrics/renderer/features.cc
@@ -7,15 +7,7 @@
 #include "base/feature_list.h"
 
 namespace page_load_metrics::features {
-
-// Reduce the number of `DidObserveNewFeatureUsage` calls. crbug.com/404425954
-// for more details.
-BASE_FEATURE(kDidObserveNewFeatureUsageImprovement,
+// Reduce the number of observer calls. crbug.com/40442595 for more details.
+BASE_FEATURE(kMetricsRenderFrameObserverImprovement,
              base::FEATURE_DISABLED_BY_DEFAULT);
-
-// Reduce the number of `DidObserveSubresourceLoad` calls. crbug.com/404425954
-// for more details.
-BASE_FEATURE(kDidObserveSubresourceLoadImprovement,
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 }  // namespace page_load_metrics::features
diff --git a/components/page_load_metrics/renderer/features.h b/components/page_load_metrics/renderer/features.h
index 4d761f1e..c8808eb3 100644
--- a/components/page_load_metrics/renderer/features.h
+++ b/components/page_load_metrics/renderer/features.h
@@ -8,7 +8,6 @@
 #include "base/feature_list.h"
 
 namespace page_load_metrics::features {
-BASE_DECLARE_FEATURE(kDidObserveNewFeatureUsageImprovement);
-BASE_DECLARE_FEATURE(kDidObserveSubresourceLoadImprovement);
+BASE_DECLARE_FEATURE(kMetricsRenderFrameObserverImprovement);
 }
 #endif  // COMPONENTS_PAGE_LOAD_METRICS_RENDERER_FEATURES_H_
diff --git a/components/page_load_metrics/renderer/metrics_render_frame_observer.cc b/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
index 5d0e144..e0b085d 100644
--- a/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
+++ b/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
@@ -109,21 +109,23 @@
       blink::WebLocalFrameObserver(render_frame ? render_frame->GetWebFrame()
                                                 : nullptr) {
   if (base::FeatureList::IsEnabled(
-          features::kDidObserveNewFeatureUsageImprovement) &&
+          features::kMetricsRenderFrameObserverImprovement) &&
       render_frame) {
     // If the optimization is enabled, `DidObserveNewFeatureUsage()` will be
     // called as a callback instead of the observer interface.
     render_frame->SetNewFeatureUsageCallback(base::BindRepeating(
         &MetricsRenderFrameObserver::DidObserveNewFeatureUsage,
         weak_factory_.GetWeakPtr()));
-  }
-  if (base::FeatureList::IsEnabled(
-          features::kDidObserveSubresourceLoadImprovement)) {
     // If the optimization is enabled, `DidObserveSubresourceLoad()` will be
     // called as a callback instead of the observer interface.
     render_frame->SetSubresourceLoadCallback(base::BindRepeating(
         &MetricsRenderFrameObserver::DidObserveSubresourceLoad,
         weak_factory_.GetWeakPtr()));
+    // If the optimization is enabled, `DidLoadResourceFromMemoryCache()` will
+    // be called as a callback instead of the observer interface.
+    render_frame->SetLoadFromMemoryCacheCallback(base::BindRepeating(
+        &MetricsRenderFrameObserver::DidLoadResourceFromMemoryCache,
+        weak_factory_.GetWeakPtr()));
   }
 }
 
diff --git a/components/password_manager/core/browser/actor_login/BUILD.gn b/components/password_manager/core/browser/actor_login/BUILD.gn
index 47b8fbf..d5ac785 100644
--- a/components/password_manager/core/browser/actor_login/BUILD.gn
+++ b/components/password_manager/core/browser/actor_login/BUILD.gn
@@ -4,7 +4,10 @@
 
 import("//build/config/features.gni")
 
-assert(!is_android)
+# The code can theoretically be used on mobile too, it's just currently
+# not needed. Remove the asserts if using it on mobile and remember
+# to also modify components/BUILD.gn to start building the tests.
+assert(!is_android && !is_ios)
 
 # All actor_login code should be able to depend on this.
 source_set("common") {
diff --git a/components/password_manager/core/browser/actor_login/internal/BUILD.gn b/components/password_manager/core/browser/actor_login/internal/BUILD.gn
index f5f68d3c..697027d 100644
--- a/components/password_manager/core/browser/actor_login/internal/BUILD.gn
+++ b/components/password_manager/core/browser/actor_login/internal/BUILD.gn
@@ -4,7 +4,10 @@
 
 import("//build/config/features.gni")
 
-assert(!is_android)
+# The code can theoretically be used on mobile too, it's just currently
+# not needed. Remove the asserts if using it on mobile and remember
+# to also modify components/BUILD.gn to start building the tests.
+assert(!is_android && !is_ios)
 
 source_set("delegate") {
   sources = [
@@ -32,6 +35,7 @@
     "//components/password_manager/core/browser",
     "//components/password_manager/core/browser:password_form",
     "//components/password_manager/core/browser/actor_login:common",
+    "//components/password_manager/core/browser/features:password_features",
     "//components/strings:components_strings",
     "//url",
   ]
@@ -51,6 +55,7 @@
     "//components/password_manager/core/browser:test_support",
     "//components/password_manager/core/browser/actor_login:common",
     "//components/password_manager/core/browser/actor_login/test:test_support",
+    "//components/password_manager/core/browser/features:password_features",
     "//testing/gmock",
     "//testing/gtest",
     "//url",
diff --git a/components/password_manager/core/browser/actor_login/internal/actor_login_credential_filler.cc b/components/password_manager/core/browser/actor_login/internal/actor_login_credential_filler.cc
index 301afc2..54a4914 100644
--- a/components/password_manager/core/browser/actor_login/internal/actor_login_credential_filler.cc
+++ b/components/password_manager/core/browser/actor_login/internal/actor_login_credential_filler.cc
@@ -7,7 +7,6 @@
 #include <ranges>
 #include <utility>
 
-#include "base/functional/callback.h"
 #include "base/functional/callback_forward.h"
 #include "base/functional/concurrent_closures.h"
 #include "base/strings/to_string.h"
@@ -19,6 +18,7 @@
 #include "components/password_manager/core/browser/actor_login/actor_login_types.h"
 #include "components/password_manager/core/browser/actor_login/internal/actor_login_util.h"
 #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
+#include "components/password_manager/core/browser/features/password_features.h"
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/browser/password_form_cache.h"
 #include "components/password_manager/core/browser/password_form_manager.h"
@@ -148,16 +148,25 @@
   autofill::FormRendererId form_renderer_id =
       signin_form_manager->GetParsedObservedForm()->form_data.renderer_id();
 
-  auto fill_form_cb = base::BindOnce(
-      &ActorLoginCredentialFiller::FillForm, weak_ptr_factory_.GetWeakPtr(),
-      driver, form_renderer_id, stored_credential->username_value,
-      stored_credential->password_value);
+  base::OnceClosure fill_cb = base::DoNothing();
+  if (base::FeatureList::IsEnabled(
+          password_manager::features::kActorLoginFillingHeuristics)) {
+    fill_cb = base::BindOnce(&ActorLoginCredentialFiller::FillAllEligibleFields,
+                             weak_ptr_factory_.GetWeakPtr(),
+                             stored_credential->username_value,
+                             stored_credential->password_value);
+  } else {
+    fill_cb = base::BindOnce(
+        &ActorLoginCredentialFiller::FillForm, weak_ptr_factory_.GetWeakPtr(),
+        driver, form_renderer_id, stored_credential->username_value,
+        stored_credential->password_value);
+  }
 
   if (client_->IsReauthBeforeFillingRequired(device_authenticator_.get())) {
     LogStatus(logger.get(), Logger::STRING_ACTOR_LOGIN_WAITING_FOR_REAUTH);
-    ReauthenticateAndFill(std::move(fill_form_cb));
+    ReauthenticateAndFill(std::move(fill_cb));
   } else {
-    std::move(fill_form_cb).Run();
+    std::move(fill_cb).Run();
   }
 }
 
@@ -244,6 +253,40 @@
                            weak_ptr_factory_.GetWeakPtr()));
 }
 
+void ActorLoginCredentialFiller::FillAllEligibleFields(
+    std::u16string username,
+    std::u16string password) {
+  PasswordManagerInterface* password_manager = client_->GetPasswordManager();
+  PasswordFormCache* form_cache = password_manager->GetPasswordFormCache();
+
+  base::ConcurrentClosures concurrent_filling;
+  for (const auto& manager : form_cache->GetFormManagers()) {
+    if (!manager->GetDriver()) {
+      continue;
+    }
+    if (!manager->GetDriver()->GetLastCommittedOrigin().IsSameOriginWith(
+            origin_)) {
+      continue;
+    }
+
+    const password_manager::PasswordForm* parsed_form =
+        manager->GetParsedObservedForm();
+    if (!parsed_form || !IsLoginForm(*parsed_form)) {
+      continue;
+    }
+    FillField(manager->GetDriver().get(),
+              parsed_form->username_element_renderer_id, username,
+              FieldType::kUsername, concurrent_filling.CreateClosure());
+    FillField(manager->GetDriver().get(),
+              parsed_form->password_element_renderer_id, password,
+              FieldType::kPassword, concurrent_filling.CreateClosure());
+  }
+
+  std::move(concurrent_filling)
+      .Done(base::BindOnce(&ActorLoginCredentialFiller::OnFillingDone,
+                           weak_ptr_factory_.GetWeakPtr()));
+}
+
 void ActorLoginCredentialFiller::FillField(PasswordManagerDriver* driver,
                                            FieldRendererId field_renderer_id,
                                            const std::u16string& value,
@@ -290,4 +333,5 @@
   std::move(callback_).Run(
       GetEndFillingResult(username_filled_, password_filled_));
 }
+
 }  // namespace actor_login
diff --git a/components/password_manager/core/browser/actor_login/internal/actor_login_credential_filler.h b/components/password_manager/core/browser/actor_login/internal/actor_login_credential_filler.h
index 6bad2691..8718063 100644
--- a/components/password_manager/core/browser/actor_login/internal/actor_login_credential_filler.h
+++ b/components/password_manager/core/browser/actor_login/internal/actor_login_credential_filler.h
@@ -63,6 +63,9 @@
                 std::u16string username,
                 std::u16string password);
 
+  // Fills all eligible fields with `username` and `password`.
+  void FillAllEligibleFields(std::u16string username, std::u16string password);
+
   // Fills the field of `type` identified by `field_renderer_id` within the
   // `driver`'s frame with `value`. `closure` will be called to signal
   // completion at the very end of the flow.
diff --git a/components/password_manager/core/browser/actor_login/internal/actor_login_credential_filler_unittest.cc b/components/password_manager/core/browser/actor_login/internal/actor_login_credential_filler_unittest.cc
index fea5c61..a9171bec 100644
--- a/components/password_manager/core/browser/actor_login/internal/actor_login_credential_filler_unittest.cc
+++ b/components/password_manager/core/browser/actor_login/internal/actor_login_credential_filler_unittest.cc
@@ -5,6 +5,7 @@
 
 #include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/types/expected.h"
@@ -19,6 +20,7 @@
 #include "components/password_manager/core/browser/actor_login/actor_login_types.h"
 #include "components/password_manager/core/browser/actor_login/test/actor_login_test_util.h"
 #include "components/password_manager/core/browser/fake_form_fetcher.h"
+#include "components/password_manager/core/browser/features/password_features.h"
 #include "components/password_manager/core/browser/mock_password_form_cache.h"
 #include "components/password_manager/core/browser/mock_password_manager.h"
 #include "components/password_manager/core/browser/password_form.h"
@@ -80,6 +82,10 @@
 class MockPasswordManagerClient
     : public password_manager::StubPasswordManagerClient {
  public:
+  MOCK_METHOD(const PasswordManagerInterface*,
+              GetPasswordManager,
+              (),
+              (const, override));
   MOCK_METHOD(bool, IsFillingEnabled, (const GURL&), (const, override));
   MOCK_METHOD(bool,
               IsReauthBeforeFillingRequired,
@@ -137,6 +143,8 @@
     // Used by `PasswordFormManager`.
     OSCryptMocker::SetUp();
 
+    ON_CALL(mock_client_, GetPasswordManager)
+        .WillByDefault(Return(&mock_password_manager_));
     ON_CALL(mock_password_manager_, GetPasswordFormCache())
         .WillByDefault(Return(&mock_form_cache_));
     ON_CALL(mock_driver_, IsInPrimaryMainFrame).WillByDefault(Return(true));
@@ -170,9 +178,9 @@
   base::test::SingleThreadTaskEnvironment task_environment_;
   autofill::test::AutofillUnitTestEnvironment autofill_test_environment_{
       {.disable_server_communication = true}};
-  MockPasswordManager mock_password_manager_;
-  MockPasswordFormCache mock_form_cache_;
-  MockPasswordManagerClient mock_client_;
+  testing::NiceMock<MockPasswordManager> mock_password_manager_;
+  testing::NiceMock<MockPasswordFormCache> mock_form_cache_;
+  testing::NiceMock<MockPasswordManagerClient> mock_client_;
   MockStubPasswordManagerDriver mock_driver_;
   FakeFormFetcher form_fetcher_;
 };
@@ -334,6 +342,9 @@
 
 // Tests filling the username and password in a single chosen form.
 TEST_F(ActorLoginCredentialFillerTest, FillUsernameAndPasswordSingleForm) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      password_manager::features::kActorLoginFillingHeuristics);
   const url::Origin origin = url::Origin::Create(GURL(kLoginUrl));
   const Credential credential =
       CreateTestCredential(kTestUsername, origin.GetURL());
@@ -381,6 +392,9 @@
 
 // Tests filling the username in a single chosen form.
 TEST_F(ActorLoginCredentialFillerTest, FillOnlyUsernameFieldSingleForm) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      password_manager::features::kActorLoginFillingHeuristics);
   const url::Origin origin = url::Origin::Create(GURL(kLoginUrl));
   const Credential credential =
       CreateTestCredential(kTestUsername, origin.GetURL());
@@ -429,6 +443,9 @@
 
 // Tests filling the password in a single chosen form.
 TEST_F(ActorLoginCredentialFillerTest, FillOnlyPasswordFieldSingleForm) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      password_manager::features::kActorLoginFillingHeuristics);
   const url::Origin origin = url::Origin::Create(GURL(kLoginUrl));
   const Credential credential =
       CreateTestCredential(kTestUsername, origin.GetURL());
@@ -477,6 +494,9 @@
 
 // Tests filling the username in a single chosen form.
 TEST_F(ActorLoginCredentialFillerTest, FillUsernameFailsSingleForm) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      password_manager::features::kActorLoginFillingHeuristics);
   const url::Origin origin = url::Origin::Create(GURL(kLoginUrl));
   const Credential credential =
       CreateTestCredential(kTestUsername, origin.GetURL());
@@ -521,6 +541,9 @@
 
 // Tests filling the password in a single chosen form.
 TEST_F(ActorLoginCredentialFillerTest, FillPasswordFailsSingleForm) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      password_manager::features::kActorLoginFillingHeuristics);
   const url::Origin origin = url::Origin::Create(GURL(kLoginUrl));
   const Credential credential =
       CreateTestCredential(kTestUsername, origin.GetURL());
@@ -564,6 +587,9 @@
 
 // Tests that filling both fields fails in a single chosen form.
 TEST_F(ActorLoginCredentialFillerTest, FillBothFailsSingleForm) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      password_manager::features::kActorLoginFillingHeuristics);
   const url::Origin origin = url::Origin::Create(GURL(kLoginUrl));
   const Credential credential =
       CreateTestCredential(kTestUsername, origin.GetURL());
@@ -606,6 +632,287 @@
   EXPECT_EQ(result.value(), LoginStatusResult::kErrorNoFillableFields);
 }
 
+// Tests filling username and password succeeds if filling all eligible fields.
+TEST_F(ActorLoginCredentialFillerTest,
+       FillUsernameAndPasswordInAllEligibleFields) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      password_manager::features::kActorLoginFillingHeuristics);
+  const url::Origin origin = url::Origin::Create(GURL(kLoginUrl));
+  const Credential credential =
+      CreateTestCredential(kTestUsername, origin.GetURL());
+  const FormData form_data = CreateSigninFormData(origin.GetURL());
+  const FormData username_only_form_data =
+      CreateUsernameOnlyFormData(origin.GetURL());
+  const FormData password_only_form_data =
+      CreatePasswordOnlyFormData(origin.GetURL());
+
+  // Make sure a saved credential with a matching username exists.
+  SetSavedCredential(&form_fetcher_, origin.GetURL(), kTestUsername,
+                     kTestPassword);
+
+  // Simulate signin forms existing on the page.
+  std::vector<std::unique_ptr<PasswordFormManager>> form_managers;
+  form_managers.push_back(CreateFormManagerWithParsedForm(origin, form_data));
+  form_managers.push_back(
+      CreateFormManagerWithParsedForm(origin, username_only_form_data));
+  form_managers.push_back(
+      CreateFormManagerWithParsedForm(origin, password_only_form_data));
+
+  const PasswordForm* parsed_form = form_managers[0]->GetParsedObservedForm();
+  const PasswordForm* username_only_parsed_form =
+      form_managers[1]->GetParsedObservedForm();
+  const PasswordForm* password_only_parsed_form =
+      form_managers[2]->GetParsedObservedForm();
+
+  base::test::TestFuture<LoginStatusResultOrError> future;
+  ActorLoginCredentialFiller filler(origin, credential, &mock_client_,
+                                    future.GetCallback());
+
+  ON_CALL(mock_form_cache_, GetFormManagers)
+      .WillByDefault(Return(base::span(form_managers)));
+
+  // There are 4 fields to fill, so there should be 4 calls to the driver,
+  // one for each field. Make the first 2 fail and the last 2 succeed.
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(parsed_form->username_element_renderer_id, Eq(kTestUsername),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(false));
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(parsed_form->password_element_renderer_id, Eq(kTestPassword),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(false));
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(username_only_parsed_form->username_element_renderer_id,
+                Eq(kTestUsername),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(true));
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(password_only_parsed_form->password_element_renderer_id,
+                Eq(kTestPassword),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(true));
+
+  filler.AttemptLogin(&mock_password_manager_);
+  const LoginStatusResultOrError& result = future.Get();
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(result.value(),
+            LoginStatusResult::kSuccessUsernameAndPasswordFilled);
+}
+
+TEST_F(ActorLoginCredentialFillerTest, FillOnlyUsernameInAllEligibleFields) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      password_manager::features::kActorLoginFillingHeuristics);
+  const url::Origin origin = url::Origin::Create(GURL(kLoginUrl));
+  const Credential credential =
+      CreateTestCredential(kTestUsername, origin.GetURL());
+  const FormData form_data = CreateSigninFormData(origin.GetURL());
+  const FormData username_only_form_data =
+      CreateUsernameOnlyFormData(origin.GetURL());
+  const FormData password_only_form_data =
+      CreatePasswordOnlyFormData(origin.GetURL());
+
+  // Make sure a saved credential with a matching username exists.
+  SetSavedCredential(&form_fetcher_, origin.GetURL(), kTestUsername,
+                     kTestPassword);
+
+  // Simulate signin forms existing on the page.
+  std::vector<std::unique_ptr<PasswordFormManager>> form_managers;
+  form_managers.push_back(CreateFormManagerWithParsedForm(origin, form_data));
+  form_managers.push_back(
+      CreateFormManagerWithParsedForm(origin, username_only_form_data));
+  form_managers.push_back(
+      CreateFormManagerWithParsedForm(origin, password_only_form_data));
+
+  const PasswordForm* parsed_form = form_managers[0]->GetParsedObservedForm();
+  const PasswordForm* username_only_parsed_form =
+      form_managers[1]->GetParsedObservedForm();
+  const PasswordForm* password_only_parsed_form =
+      form_managers[2]->GetParsedObservedForm();
+
+  base::test::TestFuture<LoginStatusResultOrError> future;
+  ActorLoginCredentialFiller filler(origin, credential, &mock_client_,
+                                    future.GetCallback());
+
+  ON_CALL(mock_form_cache_, GetFormManagers)
+      .WillByDefault(Return(base::span(form_managers)));
+
+  // There are 4 fields to fill, so there should be 4 calls to the driver,
+  // one for each field. Make all password fields filling fail and one
+  // username filling succeed.
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(parsed_form->username_element_renderer_id, Eq(kTestUsername),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(false));
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(parsed_form->password_element_renderer_id, Eq(kTestPassword),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(false));
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(username_only_parsed_form->username_element_renderer_id,
+                Eq(kTestUsername),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(true));
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(password_only_parsed_form->password_element_renderer_id,
+                Eq(kTestPassword),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(false));
+
+  filler.AttemptLogin(&mock_password_manager_);
+  const LoginStatusResultOrError& result = future.Get();
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(result.value(), LoginStatusResult::kSuccessUsernameFilled);
+}
+
+TEST_F(ActorLoginCredentialFillerTest, FillOnlyPasswordInAllEligibleFields) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      password_manager::features::kActorLoginFillingHeuristics);
+  const url::Origin origin = url::Origin::Create(GURL(kLoginUrl));
+  const Credential credential =
+      CreateTestCredential(kTestUsername, origin.GetURL());
+  const FormData form_data = CreateSigninFormData(origin.GetURL());
+  const FormData username_only_form_data =
+      CreateUsernameOnlyFormData(origin.GetURL());
+  const FormData password_only_form_data =
+      CreatePasswordOnlyFormData(origin.GetURL());
+
+  // Make sure a saved credential with a matching username exists.
+  SetSavedCredential(&form_fetcher_, origin.GetURL(), kTestUsername,
+                     kTestPassword);
+
+  // Simulate signin forms existing on the page.
+  std::vector<std::unique_ptr<PasswordFormManager>> form_managers;
+  form_managers.push_back(CreateFormManagerWithParsedForm(origin, form_data));
+  form_managers.push_back(
+      CreateFormManagerWithParsedForm(origin, username_only_form_data));
+  form_managers.push_back(
+      CreateFormManagerWithParsedForm(origin, password_only_form_data));
+
+  const PasswordForm* parsed_form = form_managers[0]->GetParsedObservedForm();
+  const PasswordForm* username_only_parsed_form =
+      form_managers[1]->GetParsedObservedForm();
+  const PasswordForm* password_only_parsed_form =
+      form_managers[2]->GetParsedObservedForm();
+
+  base::test::TestFuture<LoginStatusResultOrError> future;
+  ActorLoginCredentialFiller filler(origin, credential, &mock_client_,
+                                    future.GetCallback());
+
+  ON_CALL(mock_form_cache_, GetFormManagers)
+      .WillByDefault(Return(base::span(form_managers)));
+
+  // There are 4 fields to fill, so there should be 4 calls to the driver,
+  // one for each field. Make all username fields filling fail and one
+  // password filling succeed.
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(parsed_form->username_element_renderer_id, Eq(kTestUsername),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(false));
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(parsed_form->password_element_renderer_id, Eq(kTestPassword),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(true));
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(username_only_parsed_form->username_element_renderer_id,
+                Eq(kTestUsername),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(false));
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(password_only_parsed_form->password_element_renderer_id,
+                Eq(kTestPassword),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(false));
+
+  filler.AttemptLogin(&mock_password_manager_);
+  const LoginStatusResultOrError& result = future.Get();
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(result.value(), LoginStatusResult::kSuccessPasswordFilled);
+}
+
+TEST_F(ActorLoginCredentialFillerTest, FillingFailsInAllEligibleFields) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      password_manager::features::kActorLoginFillingHeuristics);
+  const url::Origin origin = url::Origin::Create(GURL(kLoginUrl));
+  const Credential credential =
+      CreateTestCredential(kTestUsername, origin.GetURL());
+  const FormData form_data = CreateSigninFormData(origin.GetURL());
+  const FormData username_only_form_data =
+      CreateUsernameOnlyFormData(origin.GetURL());
+  const FormData password_only_form_data =
+      CreatePasswordOnlyFormData(origin.GetURL());
+
+  // Make sure a saved credential with a matching username exists.
+  SetSavedCredential(&form_fetcher_, origin.GetURL(), kTestUsername,
+                     kTestPassword);
+
+  // Simulate signin forms existing on the page.
+  std::vector<std::unique_ptr<PasswordFormManager>> form_managers;
+  form_managers.push_back(CreateFormManagerWithParsedForm(origin, form_data));
+  form_managers.push_back(
+      CreateFormManagerWithParsedForm(origin, username_only_form_data));
+  form_managers.push_back(
+      CreateFormManagerWithParsedForm(origin, password_only_form_data));
+
+  const PasswordForm* parsed_form = form_managers[0]->GetParsedObservedForm();
+  const PasswordForm* username_only_parsed_form =
+      form_managers[1]->GetParsedObservedForm();
+  const PasswordForm* password_only_parsed_form =
+      form_managers[2]->GetParsedObservedForm();
+
+  base::test::TestFuture<LoginStatusResultOrError> future;
+  ActorLoginCredentialFiller filler(origin, credential, &mock_client_,
+                                    future.GetCallback());
+
+  ON_CALL(mock_form_cache_, GetFormManagers)
+      .WillByDefault(Return(base::span(form_managers)));
+
+  // There are 4 fields to fill, so there should be 4 calls to the driver,
+  // one for each field. Make all filling attempts fail.
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(parsed_form->username_element_renderer_id, Eq(kTestUsername),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(false));
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(parsed_form->password_element_renderer_id, Eq(kTestPassword),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(false));
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(username_only_parsed_form->username_element_renderer_id,
+                Eq(kTestUsername),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(false));
+  EXPECT_CALL(
+      mock_driver_,
+      FillField(password_only_parsed_form->password_element_renderer_id,
+                Eq(kTestPassword),
+                autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
+      .WillOnce(RunOnceCallback<3>(false));
+
+  filler.AttemptLogin(&mock_password_manager_);
+  const LoginStatusResultOrError& result = future.Get();
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(result.value(), LoginStatusResult::kErrorNoFillableFields);
+}
+
 TEST_F(ActorLoginCredentialFillerTest, FillingIsDisabled) {
   const url::Origin origin =
       url::Origin::Create(GURL("https://example.com/login"));
@@ -668,6 +975,7 @@
       FillField(parsed_form->password_element_renderer_id, Eq(kTestPassword),
                 autofill::FieldPropertiesFlags::kAutofilledActorLogin, _))
       .WillOnce(RunOnceCallback<3>(true));
+
   filler.AttemptLogin(&mock_password_manager_);
   const LoginStatusResultOrError& result = future.Get();
   ASSERT_TRUE(result.has_value());
diff --git a/components/password_manager/core/browser/actor_login/internal/actor_login_util.cc b/components/password_manager/core/browser/actor_login/internal/actor_login_util.cc
index a2b2c7f..a678863 100644
--- a/components/password_manager/core/browser/actor_login/internal/actor_login_util.cc
+++ b/components/password_manager/core/browser/actor_login/internal/actor_login_util.cc
@@ -17,8 +17,6 @@
 
 namespace actor_login {
 
-namespace {
-
 bool IsElementFocusable(autofill::FieldRendererId renderer_id,
                         const autofill::FormData& form_data) {
   auto field = std::ranges::find(form_data.fields(), renderer_id,
@@ -41,9 +39,6 @@
   return (has_focusable_username || has_focusable_password) &&
          !has_focusable_new_password;
 }
-
-}  // namespace
-
 std::u16string GetSourceSiteOrAppFromUrl(const GURL& url) {
   return base::UTF8ToUTF16(url.GetWithEmptyPath().spec());
 }
diff --git a/components/password_manager/core/browser/actor_login/internal/actor_login_util.h b/components/password_manager/core/browser/actor_login/internal/actor_login_util.h
index 7280a9b6..066ed7f2 100644
--- a/components/password_manager/core/browser/actor_login/internal/actor_login_util.h
+++ b/components/password_manager/core/browser/actor_login/internal/actor_login_util.h
@@ -10,6 +10,7 @@
 #include "url/gurl.h"
 
 namespace password_manager {
+struct PasswordForm;
 class PasswordFormCache;
 class PasswordFormManager;
 }  // namespace password_manager
@@ -20,6 +21,9 @@
 
 namespace actor_login {
 
+// Checks whether `form` is a login form.
+bool IsLoginForm(const password_manager::PasswordForm& form);
+
 // Transforms `url` into `Credential.source_site_or_app`
 std::u16string GetSourceSiteOrAppFromUrl(const GURL& url);
 
diff --git a/components/password_manager/core/browser/features/password_features.cc b/components/password_manager/core/browser/features/password_features.cc
index b1862f9..d760750e 100644
--- a/components/password_manager/core/browser/features/password_features.cc
+++ b/components/password_manager/core/browser/features/password_features.cc
@@ -11,6 +11,7 @@
 namespace password_manager::features {
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)  // Desktop
 BASE_FEATURE(kActorLogin, base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kActorLoginFillingHeuristics, base::FEATURE_DISABLED_BY_DEFAULT);
 #endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 
 #if BUILDFLAG(IS_ANDROID)
diff --git a/components/password_manager/core/browser/features/password_features.h b/components/password_manager/core/browser/features/password_features.h
index 2dd1085..d7276b5 100644
--- a/components/password_manager/core/browser/features/password_features.h
+++ b/components/password_manager/core/browser/features/password_features.h
@@ -18,6 +18,7 @@
 
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 BASE_DECLARE_FEATURE(kActorLogin);
+BASE_DECLARE_FEATURE(kActorLoginFillingHeuristics);
 #endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 
 #if BUILDFLAG(IS_ANDROID)
diff --git a/components/password_manager/core/browser/password_session_durations_metrics_recorder.cc b/components/password_manager/core/browser/password_session_durations_metrics_recorder.cc
index 1e286ada..9edb71e 100644
--- a/components/password_manager/core/browser/password_session_durations_metrics_recorder.cc
+++ b/components/password_manager/core/browser/password_session_durations_metrics_recorder.cc
@@ -82,6 +82,13 @@
   CheckForUserStateChange();
 }
 
+void PasswordSessionDurationsMetricsRecorder::OnSyncShutdown(
+    syncer::SyncService* sync) {
+  // Unreachable, since the service owning this instance is Shutdown() before
+  // the SyncService.
+  NOTREACHED();
+}
+
 void PasswordSessionDurationsMetricsRecorder::CheckForUserStateChange() {
   features_util::PasswordAccountStorageUserState new_user_state =
       features_util::ComputePasswordAccountStorageUserState(sync_service_);
diff --git a/components/password_manager/core/browser/password_session_durations_metrics_recorder.h b/components/password_manager/core/browser/password_session_durations_metrics_recorder.h
index 2767f13..52c1342 100644
--- a/components/password_manager/core/browser/password_session_durations_metrics_recorder.h
+++ b/components/password_manager/core/browser/password_session_durations_metrics_recorder.h
@@ -40,6 +40,7 @@
 
   // syncer::SyncServiceObserver:
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
  private:
   void CheckForUserStateChange();
diff --git a/components/password_manager/core/browser/sharing/incoming_password_sharing_invitation_data_type_controller.cc b/components/password_manager/core/browser/sharing/incoming_password_sharing_invitation_data_type_controller.cc
index 6986544..b538511 100644
--- a/components/password_manager/core/browser/sharing/incoming_password_sharing_invitation_data_type_controller.cc
+++ b/components/password_manager/core/browser/sharing/incoming_password_sharing_invitation_data_type_controller.cc
@@ -79,6 +79,11 @@
   sync_service_->DataTypePreconditionChanged(type());
 }
 
+void IncomingPasswordSharingInvitationDataTypeController::OnSyncShutdown(
+    syncer::SyncService* sync) {
+  // Nothing to be done, `this` will be destructed imminently.
+}
+
 void IncomingPasswordSharingInvitationDataTypeController::
     OnPasswordSharingEnabledPolicyChanged() {
   DCHECK(CalledOnValidThread());
diff --git a/components/password_manager/core/browser/sharing/incoming_password_sharing_invitation_data_type_controller.h b/components/password_manager/core/browser/sharing/incoming_password_sharing_invitation_data_type_controller.h
index 18833e5..1ac43f5 100644
--- a/components/password_manager/core/browser/sharing/incoming_password_sharing_invitation_data_type_controller.h
+++ b/components/password_manager/core/browser/sharing/incoming_password_sharing_invitation_data_type_controller.h
@@ -36,6 +36,7 @@
 
   // syncer::SyncServiceObserver implementation.
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
  private:
   void OnPasswordSharingEnabledPolicyChanged();
diff --git a/components/password_manager_strings.grdp b/components/password_manager_strings.grdp
index fdd05b2..21b8298 100644
--- a/components/password_manager_strings.grdp
+++ b/components/password_manager_strings.grdp
@@ -311,7 +311,16 @@
   <message name="IDS_PASSWORD_MANAGER_UI_PROACTIVE_RECOVERY_FOOTER_NON_BRANDED" desc="Footer text of an autofill suggestion drop-down that explains why we are suggesting to try a recovery password.">
     You recently changed a password found in a public data breach. In case of trouble, Password Manager can help you sign in.
   </message>
-  <message name="IDS_PASSWORD_MANAGER_UI_BACKUP_PASSWORD_TAG" desc="Text shown next to the backup credential in the autofill drop-down. In this case the backup suggestion will be shown right after the usual password suggestion that it corresponds to.">
-    Recovery
-  </message>
+  <if expr="is_android">
+    <then>
+      <message name="IDS_PASSWORD_MANAGER_UI_BACKUP_PASSWORD_TAG" desc="Text shown next to the backup credential in keyboard accessory bar. In this case the backup suggestion will be shown right after the usual password suggestion that it corresponds to.">
+        Recovery password
+      </message>
+    </then>
+    <else>
+      <message name="IDS_PASSWORD_MANAGER_UI_BACKUP_PASSWORD_TAG" desc="Text shown next to the backup credential in the autofill drop-down. In this case the backup suggestion will be shown right after the usual password suggestion that it corresponds to.">
+        Recovery
+      </message>
+    </else>
+  </if>
 </grit-part>
diff --git a/components/plus_addresses/core/browser/sync_utils/plus_address_data_type_controller.cc b/components/plus_addresses/core/browser/sync_utils/plus_address_data_type_controller.cc
index 50935a5..58afa7d 100644
--- a/components/plus_addresses/core/browser/sync_utils/plus_address_data_type_controller.cc
+++ b/components/plus_addresses/core/browser/sync_utils/plus_address_data_type_controller.cc
@@ -85,6 +85,10 @@
   }
 }
 
+void PlusAddressDataTypeController::OnSyncShutdown(syncer::SyncService*) {
+  // Nothing to be done, `this` will be destructed imminently.
+}
+
 void PlusAddressDataTypeController::RecreateManagedStatusFinder() {
   managed_status_finder_ = std::make_unique<signin::AccountManagedStatusFinder>(
       identity_manager_, sync_service_->GetAccountInfo(),
diff --git a/components/plus_addresses/core/browser/sync_utils/plus_address_data_type_controller.h b/components/plus_addresses/core/browser/sync_utils/plus_address_data_type_controller.h
index 44b10ff..f1d0c78 100644
--- a/components/plus_addresses/core/browser/sync_utils/plus_address_data_type_controller.h
+++ b/components/plus_addresses/core/browser/sync_utils/plus_address_data_type_controller.h
@@ -50,6 +50,7 @@
 
   // SyncServiceObserver:
   void OnStateChanged(syncer::SyncService*) override;
+  void OnSyncShutdown(syncer::SyncService*) override;
 
  private:
   void RecreateManagedStatusFinder();
diff --git a/components/policy/resources/templates/policies.yaml b/components/policy/resources/templates/policies.yaml
index 1688723..69e18b12 100644
--- a/components/policy/resources/templates/policies.yaml
+++ b/components/policy/resources/templates/policies.yaml
@@ -1395,6 +1395,7 @@
   1394: PreferSlowKexAlgorithms
   1395: ExtensionForceInstallWithNonMalwareViolationsEnabled
   1396: CacheEncryptionEnabled
+  1397: SilentPrintingEnabled
 atomic_groups:
   1: Homepage
   2: RemoteAccess
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/SilentPrintingEnabled.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/SilentPrintingEnabled.yaml
new file mode 100644
index 0000000..d6b4ea56
--- /dev/null
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/SilentPrintingEnabled.yaml
@@ -0,0 +1,30 @@
+caption: Enable Silent Printing
+desc: |-
+  Setting this policy to true enables silent printing, which immediately closes
+  print preview window when opened and prints to the default printer with
+  default options. If the default printer is 'Save as PDF', the file will be
+  saved to the Downloads folder.
+
+  Not setting this policy or setting this policy to false disables silent
+  printing, which doesn't automatically close print preview window and requires
+  the user to make a selection as usual.
+features:
+  dynamic_refresh: true
+  per_profile: false
+items:
+- caption: Enable Silent Printing
+  value: true
+- caption: Disable Silent Printing
+  value: false
+default: false
+example_value: false
+owners:
+- file://components/policy/OWNERS
+- poromov@chromium.org
+- orko@igalia.com
+schema:
+  type: boolean
+future_on:
+- chrome_os
+tags: []
+type: main
diff --git a/components/policy/test/data/pref_mapping/SilentPrintingEnabled.json b/components/policy/test/data/pref_mapping/SilentPrintingEnabled.json
new file mode 100644
index 0000000..83fafe731
--- /dev/null
+++ b/components/policy/test/data/pref_mapping/SilentPrintingEnabled.json
@@ -0,0 +1,5 @@
+[
+  {
+    "reason_for_missing_test": "Mapping not yet implemented."
+  }
+]
diff --git a/components/search_engines/search_engine_choice/search_engine_choice_service.cc b/components/search_engines/search_engine_choice/search_engine_choice_service.cc
index 4a67038..b4425890 100644
--- a/components/search_engines/search_engine_choice/search_engine_choice_service.cc
+++ b/components/search_engines/search_engine_choice/search_engine_choice_service.cc
@@ -329,7 +329,7 @@
 
 bool ManagementStatusEligibleForChoiceScreen(
     const regional_capabilities::ChoiceScreenEligibilityConfig& config,
-    policy::ManagementService& management_service) {
+    policy::ManagementService& platform_management_service) {
   if (!base::FeatureList::IsEnabled(
           switches::kChoiceScreenEligibilityCheckManagementStatus)) {
     return true;
@@ -339,15 +339,7 @@
     return true;
   }
 
-  switch (management_service.GetManagementAuthorityTrustworthiness()) {
-    case policy::ManagementAuthorityTrustworthiness::NONE:
-      return true;
-    case policy::ManagementAuthorityTrustworthiness::LOW:
-    case policy::ManagementAuthorityTrustworthiness::TRUSTED:
-    case policy::ManagementAuthorityTrustworthiness::FULLY_TRUSTED:
-      return false;
-  }
-  NOTREACHED();
+  return !platform_management_service.IsManaged();
 }
 
 // Checks account properties against the eligibility config to determine if the
@@ -413,14 +405,14 @@
     regional_capabilities::RegionalCapabilitiesService& regional_capabilities,
     TemplateURLPrepopulateData::Resolver& prepopulate_data_resolver,
     signin::IdentityManager& identity_manager,
-    policy::ManagementService& management_service)
+    policy::ManagementService& platform_management_service)
     : client_(std::move(client)),
       profile_prefs_(profile_prefs),
       local_state_(local_state),
       regional_capabilities_service_(regional_capabilities),
       prepopulate_data_resolver_(prepopulate_data_resolver),
       identity_manager_(identity_manager),
-      management_service_(management_service) {}
+      platform_management_service_(platform_management_service) {}
 
 SearchEngineChoiceService::~SearchEngineChoiceService() = default;
 
@@ -935,7 +927,7 @@
 
   // 3.1: Check eligibility based on management status.
   if (!ManagementStatusEligibleForChoiceScreen(eligibility_config,
-                                               *management_service_)) {
+                                               *platform_management_service_)) {
     return ChoiceStatus::kManaged;
   }
 
diff --git a/components/search_engines/search_engine_choice/search_engine_choice_service.h b/components/search_engines/search_engine_choice/search_engine_choice_service.h
index 87fb32c..5f1c4c7 100644
--- a/components/search_engines/search_engine_choice/search_engine_choice_service.h
+++ b/components/search_engines/search_engine_choice/search_engine_choice_service.h
@@ -99,7 +99,7 @@
       regional_capabilities::RegionalCapabilitiesService& regional_capabilities,
       TemplateURLPrepopulateData::Resolver& prepopulate_data_resolver,
       signin::IdentityManager& identity_manager,
-      policy::ManagementService& management_service);
+      policy::ManagementService& platform_management_service);
   ~SearchEngineChoiceService() override;
 
   // Runs the initialisation step for this service, checking consistency in the
@@ -278,7 +278,7 @@
   const raw_ref<TemplateURLPrepopulateData::Resolver>
       prepopulate_data_resolver_;
   const raw_ref<signin::IdentityManager> identity_manager_;
-  const raw_ref<policy::ManagementService> management_service_;
+  const raw_ref<policy::ManagementService> platform_management_service_;
   base::ObserverList<Observer> observers_;
 
   // Used to track whether `MaybeRecordChoiceScreenDisplayState()` has already
diff --git a/components/signin/public/android/BUILD.gn b/components/signin/public/android/BUILD.gn
index 6583f33..b7731a2d 100644
--- a/components/signin/public/android/BUILD.gn
+++ b/components/signin/public/android/BUILD.gn
@@ -171,6 +171,7 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//chrome/test/android:chrome_java_test_support_common",
+    "//components/cached_flags:java",
     "//content/public/test/android:content_java_test_support",
     "//google_apis/gaia/android:java",
     "//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacade.java b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
index f2d88895..53aa29e 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
@@ -84,11 +84,10 @@
 
     /**
      * Asynchronously gets OAuth2 access token for the given account and scope. May return a cached
-     * version, use {@link #invalidateAccessToken} to invalidate a token in the cache. Please note
-     * that this method expects a scope with 'oauth2:' prefix.
+     * version, use {@link #invalidateAccessToken} to invalidate a token in the cache.
      *
      * @param account the account to get the access token for.
-     * @param scope The scope to get an auth token for (with Android-style 'oauth2:' prefix).
+     * @param scope The scope to get an auth token for.
      * @param callback called on successful and unsuccessful fetching of auth token.
      */
     @MainThread
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
index acf6c55f..a30a68d5 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
@@ -66,6 +66,8 @@
     // Time, in milliseconds, between two attempts to fetch the accounts.
     private static final long GET_ACCOUNTS_BACKOFF_DELAY = 1000L;
 
+    private static final String OAUTH2_SCOPE_PREFIX = "oauth2:";
+
     private static final String TAG = "AccountManager";
 
     private final AccountManagerDelegate mDelegate;
@@ -157,12 +159,56 @@
         }
 
         pendingRequestStarted();
+
+        if (!SigninFeatureMap.sMigrateAccountManagerDelegate.isEnabled()) {
+            String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope;
+            ConnectionRetry.runAuthTask(
+                    new AuthTask() {
+                        @Override
+                        public AccessTokenData run() throws AuthException {
+                            return mDelegate.getAccessToken(
+                                    CoreAccountInfo.getAndroidAccountFrom(coreAccountInfo),
+                                    oauth2Scope);
+                        }
+
+                        @Override
+                        public void onSuccess(@Nullable AccessTokenData token) {
+                            assert token != null : "AccessTokenData must not be null on success.";
+                            callback.onGetTokenSuccess(token);
+                            pendingRequestFinished();
+                        }
+
+                        @Override
+                        public void onFailure(GoogleServiceAuthError authError) {
+                            callback.onGetTokenFailure(authError);
+                            pendingRequestFinished();
+                        }
+                    });
+            return;
+        }
+
+        getAccounts()
+                .then(
+                        unused -> {
+                            getAccessTokenHelper(coreAccountInfo, scope, callback);
+                        });
+    }
+
+    private void getAccessTokenHelper(
+            CoreAccountInfo coreAccountInfo, String scope, GetAccessTokenCallback callback) {
+        PlatformAccount platformAccount = getPlatformAccount(coreAccountInfo.getGaiaId());
+        if (platformAccount == null) {
+            callback.onGetTokenFailure(
+                    new GoogleServiceAuthError(GoogleServiceAuthErrorState.USER_NOT_SIGNED_UP));
+            pendingRequestFinished();
+            return;
+        }
+
         ConnectionRetry.runAuthTask(
                 new AuthTask() {
                     @Override
                     public AccessTokenData run() throws AuthException {
-                        return mDelegate.getAccessToken(
-                                CoreAccountInfo.getAndroidAccountFrom(coreAccountInfo), scope);
+                        return mDelegate.getAccessTokenForPlatformAccount(platformAccount, scope);
                     }
 
                     @Override
@@ -210,6 +256,11 @@
                 new AuthTask() {
                     @Override
                     public @Nullable AccessTokenData run() throws AuthException {
+                        if (SigninFeatureMap.sMigrateAccountManagerDelegate.isEnabled()) {
+                            mDelegate.invalidateAccessTokenForPlatformAccount(accessToken);
+                            return null;
+                        }
+
                         mDelegate.invalidateAccessToken(accessToken);
                         return null;
                     }
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/PlatformAccount.java b/components/signin/public/android/java/src/org/chromium/components/signin/PlatformAccount.java
index 569add4..131ce7bb 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/PlatformAccount.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/PlatformAccount.java
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 package org.chromium.components.signin;
 
+import androidx.annotation.AnyThread;
 import androidx.annotation.MainThread;
 import androidx.annotation.WorkerThread;
 
@@ -14,7 +15,7 @@
 @NullMarked
 public interface PlatformAccount {
     /** Returns gaiaId of the PlatformAccount. */
-    @MainThread
+    @AnyThread
     GaiaId getId();
 
     /** Returns email of the PlatformAccount. */
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegate.java b/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegate.java
index 50c0f10..2b3bd6f 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegate.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegate.java
@@ -33,8 +33,6 @@
  */
 @NullMarked
 final class ProfileOAuth2TokenServiceDelegate {
-    private static final String OAUTH2_SCOPE_PREFIX = "oauth2:";
-
     private final long mNativePtr;
     private final AccountManagerFacade mAccountManagerFacade;
 
@@ -51,7 +49,7 @@
      * Called by native method AndroidAccessTokenFetcher::Start() to retrieve OAuth2 tokens.
      *
      * @param coreAccountInfo The account info.
-     * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix).
+     * @param scope The scope to get an auth token for.
      * @param nativeCallback The pointer to the native callback that should be run upon completion.
      */
     @MainThread
@@ -73,10 +71,9 @@
                     });
             return;
         }
-        String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope;
         mAccountManagerFacade.getAccessToken(
                 coreAccountInfo,
-                oauth2Scope,
+                scope,
                 new AccountManagerFacade.GetAccessTokenCallback() {
                     @Override
                     public void onGetTokenSuccess(AccessTokenData token) {
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java b/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java
index f94c235a..d054e7a6 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java
@@ -84,7 +84,7 @@
     /** Adds an AccountHolder. */
     public PlatformAccount addAccount(AccountInfo accountInfo) {
         boolean added = false;
-        PlatformAccount account = new FakePlatformAccount(accountInfo);
+        FakePlatformAccount account = new FakePlatformAccount(accountInfo);
         if (SigninFeatureMap.sMigrateAccountManagerDelegate.isEnabled()) {
             added = mPlatformAccounts.add(account);
         } else {
@@ -137,6 +137,14 @@
     }
 
     @Override
+    public AccessTokenData getAccessTokenForPlatformAccount(
+            PlatformAccount account, String authTokenScopes) throws AuthException {
+        FakePlatformAccount platformAccount = (FakePlatformAccount) account;
+        assert platformAccount != null;
+        return platformAccount.getAccessTokenOrGenerateNew(authTokenScopes);
+    }
+
+    @Override
     public void invalidateAccessToken(String authToken) {
         if (authToken == null) {
             throw new IllegalArgumentException("AuthToken can not be null");
@@ -150,6 +158,20 @@
         }
     }
 
+    @Override
+    public void invalidateAccessTokenForPlatformAccount(String authToken) throws AuthException {
+        if (authToken == null) {
+            throw new IllegalArgumentException("AuthToken can not be null");
+        }
+        synchronized (mPlatformAccounts) {
+            for (PlatformAccount account : mPlatformAccounts) {
+                FakePlatformAccount fakePlatformAccount = (FakePlatformAccount) account;
+                if (fakePlatformAccount.removeAccessToken(authToken)) {
+                    break;
+                }
+            }
+        }
+    }
 
     @Override
     public @CapabilityResponse int hasCapability(Account account, String capability) {
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java b/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java
index b37a211..475642a 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java
@@ -25,6 +25,8 @@
 import org.chromium.components.signin.AccountManagerFacadeProvider;
 import org.chromium.components.signin.AccountsChangeObserver;
 import org.chromium.components.signin.AuthException;
+import org.chromium.components.signin.PlatformAccount;
+import org.chromium.components.signin.SigninFeatureMap;
 import org.chromium.components.signin.Tribool;
 import org.chromium.components.signin.base.AccountCapabilities;
 import org.chromium.components.signin.base.AccountInfo;
@@ -39,6 +41,7 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.stream.Collectors;
@@ -107,9 +110,14 @@
 
     // `mAccountHolders` can be read from non-UI threads (this is used by `getAccessToken`), but
     // should only be changed from the UI thread to guarantee the consistency of the observed state.
+    // TODO(crbug.com/429143376): Deprecate AccountHolder after sMigrateAccountManagerDelegate is
+    // enabled by default.
     private final Set<AccountHolder> mAccountHolders =
             Collections.synchronizedSet(new LinkedHashSet<>());
 
+    private final Set<PlatformAccount> mPlatformAccounts =
+            Collections.synchronizedSet(new LinkedHashSet<>());
+
     /** Can be used to cause {@link #getAccessToken} method to fail. */
     private final Map<CoreAccountId, GoogleServiceAuthError> mGetAccessTokenError = new HashMap<>();
 
@@ -155,6 +163,30 @@
     @Override
     public void getAccessToken(
             CoreAccountInfo coreAccountInfo, String scope, GetAccessTokenCallback callback) {
+        if (SigninFeatureMap.sMigrateAccountManagerDelegate.isEnabled()) {
+            @Nullable FakePlatformAccount account = getPlatformAccount(coreAccountInfo.getGaiaId());
+            if (account == null) {
+                Log.w(TAG, "Cannot find account:" + coreAccountInfo.toString());
+                ThreadUtils.postOnUiThread(
+                        () ->
+                                callback.onGetTokenFailure(
+                                        new GoogleServiceAuthError(
+                                                GoogleServiceAuthErrorState.USER_NOT_SIGNED_UP)));
+                return;
+            }
+
+            GoogleServiceAuthError authError = mGetAccessTokenError.get(coreAccountInfo.getId());
+            if (authError != null) {
+                ThreadUtils.postOnUiThread(() -> callback.onGetTokenFailure(authError));
+            } else {
+                ThreadUtils.postOnUiThread(
+                        () ->
+                                callback.onGetTokenSuccess(
+                                        account.getAccessTokenOrGenerateNew(scope)));
+            }
+            return;
+        }
+
         @Nullable AccountHolder accountHolder = getAccountHolder(coreAccountInfo.getId());
         if (accountHolder == null) {
             Log.w(TAG, "Cannot find account:" + coreAccountInfo.toString());
@@ -255,6 +287,17 @@
 
     /** Adds an account represented by {@link AccountInfo}. */
     public void addAccount(AccountInfo accountInfo) {
+        if (SigninFeatureMap.sMigrateAccountManagerDelegate.isEnabled()) {
+            ThreadUtils.runOnUiThreadBlocking(
+                    () -> {
+                        mPlatformAccounts.add(new FakePlatformAccount(accountInfo));
+                        if (mBlockedGetAccountsPromise == null) {
+                            fireOnAccountsChangedNotification();
+                        }
+                    });
+            return;
+        }
+
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     mAccountHolders.add(new AccountHolder(accountInfo));
@@ -269,11 +312,38 @@
      * equality to search for the account to update. Throws if the account can't be found.
      */
     public void updateAccount(AccountInfo accountInfo) {
+        if (SigninFeatureMap.sMigrateAccountManagerDelegate.isEnabled()) {
+            ThreadUtils.runOnUiThreadBlocking(
+                    () -> {
+                        synchronized (mPlatformAccounts) {
+                            @Nullable FakePlatformAccount platformAccount =
+                                    (FakePlatformAccount)
+                                            mPlatformAccounts.stream()
+                                                    .filter(
+                                                            (account) ->
+                                                                    Objects.equals(
+                                                                            account.getId(),
+                                                                            accountInfo
+                                                                                    .getGaiaId()))
+                                                    .findFirst()
+                                                    .orElse(null);
+                            if (platformAccount == null) {
+                                throw new IllegalArgumentException(
+                                        "Account " + accountInfo.getEmail() + " can't be found!");
+                            }
+                            mPlatformAccounts.remove(platformAccount);
+                            mPlatformAccounts.add(new FakePlatformAccount(accountInfo));
+                        }
+                        if (mBlockedGetAccountsPromise == null) {
+                            fireOnAccountsChangedNotification();
+                        }
+                    });
+            return;
+        }
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     synchronized (mAccountHolders) {
-                        @Nullable
-                        AccountHolder accountHolder =
+                        @Nullable AccountHolder accountHolder =
                                 mAccountHolders.stream()
                                         .filter(
                                                 (ah) ->
@@ -445,6 +515,19 @@
         }
     }
 
+    @AnyThread
+    private @Nullable FakePlatformAccount getPlatformAccount(GaiaId gaiaId) {
+        synchronized (mPlatformAccounts) {
+            return (FakePlatformAccount)
+                    mPlatformAccounts.stream()
+                            .filter(
+                                    platformAccount ->
+                                            Objects.equals(gaiaId, platformAccount.getId()))
+                            .findFirst()
+                            .orElse(null);
+        }
+    }
+
     @MainThread
     private void fireOnAccountsChangedNotification() {
         ThreadUtils.checkUiThread();
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakePlatformAccount.java b/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakePlatformAccount.java
index 33b15c4f..a9a6e71 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakePlatformAccount.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakePlatformAccount.java
@@ -3,17 +3,26 @@
 // found in the LICENSE file.
 package org.chromium.components.signin.test.util;
 
+import org.chromium.build.annotations.Nullable;
+import org.chromium.components.signin.AccessTokenData;
 import org.chromium.components.signin.AccountManagerDelegate;
 import org.chromium.components.signin.PlatformAccount;
 import org.chromium.components.signin.base.AccountInfo;
 import org.chromium.google_apis.gaia.GaiaId;
 
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
 /**
  * A test implementation of {@link PlatformAccount} for testing components that depend on {@link
  * AccountManagerDelegate}.
  */
 public class FakePlatformAccount implements PlatformAccount {
     private final AccountInfo mAccount;
+    private final Map<String, AccessTokenData> mAccessTokens =
+            Collections.synchronizedMap(new HashMap<>());
 
     public FakePlatformAccount(AccountInfo accountInfo) {
         mAccount = accountInfo;
@@ -46,4 +55,26 @@
         // specified capability or not.
         return AccountManagerDelegate.CapabilityResponse.EXCEPTION;
     }
+
+    /**
+     * Gets the access token for the given scope. If a token has not been previously generated for
+     * this scope, a new one will be created.
+     */
+    @Nullable
+    public AccessTokenData getAccessTokenOrGenerateNew(String scope) {
+        return mAccessTokens.computeIfAbsent(
+                scope, (ignored) -> new AccessTokenData(UUID.randomUUID().toString()));
+    }
+
+    /**
+     * Removes an auth token from the auth token map.
+     *
+     * @param authToken the auth token to remove.
+     * @return true if the auth token was found.
+     */
+    public boolean removeAccessToken(String accessToken) {
+        return mAccessTokens
+                .values()
+                .removeIf(tokenData -> accessToken.equals(tokenData.getToken()));
+    }
 }
diff --git a/components/signin/public/android/javatests/src/org/chromium/components/signin/AccountManagerFacadeTest.java b/components/signin/public/android/javatests/src/org/chromium/components/signin/AccountManagerFacadeTest.java
index 21a4a5e4..db6ebc2 100644
--- a/components/signin/public/android/javatests/src/org/chromium/components/signin/AccountManagerFacadeTest.java
+++ b/components/signin/public/android/javatests/src/org/chromium/components/signin/AccountManagerFacadeTest.java
@@ -33,6 +33,7 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DoNotBatch;
+import org.chromium.components.signin.base.AccountInfo;
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.components.signin.test.util.FakeAccountManagerDelegate;
 import org.chromium.components.signin.test.util.TestAccounts;
@@ -81,6 +82,13 @@
             return super.getAccessToken(account, scope);
         }
 
+        @Override
+        public AccessTokenData getAccessTokenForPlatformAccount(
+                PlatformAccount platformAccount, String authTokenScopes) throws AuthException {
+            maybeBlockOnLatch(mGetAuthTokenLatch);
+            return super.getAccessTokenForPlatformAccount(platformAccount, authTokenScopes);
+        }
+
         void blockGetAuthToken() {
             assertThat(mGetAuthTokenLatch).isNull();
             mGetAuthTokenLatch = new CountDownLatch(1);
@@ -141,7 +149,8 @@
         }
     }
 
-    private static final String TOKEN_SCOPE = "oauth2:http://example.com/scope";
+    private static final String TOKEN_SCOPE = "http://example.com/scope";
+    private static final String OAUTH2_SCOPE_PREFIX = "oauth2:";
 
     public static class AccountManagerFacadeTestParams implements ParameterProvider {
         private static final List<ParameterSet> sAccountManagerFacadeTestParams =
@@ -254,15 +263,26 @@
 
     @Test
     @SmallTest
-    public void testGetOAuth2AccessTokenOnSuccess() throws AuthException {
+    @ParameterAnnotations.UseMethodParameter(AccountManagerFacadeTestParams.class)
+    public void testGetOAuth2AccessTokenOnSuccess(boolean isMigrationEnabled) throws AuthException {
         FakeAccountManagerDelegate delegate = new FakeAccountManagerDelegate();
         AccountManagerFacade facade =
                 ThreadUtils.runOnUiThreadBlocking(() -> new AccountManagerFacadeImpl(delegate));
+        PlatformAccount platformAccount = delegate.addAccount(TestAccounts.ACCOUNT1);
+        waitForAccountToBeAdded(facade, TestAccounts.ACCOUNT1);
 
-        delegate.addAccount(TestAccounts.ACCOUNT1);
-        AccessTokenData expectedToken =
-                delegate.getAccessToken(
-                        CoreAccountInfo.getAndroidAccountFrom(TestAccounts.ACCOUNT1), TOKEN_SCOPE);
+        final AccessTokenData expectedToken;
+        if (isMigrationEnabled) {
+            assert SigninFeatureMap.sMigrateAccountManagerDelegate.isEnabled();
+            expectedToken = delegate.getAccessTokenForPlatformAccount(platformAccount, TOKEN_SCOPE);
+            assertNotNull(expectedToken);
+        } else {
+            expectedToken =
+                    delegate.getAccessToken(
+                            CoreAccountInfo.getAndroidAccountFrom(TestAccounts.ACCOUNT1),
+                            OAUTH2_SCOPE_PREFIX + TOKEN_SCOPE);
+            assertNotNull(expectedToken);
+        }
 
         CustomGetAccessTokenCallback callback = new CustomGetAccessTokenCallback();
         ThreadUtils.runOnUiThread(
@@ -288,12 +308,13 @@
 
     @Test
     @SmallTest
-    public void testGetAndInvalidateAccessToken() throws Exception {
+    @ParameterAnnotations.UseMethodParameter(AccountManagerFacadeTestParams.class)
+    public void testGetAndInvalidateAccessToken(boolean isMigrationEnabled) throws Exception {
         FakeAccountManagerDelegate delegate = new FakeAccountManagerDelegate();
         AccountManagerFacade facade =
                 ThreadUtils.runOnUiThreadBlocking(() -> new AccountManagerFacadeImpl(delegate));
-
         delegate.addAccount(TestAccounts.ACCOUNT1);
+        waitForAccountToBeAdded(facade, TestAccounts.ACCOUNT1);
 
         CustomGetAccessTokenCallback callback1 = new CustomGetAccessTokenCallback();
         ThreadUtils.runOnUiThread(
@@ -327,9 +348,7 @@
 
     @Test
     @SmallTest
-    @ParameterAnnotations.UseMethodParameter(AccountManagerFacadeTestParams.class)
-    public void testWaitForPendingTokenRequestsToComplete(boolean isMigrationEnabled)
-            throws Exception {
+    public void testWaitForPendingTokenRequestsToComplete() throws Exception {
         BlockingAccountManagerDelegate blockingDelegate = new BlockingAccountManagerDelegate();
         blockingDelegate.addAccount(TestAccounts.ACCOUNT1);
         blockingDelegate.blockGetAuthToken();
@@ -356,4 +375,30 @@
         pendingRequestsCompleteCallback.waitForOnly();
         assertTrue(tokenCallback.isReady());
     }
+
+    @Test
+    @SmallTest
+    public void testFetchAccessTokenIfNoAccountsAreLoaded() throws Exception {
+        FeatureOverrides.overrideFlag(SigninFeatures.MIGRATE_ACCOUNT_MANAGER_DELEGATE, true);
+
+        FakeAccountManagerDelegate delegate = new FakeAccountManagerDelegate();
+        AccountManagerFacade facade =
+                ThreadUtils.runOnUiThreadBlocking(() -> new AccountManagerFacadeImpl(delegate));
+        assert SigninFeatureMap.sMigrateAccountManagerDelegate.isEnabled();
+        CustomGetAccessTokenCallback callback = new CustomGetAccessTokenCallback();
+
+        // Fetching account with a test
+        ThreadUtils.runOnUiThread(
+                () -> facade.getAccessToken(TestAccounts.ACCOUNT1, TOKEN_SCOPE, callback));
+
+        assertNull(callback.getToken());
+    }
+
+    private static void waitForAccountToBeAdded(AccountManagerFacade facade, AccountInfo account) {
+        CriteriaHelper.pollUiThread(
+                () -> {
+                    return facade.getAccounts().isFulfilled()
+                            && facade.getAccounts().getResult().contains(account);
+                });
+    }
 }
diff --git a/components/stylus_handwriting/android/BUILD.gn b/components/stylus_handwriting/android/BUILD.gn
index 8f69e1b..8f5d63d 100644
--- a/components/stylus_handwriting/android/BUILD.gn
+++ b/components/stylus_handwriting/android/BUILD.gn
@@ -45,6 +45,7 @@
     "//base:base_java",
     "//content/public/android:content_full_java",
     "//mojo/public/mojom/base:base_java",
+    "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
     "//third_party/androidx:androidx_annotation_annotation_experimental_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_core_core_java",
diff --git a/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/StylusHandwritingFeatureMap.java b/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/StylusHandwritingFeatureMap.java
index 9bf375d3..57b6905 100644
--- a/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/StylusHandwritingFeatureMap.java
+++ b/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/StylusHandwritingFeatureMap.java
@@ -18,6 +18,8 @@
 
     public static final String CACHE_STYLUS_SETTINGS = "CacheStylusSettings";
     public static final String USE_HANDWRITING_INITIATOR = "UseHandwritingInitiator";
+    public static final String PROBE_STYLUS_WRITING_IN_BACKGROUND =
+            "ProbeStylusWritingInBackground";
     private static final StylusHandwritingFeatureMap sInstance = new StylusHandwritingFeatureMap();
 
     // Do not instantiate this class.
diff --git a/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/StylusWritingController.java b/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/StylusWritingController.java
index 8d9c24c..520e605 100644
--- a/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/StylusWritingController.java
+++ b/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/StylusWritingController.java
@@ -8,11 +8,23 @@
 import android.os.Build;
 import android.view.PointerIcon;
 
+import androidx.annotation.AnyThread;
+import androidx.annotation.IntDef;
+import androidx.annotation.MainThread;
+
+import org.chromium.base.TraceEvent;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskTraits;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.content_public.browser.StylusWritingHandler;
 import org.chromium.content_public.browser.WebContents;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.annotation.concurrent.GuardedBy;
+
 /**
  * Helper class to determine whether Direct writing service is in consideration or the Android
  * platform Stylus Writing feature, and to set the appropriate handler to WebContents.
@@ -28,6 +40,25 @@
     private boolean mShouldOverrideStylusHoverIcon;
     private boolean mIsWindowFocused;
 
+    @IntDef({
+        HandlerType.UNSET,
+        HandlerType.DIRECT_WRITING_TRIGGER,
+        HandlerType.ANDROID_HANDLER,
+        HandlerType.DISABLED_STYLUS_WRITING_HANDLER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface HandlerType {
+        int UNSET = 0;
+        int DIRECT_WRITING_TRIGGER = 1;
+        int ANDROID_HANDLER = 2;
+        int DISABLED_STYLUS_WRITING_HANDLER = 3;
+    }
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private @HandlerType int mHandlerType = HandlerType.UNSET;
+
     private @Nullable AndroidStylusWritingHandler mAndroidHandler;
     private @Nullable DirectWritingTrigger mDirectWritingTrigger;
     private @Nullable DisabledStylusWritingHandler mDisabledStylusWritingHandler;
@@ -40,10 +71,12 @@
     }
 
     /** Creates a new instance of this class. */
+    @MainThread
     public StylusWritingController(Context context) {
         this(context, false);
     }
 
+    @MainThread
     public StylusWritingController(
             Context context, boolean lazyFetchHandWritingIconFeatureEnabled) {
         mContext = context;
@@ -58,6 +91,7 @@
         }
     }
 
+    @MainThread
     private @Nullable PointerIcon getHandwritingIcon() {
         if (!mIconFetched) {
             int iconType = getHandler().getStylusPointerIcon();
@@ -71,41 +105,89 @@
     }
 
     /**
+     * Determine the handler type that is needed.
+     *
+     * <p>This can be somewhat expensive, requiring multiple binder calls.
+     *
+     * <p>May be run on a background thread.
+     */
+    @AnyThread
+    private static @HandlerType int computeHandlerType(Context context) {
+        try (TraceEvent e = TraceEvent.scoped("StylusWritingController.computeHandlerType")) {
+            if (DirectWritingSettingsHelper.isEnabled(context)) {
+                // Lazily initialize the various handlers since a lot of the time only one will be
+                // used.
+                return HandlerType.DIRECT_WRITING_TRIGGER;
+            }
+
+            // The check for Android T is already in isEnabled but we are adding it here too to make
+            // lint happy.
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+                    && AndroidStylusWritingHandler.isEnabled(context)) {
+                return HandlerType.ANDROID_HANDLER;
+            }
+
+            return HandlerType.DISABLED_STYLUS_WRITING_HANDLER;
+        }
+    }
+
+    /**
+     * Determine and cache the handler type that is needed.
+     *
+     * <p>May be run on a background thread to pre-cache the value.
+     */
+    @AnyThread
+    private @HandlerType int getHandlerType(boolean refresh) {
+        if (!StylusHandwritingFeatureMap.isEnabledOrDefault(
+                StylusHandwritingFeatureMap.CACHE_STYLUS_SETTINGS, false)) {
+            return computeHandlerType(mContext);
+        }
+
+        synchronized (mLock) {
+            if (mHandlerType == HandlerType.UNSET || refresh) {
+                mHandlerType = computeHandlerType(mContext);
+            }
+            return mHandlerType;
+        }
+    }
+
+    /**
      * Returns the appropriate StylusWritingHandler - this may change at runtime if the user
      * enables/disables the stylus writing feature in their Android settings.
      */
+    @MainThread
     private StylusApiOption chooseHandler() {
-        if (DirectWritingSettingsHelper.isEnabled(mContext)) {
-            // Lazily initialize the various handlers since a lot of the time only one will be used.
-            if (mDirectWritingTrigger == null) {
-                mDirectWritingTrigger = new DirectWritingTrigger();
+        try (TraceEvent e = TraceEvent.scoped("StylusWritingController.chooseHandler")) {
+            switch (getHandlerType(/* refresh= */ false)) {
+                case HandlerType.DIRECT_WRITING_TRIGGER:
+                    if (mDirectWritingTrigger == null) {
+                        mDirectWritingTrigger = new DirectWritingTrigger();
+                    }
+                    return mDirectWritingTrigger;
+                case HandlerType.ANDROID_HANDLER:
+                    // This case isn't reachable unless we're on Android T, but we need to make the
+                    // lint happy.
+                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+                        if (mAndroidHandler == null) {
+                            mAndroidHandler = new AndroidStylusWritingHandler(mContext);
+                        }
+                        return mAndroidHandler;
+                    }
+                    break;
             }
 
-            return mDirectWritingTrigger;
-        }
-
-        // The check for Android T is already in isEnabled but we are adding it here too to make
-        // lint happy.
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
-                && AndroidStylusWritingHandler.isEnabled(mContext)) {
-            if (mAndroidHandler == null) {
-                mAndroidHandler = new AndroidStylusWritingHandler(mContext);
+            if (mDisabledStylusWritingHandler == null) {
+                mDisabledStylusWritingHandler = new DisabledStylusWritingHandler();
             }
-
-            return mAndroidHandler;
+            return mDisabledStylusWritingHandler;
         }
-
-        if (mDisabledStylusWritingHandler == null) {
-            mDisabledStylusWritingHandler = new DisabledStylusWritingHandler();
-        }
-
-        return mDisabledStylusWritingHandler;
     }
 
     /*
      * Returns the currently selected handler, initializing it lazily if it has not been initialized
      * already.
      */
+    @MainThread
     private StylusApiOption getHandler() {
         // If the feature is enabled, we listen to settings changes and re-run the handler selection
         // logic if stylus related settings changed. If the feature is disabled, we re-run the
@@ -121,20 +203,74 @@
     }
 
     /**
+     * Determining the handler type for the first time can be an expensive blocking operation taking
+     * multiple milliseconds. This wrapper can be used to pre-cache the handler type on a background
+     * thread if needed before continuing work on the UI thread.
+     *
+     * <p>Tasks should prefer to use instance state rather than captured state, as posted tasks may
+     * otherwise operate on stale data.
+     *
+     * <p>Must be called from the UI thread.
+     */
+    @MainThread
+    private void probeSupportThenRunOrPost(boolean refresh, Runnable task) {
+        boolean background =
+                StylusHandwritingFeatureMap.isEnabledOrDefault(
+                        StylusHandwritingFeatureMap.PROBE_STYLUS_WRITING_IN_BACKGROUND, false);
+        boolean cache =
+                StylusHandwritingFeatureMap.isEnabledOrDefault(
+                        StylusHandwritingFeatureMap.CACHE_STYLUS_SETTINGS, false);
+        if (background && cache) {
+            // Note that multiple precache tasks could be posted in a race, but the underlying work
+            // is guarded by locks and the caching will avoid any expensive duplicate work.
+            PostTask.postTask(
+                    TaskTraits.USER_VISIBLE_MAY_BLOCK,
+                    () -> {
+                        getHandlerType(refresh);
+                        PostTask.postTask(
+                                TaskTraits.UI_USER_VISIBLE,
+                                () -> {
+                                    if (refresh) {
+                                        mStylusHandler = null;
+                                    }
+                                    task.run();
+                                });
+                    });
+        } else {
+            getHandlerType(refresh);
+            if (refresh) {
+                mStylusHandler = null;
+            }
+            task.run();
+        }
+    }
+
+    /**
      * Notifies the applicable {@link StylusWritingHandler} so that stylus writing messages can be
      * received and handled for performing handwriting recognition.
      *
      * @param webContents current web contents
      */
+    @MainThread
     public void onWebContentsChanged(WebContents webContents) {
         if (webContents.getViewAndroidDelegate() == null) return;
 
         mCurrentWebContents = webContents;
-        StylusApiOption handler = getHandler();
-        handler.onWebContentsChanged(mContext, webContents);
-        webContents
-                .getViewAndroidDelegate()
-                .setShouldShowStylusHoverIconCallback(this::setShouldOverrideStylusHoverIcon);
+
+        probeSupportThenRunOrPost(
+                /* refresh= */ false,
+                () -> {
+                    if (mCurrentWebContents == null) return;
+                    if (mCurrentWebContents.getViewAndroidDelegate() == null) return;
+
+                    StylusApiOption handler = getHandler();
+                    handler.onWebContentsChanged(mContext, mCurrentWebContents);
+
+                    mCurrentWebContents
+                            .getViewAndroidDelegate()
+                            .setShouldShowStylusHoverIconCallback(
+                                    this::setShouldOverrideStylusHoverIcon);
+                });
     }
 
     /**
@@ -142,21 +278,31 @@
      *
      * @param hasFocus whether window gained or lost focus
      */
+    @MainThread
     public void onWindowFocusChanged(boolean hasFocus) {
         // This notification is used to determine if the Stylus writing feature is enabled or not
         // from System settings as it can be changed while Chrome is in background. If caching of
         // stylus settings is enabled, we need to store the current focus state and send it when
         // settings change is observed.
         mIsWindowFocused = hasFocus;
-        updateStylusState();
+        probeSupportThenRunOrPost(
+                /* refresh= */ false,
+                () -> {
+                    updateStylusState();
+                });
     }
 
     /** Notify stylus related settings changed. */
+    @MainThread
     public void onSettingsChange() {
-        mStylusHandler = chooseHandler();
-        updateStylusState();
+        probeSupportThenRunOrPost(
+                /* refresh= */ true,
+                () -> {
+                    updateStylusState();
+                });
     }
 
+    @MainThread
     public @Nullable PointerIcon resolvePointerIcon() {
         if (mShouldOverrideStylusHoverIcon) {
             return mLazyFetchHandWritingIconFeatureEnabled
@@ -166,10 +312,12 @@
         return null;
     }
 
+    @MainThread
     private void setShouldOverrideStylusHoverIcon(boolean shouldOverride) {
         mShouldOverrideStylusHoverIcon = shouldOverride;
     }
 
+    @MainThread
     private void updateStylusState() {
         StylusApiOption handler = getHandler();
         handler.updateHandlerState(mContext, mIsWindowFocused);
diff --git a/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/StylusWritingSettingsState.java b/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/StylusWritingSettingsState.java
index cda7df0..b191b06 100644
--- a/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/StylusWritingSettingsState.java
+++ b/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/StylusWritingSettingsState.java
@@ -11,6 +11,9 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 
+import androidx.annotation.AnyThread;
+import androidx.annotation.MainThread;
+
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ObserverList;
 import org.chromium.base.ThreadUtils;
@@ -19,11 +22,16 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
- * Keeps an updated values of settings interesting for the stylus functionality. All methods are
- * expected to be called on the UI thread only. If this changed in the future, we should make sure
- * that the class is thread-safe.
+ * Keeps an updated values of settings interesting for the stylus functionality.
+ *
+ * <p>Settings can be (lazily) initialized and queried on any thread. However, observers are run on
+ * the UI thread and must also be registered/unregistered on the UI thread.
+ *
+ * <p>Note that settings may change in-between getter calls from a non-UI thread.
  */
 @NullMarked
 public class StylusWritingSettingsState {
@@ -32,15 +40,22 @@
     private static final String URI_DIRECT_WRITING = "direct_writing";
     private static final String URI_STYLUS_HANDWRITING = "stylus_handwriting_enabled";
 
-    private @Nullable String mDefaultInputMethod;
-    private @Nullable Integer mDirectWritingSetting;
-    private int mStylusHandWritingSetting;
-    private final ObserverList<StylusWritingController> mObservers = new ObserverList<>();
+    private final AtomicReference<@Nullable String> mDefaultInputMethod =
+            new AtomicReference<>(null);
+    private final AtomicReference<@Nullable Integer> mDirectWritingSetting =
+            new AtomicReference<>(null);
+    private final AtomicInteger mStylusHandWritingSetting = new AtomicInteger(0);
 
-    private static final StylusWritingSettingsState sInstance = new StylusWritingSettingsState();
+    // Lazily construct on the UI thread in getObserverList(), as the state object may be created on
+    // a background thread. Must only be accessed from the UI thread.
+    @Nullable private ObserverList<StylusWritingController> mObservers;
 
+    private static class LazyHolder {
+        static final StylusWritingSettingsState INSTANCE = new StylusWritingSettingsState();
+    }
+
+    @AnyThread
     private StylusWritingSettingsState() {
-        ThreadUtils.assertOnUiThread();
         ContentResolver contentResolver = ContextUtils.getApplicationContext().getContentResolver();
         ContentObserver stateObserver =
                 new ContentObserver(ThreadUtils.getUiThreadHandler()) {
@@ -80,59 +95,72 @@
     }
 
     // Updates the settings values for stylus
+    @AnyThread
     private void update() {
         ContentResolver contentResolver = ContextUtils.getApplicationContext().getContentResolver();
-        mDefaultInputMethod =
-                Settings.Secure.getString(contentResolver, Settings.Secure.DEFAULT_INPUT_METHOD);
+        mDefaultInputMethod.set(
+                Settings.Secure.getString(contentResolver, Settings.Secure.DEFAULT_INPUT_METHOD));
         try {
-            mDirectWritingSetting = Settings.System.getInt(contentResolver, URI_DIRECT_WRITING);
+            mDirectWritingSetting.set(Settings.System.getInt(contentResolver, URI_DIRECT_WRITING));
         } catch (SecurityException | SettingNotFoundException e) {
             // On some devices, URI_DIRECT_WRITING is not readable and trying to do so will
             // throw a security exception. https://crbug.com/1356155.
-            mDirectWritingSetting = null;
+            mDirectWritingSetting.set(null);
         }
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
-            mStylusHandWritingSetting =
-                    Settings.Secure.getInt(contentResolver, URI_STYLUS_HANDWRITING, 1);
+            mStylusHandWritingSetting.set(
+                    Settings.Secure.getInt(contentResolver, URI_STYLUS_HANDWRITING, 1));
         } else {
-            mStylusHandWritingSetting =
-                    Settings.Global.getInt(contentResolver, URI_STYLUS_HANDWRITING, -1);
+            mStylusHandWritingSetting.set(
+                    Settings.Global.getInt(contentResolver, URI_STYLUS_HANDWRITING, -1));
         }
     }
 
+    @MainThread
+    private ObserverList<StylusWritingController> getObserverList() {
+        if (mObservers == null) {
+            mObservers = new ObserverList();
+        }
+        return mObservers;
+    }
+
+    @MainThread
     private void notifyObservers() {
-        for (StylusWritingController controller : mObservers) {
+        for (StylusWritingController controller : getObserverList()) {
             controller.onSettingsChange();
         }
     }
 
+    @AnyThread
     public static StylusWritingSettingsState getInstance() {
-        return sInstance;
+        return LazyHolder.INSTANCE;
     }
 
+    @AnyThread
     public @Nullable String getDefaultInputMethod() {
-        ThreadUtils.assertOnUiThread();
-        return mDefaultInputMethod;
+        return mDefaultInputMethod.get();
     }
 
+    @AnyThread
     public @Nullable Integer getDirectWritingSetting() {
-        ThreadUtils.assertOnUiThread();
-        return mDirectWritingSetting;
+        return mDirectWritingSetting.get();
     }
 
+    @AnyThread
     public int getStylusHandWritingSetting() {
-        ThreadUtils.assertOnUiThread();
-        return mStylusHandWritingSetting;
+        return mStylusHandWritingSetting.get();
     }
 
+    @MainThread
     public boolean registerObserver(StylusWritingController controller) {
         ThreadUtils.assertOnUiThread();
-        return mObservers.addObserver(controller);
+        return getObserverList().addObserver(controller);
     }
 
+    @MainThread
     public boolean unregisterObserver(StylusWritingController controller) {
         ThreadUtils.assertOnUiThread();
-        return mObservers.removeObserver(controller);
+        return getObserverList().removeObserver(controller);
     }
 }
diff --git a/components/stylus_handwriting/android/stylus_handwriting_feature_map.cc b/components/stylus_handwriting/android/stylus_handwriting_feature_map.cc
index 84f13e4..fd41e5b 100644
--- a/components/stylus_handwriting/android/stylus_handwriting_feature_map.cc
+++ b/components/stylus_handwriting/android/stylus_handwriting_feature_map.cc
@@ -15,6 +15,7 @@
 
 const base::Feature* const kFeaturesExposedToJava[] = {
     &kCacheStylusSettings,
+    &kProbeStylusWritingInBackground,
 };
 
 // static
@@ -34,5 +35,7 @@
 
 // Cache Stylus related settings
 BASE_FEATURE(kCacheStylusSettings, base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kProbeStylusWritingInBackground,
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 }  // namespace stylus_handwriting::android
diff --git a/components/stylus_handwriting/android/stylus_handwriting_feature_map.h b/components/stylus_handwriting/android/stylus_handwriting_feature_map.h
index ce67f77..407e0e7 100644
--- a/components/stylus_handwriting/android/stylus_handwriting_feature_map.h
+++ b/components/stylus_handwriting/android/stylus_handwriting_feature_map.h
@@ -12,6 +12,7 @@
 namespace stylus_handwriting::android {
 
 BASE_DECLARE_FEATURE(kCacheStylusSettings);
+BASE_DECLARE_FEATURE(kProbeStylusWritingInBackground);
 
 }  // namespace stylus_handwriting::android
 
diff --git a/components/sync/model/client_tag_based_data_type_processor.cc b/components/sync/model/client_tag_based_data_type_processor.cc
index 892e358f..8f3552da 100644
--- a/components/sync/model/client_tag_based_data_type_processor.cc
+++ b/components/sync/model/client_tag_based_data_type_processor.cc
@@ -10,6 +10,7 @@
 
 #include "base/auto_reset.h"
 #include "base/debug/alias.h"
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
@@ -46,6 +47,9 @@
 namespace syncer {
 namespace {
 
+BASE_FEATURE(kSyncClearMetadataOnEmptyStorageKeys,
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 const char kErrorSiteHistogramPrefix[] = "Sync.DataTypeErrorSite.";
 
 // These values are persisted to logs. Entries should not be renumbered and
@@ -203,7 +207,10 @@
     return;
   }
 
-  if (ClearPersistedMetadataIfInvalid(*batch)) {
+  if (ShouldClearPersistedMetadata(*batch)) {
+    ClearAllProvidedMetadataAndResetState(batch->GetAllMetadata());
+    // Not having `entity_tracker_` results in doing the initial sync again.
+    CHECK(!entity_tracker_);
     DLOG(ERROR) << "The persisted metadata was invalid and was cleared for "
                 << DataTypeToDebugString(type_) << ". Start over fresh.";
   } else {
@@ -220,6 +227,9 @@
       DUMP_WILL_BE_CHECK(batch->GetAllMetadata().empty());
     }
   }
+  // Whether metadata was actually cleared or not, any pending clear has now
+  // been processed.
+  pending_clear_metadata_ = false;
 
   DUMP_WILL_BE_CHECK(model_ready_to_sync_);
   ConnectIfReady();
@@ -1406,8 +1416,8 @@
   std::move(callback).Run(std::move(all_nodes));
 }
 
-bool ClientTagBasedDataTypeProcessor::ClearPersistedMetadataIfInvalid(
-    const MetadataBatch& metadata) {
+bool ClientTagBasedDataTypeProcessor::ShouldClearPersistedMetadata(
+    const MetadataBatch& metadata) const {
   // The entity tracker must not have been created before the metadata was
   // validated.
   CHECK(!entity_tracker_);
@@ -1419,18 +1429,17 @@
   // If so, clear the metadata from storage (using the bridge's
   // ApplyDisableSyncChanges()).
   if (pending_clear_metadata_) {
-    pending_clear_metadata_ = false;
     // Avoid calling the bridge if there's nothing to clear.
     if (data_type_state.ByteSizeLong() > 0 || !metadata_map.empty()) {
       LogClearMetadataWhileStoppedHistogram(type_, /*is_delayed_call=*/true);
       // This will incur an I/O operation by asking the bridge to clear the
       // metadata in storage.
-      ClearAllProvidedMetadataAndResetState(metadata_map);
-      // Not having `entity_tracker_` results in doing the initial sync again.
-      CHECK(!entity_tracker_);
+      // Note: The caller is responsible for resetting `pending_clear_metadata_`
+      // after clearing.
       return true;
     }
-    // Else: There was nothing to clear.
+    // Else: There was nothing to clear (so also no need to do the other
+    // validity checks).
     return false;
   }
 
@@ -1439,9 +1448,6 @@
   if (!IsInitialSyncAtLeastPartiallyDone(
           data_type_state.initial_sync_state()) &&
       !metadata_map.empty()) {
-    ClearAllProvidedMetadataAndResetState(metadata_map);
-    // Not having `entity_tracker_` results in doing the initial sync again.
-    CHECK(!entity_tracker_);
     return true;
   }
 
@@ -1456,12 +1462,20 @@
           DataTypeHistogramValue(type_));
     }
 
-    ClearAllProvidedMetadataAndResetState(metadata_map);
-    // Not having `entity_tracker_` results in doing the initial sync again.
-    CHECK(!entity_tracker_);
     return true;
   }
 
+  // Check that there are no empty/missing storage keys.
+  if (base::FeatureList::IsEnabled(kSyncClearMetadataOnEmptyStorageKeys)) {
+    for (const auto& [storage_key, _] : metadata_map) {
+      if (storage_key.empty()) {
+        base::UmaHistogramEnumeration("Sync.ClearMetadataDueToEmptyStorageKey",
+                                      DataTypeHistogramValue(type_));
+        return true;
+      }
+    }
+  }
+
   return false;
 }
 
diff --git a/components/sync/model/client_tag_based_data_type_processor.h b/components/sync/model/client_tag_based_data_type_processor.h
index e078cd9..4606e81 100644
--- a/components/sync/model/client_tag_based_data_type_processor.h
+++ b/components/sync/model/client_tag_based_data_type_processor.h
@@ -260,10 +260,9 @@
   // if it is invalid.
   void ClearPersistedMetadataIfInconsistentWithActivationRequest();
 
-  // Verifies that the passed-in metadata (DataTypeState plus entity metadata)
-  // is valid, and clears it (incl. the persisted data) if not. Returns whether
-  // the metadata was cleared.
-  bool ClearPersistedMetadataIfInvalid(const MetadataBatch& metadata);
+  // Returns whether the passed-in metadata should be cleared due to (a) being
+  // invalid, or (b) `pending_clear_metadata_`.
+  bool ShouldClearPersistedMetadata(const MetadataBatch& metadata) const;
 
   // Reports error and records a metric about `site` where the error occurred.
   void ReportErrorImpl(const ModelError& error, ErrorSite site);
diff --git a/components/sync/model/client_tag_based_data_type_processor_unittest.cc b/components/sync/model/client_tag_based_data_type_processor_unittest.cc
index fff503f4..2f31c23 100644
--- a/components/sync/model/client_tag_based_data_type_processor_unittest.cc
+++ b/components/sync/model/client_tag_based_data_type_processor_unittest.cc
@@ -2941,6 +2941,47 @@
       /*expected_count=*/2);
 }
 
+TEST_F(ClientTagBasedDataTypeProcessorTest, ShouldResetForMissingStorageKey) {
+  base::HistogramTester histogram_tester;
+
+  const syncer::ClientTagHash kClientTagHash1 =
+      ClientTagHash::FromUnhashed(GetDataType(), "tag1");
+  const syncer::ClientTagHash kClientTagHash2 =
+      ClientTagHash::FromUnhashed(GetDataType(), "tag2");
+  const syncer::ClientTagHash kClientTagHash3 =
+      ClientTagHash::FromUnhashed(GetDataType(), "tag3");
+  sync_pb::EntityMetadata entity_metadata1;
+  entity_metadata1.set_client_tag_hash(kClientTagHash1.value());
+  entity_metadata1.set_creation_time(0);
+  sync_pb::EntityMetadata entity_metadata2;
+  entity_metadata2.set_client_tag_hash(kClientTagHash2.value());
+  entity_metadata2.set_creation_time(0);
+  sync_pb::EntityMetadata entity_metadata3;
+  entity_metadata3.set_client_tag_hash(kClientTagHash3.value());
+  entity_metadata3.set_creation_time(0);
+
+  db()->PutMetadata(kKey1, std::move(entity_metadata1));
+  // One of the storage keys is empty!
+  db()->PutMetadata("", std::move(entity_metadata2));
+  db()->PutMetadata(kKey3, std::move(entity_metadata3));
+
+  InitializeToReadyState();
+
+  // With a missing storage key, metadata should have been cleared.
+  EXPECT_EQ(0U, db()->metadata_count());
+  EXPECT_EQ(0U, ProcessorEntityCount());
+  EXPECT_FALSE(type_processor()->IsTrackingMetadata());
+
+  histogram_tester.ExpectUniqueSample(
+      "Sync.ClearMetadataDueToEmptyStorageKey",
+      /*sample=*/DataTypeHistogramValue(GetDataType()),
+      /*expected_bucket_count=*/1);
+
+  // Initial update.
+  worker()->UpdateFromServer();
+  EXPECT_TRUE(type_processor()->IsTrackingMetadata());
+}
+
 TEST_F(ClientTagBasedDataTypeProcessorTest,
        ShouldNotProcessInvalidRemoteIncrementalUpdate) {
   // To ensure the update is not ignored because of empty storage key.
diff --git a/components/sync/model/data_type_sync_bridge.cc b/components/sync/model/data_type_sync_bridge.cc
index 1c99f88..83445f494 100644
--- a/components/sync/model/data_type_sync_bridge.cc
+++ b/components/sync/model/data_type_sync_bridge.cc
@@ -92,11 +92,6 @@
   return sync_pb::EntitySpecifics();
 }
 
-bool DataTypeSyncBridge::IsEntityDataValid(
-    const EntityData& entity_data) const {
-  return true;
-}
-
 DataTypeLocalChangeProcessor* DataTypeSyncBridge::change_processor() {
   return change_processor_.get();
 }
diff --git a/components/sync/model/data_type_sync_bridge.h b/components/sync/model/data_type_sync_bridge.h
index 52ce205..9fae7b8 100644
--- a/components/sync/model/data_type_sync_bridge.h
+++ b/components/sync/model/data_type_sync_bridge.h
@@ -249,9 +249,7 @@
   // Returns true if the provided `entity_data` is valid. This method should be
   // implemented by the bridges and can be used to validate the incoming remote
   // updates.
-  // TODO(crbug.com/40677711): Mark this method as pure virtual to force all the
-  // bridges to implement this.
-  virtual bool IsEntityDataValid(const EntityData& entity_data) const;
+  virtual bool IsEntityDataValid(const EntityData& entity_data) const = 0;
 
   // Needs to be informed about any model change occurring via Delete() and
   // Put(). The changing metadata should be stored to persistent storage
diff --git a/components/sync/model/entity_change.h b/components/sync/model/entity_change.h
index 442772cb..bbb9dab3 100644
--- a/components/sync/model/entity_change.h
+++ b/components/sync/model/entity_change.h
@@ -17,6 +17,8 @@
  public:
   enum ChangeType { ACTION_ADD, ACTION_UPDATE, ACTION_DELETE };
 
+  // Note: `storage_key` may be empty, for data types where
+  // DataTypeSyncBridge::SupportsGetStorageKey() returns false.
   static std::unique_ptr<EntityChange> CreateAdd(const std::string& storage_key,
                                                  EntityData data);
   static std::unique_ptr<EntityChange> CreateUpdate(
diff --git a/components/sync/model/processor_entity.cc b/components/sync/model/processor_entity.cc
index 238a0607..c9467e4b 100644
--- a/components/sync/model/processor_entity.cc
+++ b/components/sync/model/processor_entity.cc
@@ -28,7 +28,7 @@
 namespace {
 
 bool MetadataIsValid(const sync_pb::EntityMetadata& metadata) {
-  return metadata.has_client_tag_hash() && metadata.has_creation_time() &&
+  return !metadata.client_tag_hash().empty() && metadata.has_creation_time() &&
          metadata.sequence_number() >= metadata.acked_sequence_number();
 }
 
diff --git a/components/sync/service/history_sync_session_durations_metrics_recorder.cc b/components/sync/service/history_sync_session_durations_metrics_recorder.cc
index e6193cc..4e9f8350 100644
--- a/components/sync/service/history_sync_session_durations_metrics_recorder.cc
+++ b/components/sync/service/history_sync_session_durations_metrics_recorder.cc
@@ -106,6 +106,13 @@
   history_sync_status_ = new_history_sync_status;
 }
 
+void HistorySyncSessionDurationsMetricsRecorder::OnSyncShutdown(
+    SyncService* sync) {
+  // Unreachable, since the service owning this instance is Shutdown() before
+  // the SyncService.
+  NOTREACHED();
+}
+
 HistorySyncSessionDurationsMetricsRecorder::HistorySyncStatus
 HistorySyncSessionDurationsMetricsRecorder::DetermineHistorySyncStatus() const {
   if (!sync_service_ ||
diff --git a/components/sync/service/history_sync_session_durations_metrics_recorder.h b/components/sync/service/history_sync_session_durations_metrics_recorder.h
index 5e45f22..c229c498 100644
--- a/components/sync/service/history_sync_session_durations_metrics_recorder.h
+++ b/components/sync/service/history_sync_session_durations_metrics_recorder.h
@@ -38,6 +38,7 @@
 
   // syncer::SyncServiceObserver:
   void OnStateChanged(SyncService* sync) override;
+  void OnSyncShutdown(SyncService* sync) override;
 
  private:
   enum class HistorySyncStatus {
diff --git a/components/sync/service/sync_session_durations_metrics_recorder.cc b/components/sync/service/sync_session_durations_metrics_recorder.cc
index a7632cff..8f1cbf7 100644
--- a/components/sync/service/sync_session_durations_metrics_recorder.cc
+++ b/components/sync/service/sync_session_durations_metrics_recorder.cc
@@ -182,6 +182,12 @@
   HandleSyncAndAccountChange();
 }
 
+void SyncSessionDurationsMetricsRecorder::OnSyncShutdown(SyncService* sync) {
+  // Unreachable, since the service owning this instance is Shutdown() before
+  // the SyncService.
+  NOTREACHED();
+}
+
 void SyncSessionDurationsMetricsRecorder::OnPrimaryAccountChanged(
     const signin::PrimaryAccountChangeEvent& event) {
   DVLOG(1) << __func__;
diff --git a/components/sync/service/sync_session_durations_metrics_recorder.h b/components/sync/service/sync_session_durations_metrics_recorder.h
index 95b811d..67c1afc 100644
--- a/components/sync/service/sync_session_durations_metrics_recorder.h
+++ b/components/sync/service/sync_session_durations_metrics_recorder.h
@@ -54,6 +54,7 @@
 
   // syncer::SyncServiceObserver:
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
   // IdentityManager::Observer:
   void OnPrimaryAccountChanged(
diff --git a/components/sync/test/fake_data_type_sync_bridge.cc b/components/sync/test/fake_data_type_sync_bridge.cc
index 029ba0b..5c52e48 100644
--- a/components/sync/test/fake_data_type_sync_bridge.cc
+++ b/components/sync/test/fake_data_type_sync_bridge.cc
@@ -58,7 +58,8 @@
   }
 
   void ClearMetadata(const std::string& storage_key) override {
-    DCHECK(!storage_key.empty());
+    // Note: `storage_key` usually shouldn't be empty here, but some tests
+    // exercise invalid-data scenarios where it is empty.
     db_->RemoveMetadata(storage_key);
   }
 
diff --git a/components/sync_preferences/common_syncable_prefs_database.cc b/components/sync_preferences/common_syncable_prefs_database.cc
index 3f92c7f..77ffbdc36 100644
--- a/components/sync_preferences/common_syncable_prefs_database.cc
+++ b/components/sync_preferences/common_syncable_prefs_database.cc
@@ -144,6 +144,7 @@
   kAutofillNameAndEmailProfileNotSelectedCounter = 94,
   kAutofillAiLastVersionDeduped = 96,
   kCrossDeviceOmniboxIsInBottomPosition = 97,
+  kAutofillWasNameAndEmailProfileUsed = 98,
   // See components/sync_preferences/README.md about adding new entries here.
   // vvvvv IMPORTANT! vvvvv
   // Note to the reviewer: IT IS YOUR RESPONSIBILITY to ensure that new syncable
@@ -182,6 +183,11 @@
           syncer::PRIORITY_PREFERENCES,
           PrefSensitivity::kExemptFromUserControlWhileSignedIn,
           MergeBehavior::kNone}},
+        {autofill::prefs::kAutofillWasNameAndEmailProfileUsed,
+         {syncable_prefs_ids::kAutofillWasNameAndEmailProfileUsed,
+          syncer::PRIORITY_PREFERENCES,
+          PrefSensitivity::kExemptFromUserControlWhileSignedIn,
+          MergeBehavior::kNone}},
         {bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
          {syncable_prefs_ids::kShowAppsShortcutInBookmarkBar,
           syncer::PREFERENCES, PrefSensitivity::kNone, MergeBehavior::kNone}},
diff --git a/components/sync_sessions/local_session_event_handler_impl.cc b/components/sync_sessions/local_session_event_handler_impl.cc
index 4488bf54..a216045 100644
--- a/components/sync_sessions/local_session_event_handler_impl.cc
+++ b/components/sync_sessions/local_session_event_handler_impl.cc
@@ -19,6 +19,7 @@
 #include "components/sync/base/time.h"
 #include "components/sync/protocol/session_specifics.pb.h"
 #include "components/sync/protocol/sync_enums.pb.h"
+#include "components/sync_sessions/session_store.h"
 #include "components/sync_sessions/sync_sessions_client.h"
 #include "components/sync_sessions/synced_session_tracker.h"
 #include "components/sync_sessions/synced_tab_delegate.h"
@@ -315,7 +316,18 @@
   auto specifics = std::make_unique<sync_pb::SessionSpecifics>();
   specifics->set_session_tag(current_session_tag_);
   current_session->ToSessionHeaderProto().Swap(specifics->mutable_header());
-  batch->Put(std::move(specifics));
+
+  // TODO(crbug.com/408182457): Some reports indicate that `specifics` is
+  // occasionally invalid here. In that case, avoid sending it to the sync
+  // machinery to avoid CHECK failures.
+  std::optional<SessionStore::SpecificsInvalidReason> invalid_reason =
+      SessionStore::GetSpecificsInvalidReason(*specifics);
+  if (!invalid_reason.has_value()) {
+    batch->Put(std::move(specifics));
+  } else {
+    base::UmaHistogramEnumeration("Sync.InvalidSessionHeader.AssociateWindows",
+                                  *invalid_reason);
+  }
 
   if (is_session_restore) {
     UmaHistogramMediumTimes("Sync.AssociateWindowsTime.OnSessionRestore",
diff --git a/components/sync_sessions/session_store.cc b/components/sync_sessions/session_store.cc
index fc12a0cf..e6a02c6 100644
--- a/components/sync_sessions/session_store.cc
+++ b/components/sync_sessions/session_store.cc
@@ -7,6 +7,7 @@
 #include <stdint.h>
 
 #include <algorithm>
+#include <optional>
 #include <set>
 #include <utility>
 
@@ -255,14 +256,35 @@
 }
 
 // static
-bool SessionStore::AreValidSpecifics(const SessionSpecifics& specifics) {
+std::optional<SessionStore::SpecificsInvalidReason>
+SessionStore::GetSpecificsInvalidReason(
+    const sync_pb::SessionSpecifics& specifics) {
+  // A session tag is always required.
   if (specifics.session_tag().empty()) {
-    return false;
+    return SpecificsInvalidReason::kMissingSessionTag;
   }
+
+  // Only one of header or tab may be set.
+  if (specifics.has_header() && specifics.has_tab()) {
+    return SpecificsInvalidReason::kBothHeaderAndTab;
+  }
+
+  // Tabs must have both a valid tab ID and tab node ID.
   if (specifics.has_tab()) {
-    return specifics.tab_node_id() >= 0 && specifics.tab().tab_id() > 0;
+    if (specifics.tab_node_id() < 0) {
+      return SpecificsInvalidReason::kTabBadTabNodeId;
+    }
+    if (specifics.tab().tab_id() <= 0) {
+      return SpecificsInvalidReason::kTabBadTabId;
+    }
+    return std::nullopt;
   }
+
   if (specifics.has_header()) {
+    // A header entity must not have a tab node ID.
+    if (specifics.tab_node_id() != TabNodePool::kInvalidTabNodeID) {
+      return SpecificsInvalidReason::kHeaderWithTabNodeId;
+    }
     // Verify that tab IDs appear only once within a header. Intended to prevent
     // http://crbug.com/360822.
     std::set<int> session_tab_ids;
@@ -270,14 +292,20 @@
       for (int tab_id : window.tab()) {
         bool success = session_tab_ids.insert(tab_id).second;
         if (!success) {
-          return false;
+          return SpecificsInvalidReason::kHeaderWithDuplicateTabIds;
         }
       }
     }
-    return !specifics.has_tab() &&
-           specifics.tab_node_id() == TabNodePool::kInvalidTabNodeID;
+    return std::nullopt;
   }
-  return false;
+
+  // Neither header nor tab is set.
+  return SpecificsInvalidReason::kNeitherHeaderNorTab;
+}
+
+// static
+bool SessionStore::AreValidSpecifics(const SessionSpecifics& specifics) {
+  return !GetSpecificsInvalidReason(specifics).has_value();
 }
 
 // static
diff --git a/components/sync_sessions/session_store.h b/components/sync_sessions/session_store.h
index 852e129e0..81a63ed 100644
--- a/components/sync_sessions/session_store.h
+++ b/components/sync_sessions/session_store.h
@@ -52,6 +52,24 @@
                    SyncSessionsClient* sessions_client,
                    OpenCallback callback);
 
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  // LINT.IfChange(SessionSpecificsInvalidReason)
+  enum class SpecificsInvalidReason {
+    kMissingSessionTag = 0,
+    kBothHeaderAndTab = 1,
+    kNeitherHeaderNorTab = 2,
+    kTabBadTabNodeId = 3,
+    kTabBadTabId = 4,
+    kHeaderWithDuplicateTabIds = 5,
+    kHeaderWithTabNodeId = 6,
+    kMaxValue = kHeaderWithTabNodeId
+  };
+  // LINT.ThenChange(/tools/metrics/histograms/metadata/sync/enums.xml:SessionSpecificsInvalidReason)
+
+  // Returns the reason the given `specifics` is invalid, or nullopt if valid.
+  static std::optional<SpecificsInvalidReason> GetSpecificsInvalidReason(
+      const sync_pb::SessionSpecifics& specifics);
   // Verifies whether a proto is malformed (e.g. required fields are missing).
   static bool AreValidSpecifics(const sync_pb::SessionSpecifics& specifics);
   // |specifics| must be valid, see AreValidSpecifics().
diff --git a/components/sync_user_events/user_event_data_type_controller.cc b/components/sync_user_events/user_event_data_type_controller.cc
index db48d06..fd47899c 100644
--- a/components/sync_user_events/user_event_data_type_controller.cc
+++ b/components/sync_user_events/user_event_data_type_controller.cc
@@ -48,4 +48,8 @@
   sync->DataTypePreconditionChanged(type());
 }
 
+void UserEventDataTypeController::OnSyncShutdown(syncer::SyncService* sync) {
+  // Nothing to be done, `this` will be destructed imminently.
+}
+
 }  // namespace syncer
diff --git a/components/sync_user_events/user_event_data_type_controller.h b/components/sync_user_events/user_event_data_type_controller.h
index 9090c13..c8494225 100644
--- a/components/sync_user_events/user_event_data_type_controller.h
+++ b/components/sync_user_events/user_event_data_type_controller.h
@@ -37,6 +37,7 @@
 
   // syncer::SyncServiceObserver implementation.
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
  private:
   const raw_ptr<SyncService> sync_service_;
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 088288f1..f0a0d28c 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -83,6 +83,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_widget_host_iterator.h"
 #include "content/public/common/content_client.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "third_party/blink/public/mojom/input/input_handler.mojom.h"
 #include "third_party/perfetto/include/perfetto/tracing/track.h"
@@ -488,7 +489,18 @@
           // left in a rotation throttle and ending it here.
           end_rotation = true;
         } else {
+          // A standalone non-rotation resize has occurred. For WebView, we sync
+          // immediately. For the browser, schedule a vsync-aligned update to
+          // process it smoothly.
           sync_needed = true;
+          if (base::FeatureList::IsEnabled(features::kFluidResize) &&
+              rwhva_->using_browser_compositor_) {
+            sync_needed = false;
+            if (!rwhva_->visual_properties_update_pending_) {
+              rwhva_->visual_properties_update_pending_ = true;
+              rwhva_->SetNeedsAnimate();
+            }
+          }
         }
       }
     }
@@ -819,15 +831,18 @@
     const std::optional<viz::LocalSurfaceId>& child_local_surface_id,
     bool reuse_current_local_surface_id,
     bool ignore_ack) {
-    // Always merge the child_id, even if we cannot sync at this time.
-    if (child_local_surface_id)
-      local_surface_id_allocator_.UpdateFromChild(*child_local_surface_id);
+  // Always merge the child_id, even if we cannot sync at this time.
+  if (child_local_surface_id) {
+    local_surface_id_allocator_.UpdateFromChild(*child_local_surface_id);
+  }
 
-    if (!CanSynchronizeVisualProperties())
-      return false;
+  if (!CanSynchronizeVisualProperties()) {
+    return false;
+  }
 
-    if (!child_local_surface_id && !reuse_current_local_surface_id)
-      local_surface_id_allocator_.GenerateId();
+  if (!child_local_surface_id && !reuse_current_local_surface_id) {
+    local_surface_id_allocator_.GenerateId();
+  }
 
   // If we still have an invalid viz::LocalSurfaceId, then we are hidden and
   // evicted. This will have been triggered by a child acknowledging a previous
@@ -2001,6 +2016,21 @@
 }
 
 void RenderWidgetHostViewAndroid::SetNeedsAnimate() {
+  if (base::FeatureList::IsEnabled(features::kFluidResize)) {
+    // The synchronous (WebView) compositor does not have a proper browser
+    // compositor with which to drive animations.
+    CHECK(using_browser_compositor_);
+
+    // No-op if we are not attached to a window, as we are not visible. Visual
+    // properties will be synchronized when the view is shown.
+    if (observing_root_window_) {
+      if (auto* compositor = view_.GetWindowAndroid()->GetCompositor()) {
+        compositor->SetNeedsAnimate();
+      }
+    }
+    return;
+  }
+
   DCHECK(view_.GetWindowAndroid());
   DCHECK(using_browser_compositor_);
   view_.GetWindowAndroid()->SetNeedsAnimate();
@@ -2365,6 +2395,16 @@
   // an OOPIF client.
   if (touch_selection_controller_)
     needs_animate |= touch_selection_controller_->Animate(frame_time);
+
+  if (visual_properties_update_pending_ &&
+      base::FeatureList::IsEnabled(features::kFluidResize)) {
+    visual_properties_update_pending_ = false;
+    // Use a short deadline for fluid resizing. We don't want to block the
+    // UI thread, but we want the update to happen quickly.
+    SynchronizeVisualProperties(cc::DeadlinePolicy::UseDefaultDeadline(),
+                                std::nullopt);
+  }
+
   return needs_animate;
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index d2962827..b4420c83 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -529,6 +529,8 @@
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
                            GestureManagerListensToChildFrames);
   FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAndroidTest, DisplayFeature);
+  FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAndroidFluidResizeBrowserTest,
+                           ResizeDefersSynchronizationToNextFrame);
 
   class ScreenStateChangeHandler {
    public:
@@ -829,6 +831,10 @@
 
   std::optional<base::flat_set<ui::DomCode>> locked_keyboard_keys_;
 
+  // Used to schedule a single visual properties update on the next vsync tick
+  // to achieve fluid resizing.
+  bool visual_properties_update_pending_ = false;
+
   base::WeakPtrFactory<RenderWidgetHostViewAndroid> weak_ptr_factory_{this};
 };
 
diff --git a/content/browser/renderer_host/render_widget_host_view_android_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_android_browsertest.cc
index 0d21937..2bb5137 100644
--- a/content/browser/renderer_host/render_widget_host_view_android_browsertest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android_browsertest.cc
@@ -4,12 +4,14 @@
 
 #include "content/browser/renderer_host/render_widget_host_view_android.h"
 
+#include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_timeouts.h"
 #include "base/test/test_trace_processor.h"
 #include "components/input/features.h"
 #include "components/input/utils.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
@@ -142,4 +144,79 @@
   EXPECT_EQ(slice_count, expected_count);
 }
 
+class RenderWidgetHostViewAndroidFluidResizeBrowserTest
+    : public RenderWidgetHostViewAndroidBrowserTest {
+ public:
+  RenderWidgetHostViewAndroidFluidResizeBrowserTest() {
+    feature_list_.InitAndEnableFeature(features::kFluidResize);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewAndroidFluidResizeBrowserTest,
+                       ResizeDefersSynchronizationToNextFrame) {
+  RenderFrameSubmissionObserver render_frame_submission_observer(
+      shell()->web_contents());
+  EXPECT_TRUE(NavigateToURL(
+      shell(), GURL("data:text/html,<!doctype html>"
+                    "<body style='background-color: magenta;'></body>")));
+  if (render_frame_submission_observer.render_frame_count() == 0) {
+    render_frame_submission_observer.WaitForAnyFrameSubmission();
+  }
+
+  auto* view = static_cast<RenderWidgetHostViewAndroid*>(
+      shell()->web_contents()->GetRenderWidgetHostView());
+  ASSERT_NE(view, nullptr);
+  // A single call to Animate() may not be sufficient to settle visual
+  // properties. Animate() triggers a synchronization with the renderer, and the
+  // renderer's response can, in turn, trigger further visual property changes
+  // in the browser, re-setting `visual_properties_update_pending_`. The loop
+  // ensures this cycle is complete and the state is stable before the test
+  // proceeds. On each iteration, it waits for a new frame and validates that
+  // one was produced to ensure progress is being made.
+  auto last_rfm = render_frame_submission_observer.LastRenderFrameMetadata();
+  while (view->visual_properties_update_pending_) {
+    view->Animate(base::TimeTicks::Now());
+    render_frame_submission_observer.WaitForAnyFrameSubmission();
+    if (view->visual_properties_update_pending_) {
+      CHECK(last_rfm.local_surface_id !=
+            render_frame_submission_observer.LastRenderFrameMetadata()
+                .local_surface_id);
+      last_rfm = render_frame_submission_observer.LastRenderFrameMetadata();
+    }
+  }
+
+  EXPECT_FALSE(view->visual_properties_update_pending_);
+
+  const gfx::Size current_size_dip = view->GetViewBounds().size();
+  const float scale = view->GetDeviceScaleFactor();
+  const gfx::Size current_size_px =
+      gfx::ScaleToCeiledSize(current_size_dip, scale);
+  const gfx::Size new_size(current_size_px.width() / 2,
+                           current_size_px.height() / 2);
+  // Ensure we're actually resizing.
+  ASSERT_NE(new_size, current_size_px);
+  view->screen_state_change_handler_.OnPhysicalBackingSizeChanged(new_size, 0);
+
+  if (view->using_browser_compositor_) {
+    EXPECT_TRUE(view->visual_properties_update_pending_);
+    // Confirmed visual properties update is pending. We now wait for the
+    // renderer to submit a frame acknowledging the resize.
+    RenderFrameSubmissionObserver frame_observer(shell()->web_contents());
+    frame_observer.WaitForAnyFrameSubmission();
+
+    // The renderer has submitted a frame, but the browser's animation tick
+    // that clears the pending flag might not have run yet. To avoid a race
+    // condition, we manually call Animate() to process the update.
+    view->Animate(base::TimeTicks::Now());
+
+    EXPECT_FALSE(view->visual_properties_update_pending_);
+  } else {
+    // Confirmed no pending visual properties update.
+    EXPECT_FALSE(view->visual_properties_update_pending_);
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/webrtc/resources/webrtc_internals.js b/content/browser/webrtc/resources/webrtc_internals.js
index 21cdea47df..02c3ddf 100644
--- a/content/browser/webrtc/resources/webrtc_internals.js
+++ b/content/browser/webrtc/resources/webrtc_internals.js
@@ -371,11 +371,13 @@
   // Create a map from the stats entries so it behaves like a getStats maplike
   // and then sort it.
   const stats = sortStatsReport(new Map(data.reports));
+
+  // This augments stats with [delta] values.
+  statsRatesCalculator.addStatsReport(stats);
   stats.forEach(report => {
     statsTable.addStatsReport(peerConnectionElement, report);
     drawSingleReport(peerConnectionElement, report);
   });
-  statsRatesCalculator.addStatsReport(stats);
 
   let ids = [];
   stats.forEach(report => {
diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h
index 7864b80..c5643723 100644
--- a/content/public/renderer/render_frame.h
+++ b/content/public/renderer/render_frame.h
@@ -11,6 +11,7 @@
 #include <string>
 #include <string_view>
 
+#include "base/byte_count.h"
 #include "base/supports_user_data.h"
 #include "base/task/single_thread_task_runner.h"
 #include "content/common/buildflags.h"
@@ -22,6 +23,8 @@
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 #include "third_party/blink/public/mojom/frame/triggering_event_info.mojom-shared.h"
 #include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/public/platform/web_url_response.h"
 #include "third_party/blink/public/web/web_navigation_policy.h"
 #include "ui/accessibility/ax_mode.h"
 #include "ui/accessibility/ax_tree_update.h"
@@ -231,6 +234,15 @@
       base::RepeatingCallback<void(const blink::SubresourceLoadMetrics&)>;
   virtual void SetSubresourceLoadCallback(SubresourceLoadCallback callback) = 0;
 
+  using LoadFromMemoryCacheCallback =
+      base::RepeatingCallback<void(const GURL& response_url,
+                                   int request_id,
+                                   base::ByteCount encoded_body_length,
+                                   const std::string& mime_type,
+                                   bool from_archive)>;
+  virtual void SetLoadFromMemoryCacheCallback(
+      LoadFromMemoryCacheCallback callback) = 0;
+
  protected:
   ~RenderFrame() override {}
 
diff --git a/content/public/renderer/render_frame_observer.h b/content/public/renderer/render_frame_observer.h
index 15e31f9..88a4c31 100644
--- a/content/public/renderer/render_frame_observer.h
+++ b/content/public/renderer/render_frame_observer.h
@@ -287,6 +287,10 @@
   // Reports that a resource was loaded from the blink memory cache.
   // |request_id| uniquely identifies this resource within this render frame.
   // |from_archive| indicates if the resource originated from a MHTML archive.
+  //
+  // TODO(crbug.com/404425954): `DidLoadResourceFromMemoryCache()` is going to
+  // be deprecated. Use `SetLoadFromMemoryCacheCallback()` in `RenderFrame`
+  // instead.
   virtual void DidLoadResourceFromMemoryCache(
       const GURL& response_url,
       int request_id,
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index bb432f9..b581032 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -4529,11 +4529,18 @@
 void RenderFrameImpl::DidLoadResourceFromMemoryCache(
     const blink::WebURLRequest& request,
     const blink::WebURLResponse& response) {
-  for (auto& observer : observers_) {
-    observer.DidLoadResourceFromMemoryCache(
+  if (load_from_memory_cache_callback_) {
+    load_from_memory_cache_callback_.Run(
         request.Url(), response.RequestId(),
         base::ByteCount(response.EncodedBodyLength()),
         response.MimeType().Utf8(), response.FromArchive());
+  } else {
+    for (auto& observer : observers_) {
+      observer.DidLoadResourceFromMemoryCache(
+          request.Url(), response.RequestId(),
+          base::ByteCount(response.EncodedBodyLength()),
+          response.MimeType().Utf8(), response.FromArchive());
+    }
   }
 }
 
@@ -4626,6 +4633,11 @@
   subresource_load_callback_ = std::move(callback);
 }
 
+void RenderFrameImpl::SetLoadFromMemoryCacheCallback(
+    LoadFromMemoryCacheCallback callback) {
+  load_from_memory_cache_callback_ = std::move(callback);
+}
+
 void RenderFrameImpl::DidObserveNewFeatureUsage(
     const blink::UseCounterFeature& feature) {
   TRACE_EVENT_WITH_FLOW0("navigation",
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 51e0fbd1..29c0e17 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -777,6 +777,8 @@
 
   void SetNewFeatureUsageCallback(NewFeatureUsageCallback callback) override;
   void SetSubresourceLoadCallback(SubresourceLoadCallback callback) override;
+  void SetLoadFromMemoryCacheCallback(
+      LoadFromMemoryCacheCallback callback) override;
 
  protected:
   explicit RenderFrameImpl(CreateParams params);
@@ -1251,10 +1253,12 @@
   // The callback to send the feature usage to the browser process through
   // PageLoadMetrics.
   NewFeatureUsageCallback new_feature_usage_callback_;
-
-  // The callback to send the feature usage to the browser process through
-  // PageLoadMetrics.
+  // The callback to send the loaded subresource info to the browser process
+  // through PageLoadMetrics.
   SubresourceLoadCallback subresource_load_callback_;
+  // The callback to send the loaded resource info from memory cache to the
+  // browser process through PageLoadMetrics.
+  LoadFromMemoryCacheCallback load_from_memory_cache_callback_;
 
   // The text selection the last time DidChangeSelection got called. May contain
   // additional characters before and after the selected text, for IMEs. The
diff --git a/infra/config/generated/builders/ci/tvos-rel-fyi/properties.json b/infra/config/generated/builders/ci/tvos-rel-fyi/properties.json
index a7c37383..8f96a124 100644
--- a/infra/config/generated/builders/ci/tvos-rel-fyi/properties.json
+++ b/infra/config/generated/builders/ci/tvos-rel-fyi/properties.json
@@ -69,5 +69,5 @@
   },
   "builder_group": "chromium.fyi",
   "recipe": "chromium",
-  "xcode_build_version": "16f6"
+  "xcode_build_version": "17a324"
 }
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/tvos-rel-fyi/targets/chromium.fyi.json b/infra/config/generated/builders/ci/tvos-rel-fyi/targets/chromium.fyi.json
index e8357a1..5feccb9 100644
--- a/infra/config/generated/builders/ci/tvos-rel-fyi/targets/chromium.fyi.json
+++ b/infra/config/generated/builders/ci/tvos-rel-fyi/targets/chromium.fyi.json
@@ -16,11 +16,11 @@
           "--platform",
           "Apple TV 4K (3rd generation)",
           "--version",
-          "18.5",
+          "26.0",
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "16f6",
+          "17a324",
           "--xctest"
         ],
         "merge": {
@@ -28,7 +28,7 @@
         },
         "module_name": "//base:base_unittests",
         "module_scheme": "gtest",
-        "name": "base_unittests Apple TV 4K (3rd generation) 18.5",
+        "name": "base_unittests Apple TV 4K (3rd generation) 26.0",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -47,11 +47,11 @@
           },
           "named_caches": [
             {
-              "name": "runtime_tvos_18_5",
-              "path": "Runtime-tvos-18.5"
+              "name": "runtime_tvos_26_0",
+              "path": "Runtime-tvos-26.0"
             },
             {
-              "name": "xcode_ios_16f6",
+              "name": "xcode_ios_17a324",
               "path": "Xcode.app"
             }
           ],
@@ -59,7 +59,7 @@
         },
         "test": "base_unittests",
         "test_id_prefix": "ninja://base:base_unittests/",
-        "variant_id": "Apple TV 4K (3rd generation) 18.5"
+        "variant_id": "Apple TV 4K (3rd generation) 26.0"
       },
       {
         "args": [
@@ -68,11 +68,11 @@
           "--platform",
           "Apple TV 4K (3rd generation)",
           "--version",
-          "18.5",
+          "26.0",
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "16f6",
+          "17a324",
           "--xctest"
         ],
         "merge": {
@@ -80,7 +80,7 @@
         },
         "module_name": "//components:components_browsertests",
         "module_scheme": "gtest",
-        "name": "components_browsertests Apple TV 4K (3rd generation) 18.5",
+        "name": "components_browsertests Apple TV 4K (3rd generation) 26.0",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -99,11 +99,11 @@
           },
           "named_caches": [
             {
-              "name": "runtime_tvos_18_5",
-              "path": "Runtime-tvos-18.5"
+              "name": "runtime_tvos_26_0",
+              "path": "Runtime-tvos-26.0"
             },
             {
-              "name": "xcode_ios_16f6",
+              "name": "xcode_ios_17a324",
               "path": "Xcode.app"
             }
           ],
@@ -111,7 +111,7 @@
         },
         "test": "components_browsertests",
         "test_id_prefix": "ninja://components:components_browsertests/",
-        "variant_id": "Apple TV 4K (3rd generation) 18.5"
+        "variant_id": "Apple TV 4K (3rd generation) 26.0"
       },
       {
         "args": [
@@ -120,11 +120,11 @@
           "--platform",
           "Apple TV 4K (3rd generation)",
           "--version",
-          "18.5",
+          "26.0",
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "16f6",
+          "17a324",
           "--xctest"
         ],
         "merge": {
@@ -132,7 +132,7 @@
         },
         "module_name": "//components:components_unittests",
         "module_scheme": "gtest",
-        "name": "components_unittests Apple TV 4K (3rd generation) 18.5",
+        "name": "components_unittests Apple TV 4K (3rd generation) 26.0",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -151,11 +151,11 @@
           },
           "named_caches": [
             {
-              "name": "runtime_tvos_18_5",
-              "path": "Runtime-tvos-18.5"
+              "name": "runtime_tvos_26_0",
+              "path": "Runtime-tvos-26.0"
             },
             {
-              "name": "xcode_ios_16f6",
+              "name": "xcode_ios_17a324",
               "path": "Xcode.app"
             }
           ],
@@ -163,7 +163,7 @@
         },
         "test": "components_unittests",
         "test_id_prefix": "ninja://components:components_unittests/",
-        "variant_id": "Apple TV 4K (3rd generation) 18.5"
+        "variant_id": "Apple TV 4K (3rd generation) 26.0"
       },
       {
         "args": [
@@ -172,11 +172,11 @@
           "--platform",
           "Apple TV 4K (3rd generation)",
           "--version",
-          "18.5",
+          "26.0",
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "16f6",
+          "17a324",
           "--xctest"
         ],
         "merge": {
@@ -184,7 +184,7 @@
         },
         "module_name": "//content/test:content_unittests",
         "module_scheme": "gtest",
-        "name": "content_unittests Apple TV 4K (3rd generation) 18.5",
+        "name": "content_unittests Apple TV 4K (3rd generation) 26.0",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -203,11 +203,11 @@
           },
           "named_caches": [
             {
-              "name": "runtime_tvos_18_5",
-              "path": "Runtime-tvos-18.5"
+              "name": "runtime_tvos_26_0",
+              "path": "Runtime-tvos-26.0"
             },
             {
-              "name": "xcode_ios_16f6",
+              "name": "xcode_ios_17a324",
               "path": "Xcode.app"
             }
           ],
@@ -215,7 +215,7 @@
         },
         "test": "content_unittests",
         "test_id_prefix": "ninja://content/test:content_unittests/",
-        "variant_id": "Apple TV 4K (3rd generation) 18.5"
+        "variant_id": "Apple TV 4K (3rd generation) 26.0"
       },
       {
         "args": [
@@ -224,11 +224,11 @@
           "--platform",
           "Apple TV 4K (3rd generation)",
           "--version",
-          "18.5",
+          "26.0",
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "16f6",
+          "17a324",
           "--xctest"
         ],
         "merge": {
@@ -236,7 +236,7 @@
         },
         "module_name": "//media:media_unittests",
         "module_scheme": "gtest",
-        "name": "media_unittests Apple TV 4K (3rd generation) 18.5",
+        "name": "media_unittests Apple TV 4K (3rd generation) 26.0",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -255,11 +255,11 @@
           },
           "named_caches": [
             {
-              "name": "runtime_tvos_18_5",
-              "path": "Runtime-tvos-18.5"
+              "name": "runtime_tvos_26_0",
+              "path": "Runtime-tvos-26.0"
             },
             {
-              "name": "xcode_ios_16f6",
+              "name": "xcode_ios_17a324",
               "path": "Xcode.app"
             }
           ],
@@ -267,7 +267,7 @@
         },
         "test": "media_unittests",
         "test_id_prefix": "ninja://media:media_unittests/",
-        "variant_id": "Apple TV 4K (3rd generation) 18.5"
+        "variant_id": "Apple TV 4K (3rd generation) 26.0"
       }
     ]
   }
diff --git a/infra/config/generated/builders/try/tvos-rel-fyi/properties.json b/infra/config/generated/builders/try/tvos-rel-fyi/properties.json
index a184c0e..fbe4183 100644
--- a/infra/config/generated/builders/try/tvos-rel-fyi/properties.json
+++ b/infra/config/generated/builders/try/tvos-rel-fyi/properties.json
@@ -62,5 +62,5 @@
   },
   "builder_group": "tryserver.chromium.mac",
   "recipe": "chromium_trybot",
-  "xcode_build_version": "16f6"
+  "xcode_build_version": "17a324"
 }
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/tvos-rel-fyi/targets/chromium.fyi.json b/infra/config/generated/builders/try/tvos-rel-fyi/targets/chromium.fyi.json
index e8357a1..5feccb9 100644
--- a/infra/config/generated/builders/try/tvos-rel-fyi/targets/chromium.fyi.json
+++ b/infra/config/generated/builders/try/tvos-rel-fyi/targets/chromium.fyi.json
@@ -16,11 +16,11 @@
           "--platform",
           "Apple TV 4K (3rd generation)",
           "--version",
-          "18.5",
+          "26.0",
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "16f6",
+          "17a324",
           "--xctest"
         ],
         "merge": {
@@ -28,7 +28,7 @@
         },
         "module_name": "//base:base_unittests",
         "module_scheme": "gtest",
-        "name": "base_unittests Apple TV 4K (3rd generation) 18.5",
+        "name": "base_unittests Apple TV 4K (3rd generation) 26.0",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -47,11 +47,11 @@
           },
           "named_caches": [
             {
-              "name": "runtime_tvos_18_5",
-              "path": "Runtime-tvos-18.5"
+              "name": "runtime_tvos_26_0",
+              "path": "Runtime-tvos-26.0"
             },
             {
-              "name": "xcode_ios_16f6",
+              "name": "xcode_ios_17a324",
               "path": "Xcode.app"
             }
           ],
@@ -59,7 +59,7 @@
         },
         "test": "base_unittests",
         "test_id_prefix": "ninja://base:base_unittests/",
-        "variant_id": "Apple TV 4K (3rd generation) 18.5"
+        "variant_id": "Apple TV 4K (3rd generation) 26.0"
       },
       {
         "args": [
@@ -68,11 +68,11 @@
           "--platform",
           "Apple TV 4K (3rd generation)",
           "--version",
-          "18.5",
+          "26.0",
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "16f6",
+          "17a324",
           "--xctest"
         ],
         "merge": {
@@ -80,7 +80,7 @@
         },
         "module_name": "//components:components_browsertests",
         "module_scheme": "gtest",
-        "name": "components_browsertests Apple TV 4K (3rd generation) 18.5",
+        "name": "components_browsertests Apple TV 4K (3rd generation) 26.0",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -99,11 +99,11 @@
           },
           "named_caches": [
             {
-              "name": "runtime_tvos_18_5",
-              "path": "Runtime-tvos-18.5"
+              "name": "runtime_tvos_26_0",
+              "path": "Runtime-tvos-26.0"
             },
             {
-              "name": "xcode_ios_16f6",
+              "name": "xcode_ios_17a324",
               "path": "Xcode.app"
             }
           ],
@@ -111,7 +111,7 @@
         },
         "test": "components_browsertests",
         "test_id_prefix": "ninja://components:components_browsertests/",
-        "variant_id": "Apple TV 4K (3rd generation) 18.5"
+        "variant_id": "Apple TV 4K (3rd generation) 26.0"
       },
       {
         "args": [
@@ -120,11 +120,11 @@
           "--platform",
           "Apple TV 4K (3rd generation)",
           "--version",
-          "18.5",
+          "26.0",
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "16f6",
+          "17a324",
           "--xctest"
         ],
         "merge": {
@@ -132,7 +132,7 @@
         },
         "module_name": "//components:components_unittests",
         "module_scheme": "gtest",
-        "name": "components_unittests Apple TV 4K (3rd generation) 18.5",
+        "name": "components_unittests Apple TV 4K (3rd generation) 26.0",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -151,11 +151,11 @@
           },
           "named_caches": [
             {
-              "name": "runtime_tvos_18_5",
-              "path": "Runtime-tvos-18.5"
+              "name": "runtime_tvos_26_0",
+              "path": "Runtime-tvos-26.0"
             },
             {
-              "name": "xcode_ios_16f6",
+              "name": "xcode_ios_17a324",
               "path": "Xcode.app"
             }
           ],
@@ -163,7 +163,7 @@
         },
         "test": "components_unittests",
         "test_id_prefix": "ninja://components:components_unittests/",
-        "variant_id": "Apple TV 4K (3rd generation) 18.5"
+        "variant_id": "Apple TV 4K (3rd generation) 26.0"
       },
       {
         "args": [
@@ -172,11 +172,11 @@
           "--platform",
           "Apple TV 4K (3rd generation)",
           "--version",
-          "18.5",
+          "26.0",
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "16f6",
+          "17a324",
           "--xctest"
         ],
         "merge": {
@@ -184,7 +184,7 @@
         },
         "module_name": "//content/test:content_unittests",
         "module_scheme": "gtest",
-        "name": "content_unittests Apple TV 4K (3rd generation) 18.5",
+        "name": "content_unittests Apple TV 4K (3rd generation) 26.0",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -203,11 +203,11 @@
           },
           "named_caches": [
             {
-              "name": "runtime_tvos_18_5",
-              "path": "Runtime-tvos-18.5"
+              "name": "runtime_tvos_26_0",
+              "path": "Runtime-tvos-26.0"
             },
             {
-              "name": "xcode_ios_16f6",
+              "name": "xcode_ios_17a324",
               "path": "Xcode.app"
             }
           ],
@@ -215,7 +215,7 @@
         },
         "test": "content_unittests",
         "test_id_prefix": "ninja://content/test:content_unittests/",
-        "variant_id": "Apple TV 4K (3rd generation) 18.5"
+        "variant_id": "Apple TV 4K (3rd generation) 26.0"
       },
       {
         "args": [
@@ -224,11 +224,11 @@
           "--platform",
           "Apple TV 4K (3rd generation)",
           "--version",
-          "18.5",
+          "26.0",
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "16f6",
+          "17a324",
           "--xctest"
         ],
         "merge": {
@@ -236,7 +236,7 @@
         },
         "module_name": "//media:media_unittests",
         "module_scheme": "gtest",
-        "name": "media_unittests Apple TV 4K (3rd generation) 18.5",
+        "name": "media_unittests Apple TV 4K (3rd generation) 26.0",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -255,11 +255,11 @@
           },
           "named_caches": [
             {
-              "name": "runtime_tvos_18_5",
-              "path": "Runtime-tvos-18.5"
+              "name": "runtime_tvos_26_0",
+              "path": "Runtime-tvos-26.0"
             },
             {
-              "name": "xcode_ios_16f6",
+              "name": "xcode_ios_17a324",
               "path": "Xcode.app"
             }
           ],
@@ -267,7 +267,7 @@
         },
         "test": "media_unittests",
         "test_id_prefix": "ninja://media:media_unittests/",
-        "variant_id": "Apple TV 4K (3rd generation) 18.5"
+        "variant_id": "Apple TV 4K (3rd generation) 26.0"
       }
     ]
   }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 0b75ff25..cdecdd6c 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -68063,8 +68063,8 @@
       priority: 35
       execution_timeout_secs: 10800
       caches {
-        name: "xcode_ios_16f6"
-        path: "xcode_ios_16f6.app"
+        name: "xcode_ios_17a324"
+        path: "xcode_ios_17a324.app"
       }
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -126987,8 +126987,8 @@
         seconds: 120
       }
       caches {
-        name: "xcode_ios_16f6"
-        path: "xcode_ios_16f6.app"
+        name: "xcode_ios_17a324"
+        path: "xcode_ios_17a324.app"
       }
       build_numbers: YES
       service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index 551ba21c..18d6b06 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -1777,7 +1777,7 @@
             "mac_default_arm64",
             "mac_toolchain",
             "out_dir_arg",
-            "xcode_16_beta",
+            "xcode_26_beta",
             "xctest",
         ],
     ),
@@ -1789,7 +1789,7 @@
     ),
     contact_team_email = "cobalt-appletv@google.com",
     execution_timeout = 3 * time.hour,
-    xcode = xcode.x16betabots,
+    xcode = xcode.x26betabots,
 )
 
 fyi_ios_builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
index 9ff79a4e..faeaf10 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -730,7 +730,7 @@
     builderless = True,
     cpu = cpu.ARM64,
     contact_team_email = "cobalt-appletv@google.com",
-    xcode = xcode.x16betabots,
+    xcode = xcode.x26betabots,
 )
 
 ios_builder(
diff --git a/infra/config/targets/bundles.star b/infra/config/targets/bundles.star
index 569fd39..9bc779c 100644
--- a/infra/config/targets/bundles.star
+++ b/infra/config/targets/bundles.star
@@ -6849,7 +6849,7 @@
         targets.bundle(
             targets = "tvos_tests",
             variants = [
-                "SIM_APPLE_TV_4K_3RD_GENERATION_18_5",
+                "SIM_APPLE_TV_4K_3RD_GENERATION_26_0",
             ],
         ),
     ],
diff --git a/infra/config/targets/mixins.star b/infra/config/targets/mixins.star
index 91b0cb0f..f1cf10db 100644
--- a/infra/config/targets/mixins.star
+++ b/infra/config/targets/mixins.star
@@ -1401,13 +1401,13 @@
 )
 
 targets.mixin(
-    name = "tvos_runtime_cache_18_5",
+    name = "tvos_runtime_cache_26_0",
     generate_pyl_entry = False,
     swarming = targets.swarming(
         named_caches = [
             swarming.cache(
-                name = "runtime_tvos_18_5",
-                path = "Runtime-tvos-18.5",
+                name = "runtime_tvos_26_0",
+                path = "Runtime-tvos-26.0",
             ),
         ],
     ),
diff --git a/infra/config/targets/variants.star b/infra/config/targets/variants.star
index 63d6e5b..aee276bb 100644
--- a/infra/config/targets/variants.star
+++ b/infra/config/targets/variants.star
@@ -249,17 +249,17 @@
 )
 
 targets.variant(
-    name = "SIM_APPLE_TV_4K_3RD_GENERATION_18_5",
-    identifier = "Apple TV 4K (3rd generation) 18.5",
+    name = "SIM_APPLE_TV_4K_3RD_GENERATION_26_0",
+    identifier = "Apple TV 4K (3rd generation) 26.0",
     generate_pyl_entry = False,
     mixins = [
-        "tvos_runtime_cache_18_5",
+        "tvos_runtime_cache_26_0",
     ],
     args = [
         "--platform",
         "Apple TV 4K (3rd generation)",
         "--version",
-        "18.5",
+        "26.0",
     ],
 )
 
diff --git a/internal b/internal
index 9044ebb..86a4a8a 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 9044ebbb524768005838a1dc2a8b0e7114e575eb
+Subproject commit 86a4a8afd51091e4227435859e44c9be53d99e77
diff --git a/ios/chrome/browser/authentication/account_menu/coordinator/BUILD.gn b/ios/chrome/browser/authentication/account_menu/coordinator/BUILD.gn
index 960d01d9..64b0ac1 100644
--- a/ios/chrome/browser/authentication/account_menu/coordinator/BUILD.gn
+++ b/ios/chrome/browser/authentication/account_menu/coordinator/BUILD.gn
@@ -71,8 +71,8 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "account_menu_coordinator_unittests.mm",
-    "account_menu_mediator_unittests.mm",
+    "account_menu_coordinator_unittest.mm",
+    "account_menu_mediator_unittest.mm",
   ]
   deps = [
     ":coordinator",
diff --git a/ios/chrome/browser/authentication/account_menu/coordinator/account_menu_coordinator_unittests.mm b/ios/chrome/browser/authentication/account_menu/coordinator/account_menu_coordinator_unittest.mm
similarity index 100%
rename from ios/chrome/browser/authentication/account_menu/coordinator/account_menu_coordinator_unittests.mm
rename to ios/chrome/browser/authentication/account_menu/coordinator/account_menu_coordinator_unittest.mm
diff --git a/ios/chrome/browser/authentication/account_menu/coordinator/account_menu_mediator_unittests.mm b/ios/chrome/browser/authentication/account_menu/coordinator/account_menu_mediator_unittest.mm
similarity index 100%
rename from ios/chrome/browser/authentication/account_menu/coordinator/account_menu_mediator_unittests.mm
rename to ios/chrome/browser/authentication/account_menu/coordinator/account_menu_mediator_unittest.mm
diff --git a/ios/chrome/browser/authentication/account_menu/ui/BUILD.gn b/ios/chrome/browser/authentication/account_menu/ui/BUILD.gn
index d085e5ce..5f953657 100644
--- a/ios/chrome/browser/authentication/account_menu/ui/BUILD.gn
+++ b/ios/chrome/browser/authentication/account_menu/ui/BUILD.gn
@@ -36,7 +36,7 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "account_menu_view_controller_unittests.mm" ]
+  sources = [ "account_menu_view_controller_unittest.mm" ]
   deps = [
     ":ui",
     "//base",
diff --git a/ios/chrome/browser/authentication/account_menu/ui/account_menu_view_controller_unittests.mm b/ios/chrome/browser/authentication/account_menu/ui/account_menu_view_controller_unittest.mm
similarity index 100%
rename from ios/chrome/browser/authentication/account_menu/ui/account_menu_view_controller_unittests.mm
rename to ios/chrome/browser/authentication/account_menu/ui/account_menu_view_controller_unittest.mm
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/BUILD.gn
index 3e852cb..ce58dc9 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/BUILD.gn
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/BUILD.gn
@@ -47,16 +47,27 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "add_account_signin_manager_unittest.mm" ]
+  sources = [
+    "add_account_signin_coordinator_unittest.mm",
+    "add_account_signin_manager_unittest.mm",
+  ]
   deps = [
     ":add_account_signin",
     "//base/test:test_support",
     "//components/prefs",
     "//components/prefs:test_support",
     "//components/signin/public/identity_manager:test_support",
+    "//ios/chrome/app/profile",
+    "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
+    "//ios/chrome/browser/shared/model/browser/test:test_support",
+    "//ios/chrome/browser/shared/model/profile/test",
+    "//ios/chrome/browser/signin/model:authentication_service_factory",
     "//ios/chrome/browser/signin/model:fake_system_identity",
     "//ios/chrome/browser/signin/model:fake_system_identity_manager",
+    "//ios/chrome/browser/signin/model:test_support",
+    "//ios/chrome/test:test_support",
+    "//ios/chrome/test/fakes",
     "//ios/web/common:uikit",
     "//ios/web/public/test",
     "//testing/gmock",
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator_unittest.mm b/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator_unittest.mm
new file mode 100644
index 0000000..a9e40fb
--- /dev/null
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator_unittest.mm
@@ -0,0 +1,127 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator.h"
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/app/profile/profile_state.h"
+#import "ios/chrome/browser/authentication/ui_bundled/continuation.h"
+#import "ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_manager.h"
+#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
+#import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
+#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
+#import "ios/chrome/browser/signin/model/authentication_service_factory.h"
+#import "ios/chrome/browser/signin/model/fake_authentication_service_delegate.h"
+#import "ios/chrome/test/fakes/fake_ui_view_controller.h"
+#import "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
+#import "ios/web/public/test/web_task_environment.h"
+#import "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#import "third_party/ocmock/gtest_support.h"
+
+namespace {
+
+class AddAccountSigninCoordinatorTest : public PlatformTest {
+ public:
+  AddAccountSigninCoordinatorTest() {
+    // The profile state will receive UI blocker request. They are not tested
+    // here, so it’s a non-strict mock.
+    profile_state_ = OCMClassMock([ProfileState class]);
+    scene_state_ = [[SceneState alloc] initWithAppState:nil];
+    scene_state_.profileState = profile_state_;
+    TestProfileIOS::Builder builder = TestProfileIOS::Builder();
+    builder.AddTestingFactory(
+        AuthenticationServiceFactory::GetInstance(),
+        AuthenticationServiceFactory::GetFactoryWithDelegate(
+            std::make_unique<FakeAuthenticationServiceDelegate>()));
+    profile_ = std::move(builder).Build();
+    browser_ = std::make_unique<TestBrowser>(profile_.get(), scene_state_);
+    base_view_controller_ = [[FakeUIViewController alloc] init];
+    coordinator_ = [[AddAccountSigninCoordinator alloc]
+        initWithBaseViewController:base_view_controller_
+                           browser:browser_.get()
+                      contextStyle:SigninContextStyle::kDefault
+                       accessPoint:signin_metrics::AccessPoint::kSettings
+                       promoAction:signin_metrics::PromoAction::
+                                       PROMO_ACTION_NO_SIGNIN_PROMO
+                      signinIntent:AddAccountSigninIntent::kAddAccount
+                    prefilledEmail:nil
+              continuationProvider:DoNothingContinuationProvider()];
+
+    add_account_signin_manager_mock_ =
+        OCMStrictClassMock([AddAccountSigninManager class]);
+    OCMExpect([(id)add_account_signin_manager_mock_ alloc])
+        .andReturn(add_account_signin_manager_mock_);
+    OCMExpect([[(id)add_account_signin_manager_mock_ ignoringNonObjectArgs]
+                  initWithBaseViewController:base_view_controller_
+                                 prefService:nullptr
+                             identityManager:nullptr
+                  identityInteractionManager:[OCMArg any]
+                              prefilledEmail:nil])
+        .andReturn(add_account_signin_manager_mock_);
+    OCMExpect([add_account_signin_manager_mock_
+        setDelegate:GetAddAccountSigninManagerDelegate()]);
+  }
+
+  ~AddAccountSigninCoordinatorTest() override {
+    EXPECT_OCMOCK_VERIFY((id)add_account_signin_manager_mock_);
+  }
+
+  id<AddAccountSigninManagerDelegate> GetAddAccountSigninManagerDelegate() {
+    return static_cast<id<AddAccountSigninManagerDelegate>>(coordinator_);
+  }
+
+ protected:
+  web::WebTaskEnvironment task_environment_;
+  std::unique_ptr<TestProfileIOS> profile_;
+  std::unique_ptr<TestBrowser> browser_;
+  // Required for UI blocker.
+  ProfileState* profile_state_;
+  IOSChromeScopedTestingLocalState scoped_testing_local_state_;
+  SceneState* scene_state_;
+
+  AddAccountSigninCoordinator* coordinator_;
+  UIViewController* base_view_controller_;
+  AddAccountSigninManager* add_account_signin_manager_mock_;
+};
+
+// Tests that AddAccountSigninCoordinator doesn't call its signinCompletion
+// block when being stopped while showing an alert dialog.
+TEST_F(AddAccountSigninCoordinatorTest, StopCoordinatorWhileShowingErrorAlert) {
+  // Open the coordiantor.
+  OCMExpect([add_account_signin_manager_mock_
+      showSigninWithIntent:AddAccountSigninIntent::kAddAccount]);
+  __block BOOL signinCompletionCalled = NO;
+  coordinator_.signinCompletion =
+      ^(SigninCoordinatorResult result, id<SystemIdentity> identity) {
+        signinCompletionCalled = YES;
+      };
+  [coordinator_ start];
+  // Generate an error from AddAccountSigninManager.
+  base::RunLoop run_loop1;
+  task_environment_.GetMainThreadTaskRunner()->PostTask(
+      FROM_HERE, run_loop1.QuitClosure());
+  run_loop1.Run();
+  OCMExpect([add_account_signin_manager_mock_ setDelegate:nil]);
+  NSError* error = [NSError errorWithDomain:@"Error Domain"
+                                       code:-42
+                                   userInfo:nil];
+  [GetAddAccountSigninManagerDelegate()
+      addAccountSigninManagerFinishedWithResult:SigninAddAccountToDeviceResult::
+                                                    kError
+                                       identity:nil
+                                          error:error];
+  // Stop the coordinator.
+  [coordinator_ stop];
+  base::RunLoop run_loop2;
+  task_environment_.GetMainThreadTaskRunner()->PostTask(
+      FROM_HERE, run_loop2.QuitClosure());
+  run_loop2.Run();
+  // The sign-in completion should not be called since the owner stopped the
+  // coordinator.
+  EXPECT_FALSE(signinCompletionCalled);
+}
+
+}  // namespace
diff --git a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
index ed5bdf6..4659ca4 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
+++ b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
@@ -341,6 +341,7 @@
 #import "ios/public/provider/chrome/browser/voice_search/voice_search_api.h"
 #import "ios/public/provider/chrome/browser/voice_search/voice_search_controller.h"
 #import "ios/web/public/web_state.h"
+#import "ios/web/public/web_state_id.h"
 #import "ui/base/device_form_factor.h"
 #import "ui/base/l10n/l10n_util.h"
 
@@ -4413,10 +4414,10 @@
 #pragma mark - PasswordControllerDelegate methods
 
 - (BOOL)displaySignInNotification:(UIViewController*)viewController
-                        fromTabId:(NSString*)tabId {
-  NSString* visibleTabId = self.activeWebState->GetStableIdentifier();
+                        fromTabId:(web::WebStateID)tabId {
   // Ignore unless the call comes from currently visible tab.
-  if (![tabId isEqualToString:visibleTabId]) {
+  web::WebStateID visibleTabId = self.activeWebState->GetUniqueIdentifier();
+  if (tabId != visibleTabId) {
     return NO;
   }
   [self.viewController addChildViewController:viewController];
diff --git a/ios/chrome/browser/credential_provider/model/credential_provider_service.h b/ios/chrome/browser/credential_provider/model/credential_provider_service.h
index 918734c6..a6922a96 100644
--- a/ios/chrome/browser/credential_provider/model/credential_provider_service.h
+++ b/ios/chrome/browser/credential_provider/model/credential_provider_service.h
@@ -173,6 +173,7 @@
 
   // syncer::SyncServiceObserver:
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
   // Observer for change in enabled or managed state of prefs that govern the
   // CPE.
diff --git a/ios/chrome/browser/credential_provider/model/credential_provider_service.mm b/ios/chrome/browser/credential_provider/model/credential_provider_service.mm
index ece25a9..0bc745e9 100644
--- a/ios/chrome/browser/credential_provider/model/credential_provider_service.mm
+++ b/ios/chrome/browser/credential_provider/model/credential_provider_service.mm
@@ -664,6 +664,11 @@
   UpdatePasswordSyncSetting();
 }
 
+void CredentialProviderService::OnSyncShutdown(syncer::SyncService* sync) {
+  // Unreachable, since this service is Shutdown() before the SyncService.
+  NOTREACHED();
+}
+
 // PasskeyModel::Observer:
 void CredentialProviderService::OnPasskeysChanged(
     const std::vector<webauthn::PasskeyModelChange>& changes) {
diff --git a/ios/chrome/browser/find_in_page/model/BUILD.gn b/ios/chrome/browser/find_in_page/model/BUILD.gn
index 1d9407f..dc0e211a 100644
--- a/ios/chrome/browser/find_in_page/model/BUILD.gn
+++ b/ios/chrome/browser/find_in_page/model/BUILD.gn
@@ -35,11 +35,7 @@
 source_set("eg2_tests") {
   configs += [ "//build/config/ios:xctest_config" ]
   testonly = true
-  sources = [
-    "find_in_page_egtest.mm",
-    "find_in_page_egtest_util.h",
-    "find_in_page_egtest_util.mm",
-  ]
+  sources = [ "find_in_page_egtest.mm" ]
   deps = [
     ":eg_test_support+eg2",
     "//components/omnibox/browser:pref_names",
diff --git a/ios/chrome/browser/find_in_page/model/find_in_page_egtest.mm b/ios/chrome/browser/find_in_page/model/find_in_page_egtest.mm
index d25f613fc..06ce4f9b 100644
--- a/ios/chrome/browser/find_in_page/model/find_in_page_egtest.mm
+++ b/ios/chrome/browser/find_in_page/model/find_in_page_egtest.mm
@@ -5,18 +5,43 @@
 #import "base/ios/ios_util.h"
 #import "base/test/ios/wait_util.h"
 #import "ios/chrome/browser/find_in_page/model/find_in_page_app_interface.h"
-#import "ios/chrome/browser/find_in_page/model/find_in_page_egtest_util.h"
 #import "ios/chrome/browser/popup_menu/ui_bundled/popup_menu_constants.h"
+#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
+#import "ios/chrome/test/earl_grey/chrome_actions.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/testing/earl_grey/app_launch_manager.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
+#import "net/test/embedded_test_server/embedded_test_server.h"
+
+using chrome_test_util::WebStateScrollViewMatcher;
 
 namespace {
 
+// Constants for Find in Page test content.
+const char kFindInPageTestRepeatingText[] = "repeating";
+const char kFindInPageTestShortTextID[] = "shortText";
+const char kFindInPageTestShortText[] = "ShortQuery";
+const char kFindInPageTestLongText[] =
+    "This is a particularly long string with a great number of characters";
+const char kFindInPageTestSpecialCharactersText[] = "!@#$%^&*()_+";
+const char kFindInPageTestNumbersText[] = "1234567890";
+const char kFindInPageTestAlphanumericText[] = "f00bar";
+const char kFindInPageTestNonASCIIText[] = "大家好🦑";
+const char kFindInPageTestWithSpanishAccentText[] = "á";
+const char kFindInPageTestWithoutSpanishAccentText[] = "a";
+const char kFindInPageTestLowercaseAndUppercaseText[] =
+    "ThIs tExT Is bOtH UpPeRcAsE AnD LoWeRcAsE";
+const char kFindInPageTestRTLText[] = "He said \"שלם\" (shalom] to me.";
+
+// Relative URLs for testing purposes.
+const char kFindInPageTestURL[] = "/findinpage.html";
+const char kFindInPageCrossOriginFrameTestURL[] = "/crossorigin.html";
+const char kFindInPageComplexPDFTestURL[] = "/complex_document.pdf";
+
 // Constants to identify the Find navigator UI components in the view hierarchy.
 constexpr char kFindInPageDoneButtonID[] = "find.doneButton";
 constexpr char kFindInPageSearchFieldID[] = "find.searchField";
@@ -24,19 +49,84 @@
 constexpr char kFindInPageNextButtonID[] = "find.nextButton";
 constexpr char kFindInPagePreviousButtonID[] = "find.previousButton";
 
+// Returns Paste button matcher from UIMenuController.
+id<GREYMatcher> PasteButton() {
+  NSString* a11yLabelPaste = @"Paste";
+  return grey_allOf(grey_accessibilityLabel(a11yLabelPaste),
+                    chrome_test_util::SystemSelectionCallout(), nil);
+}
+
+// Returns the test content for different test cases.
+std::string FindInPageTestContent() {
+  std::ostringstream oss;
+  oss << "<div>";
+  oss << "Text that repeats: " << kFindInPageTestRepeatingText
+      << kFindInPageTestRepeatingText << "</p>";
+  oss << "  <p id=\"" << kFindInPageTestShortTextID << "\">"
+      << kFindInPageTestShortText << "</p>";
+  oss << "  <p>" << kFindInPageTestLongText << "</p>";
+  oss << "  <p>Special characters: " << kFindInPageTestSpecialCharactersText
+      << "</p>";
+  oss << "  <p>Numbers: " << kFindInPageTestNumbersText << "</p>";
+  oss << "  <p>Alphanumeric text: " << kFindInPageTestAlphanumericText
+      << "</p>";
+  oss << "  <p>Non-ASCII text: " << kFindInPageTestNonASCIIText << "</p>";
+  oss << "  <p>Text without spanish accent: "
+      << kFindInPageTestWithoutSpanishAccentText << "</p>";
+  oss << "  <p>Case sensitivity: " << kFindInPageTestLowercaseAndUppercaseText
+      << "</p>";
+  oss << "  <p dir=\"RTL\">" << kFindInPageTestRTLText << "</p>";
+  oss << "  <div>";
+  oss << "<div style=\"height: 2000px; background-color: lightgray;\"/>";
+  oss << "</div>";
+  return oss.str();
+}
+
+// Response handler that serves a test page for Find in Page.
+std::unique_ptr<net::test_server::HttpResponse> FindInPageTestPageHttpResponse(
+    const net::test_server::HttpRequest& request) {
+  if (request.relative_url != kFindInPageTestURL) {
+    return nullptr;
+  }
+  std::unique_ptr<net::test_server::BasicHttpResponse> http_response =
+      std::make_unique<net::test_server::BasicHttpResponse>();
+  http_response->set_code(net::HTTP_OK);
+  http_response->set_content(
+      "<html><head><meta charset=\"UTF-8\"></head><body>" +
+      FindInPageTestContent() + "</body></html>");
+  return std::move(http_response);
+}
+
+// Response handler that serves a test page with a cross-origin iframe for Find
+// in Page. `sourceURL` is used as `src` for the iframe.
+std::unique_ptr<net::test_server::HttpResponse>
+FindInPageTestCrossOriginFramePageHttpResponse(
+    const GURL& sourceURL,
+    const net::test_server::HttpRequest& request) {
+  if (request.relative_url != kFindInPageCrossOriginFrameTestURL) {
+    return nullptr;
+  }
+  std::unique_ptr<net::test_server::BasicHttpResponse> http_response =
+      std::make_unique<net::test_server::BasicHttpResponse>();
+  http_response->set_code(net::HTTP_OK);
+  http_response->set_content(
+      "<html><head><meta charset=\"UTF-8\"></head><body>" +
+      FindInPageTestContent() + "<iframe src=\"" + sourceURL.spec() +
+      "\"></iframe></body></html>");
+  return std::move(http_response);
+}
+
 }  // namespace
 
-// Tests for Native Find in Page. This tests the variant of Native Find in Page
-// with a Find interaction i.e. with the system UI or Find navigator. Many tests
-// use the `secondTestServer` to ensure what is being tested also works with
-// cross-origin iframes.
-@interface FindInPageTestCase
-    : ChromeTestCase <FindInPageTestCaseHelperDelegate>
+// Tests for Find in Page. Many tests use the `secondTestServer` to ensure what
+// is being tested also works with cross-origin iframes.
+@interface FindInPageTestCase : ChromeTestCase
 
 @end
 
 @implementation FindInPageTestCase {
-  FindInPageTestCaseHelper* _helper;
+  // Second test server for cross-origin iframe tests.
+  std::unique_ptr<net::test_server::EmbeddedTestServer> _secondTestServer;
 }
 
 - (void)setUp {
@@ -44,15 +134,11 @@
 
   // Clear saved search term.
   [FindInPageAppInterface clearSearchTerm];
-
-  // Creating helper.
-  _helper = [[FindInPageTestCaseHelper alloc] init];
-  _helper.testServer = self.testServer;
-  _helper.delegate = self;
 }
 
-#pragma mark - FindInPageTestCaseHelperDelegate
+#pragma mark - Helpers
 
+// Opens Find in Page.
 - (void)openFindInPageWithOverflowMenu {
   [ChromeEarlGrey waitForKeyboardToDisappear];
   [ChromeEarlGreyUI openToolsMenu];
@@ -72,17 +158,20 @@
   [ChromeEarlGreyUI waitForAppToIdle];
 }
 
+// Closes Find in page.
 - (void)closeFindInPageWithDoneButton {
   [[EarlGrey
       selectElementWithMatcher:grey_accessibilityID(@(kFindInPageDoneButtonID))]
       performAction:grey_tap()];
 }
 
+// Replaces the text in the Find in page textfield.
 - (void)replaceFindInPageText:(NSString*)text {
   [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
       performAction:grey_replaceText(text)];
 }
 
+// Paste text into Find in page textfield.
 - (void)pasteTextToFindInPage:(NSString*)text {
   [ChromeEarlGrey copyTextToPasteboard:text];
   [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
@@ -90,15 +179,19 @@
   [[EarlGrey selectElementWithMatcher:PasteButton()] performAction:grey_tap()];
 }
 
+// Clear text in Find in Page text field.
 - (void)clearFindInPageText {
   [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
       performAction:grey_replaceText(@"")];
 }
 
+// Matcher for find in page textfield.
 - (id<GREYMatcher>)findInPageInputField {
   return grey_accessibilityID(@(kFindInPageSearchFieldID));
 }
 
+// Asserts that there is a string "`resultIndex` of `resultCount`" present in
+// the results count label. Waits for up to 1 second for this to happen.
 - (void)assertResultStringIsResult:(int)resultIndex
                         outOfTotal:(int)resultCount {
   // Returns "<current> of <total>" search results label (e.g "1 of 5").
@@ -120,6 +213,9 @@
       @"Timeout waiting for correct Find in Page results string to appear");
 }
 
+// Asserts that there is a string "0 of 0" present in the results count label,
+// or that the label is not visible. Waits for up to 1 second for this to
+// happen.
 - (void)assertResultStringIsEmptyOrZero {
   ConditionBlock condition = ^{
     NSError* error = nil;
@@ -137,6 +233,8 @@
       @"Timeout waiting for correct Find in Page results string to appear");
 }
 
+// Asserts that there is a string in the results count label, that is not "0 of
+// 0". Waits for up to 1 second for this to happen.
 - (void)assertResultStringIsNonZero {
   ConditionBlock condition = ^{
     NSError* error = nil;
@@ -155,42 +253,181 @@
       @"Timeout waiting for correct Find in Page results string to appear");
 }
 
+// Taps Next button in Find in page.
 - (void)advanceToNextResult {
   [[EarlGrey
       selectElementWithMatcher:grey_accessibilityID(@(kFindInPageNextButtonID))]
       performAction:grey_tap()];
 }
 
+// Taps Previous button in Find in page.
 - (void)advanceToPreviousResult {
   [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                           @(kFindInPagePreviousButtonID))]
       performAction:grey_tap()];
 }
 
+// Sets up two test servers to test Find in Page on web pages which might
+// contain cross-origin iframes.
+- (void)setUpTestServersForWebPageTest {
+  // Set up first server to test Find in Page content.
+  self.testServer->RegisterRequestHandler(
+      base::BindRepeating(&FindInPageTestPageHttpResponse));
+  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+
+  // Set up second server for cross-origin iframe tests.
+  GURL sourceURLForFrame = self.testServer->GetURL(kFindInPageTestURL);
+  _secondTestServer = std::make_unique<net::test_server::EmbeddedTestServer>();
+  [self secondTestServer]->RegisterRequestHandler(base::BindRepeating(
+      &FindInPageTestCrossOriginFramePageHttpResponse, sourceURLForFrame));
+  GREYAssertTrue([self secondTestServer]->Start(),
+                 @"Second test server serving page with iframe did not start.");
+}
+
+- (void)setUpTestServerForPDFTest {
+  // This is sufficient to ensure `ios/testing/data/http_server_files/` file
+  // system directory is being served, as this is the default configuration.
+  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+}
+
+// Second test server so cross-origin iframes can be tested together with
+// ChromeTestCase's `testServer`.
+- (net::test_server::EmbeddedTestServer*)secondTestServer {
+  return _secondTestServer.get();
+}
+// Matcher similar to `grey_text` but more generic i.e. only looks at `hasText`
+// prefix.
+- (id<GREYMatcher>)matcherForText:(NSString*)text {
+  NSString* prefix = @"hasText";
+  GREYMatchesBlock matchesBlock = ^BOOL(id element) {
+    return [[element text] isEqualToString:text];
+  };
+
+  GREYDescribeToBlock describeToBlock = ^void(id<GREYDescription> description) {
+    [description
+        appendText:[NSString stringWithFormat:@"%@('%@')", prefix, text]];
+  };
+  // A matcher for non-SwiftUI elements
+  id<GREYMatcher> matcher =
+      [[GREYElementMatcherBlock alloc] initWithMatchesBlock:matchesBlock
+                                           descriptionBlock:describeToBlock];
+  return matcher;
+}
+
 #pragma mark - Tests
 
 // Tests that FIP can be opened with Overflow menu.
 - (void)testFindInPageFromOverflowMenu {
-  [_helper helperTestFindInPageFromOverflowMenu];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page.
+  GURL destinationURL = self.testServer->GetURL(kFindInPageTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP with Overflow menu and check it is visible.
+  [self openFindInPageWithOverflowMenu];
+  [ChromeEarlGrey
+      waitForSufficientlyVisibleElementWithMatcher:[self findInPageInputField]];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that characters appear in the search box and that results UI updates as
 // each characters is entered/deleted.
 - (void)testFindInPageTextInput {
-  [_helper helperTestFindInPageTextInput];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page.
+  GURL destinationURL = self.testServer->GetURL(kFindInPageTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP.
+  [self openFindInPageWithOverflowMenu];
+  // Test the result string is empty or "0".
+  [self assertResultStringIsEmptyOrZero];
+
+  [self replaceFindInPageText:@(kFindInPageTestRepeatingText)];
+  // Test the input field contains the text that was just typed.
+  [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
+      assertWithMatcher:[self matcherForText:@(kFindInPageTestRepeatingText)]];
+  // Test the result UI is updated accordingly.
+  [self assertResultStringIsResult:1 outOfTotal:2];
+
+  [self
+      replaceFindInPageText:[NSString
+                                stringWithFormat:@"%s%s",
+                                                 kFindInPageTestRepeatingText,
+                                                 kFindInPageTestRepeatingText]];
+  [self assertResultStringIsResult:1 outOfTotal:1];
+
+  [self
+      replaceFindInPageText:[NSString
+                                stringWithFormat:@"%s%s%s",
+                                                 kFindInPageTestRepeatingText,
+                                                 kFindInPageTestRepeatingText,
+                                                 kFindInPageTestRepeatingText]];
+  [self assertResultStringIsEmptyOrZero];
+
+  [self clearFindInPageText];
+  [self assertResultStringIsEmptyOrZero];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that the number of results for a query accounts for all the matches
 // across frames, here with a main frame and a cross-origin iframe.
 - (void)testFindInPageSupportsCrossOriginFrame {
-  [_helper helperTestFindInPageSupportsCrossOriginFrame];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe.
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP.
+  [self openFindInPageWithOverflowMenu];
+  [self assertResultStringIsEmptyOrZero];
+
+  [self replaceFindInPageText:@(kFindInPageTestShortText)];
+  // Tests there are two matches: one is in the main frame, the other in the
+  // cross-origin iframe.
+  [self assertResultStringIsResult:1 outOfTotal:2];
+
+  [self advanceToNextResult];
+  // Tests that the second match can be navigated to.
+  [self assertResultStringIsResult:2 outOfTotal:2];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that FIP can find different types of characters: special characters,
 // number, strings with both letters and numbers as well as non-ASCII
 // characters.
 - (void)testFindInPageSpecialCharacters {
-  [_helper helperTestFindInPageSpecialCharacters];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe.
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP.
+  [self openFindInPageWithOverflowMenu];
+  [self assertResultStringIsEmptyOrZero];
+
+  // Tests special characters.
+  [self replaceFindInPageText:@(kFindInPageTestSpecialCharactersText)];
+  [self assertResultStringIsResult:1 outOfTotal:2];
+
+  // Tests numbers.
+  [self replaceFindInPageText:@(kFindInPageTestNumbersText)];
+  [self assertResultStringIsResult:1 outOfTotal:2];
+
+  // Tests alphanumeric values.
+  [self replaceFindInPageText:@(kFindInPageTestAlphanumericText)];
+  [self assertResultStringIsResult:1 outOfTotal:2];
+
+  // Tests non-ASCII characters.
+  [self replaceFindInPageText:@(kFindInPageTestNonASCIIText)];
+  [self assertResultStringIsResult:1 outOfTotal:2];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that text can be copied from the web page and pasted into the FIP input
@@ -202,18 +439,78 @@
     EARL_GREY_TEST_DISABLED(@"Flaky on iOS 18 simulators.");
   }
 #endif
-  [_helper helperTestFindInPageCopyPaste];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page.
+  GURL destinationURL = self.testServer->GetURL(kFindInPageTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Select and copy text on the web page.
+  [ChromeEarlGreyUI
+      longPressElementOnWebView:
+          [ElementSelector selectorWithElementID:kFindInPageTestShortTextID]];
+
+  [[EarlGrey
+      selectElementWithMatcher:
+          grey_allOf(chrome_test_util::SystemSelectionCalloutCopyButton(),
+                     grey_sufficientlyVisible(), nil)]
+      performAction:grey_tap()];
+
+  // Open FIP.
+  [self openFindInPageWithOverflowMenu];
+
+  // Paste content of pasteboard in the FIP text field.
+  [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:PasteButton()] performAction:grey_tap()];
+
+  // Tests that the number of results is updated accordingly.
+  [self assertResultStringIsResult:1 outOfTotal:1];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that FIP yields no results for an empty search query.
 - (void)testFindInPageEmptySearchQuery {
-  [_helper helperTestFindInPageEmptySearchQuery];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe.
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP.
+  [self openFindInPageWithOverflowMenu];
+
+  // Assert that searching text from the page yields results.
+  [self replaceFindInPageText:@(kFindInPageTestShortText)];
+  [self assertResultStringIsNonZero];
+
+  // Test that the number of results is zero after clearing the FIP text
+  // field.
+  [self clearFindInPageText];
+  [self assertResultStringIsEmptyOrZero];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that FIP yields no results for a non-empty query with no matches in the
 // page.
 - (void)testFindInPageQueryWithNoMatches {
-  [_helper helperTestFindInPageQueryWithNoMatches];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe.
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP and type text that is not in the page.
+  const char* queryWithNoMatches =
+      "example query which should not match with the content of the page";
+  [ChromeEarlGrey waitForWebStateNotContainingText:queryWithNoMatches];
+  [self openFindInPageWithOverflowMenu];
+  [self replaceFindInPageText:@(queryWithNoMatches)];
+  // Test the result label shows no results.
+  [self assertResultStringIsEmptyOrZero];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that FIP yields no matches for a text with spanish accents e.g. 'á' if
@@ -225,46 +522,273 @@
   if (base::ios::IsRunningOnIOS26OrLater()) {
     EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 26.");
   }
-  [_helper helperTestFindInPageDifferentAccent];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe.
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Assert the text without accent is there but the text with accents does
+  // not match.
+  [ChromeEarlGrey
+      waitForWebStateContainingText:kFindInPageTestWithoutSpanishAccentText];
+  [ChromeEarlGrey
+      waitForWebStateNotContainingText:kFindInPageTestWithSpanishAccentText];
+
+  // Open FIP and assert that text with no accents yields matches.
+  [self openFindInPageWithOverflowMenu];
+  [self replaceFindInPageText:@(kFindInPageTestWithoutSpanishAccentText)];
+  [self assertResultStringIsNonZero];
+
+  // Replace the text without spanish accent with the same text with spanish
+  // accents and test that there are no more matches.
+  [self replaceFindInPageText:@(kFindInPageTestWithSpanishAccentText)];
+  [self assertResultStringIsEmptyOrZero];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Test that there is no query persistence with this variant of Native Find in
 // Page i.e. with Find interaction.
 - (void)testFindInPageHistory {
-  [_helper helperTestFindInPageHistoryWithQueryPersistence:NO];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe.
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP and test the input field is empty.
+  [self openFindInPageWithOverflowMenu];
+  [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
+      assertWithMatcher:[self matcherForText:@""]];
+
+  // Type a query and assert it is contained in the input field before closing
+  // FIP.
+  [self replaceFindInPageText:@(kFindInPageTestShortText)];
+  [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
+      assertWithMatcher:[self matcherForText:@(kFindInPageTestShortText)]];
+  [self closeFindInPageWithDoneButton];
+
+  // Open FIP again and test depending on query persistence.
+  [self openFindInPageWithOverflowMenu];
+  [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
+      assertWithMatcher:[self matcherForText:@""]];
+  [self closeFindInPageWithDoneButton];
+
+  // Open the same URL in a different non-Incognito tab.
+  [ChromeEarlGrey openNewTab];
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP in this new tab and test depending on query persistence.
+  [self openFindInPageWithOverflowMenu];
+  [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
+      assertWithMatcher:[self matcherForText:@""]];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that there is no query persistence from an non-Incognito to an
 // Incognito tab.
 - (void)testFindInPageNormalToIncognito {
-  [_helper helperTestFindInPageNormalToIncognito];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe.
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP and type short query.
+  [self openFindInPageWithOverflowMenu];
+  [self replaceFindInPageText:@(kFindInPageTestShortText)];
+  [self closeFindInPageWithDoneButton];
+
+  // Load same URL in a new Incognito tab.
+  [ChromeEarlGrey openNewIncognitoTab];
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP and test the input field is empty.
+  [self openFindInPageWithOverflowMenu];
+  [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
+      assertWithMatcher:[self matcherForText:@""]];
+  [self closeFindInPageWithDoneButton];
+}
+
+// Tests that switching orientation during a Find session does not throw away
+// the query or the current results.
+- (void)testFindInPageSwitchOrientation {
+  if (base::ios::IsRunningOnIOS26OrLater()) {
+    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 26.");
+  }
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe.
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP, type short query, move to second match and wait for expected
+  // results.
+  [self openFindInPageWithOverflowMenu];
+  [self replaceFindInPageText:@(kFindInPageTestShortText)];
+  [self advanceToNextResult];
+  [self assertResultStringIsResult:2 outOfTotal:2];
+
+  // Switch to landscape.
+  GREYAssert(
+      [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationLandscapeLeft
+                                    error:nil],
+      @"Could not rotate device to Landscape Left");
+
+  // Test the query is still there will the same result.
+  [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
+      assertWithMatcher:[self matcherForText:@(kFindInPageTestShortText)]];
+  [self assertResultStringIsResult:2 outOfTotal:2];
+
+  // Switch back to portrait.
+  GREYAssert([EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
+                                           error:nil],
+             @"Could not rotate device to Portrait");
+
+  // Test the query is still there will the same result.
+  [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
+      assertWithMatcher:[self matcherForText:@(kFindInPageTestShortText)]];
+  [self assertResultStringIsResult:2 outOfTotal:2];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that Next/Previous buttons work and wrap.
 - (void)testFindInPageNextPreviousArrows {
-  [_helper helperTestFindInPageNextPreviousArrows];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe.
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP and type query with four expected matches.
+  [self openFindInPageWithOverflowMenu];
+  [self replaceFindInPageText:@(kFindInPageTestRepeatingText)];
+  [self assertResultStringIsResult:1 outOfTotal:4];
+
+  // Test that tapping "Next" button works and wraps.
+  [self advanceToNextResult];
+  [self assertResultStringIsResult:2 outOfTotal:4];
+  [self advanceToNextResult];
+  [self assertResultStringIsResult:3 outOfTotal:4];
+  [self advanceToNextResult];
+  [self assertResultStringIsResult:4 outOfTotal:4];
+  [self advanceToNextResult];
+  [self assertResultStringIsResult:1 outOfTotal:4];
+
+  // Test that tapping "Previous" button also works and wraps.
+  [self advanceToPreviousResult];
+  [self assertResultStringIsResult:4 outOfTotal:4];
+  [self advanceToPreviousResult];
+  [self assertResultStringIsResult:3 outOfTotal:4];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests the various ways to dismiss the keyboard during a Find session.
 // TODO(crbug.com/40283787): Test fails on downstream bots.
 - (void)DISABLED_testFindInPageDismissKeyboard {
-  [_helper helperTestFindInPageDismissKeyboard];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe.
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP and type short query.
+  [self openFindInPageWithOverflowMenu];
+  [self replaceFindInPageText:@(kFindInPageTestShortText)];
+
+  // Tap Done button and test the keyboard is dismissed as a result.
+  [self closeFindInPageWithDoneButton];
+  [ChromeEarlGrey waitForKeyboardToDisappear];
+
+  // Open FIP and type short query again.
+  [self openFindInPageWithOverflowMenu];
+  [self replaceFindInPageText:@(kFindInPageTestShortText)];
+
+  // Tap an element on the page and test the keyboard is dismissed as a
+  // result.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
+      performAction:chrome_test_util::TapWebElementWithId(
+                        kFindInPageTestShortTextID)];
+  [ChromeEarlGrey waitForKeyboardToDisappear];
 }
 
 // Tests that FIP can find long strings of characters.
 - (void)testFindInPageLongString {
-  [_helper helperTestFindInPageLongString];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe.
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP and type short query.
+  [self openFindInPageWithOverflowMenu];
+  [self replaceFindInPageText:@(kFindInPageTestLongText)];
+
+  // Test the number of results is as expected.
+  [self assertResultStringIsResult:1 outOfTotal:2];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that FIP is not case sensitive.
 - (void)testFindInPageNotCaseSensitive {
-  [_helper helperTestFindInPageNotCaseSensitive];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe.
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Assert the page contains string that is both lowercase and uppercase.
+  [ChromeEarlGrey
+      waitForWebStateContainingText:kFindInPageTestLowercaseAndUppercaseText];
+
+  // Open FIP and type lowercase version of contained text.
+  [self openFindInPageWithOverflowMenu];
+  [self replaceFindInPageText:[@(kFindInPageTestLowercaseAndUppercaseText)
+                                  lowercaseString]];
+  // Test the number of results is as expected.
+  [self assertResultStringIsResult:1 outOfTotal:2];
+
+  // Clear input field and type uppercase version of contained text.
+  [self replaceFindInPageText:[@(kFindInPageTestLowercaseAndUppercaseText)
+                                  uppercaseString]];
+  // Test the number of results is as expected.
+  [self assertResultStringIsResult:1 outOfTotal:2];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that there is no leak of the FIP search query from Incognito tabs to
 // normal tabs.
 - (void)testFindInPageIncognitoHistory {
-  [_helper helperTestFindInPageIncognitoHistory];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe in new Incognito tab.
+  [ChromeEarlGrey openNewIncognitoTab];
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP and type short query.
+  [self openFindInPageWithOverflowMenu];
+  [self replaceFindInPageText:@(kFindInPageTestShortText)];
+  [self closeFindInPageWithDoneButton];
+
+  // Open a new normal tab and load the same URL.
+  [ChromeEarlGrey openNewTab];
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP again and test the input field is empty.
+  [self openFindInPageWithOverflowMenu];
+  [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
+      assertWithMatcher:[self matcherForText:@""]];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that there is no query persistence when coming back to a normal tab
@@ -278,23 +802,80 @@
     }
   }
 
-  [_helper helperTestFindInPageSwitchingTabsWithQueryPersistence:NO];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe in a second normal tab.
+  [ChromeEarlGrey openNewTab];
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP and type short query.
+  [self openFindInPageWithOverflowMenu];
+  [self replaceFindInPageText:@(kFindInPageTestShortText)];
+
+  // Switching to first tab and then back to second tab.
+  [ChromeEarlGrey selectTabAtIndex:0];
+  [ChromeEarlGrey selectTabAtIndex:1];
+
+  // Test query persistence.
+  [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
+      assertWithMatcher:[self matcherForText:@""]];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that FIP can find RTL text in a web page.
 // TODO(crbug.com/366752786): Re-enable once de-flaked.
 - (void)FLAKY_testFindInPageRTL {
-  [_helper helperTestFindInPageRTL];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe.
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP, type RTL text and test that the results are as expected.
+  [self openFindInPageWithOverflowMenu];
+  [self pasteTextToFindInPage:@(kFindInPageTestRTLText)];
+  [self assertResultStringIsResult:1 outOfTotal:2];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that Find in Page can find matches in an Incognito tab.
 - (void)testFindInPageIncognito {
-  [_helper helperTestFindInPageIncognito];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe in a new Incognito tab.
+  [ChromeEarlGrey openNewIncognitoTab];
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP, type text contained in test page and test that the results are
+  // as expected.
+  [self openFindInPageWithOverflowMenu];
+  [self replaceFindInPageText:@(kFindInPageTestShortText)];
+  [self assertResultStringIsResult:1 outOfTotal:2];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests accessibility of the Find in Page screen.
 - (void)testFindInPageAccessibility {
-  [_helper helperTestFindInPageAccessibility];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page with cross-origin iframe.
+  GURL destinationURL =
+      [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP, type query and check the expected number of results.
+  [self openFindInPageWithOverflowMenu];
+  [self replaceFindInPageText:@(kFindInPageTestShortText)];
+  [self assertResultStringIsResult:1 outOfTotal:2];
+
+  // Test accessibility.
+  [ChromeEarlGrey verifyAccessibilityForCurrentScreen];
+  [self closeFindInPageWithDoneButton];
 }
 
 // Tests that Native Find in Page works as expected for PDF documents.
@@ -304,7 +885,47 @@
     EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 26.");
   }
 
-  [_helper helperTestFindInPagePDF];
+  [self setUpTestServerForPDFTest];
+
+  // Load test PDF document.
+  GURL destinationURL = self.testServer->GetURL(kFindInPageComplexPDFTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Open FIP and test that the input field is empty and there are no results.
+  [self openFindInPageWithOverflowMenu];
+  [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
+      assertWithMatcher:[self matcherForText:@""]];
+  [self assertResultStringIsEmptyOrZero];
+
+  // Type text with 18 expected matches and test that results are as
+  // expected.
+  [self replaceFindInPageText:@"the F"];
+  [self assertResultStringIsResult:1 outOfTotal:18];
+
+  // Test that the Next button works.
+  [self advanceToNextResult];
+  [self assertResultStringIsResult:2 outOfTotal:18];
+  [self advanceToNextResult];
+  [self assertResultStringIsResult:3 outOfTotal:18];
+
+  // Type more specific query and test that results are as expected.
+  [self replaceFindInPageText:@"the Form"];
+  [self assertResultStringIsResult:1 outOfTotal:6];
+
+  // Test that the Previous button works and wraps.
+  [self advanceToPreviousResult];
+  [self assertResultStringIsResult:6 outOfTotal:6];
+  [self advanceToPreviousResult];
+  [self assertResultStringIsResult:5 outOfTotal:6];
+
+  // Type even more specific query and test that results are as expected.
+  [self replaceFindInPageText:@"the Form 1050"];
+  [self assertResultStringIsEmptyOrZero];
+
+  // Test that the Done button does close Find in Page.
+  [self closeFindInPageWithDoneButton];
+  [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
+      assertWithMatcher:grey_notVisible()];
 }
 
 // Tests that FIP exit fullscreen when done.
@@ -313,7 +934,33 @@
   // Relaunch the app to take the configuration into account.
   [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
 
-  [_helper helperTestFindInPageExitFullscreen];
+  [self setUpTestServersForWebPageTest];
+
+  // Load test page.
+  GURL destinationURL = self.testServer->GetURL(kFindInPageComplexPDFTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  // Ensure the toolbars are not in fullscreen mode by checking if share
+  // button is visible.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabShareButton()]
+      assertWithMatcher:grey_sufficientlyVisible()];
+
+  // Open FIP with Overflow menu and check it is visible and the share button
+  // is not visible.
+  [self openFindInPageWithOverflowMenu];
+  [ChromeEarlGrey
+      waitForSufficientlyVisibleElementWithMatcher:[self findInPageInputField]];
+
+  [ChromeEarlGrey waitForUIElementToDisappearWithMatcher:chrome_test_util::
+                                                             TabShareButton()];
+
+  // Close find in page with Done button and ensure the share button is
+  // visible again.
+  [self closeFindInPageWithDoneButton];
+  [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
+      assertWithMatcher:grey_notVisible()];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::TabShareButton()]
+      assertWithMatcher:grey_sufficientlyVisible()];
 }
 
 // Tests that FIP works properly with bottom omnibox.
@@ -327,7 +974,34 @@
   }
 #endif
 
-  [_helper helperTestFindInPageWithBottomOmnibox];
+  // Set bottom Omnibox.
+  [ChromeEarlGrey setBoolValue:YES forLocalStatePref:prefs::kBottomOmnibox];
+
+  // Load test page.
+  [self setUpTestServersForWebPageTest];
+  GURL destinationURL = self.testServer->GetURL(kFindInPageTestURL);
+  [ChromeEarlGrey loadURL:destinationURL];
+
+  [ChromeEarlGreyUI waitForToolbarVisible:YES];
+
+  // Open FIP with Overflow menu and check it is visible and the share button
+  // is not visible.
+  [self openFindInPageWithOverflowMenu];
+  [ChromeEarlGrey
+      waitForSufficientlyVisibleElementWithMatcher:[self findInPageInputField]];
+
+  // Hide keyboard.
+  [ChromeEarlGrey simulatePhysicalKeyboardEvent:@"\n" flags:0];
+
+  // Scroll up and down the page.
+  [[EarlGrey selectElementWithMatcher:WebStateScrollViewMatcher()]
+      performAction:grey_scrollInDirection(kGREYDirectionDown, 150)];
+  [[EarlGrey selectElementWithMatcher:WebStateScrollViewMatcher()]
+      performAction:grey_scrollInDirection(kGREYDirectionUp, 150)];
+
+  // Ensure that the bottom Omnibox is not visible.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxAtBottom()]
+      assertWithMatcher:grey_notVisible()];
 }
 
 @end
diff --git a/ios/chrome/browser/find_in_page/model/find_in_page_egtest_util.h b/ios/chrome/browser/find_in_page/model/find_in_page_egtest_util.h
deleted file mode 100644
index 0a80d6e..0000000
--- a/ios/chrome/browser/find_in_page/model/find_in_page_egtest_util.h
+++ /dev/null
@@ -1,156 +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.
-
-#ifndef IOS_CHROME_BROWSER_FIND_IN_PAGE_MODEL_FIND_IN_PAGE_EGTEST_UTIL_H_
-#define IOS_CHROME_BROWSER_FIND_IN_PAGE_MODEL_FIND_IN_PAGE_EGTEST_UTIL_H_
-
-#import <Foundation/Foundation.h>
-
-namespace net {
-namespace test_server {
-class EmbeddedTestServer;
-}
-}  // namespace net
-
-@protocol GREYMatcher;
-
-// Constants for Find in Page test content.
-extern const char kFindInPageTestRepeatingText[];
-extern const char kFindInPageTestShortTextID[];
-extern const char kFindInPageTestShortText[];
-extern const char kFindInPageTestLongText[];
-extern const char kFindInPageTestSpecialCharactersText[];
-extern const char kFindInPageTestNumbersText[];
-extern const char kFindInPageTestAlphanumericText[];
-extern const char kFindInPageTestNonASCIIText[];
-extern const char kFindInPageTestWithSpanishAccentText[];
-extern const char kFindInPageTestWithoutSpanishAccentText[];
-extern const char kFindInPageTestLowercaseAndUppercaseText[];
-extern const char kFindInPageTestRTLText[];
-
-// Relative URLs for testing purposes.
-extern const char kFindInPageTestURL[];
-extern const char kFindInPageCrossOriginFrameTestURL[];
-extern const char kFindInPageComplexPDFTestURL[];
-
-// Returns Paste button matcher from UIMenuController.
-id<GREYMatcher> PasteButton();
-
-// Delegate for FindInPageTestCaseHelper. A test case can implement this
-// protocol to define how to perform basic Find in Page operations, and then set
-// itself as the delegate of this helper.
-@protocol FindInPageTestCaseHelperDelegate
-
-// Opens Find in Page.
-- (void)openFindInPageWithOverflowMenu;
-// Closes Find in page.
-- (void)closeFindInPageWithDoneButton;
-// Replaces the text in the Find in page textfield.
-- (void)replaceFindInPageText:(NSString*)text;
-// Paste text into Find in page textfield.
-- (void)pasteTextToFindInPage:(NSString*)text;
-// Clear text in Find in Page text field.
-- (void)clearFindInPageText;
-// Matcher for find in page textfield.
-- (id<GREYMatcher>)findInPageInputField;
-// Asserts that there is a string "`resultIndex` of `resultCount`" present in
-// the results count label. Waits for up to 1 second for this to happen.
-- (void)assertResultStringIsResult:(int)resultIndex outOfTotal:(int)resultCount;
-// Asserts that there is a string "0 of 0" present in the results count label,
-// or that the label is not visible. Waits for up to 1 second for this to
-// happen.
-- (void)assertResultStringIsEmptyOrZero;
-// Asserts that there is a string in the results count label, that is not "0 of
-// 0". Waits for up to 1 second for this to happen.
-- (void)assertResultStringIsNonZero;
-// Taps Next button in Find in page.
-- (void)advanceToNextResult;
-// Taps Previous button in Find in page.
-- (void)advanceToPreviousResult;
-
-@end
-
-// Test helper for Native Find in Page. Many tests use the `secondTestServer` to
-// ensure what is being tested also works with cross-origin iframes.
-@interface FindInPageTestCaseHelper : NSObject
-
-// Delegate.
-@property(nonatomic, weak) id<FindInPageTestCaseHelperDelegate> delegate;
-
-// First test server.
-@property(nonatomic, assign) net::test_server::EmbeddedTestServer* testServer;
-
-// Sets up two test servers to test Find in Page on web pages which might
-// contain cross-origin iframes.
-- (void)setUpTestServersForWebPageTest;
-// Second test server so cross-origin iframes can be tested together with
-// ChromeTestCase's `testServer`.
-- (net::test_server::EmbeddedTestServer*)secondTestServer;
-// Matcher similar to `grey_text` but more generic i.e. only looks at `hasText`
-// prefix.
-- (id<GREYMatcher>)matcherForText:(NSString*)text;
-
-#pragma mark - Test scenarios
-
-// Tests that FIP can be opened with Overflow menu.
-- (void)helperTestFindInPageFromOverflowMenu;
-// Tests that characters appear in the search box and that results UI updates as
-// each characters is entered/deleted.
-- (void)helperTestFindInPageTextInput;
-// Tests that the number of results for a query accounts for all the matches
-// across frames, here with a main frame and a cross-origin iframe.
-- (void)helperTestFindInPageSupportsCrossOriginFrame;
-// Tests that FIP can find different types of characters: special characters,
-// number, strings with both letters and numbers as well as non-ASCII
-// characters.
-- (void)helperTestFindInPageSpecialCharacters;
-// Tests that text can be copied from the web page and pasted into the FIP input
-// field and that the results UI updates accordingly.
-- (void)helperTestFindInPageCopyPaste;
-// Tests that FIP yields no results for an empty search query.
-- (void)helperTestFindInPageEmptySearchQuery;
-// Tests that FIP yields no results for a non-empty query with no matches in the
-// page.
-- (void)helperTestFindInPageQueryWithNoMatches;
-// Tests that FIP yields no matches for a text with spanish accents e.g. 'á' if
-// the web page contains the same text without spanish accents e.g. 'a'. This
-// test assumes removing accents from `kFindInPageTestWithSpanishAccentText`
-// yields `kFindInPageTestWithoutSpanishAccentText`.
-- (void)helperTestFindInPageDifferentAccent;
-// Test that there is no query persistence with this variant of Native Find in
-// Page i.e. with Find interaction.
-- (void)helperTestFindInPageHistoryWithQueryPersistence:(BOOL)queryPersistence;
-// Tests that there is no query persistence from an non-Incognito to an
-// Incognito tab.
-- (void)helperTestFindInPageNormalToIncognito;
-// Tests that Next/Previous buttons work and wrap.
-- (void)helperTestFindInPageNextPreviousArrows;
-// Tests the various ways to dismiss the keyboard during a Find session.
-- (void)helperTestFindInPageDismissKeyboard;
-// Tests that FIP can find long strings of characters.
-- (void)helperTestFindInPageLongString;
-// Tests that FIP is not case sensitive.
-- (void)helperTestFindInPageNotCaseSensitive;
-// Tests that there is no leak of the FIP search query from Incognito tabs to
-// normal tabs.
-- (void)helperTestFindInPageIncognitoHistory;
-// Tests that there is no query persistence when coming back to a normal tab
-// after switching temporarily to another tab.
-- (void)helperTestFindInPageSwitchingTabsWithQueryPersistence:
-    (BOOL)queryPersistence;
-// Tests that FIP can find RTL text in a web page.
-- (void)helperTestFindInPageRTL;
-// Tests that Find in Page can find matches in an Incognito tab.
-- (void)helperTestFindInPageIncognito;
-// Tests accessibility of the Find in Page screen.
-- (void)helperTestFindInPageAccessibility;
-// Tests that Native Find in Page works as expected for PDF documents.
-- (void)helperTestFindInPagePDF;
-// Tests that FIP exit fullscreen when done.
-- (void)helperTestFindInPageExitFullscreen;
-// Tests that FIP works properly with bottom Omnibox.
-- (void)helperTestFindInPageWithBottomOmnibox;
-@end
-
-#endif  // IOS_CHROME_BROWSER_FIND_IN_PAGE_MODEL_FIND_IN_PAGE_EGTEST_UTIL_H_
diff --git a/ios/chrome/browser/find_in_page/model/find_in_page_egtest_util.mm b/ios/chrome/browser/find_in_page/model/find_in_page_egtest_util.mm
deleted file mode 100644
index 7ae285e..0000000
--- a/ios/chrome/browser/find_in_page/model/find_in_page_egtest_util.mm
+++ /dev/null
@@ -1,864 +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.
-
-#import "ios/chrome/browser/find_in_page/model/find_in_page_egtest_util.h"
-
-#import <sstream>
-
-#import "components/omnibox/browser/omnibox_pref_names.h"
-#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
-#import "ios/chrome/test/earl_grey/chrome_actions.h"
-#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
-#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
-#import "ios/chrome/test/earl_grey/chrome_matchers.h"
-#import "ios/chrome/test/earl_grey/chrome_xcui_actions.h"
-#import "ios/testing/earl_grey/app_launch_manager.h"
-#import "ios/testing/earl_grey/earl_grey_test.h"
-#import "ios/web/public/test/element_selector.h"
-#import "net/test/embedded_test_server/embedded_test_server.h"
-
-using chrome_test_util::WebStateScrollViewMatcher;
-
-namespace {
-
-// Returns the test content for different test cases.
-std::string FindInPageTestContent() {
-  std::ostringstream oss;
-  oss << "<div>";
-  oss << "Text that repeats: " << kFindInPageTestRepeatingText
-      << kFindInPageTestRepeatingText << "</p>";
-  oss << "  <p id=\"" << kFindInPageTestShortTextID << "\">"
-      << kFindInPageTestShortText << "</p>";
-  oss << "  <p>" << kFindInPageTestLongText << "</p>";
-  oss << "  <p>Special characters: " << kFindInPageTestSpecialCharactersText
-      << "</p>";
-  oss << "  <p>Numbers: " << kFindInPageTestNumbersText << "</p>";
-  oss << "  <p>Alphanumeric text: " << kFindInPageTestAlphanumericText
-      << "</p>";
-  oss << "  <p>Non-ASCII text: " << kFindInPageTestNonASCIIText << "</p>";
-  oss << "  <p>Text without spanish accent: "
-      << kFindInPageTestWithoutSpanishAccentText << "</p>";
-  oss << "  <p>Case sensitivity: " << kFindInPageTestLowercaseAndUppercaseText
-      << "</p>";
-  oss << "  <p dir=\"RTL\">" << kFindInPageTestRTLText << "</p>";
-  oss << "  <div>";
-  oss << "<div style=\"height: 2000px; background-color: lightgray;\"/>";
-  oss << "</div>";
-  return oss.str();
-}
-
-// Response handler that serves a test page for Find in Page.
-std::unique_ptr<net::test_server::HttpResponse> FindInPageTestPageHttpResponse(
-    const net::test_server::HttpRequest& request) {
-  if (request.relative_url != kFindInPageTestURL) {
-    return nullptr;
-  }
-  std::unique_ptr<net::test_server::BasicHttpResponse> http_response =
-      std::make_unique<net::test_server::BasicHttpResponse>();
-  http_response->set_code(net::HTTP_OK);
-  http_response->set_content(
-      "<html><head><meta charset=\"UTF-8\"></head><body>" +
-      FindInPageTestContent() + "</body></html>");
-  return std::move(http_response);
-}
-
-// Response handler that serves a test page with a cross-origin iframe for Find
-// in Page. `sourceURL` is used as `src` for the iframe.
-std::unique_ptr<net::test_server::HttpResponse>
-FindInPageTestCrossOriginFramePageHttpResponse(
-    const GURL& sourceURL,
-    const net::test_server::HttpRequest& request) {
-  if (request.relative_url != kFindInPageCrossOriginFrameTestURL) {
-    return nullptr;
-  }
-  std::unique_ptr<net::test_server::BasicHttpResponse> http_response =
-      std::make_unique<net::test_server::BasicHttpResponse>();
-  http_response->set_code(net::HTTP_OK);
-  http_response->set_content(
-      "<html><head><meta charset=\"UTF-8\"></head><body>" +
-      FindInPageTestContent() + "<iframe src=\"" + sourceURL.spec() +
-      "\"></iframe></body></html>");
-  return std::move(http_response);
-}
-
-}  // namespace
-
-const char kFindInPageTestRepeatingText[] = "repeating";
-const char kFindInPageTestShortTextID[] = "shortText";
-const char kFindInPageTestShortText[] = "ShortQuery";
-const char kFindInPageTestLongText[] =
-    "This is a particularly long string with a great number of characters";
-const char kFindInPageTestSpecialCharactersText[] = "!@#$%^&*()_+";
-const char kFindInPageTestNumbersText[] = "1234567890";
-const char kFindInPageTestAlphanumericText[] = "f00bar";
-const char kFindInPageTestNonASCIIText[] = "大家好🦑";
-const char kFindInPageTestWithSpanishAccentText[] = "á";
-const char kFindInPageTestWithoutSpanishAccentText[] = "a";
-const char kFindInPageTestLowercaseAndUppercaseText[] =
-    "ThIs tExT Is bOtH UpPeRcAsE AnD LoWeRcAsE";
-const char kFindInPageTestRTLText[] = "He said \"שלם\" (shalom] to me.";
-
-const char kFindInPageTestURL[] = "/findinpage.html";
-const char kFindInPageCrossOriginFrameTestURL[] = "/crossorigin.html";
-const char kFindInPageComplexPDFTestURL[] = "/complex_document.pdf";
-
-id<GREYMatcher> PasteButton() {
-  NSString* a11yLabelPaste = @"Paste";
-  return grey_allOf(grey_accessibilityLabel(a11yLabelPaste),
-                    chrome_test_util::SystemSelectionCallout(), nil);
-}
-
-@implementation FindInPageTestCaseHelper {
-  // Second test server for cross-origin iframe tests.
-  std::unique_ptr<net::test_server::EmbeddedTestServer> _secondTestServer;
-}
-
-- (void)setUpTestServersForWebPageTest {
-  // Set up first server to test Find in Page content.
-  self.testServer->RegisterRequestHandler(
-      base::BindRepeating(&FindInPageTestPageHttpResponse));
-  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
-
-  // Set up second server for cross-origin iframe tests.
-  GURL sourceURLForFrame = self.testServer->GetURL(kFindInPageTestURL);
-  _secondTestServer = std::make_unique<net::test_server::EmbeddedTestServer>();
-  [self secondTestServer]->RegisterRequestHandler(base::BindRepeating(
-      &FindInPageTestCrossOriginFramePageHttpResponse, sourceURLForFrame));
-  GREYAssertTrue([self secondTestServer]->Start(),
-                 @"Second test server serving page with iframe did not start.");
-}
-
-- (net::test_server::EmbeddedTestServer*)secondTestServer {
-  return _secondTestServer.get();
-}
-
-- (void)setUpTestServerForPDFTest {
-  // This is sufficient to ensure `ios/testing/data/http_server_files/` file
-  // system directory is being served, as this is the default configuration.
-  GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
-}
-
-- (id<GREYMatcher>)matcherForText:(NSString*)text {
-  NSString* prefix = @"hasText";
-  GREYMatchesBlock matchesBlock = ^BOOL(id element) {
-    return [[element text] isEqualToString:text];
-  };
-
-  GREYDescribeToBlock describeToBlock = ^void(id<GREYDescription> description) {
-    [description
-        appendText:[NSString stringWithFormat:@"%@('%@')", prefix, text]];
-  };
-  // A matcher for non-SwiftUI elements
-  id<GREYMatcher> matcher =
-      [[GREYElementMatcherBlock alloc] initWithMatchesBlock:matchesBlock
-                                           descriptionBlock:describeToBlock];
-  return matcher;
-}
-
-// Tests that FIP can be opened with Overflow menu.
-- (void)helperTestFindInPageFromOverflowMenu {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page.
-    GURL destinationURL = self.testServer->GetURL(kFindInPageTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP with Overflow menu and check it is visible.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher:
-                        [self.delegate findInPageInputField]];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that characters appear in the search box and that results UI updates as
-// each characters is entered/deleted.
-- (void)helperTestFindInPageTextInput {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page.
-    GURL destinationURL = self.testServer->GetURL(kFindInPageTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP.
-    [self.delegate openFindInPageWithOverflowMenu];
-    // Test the result string is empty or "0".
-    [self.delegate assertResultStringIsEmptyOrZero];
-
-    [self.delegate replaceFindInPageText:@(kFindInPageTestRepeatingText)];
-    // Test the input field contains the text that was just typed.
-    [[EarlGrey selectElementWithMatcher:[self.delegate findInPageInputField]]
-        assertWithMatcher:[self
-                              matcherForText:@(kFindInPageTestRepeatingText)]];
-    // Test the result UI is updated accordingly.
-    [self.delegate assertResultStringIsResult:1 outOfTotal:2];
-
-    [self.delegate
-        replaceFindInPageText:
-            [NSString stringWithFormat:@"%s%s", kFindInPageTestRepeatingText,
-                                       kFindInPageTestRepeatingText]];
-    [self.delegate assertResultStringIsResult:1 outOfTotal:1];
-
-    [self.delegate
-        replaceFindInPageText:
-            [NSString stringWithFormat:@"%s%s%s", kFindInPageTestRepeatingText,
-                                       kFindInPageTestRepeatingText,
-                                       kFindInPageTestRepeatingText]];
-    [self.delegate assertResultStringIsEmptyOrZero];
-
-    [self.delegate clearFindInPageText];
-    [self.delegate assertResultStringIsEmptyOrZero];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that the number of results for a query accounts for all the matches
-// across frames, here with a main frame and a cross-origin iframe.
-- (void)helperTestFindInPageSupportsCrossOriginFrame {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe.
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate assertResultStringIsEmptyOrZero];
-
-    [self.delegate replaceFindInPageText:@(kFindInPageTestShortText)];
-    // Tests there are two matches: one is in the main frame, the other in the
-    // cross-origin iframe.
-    [self.delegate assertResultStringIsResult:1 outOfTotal:2];
-
-    [self.delegate advanceToNextResult];
-    // Tests that the second match can be navigated to.
-    [self.delegate assertResultStringIsResult:2 outOfTotal:2];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that FIP can find different types of characters: special characters,
-// number, strings with both letters and numbers as well as non-ASCII
-// characters.
-- (void)helperTestFindInPageSpecialCharacters {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe.
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate assertResultStringIsEmptyOrZero];
-
-    // Tests special characters.
-    [self.delegate
-        replaceFindInPageText:@(kFindInPageTestSpecialCharactersText)];
-    [self.delegate assertResultStringIsResult:1 outOfTotal:2];
-
-    // Tests numbers.
-    [self.delegate replaceFindInPageText:@(kFindInPageTestNumbersText)];
-    [self.delegate assertResultStringIsResult:1 outOfTotal:2];
-
-    // Tests alphanumeric values.
-    [self.delegate replaceFindInPageText:@(kFindInPageTestAlphanumericText)];
-    [self.delegate assertResultStringIsResult:1 outOfTotal:2];
-
-    // Tests non-ASCII characters.
-    [self.delegate replaceFindInPageText:@(kFindInPageTestNonASCIIText)];
-    [self.delegate assertResultStringIsResult:1 outOfTotal:2];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that text can be copied from the web page and pasted into the FIP input
-// field and that the results UI updates accordingly.
-- (void)helperTestFindInPageCopyPaste {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page.
-    GURL destinationURL = self.testServer->GetURL(kFindInPageTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Select and copy text on the web page.
-    [ChromeEarlGreyUI
-        longPressElementOnWebView:
-            [ElementSelector selectorWithElementID:kFindInPageTestShortTextID]];
-
-    [[EarlGrey
-        selectElementWithMatcher:
-            grey_allOf(chrome_test_util::SystemSelectionCalloutCopyButton(),
-                       grey_sufficientlyVisible(), nil)]
-        performAction:grey_tap()];
-
-    // Open FIP.
-    [self.delegate openFindInPageWithOverflowMenu];
-
-    // Paste content of pasteboard in the FIP text field.
-    [[EarlGrey selectElementWithMatcher:[self.delegate findInPageInputField]]
-        performAction:grey_tap()];
-    [[EarlGrey selectElementWithMatcher:PasteButton()]
-        performAction:grey_tap()];
-
-    // Tests that the number of results is updated accordingly.
-    [self.delegate assertResultStringIsResult:1 outOfTotal:1];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that FIP yields no results for an empty search query.
-- (void)helperTestFindInPageEmptySearchQuery {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe.
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP.
-    [self.delegate openFindInPageWithOverflowMenu];
-
-    // Assert that searching text from the page yields results.
-    [self.delegate replaceFindInPageText:@(kFindInPageTestShortText)];
-    [self.delegate assertResultStringIsNonZero];
-
-    // Test that the number of results is zero after clearing the FIP text
-    // field.
-    [self.delegate clearFindInPageText];
-    [self.delegate assertResultStringIsEmptyOrZero];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that FIP yields no results for a non-empty query with no matches in the
-// page.
-- (void)helperTestFindInPageQueryWithNoMatches {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe.
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP and type text that is not in the page.
-    const char* queryWithNoMatches =
-        "example query which should not match with the content of the page";
-    [ChromeEarlGrey waitForWebStateNotContainingText:queryWithNoMatches];
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate replaceFindInPageText:@(queryWithNoMatches)];
-    // Test the result label shows no results.
-    [self.delegate assertResultStringIsEmptyOrZero];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that FIP yields no matches for a text with spanish accents e.g. 'á' if
-// the web page contains the same text without spanish accents e.g. 'a'. This
-// test assumes removing accents from `kFindInPageTestWithSpanishAccentText`
-// yields `kFindInPageTestWithoutSpanishAccentText`.
-- (void)helperTestFindInPageDifferentAccent {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe.
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Assert the text without accent is there but the text with accents does
-    // not match.
-    [ChromeEarlGrey
-        waitForWebStateContainingText:kFindInPageTestWithoutSpanishAccentText];
-    [ChromeEarlGrey
-        waitForWebStateNotContainingText:kFindInPageTestWithSpanishAccentText];
-
-    // Open FIP and assert that text with no accents yields matches.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate
-        replaceFindInPageText:@(kFindInPageTestWithoutSpanishAccentText)];
-    [self.delegate assertResultStringIsNonZero];
-
-    // Replace the text without spanish accent with the same text with spanish
-    // accents and test that there are no more matches.
-    [self.delegate
-        replaceFindInPageText:@(kFindInPageTestWithSpanishAccentText)];
-    [self.delegate assertResultStringIsEmptyOrZero];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Test query persistence in the same tab and in a new normal tab.
-- (void)helperTestFindInPageHistoryWithQueryPersistence:(BOOL)queryPersistence {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe.
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP and test the input field is empty.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [[EarlGrey selectElementWithMatcher:[self.delegate findInPageInputField]]
-        assertWithMatcher:[self matcherForText:@""]];
-
-    // Type a query and assert it is contained in the input field before closing
-    // FIP.
-    [self.delegate replaceFindInPageText:@(kFindInPageTestShortText)];
-    [[EarlGrey selectElementWithMatcher:[self.delegate findInPageInputField]]
-        assertWithMatcher:[self matcherForText:@(kFindInPageTestShortText)]];
-    [self.delegate closeFindInPageWithDoneButton];
-
-    // Open FIP again and test depending on query persistence.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [[EarlGrey selectElementWithMatcher:[self.delegate findInPageInputField]]
-        assertWithMatcher:[self matcherForText:queryPersistence
-                                                   ? @(kFindInPageTestShortText)
-                                                   : @""]];
-    [self.delegate closeFindInPageWithDoneButton];
-
-    // Open the same URL in a different non-Incognito tab.
-    [ChromeEarlGrey openNewTab];
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP in this new tab and test depending on query persistence.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [[EarlGrey selectElementWithMatcher:[self.delegate findInPageInputField]]
-        assertWithMatcher:[self matcherForText:queryPersistence
-                                                   ? @(kFindInPageTestShortText)
-                                                   : @""]];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that there is no query persistence from an non-Incognito to an
-// Incognito tab.
-- (void)helperTestFindInPageNormalToIncognito {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe.
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP and type short query.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate replaceFindInPageText:@(kFindInPageTestShortText)];
-    [self.delegate closeFindInPageWithDoneButton];
-
-    // Load same URL in a new Incognito tab.
-    [ChromeEarlGrey openNewIncognitoTab];
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP and test the input field is empty.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [[EarlGrey selectElementWithMatcher:[self.delegate findInPageInputField]]
-        assertWithMatcher:[self matcherForText:@""]];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that switching orientation during a Find session does not throw away
-// the query or the current results.
-- (void)helperTestFindInPageSwitchOrientation {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe.
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP, type short query, move to second match and wait for expected
-    // results.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate replaceFindInPageText:@(kFindInPageTestShortText)];
-    [self.delegate advanceToNextResult];
-    [self.delegate assertResultStringIsResult:2 outOfTotal:2];
-
-    // Switch to landscape.
-    GREYAssert(
-        [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationLandscapeLeft
-                                      error:nil],
-        @"Could not rotate device to Landscape Left");
-
-    // Test the query is still there will the same result.
-    [[EarlGrey selectElementWithMatcher:[self.delegate findInPageInputField]]
-        assertWithMatcher:[self matcherForText:@(kFindInPageTestShortText)]];
-    [self.delegate assertResultStringIsResult:2 outOfTotal:2];
-
-    // Switch back to portrait.
-    GREYAssert([EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
-                                             error:nil],
-               @"Could not rotate device to Portrait");
-
-    // Test the query is still there will the same result.
-    [[EarlGrey selectElementWithMatcher:[self.delegate findInPageInputField]]
-        assertWithMatcher:[self matcherForText:@(kFindInPageTestShortText)]];
-    [self.delegate assertResultStringIsResult:2 outOfTotal:2];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that Next/Previous buttons work and wrap.
-- (void)helperTestFindInPageNextPreviousArrows {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe.
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP and type query with four expected matches.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate replaceFindInPageText:@(kFindInPageTestRepeatingText)];
-    [self.delegate assertResultStringIsResult:1 outOfTotal:4];
-
-    // Test that tapping "Next" button works and wraps.
-    [self.delegate advanceToNextResult];
-    [self.delegate assertResultStringIsResult:2 outOfTotal:4];
-    [self.delegate advanceToNextResult];
-    [self.delegate assertResultStringIsResult:3 outOfTotal:4];
-    [self.delegate advanceToNextResult];
-    [self.delegate assertResultStringIsResult:4 outOfTotal:4];
-    [self.delegate advanceToNextResult];
-    [self.delegate assertResultStringIsResult:1 outOfTotal:4];
-
-    // Test that tapping "Previous" button also works and wraps.
-    [self.delegate advanceToPreviousResult];
-    [self.delegate assertResultStringIsResult:4 outOfTotal:4];
-    [self.delegate advanceToPreviousResult];
-    [self.delegate assertResultStringIsResult:3 outOfTotal:4];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests the various ways to dismiss the keyboard during a Find session.
-- (void)helperTestFindInPageDismissKeyboard {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe.
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP and type short query.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate replaceFindInPageText:@(kFindInPageTestShortText)];
-
-    // Tap Done button and test the keyboard is dismissed as a result.
-    [self.delegate closeFindInPageWithDoneButton];
-    [ChromeEarlGrey waitForKeyboardToDisappear];
-
-    // Open FIP and type short query again.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate replaceFindInPageText:@(kFindInPageTestShortText)];
-
-    // Tap an element on the page and test the keyboard is dismissed as a
-    // result.
-    [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
-        performAction:chrome_test_util::TapWebElementWithId(
-                          kFindInPageTestShortTextID)];
-    [ChromeEarlGrey waitForKeyboardToDisappear];
-  }
-}
-
-// Tests that FIP can find long strings of characters.
-- (void)helperTestFindInPageLongString {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe.
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP and type short query.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate replaceFindInPageText:@(kFindInPageTestLongText)];
-
-    // Test the number of results is as expected.
-    [self.delegate assertResultStringIsResult:1 outOfTotal:2];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that FIP is not case sensitive.
-- (void)helperTestFindInPageNotCaseSensitive {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe.
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Assert the page contains string that is both lowercase and uppercase.
-    [ChromeEarlGrey
-        waitForWebStateContainingText:kFindInPageTestLowercaseAndUppercaseText];
-
-    // Open FIP and type lowercase version of contained text.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate
-        replaceFindInPageText:[@(kFindInPageTestLowercaseAndUppercaseText)
-                                  lowercaseString]];
-    // Test the number of results is as expected.
-    [self.delegate assertResultStringIsResult:1 outOfTotal:2];
-
-    // Clear input field and type uppercase version of contained text.
-    [self.delegate
-        replaceFindInPageText:[@(kFindInPageTestLowercaseAndUppercaseText)
-                                  uppercaseString]];
-    // Test the number of results is as expected.
-    [self.delegate assertResultStringIsResult:1 outOfTotal:2];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that there is no leak of the FIP search query from Incognito tabs to
-// normal tabs.
-- (void)helperTestFindInPageIncognitoHistory {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe in new Incognito tab.
-    [ChromeEarlGrey openNewIncognitoTab];
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP and type short query.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate replaceFindInPageText:@(kFindInPageTestShortText)];
-    [self.delegate closeFindInPageWithDoneButton];
-
-    // Open a new normal tab and load the same URL.
-    [ChromeEarlGrey openNewTab];
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP again and test the input field is empty.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [[EarlGrey selectElementWithMatcher:[self.delegate findInPageInputField]]
-        assertWithMatcher:[self matcherForText:@""]];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests query persistence when coming back to a normal tab after switching
-// temporarily to another tab.
-- (void)helperTestFindInPageSwitchingTabsWithQueryPersistence:
-    (BOOL)queryPersistence {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe in a second normal tab.
-    [ChromeEarlGrey openNewTab];
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP and type short query.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate replaceFindInPageText:@(kFindInPageTestShortText)];
-
-    // Switching to first tab and then back to second tab.
-    [ChromeEarlGrey selectTabAtIndex:0];
-    [ChromeEarlGrey selectTabAtIndex:1];
-
-    // Test query persistence.
-    [[EarlGrey selectElementWithMatcher:[self.delegate findInPageInputField]]
-        assertWithMatcher:[self matcherForText:queryPersistence
-                                                   ? @(kFindInPageTestShortText)
-                                                   : @""]];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that FIP can find RTL text in a web page.
-- (void)helperTestFindInPageRTL {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe.
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP, type RTL text and test that the results are as expected.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate pasteTextToFindInPage:@(kFindInPageTestRTLText)];
-    [self.delegate assertResultStringIsResult:1 outOfTotal:2];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that Find in Page can find matches in an Incognito tab.
-- (void)helperTestFindInPageIncognito {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe in a new Incognito tab.
-    [ChromeEarlGrey openNewIncognitoTab];
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP, type text contained in test page and test that the results are
-    // as expected.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate replaceFindInPageText:@(kFindInPageTestShortText)];
-    [self.delegate assertResultStringIsResult:1 outOfTotal:2];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests accessibility of the Find in Page screen.
-- (void)helperTestFindInPageAccessibility {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page with cross-origin iframe.
-    GURL destinationURL =
-        [self secondTestServer]->GetURL(kFindInPageCrossOriginFrameTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP, type query and check the expected number of results.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [self.delegate replaceFindInPageText:@(kFindInPageTestShortText)];
-    [self.delegate assertResultStringIsResult:1 outOfTotal:2];
-
-    // Test accessibility.
-    [ChromeEarlGrey verifyAccessibilityForCurrentScreen];
-    [self.delegate closeFindInPageWithDoneButton];
-  }
-}
-
-// Tests that Native Find in Page works as expected for PDF documents.
-- (void)helperTestFindInPagePDF {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServerForPDFTest];
-
-    // Load test PDF document.
-    GURL destinationURL = self.testServer->GetURL(kFindInPageComplexPDFTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Open FIP and test that the input field is empty and there are no results.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [[EarlGrey selectElementWithMatcher:[self.delegate findInPageInputField]]
-        assertWithMatcher:[self matcherForText:@""]];
-    [self.delegate assertResultStringIsEmptyOrZero];
-
-    // Type text with 18 expected matches and test that results are as
-    // expected.
-    [self.delegate replaceFindInPageText:@"the F"];
-    [self.delegate assertResultStringIsResult:1 outOfTotal:18];
-
-    // Test that the Next button works.
-    [self.delegate advanceToNextResult];
-    [self.delegate assertResultStringIsResult:2 outOfTotal:18];
-    [self.delegate advanceToNextResult];
-    [self.delegate assertResultStringIsResult:3 outOfTotal:18];
-
-    // Type more specific query and test that results are as expected.
-    [self.delegate replaceFindInPageText:@"the Form"];
-    [self.delegate assertResultStringIsResult:1 outOfTotal:6];
-
-    // Test that the Previous button works and wraps.
-    [self.delegate advanceToPreviousResult];
-    [self.delegate assertResultStringIsResult:6 outOfTotal:6];
-    [self.delegate advanceToPreviousResult];
-    [self.delegate assertResultStringIsResult:5 outOfTotal:6];
-
-    // Type even more specific query and test that results are as expected.
-    [self.delegate replaceFindInPageText:@"the Form 1050"];
-    [self.delegate assertResultStringIsEmptyOrZero];
-
-    // Test that the Done button does close Find in Page.
-    [self.delegate closeFindInPageWithDoneButton];
-    [[EarlGrey selectElementWithMatcher:[self.delegate findInPageInputField]]
-        assertWithMatcher:grey_notVisible()];
-  }
-}
-
-// Tests that FIP exit fullscreen when done.
-- (void)helperTestFindInPageExitFullscreen {
-  if (@available(iOS 16.1.1, *)) {
-    [self setUpTestServersForWebPageTest];
-
-    // Load test page.
-    GURL destinationURL = self.testServer->GetURL(kFindInPageComplexPDFTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    // Ensure the toolbars are not in fullscreen mode by checking if share
-    // button is visible.
-    [[EarlGrey selectElementWithMatcher:chrome_test_util::TabShareButton()]
-        assertWithMatcher:grey_sufficientlyVisible()];
-
-    // Open FIP with Overflow menu and check it is visible and the share button
-    // is not visible.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher:
-                        [self.delegate findInPageInputField]];
-
-    [ChromeEarlGrey waitForUIElementToDisappearWithMatcher:
-                        chrome_test_util::TabShareButton()];
-
-    // Close find in page with Done button and ensure the share button is
-    // visible again.
-    [self.delegate closeFindInPageWithDoneButton];
-    [[EarlGrey selectElementWithMatcher:[self.delegate findInPageInputField]]
-        assertWithMatcher:grey_notVisible()];
-    [[EarlGrey selectElementWithMatcher:chrome_test_util::TabShareButton()]
-        assertWithMatcher:grey_sufficientlyVisible()];
-  }
-}
-
-// Tests that FIP works properly with bottom omnibox.
-- (void)helperTestFindInPageWithBottomOmnibox {
-  if (@available(iOS 16.1.1, *)) {
-    // Set bottom Omnibox.
-    [ChromeEarlGrey setBoolValue:YES
-               forLocalStatePref:omnibox::kIsOmniboxInBottomPosition];
-
-    // Load test page.
-    [self setUpTestServersForWebPageTest];
-    GURL destinationURL = self.testServer->GetURL(kFindInPageTestURL);
-    [ChromeEarlGrey loadURL:destinationURL];
-
-    [ChromeEarlGreyUI waitForToolbarVisible:YES];
-
-    // Open FIP with Overflow menu and check it is visible and the share button
-    // is not visible.
-    [self.delegate openFindInPageWithOverflowMenu];
-    [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher:
-                        [self.delegate findInPageInputField]];
-
-    // Hide keyboard.
-    [ChromeEarlGrey simulatePhysicalKeyboardEvent:@"\n" flags:0];
-
-    // Scroll up and down the page.
-    [[EarlGrey selectElementWithMatcher:WebStateScrollViewMatcher()]
-        performAction:grey_scrollInDirection(kGREYDirectionDown, 150)];
-    [[EarlGrey selectElementWithMatcher:WebStateScrollViewMatcher()]
-        performAction:grey_scrollInDirection(kGREYDirectionUp, 150)];
-
-    // Ensure that the bottom Omnibox is not visible.
-    [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxAtBottom()]
-        assertWithMatcher:grey_notVisible()];
-  }
-}
-
-@end
diff --git a/ios/chrome/browser/first_run/ui_bundled/interactive_lens/ui/lens_interactive_promo_results_page_presenter.mm b/ios/chrome/browser/first_run/ui_bundled/interactive_lens/ui/lens_interactive_promo_results_page_presenter.mm
index ea1cfb1..896fdcd 100644
--- a/ios/chrome/browser/first_run/ui_bundled/interactive_lens/ui/lens_interactive_promo_results_page_presenter.mm
+++ b/ios/chrome/browser/first_run/ui_bundled/interactive_lens/ui/lens_interactive_promo_results_page_presenter.mm
@@ -164,7 +164,7 @@
                    completion:dismissalCompletion];
 }
 
-#pragma mark - LensOverlayBottomSheetPresentationDelegate
+#pragma mark - LensOverlayBottomSheetPresentationCommands
 
 - (void)requestMaximizeBottomSheet {
   // No-op. Sheet size is constant in this presentation.
@@ -174,11 +174,11 @@
   // No-op. Sheet size is constant in this presentation.
 }
 
-- (void)didLoadSelectionResult {
+- (void)adjustForSelectionResult {
   [self adjustSelectionOcclusionInsets];
 }
 
-- (void)didLoadTranslateResult {
+- (void)adjustForTranslateResult {
   // No-op.
 }
 
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index c341816b0..d8996ab 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -2574,6 +2574,10 @@
      flag_descriptions::kLensOverlayNavigationHistoryName,
      flag_descriptions::kLensOverlayNavigationHistoryDescription,
      flags_ui::kOsIos, FEATURE_VALUE_TYPE(kLensOverlayNavigationHistory)},
+    {"lens-overlay-custom-bottom-sheet",
+     flag_descriptions::kLensOverlayCustomBottomSheetName,
+     flag_descriptions::kLensOverlayCustomBottomSheetDescription,
+     flags_ui::kOsIos, FEATURE_VALUE_TYPE(kLensOverlayCustomBottomSheet)},
     {"page-action-menu", flag_descriptions::kPageActionMenuName,
      flag_descriptions::kPageActionMenuDescription, flags_ui::kOsIos,
      FEATURE_WITH_PARAMS_VALUE_TYPE(kPageActionMenu,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 0eaf02c8..055e9707 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -961,6 +961,12 @@
 const char kLensLoadAIMInLensResultPageDescription[] =
     "Opens in Lens result page rather than a new tab.";
 
+extern const char kLensOverlayCustomBottomSheetName[] =
+    "Use a custom bottom sheet presentation for Lens Overlay";
+extern const char kLensOverlayCustomBottomSheetDescription[] =
+    "When enabled the system bottom sheet for the Lens result page is "
+    "replaced by a custom bottom sheet presentation";
+
 extern const char kLensOverlayDisableIPHPanGestureName[] =
     "Disable Lens Overlay IPH Pan Dismissal";
 extern const char kLensOverlayDisableIPHPanGestureDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 7bf17506..97e6231 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -559,6 +559,9 @@
 extern const char kLensLoadAIMInLensResultPageName[];
 extern const char kLensLoadAIMInLensResultPageDescription[];
 
+extern const char kLensOverlayCustomBottomSheetName[];
+extern const char kLensOverlayCustomBottomSheetDescription[];
+
 extern const char kLensOverlayForceShowOnboardingScreenName[];
 extern const char kLensOverlayForceShowOnboardingScreenDescription[];
 
diff --git a/ios/chrome/browser/history/ui_bundled/base_history_view_controller.mm b/ios/chrome/browser/history/ui_bundled/base_history_view_controller.mm
index 3d09b919..ef54bad24 100644
--- a/ios/chrome/browser/history/ui_bundled/base_history_view_controller.mm
+++ b/ios/chrome/browser/history/ui_bundled/base_history_view_controller.mm
@@ -699,10 +699,7 @@
     return;  // UI has been updated in the meaning time.
   }
 
-  // TODO(crbug.com/371568658): Remove NotFatalUntil when we're sure this
-  // check doesn't fail.
-  CHECK_EQ(_indicatorState, IndicatorState::FETCHING_RESULTS,
-           base::NotFatalUntil::M139);
+  CHECK_EQ(_indicatorState, IndicatorState::FETCHING_RESULTS);
   _indicatorState = IndicatorState::SHOWING_LOADING_INDICATOR;
   [self startLoadingIndicatorWithLoadingMessage:l10n_util::GetNSString(
                                                     IDS_HISTORY_NO_RESULTS)];
@@ -719,10 +716,7 @@
 // removes the loading indicator and updates the UI with the results. If the
 // query hasn't returned, then no-op.
 - (void)maybeRemoveLoadingIndicator {
-  // TODO(crbug.com/371568658): Remove NotFatalUntil when we're sure this
-  // check doesn't fail.
-  CHECK_EQ(_indicatorState, IndicatorState::SHOWING_LOADING_INDICATOR,
-           base::NotFatalUntil::M139);
+  CHECK_EQ(_indicatorState, IndicatorState::SHOWING_LOADING_INDICATOR);
   _indicatorState = IndicatorState::WAITING_FOR_RESULTS;
 
   // If results have returned, then the UI is updated right away.
@@ -744,11 +738,8 @@
     return;
   }
 
-  // TODO(crbug.com/371568658): Remove NotFatalUntil when we're sure this
-  // check doesn't fail.
   CHECK(_indicatorState == IndicatorState::WAITING_FOR_RESULTS ||
-            _indicatorState == IndicatorState::FETCHING_RESULTS,
-        base::NotFatalUntil::M139);
+        _indicatorState == IndicatorState::FETCHING_RESULTS);
   _indicatorState = IndicatorState::IDLE;
 
   // Cancel all pending callbacks related to loading indicator.
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
index 8701739d..d034d7c 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
@@ -1574,8 +1574,8 @@
   }
 
   _resultsPagePresenter.delegate = self;
-  _resultMediator.presentationDelegate = _resultsPagePresenter;
-  _mediator.presentationDelegate = _resultsPagePresenter;
+  _resultMediator.bottomSheetCommands = _resultsPagePresenter;
+  _mediator.bottomSheetCommands = _resultsPagePresenter;
 }
 
 // Presents the result botom sheet.
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator.h b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator.h
index e339974..3344bb11 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator.h
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator.h
@@ -9,7 +9,7 @@
 
 #import "ios/chrome/browser/lens_overlay/coordinator/lens_omnibox_client_delegate.h"
 #import "ios/chrome/browser/lens_overlay/coordinator/lens_result_page_mediator_delegate.h"
-#import "ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_delegate.h"
+#import "ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_commands.h"
 #import "ios/chrome/browser/lens_overlay/ui/lens_overlay_result_consumer.h"
 #import "ios/chrome/browser/lens_overlay/ui/lens_toolbar_mutator.h"
 #import "ios/chrome/browser/omnibox/ui/omnibox_focus_delegate.h"
@@ -55,9 +55,9 @@
 /// Lens backend handler.
 @property(nonatomic, weak) id<ChromeLensOverlay> lensHandler;
 
-/// Presentation delegate for requesting bottom sheet resizing.
-@property(nonatomic, weak) id<LensOverlayBottomSheetPresentationDelegate>
-    presentationDelegate;
+/// Presentation commands for requesting bottom sheet resizing.
+@property(nonatomic, weak) id<LensOverlayBottomSheetPresentationCommands>
+    bottomSheetCommands;
 
 /// Utility for recoding Lens Overlay metrics.
 @property(nonatomic, weak) LensOverlayMetricsRecorder* metricsRecorder;
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator.mm
index c850286..b202cc3f 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator.mm
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator.mm
@@ -197,7 +197,7 @@
   [self.omniboxCoordinator focusOmnibox];
   [self.toolbarConsumer setOmniboxFocused:YES];
   [self.omniboxCoordinator.animatee setClearButtonFaded:NO];
-  [self.presentationDelegate requestMaximizeBottomSheet];
+  [self.bottomSheetCommands requestMaximizeBottomSheet];
 }
 
 - (void)defocusOmnibox {
@@ -257,13 +257,13 @@
       // bottom sheet hidden, as no auto selection happens at this stage.
       [self.lensHandler resetSelectionAreaToInitialPosition:^{
       }];
-      [self.presentationDelegate
+      [self.bottomSheetCommands
           showInfoMessage:LensOverlayBottomSheetInfoMessageType::
                               kImageTranslatedIndication];
     } else if (noSelectionInTranslate) {
       // A missing selection without a switch in modes indicates the user
       // intended to dismiss the current selection.
-      [self.presentationDelegate
+      [self.bottomSheetCommands
           showInfoMessage:LensOverlayBottomSheetInfoMessageType::
                               kImageTranslatedIndication];
     }
@@ -282,8 +282,8 @@
   };
   [self.toolbarConsumer setOmniboxEnabled:YES];
   // Make sure the bottom sheet is dismissed before triggering any alert.
-  if (self.presentationDelegate) {
-    [self.presentationDelegate hideBottomSheetWithCompletion:completion];
+  if (self.bottomSheetCommands) {
+    [self.bottomSheetCommands hideBottomSheetWithCompletion:completion];
   } else {
     completion();
   }
@@ -482,9 +482,9 @@
   [self updateOmniboxText:result.queryText];
 
   if (result.isGeneratedInTranslate) {
-    [self.presentationDelegate didLoadTranslateResult];
+    [self.bottomSheetCommands adjustForTranslateResult];
   } else {
-    [self.presentationDelegate didLoadSelectionResult];
+    [self.bottomSheetCommands adjustForSelectionResult];
   }
 }
 
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_result_page_mediator.h b/ios/chrome/browser/lens_overlay/coordinator/lens_result_page_mediator.h
index efea814d..3a2d389 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_result_page_mediator.h
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_result_page_mediator.h
@@ -9,7 +9,7 @@
 
 #import "ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider_delegate.h"
 #import "ios/chrome/browser/lens_overlay/coordinator/lens_web_provider.h"
-#import "ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_delegate.h"
+#import "ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_commands.h"
 #import "ios/chrome/browser/lens_overlay/ui/lens_overlay_result_consumer.h"
 #import "ios/chrome/browser/lens_overlay/ui/lens_result_page_mutator.h"
 #import "ios/web/public/web_state.h"
@@ -49,8 +49,8 @@
 @property(nonatomic, weak) id<LensOverlayTabChangeAudience> tabChangeAudience;
 
 /// Presentation delegate for requesting bottom sheet resizing.
-@property(nonatomic, weak) id<LensOverlayBottomSheetPresentationDelegate>
-    presentationDelegate;
+@property(nonatomic, weak) id<LensOverlayBottomSheetPresentationCommands>
+    bottomSheetCommands;
 
 /// WebState context menu configuration provider.
 @property(nonatomic, weak)
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_result_page_mediator.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_result_page_mediator.mm
index 62fbc20..f5793e8e 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_result_page_mediator.mm
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_result_page_mediator.mm
@@ -331,7 +331,7 @@
     }
 
     if (IsMaximizeBottomSheetURL(URL)) {
-      [self.presentationDelegate requestMaximizeBottomSheet];
+      [self.bottomSheetCommands requestMaximizeBottomSheet];
       return;
     }
 
diff --git a/ios/chrome/browser/lens_overlay/ui/BUILD.gn b/ios/chrome/browser/lens_overlay/ui/BUILD.gn
index a45188b..c30547f 100644
--- a/ios/chrome/browser/lens_overlay/ui/BUILD.gn
+++ b/ios/chrome/browser/lens_overlay/ui/BUILD.gn
@@ -18,7 +18,7 @@
 
 source_set("protocols") {
   sources = [
-    "lens_overlay_bottom_sheet_presentation_delegate.h",
+    "lens_overlay_bottom_sheet_presentation_commands.h",
     "lens_overlay_error_handler.h",
     "lens_overlay_result_consumer.h",
     "lens_overlay_results_page_presenting.h",
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_delegate.h b/ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_commands.h
similarity index 81%
rename from ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_delegate.h
rename to ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_commands.h
index 7b8018ca..375455b25 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_delegate.h
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_commands.h
@@ -1,9 +1,9 @@
-// Copyright 2024 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.
 
-#ifndef IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_OVERLAY_BOTTOM_SHEET_PRESENTATION_DELEGATE_H_
-#define IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_OVERLAY_BOTTOM_SHEET_PRESENTATION_DELEGATE_H_
+#ifndef IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_OVERLAY_BOTTOM_SHEET_PRESENTATION_COMMANDS_H_
+#define IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_OVERLAY_BOTTOM_SHEET_PRESENTATION_COMMANDS_H_
 
 #import <Foundation/Foundation.h>
 
@@ -15,10 +15,10 @@
   kImageTranslatedIndication,
 };
 
-// Presentation delegate for the bottom sheet.
+// Presentation commands for the bottom sheet.
 // Bottom sheet content may request the container to be maximized or minimized,
 // e.g. when the user selects a result that opens an image viewer.
-@protocol LensOverlayBottomSheetPresentationDelegate <NSObject>
+@protocol LensOverlayBottomSheetPresentationCommands <NSObject>
 
 // Request resizing the bottom sheet to maximum size.
 - (void)requestMaximizeBottomSheet;
@@ -27,10 +27,10 @@
 - (void)requestMinimizeBottomSheet;
 
 // Handle a selection result loaded in the bottom sheet.
-- (void)didLoadSelectionResult;
+- (void)adjustForSelectionResult;
 
 // Handle a translation result loaded in the bottom sheet.
-- (void)didLoadTranslateResult;
+- (void)adjustForTranslateResult;
 
 // Hides the bottom sheet without destroying the presentation.
 - (void)hideBottomSheetWithCompletion:(void (^)(void))completion;
@@ -40,4 +40,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_OVERLAY_BOTTOM_SHEET_PRESENTATION_DELEGATE_H_
+#endif  // IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_OVERLAY_BOTTOM_SHEET_PRESENTATION_COMMANDS_H_
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.mm b/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.mm
index 4556061..ade60b5 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.mm
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.mm
@@ -578,7 +578,7 @@
   }
 }
 
-#pragma mark - LensOverlayBottomSheetPresentationDelegate
+#pragma mark - LensOverlayBottomSheetPresentationCommands
 
 // Request resizing the bottom sheet to maximum size.
 - (void)requestMaximizeBottomSheet {
@@ -590,14 +590,14 @@
   [_detentsManager requestMinimizeBottomSheet];
 }
 
-- (void)didLoadSelectionResult {
+- (void)adjustForSelectionResult {
   _detentsManager.presentationStrategy =
       SheetDetentPresentationStategySelection;
   [_presentationNavigationController popToRootViewControllerAnimated:YES];
   [self adjustSelectionOcclusionInsets];
 }
 
-- (void)didLoadTranslateResult {
+- (void)adjustForTranslateResult {
   _detentsManager.presentationStrategy =
       SheetDetentPresentationStategyTranslate;
   [_presentationNavigationController popToRootViewControllerAnimated:YES];
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenting.h b/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenting.h
index e31f7c98..4a6089f 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenting.h
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenting.h
@@ -8,13 +8,13 @@
 #import <UIKit/UIKit.h>
 
 #import "ios/chrome/browser/lens_overlay/model/lens_overlay_sheet_detent_state.h"
-#import "ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_delegate.h"
+#import "ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_commands.h"
 
 @protocol LensOverlayResultsPagePresenterDelegate;
 
 // Protocol for presenting the Lens results bottom sheet.
 @protocol LensOverlayResultsPagePresenting <
-    LensOverlayBottomSheetPresentationDelegate>
+    LensOverlayBottomSheetPresentationCommands>
 
 // Whether the results page is currently presented.
 @property(nonatomic, assign, readonly) BOOL isResultPageVisible;
diff --git a/ios/chrome/browser/omnibox/ui/omnibox_text_field_ios.mm b/ios/chrome/browser/omnibox/ui/omnibox_text_field_ios.mm
index e3c63dc..0a9f09e7 100644
--- a/ios/chrome/browser/omnibox/ui/omnibox_text_field_ios.mm
+++ b/ios/chrome/browser/omnibox/ui/omnibox_text_field_ios.mm
@@ -1045,8 +1045,6 @@
          autocompleteLength == 0);
   }
   if (updateText) {
-    self.attributedText = fieldText;
-
     // TODO(crbug.com/330964534): Remove DUMP_WILL_BE_CHECK after investigating
     // crash.
     if (!self.endOfDocument || !self.beginningOfDocument) {
@@ -1055,6 +1053,8 @@
           << " text length: " << text.length << " has text position: "
           << (self.beginningOfDocument || self.endOfDocument);
     } else {
+      self.attributedText = fieldText;
+
       UITextPosition* endOfUserText =
           [self positionFromPosition:self.beginningOfDocument
                               offset:beginningOfAutocomplete];
diff --git a/ios/chrome/browser/passwords/model/password_controller.mm b/ios/chrome/browser/passwords/model/password_controller.mm
index 502a858..b92bef6d5 100644
--- a/ios/chrome/browser/passwords/model/password_controller.mm
+++ b/ios/chrome/browser/passwords/model/password_controller.mm
@@ -77,6 +77,7 @@
 #import "ios/web/public/js_messaging/web_frame.h"
 #import "ios/web/public/navigation/navigation_context.h"
 #import "ios/web/public/web_state.h"
+#import "ios/web/public/web_state_id.h"
 #import "services/network/public/cpp/shared_url_loader_factory.h"
 #import "ui/base/device_form_factor.h"
 #import "ui/base/l10n/l10n_util_mac.h"
@@ -335,7 +336,7 @@
           URLLoaderFactory:_webState->GetBrowserState()
                                ->GetSharedURLLoaderFactory()];
   if (![_delegate displaySignInNotification:self.notifyAutoSigninViewController
-                                  fromTabId:_webState->GetStableIdentifier()]) {
+                                  fromTabId:_webState->GetUniqueIdentifier()]) {
     // The notification was not shown. Store the password form in
     // `_pendingAutoSigninPasswordForm` to show the notification later.
     _pendingAutoSigninPasswordForm = std::move(formSignedIn);
diff --git a/ios/chrome/browser/passwords/model/password_controller_delegate.h b/ios/chrome/browser/passwords/model/password_controller_delegate.h
index 60a52f4f..d8b8082 100644
--- a/ios/chrome/browser/passwords/model/password_controller_delegate.h
+++ b/ios/chrome/browser/passwords/model/password_controller_delegate.h
@@ -9,6 +9,10 @@
 struct CredentialUIEntry;
 }  // namespace password_manager
 
+namespace web {
+class WebStateID;
+}  // namespace web
+
 // Delegate for registering view controller and displaying its view. Used to
 // add views to BVC.
 // TODO(crbug.com/40806286): Refactor this API to not be coupled to the BVC and
@@ -18,7 +22,7 @@
 // Adds `viewController` as child controller in order to display auto sign-in
 // notification. Returns YES if view was displayed, NO otherwise.
 - (BOOL)displaySignInNotification:(UIViewController*)viewController
-                        fromTabId:(NSString*)tabId;
+                        fromTabId:(web::WebStateID)tabId;
 
 // Opens the list of saved passwords in the settings.
 - (void)displaySavedPasswordList;
diff --git a/ios/chrome/browser/reader_mode/model/constants.h b/ios/chrome/browser/reader_mode/model/constants.h
index c6faab9..0ffa0de2 100644
--- a/ios/chrome/browser/reader_mode/model/constants.h
+++ b/ios/chrome/browser/reader_mode/model/constants.h
@@ -105,6 +105,20 @@
 };
 // LINT.ThenChange(/tools/metrics/histograms/metadata/ios/enums.xml:ReaderModeAccessPoint)
 
+// Recorded for IOS.ReaderMode.AccessPointWithMode. Entries should not be
+// renumbered and numeric values should never be reused.
+// LINT.IfChange(ReaderModeAccessPointWithMode)
+enum class ReaderModeAccessPointWithMode {
+  kContextualChipInRegular = 0,
+  kContextualChipInIncognito = 1,
+  kToolsMenuInRegular = 2,
+  kToolsMenuInIncognito = 3,
+  kAIHubInRegular = 4,
+  kAIHubInIncognito = 5,
+  kMaxValue = kAIHubInIncognito,
+};
+// LINT.ThenChange(/tools/metrics/histograms/metadata/ios/enums.xml:ReaderModeAccessPointWithMode)
+
 // Recorded for IOS.ReaderMode.Distiller.Result. Entries should not be
 // renumbered and numeric values should never be reused.
 // LINT.IfChange(ReaderModeDistillerOutcome)
@@ -174,6 +188,9 @@
 // Histogram name for Reader Mode access point for starting distillation.
 extern const char kReaderModeAccessPointHistogram[];
 
+// Histogram name for Reader Mode access point with application mode.
+extern const char kReaderModeAccessPointWithModeHistogram[];
+
 // Returns the Reader mode symbol name.
 NSString* GetReaderModeSymbolName();
 
diff --git a/ios/chrome/browser/reader_mode/model/constants.mm b/ios/chrome/browser/reader_mode/model/constants.mm
index 7193ede..ce5af18 100644
--- a/ios/chrome/browser/reader_mode/model/constants.mm
+++ b/ios/chrome/browser/reader_mode/model/constants.mm
@@ -34,6 +34,9 @@
 
 const char kReaderModeAccessPointHistogram[] = "IOS.ReaderMode.AccessPoint";
 
+const char kReaderModeAccessPointWithModeHistogram[] =
+    "IOS.ReaderMode.AccessPointWithMode";
+
 NSString* GetReaderModeSymbolName() {
   if (@available(iOS 18, *)) {
     return kReaderModeSymbolPostIOS18;
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_metrics_helper.h b/ios/chrome/browser/reader_mode/model/reader_mode_metrics_helper.h
index 6dceb8a..ab8a391 100644
--- a/ios/chrome/browser/reader_mode/model/reader_mode_metrics_helper.h
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_metrics_helper.h
@@ -39,7 +39,8 @@
   bool ReaderModeIsRecentlyUsed();
 
   // Records histograms for the Reading Mode distillation event.
-  void RecordReaderDistillerTriggered(ReaderModeAccessPoint access_point);
+  void RecordReaderDistillerTriggered(ReaderModeAccessPoint access_point,
+                                      bool is_incognito);
   void RecordReaderDistillerCompleted(ReaderModeAccessPoint access_point,
                                       ReaderModeDistillerResult result);
 
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_metrics_helper.mm b/ios/chrome/browser/reader_mode/model/reader_mode_metrics_helper.mm
index 25845b02..ff3dd68 100644
--- a/ios/chrome/browser/reader_mode/model/reader_mode_metrics_helper.mm
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_metrics_helper.mm
@@ -81,6 +81,23 @@
   }
 }
 
+ReaderModeAccessPointWithMode GetAccessPointWithMode(
+    ReaderModeAccessPoint access_point,
+    bool is_incognito) {
+  switch (access_point) {
+    case ReaderModeAccessPoint::kAIHub:
+      return is_incognito ? ReaderModeAccessPointWithMode::kAIHubInIncognito
+                          : ReaderModeAccessPointWithMode::kAIHubInRegular;
+    case ReaderModeAccessPoint::kToolsMenu:
+      return is_incognito ? ReaderModeAccessPointWithMode::kToolsMenuInIncognito
+                          : ReaderModeAccessPointWithMode::kToolsMenuInRegular;
+    case ReaderModeAccessPoint::kContextualChip:
+      return is_incognito
+                 ? ReaderModeAccessPointWithMode::kContextualChipInIncognito
+                 : ReaderModeAccessPointWithMode::kContextualChipInRegular;
+  }
+}
+
 }  // namespace
 
 ReaderModeMetricsHelper::ReaderModeMetricsHelper(
@@ -136,10 +153,14 @@
 }
 
 void ReaderModeMetricsHelper::RecordReaderDistillerTriggered(
-    ReaderModeAccessPoint access_point) {
+    ReaderModeAccessPoint access_point,
+    bool is_incognito) {
   distiller_timer_ = std::make_unique<base::ElapsedTimer>();
   last_reader_mode_state_ = ReaderModeState::kDistillationStarted;
   base::UmaHistogramEnumeration(kReaderModeAccessPointHistogram, access_point);
+  base::UmaHistogramEnumeration(
+      kReaderModeAccessPointWithModeHistogram,
+      GetAccessPointWithMode(access_point, is_incognito));
 }
 
 void ReaderModeMetricsHelper::RecordReaderDistillerTimedOut() {
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_metrics_helper_unittest.mm b/ios/chrome/browser/reader_mode/model/reader_mode_metrics_helper_unittest.mm
index ae3b67e..627e2925 100644
--- a/ios/chrome/browser/reader_mode/model/reader_mode_metrics_helper_unittest.mm
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_metrics_helper_unittest.mm
@@ -200,7 +200,7 @@
 // mode state.
 TEST_F(ReaderModeMetricsHelperTest, ReaderDistillerTriggered) {
   metrics_helper()->RecordReaderDistillerTriggered(
-      ReaderModeAccessPoint::kContextualChip);
+      ReaderModeAccessPoint::kContextualChip, /*is_incognito=*/false);
   metrics_helper()->Flush();
 
   EXPECT_THAT(histogram_tester_.GetAllSamples(kReaderModeStateHistogram),
@@ -213,7 +213,7 @@
 // mode state.
 TEST_F(ReaderModeMetricsHelperTest, ReaderDistillerCompleted) {
   metrics_helper()->RecordReaderDistillerTriggered(
-      ReaderModeAccessPoint::kContextualChip);
+      ReaderModeAccessPoint::kContextualChip, /*is_incognito=*/false);
   task_environment_.AdvanceClock(base::Seconds(1));
 
   metrics_helper()->RecordReaderDistillerCompleted(
@@ -290,7 +290,7 @@
 // Tests that canceling distillation records latency and reader mode state.
 TEST_F(ReaderModeMetricsHelperTest, DistillationCanceledOnTimeout) {
   metrics_helper()->RecordReaderDistillerTriggered(
-      ReaderModeAccessPoint::kAIHub);
+      ReaderModeAccessPoint::kAIHub, /*is_incognito=*/false);
   task_environment_.AdvanceClock(base::Seconds(1));
 
   // Cancelation triggers a metrics flush.
@@ -305,7 +305,7 @@
 // Tests that Reader Mode access point is recorded when a value is set.
 TEST_F(ReaderModeMetricsHelperTest, ReaderModeAccessPointRecorded) {
   metrics_helper()->RecordReaderDistillerTriggered(
-      ReaderModeAccessPoint::kAIHub);
+      ReaderModeAccessPoint::kAIHub, /*is_incognito=*/false);
   metrics_helper()->Flush();
 
   EXPECT_THAT(histogram_tester_.GetAllSamples(kReaderModeStateHistogram),
@@ -314,6 +314,32 @@
               BucketsAre(Bucket(ReaderModeAccessPoint::kAIHub, 1)));
 }
 
+// Tests that Reader Mode access point with mode is recorded for regular mode.
+TEST_F(ReaderModeMetricsHelperTest, ReaderModeAccessPointWithModeForRegular) {
+  metrics_helper()->RecordReaderDistillerTriggered(
+      ReaderModeAccessPoint::kAIHub, /*is_incognito=*/false);
+  metrics_helper()->Flush();
+
+  EXPECT_THAT(histogram_tester_.GetAllSamples(kReaderModeStateHistogram),
+              BucketsAre(Bucket(ReaderModeState::kDistillationStarted, 1)));
+  EXPECT_THAT(
+      histogram_tester_.GetAllSamples(kReaderModeAccessPointWithModeHistogram),
+      BucketsAre(Bucket(ReaderModeAccessPointWithMode::kAIHubInRegular, 1)));
+}
+
+// Tests that Reader Mode access point with mode is recorded for incognito mode.
+TEST_F(ReaderModeMetricsHelperTest, ReaderModeAccessPointWithModeForIncognito) {
+  metrics_helper()->RecordReaderDistillerTriggered(
+      ReaderModeAccessPoint::kAIHub, /*is_incognito=*/true);
+  metrics_helper()->Flush();
+
+  EXPECT_THAT(histogram_tester_.GetAllSamples(kReaderModeStateHistogram),
+              BucketsAre(Bucket(ReaderModeState::kDistillationStarted, 1)));
+  EXPECT_THAT(
+      histogram_tester_.GetAllSamples(kReaderModeAccessPointWithModeHistogram),
+      BucketsAre(Bucket(ReaderModeAccessPointWithMode::kAIHubInIncognito, 1)));
+}
+
 // Tests metrics functionality based on the heuristic result.
 class ReaderModeMetricsHelperWithEligibilityTest
     : public ReaderModeMetricsHelperTest,
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.mm b/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.mm
index 94e9074..0e8cf5f 100644
--- a/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.mm
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.mm
@@ -505,7 +505,8 @@
 
 void ReaderModeTabHelper::CreateReaderModeContent(
     ReaderModeAccessPoint access_point) {
-  metrics_helper_.RecordReaderDistillerTriggered(access_point);
+  bool is_incognito = web_state_->GetBrowserState()->IsOffTheRecord();
+  metrics_helper_.RecordReaderDistillerTriggered(access_point, is_incognito);
 
   if (!reader_mode_web_state_) {
     web::WebState::CreateParams create_params = web::WebState::CreateParams(
diff --git a/ios/chrome/browser/reminder_notifications/model/BUILD.gn b/ios/chrome/browser/reminder_notifications/model/BUILD.gn
index 5d6e723..eab1b6e9 100644
--- a/ios/chrome/browser/reminder_notifications/model/BUILD.gn
+++ b/ios/chrome/browser/reminder_notifications/model/BUILD.gn
@@ -34,7 +34,7 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "reminder_notification_client_unittests.mm" ]
+  sources = [ "reminder_notification_client_unittest.mm" ]
   deps = [
     ":client",
     "//base",
diff --git a/ios/chrome/browser/reminder_notifications/model/reminder_notification_client_unittests.mm b/ios/chrome/browser/reminder_notifications/model/reminder_notification_client_unittest.mm
similarity index 100%
rename from ios/chrome/browser/reminder_notifications/model/reminder_notification_client_unittests.mm
rename to ios/chrome/browser/reminder_notifications/model/reminder_notification_client_unittest.mm
diff --git a/ios/chrome/browser/search_engines/model/search_engine_choice_service_factory.mm b/ios/chrome/browser/search_engines/model/search_engine_choice_service_factory.mm
index 8afed98..563a012 100644
--- a/ios/chrome/browser/search_engines/model/search_engine_choice_service_factory.mm
+++ b/ios/chrome/browser/search_engines/model/search_engine_choice_service_factory.mm
@@ -55,7 +55,7 @@
       CHECK_DEREF(ios::TemplateURLPrepopulateDataResolverFactory::GetForProfile(
           profile)),
       CHECK_DEREF(IdentityManagerFactory::GetForProfile(profile)),
-      CHECK_DEREF(policy::ManagementServiceIOSFactory::GetForProfile(profile)));
+      CHECK_DEREF(policy::ManagementServiceIOSFactory::GetForPlatform()));
 
   service->Init();
   return service;
diff --git a/ios/chrome/browser/shared/public/features/features.h b/ios/chrome/browser/shared/public/features/features.h
index 8e7fd1e..b3abb0a 100644
--- a/ios/chrome/browser/shared/public/features/features.h
+++ b/ios/chrome/browser/shared/public/features/features.h
@@ -293,6 +293,9 @@
 // Feature flag to add lens overlay navigation to history.
 BASE_DECLARE_FEATURE(kLensOverlayNavigationHistory);
 
+// Feature flag to add a custom bottom sheet presentation Lens results.
+BASE_DECLARE_FEATURE(kLensOverlayCustomBottomSheet);
+
 // Feature flag to check headers for lens searches.
 BASE_DECLARE_FEATURE(kLensSearchHeadersCheckEnabled);
 
diff --git a/ios/chrome/browser/shared/public/features/features.mm b/ios/chrome/browser/shared/public/features/features.mm
index 9ca331fc..c34ffe3 100644
--- a/ios/chrome/browser/shared/public/features/features.mm
+++ b/ios/chrome/browser/shared/public/features/features.mm
@@ -197,6 +197,8 @@
 
 BASE_FEATURE(kLensOverlayNavigationHistory, base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kLensOverlayCustomBottomSheet, base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kLensSearchHeadersCheckEnabled, base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Variations of MIA NTP entrypoint.
diff --git a/ios/chrome/browser/signin/model/BUILD.gn b/ios/chrome/browser/signin/model/BUILD.gn
index d1abd137..ee805b97 100644
--- a/ios/chrome/browser/signin/model/BUILD.gn
+++ b/ios/chrome/browser/signin/model/BUILD.gn
@@ -456,13 +456,13 @@
     "authentication_service_unittest.mm",
     "chrome_account_manager_service_unittest.mm",
     "device_accounts_provider_impl_unittest.mm",
-    "gaia_auth_fetcher_ios_ns_url_session_bridge_unittests.mm",
+    "gaia_auth_fetcher_ios_ns_url_session_bridge_unittest.mm",
     "gaia_auth_fetcher_ios_unittest.mm",
     "pattern_account_restriction_unittest.mm",
     "resized_avatar_cache_unittest.mm",
     "signin_profile_info_updater_unittest.mm",
     "signin_util_unittest.mm",
-    "system_account_updater_unittests.mm",
+    "system_account_updater_unittest.mm",
     "system_identity_manager_unittest.mm",
   ]
   deps = [
diff --git a/ios/chrome/browser/signin/model/DEPS b/ios/chrome/browser/signin/model/DEPS
index 3da22df4..8f5798d 100644
--- a/ios/chrome/browser/signin/model/DEPS
+++ b/ios/chrome/browser/signin/model/DEPS
@@ -27,7 +27,7 @@
     "+ios/chrome/browser/widget_kit/model/features.h",
     "+ios/chrome/browser/widget_kit/model/model_swift.h",
   ],
-  "^system_account_updater_unittests.mm": [
+  "^system_account_updater_unittest.mm": [
     "+ios/chrome/browser/widget_kit/model/features.h",
   ],
 }
diff --git a/ios/chrome/browser/signin/model/gaia_auth_fetcher_ios_ns_url_session_bridge_unittests.mm b/ios/chrome/browser/signin/model/gaia_auth_fetcher_ios_ns_url_session_bridge_unittest.mm
similarity index 100%
rename from ios/chrome/browser/signin/model/gaia_auth_fetcher_ios_ns_url_session_bridge_unittests.mm
rename to ios/chrome/browser/signin/model/gaia_auth_fetcher_ios_ns_url_session_bridge_unittest.mm
diff --git a/ios/chrome/browser/signin/model/system_account_updater_unittests.mm b/ios/chrome/browser/signin/model/system_account_updater_unittest.mm
similarity index 100%
rename from ios/chrome/browser/signin/model/system_account_updater_unittests.mm
rename to ios/chrome/browser/signin/model/system_account_updater_unittest.mm
diff --git a/ios/chrome/common/credential_provider/BUILD.gn b/ios/chrome/common/credential_provider/BUILD.gn
index 65d1b70..7e039f53 100644
--- a/ios/chrome/common/credential_provider/BUILD.gn
+++ b/ios/chrome/common/credential_provider/BUILD.gn
@@ -70,11 +70,11 @@
   sources = [
     "archivable_credential_store_unittest.mm",
     "archivable_credential_unittest.mm",
-    "as_credential_identity_categories_unittests.mm",
+    "as_credential_identity_categories_unittest.mm",
     "credential_store_util_unittest.mm",
-    "memory_credential_store_unittests.mm",
-    "multi_store_credential_store_unittests.mm",
-    "user_defaults_credential_store_unittests.mm",
+    "memory_credential_store_unittest.mm",
+    "multi_store_credential_store_unittest.mm",
+    "user_defaults_credential_store_unittest.mm",
   ]
   deps = [
     ":credential_provider",
diff --git a/ios/chrome/common/credential_provider/as_credential_identity_categories_unittests.mm b/ios/chrome/common/credential_provider/as_credential_identity_categories_unittest.mm
similarity index 100%
rename from ios/chrome/common/credential_provider/as_credential_identity_categories_unittests.mm
rename to ios/chrome/common/credential_provider/as_credential_identity_categories_unittest.mm
diff --git a/ios/chrome/common/credential_provider/memory_credential_store_unittests.mm b/ios/chrome/common/credential_provider/memory_credential_store_unittest.mm
similarity index 100%
rename from ios/chrome/common/credential_provider/memory_credential_store_unittests.mm
rename to ios/chrome/common/credential_provider/memory_credential_store_unittest.mm
diff --git a/ios/chrome/common/credential_provider/multi_store_credential_store_unittests.mm b/ios/chrome/common/credential_provider/multi_store_credential_store_unittest.mm
similarity index 100%
rename from ios/chrome/common/credential_provider/multi_store_credential_store_unittests.mm
rename to ios/chrome/common/credential_provider/multi_store_credential_store_unittest.mm
diff --git a/ios/chrome/common/credential_provider/user_defaults_credential_store_unittests.mm b/ios/chrome/common/credential_provider/user_defaults_credential_store_unittest.mm
similarity index 100%
rename from ios/chrome/common/credential_provider/user_defaults_credential_store_unittests.mm
rename to ios/chrome/common/credential_provider/user_defaults_credential_store_unittest.mm
diff --git a/ios/chrome/test/wpt/cwt_webdriver_app_interface.mm b/ios/chrome/test/wpt/cwt_webdriver_app_interface.mm
index 6afe0c2..d50a5417 100644
--- a/ios/chrome/test/wpt/cwt_webdriver_app_interface.mm
+++ b/ios/chrome/test/wpt/cwt_webdriver_app_interface.mm
@@ -9,6 +9,7 @@
 #import "base/files/file.h"
 #import "base/files/file_path.h"
 #import "base/json/json_writer.h"
+#import "base/strings/string_number_conversions.h"
 #import "base/strings/stringprintf.h"
 #import "base/strings/sys_string_conversions.h"
 #import "base/strings/utf_string_conversions.h"
@@ -35,7 +36,8 @@
 namespace {
 
 NSString* GetIdForWebState(web::WebState* web_state) {
-  return web_state->GetStableIdentifier();
+  return base::SysUTF8ToNSString(base::NumberToString(
+      web_state->GetUniqueIdentifier().ToSessionID().id()));
 }
 
 WebStateList* GetCurrentWebStateList() {
@@ -45,14 +47,21 @@
 }
 
 web::WebState* GetWebStateWithId(NSString* tab_id) {
+  int value = 0;
+  if (!base::StringToInt(base::SysNSStringToUTF8(tab_id), &value)) {
+    return nullptr;
+  }
+
+  web::WebStateID web_state_id = web::WebStateID::FromSerializedValue(value);
+
   WebStateList* web_state_list = GetCurrentWebStateList();
   for (int i = 0; i < web_state_list->count(); ++i) {
     web::WebState* web_state = web_state_list->GetWebStateAt(i);
-    if ([tab_id isEqualToString:GetIdForWebState(web_state)]) {
+    if (web_state_id == web_state->GetUniqueIdentifier()) {
       return web_state;
     }
   }
-  return nil;
+  return nullptr;
 }
 
 // Returns the index of the WebState with the given tab_id, or
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
index 697d0cf..f7227d0 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-80ec3fd9061d21f7833d0e4f934bd99c9bc8c364
\ No newline at end of file
+91897c0366080a70c179989739196f228a797043
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
index 6405319..f98f3f0 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-805cbd01caccda2bc37198ff151d8c85bb38ac8d
\ No newline at end of file
+8d16be69a936d4d34f32c9c671d05c0829e4e390
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index 173f105fc..4daa156 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-fe084958a725fe171912b80f6902b1cabcb45a3b
\ No newline at end of file
+29894faed28273d6911021b0519a00c0446eb39d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
index 89ee59e..1ba8c42 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-c9739407c74da3368accbaebddae9592567216e9
\ No newline at end of file
+b09fab3cce33d8153183a9deee7c5667f6481244
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index 9f5afb9..3acb5661 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-44002f0c0303b50bd6cbd72fadea747ea52899c1
\ No newline at end of file
+05ef254aef4498cb81b1c35e0101353089c0aada
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
index a9c240e9..845caee 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-11377d3fb540e6b331bc7e1b2a2550bbe73f3e95
\ No newline at end of file
+221ee2c6845448796edea782321002958d075fb9
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
index 80d722f..5851407f8 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-29334d9a40511aec4cf6862443e1f17afaa7bc35
\ No newline at end of file
+b901a5876bd0a7e664c246f55c7dc1195181f0e3
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
index a8b47d9..17dbd65 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-8878f94de111cdd125a69df1fe5be6d5d1169cb6
\ No newline at end of file
+8ab08e1897436633daa63b4d99d9a374a6998d23
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
index 5ce5feec..95d87fa4 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-b2eb36bbd8df644693e539dbdb911cc5b36bfa97
\ No newline at end of file
+ce0b83799c714458f50a797c0039b4c26fe35c71
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
index e30aad4..e64ad5a 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-c75fec51126b69b2a7828d315d820faf374ccd45
\ No newline at end of file
+7f3035c2f151f0691bfb9927f153cd7c0530be9c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 6b4c59f..2ade6a6 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-41547247677c4364dafcae439bf58ddb05ef0855
\ No newline at end of file
+9b06ef79487e0abc6c922c7ff65f9369c7a686d0
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
index 487ec148..578cec8 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-5b0b245623004fd47376cca72786ecf12717bc03
\ No newline at end of file
+82ee45af9ba77f6f669f08a3ecf57c425cfbb00d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index ff35c97..c279039 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-3fe46b73d7a216e2009f2e914b565f790248d369
\ No newline at end of file
+d72f9d319629ae69a7a49129e569a4d8b04e6658
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 60560a7..86f42eb 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-ae71be027ee42d4767ed618ad812e22205cd53cf
\ No newline at end of file
+61605648f6c4f283f6a8315fa7f83f2023c0657e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 1de4170..07dd2ede 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-8d0c6506f45371f58eee0dc223403884b190ae68
\ No newline at end of file
+34ccd4e20d7b9175338113966697b53e76bc992d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
index c39fa661..b04f25f5 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-6bfb71494c38d67268605c5e6cc6ebe176b1b4e6
\ No newline at end of file
+70063bd3fef7e27ff03cf46b6ffb0aa0ffe5bfa7
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 7c3e33c8..031bffe 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-9d995cef6dcf60770eae2e29a1f3fa126783f894
\ No newline at end of file
+c8fb4a481cd9e1e8c0bd9855b0fa8e40f54340e2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 4c9bd9e..d6eb338 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-b8ffc54b0870ea3bbd7a471fb897e82362f520b9
\ No newline at end of file
+77d4d9635af005e7fc7da635112ffde2a085c5a1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index d7cf7c8..a248b15f 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-def947efec5af309a356ace067338b680c385314
\ No newline at end of file
+b4ad82af5b0629302a37184ec25d8d5c510ac179
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index 49d36d25..7d9a2f4 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-a310301bbc9e15a95c049a30232f6e3cda8afd17
\ No newline at end of file
+5a2ee315a3051da6b5f8194f9dc27828b0e5b0e2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 1bf085a..4a1cc172 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-f5e44e21ed9c81c2c680583e6941e89f3c5e95b8
\ No newline at end of file
+0f64f7ffe6484a1e3e8ade4d91d622af119e8797
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
index 21a8c88..cfaf3273 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-8a1bb878e107341b961d47e81e73cadd4e9bbae4
\ No newline at end of file
+9ccc5c5e9d8819abe3439bf5df1b77ca9c2afa1f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index f327f703..e54f53db 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-da99add71f408c825599c5ce84629d17af980abd
\ No newline at end of file
+09a44e71a6609d911bae78bf6724c57c4098f5be
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 620f782..d45233808 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-b88d76aa361e73887edf0956f25bcc7ba35017b7
\ No newline at end of file
+b25a78d4b0283e29d0c79c4dd0262cce7ad86126
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 54dd6b4..3629271 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-2c75ed5630df559c428ddba0016193f82a73d95e
\ No newline at end of file
+798ea0cad14128cf37e5f58931cbd07b8eb8953f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
index 4dd1c98..8b937c5 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-054e529596e3ef881ebb3d60b49e9204ca1aca00
\ No newline at end of file
+c4da18d698e479d11298ce60e0f0e6f9878f649b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index efd08dabb..b9c4b6f 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-97929bc8958017b63809bc547dce2500183126ad
\ No newline at end of file
+75ac58fcbc25a3d958876bddaf20da4ef560fe04
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
index cd20392..a67ba38 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-23bbdf8e9c26a325c61b258ca06de4e5b32f3512
\ No newline at end of file
+a1bd9a9f2312f237b87832b8a9214548cf5d23eb
\ No newline at end of file
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index 6772048d..b7e376d4 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -10,9 +10,7 @@
 import("//ios/build/config.gni")
 import("//ios/web/public/js_messaging/optimize_js.gni")
 import("//ios/web_view/features.gni")
-import("//ios/web_view/repack.gni")
-import("//testing/test.gni")
-import("//tools/grit/repack.gni")
+import("//ios/web_view/framework/sources.gni")
 
 group("all_tests") {
   testonly = true
@@ -22,6 +20,22 @@
   ]
 }
 
+# Target used to avoid breaking bot configuration files referring to the old
+# name of the target. Will be removed when the bot configuration files have
+# been updated.
+group("ios_web_view_inttests") {
+  testonly = true
+  deps = [ "//ios/web_view/test:ios_web_view_inttests" ]
+}
+
+# Target used to avoid breaking bot configuration files referring to the old
+# name of the target. Will be removed when the bot configuration files have
+# been updated.
+group("ios_web_view_unittests") {
+  testonly = true
+  deps = [ "//ios/web_view/test:ios_web_view_unittests" ]
+}
+
 config("config") {
   # TODO(crbug.com/40120082): This will only guarantee ios/web_view source files
   # are extension safe. We also need to pass this flag to all all dependencies.
@@ -37,407 +51,6 @@
   ]
 }
 
-# These are defined as vars so they can be shared with different targets below.
-ios_web_view_public_headers = [
-  "public/cwv_omnibox_input.h",
-  "public/cwv_autofill_controller.h",
-  "public/cwv_autofill_controller_delegate.h",
-  "public/cwv_autofill_data_manager.h",
-  "public/cwv_autofill_data_manager_observer.h",
-  "public/cwv_autofill_form.h",
-  "public/cwv_autofill_profile.h",
-  "public/cwv_autofill_suggestion.h",
-  "public/cwv_back_forward_list.h",
-  "public/cwv_back_forward_list_item.h",
-  "public/cwv_cert_status.h",
-  "public/cwv_credential_provider_extension_utils.h",
-  "public/cwv_credit_card.h",
-  "public/cwv_credit_card_saver.h",
-  "public/cwv_credit_card_verifier.h",
-  "public/cwv_defines.h",
-  "public/cwv_download_task.h",
-  "public/cwv_export.h",
-  "public/cwv_favicon.h",
-  "public/cwv_find_in_page_controller.h",
-  "public/cwv_flags.h",
-  "public/cwv_html_element.h",
-  "public/cwv_global_state.h",
-  "public/cwv_identity.h",
-  "public/cwv_leak_check_credential.h",
-  "public/cwv_leak_check_service.h",
-  "public/cwv_leak_check_service_observer.h",
-  "public/cwv_lookalike_url_handler.h",
-  "public/cwv_metrics_provider.h",
-  "public/cwv_navigation_action.h",
-  "public/cwv_navigation_delegate.h",
-  "public/cwv_navigation_response.h",
-  "public/cwv_navigation_type.h",
-  "public/cwv_password.h",
-  "public/cwv_preferences.h",
-  "public/cwv_preview_element_info.h",
-  "public/cwv_reuse_check_service.h",
-  "public/cwv_ssl_error_handler.h",
-  "public/cwv_ssl_status.h",
-  "public/cwv_suggestion_type.h",
-  "public/cwv_sync_controller.h",
-  "public/cwv_sync_controller_data_source.h",
-  "public/cwv_sync_controller_delegate.h",
-  "public/cwv_sync_errors.h",
-  "public/cwv_translation_controller.h",
-  "public/cwv_translation_controller_delegate.h",
-  "public/cwv_translation_language.h",
-  "public/cwv_translation_language_detection_details.h",
-  "public/cwv_translation_policy.h",
-  "public/cwv_trusted_vault_observer.h",
-  "public/cwv_trusted_vault_provider.h",
-  "public/cwv_trusted_vault_utils.h",
-  "public/cwv_ui_delegate.h",
-  "public/cwv_unsafe_url_handler.h",
-  "public/cwv_user_content_controller.h",
-  "public/cwv_user_script.h",
-  "public/cwv_weak_check_utils.h",
-  "public/cwv_web_view.h",
-  "public/cwv_web_view_configuration.h",
-  "public/cwv_x509_certificate.h",
-]
-
-ios_web_view_sources = [
-  "internal/affiliations/web_view_affiliation_service_factory.h",
-  "internal/affiliations/web_view_affiliation_service_factory.mm",
-  "internal/app/application_context.h",
-  "internal/app/application_context.mm",
-  "internal/app/web_view_io_thread.h",
-  "internal/app/web_view_io_thread.mm",
-  "internal/autofill/cwv_autofill_client_ios_bridge.h",
-  "internal/autofill/cwv_autofill_controller+testing.h",
-  "internal/autofill/cwv_autofill_controller.mm",
-  "internal/autofill/cwv_autofill_controller_internal.h",
-  "internal/autofill/cwv_autofill_data_manager.mm",
-  "internal/autofill/cwv_autofill_data_manager_internal.h",
-  "internal/autofill/cwv_autofill_form.mm",
-  "internal/autofill/cwv_autofill_form_internal.h",
-  "internal/autofill/cwv_autofill_prefs.h",
-  "internal/autofill/cwv_autofill_prefs.mm",
-  "internal/autofill/cwv_autofill_profile.mm",
-  "internal/autofill/cwv_autofill_profile_internal.h",
-  "internal/autofill/cwv_autofill_suggestion.mm",
-  "internal/autofill/cwv_autofill_suggestion_internal.h",
-  "internal/autofill/cwv_credit_card.mm",
-  "internal/autofill/cwv_credit_card_internal.h",
-  "internal/autofill/cwv_credit_card_saver.mm",
-  "internal/autofill/cwv_credit_card_saver_internal.h",
-  "internal/autofill/cwv_credit_card_verifier.mm",
-  "internal/autofill/cwv_credit_card_verifier_internal.h",
-  "internal/autofill/cwv_password_affiliation.h",
-  "internal/autofill/cwv_password_affiliation.mm",
-  "internal/autofill/ios_web_view_payments_autofill_client.h",
-  "internal/autofill/ios_web_view_payments_autofill_client.mm",
-  "internal/autofill/web_view_autocomplete_history_manager_factory.h",
-  "internal/autofill/web_view_autocomplete_history_manager_factory.mm",
-  "internal/autofill/web_view_autofill_client_ios.h",
-  "internal/autofill/web_view_autofill_client_ios.mm",
-  "internal/autofill/web_view_autofill_log_router_factory.h",
-  "internal/autofill/web_view_autofill_log_router_factory.mm",
-  "internal/autofill/web_view_personal_data_manager_factory.h",
-  "internal/autofill/web_view_personal_data_manager_factory.mm",
-  "internal/autofill/web_view_strike_database_factory.h",
-  "internal/autofill/web_view_strike_database_factory.mm",
-  "internal/browser_state_keyed_service_factories.h",
-  "internal/browser_state_keyed_service_factories.mm",
-  "internal/browser_state_prefs.h",
-  "internal/browser_state_prefs.mm",
-  "internal/component_updater/web_view_component_updater_configurator.h",
-  "internal/component_updater/web_view_component_updater_configurator.mm",
-  "internal/cwv_back_forward_list.mm",
-  "internal/cwv_back_forward_list_internal.h",
-  "internal/cwv_back_forward_list_item.mm",
-  "internal/cwv_back_forward_list_item_internal.h",
-  "internal/cwv_download_task.mm",
-  "internal/cwv_download_task_internal.h",
-  "internal/cwv_favicon.mm",
-  "internal/cwv_favicon_internal.h",
-  "internal/cwv_find_in_page_controller.mm",
-  "internal/cwv_find_in_page_controller_internal.h",
-  "internal/cwv_flags.mm",
-  "internal/cwv_flags_internal.h",
-  "internal/cwv_global_state_internal.h",
-  "internal/cwv_html_element.mm",
-  "internal/cwv_html_element_internal.h",
-  "internal/cwv_lookalike_url_handler.mm",
-  "internal/cwv_lookalike_url_handler_internal.h",
-  "internal/cwv_navigation_action.mm",
-  "internal/cwv_navigation_action_internal.h",
-  "internal/cwv_navigation_response.mm",
-  "internal/cwv_navigation_response_internal.h",
-  "internal/cwv_navigation_type.mm",
-  "internal/cwv_navigation_type_internal.h",
-  "internal/cwv_omnibox_input.mm",
-  "internal/cwv_preferences.mm",
-  "internal/cwv_preferences_internal.h",
-  "internal/cwv_preview_element_info.mm",
-  "internal/cwv_preview_element_info_internal.h",
-  "internal/cwv_ssl_error_handler.mm",
-  "internal/cwv_ssl_error_handler_internal.h",
-  "internal/cwv_ssl_status.mm",
-  "internal/cwv_ssl_status_internal.h",
-  "internal/cwv_ssl_util.h",
-  "internal/cwv_ssl_util.mm",
-  "internal/cwv_user_content_controller.mm",
-  "internal/cwv_user_content_controller_internal.h",
-  "internal/cwv_user_script.mm",
-  "internal/cwv_web_view.mm",
-  "internal/cwv_web_view_configuration.mm",
-  "internal/cwv_web_view_configuration_internal.h",
-  "internal/cwv_web_view_internal.h",
-  "internal/cwv_x509_certificate.mm",
-  "internal/cwv_x509_certificate_internal.h",
-  "internal/js_messaging/web_view_scripts_java_script_feature.h",
-  "internal/js_messaging/web_view_scripts_java_script_feature.mm",
-  "internal/language/web_view_accept_languages_service_factory.h",
-  "internal/language/web_view_accept_languages_service_factory.mm",
-  "internal/language/web_view_language_model_manager_factory.h",
-  "internal/language/web_view_language_model_manager_factory.mm",
-  "internal/language/web_view_url_language_histogram_factory.h",
-  "internal/language/web_view_url_language_histogram_factory.mm",
-  "internal/metrics/cwv_metrics_provider.mm",
-  "internal/metrics/cwv_metrics_provider_internal.h",
-  "internal/passwords/cwv_credential_provider_extension_utils.mm",
-  "internal/passwords/cwv_leak_check_credential.mm",
-  "internal/passwords/cwv_leak_check_credential_internal.h",
-  "internal/passwords/cwv_leak_check_service.mm",
-  "internal/passwords/cwv_leak_check_service_internal.h",
-  "internal/passwords/cwv_password.mm",
-  "internal/passwords/cwv_password_internal.h",
-  "internal/passwords/cwv_reuse_check_service.mm",
-  "internal/passwords/cwv_reuse_check_service_internal.h",
-  "internal/passwords/cwv_weak_check_utils.mm",
-  "internal/passwords/cwv_weak_check_utils_internal.h",
-  "internal/passwords/web_view_account_password_store_factory.h",
-  "internal/passwords/web_view_account_password_store_factory.mm",
-  "internal/passwords/web_view_bulk_leak_check_service_factory.h",
-  "internal/passwords/web_view_bulk_leak_check_service_factory.mm",
-  "internal/passwords/web_view_password_feature_manager.h",
-  "internal/passwords/web_view_password_feature_manager.mm",
-  "internal/passwords/web_view_password_manager_client.h",
-  "internal/passwords/web_view_password_manager_client.mm",
-  "internal/passwords/web_view_password_manager_log_router_factory.h",
-  "internal/passwords/web_view_password_manager_log_router_factory.mm",
-  "internal/passwords/web_view_password_requirements_service_factory.h",
-  "internal/passwords/web_view_password_requirements_service_factory.mm",
-  "internal/passwords/web_view_password_reuse_manager_factory.h",
-  "internal/passwords/web_view_password_reuse_manager_factory.mm",
-  "internal/passwords/web_view_profile_password_store_factory.h",
-  "internal/passwords/web_view_profile_password_store_factory.mm",
-  "internal/safe_browsing/cwv_unsafe_url_handler.mm",
-  "internal/safe_browsing/cwv_unsafe_url_handler_internal.h",
-  "internal/safe_browsing/web_view_safe_browsing_client.h",
-  "internal/safe_browsing/web_view_safe_browsing_client.mm",
-  "internal/safe_browsing/web_view_safe_browsing_client_factory.h",
-  "internal/safe_browsing/web_view_safe_browsing_client_factory.mm",
-  "internal/safe_browsing/web_view_safe_browsing_helper_factory.h",
-  "internal/safe_browsing/web_view_safe_browsing_helper_factory.mm",
-  "internal/signin/account_capabilities_fetcher_factory_ios_web_view.h",
-  "internal/signin/account_capabilities_fetcher_factory_ios_web_view.mm",
-  "internal/signin/account_capabilities_fetcher_ios_web_view.h",
-  "internal/signin/account_capabilities_fetcher_ios_web_view.mm",
-  "internal/signin/cwv_identity.mm",
-  "internal/signin/ios_web_view_signin_client.h",
-  "internal/signin/ios_web_view_signin_client.mm",
-  "internal/signin/web_view_device_accounts_provider_impl.h",
-  "internal/signin/web_view_device_accounts_provider_impl.mm",
-  "internal/signin/web_view_gaia_auth_fetcher.h",
-  "internal/signin/web_view_gaia_auth_fetcher.mm",
-  "internal/signin/web_view_identity_manager_factory.h",
-  "internal/signin/web_view_identity_manager_factory.mm",
-  "internal/signin/web_view_signin_client_factory.h",
-  "internal/signin/web_view_signin_client_factory.mm",
-  "internal/sync/cwv_sync_controller.mm",
-  "internal/sync/cwv_sync_controller_internal.h",
-  "internal/sync/cwv_trusted_vault_observer.mm",
-  "internal/sync/cwv_trusted_vault_observer_internal.h",
-  "internal/sync/cwv_trusted_vault_utils.mm",
-  "internal/sync/web_view_data_type_store_service_factory.h",
-  "internal/sync/web_view_data_type_store_service_factory.mm",
-  "internal/sync/web_view_device_info_sync_service_factory.h",
-  "internal/sync/web_view_device_info_sync_service_factory.mm",
-  "internal/sync/web_view_gcm_profile_service_factory.h",
-  "internal/sync/web_view_gcm_profile_service_factory.mm",
-  "internal/sync/web_view_instance_id_profile_service_factory.h",
-  "internal/sync/web_view_instance_id_profile_service_factory.mm",
-  "internal/sync/web_view_profile_invalidation_provider_factory.h",
-  "internal/sync/web_view_profile_invalidation_provider_factory.mm",
-  "internal/sync/web_view_sync_client.h",
-  "internal/sync/web_view_sync_client.mm",
-  "internal/sync/web_view_sync_invalidations_service_factory.h",
-  "internal/sync/web_view_sync_invalidations_service_factory.mm",
-  "internal/sync/web_view_sync_service_factory.h",
-  "internal/sync/web_view_sync_service_factory.mm",
-  "internal/sync/web_view_trusted_vault_client.h",
-  "internal/sync/web_view_trusted_vault_client.mm",
-  "internal/translate/cwv_translation_controller.mm",
-  "internal/translate/cwv_translation_controller_internal.h",
-  "internal/translate/cwv_translation_language.mm",
-  "internal/translate/cwv_translation_language_detection_details.mm",
-  "internal/translate/cwv_translation_language_detection_details_internal.h",
-  "internal/translate/cwv_translation_language_internal.h",
-  "internal/translate/cwv_translation_policy.mm",
-  "internal/translate/web_view_translate_client.h",
-  "internal/translate/web_view_translate_client.mm",
-  "internal/translate/web_view_translate_ranker_factory.h",
-  "internal/translate/web_view_translate_ranker_factory.mm",
-  "internal/translate/web_view_translate_service.h",
-  "internal/translate/web_view_translate_service.mm",
-  "internal/utils/nsobject_description_utils.h",
-  "internal/utils/nsobject_description_utils.mm",
-  "internal/web_view_browser_state.h",
-  "internal/web_view_browser_state.mm",
-  "internal/web_view_download_manager.h",
-  "internal/web_view_download_manager.mm",
-  "internal/web_view_java_script_dialog_presenter.h",
-  "internal/web_view_java_script_dialog_presenter.mm",
-  "internal/web_view_message_handler_java_script_feature.h",
-  "internal/web_view_message_handler_java_script_feature.mm",
-  "internal/web_view_url_request_context_getter.h",
-  "internal/web_view_url_request_context_getter.mm",
-  "internal/web_view_web_client.h",
-  "internal/web_view_web_client.mm",
-  "internal/web_view_web_main_delegate.h",
-  "internal/web_view_web_main_delegate.mm",
-  "internal/web_view_web_main_parts.h",
-  "internal/web_view_web_main_parts.mm",
-  "internal/web_view_web_state_policy_decider.h",
-  "internal/web_view_web_state_policy_decider.mm",
-  "internal/webdata_services/web_view_web_data_service_wrapper_factory.h",
-  "internal/webdata_services/web_view_web_data_service_wrapper_factory.mm",
-  "internal/webui/web_view_sync_internals_ui.h",
-  "internal/webui/web_view_sync_internals_ui.mm",
-  "internal/webui/web_view_web_ui_ios_controller_factory.h",
-  "internal/webui/web_view_web_ui_ios_controller_factory.mm",
-  "internal/webui/web_view_web_ui_provider.mm",
-]
-
-ios_web_view_deps = [
-  ":web_view_resources",
-  "//base",
-  "//components/affiliations/core/browser:affiliations",
-  "//components/autofill/core/browser",
-  "//components/autofill/core/common",
-  "//components/autofill/ios/browser",
-  "//components/autofill/ios/browser:util",
-  "//components/autofill/ios/form_util",
-  "//components/autofill/ios/form_util:form_handler_feature",
-  "//components/autofill/ios/form_util:programmatic_form_submission_handler_feature",
-  "//components/browser_sync",
-  "//components/component_updater",
-  "//components/component_updater/installer_policies",
-  "//components/gcm_driver",
-  "//components/history/core/common",
-  "//components/image_fetcher/ios",
-  "//components/infobars/core",
-  "//components/invalidation",
-  "//components/invalidation:legacy_topics_cleanup",
-  "//components/keyed_service/core",
-  "//components/keyed_service/ios",
-  "//components/language/core/browser",
-  "//components/language/core/common",
-  "//components/language/core/language_model",
-  "//components/language/ios/browser",
-  "//components/leveldb_proto",
-  "//components/lookalikes/core",
-  "//components/metrics:library_support",
-  "//components/metrics/demographics",
-  "//components/omnibox/browser:location_bar",
-  "//components/os_crypt/async/browser",
-  "//components/password_manager/core/browser",
-  "//components/password_manager/core/browser/affiliation:affiliation_fetching",
-  "//components/password_manager/core/browser/affiliation:affiliation_match_helper",
-  "//components/password_manager/core/browser/features:password_features",
-  "//components/password_manager/core/browser/generation:core",
-  "//components/password_manager/core/browser/leak_detection",
-  "//components/password_manager/core/browser/sharing",
-  "//components/password_manager/core/common",
-  "//components/password_manager/ios",
-  "//components/plus_addresses/core/browser/settings",
-  "//components/plus_addresses/core/browser/webdata",
-  "//components/pref_registry",
-  "//components/prefs",
-  "//components/profile_metrics",
-  "//components/proxy_config",
-  "//components/safe_browsing/core/common:safe_browsing_prefs",
-  "//components/safe_browsing/ios/browser:allow_list",
-  "//components/security_interstitials/core:unsafe_resource",
-  "//components/security_state/ios",
-  "//components/services/patch:in_process",
-  "//components/services/unzip:in_process",
-  "//components/sessions:session_id",
-  "//components/signin/core/browser",
-  "//components/signin/internal/identity_manager",
-  "//components/signin/ios/browser",
-  "//components/signin/public/base",
-  "//components/signin/public/identity_manager",
-  "//components/signin/public/identity_manager/ios",
-  "//components/signin/public/webdata",
-  "//components/ssl_errors",
-  "//components/strings",
-  "//components/sync",
-  "//components/sync/invalidations",
-  "//components/sync_device_info",
-  "//components/translate/core/browser",
-  "//components/translate/core/common",
-  "//components/translate/ios/browser",
-  "//components/trusted_vault",
-  "//components/unified_consent",
-  "//components/update_client",
-  "//components/update_client:common_impl",
-  "//components/url_formatter",
-  "//components/variations",
-  "//components/variations/net",
-  "//components/version_info",
-  "//components/version_info:version_string",
-  "//components/web_resource",
-  "//components/webdata_services",
-  "//components/webui/flags",
-  "//components/webui/flags:switches",
-  "//google_apis",
-  "//ios/components/credential_provider_extension:password_spec_fetcher",
-  "//ios/components/credential_provider_extension:password_util",
-  "//ios/components/io_thread",
-  "//ios/components/security_interstitials",
-  "//ios/components/security_interstitials/https_only_mode:feature",
-  "//ios/components/security_interstitials/lookalikes",
-  "//ios/components/security_interstitials/safe_browsing",
-  "//ios/components/security_interstitials/safe_browsing:util",
-  "//ios/components/webui:provider",
-  "//ios/components/webui:url_constants",
-  "//ios/components/webui/sync_internals",
-  "//ios/net",
-  "//ios/third_party/webkit",
-  "//ios/web/common",
-  "//ios/web/common:user_agent",
-  "//ios/web/navigation:wk_navigation_util",
-  "//ios/web/public",
-  "//ios/web/public/browsing_data",
-  "//ios/web/public/download",
-  "//ios/web/public/find_in_page",
-  "//ios/web/public/init",
-  "//ios/web/public/js_messaging",
-  "//ios/web/public/security",
-  "//ios/web/public/session",
-  "//ios/web/public/session/proto",
-  "//ios/web/public/web_view_only",
-  "//ios/web/public/webui",
-  "//ios/web/web_state/ui:wk_web_view_configuration_provider_header",
-  "//ios/web/webui",
-  "//ios/web_view/internal/js_messaging:cwv_messaging_js",
-  "//net",
-  "//net:extras",
-  "//services/metrics/public/cpp:metrics_cpp",
-  "//services/network:network_service",
-  "//ui/base",
-  "//ui/display",
-  "//url",
-]
-
 source_set("web_view_sources") {
   sources = ios_web_view_public_headers
   sources += ios_web_view_sources
@@ -495,169 +108,6 @@
   configs += [ ":config" ]
 }
 
-source_set("run_all_unittests") {
-  testonly = true
-  sources = [ "test/run_all_unittests.cc" ]
-  deps = [
-    "//base",
-    "//base/test:test_support",
-    "//mojo/core/embedder",
-  ]
-}
-
-config("unittest_config") {
-  defines = [ "CWV_UNIT_TEST" ]
-}
-
-test("ios_web_view_unittests") {
-  testonly = true
-  sources = [
-    "internal/autofill/cwv_autofill_controller_unittest.mm",
-    "internal/autofill/cwv_autofill_data_manager_unittest.mm",
-    "internal/autofill/cwv_autofill_form_unittest.mm",
-    "internal/autofill/cwv_autofill_profile_unittest.mm",
-    "internal/autofill/cwv_autofill_suggestion_unittest.mm",
-    "internal/autofill/cwv_credit_card_saver_unittest.mm",
-    "internal/autofill/cwv_credit_card_unittest.mm",
-    "internal/autofill/cwv_credit_card_verifier_unittest.mm",
-    "internal/cwv_download_task_unittest.mm",
-    "internal/cwv_favicon_unittest.mm",
-    "internal/cwv_flags_unittest.mm",
-    "internal/cwv_global_state.mm",
-    "internal/cwv_html_element_unittest.mm",
-    "internal/cwv_lookalike_url_handler_unittest.mm",
-    "internal/cwv_omnibox_input_unittest.mm",
-    "internal/cwv_preferences_unittest.mm",
-    "internal/cwv_preview_element_info_unittest.mm",
-    "internal/cwv_ssl_error_handler_unittest.mm",
-    "internal/cwv_ssl_status_unittest.mm",
-    "internal/cwv_web_view_configuration_internal_unittest.mm",
-    "internal/cwv_web_view_unittest.mm",
-    "internal/cwv_x509_certificate_unittest.mm",
-    "internal/metrics/cwv_metrics_provider_unittest.mm",
-    "internal/passwords/cwv_credential_provider_extension_utils_unittest.mm",
-    "internal/passwords/cwv_leak_check_credential_unittest.mm",
-    "internal/passwords/cwv_leak_check_service_unittest.mm",
-    "internal/passwords/cwv_password_unittest.mm",
-    "internal/passwords/cwv_reuse_check_service_unittest.mm",
-    "internal/passwords/cwv_weak_check_utils_unittest.mm",
-    "internal/passwords/web_view_password_manager_client_unittest.mm",
-    "internal/safe_browsing/cwv_unsafe_url_handler_unittest.mm",
-    "internal/signin/account_capabilities_fetcher_ios_web_view_unittest.mm",
-    "internal/signin/cwv_identity_unittest.mm",
-    "internal/signin/web_view_device_accounts_provider_impl_unittest.mm",
-    "internal/signin/web_view_gaia_auth_fetcher_unittest.mm",
-    "internal/sync/cwv_sync_controller_unittest.mm",
-    "internal/sync/cwv_trusted_vault_observer_unittest.mm",
-    "internal/translate/cwv_translation_controller_unittest.mm",
-    "internal/translate/cwv_translation_language_unittest.mm",
-    "internal/translate/cwv_translation_policy_unittest.mm",
-    "internal/web_view_web_client_unittest.mm",
-  ]
-
-  configs += [ ":unittest_config" ]
-
-  deps = [
-    ":run_all_unittests",
-    ":web_view_sources",
-    "test:test_support",
-    "//base/test:test_support",
-    "//components/affiliations/core/browser:test_support",
-    "//components/autofill/core/browser:test_support",
-    "//components/autofill/ios/browser:test_support",
-    "//components/autofill/ios/form_util:test_support",
-    "//components/language_detection/core",
-    "//components/password_manager/core/browser:test_support",
-    "//components/password_manager/core/browser/leak_detection:test_support",
-    "//components/prefs:test_support",
-    "//components/signin/public/base:test_support",
-    "//components/signin/public/identity_manager:test_support",
-    "//components/sync:test_support",
-    "//components/sync_device_info:test_support",
-    "//components/translate/core/browser:test_support",
-    "//components/translate/core/language_detection",
-    "//ios/web/common:uikit",
-    "//ios/web/common:web_view_creation_util",
-    "//ios/web/public/js_messaging",
-    "//ios/web/public/security",
-    "//ios/web/public/test",
-    "//ios/web/public/test:test_fixture",
-    "//net:test_support",
-    "//testing/gtest",
-    "//third_party/ocmock",
-  ]
-
-  assert_no_deps = ios_assert_no_deps
-}
-
-test("ios_web_view_inttests") {
-  testonly = true
-
-  deps = [ "//ios/web_view/test:inttests" ]
-
-  bundle_deps = [ ":web_view+bundle" ]
-
-  assert_no_deps = ios_assert_no_deps
-}
-
-repack_locales("repack_locales") {
-  visibility = [ ":web_view_resources" ]
-  source_patterns = [
-    "${root_gen_dir}/components/strings/components_strings_",
-    "${root_gen_dir}/components/strings/components_locale_settings_",
-    "${root_gen_dir}/ui/strings/app_locale_settings_",
-    "${root_gen_dir}/ui/strings/ax_strings_",
-    "${root_gen_dir}/ui/strings/ui_strings_",
-  ]
-
-  deps = [
-    "//components/strings:components_locale_settings",
-    "//components/strings:components_strings",
-    "//ui/strings:app_locale_settings",
-    "//ui/strings:ax_strings",
-    "//ui/strings:ui_strings",
-  ]
-  input_locales = platform_pak_locales
-  output_locales = locales_as_apple_outputs
-  copy_data_to_bundle = true
-}
-
-repack("repack_resources") {
-  visibility = [ ":web_view_resources" ]
-  deps = [
-    "//components/resources:components_resources",
-    "//components/sync/service/resources",
-    "//ios/web:resources",
-    "//ui/webui/resources",
-  ]
-  sources = [
-    "$root_gen_dir/components/components_resources.pak",
-    "$root_gen_dir/components/sync_service_sync_internals_resources.pak",
-    "$root_gen_dir/ios/web/ios_web_resources.pak",
-    "$root_gen_dir/ui/webui/resources/webui_resources.pak",
-  ]
-  output = "$target_gen_dir/web_view_resources.pak"
-  copy_data_to_bundle = true
-}
-
-ios_web_view_repack_all_scales("repack_scalable_resources") {
-  visibility = [ ":web_view_resources" ]
-  scales = [
-    "100",
-    "200",
-    "300",
-  ]
-}
-
-group("web_view_resources") {
-  visibility = [ "//ios/web_view:*" ]
-  deps = [
-    ":repack_locales",
-    ":repack_resources",
-    ":repack_scalable_resources",
-  ]
-}
-
 _package_dir = "$root_out_dir/ios_web_view"
 
 action("ios_web_view_generate_license") {
diff --git a/ios/web_view/framework/sources.gni b/ios/web_view/framework/sources.gni
new file mode 100644
index 0000000..2b42e25
--- /dev/null
+++ b/ios/web_view/framework/sources.gni
@@ -0,0 +1,404 @@
+# 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.
+
+# These are defined as vars so they can be shared with different targets below.
+ios_web_view_public_headers = [
+  "//ios/web_view/public/cwv_omnibox_input.h",
+  "//ios/web_view/public/cwv_autofill_controller.h",
+  "//ios/web_view/public/cwv_autofill_controller_delegate.h",
+  "//ios/web_view/public/cwv_autofill_data_manager.h",
+  "//ios/web_view/public/cwv_autofill_data_manager_observer.h",
+  "//ios/web_view/public/cwv_autofill_form.h",
+  "//ios/web_view/public/cwv_autofill_profile.h",
+  "//ios/web_view/public/cwv_autofill_suggestion.h",
+  "//ios/web_view/public/cwv_back_forward_list.h",
+  "//ios/web_view/public/cwv_back_forward_list_item.h",
+  "//ios/web_view/public/cwv_cert_status.h",
+  "//ios/web_view/public/cwv_credential_provider_extension_utils.h",
+  "//ios/web_view/public/cwv_credit_card.h",
+  "//ios/web_view/public/cwv_credit_card_saver.h",
+  "//ios/web_view/public/cwv_credit_card_verifier.h",
+  "//ios/web_view/public/cwv_defines.h",
+  "//ios/web_view/public/cwv_download_task.h",
+  "//ios/web_view/public/cwv_export.h",
+  "//ios/web_view/public/cwv_favicon.h",
+  "//ios/web_view/public/cwv_find_in_page_controller.h",
+  "//ios/web_view/public/cwv_flags.h",
+  "//ios/web_view/public/cwv_html_element.h",
+  "//ios/web_view/public/cwv_global_state.h",
+  "//ios/web_view/public/cwv_identity.h",
+  "//ios/web_view/public/cwv_leak_check_credential.h",
+  "//ios/web_view/public/cwv_leak_check_service.h",
+  "//ios/web_view/public/cwv_leak_check_service_observer.h",
+  "//ios/web_view/public/cwv_lookalike_url_handler.h",
+  "//ios/web_view/public/cwv_metrics_provider.h",
+  "//ios/web_view/public/cwv_navigation_action.h",
+  "//ios/web_view/public/cwv_navigation_delegate.h",
+  "//ios/web_view/public/cwv_navigation_response.h",
+  "//ios/web_view/public/cwv_navigation_type.h",
+  "//ios/web_view/public/cwv_password.h",
+  "//ios/web_view/public/cwv_preferences.h",
+  "//ios/web_view/public/cwv_preview_element_info.h",
+  "//ios/web_view/public/cwv_reuse_check_service.h",
+  "//ios/web_view/public/cwv_ssl_error_handler.h",
+  "//ios/web_view/public/cwv_ssl_status.h",
+  "//ios/web_view/public/cwv_suggestion_type.h",
+  "//ios/web_view/public/cwv_sync_controller.h",
+  "//ios/web_view/public/cwv_sync_controller_data_source.h",
+  "//ios/web_view/public/cwv_sync_controller_delegate.h",
+  "//ios/web_view/public/cwv_sync_errors.h",
+  "//ios/web_view/public/cwv_translation_controller.h",
+  "//ios/web_view/public/cwv_translation_controller_delegate.h",
+  "//ios/web_view/public/cwv_translation_language.h",
+  "//ios/web_view/public/cwv_translation_language_detection_details.h",
+  "//ios/web_view/public/cwv_translation_policy.h",
+  "//ios/web_view/public/cwv_trusted_vault_observer.h",
+  "//ios/web_view/public/cwv_trusted_vault_provider.h",
+  "//ios/web_view/public/cwv_trusted_vault_utils.h",
+  "//ios/web_view/public/cwv_ui_delegate.h",
+  "//ios/web_view/public/cwv_unsafe_url_handler.h",
+  "//ios/web_view/public/cwv_user_content_controller.h",
+  "//ios/web_view/public/cwv_user_script.h",
+  "//ios/web_view/public/cwv_weak_check_utils.h",
+  "//ios/web_view/public/cwv_web_view.h",
+  "//ios/web_view/public/cwv_web_view_configuration.h",
+  "//ios/web_view/public/cwv_x509_certificate.h",
+]
+
+ios_web_view_sources = [
+  "//ios/web_view/internal/affiliations/web_view_affiliation_service_factory.h",
+  "//ios/web_view/internal/affiliations/web_view_affiliation_service_factory.mm",
+  "//ios/web_view/internal/app/application_context.h",
+  "//ios/web_view/internal/app/application_context.mm",
+  "//ios/web_view/internal/app/web_view_io_thread.h",
+  "//ios/web_view/internal/app/web_view_io_thread.mm",
+  "//ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h",
+  "//ios/web_view/internal/autofill/cwv_autofill_controller+testing.h",
+  "//ios/web_view/internal/autofill/cwv_autofill_controller.mm",
+  "//ios/web_view/internal/autofill/cwv_autofill_controller_internal.h",
+  "//ios/web_view/internal/autofill/cwv_autofill_data_manager.mm",
+  "//ios/web_view/internal/autofill/cwv_autofill_data_manager_internal.h",
+  "//ios/web_view/internal/autofill/cwv_autofill_form.mm",
+  "//ios/web_view/internal/autofill/cwv_autofill_form_internal.h",
+  "//ios/web_view/internal/autofill/cwv_autofill_prefs.h",
+  "//ios/web_view/internal/autofill/cwv_autofill_prefs.mm",
+  "//ios/web_view/internal/autofill/cwv_autofill_profile.mm",
+  "//ios/web_view/internal/autofill/cwv_autofill_profile_internal.h",
+  "//ios/web_view/internal/autofill/cwv_autofill_suggestion.mm",
+  "//ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h",
+  "//ios/web_view/internal/autofill/cwv_credit_card.mm",
+  "//ios/web_view/internal/autofill/cwv_credit_card_internal.h",
+  "//ios/web_view/internal/autofill/cwv_credit_card_saver.mm",
+  "//ios/web_view/internal/autofill/cwv_credit_card_saver_internal.h",
+  "//ios/web_view/internal/autofill/cwv_credit_card_verifier.mm",
+  "//ios/web_view/internal/autofill/cwv_credit_card_verifier_internal.h",
+  "//ios/web_view/internal/autofill/cwv_password_affiliation.h",
+  "//ios/web_view/internal/autofill/cwv_password_affiliation.mm",
+  "//ios/web_view/internal/autofill/ios_web_view_payments_autofill_client.h",
+  "//ios/web_view/internal/autofill/ios_web_view_payments_autofill_client.mm",
+  "//ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.h",
+  "//ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.mm",
+  "//ios/web_view/internal/autofill/web_view_autofill_client_ios.h",
+  "//ios/web_view/internal/autofill/web_view_autofill_client_ios.mm",
+  "//ios/web_view/internal/autofill/web_view_autofill_log_router_factory.h",
+  "//ios/web_view/internal/autofill/web_view_autofill_log_router_factory.mm",
+  "//ios/web_view/internal/autofill/web_view_personal_data_manager_factory.h",
+  "//ios/web_view/internal/autofill/web_view_personal_data_manager_factory.mm",
+  "//ios/web_view/internal/autofill/web_view_strike_database_factory.h",
+  "//ios/web_view/internal/autofill/web_view_strike_database_factory.mm",
+  "//ios/web_view/internal/browser_state_keyed_service_factories.h",
+  "//ios/web_view/internal/browser_state_keyed_service_factories.mm",
+  "//ios/web_view/internal/browser_state_prefs.h",
+  "//ios/web_view/internal/browser_state_prefs.mm",
+  "//ios/web_view/internal/component_updater/web_view_component_updater_configurator.h",
+  "//ios/web_view/internal/component_updater/web_view_component_updater_configurator.mm",
+  "//ios/web_view/internal/cwv_back_forward_list.mm",
+  "//ios/web_view/internal/cwv_back_forward_list_internal.h",
+  "//ios/web_view/internal/cwv_back_forward_list_item.mm",
+  "//ios/web_view/internal/cwv_back_forward_list_item_internal.h",
+  "//ios/web_view/internal/cwv_download_task.mm",
+  "//ios/web_view/internal/cwv_download_task_internal.h",
+  "//ios/web_view/internal/cwv_favicon.mm",
+  "//ios/web_view/internal/cwv_favicon_internal.h",
+  "//ios/web_view/internal/cwv_find_in_page_controller.mm",
+  "//ios/web_view/internal/cwv_find_in_page_controller_internal.h",
+  "//ios/web_view/internal/cwv_flags.mm",
+  "//ios/web_view/internal/cwv_flags_internal.h",
+  "//ios/web_view/internal/cwv_global_state_internal.h",
+  "//ios/web_view/internal/cwv_html_element.mm",
+  "//ios/web_view/internal/cwv_html_element_internal.h",
+  "//ios/web_view/internal/cwv_lookalike_url_handler.mm",
+  "//ios/web_view/internal/cwv_lookalike_url_handler_internal.h",
+  "//ios/web_view/internal/cwv_navigation_action.mm",
+  "//ios/web_view/internal/cwv_navigation_action_internal.h",
+  "//ios/web_view/internal/cwv_navigation_response.mm",
+  "//ios/web_view/internal/cwv_navigation_response_internal.h",
+  "//ios/web_view/internal/cwv_navigation_type.mm",
+  "//ios/web_view/internal/cwv_navigation_type_internal.h",
+  "//ios/web_view/internal/cwv_omnibox_input.mm",
+  "//ios/web_view/internal/cwv_preferences.mm",
+  "//ios/web_view/internal/cwv_preferences_internal.h",
+  "//ios/web_view/internal/cwv_preview_element_info.mm",
+  "//ios/web_view/internal/cwv_preview_element_info_internal.h",
+  "//ios/web_view/internal/cwv_ssl_error_handler.mm",
+  "//ios/web_view/internal/cwv_ssl_error_handler_internal.h",
+  "//ios/web_view/internal/cwv_ssl_status.mm",
+  "//ios/web_view/internal/cwv_ssl_status_internal.h",
+  "//ios/web_view/internal/cwv_ssl_util.h",
+  "//ios/web_view/internal/cwv_ssl_util.mm",
+  "//ios/web_view/internal/cwv_user_content_controller.mm",
+  "//ios/web_view/internal/cwv_user_content_controller_internal.h",
+  "//ios/web_view/internal/cwv_user_script.mm",
+  "//ios/web_view/internal/cwv_web_view.mm",
+  "//ios/web_view/internal/cwv_web_view_configuration.mm",
+  "//ios/web_view/internal/cwv_web_view_configuration_internal.h",
+  "//ios/web_view/internal/cwv_web_view_internal.h",
+  "//ios/web_view/internal/cwv_x509_certificate.mm",
+  "//ios/web_view/internal/cwv_x509_certificate_internal.h",
+  "//ios/web_view/internal/js_messaging/web_view_scripts_java_script_feature.h",
+  "//ios/web_view/internal/js_messaging/web_view_scripts_java_script_feature.mm",
+  "//ios/web_view/internal/language/web_view_accept_languages_service_factory.h",
+  "//ios/web_view/internal/language/web_view_accept_languages_service_factory.mm",
+  "//ios/web_view/internal/language/web_view_language_model_manager_factory.h",
+  "//ios/web_view/internal/language/web_view_language_model_manager_factory.mm",
+  "//ios/web_view/internal/language/web_view_url_language_histogram_factory.h",
+  "//ios/web_view/internal/language/web_view_url_language_histogram_factory.mm",
+  "//ios/web_view/internal/metrics/cwv_metrics_provider.mm",
+  "//ios/web_view/internal/metrics/cwv_metrics_provider_internal.h",
+  "//ios/web_view/internal/passwords/cwv_credential_provider_extension_utils.mm",
+  "//ios/web_view/internal/passwords/cwv_leak_check_credential.mm",
+  "//ios/web_view/internal/passwords/cwv_leak_check_credential_internal.h",
+  "//ios/web_view/internal/passwords/cwv_leak_check_service.mm",
+  "//ios/web_view/internal/passwords/cwv_leak_check_service_internal.h",
+  "//ios/web_view/internal/passwords/cwv_password.mm",
+  "//ios/web_view/internal/passwords/cwv_password_internal.h",
+  "//ios/web_view/internal/passwords/cwv_reuse_check_service.mm",
+  "//ios/web_view/internal/passwords/cwv_reuse_check_service_internal.h",
+  "//ios/web_view/internal/passwords/cwv_weak_check_utils.mm",
+  "//ios/web_view/internal/passwords/cwv_weak_check_utils_internal.h",
+  "//ios/web_view/internal/passwords/web_view_account_password_store_factory.h",
+  "//ios/web_view/internal/passwords/web_view_account_password_store_factory.mm",
+  "//ios/web_view/internal/passwords/web_view_bulk_leak_check_service_factory.h",
+  "//ios/web_view/internal/passwords/web_view_bulk_leak_check_service_factory.mm",
+  "//ios/web_view/internal/passwords/web_view_password_feature_manager.h",
+  "//ios/web_view/internal/passwords/web_view_password_feature_manager.mm",
+  "//ios/web_view/internal/passwords/web_view_password_manager_client.h",
+  "//ios/web_view/internal/passwords/web_view_password_manager_client.mm",
+  "//ios/web_view/internal/passwords/web_view_password_manager_log_router_factory.h",
+  "//ios/web_view/internal/passwords/web_view_password_manager_log_router_factory.mm",
+  "//ios/web_view/internal/passwords/web_view_password_requirements_service_factory.h",
+  "//ios/web_view/internal/passwords/web_view_password_requirements_service_factory.mm",
+  "//ios/web_view/internal/passwords/web_view_password_reuse_manager_factory.h",
+  "//ios/web_view/internal/passwords/web_view_password_reuse_manager_factory.mm",
+  "//ios/web_view/internal/passwords/web_view_profile_password_store_factory.h",
+  "//ios/web_view/internal/passwords/web_view_profile_password_store_factory.mm",
+  "//ios/web_view/internal/safe_browsing/cwv_unsafe_url_handler.mm",
+  "//ios/web_view/internal/safe_browsing/cwv_unsafe_url_handler_internal.h",
+  "//ios/web_view/internal/safe_browsing/web_view_safe_browsing_client.h",
+  "//ios/web_view/internal/safe_browsing/web_view_safe_browsing_client.mm",
+  "//ios/web_view/internal/safe_browsing/web_view_safe_browsing_client_factory.h",
+  "//ios/web_view/internal/safe_browsing/web_view_safe_browsing_client_factory.mm",
+  "//ios/web_view/internal/safe_browsing/web_view_safe_browsing_helper_factory.h",
+  "//ios/web_view/internal/safe_browsing/web_view_safe_browsing_helper_factory.mm",
+  "//ios/web_view/internal/signin/account_capabilities_fetcher_factory_ios_web_view.h",
+  "//ios/web_view/internal/signin/account_capabilities_fetcher_factory_ios_web_view.mm",
+  "//ios/web_view/internal/signin/account_capabilities_fetcher_ios_web_view.h",
+  "//ios/web_view/internal/signin/account_capabilities_fetcher_ios_web_view.mm",
+  "//ios/web_view/internal/signin/cwv_identity.mm",
+  "//ios/web_view/internal/signin/ios_web_view_signin_client.h",
+  "//ios/web_view/internal/signin/ios_web_view_signin_client.mm",
+  "//ios/web_view/internal/signin/web_view_device_accounts_provider_impl.h",
+  "//ios/web_view/internal/signin/web_view_device_accounts_provider_impl.mm",
+  "//ios/web_view/internal/signin/web_view_gaia_auth_fetcher.h",
+  "//ios/web_view/internal/signin/web_view_gaia_auth_fetcher.mm",
+  "//ios/web_view/internal/signin/web_view_identity_manager_factory.h",
+  "//ios/web_view/internal/signin/web_view_identity_manager_factory.mm",
+  "//ios/web_view/internal/signin/web_view_signin_client_factory.h",
+  "//ios/web_view/internal/signin/web_view_signin_client_factory.mm",
+  "//ios/web_view/internal/sync/cwv_sync_controller.mm",
+  "//ios/web_view/internal/sync/cwv_sync_controller_internal.h",
+  "//ios/web_view/internal/sync/cwv_trusted_vault_observer.mm",
+  "//ios/web_view/internal/sync/cwv_trusted_vault_observer_internal.h",
+  "//ios/web_view/internal/sync/cwv_trusted_vault_utils.mm",
+  "//ios/web_view/internal/sync/web_view_data_type_store_service_factory.h",
+  "//ios/web_view/internal/sync/web_view_data_type_store_service_factory.mm",
+  "//ios/web_view/internal/sync/web_view_device_info_sync_service_factory.h",
+  "//ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm",
+  "//ios/web_view/internal/sync/web_view_gcm_profile_service_factory.h",
+  "//ios/web_view/internal/sync/web_view_gcm_profile_service_factory.mm",
+  "//ios/web_view/internal/sync/web_view_instance_id_profile_service_factory.h",
+  "//ios/web_view/internal/sync/web_view_instance_id_profile_service_factory.mm",
+  "//ios/web_view/internal/sync/web_view_profile_invalidation_provider_factory.h",
+  "//ios/web_view/internal/sync/web_view_profile_invalidation_provider_factory.mm",
+  "//ios/web_view/internal/sync/web_view_sync_client.h",
+  "//ios/web_view/internal/sync/web_view_sync_client.mm",
+  "//ios/web_view/internal/sync/web_view_sync_invalidations_service_factory.h",
+  "//ios/web_view/internal/sync/web_view_sync_invalidations_service_factory.mm",
+  "//ios/web_view/internal/sync/web_view_sync_service_factory.h",
+  "//ios/web_view/internal/sync/web_view_sync_service_factory.mm",
+  "//ios/web_view/internal/sync/web_view_trusted_vault_client.h",
+  "//ios/web_view/internal/sync/web_view_trusted_vault_client.mm",
+  "//ios/web_view/internal/translate/cwv_translation_controller.mm",
+  "//ios/web_view/internal/translate/cwv_translation_controller_internal.h",
+  "//ios/web_view/internal/translate/cwv_translation_language.mm",
+  "//ios/web_view/internal/translate/cwv_translation_language_detection_details.mm",
+  "//ios/web_view/internal/translate/cwv_translation_language_detection_details_internal.h",
+  "//ios/web_view/internal/translate/cwv_translation_language_internal.h",
+  "//ios/web_view/internal/translate/cwv_translation_policy.mm",
+  "//ios/web_view/internal/translate/web_view_translate_client.h",
+  "//ios/web_view/internal/translate/web_view_translate_client.mm",
+  "//ios/web_view/internal/translate/web_view_translate_ranker_factory.h",
+  "//ios/web_view/internal/translate/web_view_translate_ranker_factory.mm",
+  "//ios/web_view/internal/translate/web_view_translate_service.h",
+  "//ios/web_view/internal/translate/web_view_translate_service.mm",
+  "//ios/web_view/internal/utils/nsobject_description_utils.h",
+  "//ios/web_view/internal/utils/nsobject_description_utils.mm",
+  "//ios/web_view/internal/web_view_browser_state.h",
+  "//ios/web_view/internal/web_view_browser_state.mm",
+  "//ios/web_view/internal/web_view_download_manager.h",
+  "//ios/web_view/internal/web_view_download_manager.mm",
+  "//ios/web_view/internal/web_view_java_script_dialog_presenter.h",
+  "//ios/web_view/internal/web_view_java_script_dialog_presenter.mm",
+  "//ios/web_view/internal/web_view_message_handler_java_script_feature.h",
+  "//ios/web_view/internal/web_view_message_handler_java_script_feature.mm",
+  "//ios/web_view/internal/web_view_url_request_context_getter.h",
+  "//ios/web_view/internal/web_view_url_request_context_getter.mm",
+  "//ios/web_view/internal/web_view_web_client.h",
+  "//ios/web_view/internal/web_view_web_client.mm",
+  "//ios/web_view/internal/web_view_web_main_delegate.h",
+  "//ios/web_view/internal/web_view_web_main_delegate.mm",
+  "//ios/web_view/internal/web_view_web_main_parts.h",
+  "//ios/web_view/internal/web_view_web_main_parts.mm",
+  "//ios/web_view/internal/web_view_web_state_policy_decider.h",
+  "//ios/web_view/internal/web_view_web_state_policy_decider.mm",
+  "//ios/web_view/internal/webdata_services/web_view_web_data_service_wrapper_factory.h",
+  "//ios/web_view/internal/webdata_services/web_view_web_data_service_wrapper_factory.mm",
+  "//ios/web_view/internal/webui/web_view_sync_internals_ui.h",
+  "//ios/web_view/internal/webui/web_view_sync_internals_ui.mm",
+  "//ios/web_view/internal/webui/web_view_web_ui_ios_controller_factory.h",
+  "//ios/web_view/internal/webui/web_view_web_ui_ios_controller_factory.mm",
+  "//ios/web_view/internal/webui/web_view_web_ui_provider.mm",
+]
+
+ios_web_view_deps = [
+  "//base",
+  "//components/affiliations/core/browser:affiliations",
+  "//components/autofill/core/browser",
+  "//components/autofill/core/common",
+  "//components/autofill/ios/browser",
+  "//components/autofill/ios/browser:util",
+  "//components/autofill/ios/form_util",
+  "//components/autofill/ios/form_util:form_handler_feature",
+  "//components/autofill/ios/form_util:programmatic_form_submission_handler_feature",
+  "//components/browser_sync",
+  "//components/component_updater",
+  "//components/component_updater/installer_policies",
+  "//components/gcm_driver",
+  "//components/history/core/common",
+  "//components/image_fetcher/ios",
+  "//components/infobars/core",
+  "//components/invalidation",
+  "//components/invalidation:legacy_topics_cleanup",
+  "//components/keyed_service/core",
+  "//components/keyed_service/ios",
+  "//components/language/core/browser",
+  "//components/language/core/common",
+  "//components/language/core/language_model",
+  "//components/language/ios/browser",
+  "//components/leveldb_proto",
+  "//components/lookalikes/core",
+  "//components/metrics:library_support",
+  "//components/metrics/demographics",
+  "//components/omnibox/browser:location_bar",
+  "//components/os_crypt/async/browser",
+  "//components/password_manager/core/browser",
+  "//components/password_manager/core/browser/affiliation:affiliation_fetching",
+  "//components/password_manager/core/browser/affiliation:affiliation_match_helper",
+  "//components/password_manager/core/browser/features:password_features",
+  "//components/password_manager/core/browser/generation:core",
+  "//components/password_manager/core/browser/leak_detection",
+  "//components/password_manager/core/browser/sharing",
+  "//components/password_manager/core/common",
+  "//components/password_manager/ios",
+  "//components/plus_addresses/core/browser/settings",
+  "//components/plus_addresses/core/browser/webdata",
+  "//components/pref_registry",
+  "//components/prefs",
+  "//components/profile_metrics",
+  "//components/proxy_config",
+  "//components/safe_browsing/core/common:safe_browsing_prefs",
+  "//components/safe_browsing/ios/browser:allow_list",
+  "//components/security_interstitials/core:unsafe_resource",
+  "//components/security_state/ios",
+  "//components/services/patch:in_process",
+  "//components/services/unzip:in_process",
+  "//components/sessions:session_id",
+  "//components/signin/core/browser",
+  "//components/signin/internal/identity_manager",
+  "//components/signin/ios/browser",
+  "//components/signin/public/base",
+  "//components/signin/public/identity_manager",
+  "//components/signin/public/identity_manager/ios",
+  "//components/signin/public/webdata",
+  "//components/ssl_errors",
+  "//components/strings",
+  "//components/sync",
+  "//components/sync/invalidations",
+  "//components/sync_device_info",
+  "//components/translate/core/browser",
+  "//components/translate/core/common",
+  "//components/translate/ios/browser",
+  "//components/trusted_vault",
+  "//components/unified_consent",
+  "//components/update_client",
+  "//components/update_client:common_impl",
+  "//components/url_formatter",
+  "//components/variations",
+  "//components/variations/net",
+  "//components/version_info",
+  "//components/version_info:version_string",
+  "//components/web_resource",
+  "//components/webdata_services",
+  "//components/webui/flags",
+  "//components/webui/flags:switches",
+  "//google_apis",
+  "//ios/components/credential_provider_extension:password_spec_fetcher",
+  "//ios/components/credential_provider_extension:password_util",
+  "//ios/components/io_thread",
+  "//ios/components/security_interstitials",
+  "//ios/components/security_interstitials/https_only_mode:feature",
+  "//ios/components/security_interstitials/lookalikes",
+  "//ios/components/security_interstitials/safe_browsing",
+  "//ios/components/security_interstitials/safe_browsing:util",
+  "//ios/components/webui:provider",
+  "//ios/components/webui:url_constants",
+  "//ios/components/webui/sync_internals",
+  "//ios/net",
+  "//ios/third_party/webkit",
+  "//ios/web/common",
+  "//ios/web/common:user_agent",
+  "//ios/web/navigation:wk_navigation_util",
+  "//ios/web/public",
+  "//ios/web/public/browsing_data",
+  "//ios/web/public/download",
+  "//ios/web/public/find_in_page",
+  "//ios/web/public/init",
+  "//ios/web/public/js_messaging",
+  "//ios/web/public/security",
+  "//ios/web/public/session",
+  "//ios/web/public/session/proto",
+  "//ios/web/public/web_view_only",
+  "//ios/web/public/webui",
+  "//ios/web/web_state/ui:wk_web_view_configuration_provider_header",
+  "//ios/web/webui",
+  "//ios/web_view/internal/js_messaging:cwv_messaging_js",
+  "//ios/web_view/resources",
+  "//net",
+  "//net:extras",
+  "//services/metrics/public/cpp:metrics_cpp",
+  "//services/network:network_service",
+  "//ui/base",
+  "//ui/display",
+  "//url",
+]
diff --git a/ios/web_view/resources/BUILD.gn b/ios/web_view/resources/BUILD.gn
new file mode 100644
index 0000000..f4e6dca
--- /dev/null
+++ b/ios/web_view/resources/BUILD.gn
@@ -0,0 +1,64 @@
+# Copyright 2025 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//ios/web_view/resources/repack.gni")
+import("//tools/grit/repack.gni")
+
+group("resources") {
+  visibility = [ "//ios/web_view:*" ]
+  deps = [
+    ":repack_locales",
+    ":repack_resources",
+    ":repack_scalable_resources",
+  ]
+}
+
+repack_locales("repack_locales") {
+  visibility = [ ":resources" ]
+  source_patterns = [
+    "${root_gen_dir}/components/strings/components_strings_",
+    "${root_gen_dir}/components/strings/components_locale_settings_",
+    "${root_gen_dir}/ui/strings/app_locale_settings_",
+    "${root_gen_dir}/ui/strings/ax_strings_",
+    "${root_gen_dir}/ui/strings/ui_strings_",
+  ]
+
+  deps = [
+    "//components/strings:components_locale_settings",
+    "//components/strings:components_strings",
+    "//ui/strings:app_locale_settings",
+    "//ui/strings:ax_strings",
+    "//ui/strings:ui_strings",
+  ]
+  input_locales = platform_pak_locales
+  output_locales = locales_as_apple_outputs
+  copy_data_to_bundle = true
+}
+
+repack("repack_resources") {
+  visibility = [ ":resources" ]
+  deps = [
+    "//components/resources:components_resources",
+    "//components/sync/service/resources",
+    "//ios/web:resources",
+    "//ui/webui/resources",
+  ]
+  sources = [
+    "$root_gen_dir/components/components_resources.pak",
+    "$root_gen_dir/components/sync_service_sync_internals_resources.pak",
+    "$root_gen_dir/ios/web/ios_web_resources.pak",
+    "$root_gen_dir/ui/webui/resources/webui_resources.pak",
+  ]
+  output = "$target_gen_dir/web_view_resources.pak"
+  copy_data_to_bundle = true
+}
+
+ios_web_view_repack_all_scales("repack_scalable_resources") {
+  visibility = [ ":resources" ]
+  scales = [
+    "100",
+    "200",
+    "300",
+  ]
+}
diff --git a/ios/web_view/repack.gni b/ios/web_view/resources/repack.gni
similarity index 100%
rename from ios/web_view/repack.gni
rename to ios/web_view/resources/repack.gni
diff --git a/ios/web_view/test/BUILD.gn b/ios/web_view/test/BUILD.gn
index 975e0dc..38944fa 100644
--- a/ios/web_view/test/BUILD.gn
+++ b/ios/web_view/test/BUILD.gn
@@ -6,6 +6,10 @@
 import("//ios/build/config.gni")
 import("//testing/test.gni")
 
+config("unittest_config") {
+  defines = [ "CWV_UNIT_TEST" ]
+}
+
 source_set("inttests") {
   testonly = true
 
@@ -68,3 +72,104 @@
     "//ui/base",
   ]
 }
+
+source_set("run_all_unittests") {
+  testonly = true
+  sources = [ "run_all_unittests.cc" ]
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//mojo/core/embedder",
+  ]
+}
+
+test("ios_web_view_unittests") {
+  testonly = true
+  sources = [
+    "../internal/autofill/cwv_autofill_controller_unittest.mm",
+    "../internal/autofill/cwv_autofill_data_manager_unittest.mm",
+    "../internal/autofill/cwv_autofill_form_unittest.mm",
+    "../internal/autofill/cwv_autofill_profile_unittest.mm",
+    "../internal/autofill/cwv_autofill_suggestion_unittest.mm",
+    "../internal/autofill/cwv_credit_card_saver_unittest.mm",
+    "../internal/autofill/cwv_credit_card_unittest.mm",
+    "../internal/autofill/cwv_credit_card_verifier_unittest.mm",
+    "../internal/cwv_download_task_unittest.mm",
+    "../internal/cwv_favicon_unittest.mm",
+    "../internal/cwv_flags_unittest.mm",
+    "../internal/cwv_global_state.mm",
+    "../internal/cwv_html_element_unittest.mm",
+    "../internal/cwv_lookalike_url_handler_unittest.mm",
+    "../internal/cwv_omnibox_input_unittest.mm",
+    "../internal/cwv_preferences_unittest.mm",
+    "../internal/cwv_preview_element_info_unittest.mm",
+    "../internal/cwv_ssl_error_handler_unittest.mm",
+    "../internal/cwv_ssl_status_unittest.mm",
+    "../internal/cwv_web_view_configuration_internal_unittest.mm",
+    "../internal/cwv_web_view_unittest.mm",
+    "../internal/cwv_x509_certificate_unittest.mm",
+    "../internal/metrics/cwv_metrics_provider_unittest.mm",
+    "../internal/passwords/cwv_credential_provider_extension_utils_unittest.mm",
+    "../internal/passwords/cwv_leak_check_credential_unittest.mm",
+    "../internal/passwords/cwv_leak_check_service_unittest.mm",
+    "../internal/passwords/cwv_password_unittest.mm",
+    "../internal/passwords/cwv_reuse_check_service_unittest.mm",
+    "../internal/passwords/cwv_weak_check_utils_unittest.mm",
+    "../internal/passwords/web_view_password_manager_client_unittest.mm",
+    "../internal/safe_browsing/cwv_unsafe_url_handler_unittest.mm",
+    "../internal/signin/account_capabilities_fetcher_ios_web_view_unittest.mm",
+    "../internal/signin/cwv_identity_unittest.mm",
+    "../internal/signin/web_view_device_accounts_provider_impl_unittest.mm",
+    "../internal/signin/web_view_gaia_auth_fetcher_unittest.mm",
+    "../internal/sync/cwv_sync_controller_unittest.mm",
+    "../internal/sync/cwv_trusted_vault_observer_unittest.mm",
+    "../internal/translate/cwv_translation_controller_unittest.mm",
+    "../internal/translate/cwv_translation_language_unittest.mm",
+    "../internal/translate/cwv_translation_policy_unittest.mm",
+    "../internal/web_view_web_client_unittest.mm",
+  ]
+
+  configs += [ ":unittest_config" ]
+
+  deps = [
+    ":run_all_unittests",
+    ":test_support",
+    "//base/test:test_support",
+    "//components/affiliations/core/browser:test_support",
+    "//components/autofill/core/browser:test_support",
+    "//components/autofill/ios/browser:test_support",
+    "//components/autofill/ios/form_util:test_support",
+    "//components/language_detection/core",
+    "//components/password_manager/core/browser:test_support",
+    "//components/password_manager/core/browser/leak_detection:test_support",
+    "//components/prefs:test_support",
+    "//components/signin/public/base:test_support",
+    "//components/signin/public/identity_manager:test_support",
+    "//components/sync:test_support",
+    "//components/sync_device_info:test_support",
+    "//components/translate/core/browser:test_support",
+    "//components/translate/core/language_detection",
+    "//ios/web/common:uikit",
+    "//ios/web/common:web_view_creation_util",
+    "//ios/web/public/js_messaging",
+    "//ios/web/public/security",
+    "//ios/web/public/test",
+    "//ios/web/public/test:test_fixture",
+    "//ios/web_view:web_view_sources",
+    "//net:test_support",
+    "//testing/gtest",
+    "//third_party/ocmock",
+  ]
+
+  assert_no_deps = ios_assert_no_deps
+}
+
+test("ios_web_view_inttests") {
+  testonly = true
+
+  deps = [ ":inttests" ]
+
+  bundle_deps = [ "//ios/web_view:web_view+bundle" ]
+
+  assert_no_deps = ios_assert_no_deps
+}
diff --git a/ios_internal b/ios_internal
index c3935b3..695ab190 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit c3935b3d5c33aad75608bd4ce39545d47b8d5b24
+Subproject commit 695ab1904c1b36f5a5e9864c0304ac499a0007ff
diff --git a/media/cdm/library_cdm/clear_key_cdm/cdm_file_adapter.cc b/media/cdm/library_cdm/clear_key_cdm/cdm_file_adapter.cc
index f20350b..17a4015 100644
--- a/media/cdm/library_cdm/clear_key_cdm/cdm_file_adapter.cc
+++ b/media/cdm/library_cdm/clear_key_cdm/cdm_file_adapter.cc
@@ -35,9 +35,9 @@
 }
 
 CdmFileAdapter::~CdmFileAdapter() {
-  DCHECK(!open_cb_);
-  DCHECK(!read_cb_);
-  DCHECK(!write_cb_);
+  CHECK(!open_cb_);
+  CHECK(!read_cb_);
+  CHECK(!write_cb_);
   file_io_->Close();
 }
 
diff --git a/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc b/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc
index 3ebeebc..73eb1fe 100644
--- a/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc
+++ b/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc
@@ -71,7 +71,7 @@
 bool ToCdmVideoFrame(const VideoFrame& video_frame,
                      CdmHostProxy* cdm_host_proxy,
                      CdmVideoDecoder::CdmVideoFrame* cdm_video_frame) {
-  DCHECK(cdm_video_frame);
+  CHECK(cdm_video_frame);
 
   if (!video_frame.IsMappable()) {
     DVLOG(1) << "VideoFrame is not mappable";
@@ -179,7 +179,7 @@
                       std::unique_ptr<VideoDecoder> video_decoder)
       : cdm_host_proxy_(cdm_host_proxy),
         video_decoder_(std::move(video_decoder)) {
-    DCHECK(cdm_host_proxy_);
+    CHECK(cdm_host_proxy_);
   }
 
   VideoDecoderAdapter(const VideoDecoderAdapter&) = delete;
@@ -191,7 +191,7 @@
   DecoderStatus Initialize(const cdm::VideoDecoderConfig_3& config) final {
     auto clear_config = ToClearMediaVideoDecoderConfig(config);
     DVLOG(1) << __func__ << ": " << clear_config.AsHumanReadableString();
-    DCHECK(!last_init_result_.has_value());
+    CHECK(!last_init_result_.has_value());
 
     // Initialize |video_decoder_| and wait for completion.
     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
@@ -231,7 +231,7 @@
   cdm::Status Decode(scoped_refptr<DecoderBuffer> buffer,
                      CdmVideoFrame* decoded_frame) final {
     DVLOG(3) << __func__;
-    DCHECK(!last_decode_status_.has_value());
+    CHECK(!last_decode_status_.has_value());
 
     // Call |video_decoder_| Decode() and wait for completion.
     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
@@ -265,7 +265,7 @@
  private:
   void OnInitialized(base::OnceClosure quit_closure, DecoderStatus status) {
     DVLOG(1) << __func__ << " success = " << status.is_ok();
-    DCHECK(!last_init_result_.has_value());
+    CHECK(!last_init_result_.has_value());
     last_init_result_ = std::move(status);
     std::move(quit_closure).Run();
   }
@@ -285,7 +285,7 @@
   }
 
   void OnDecoded(base::OnceClosure quit_closure, DecoderStatus decode_status) {
-    DCHECK(!last_decode_status_.has_value());
+    CHECK(!last_decode_status_.has_value());
     last_decode_status_ = std::move(decode_status);
     std::move(quit_closure).Run();
   }
diff --git a/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc b/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc
index 3d7b4021..d819346 100644
--- a/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc
+++ b/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc
@@ -72,7 +72,7 @@
 static scoped_refptr<media::DecoderBuffer> CopyDecoderBufferFrom(
     const cdm::InputBuffer_2& input_buffer) {
   if (!input_buffer.data) {
-    DCHECK(!input_buffer.data_size);
+    CHECK(!input_buffer.data_size);
     return media::DecoderBuffer::CreateEOSBuffer();
   }
 
@@ -348,7 +348,7 @@
           base::BindRepeating(&ClearKeyCdm::OnSessionExpirationUpdate,
                               base::Unretained(this)))),
       key_system_(key_system) {
-  DCHECK(g_is_cdm_module_initialized);
+  CHECK(g_is_cdm_module_initialized);
 }
 
 ClearKeyCdm::~ClearKeyCdm() = default;
@@ -425,7 +425,7 @@
                               uint32_t session_id_length) {
   DVLOG(1) << __func__;
   DCHECK_EQ(session_type, cdm::kPersistentLicense);
-  DCHECK(allow_persistent_state_);
+  CHECK(allow_persistent_state_);
   std::string web_session_str(session_id, session_id_length);
 
   auto promise = std::make_unique<CdmCallbackPromise<std::string>>(
@@ -541,7 +541,7 @@
 
 void ClearKeyCdm::TimerExpired(void* context) {
   DVLOG(1) << __func__;
-  DCHECK(has_set_timer_);
+  CHECK(has_set_timer_);
   std::string renewal_message;
 
   if (key_system_ == kExternalClearKeyMessageTypeTestKeySystem) {
@@ -575,7 +575,7 @@
 cdm::Status ClearKeyCdm::Decrypt(const cdm::InputBuffer_2& encrypted_buffer,
                                  cdm::DecryptedBlock* decrypted_block) {
   DVLOG(1) << __func__;
-  DCHECK(encrypted_buffer.data);
+  CHECK(encrypted_buffer.data);
 
   scoped_refptr<DecoderBuffer> buffer;
   cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
@@ -585,7 +585,7 @@
   }
 
   auto buffer_span = base::span(*buffer);
-  DCHECK(!buffer_span.empty());
+  CHECK(!buffer_span.empty());
   decrypted_block->SetDecryptedBuffer(
       cdm_host_proxy_->Allocate(buffer_span.size()));
   memcpy(reinterpret_cast<void*>(decrypted_block->DecryptedBuffer()->Data()),
@@ -758,7 +758,7 @@
 cdm::Status ClearKeyCdm::DecryptToMediaDecoderBuffer(
     const cdm::InputBuffer_2& encrypted_buffer,
     scoped_refptr<DecoderBuffer>* decrypted_buffer) {
-  DCHECK(decrypted_buffer);
+  CHECK(decrypted_buffer);
 
   scoped_refptr<DecoderBuffer> buffer = CopyDecoderBufferFrom(encrypted_buffer);
 
diff --git a/media/cdm/library_cdm/clear_key_cdm/clear_key_persistent_session_cdm.cc b/media/cdm/library_cdm/clear_key_cdm/clear_key_persistent_session_cdm.cc
index 368876d..41299f7 100644
--- a/media/cdm/library_cdm/clear_key_cdm/clear_key_persistent_session_cdm.cc
+++ b/media/cdm/library_cdm/clear_key_cdm/clear_key_persistent_session_cdm.cc
@@ -341,7 +341,7 @@
     std::unique_ptr<CdmFileAdapter> file,
     std::unique_ptr<SimpleCdmPromise> promise,
     bool success) {
-  DCHECK(success);
+  CHECK(success);
 }
 
 CdmContext* ClearKeyPersistentSessionCdm::GetCdmContext() {
diff --git a/media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_audio_decoder.cc b/media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_audio_decoder.cc
index 0213ea3..771754b4 100644
--- a/media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_audio_decoder.cc
+++ b/media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_audio_decoder.cc
@@ -258,7 +258,7 @@
   }
 
   if (!output_timestamp_helper_->base_timestamp() && !is_end_of_stream) {
-    DCHECK(timestamp != kNoTimestamp);
+    CHECK(timestamp != kNoTimestamp);
     output_timestamp_helper_->SetBaseTimestamp(timestamp);
   }
 
diff --git a/media/cdm/library_cdm/mock_library_cdm.cc b/media/cdm/library_cdm/mock_library_cdm.cc
index f46dbd4..02d2622 100644
--- a/media/cdm/library_cdm/mock_library_cdm.cc
+++ b/media/cdm/library_cdm/mock_library_cdm.cc
@@ -29,7 +29,7 @@
 }
 
 MockLibraryCdm::~MockLibraryCdm() {
-  DCHECK(g_mock_library_cdm);
+  CHECK(g_mock_library_cdm);
   g_mock_library_cdm = nullptr;
 }
 
@@ -49,7 +49,7 @@
                            GetCdmHostFunc get_cdm_host_func,
                            void* user_data) {
   DVLOG(1) << __func__;
-  DCHECK(!g_mock_library_cdm);
+  CHECK(!g_mock_library_cdm);
 
   std::string key_system_string(key_system, key_system_size);
 
diff --git a/net/cookies/cookie_util.cc b/net/cookies/cookie_util.cc
index a9e4e448..702915b1 100644
--- a/net/cookies/cookie_util.cc
+++ b/net/cookies/cookie_util.cc
@@ -690,7 +690,7 @@
                                        : CookieAccessScheme::kNonCryptographic;
 }
 
-bool IsDomainMatch(const std::string& domain, const std::string& host) {
+bool IsDomainMatch(const std::string_view domain, const std::string_view host) {
   // Can domain match in two ways; as a domain cookie (where the cookie
   // domain begins with ".") or as a host cookie (where it doesn't).
 
@@ -723,7 +723,7 @@
                        domain) == 0);
 }
 
-bool IsOnPath(const std::string& cookie_path, const std::string& url_path) {
+bool IsOnPath(const std::string_view cookie_path, const std::string_view url_path) {
   // A zero length would be unsafe for our trailing '/' checks, and
   // would also make no sense for our prefix match.  The code that
   // creates a CanonicalCookie should make sure the path is never zero length,
diff --git a/net/cookies/cookie_util.h b/net/cookies/cookie_util.h
index cbb7365..a8e1ac3 100644
--- a/net/cookies/cookie_util.h
+++ b/net/cookies/cookie_util.h
@@ -250,15 +250,15 @@
 // |domain| is the output of cookie.Domain() for some cookie. This returns true
 // if a |domain| indicates that the cookie can be accessed by |host|.
 // See comment on CanonicalCookie::IsDomainMatch().
-NET_EXPORT bool IsDomainMatch(const std::string& domain,
-                              const std::string& host);
+NET_EXPORT bool IsDomainMatch(const std::string_view domain,
+                              const std::string_view host);
 
 // Returns true if the given |url_path| path-matches |cookie_path|
 // as described in section 5.1.4 in RFC 6265. This returns true if |cookie_path|
 // and |url_path| are identical, or if |url_path| is a subdirectory of
 // |cookie_path|.
-NET_EXPORT bool IsOnPath(const std::string& cookie_path,
-                         const std::string& url_path);
+NET_EXPORT bool IsOnPath(const std::string_view cookie_path,
+                         const std::string_view url_path);
 
 // Returns the CookiePrefix (or COOKIE_PREFIX_NONE if none) that
 // applies to the given cookie |name|.
diff --git a/net/disk_cache/sql/sql_backend_impl.cc b/net/disk_cache/sql/sql_backend_impl.cc
index 579434f4..0cf515c 100644
--- a/net/disk_cache/sql/sql_backend_impl.cc
+++ b/net/disk_cache/sql/sql_backend_impl.cc
@@ -1003,6 +1003,10 @@
 }
 
 void SqlBackendImpl::TriggerDeleteDoomedEntries() {
+  // TODO(crbug.com/443171275): Get information on whether a doomed entry
+  // exists when initializing SqlPersistentStore, and if it does not exist, do
+  // not execute TriggerDeleteDoomedEntries.
+  // TODO(crbug.com/443171275): Execute only when the browser is idle.
   exclusive_operation_coordinator_.PostOrRunExclusiveOperation(base::BindOnce(
       base::BindOnce(&SqlBackendImpl::HandleDeleteDoomedEntriesOperation,
                      weak_factory_.GetWeakPtr())));
diff --git a/net/disk_cache/sql/sql_persistent_store.cc b/net/disk_cache/sql/sql_persistent_store.cc
index 3e18be6..b5bf0f6 100644
--- a/net/disk_cache/sql/sql_persistent_store.cc
+++ b/net/disk_cache/sql/sql_persistent_store.cc
@@ -27,6 +27,7 @@
 #include "net/base/features.h"
 #include "net/base/io_buffer.h"
 #include "net/disk_cache/cache_util.h"
+#include "net/disk_cache/sql/indexed_pair_set.h"
 #include "net/disk_cache/sql/sql_backend_constants.h"
 #include "net/disk_cache/sql/sql_persistent_store_queries.h"
 #include "sql/database.h"
@@ -42,6 +43,23 @@
 
 constexpr std::string_view kHistogramPrefix = "Net.SqlDiskCache.Backend.";
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+// LINT.IfChange(IndexMismatchLocation)
+enum class IndexMismatchLocation {
+  kOpenOrCreateEntry = 0,
+  kCreateEntry = 1,
+  kDoomEntry = 2,
+  kStartEviction = 3,
+  kDeleteLiveEntry = 4,
+  kDeleteLiveEntriesBetween = 5,
+  kMaxValue = kDeleteLiveEntriesBetween,
+};
+// LINT.ThenChange(//tools/metrics/histograms/metadata/net/enums.xml:SqlDiskCacheIndexMismatchLocation)
+
+using HashResIdSet =
+    IndexedPairSet<CacheEntryKey::Hash, SqlPersistentStore::ResId>;
+
 // Holds summary statistics about the cache store.
 struct StoreStatus {
   int32_t entry_count = 0;
@@ -50,10 +68,14 @@
 
 // The result of a successful initialization.
 struct InitResult {
-  explicit InitResult(int64_t max_bytes) : max_bytes(max_bytes) {}
+  InitResult(int64_t max_bytes, HashResIdSet&& index)
+      : max_bytes(max_bytes), index(std::move(index)) {}
   ~InitResult() = default;
+  InitResult(InitResult&& other) = default;
+  InitResult& operator=(InitResult&& other) = default;
 
   int64_t max_bytes = 0;
+  HashResIdSet index;
 };
 
 // A helper struct to associate an IOBuffer with a starting offset.
@@ -62,6 +84,19 @@
   int64_t start;
 };
 
+// A helper struct to hold a resource ID and a cache entry key hash.
+struct ResIdAndHashKey {
+  ResIdAndHashKey(SqlPersistentStore::ResId res_id,
+                  CacheEntryKey::Hash key_hash)
+      : res_id(res_id), key_hash(key_hash) {}
+  ~ResIdAndHashKey() = default;
+  ResIdAndHashKey(ResIdAndHashKey&&) = default;
+  ResIdAndHashKey& operator=(ResIdAndHashKey&&) = default;
+
+  SqlPersistentStore::ResId res_id;
+  CacheEntryKey::Hash key_hash;
+};
+
 using disk_cache_sql_queries::GetQuery;
 using disk_cache_sql_queries::Query;
 
@@ -75,6 +110,8 @@
     SqlPersistentStore::OptionalEntryInfoWithIdAndKey;
 using IntOrError = SqlPersistentStore::IntOrError;
 using InitResultOrError = base::expected<InitResult, Error>;
+using ResIdAndHashKeyList = std::vector<ResIdAndHashKey>;
+using ResIdAndHashKeyListOrError = base::expected<ResIdAndHashKeyList, Error>;
 
 // A helper struct to bundle an operation's result with a flag indicating
 // whether an eviction check is needed. This allows the background sequence,
@@ -100,8 +137,8 @@
 using EntryInfoOrErrorAndEvictionRequested =
     ResultAndEvictionRequested<EntryInfoOrError>;
 using IntOrErrorAndEvictionRequested = ResultAndEvictionRequested<IntOrError>;
-
-
+using ResIdAndHashKeyListOrErrorAndEvictionRequested =
+    ResultAndEvictionRequested<ResIdAndHashKeyListOrError>;
 
 bool IsBlobSizeValid(int64_t blob_start,
                      int64_t blob_end,
@@ -168,6 +205,10 @@
     dict.Add("entry_info", "not found");
   }
 }
+void PopulateTraceDetails(const ResIdAndHashKeyList& result,
+                          perfetto::TracedDictionary& dict) {
+  dict.Add("doomed_entry_count", result.size());
+}
 void PopulateTraceDetails(Error error,
                           const StoreStatus& store_status,
                           perfetto::TracedDictionary& dict) {
@@ -291,16 +332,18 @@
   EntryInfoOrErrorAndEvictionRequested OpenOrCreateEntry(
       const CacheEntryKey& key);
   OptionalEntryInfoOrError OpenEntry(const CacheEntryKey& key);
-  EntryInfoOrErrorAndEvictionRequested CreateEntry(const CacheEntryKey& key);
+  EntryInfoOrErrorAndEvictionRequested CreateEntry(const CacheEntryKey& key,
+                                                   bool run_existance_check);
 
   ErrorAndEvictionRequested DoomEntry(const CacheEntryKey& key, ResId res_id);
   ErrorAndEvictionRequested DeleteDoomedEntry(const CacheEntryKey& key,
                                               ResId res_id);
   Error DeleteDoomedEntries(base::flat_set<ResId> excluded_res_ids);
-  ErrorAndEvictionRequested DeleteLiveEntry(const CacheEntryKey& key);
+  ResIdAndHashKeyListOrErrorAndEvictionRequested DeleteLiveEntry(
+      const CacheEntryKey& key);
 
   ErrorAndEvictionRequested DeleteAllEntries();
-  ErrorAndEvictionRequested DeleteLiveEntriesBetween(
+  ResIdAndHashKeyListOrErrorAndEvictionRequested DeleteLiveEntriesBetween(
       base::Time initial_time,
       base::Time end_time,
       base::flat_set<CacheEntryKey> excluded_keys);
@@ -328,7 +371,7 @@
   int64_t CalculateSizeOfEntriesBetween(base::Time initial_time,
                                         base::Time end_time);
   OptionalEntryInfoWithIdAndKey OpenLatestEntryBeforeResId(ResId res_id_cursor);
-  ErrorAndEvictionRequested RunEviction(
+  ResIdAndHashKeyListOrErrorAndEvictionRequested RunEviction(
       base::flat_set<CacheEntryKey> excluded_keys);
   bool MaybeRunCheckpoint();
 
@@ -336,10 +379,14 @@
     strict_corruption_check_enabled_ = true;
   }
 
+  void SetSimulateDbFailureForTesting(bool fail) {
+    simulate_db_failure_for_testing_ = fail;
+  }
+
  private:
   void DatabaseErrorCallback(int error, sql::Statement* statement);
 
-  Error InitializeInternal(bool& corruption_detected);
+  Error InitializeInternal(bool& corruption_detected, HashResIdSet& index);
   EntryInfoOrError OpenOrCreateEntryInternal(const CacheEntryKey& key,
                                              bool& corruption_detected);
   OptionalEntryInfoOrError OpenEntryInternal(const CacheEntryKey& key);
@@ -352,10 +399,10 @@
       const base::flat_set<ResId>& excluded_res_ids,
       size_t& deleted_count,
       bool& corruption_detected);
-  Error DeleteLiveEntryInternal(const CacheEntryKey& key,
-                                bool& corruption_detected);
+  ResIdAndHashKeyListOrError DeleteLiveEntryInternal(const CacheEntryKey& key,
+                                                     bool& corruption_detected);
   Error DeleteAllEntriesInternal(bool& corruption_detected);
-  Error DeleteLiveEntriesBetweenInternal(
+  ResIdAndHashKeyListOrError DeleteLiveEntriesBetweenInternal(
       base::Time initial_time,
       base::Time end_time,
       const base::flat_set<CacheEntryKey>& excluded_keys,
@@ -391,8 +438,9 @@
   OptionalEntryInfoWithIdAndKey OpenLatestEntryBeforeResIdInternal(
       ResId res_id_cursor,
       bool& corruption_detected);
-  Error RunEvictionInternal(const base::flat_set<CacheEntryKey>& excluded_keys,
-                            bool& corruption_detected);
+  ResIdAndHashKeyListOrError RunEvictionInternal(
+      const base::flat_set<CacheEntryKey>& excluded_keys,
+      bool& corruption_detected);
 
   // Trims blobs that overlap with the new write range [offset, end), and
   // updates the total size delta.
@@ -484,6 +532,7 @@
   std::optional<Error> db_init_status_;
   StoreStatus store_status_;
   bool strict_corruption_check_enabled_ = false;
+  bool simulate_db_failure_for_testing_ = false;
   // The number of pages in the write-ahead log file. This is updated by
   // `OnCommitCallback` and reset to 0 after a checkpoint.
   int wal_pages_ = 0;
@@ -494,7 +543,8 @@
   base::ElapsedTimer timer;
   CHECK(!db_init_status_.has_value());
   bool corruption_detected = false;
-  db_init_status_ = InitializeInternal(corruption_detected);
+  HashResIdSet index;
+  db_init_status_ = InitializeInternal(corruption_detected, index);
   RecordTimeAndErrorResultHistogram("Initialize", timer.Elapsed(),
                                     *db_init_status_, corruption_detected);
   TRACE_EVENT_END1("disk_cache", "SqlBackend.Initialize", "result",
@@ -521,11 +571,15 @@
   }
 
   return *db_init_status_ == Error::kOk
-             ? InitResultOrError(InitResult(max_bytes_))
+             ? InitResultOrError(InitResult(max_bytes_, std::move(index)))
              : base::unexpected(*db_init_status_);
 }
 
-Error Backend::InitializeInternal(bool& corruption_detected) {
+Error Backend::InitializeInternal(bool& corruption_detected,
+                                  HashResIdSet& index) {
+  if (simulate_db_failure_for_testing_) {
+    return Error::kFailedForTesting;
+  }
   CHECK(!db_init_status_.has_value());
 
   db_.set_error_callback(base::BindRepeating(&Backend::DatabaseErrorCallback,
@@ -571,7 +625,33 @@
     return Error::kFailedToInitializeMetaTable;
   }
 
+  {
+    base::ElapsedTimer timer;
+    sql::Statement statement(db_.GetCachedStatement(
+        SQL_FROM_HERE,
+        GetQuery(
+            Query::kGetCacheKeyHashes_SelectCacheKeyHashFromLiveResources)));
+    while (statement.Step()) {
+      const auto res_id = ResId(statement.ColumnInt64(0));
+      const auto key_hash = CacheEntryKey::Hash(statement.ColumnInt64(1));
+      const bool doomed = statement.ColumnBool(2);
+      if (doomed) {
+        // TODO(crbug.com/443171275): Return information to SqlBackendImpl that
+        // a doomed entry exists, and if no doomed entry exists, do not execute
+        // TriggerDeleteDoomedEntries.
+      } else {
+        index.Insert(key_hash, res_id);
+      }
+    }
+    // TODO(crbug.com/443171275): If this process takes a very long time,
+    // load the in-memory index when the browser is idle.
+    base::UmaHistogramMicrosecondsTimes(
+        base::StrCat({kHistogramPrefix, "LoadInMemoryIndexTime"}),
+        timer.Elapsed());
+  }
 
+  // TODO(crbug.com/443171275): Use `index.size()` and remove
+  // `kSqlBackendMetaTableKeyEntryCount` metadata.
   int64_t tmp_entry_count = 0;
   if (!GetOrInitializeMetaValue(meta_table_, kSqlBackendMetaTableKeyEntryCount,
                                 tmp_entry_count,
@@ -635,6 +715,9 @@
 
 EntryInfoOrError Backend::OpenOrCreateEntryInternal(const CacheEntryKey& key,
                                                     bool& corruption_detected) {
+  if (simulate_db_failure_for_testing_) {
+    return base::unexpected(Error::kFailedForTesting);
+  }
   // Try to open first.
   auto open_result = OpenEntryInternal(key);
   if (open_result.has_value() && open_result->has_value()) {
@@ -670,6 +753,9 @@
 }
 
 OptionalEntryInfoOrError Backend::OpenEntryInternal(const CacheEntryKey& key) {
+  if (simulate_db_failure_for_testing_) {
+    return base::unexpected(Error::kFailedForTesting);
+  }
   CheckDatabaseInitStatus();
 
   sql::Statement statement(db_.GetCachedStatement(
@@ -700,7 +786,8 @@
 }
 
 EntryInfoOrErrorAndEvictionRequested Backend::CreateEntry(
-    const CacheEntryKey& key) {
+    const CacheEntryKey& key,
+    bool run_existance_check) {
   TRACE_EVENT_BEGIN1("disk_cache", "SqlBackend.CreateEntry", "data",
                      [&](perfetto::TracedValue trace_context) {
                        auto dict = std::move(trace_context).WriteDictionary();
@@ -709,8 +796,8 @@
                      });
   base::ElapsedTimer timer;
   bool corruption_detected = false;
-  auto result = CreateEntryInternal(key, /*run_existance_check=*/true,
-                                    corruption_detected);
+  auto result =
+      CreateEntryInternal(key, run_existance_check, corruption_detected);
   RecordTimeAndErrorResultHistogram("CreateEntry", timer.Elapsed(),
                                     result.error_or(Error::kOk),
                                     corruption_detected);
@@ -727,6 +814,9 @@
 EntryInfoOrError Backend::CreateEntryInternal(const CacheEntryKey& key,
                                               bool run_existance_check,
                                               bool& corruption_detected) {
+  if (simulate_db_failure_for_testing_) {
+    return base::unexpected(Error::kFailedForTesting);
+  }
   CheckDatabaseInitStatus();
   sql::Transaction transaction(&db_);
   if (!transaction.Begin()) {
@@ -807,6 +897,9 @@
 }
 
 Error Backend::DoomEntryInternal(ResId res_id, bool& corruption_detected) {
+  if (simulate_db_failure_for_testing_) {
+    return Error::kFailedForTesting;
+  }
   CheckDatabaseInitStatus();
   sql::Transaction transaction(&db_);
   if (!transaction.Begin()) {
@@ -876,6 +969,9 @@
 }
 
 Error Backend::DeleteDoomedEntryInternal(ResId res_id) {
+  if (simulate_db_failure_for_testing_) {
+    return Error::kFailedForTesting;
+  }
   CheckDatabaseInitStatus();
   sql::Transaction transaction(&db_);
   if (!transaction.Begin()) {
@@ -935,6 +1031,9 @@
     const base::flat_set<ResId>& excluded_res_ids,
     size_t& deleted_count,
     bool& corruption_detected) {
+  if (simulate_db_failure_for_testing_) {
+    return Error::kFailedForTesting;
+  }
   CheckDatabaseInitStatus();
   sql::Transaction transaction(&db_);
   if (!transaction.Begin()) {
@@ -980,7 +1079,8 @@
   return transaction.Commit() ? Error::kOk : Error::kFailedToCommitTransaction;
 }
 
-ErrorAndEvictionRequested Backend::DeleteLiveEntry(const CacheEntryKey& key) {
+ResIdAndHashKeyListOrErrorAndEvictionRequested Backend::DeleteLiveEntry(
+    const CacheEntryKey& key) {
   TRACE_EVENT_BEGIN1("disk_cache", "SqlBackend.DeleteLiveEntry", "data",
                      [&](perfetto::TracedValue trace_context) {
                        auto dict = std::move(trace_context).WriteDictionary();
@@ -990,7 +1090,8 @@
   base::ElapsedTimer timer;
   bool corruption_detected = false;
   auto result = DeleteLiveEntryInternal(key, corruption_detected);
-  RecordTimeAndErrorResultHistogram("DeleteLiveEntry", timer.Elapsed(), result,
+  RecordTimeAndErrorResultHistogram("DeleteLiveEntry", timer.Elapsed(),
+                                    result.error_or(Error::kOk),
                                     corruption_detected);
   TRACE_EVENT_END1("disk_cache", "SqlBackend.DeleteLiveEntry", "result",
                    [&](perfetto::TracedValue trace_context) {
@@ -999,15 +1100,20 @@
                      dict.Add("corruption_detected", corruption_detected);
                    });
   MaybeCrashIfCorrupted(corruption_detected);
-  return ErrorAndEvictionRequested(result, ShouldStartEviction());
+  return ResIdAndHashKeyListOrErrorAndEvictionRequested(std::move(result),
+                                                        ShouldStartEviction());
 }
 
-Error Backend::DeleteLiveEntryInternal(const CacheEntryKey& key,
-                                       bool& corruption_detected) {
+ResIdAndHashKeyListOrError Backend::DeleteLiveEntryInternal(
+    const CacheEntryKey& key,
+    bool& corruption_detected) {
+  if (simulate_db_failure_for_testing_) {
+    return base::unexpected(Error::kFailedForTesting);
+  }
   CheckDatabaseInitStatus();
   sql::Transaction transaction(&db_);
   if (!transaction.Begin()) {
-    return Error::kFailedToStartTransaction;
+    return base::unexpected(Error::kFailedToStartTransaction);
   }
 
   // We need to collect the res_ids of deleted entries to later remove their
@@ -1015,30 +1121,34 @@
   std::vector<ResId> res_ids_to_be_deleted;
   // Use checked numerics to safely update the total cache size.
   base::CheckedNumeric<int64_t> total_size_delta = 0;
-  int64_t deleted_count = 0;
+  ResIdAndHashKeyList deleted_enties;
   {
     sql::Statement statement(db_.GetCachedStatement(
         SQL_FROM_HERE, GetQuery(Query::kDeleteLiveEntry_DeleteFromResources)));
     statement.BindInt64(0, key.hash().value());
     statement.BindString(1, key.string());
     while (statement.Step()) {
-      ++deleted_count;
-      res_ids_to_be_deleted.emplace_back(statement.ColumnInt64(0));
+      const auto res_id = ResId(statement.ColumnInt64(0));
+      res_ids_to_be_deleted.emplace_back(res_id);
+      deleted_enties.emplace_back(res_id, key.hash());
       // The size of the deleted entry is subtracted from the total.
       total_size_delta -= statement.ColumnInt64(1);
     }
   }
 
   // If no entries were deleted, the key wasn't found.
-  if (deleted_count == 0) {
-    return transaction.Commit() ? Error::kNotFound
-                                : Error::kFailedToCommitTransaction;
+  if (res_ids_to_be_deleted.empty()) {
+    return transaction.Commit()
+               ? base::unexpected(Error::kNotFound)
+               : base::unexpected(Error::kFailedToCommitTransaction);
   }
 
   // Delete the blobs associated with the deleted entries.
   if (Error delete_result = DeleteBlobsByResIds(res_ids_to_be_deleted);
       delete_result != Error::kOk) {
-    return delete_result;
+    // If blob deletion fails, returns the error. The transaction will be
+    // rolled back. So no need to return `deleted_enties`.
+    return base::unexpected(delete_result);
   }
 
   // If we detected corruption, or if the size update calculation overflowed,
@@ -1046,14 +1156,20 @@
   // scratch.
   if (corruption_detected || !total_size_delta.IsValid()) {
     corruption_detected = true;
-    return RecalculateStoreStatusAndCommitTransaction(transaction);
+    auto error = RecalculateStoreStatusAndCommitTransaction(transaction);
+    return error == Error::kOk
+               ? ResIdAndHashKeyListOrError(std::move(deleted_enties))
+               : base::unexpected(error);
   }
 
-  return UpdateStoreStatusAndCommitTransaction(
+  auto error = UpdateStoreStatusAndCommitTransaction(
       transaction,
       /*entry_count_delta=*/
       -static_cast<int64_t>(res_ids_to_be_deleted.size()),
       /*total_size_delta=*/total_size_delta.ValueOrDie(), corruption_detected);
+  return error == Error::kOk
+             ? ResIdAndHashKeyListOrError(std::move(deleted_enties))
+             : base::unexpected(error);
 }
 
 ErrorAndEvictionRequested Backend::DeleteAllEntries() {
@@ -1077,6 +1193,9 @@
 }
 
 Error Backend::DeleteAllEntriesInternal(bool& corruption_detected) {
+  if (simulate_db_failure_for_testing_) {
+    return Error::kFailedForTesting;
+  }
   CheckDatabaseInitStatus();
   sql::Transaction transaction(&db_);
   if (!transaction.Begin()) {
@@ -1110,10 +1229,10 @@
       /*total_size_delta=*/-store_status_.total_size, corruption_detected);
 }
 
-ErrorAndEvictionRequested Backend::DeleteLiveEntriesBetween(
-    base::Time initial_time,
-    base::Time end_time,
-    base::flat_set<CacheEntryKey> excluded_keys) {
+ResIdAndHashKeyListOrErrorAndEvictionRequested
+Backend::DeleteLiveEntriesBetween(base::Time initial_time,
+                                  base::Time end_time,
+                                  base::flat_set<CacheEntryKey> excluded_keys) {
   TRACE_EVENT_BEGIN1("disk_cache", "SqlBackend.DeleteLiveEntriesBetween",
                      "data", [&](perfetto::TracedValue trace_context) {
                        auto dict = std::move(trace_context).WriteDictionary();
@@ -1126,32 +1245,37 @@
   // Flag to indicate if we encounter signs of database corruption. In
   // DeleteLiveEntriesBetween, database corruption is ignored.
   bool corruption_detected = false;
-  Error result = DeleteLiveEntriesBetweenInternal(
+  auto result = DeleteLiveEntriesBetweenInternal(
       initial_time, end_time, excluded_keys, corruption_detected);
   RecordTimeAndErrorResultHistogram("DeleteLiveEntriesBetween", timer.Elapsed(),
-                                    result, corruption_detected);
+                                    result.error_or(Error::kOk),
+                                    corruption_detected);
   TRACE_EVENT_END1("disk_cache", "SqlBackend.DeleteLiveEntriesBetween",
                    "result", [&](perfetto::TracedValue trace_context) {
                      auto dict = std::move(trace_context).WriteDictionary();
                      PopulateTraceDetails(result, store_status_, dict);
                    });
   MaybeCrashIfCorrupted(corruption_detected);
-  return ErrorAndEvictionRequested(result, ShouldStartEviction());
+  return ResIdAndHashKeyListOrErrorAndEvictionRequested(std::move(result),
+                                                        ShouldStartEviction());
 }
 
-Error Backend::DeleteLiveEntriesBetweenInternal(
+ResIdAndHashKeyListOrError Backend::DeleteLiveEntriesBetweenInternal(
     base::Time initial_time,
     base::Time end_time,
     const base::flat_set<CacheEntryKey>& excluded_keys,
     bool& corruption_detected) {
+  if (simulate_db_failure_for_testing_) {
+    return base::unexpected(Error::kFailedForTesting);
+  }
   CheckDatabaseInitStatus();
   sql::Transaction transaction(&db_);
   if (!transaction.Begin()) {
-    return Error::kFailedToStartTransaction;
+    return base::unexpected(Error::kFailedToStartTransaction);
   }
 
   std::vector<ResId> res_ids_to_be_deleted;
-  int64_t entry_count_delta = 0;
+  ResIdAndHashKeyList deleted_enties;
   base::CheckedNumeric<int64_t> total_size_delta = 0;
   {
     sql::Statement statement(db_.GetCachedStatement(
@@ -1160,12 +1284,13 @@
     statement.BindTime(0, initial_time);
     statement.BindTime(1, end_time);
     while (statement.Step()) {
-      if (excluded_keys.contains(CacheEntryKey(statement.ColumnString(2)))) {
+      const auto cache_key = CacheEntryKey(statement.ColumnString(2));
+      if (excluded_keys.contains(cache_key)) {
         continue;
       }
-      --entry_count_delta;
-      ResId res_id(statement.ColumnInt64(0));
-      res_ids_to_be_deleted.push_back(res_id);
+      const auto res_id = ResId(statement.ColumnInt64(0));
+      deleted_enties.emplace_back(res_id, cache_key.hash());
+      res_ids_to_be_deleted.emplace_back(res_id);
       total_size_delta -= statement.ColumnInt64(1);
     }
   }
@@ -1173,13 +1298,13 @@
   // Delete the blobs associated with the entries to be deleted.
   if (auto error = DeleteBlobsByResIds(res_ids_to_be_deleted);
       error != Error::kOk) {
-    return error;
+    return base::unexpected(error);
   }
 
   // Delete the selected entries from the `resources` table.
   if (auto error = DeleteResourcesByResIds(res_ids_to_be_deleted);
       error != Error::kOk) {
-    return error;
+    return base::unexpected(error);
   }
 
   // If we detected corruption, or if the size update calculation overflowed,
@@ -1187,14 +1312,20 @@
   // scratch.
   if (corruption_detected || !total_size_delta.IsValid()) {
     corruption_detected = true;
-    return RecalculateStoreStatusAndCommitTransaction(transaction);
+    auto error = RecalculateStoreStatusAndCommitTransaction(transaction);
+    return error == Error::kOk
+               ? ResIdAndHashKeyListOrError(std::move(deleted_enties))
+               : base::unexpected(error);
   }
 
   // Update the in-memory and on-disk store status (entry count and total size)
   // and commit the transaction.
-  return UpdateStoreStatusAndCommitTransaction(transaction, entry_count_delta,
-                                               total_size_delta.ValueOrDie(),
-                                               corruption_detected);
+  auto error = UpdateStoreStatusAndCommitTransaction(
+      transaction, -deleted_enties.size(), total_size_delta.ValueOrDie(),
+      corruption_detected);
+  return error == Error::kOk
+             ? ResIdAndHashKeyListOrError(std::move(deleted_enties))
+             : base::unexpected(error);
 }
 
 Error Backend::UpdateEntryLastUsed(const CacheEntryKey& key,
@@ -1219,6 +1350,9 @@
 
 Error Backend::UpdateEntryLastUsedInternal(const CacheEntryKey& key,
                                            base::Time last_used) {
+  if (simulate_db_failure_for_testing_) {
+    return Error::kFailedForTesting;
+  }
   CheckDatabaseInitStatus();
   sql::Transaction transaction(&db_);
   if (!transaction.Begin()) {
@@ -1282,6 +1416,9 @@
     scoped_refptr<net::IOBuffer> buffer,
     int64_t header_size_delta,
     bool& corruption_detected) {
+  if (simulate_db_failure_for_testing_) {
+    return Error::kFailedForTesting;
+  }
   CHECK(buffer);
   CheckDatabaseInitStatus();
 
@@ -1359,6 +1496,9 @@
                                       int buf_len,
                                       bool truncate,
                                       bool& corruption_detected) {
+  if (simulate_db_failure_for_testing_) {
+    return Error::kFailedForTesting;
+  }
   CheckDatabaseInitStatus();
   sql::Transaction transaction(&db_);
   if (!transaction.Begin()) {
@@ -1767,6 +1907,9 @@
                                           int64_t body_end,
                                           bool sparse_reading,
                                           bool& corruption_detected) {
+  if (simulate_db_failure_for_testing_) {
+    return base::unexpected(Error::kFailedForTesting);
+  }
   CheckDatabaseInitStatus();
 
   if (offset < 0 || buf_len < 0 || !buffer || buf_len > buffer->size()) {
@@ -2012,26 +2155,28 @@
   return std::nullopt;
 }
 
-ErrorAndEvictionRequested Backend::RunEviction(
+ResIdAndHashKeyListOrErrorAndEvictionRequested Backend::RunEviction(
     base::flat_set<CacheEntryKey> excluded_keys) {
   TRACE_EVENT0("disk_cache", "SqlBackend.RunEviction");
   base::ElapsedTimer timer;
   bool corruption_detected = false;
   auto result =
       RunEvictionInternal(std::move(excluded_keys), corruption_detected);
-  RecordTimeAndErrorResultHistogram("RunEviction", timer.Elapsed(), result,
+  RecordTimeAndErrorResultHistogram("RunEviction", timer.Elapsed(),
+                                    result.error_or(Error::kOk),
                                     corruption_detected);
   MaybeCrashIfCorrupted(corruption_detected);
-  return ErrorAndEvictionRequested(result, ShouldStartEviction());
+  return ResIdAndHashKeyListOrErrorAndEvictionRequested(std::move(result),
+                                                        ShouldStartEviction());
 }
 
-Error Backend::RunEvictionInternal(
+ResIdAndHashKeyListOrError Backend::RunEvictionInternal(
     const base::flat_set<CacheEntryKey>& excluded_keys,
     bool& corruption_detected) {
   int64_t size_to_be_removed = GetSizeOfAllEntries() - low_watermark_;
   sql::Transaction transaction(&db_);
   if (!transaction.Begin()) {
-    return Error::kFailedToExecute;
+    return base::unexpected(Error::kFailedToExecute);
   }
 
   std::vector<ResId> res_ids_to_be_deleted;
@@ -2039,15 +2184,19 @@
   // Use checked numerics to safely update the total cache size.
   base::CheckedNumeric<int64_t> checked_total_size_delta = 0;
   base::CheckedNumeric<int64_t> checked_removed_total_size = 0;
+  ResIdAndHashKeyList deleted_enties;
   {
     sql::Statement statement(db_.GetCachedStatement(
         SQL_FROM_HERE, GetQuery(Query::kRunEviction_SelectLiveResources)));
     while (size_to_be_removed > checked_removed_total_size.ValueOrDie() &&
            statement.Step()) {
-      if (excluded_keys.contains(CacheEntryKey(statement.ColumnString(1)))) {
+      const ResId res_id = ResId(statement.ColumnInt64(0));
+      const CacheEntryKey cache_key = CacheEntryKey(statement.ColumnString(1));
+      if (excluded_keys.contains(cache_key)) {
         continue;
       }
-      res_ids_to_be_deleted.emplace_back(ResId(statement.ColumnInt64(0)));
+      deleted_enties.emplace_back(res_id, cache_key.hash());
+      res_ids_to_be_deleted.emplace_back(res_id);
       --entry_count_delta;
       const int64_t bytes_usage = statement.ColumnInt64(2);
       checked_total_size_delta -= bytes_usage;
@@ -2056,7 +2205,7 @@
       if (!checked_total_size_delta.IsValid() ||
           !checked_removed_total_size.IsValid()) {
         corruption_detected = true;
-        return Error::kInvalidData;
+        return base::unexpected(Error::kInvalidData);
       }
     }
   }
@@ -2064,18 +2213,21 @@
   for (const auto& res_id_to_be_deleted : res_ids_to_be_deleted) {
     if (Error delete_result = DeleteBlobsByResId(res_id_to_be_deleted);
         delete_result != Error::kOk) {
-      return delete_result;
+      return base::unexpected(delete_result);
     }
     sql::Statement statement(db_.GetCachedStatement(
         SQL_FROM_HERE, GetQuery(Query::kRunEviction_DeleteFromResources)));
     statement.BindInt64(0, res_id_to_be_deleted.value());
     if (!statement.Run()) {
-      return Error::kFailedToExecute;
+      return base::unexpected(Error::kFailedToExecute);
     }
   }
-  return UpdateStoreStatusAndCommitTransaction(
+  auto error = UpdateStoreStatusAndCommitTransaction(
       transaction, entry_count_delta, checked_total_size_delta.ValueOrDie(),
       corruption_detected);
+  return error == Error::kOk
+             ? ResIdAndHashKeyListOrError(std::move(deleted_enties))
+             : base::unexpected(error);
 }
 
 Error Backend::UpdateStoreStatusAndCommitTransaction(
@@ -2240,6 +2392,7 @@
               if (weak_ptr) {
                 if (result.has_value()) {
                   weak_ptr->SetMaxSize(result->max_bytes);
+                  weak_ptr->index_ = std::move(result->index);
                 }
                 std::move(callback).Run(result.has_value() ? Error::kOk
                                                            : result.error());
@@ -2251,7 +2404,9 @@
                          EntryInfoOrErrorCallback callback) override {
     backend_.AsyncCall(&Backend::OpenOrCreateEntry)
         .WithArgs(key)
-        .Then(WrapCallbackWithEvictionRequested(std::move(callback)));
+        .Then(WrapEntryInfoOrErrorCallback(
+            std::move(callback), key,
+            IndexMismatchLocation::kOpenOrCreateEntry));
   }
   void OpenEntry(const CacheEntryKey& key,
                  OptionalEntryInfoOrErrorCallback callback) override {
@@ -2261,16 +2416,36 @@
   }
   void CreateEntry(const CacheEntryKey& key,
                    EntryInfoOrErrorCallback callback) override {
+    bool run_existance_check = !index_ || index_->Contains(key.hash());
     backend_.AsyncCall(&Backend::CreateEntry)
-        .WithArgs(key)
-        .Then(WrapCallbackWithEvictionRequested(std::move(callback)));
+        .WithArgs(key, run_existance_check)
+        .Then(WrapEntryInfoOrErrorCallback(
+            std::move(callback), key, IndexMismatchLocation::kCreateEntry));
   }
   void DoomEntry(const CacheEntryKey& key,
                  ResId res_id,
                  ErrorCallback callback) override {
     backend_.AsyncCall(&Backend::DoomEntry)
         .WithArgs(key, res_id)
-        .Then(WrapCallbackWithEvictionRequested(std::move(callback)));
+        .Then(base::BindOnce(
+            [](base::WeakPtr<SqlPersistentStoreImpl> weak_ptr,
+               CacheEntryKey::Hash key_hash, ResId res_id,
+               ErrorCallback callback, ErrorAndEvictionRequested result) {
+              if (weak_ptr) {
+                if (result.result == Error::kOk) {
+                  CHECK(weak_ptr->index_.has_value());
+                  if (!weak_ptr->index_->Remove(key_hash, res_id)) {
+                    weak_ptr->RecordIndexMismatch(
+                        IndexMismatchLocation::kDoomEntry);
+                  }
+                }
+                weak_ptr->eviction_requested_ = result.eviction_requested;
+                // We should not run the callback when `this` was deleted.
+                std::move(callback).Run(std::move(result.result));
+              }
+            },
+            weak_factory_.GetWeakPtr(), key.hash(), res_id,
+            std::move(callback)));
   }
   void DeleteDoomedEntry(const CacheEntryKey& key,
                          ResId res_id,
@@ -2289,11 +2464,24 @@
                        ErrorCallback callback) override {
     backend_.AsyncCall(&Backend::DeleteLiveEntry)
         .WithArgs(key)
-        .Then(WrapCallbackWithEvictionRequested(std::move(callback)));
+        .Then(WrapErrorCallbackToRemoveFromIndex(
+            std::move(callback), IndexMismatchLocation::kDeleteLiveEntry));
   }
   void DeleteAllEntries(ErrorCallback callback) override {
     backend_.AsyncCall(&Backend::DeleteAllEntries)
-        .Then(WrapCallbackWithEvictionRequested(std::move(callback)));
+        .Then(base::BindOnce(
+            [](base::WeakPtr<SqlPersistentStoreImpl> weak_ptr,
+               ErrorCallback callback, ErrorAndEvictionRequested result) {
+              if (weak_ptr) {
+                if (result.result == Error::kOk) {
+                  CHECK(weak_ptr->index_.has_value());
+                  weak_ptr->index_->Clear();
+                }
+                // We should not run the callback when `this` was deleted.
+                std::move(callback).Run(std::move(result.result));
+              }
+            },
+            weak_factory_.GetWeakPtr(), std::move(callback)));
   }
   void DeleteLiveEntriesBetween(base::Time initial_time,
                                 base::Time end_time,
@@ -2301,7 +2489,9 @@
                                 ErrorCallback callback) override {
     backend_.AsyncCall(&Backend::DeleteLiveEntriesBetween)
         .WithArgs(initial_time, end_time, std::move(excluded_keys))
-        .Then(WrapCallbackWithEvictionRequested(std::move(callback)));
+        .Then(WrapErrorCallbackToRemoveFromIndex(
+            std::move(callback),
+            IndexMismatchLocation::kDeleteLiveEntriesBetween));
   }
   void UpdateEntryLastUsed(const CacheEntryKey& key,
                            base::Time last_used,
@@ -2379,11 +2569,22 @@
         .WithArgs(std::move(excluded_keys))
         .Then(base::BindOnce(
             [](base::WeakPtr<SqlPersistentStoreImpl> weak_ptr,
-               ErrorCallback callback, ErrorAndEvictionRequested result) {
+               ErrorCallback callback,
+               ResIdAndHashKeyListOrErrorAndEvictionRequested result) {
               if (weak_ptr) {
                 weak_ptr->eviction_in_progress_ = false;
+                if (result.result.has_value()) {
+                  CHECK(weak_ptr->index_.has_value());
+                  for (const auto& res_id_and_key : *result.result) {
+                    if (!weak_ptr->index_->Remove(res_id_and_key.key_hash,
+                                                  res_id_and_key.res_id)) {
+                      weak_ptr->RecordIndexMismatch(
+                          IndexMismatchLocation::kStartEviction);
+                    }
+                  }
+                }
                 weak_ptr->eviction_requested_ = result.eviction_requested;
-                std::move(callback).Run(result.result);
+                std::move(callback).Run(result.result.error_or(Error::kOk));
               }
             },
             weak_factory_.GetWeakPtr(), std::move(callback)));
@@ -2402,9 +2603,25 @@
   }
 
   void EnableStrictCorruptionCheckForTesting() override {
+    strict_corruption_check_enabled_ = true;
     backend_.AsyncCall(&Backend::EnableStrictCorruptionCheckForTesting);
   }
 
+  void SetSimulateDbFailureForTesting(bool fail) override {
+    backend_.AsyncCall(&Backend::SetSimulateDbFailureForTesting).WithArgs(fail);
+  }
+
+  IndexState GetIndexStateForHash(CacheEntryKey::Hash key_hash) const override {
+    if (!index_.has_value()) {
+      return IndexState::kNotReady;
+    }
+
+    if (index_->Contains(key_hash)) {
+      return IndexState::kHashFound;
+    }
+    return IndexState::kHashNotFound;
+  }
+
  private:
   void SetMaxSize(int64_t max_bytes) {
     max_size_ = max_bytes;
@@ -2445,13 +2662,74 @@
         weak_factory_.GetWeakPtr(), std::move(callback));
   }
 
+  base::OnceCallback<void(EntryInfoOrErrorAndEvictionRequested)>
+  WrapEntryInfoOrErrorCallback(EntryInfoOrErrorCallback callback,
+                               const CacheEntryKey& key,
+                               IndexMismatchLocation location) {
+    return base::BindOnce(
+        [](base::WeakPtr<SqlPersistentStoreImpl> weak_ptr,
+           EntryInfoOrErrorCallback callback, CacheEntryKey::Hash key_hash,
+           IndexMismatchLocation location,
+           EntryInfoOrErrorAndEvictionRequested result) {
+          if (weak_ptr) {
+            if (result.result.has_value()) {
+              CHECK(weak_ptr->index_.has_value());
+              if (!result.result->opened) {
+                if (!weak_ptr->index_->Insert(key_hash,
+                                              result.result->res_id)) {
+                  weak_ptr->RecordIndexMismatch(location);
+                };
+              }
+            }
+            weak_ptr->eviction_requested_ = result.eviction_requested;
+            // We should not run the callback when `this` was deleted.
+            std::move(callback).Run(std::move(result.result));
+          }
+        },
+        weak_factory_.GetWeakPtr(), std::move(callback), key.hash(), location);
+  }
+
+  base::OnceCallback<void(ResIdAndHashKeyListOrErrorAndEvictionRequested)>
+  WrapErrorCallbackToRemoveFromIndex(ErrorCallback callback,
+                                     IndexMismatchLocation location) {
+    return base::BindOnce(
+        [](base::WeakPtr<SqlPersistentStoreImpl> weak_ptr,
+           ErrorCallback callback, IndexMismatchLocation location,
+           ResIdAndHashKeyListOrErrorAndEvictionRequested result) {
+          if (weak_ptr) {
+            if (result.result.has_value()) {
+              CHECK(weak_ptr->index_.has_value());
+              for (const auto& hash_and_res_id : result.result.value()) {
+                if (!weak_ptr->index_->Remove(hash_and_res_id.key_hash,
+                                              hash_and_res_id.res_id)) {
+                  weak_ptr->RecordIndexMismatch(location);
+                }
+              }
+            }
+            weak_ptr->eviction_requested_ = result.eviction_requested;
+            // We should not run the callback when `this` was deleted.
+            std::move(callback).Run(
+                std::move(result.result.error_or(Error::kOk)));
+          }
+        },
+        weak_factory_.GetWeakPtr(), std::move(callback), location);
+  }
+
+  void RecordIndexMismatch(IndexMismatchLocation location) {
+    base::UmaHistogramEnumeration(
+        base::StrCat({kHistogramPrefix, "IndexMismatch"}), location);
+    CHECK(!strict_corruption_check_enabled_);
+  }
+
   base::SequenceBound<Backend> backend_;
 
   int64_t max_size_ = 0;
   int64_t max_file_size_ = 0;
   bool eviction_in_progress_ = false;
   bool eviction_requested_ = false;
+  bool strict_corruption_check_enabled_ = false;
 
+  std::optional<HashResIdSet> index_;
   base::WeakPtrFactory<SqlPersistentStoreImpl> weak_factory_{this};
 };
 
diff --git a/net/disk_cache/sql/sql_persistent_store.h b/net/disk_cache/sql/sql_persistent_store.h
index ed5de01..1ade9a0e 100644
--- a/net/disk_cache/sql/sql_persistent_store.h
+++ b/net/disk_cache/sql/sql_persistent_store.h
@@ -67,7 +67,8 @@
     kNotFound = 13,
     kInvalidArgument = 14,
     kBodyEndMismatch = 15,
-    kMaxValue = kBodyEndMismatch
+    kFailedForTesting = 16,
+    kMaxValue = kFailedForTesting
   };
   // LINT.ThenChange(//tools/metrics/histograms/metadata/net/enums.xml:SqlDiskCacheStoreError)
 
@@ -318,9 +319,26 @@
   // kSqlDiskCacheIdleCheckpointThreshold, a checkpoint is executed.
   virtual void MaybeRunCheckpoint(base::OnceCallback<void(bool)> callback) = 0;
 
+  enum class IndexState {
+    // The in-memory index is not available (e.g., not yet loaded or
+    // invalidated).
+    kNotReady,
+    // The index is ready and the hash was found. This may be a false positive.
+    kHashFound,
+    // The index is ready, but the hash was not found.
+    kHashNotFound,
+  };
+
+  // Synchronously checks the state of a key hash against the in-memory index.
+  virtual IndexState GetIndexStateForHash(
+      CacheEntryKey::Hash key_hash) const = 0;
+
   // Enables a strict corruption checking mode for testing purposes.
   virtual void EnableStrictCorruptionCheckForTesting() = 0;
 
+  // Sets a flag to simulate database operation failures for testing.
+  virtual void SetSimulateDbFailureForTesting(bool fail) = 0;
+
  protected:
   SqlPersistentStore() = default;
 };
diff --git a/net/disk_cache/sql/sql_persistent_store_queries.h b/net/disk_cache/sql/sql_persistent_store_queries.h
index 8bd16cb..df2db29 100644
--- a/net/disk_cache/sql/sql_persistent_store_queries.h
+++ b/net/disk_cache/sql/sql_persistent_store_queries.h
@@ -356,6 +356,17 @@
     kCalculateTotalSize_SelectTotalSizeFromLiveResources[] =
         "SELECT SUM(bytes_usage) FROM resources WHERE doomed=0";
 
+inline constexpr const char
+    kGetCacheKeyHashes_SelectCacheKeyHashFromLiveResources[] =
+        // clang-format off
+    "SELECT "
+        "res_id, "          // 0
+        "cache_key_hash, "  // 1
+        "doomed "           // 2
+    "FROM resources "
+    "ORDER BY cache_key_hash";
+// clang-format on
+
 }  // namespace internal
 
 // An enum for all SQL queries. This helps ensure that all queries are tested.
@@ -394,8 +405,9 @@
   kRunEviction_DeleteFromResources,
   kCalculateResourceEntryCount_SelectCountFromLiveResources,
   kCalculateTotalSize_SelectTotalSizeFromLiveResources,
+  kGetCacheKeyHashes_SelectCacheKeyHashFromLiveResources,
 
-  kMaxValue = kCalculateTotalSize_SelectTotalSizeFromLiveResources,
+  kMaxValue = kGetCacheKeyHashes_SelectCacheKeyHashFromLiveResources,
 };
 
 inline base::cstring_view GetQuery(Query query) {
@@ -468,6 +480,8 @@
           kCalculateResourceEntryCount_SelectCountFromLiveResources;
     case Query::kCalculateTotalSize_SelectTotalSizeFromLiveResources:
       return internal::kCalculateTotalSize_SelectTotalSizeFromLiveResources;
+    case Query::kGetCacheKeyHashes_SelectCacheKeyHashFromLiveResources:
+      return internal::kGetCacheKeyHashes_SelectCacheKeyHashFromLiveResources;
   }
   NOTREACHED();
 }
diff --git a/net/disk_cache/sql/sql_persistent_store_queries_unittest.cc b/net/disk_cache/sql/sql_persistent_store_queries_unittest.cc
index b0ab241..279f5eb6 100644
--- a/net/disk_cache/sql/sql_persistent_store_queries_unittest.cc
+++ b/net/disk_cache/sql/sql_persistent_store_queries_unittest.cc
@@ -208,7 +208,10 @@
             "COVERING INDEX index_live_resources_last_used"},
            {Query::kCalculateTotalSize_SelectTotalSizeFromLiveResources,
             "`--SCAN resources USING "
-            "INDEX index_live_resources_last_used"}});
+            "INDEX index_live_resources_last_used"},
+           {Query::kGetCacheKeyHashes_SelectCacheKeyHashFromLiveResources,
+            "`--SCAN resources USING COVERING INDEX "
+            "index_resources_cache_key_hash_doomed"}});
   static_assert(kAllQueriesAndPlans.size() + kSchemaAndIndexQueries.size() ==
                 static_cast<int>(Query::kMaxValue) + 1);
   for (const auto& it : kAllQueriesAndPlans) {
diff --git a/net/disk_cache/sql/sql_persistent_store_unittest.cc b/net/disk_cache/sql/sql_persistent_store_unittest.cc
index b43d3e6b..6262cb87 100644
--- a/net/disk_cache/sql/sql_persistent_store_unittest.cc
+++ b/net/disk_cache/sql/sql_persistent_store_unittest.cc
@@ -1594,6 +1594,17 @@
   ASSERT_EQ(GetEntryCount(), 5);
   int64_t initial_total_size = GetSizeOfAllEntries();
 
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey1.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey2.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey3.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey4.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey5.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+
   // Delete entries between kTime1 (inclusive) and kTime3 (exclusive).
   // kKey2 should be excluded.
   // Expected to delete: kKey1.
@@ -1601,6 +1612,16 @@
   base::flat_set<CacheEntryKey> excluded_keys = {kKey2};
   ASSERT_EQ(DeleteLiveEntriesBetween(kTime1, kTime3, excluded_keys),
             SqlPersistentStore::Error::kOk);
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey1.hash()),
+            SqlPersistentStore::IndexState::kHashNotFound);
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey2.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey3.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey4.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey5.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
 
   EXPECT_EQ(GetEntryCount(), 4);
   const int64_t expected_size_after_delete =
@@ -3455,6 +3476,8 @@
   // Verify oldest entries are gone.
   int evicted_count = count_before_eviction - count_after_eviction;
   for (int j = 0; j < evicted_count; ++j) {
+    EXPECT_EQ(store_->GetIndexStateForHash(keys[j].hash()),
+              SqlPersistentStore::IndexState::kHashNotFound);
     auto result = OpenEntry(keys[j]);
     ASSERT_TRUE(result.has_value());
     EXPECT_FALSE(result->has_value());
@@ -3462,6 +3485,8 @@
 
   // Verify newest entries are still there.
   for (size_t j = evicted_count; j < keys.size(); ++j) {
+    EXPECT_EQ(store_->GetIndexStateForHash(keys[j].hash()),
+              SqlPersistentStore::IndexState::kHashFound);
     auto result = OpenEntry(keys[j]);
     ASSERT_TRUE(result.has_value());
     EXPECT_TRUE(result->has_value());
@@ -3712,4 +3737,182 @@
   MaybeRunCheckpoint(/*expected_result=*/true);
 }
 
+TEST_F(SqlPersistentStoreTest, IndexState) {
+  const CacheEntryKey kKey1("key1");
+  const CacheEntryKey kKey2("key2");
+
+  CreateStore();
+
+  // Before initialization, index is not ready.
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey1.hash()),
+            SqlPersistentStore::IndexState::kNotReady);
+
+  base::test::TestFuture<SqlPersistentStore::Error> future;
+  store_->Initialize(future.GetCallback());
+  // During initialization, index is not ready.
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey1.hash()),
+            SqlPersistentStore::IndexState::kNotReady);
+  ASSERT_EQ(future.Get(), SqlPersistentStore::Error::kOk);
+
+  // After initialization with an empty store, hash is not found.
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey1.hash()),
+            SqlPersistentStore::IndexState::kHashNotFound);
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey2.hash()),
+            SqlPersistentStore::IndexState::kHashNotFound);
+
+  // Create an entry.
+  SqlPersistentStore::EntryInfoOrError result = this->CreateEntry(kKey1);
+  ASSERT_TRUE(result.has_value());
+  SqlPersistentStore::ResId res_id1 = result->res_id;
+
+  // Now the hash for key1 should be found.
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey1.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+  // Key2 should still not be found.
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey2.hash()),
+            SqlPersistentStore::IndexState::kHashNotFound);
+
+  // Create another entry.
+  result = this->CreateEntry(kKey2);
+  ASSERT_TRUE(result.has_value());
+
+  // Both hashes should be found.
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey1.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey2.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+
+  // Doom the first entry.
+  ASSERT_EQ(SqlPersistentStore::Error::kOk, this->DoomEntry(kKey1, res_id1));
+
+  // The hash for the doomed entry should be removed from the index.
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey1.hash()),
+            SqlPersistentStore::IndexState::kHashNotFound);
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey2.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+
+  // Delete the second entry.
+  ASSERT_EQ(SqlPersistentStore::Error::kOk, this->DeleteLiveEntry(kKey2));
+
+  // The hash for the deleted entry should be removed.
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey2.hash()),
+            SqlPersistentStore::IndexState::kHashNotFound);
+
+  // Re-create entry for key1.
+  result = this->CreateEntry(kKey1);
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey1.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+
+  // Delete all entries.
+  ASSERT_EQ(SqlPersistentStore::Error::kOk, this->DeleteAllEntries());
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey1.hash()),
+            SqlPersistentStore::IndexState::kHashNotFound);
+}
+
+// Test index reloading from a non-empty database.
+TEST_F(SqlPersistentStoreTest, IndexReloads) {
+  const CacheEntryKey kKey1("key1");
+  const CacheEntryKey kKey2("key2");
+
+  CreateAndInitStore();
+
+  // Create two entries.
+  ASSERT_TRUE(this->CreateEntry(kKey1).has_value());
+  ASSERT_TRUE(this->CreateEntry(kKey2).has_value());
+
+  // The hashes should be found.
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey1.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey2.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+
+  // Close and reopen the store.
+  ClearStore();
+  CreateAndInitStore();
+
+  // The index should be re-populated.
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey1.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+  EXPECT_EQ(store_->GetIndexStateForHash(kKey2.hash()),
+            SqlPersistentStore::IndexState::kHashFound);
+  EXPECT_EQ(store_->GetIndexStateForHash(CacheEntryKey("other").hash()),
+            SqlPersistentStore::IndexState::kHashNotFound);
+}
+
+TEST_F(SqlPersistentStoreTest, SimulateDbFailureInitializationFailure) {
+  CreateStore();
+  store_->SetSimulateDbFailureForTesting(true);
+  ASSERT_EQ(Init(), SqlPersistentStore::Error::kFailedForTesting);
+}
+
+TEST_F(SqlPersistentStoreTest, SimulateDbFailure) {
+  CreateAndInitStore();
+
+  store_->SetSimulateDbFailureForTesting(true);
+
+  const CacheEntryKey kKey("my-key");
+  auto create_result = CreateEntry(kKey);
+  ASSERT_FALSE(create_result.has_value());
+  EXPECT_EQ(create_result.error(),
+            SqlPersistentStore::Error::kFailedForTesting);
+
+  auto open_result = OpenEntry(kKey);
+  ASSERT_FALSE(open_result.has_value());
+  EXPECT_EQ(open_result.error(), SqlPersistentStore::Error::kFailedForTesting);
+
+  auto open_or_create_result = OpenOrCreateEntry(kKey);
+  ASSERT_FALSE(open_or_create_result.has_value());
+  EXPECT_EQ(open_or_create_result.error(),
+            SqlPersistentStore::Error::kFailedForTesting);
+
+  EXPECT_EQ(DoomEntry(kKey, SqlPersistentStore::ResId(1)),
+            SqlPersistentStore::Error::kFailedForTesting);
+
+  EXPECT_EQ(DeleteDoomedEntry(kKey, SqlPersistentStore::ResId(1)),
+            SqlPersistentStore::Error::kFailedForTesting);
+
+  EXPECT_EQ(DeleteDoomedEntries({}),
+            SqlPersistentStore::Error::kFailedForTesting);
+
+  EXPECT_EQ(DeleteLiveEntry(kKey),
+            SqlPersistentStore::Error::kFailedForTesting);
+
+  EXPECT_EQ(DeleteAllEntries(), SqlPersistentStore::Error::kFailedForTesting);
+
+  EXPECT_EQ(DeleteLiveEntriesBetween(base::Time::Now(),
+                                     base::Time::Now() + base::Seconds(1), {}),
+            SqlPersistentStore::Error::kFailedForTesting);
+
+  EXPECT_EQ(UpdateEntryLastUsed(kKey, base::Time::Now()),
+            SqlPersistentStore::Error::kFailedForTesting);
+
+  // Prepare new header data.
+  const std::string kNewHeadData = "new_header_data";
+  auto buffer = base::MakeRefCounted<net::StringIOBuffer>(kNewHeadData);
+  EXPECT_EQ(
+      UpdateEntryHeaderAndLastUsed(kKey, SqlPersistentStore::ResId(1),
+                                   base::Time::Now(), buffer, buffer->size()),
+      SqlPersistentStore::Error::kFailedForTesting);
+
+  EXPECT_EQ(WriteEntryData(kKey, SqlPersistentStore::ResId(1), 0, 0, buffer, 0,
+                           false),
+            SqlPersistentStore::Error::kFailedForTesting);
+
+  auto read_data_result =
+      ReadEntryData(SqlPersistentStore::ResId(1), 0, buffer, 0, 0, false);
+  ASSERT_FALSE(read_data_result.has_value());
+  EXPECT_EQ(read_data_result.error(),
+            SqlPersistentStore::Error::kFailedForTesting);
+
+  store_->SetSimulateDbFailureForTesting(false);
+
+  create_result = CreateEntry(kKey);
+  ASSERT_TRUE(create_result.has_value());
+
+  open_result = OpenEntry(kKey);
+  ASSERT_TRUE(open_result.has_value());
+  ASSERT_TRUE(open_result->has_value());
+}
+
 }  // namespace disk_cache
diff --git a/sandbox/win/src/crosscall_client.h b/sandbox/win/src/crosscall_client.h
index ed82953..b65c8d6 100644
--- a/sandbox/win/src/crosscall_client.h
+++ b/sandbox/win/src/crosscall_client.h
@@ -13,6 +13,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <string_view>
+
 #include "base/compiler_specific.h"
 #include "base/memory/raw_ptr_exclusion.h"
 #include "base/memory/raw_ref.h"
@@ -125,14 +127,14 @@
 };
 
 // This copy helper template specialization catches the cases where the
-// parameter is a pointer to a string.
+// parameter is a wstring_view object.
 template <>
-class CopyHelper<const wchar_t*> {
+class CopyHelper<std::wstring_view> {
  public:
-  explicit CopyHelper(const wchar_t* t) : t_(t) {}
+  explicit CopyHelper(std::wstring_view t) : t_(t) {}
 
   // Returns the pointer to the start of the string.
-  const void* GetStart() const { return t_; }
+  const void* GetStart() const { return t_.data(); }
 
   // Update the stored value with the value in the buffer. This is not
   // supported for this type.
@@ -141,15 +143,9 @@
     return true;
   }
 
-  // Returns the size of the string in bytes. We define a nullptr string to
-  // be of zero length.
+  // Returns the size of the string in bytes.
   uint32_t GetSize() const {
-    __try {
-      return (!t_) ? 0
-                   : static_cast<uint32_t>(StringLength(t_) * sizeof(t_[0]));
-    } __except (EXCEPTION_EXECUTE_HANDLER) {
-      return UINT32_MAX;
-    }
+    return static_cast<uint32_t>(t_.size() * sizeof(wchar_t));
   }
 
   // Returns true if the current type is used as an In or InOut parameter.
@@ -158,56 +154,7 @@
   ArgType GetType() { return WCHAR_TYPE; }
 
  private:
-  // We provide our not very optimized version of wcslen(), since we don't
-  // want to risk having the linker use the version in the CRT since the CRT
-  // might not be present when we do an early IPC call.
-  static size_t StringLength(const wchar_t* wcs) {
-    const wchar_t* eos = wcs;
-    while (*eos++)
-      ;
-    return static_cast<size_t>(eos - wcs - 1);
-  }
-
-  const wchar_t* t_;
-};
-
-// Specialization for non-const strings. We just reuse the implementation of the
-// const string specialization.
-template <>
-class CopyHelper<wchar_t*> : public CopyHelper<const wchar_t*> {
- public:
-  typedef CopyHelper<const wchar_t*> Base;
-  explicit CopyHelper(wchar_t* t) : Base(t) {}
-
-  const void* GetStart() const { return Base::GetStart(); }
-
-  bool Update(void* buffer) { return Base::Update(buffer); }
-
-  uint32_t GetSize() const { return Base::GetSize(); }
-
-  bool IsInOut() { return Base::IsInOut(); }
-
-  ArgType GetType() { return Base::GetType(); }
-};
-
-// Specialization for wchar_t arrays strings. We just reuse the implementation
-// of the const string specialization.
-template <size_t n>
-class CopyHelper<const wchar_t[n]> : public CopyHelper<const wchar_t*> {
- public:
-  typedef const wchar_t array[n];
-  typedef CopyHelper<const wchar_t*> Base;
-  explicit CopyHelper(array t) : Base(t) {}
-
-  const void* GetStart() const { return Base::GetStart(); }
-
-  bool Update(void* buffer) { return Base::Update(buffer); }
-
-  uint32_t GetSize() const { return Base::GetSize(); }
-
-  bool IsInOut() { return Base::IsInOut(); }
-
-  ArgType GetType() { return Base::GetType(); }
+  std::wstring_view t_;
 };
 
 // Generic encapsulation class containing a pointer to a buffer and the
diff --git a/sandbox/win/src/filesystem_interception.cc b/sandbox/win/src/filesystem_interception.cc
index f50bf84..88ccb41 100644
--- a/sandbox/win/src/filesystem_interception.cc
+++ b/sandbox/win/src/filesystem_interception.cc
@@ -115,7 +115,8 @@
     CrossCallReturn answer = {0};
     // The following call must match in the parameters with
     // FilesystemDispatcher::ProcessNtCreateFile.
-    ResultCode code = CrossCall(ipc, IpcTag::NTCREATEFILE, name.get(),
+    ResultCode code = CrossCall(ipc, IpcTag::NTCREATEFILE,
+                                std::wstring_view(name.get(), name_len),
                                 attributes, desired_access, file_attributes,
                                 sharing, disposition, options, &answer);
     if (SBOX_ALL_OK != code) {
@@ -181,8 +182,9 @@
 
     SharedMemIPCClient ipc(memory);
     CrossCallReturn answer = {0};
-    ResultCode code = CrossCall(ipc, IpcTag::NTOPENFILE, name.get(), attributes,
-                                desired_access, sharing, options, &answer);
+    ResultCode code = CrossCall(
+        ipc, IpcTag::NTOPENFILE, std::wstring_view(name.get(), name_len),
+        attributes, desired_access, sharing, options, &answer);
     if (SBOX_ALL_OK != code)
       break;
 
@@ -238,7 +240,8 @@
                                  sizeof(FILE_BASIC_INFORMATION));
     SharedMemIPCClient ipc(memory);
     CrossCallReturn answer = {0};
-    ResultCode code = CrossCall(ipc, IpcTag::NTQUERYATTRIBUTESFILE, name.get(),
+    ResultCode code = CrossCall(ipc, IpcTag::NTQUERYATTRIBUTESFILE,
+                                std::wstring_view(name.get(), name_len),
                                 attributes, file_info, &answer);
 
     if (SBOX_ALL_OK != code)
@@ -289,7 +292,8 @@
     SharedMemIPCClient ipc(memory);
     CrossCallReturn answer = {0};
     ResultCode code = CrossCall(ipc, IpcTag::NTQUERYFULLATTRIBUTESFILE,
-                                name.get(), attributes, file_info, &answer);
+                                std::wstring_view(name.get(), name_len),
+                                attributes, file_info, &answer);
 
     if (SBOX_ALL_OK != code)
       break;
diff --git a/sandbox/win/src/ipc_unittest.cc b/sandbox/win/src/ipc_unittest.cc
index ffba545..16dcbaa 100644
--- a/sandbox/win/src/ipc_unittest.cc
+++ b/sandbox/win/src/ipc_unittest.cc
@@ -166,7 +166,7 @@
 
   CrossCallReturn answer;
   IpcTag tag1 = IpcTag::PING1;
-  const wchar_t* text = L"98765 - 43210";
+  std::wstring_view text = L"98765 - 43210";
   std::wstring copied_text;
   CrossCallParamsEx* actual_params;
 
@@ -175,12 +175,12 @@
   EXPECT_EQ(1u, actual_params->GetParamsCount());
   EXPECT_EQ(tag1, actual_params->GetTag());
   EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text));
-  EXPECT_STREQ(text, copied_text.c_str());
+  EXPECT_EQ(text, copied_text);
   copied_text.clear();
 
   // Check with an empty string.
   IpcTag tag2 = IpcTag::PING2;
-  const wchar_t* null_text = nullptr;
+  std::wstring_view null_text;
   CrossCall(client, tag2, null_text, &answer);
   actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
   EXPECT_EQ(1u, actual_params->GetParamsCount());
@@ -211,20 +211,20 @@
   EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text));
   EXPECT_TRUE(copied_text.empty());
   EXPECT_TRUE(actual_params->GetParameterStr(1, &copied_text));
-  EXPECT_STREQ(text, copied_text.c_str());
+  EXPECT_EQ(text, copied_text);
 
   param_size = 1;
   std::wstring copied_text_p0, copied_text_p2;
 
-  const wchar_t* text2 = L"AeFG";
+  std::wstring_view text2 = L"AeFG";
   CrossCall(client, tag1, text2, null_text, text, &answer);
   actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
   EXPECT_EQ(3u, actual_params->GetParamsCount());
   EXPECT_EQ(tag1, actual_params->GetTag());
   EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text_p0));
-  EXPECT_STREQ(text2, copied_text_p0.c_str());
+  EXPECT_EQ(text2, copied_text_p0);
   EXPECT_TRUE(actual_params->GetParameterStr(2, &copied_text_p2));
-  EXPECT_STREQ(text, copied_text_p2.c_str());
+  EXPECT_EQ(text, copied_text_p2);
   type = INVALID_TYPE;
   param_addr = actual_params->GetRawParameter(1, &param_size, &type);
   EXPECT_TRUE(param_addr);
@@ -245,7 +245,7 @@
 
   IpcTag tag1 = IpcTag::PING1;
   IpcTag tag2 = IpcTag::PING2;
-  const wchar_t* text = L"godzilla";
+  std::wstring_view text = L"godzilla";
   CrossCallParamsEx* actual_params;
 
   char* mem = reinterpret_cast<char*>(client_control);
diff --git a/storage/browser/quota/quota_database.cc b/storage/browser/quota/quota_database.cc
index d019d91b..66b7260 100644
--- a/storage/browser/quota/quota_database.cc
+++ b/storage/browser/quota/quota_database.cc
@@ -52,8 +52,8 @@
 // Version 4 - 2011-10-17 - http://crrev.com/105822 (unsupported)
 // Version 5 - 2015-10-19 - https://crrev.com/354932 (unsupported)
 // Version 6 - 2021-04-27 - https://crrev.com/c/2757450 (unsupported)
-// Version 7 - 2021-05-20 - https://crrev.com/c/2910136
-// Version 8 - 2021-09-01 - https://crrev.com/c/3119831
+// Version 7 - 2021-05-20 - https://crrev.com/c/2910136 (unsupported)
+// Version 8 - 2021-09-01 - https://crrev.com/c/3119831 (unsupported)
 // Version 9 - 2022-05-13 - https://crrev.com/c/3601253
 // Version 10 - 2023-04-10 - https://crrev.com/c/4412082
 //
diff --git a/storage/browser/quota/quota_database_migrations.cc b/storage/browser/quota/quota_database_migrations.cc
index b69fbcc5..2cfce87 100644
--- a/storage/browser/quota/quota_database_migrations.cc
+++ b/storage/browser/quota/quota_database_migrations.cc
@@ -30,51 +30,6 @@
 // Quota type for deprecated persistent quota.
 constexpr int kDeprecatedPersistentQuotaType = 1;
 
-// Overwrites the buckets table with the new_buckets table after data has been
-// copied from the former into the latter.
-bool OverwriteBucketsTableSetUpIndexes(sql::Database* db) {
-  // Replace buckets table with new table.
-  static constexpr char kDeleteBucketTableSql[] = "DROP TABLE buckets";
-  if (!db->Execute(kDeleteBucketTableSql))
-    return false;
-
-  static constexpr char kRenameBucketTableSql[] =
-      "ALTER TABLE new_buckets RENAME to buckets";
-  if (!db->Execute(kRenameBucketTableSql))
-    return false;
-
-  // Create indices on new table.
-  // clang-format off
-  static constexpr char kStorageKeyIndexSql[] =
-      "CREATE UNIQUE INDEX buckets_by_storage_key "
-        "ON buckets(storage_key, type, name)";
-  // clang-format on
-  if (!db->Execute(kStorageKeyIndexSql))
-    return false;
-
-  static constexpr char kHostIndexSql[] =
-      "CREATE INDEX buckets_by_host ON buckets(host, type)";
-  if (!db->Execute(kHostIndexSql))
-    return false;
-
-  static constexpr char kLastAccessedIndexSql[] =
-      "CREATE INDEX buckets_by_last_accessed ON buckets(type, last_accessed)";
-  if (!db->Execute(kLastAccessedIndexSql))
-    return false;
-
-  static constexpr char kLastModifiedIndexSql[] =
-      "CREATE INDEX buckets_by_last_modified ON buckets(type, last_modified)";
-  if (!db->Execute(kLastModifiedIndexSql))
-    return false;
-
-  static constexpr char kExpirationIndexSql[] =
-      "CREATE INDEX buckets_by_expiration ON buckets(expiration)";
-  if (!db->Execute(kExpirationIndexSql))
-    return false;
-
-  return true;
-}
-
 }  // namespace
 
 // static
@@ -82,25 +37,11 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(quota_database.sequence_checker_);
   DCHECK_EQ(0, quota_database.db_->transaction_nesting());
 
-  // Reset tables for versions lower than 7 since they are unsupported.
-  if (quota_database.meta_table_->GetVersionNumber() < 7) {
+  // Reset tables for versions lower than 9 since they are unsupported.
+  if (quota_database.meta_table_->GetVersionNumber() < 9) {
     return false;
   }
 
-  if (quota_database.meta_table_->GetVersionNumber() == 7) {
-    bool success = MigrateFromVersion7ToVersion8(quota_database);
-    RecordMigrationHistogram(/*old_version=*/7, /*new_version=*/8, success);
-    if (!success)
-      return false;
-  }
-
-  if (quota_database.meta_table_->GetVersionNumber() == 8) {
-    bool success = MigrateFromVersion8ToVersion9(quota_database);
-    RecordMigrationHistogram(/*old_version=*/8, /*new_version=*/9, success);
-    if (!success)
-      return false;
-  }
-
   if (quota_database.meta_table_->GetVersionNumber() == 9) {
     bool success = MigrateFromVersion9ToVersion10(quota_database);
     RecordMigrationHistogram(/*old_version=*/9, /*new_version=*/10, success);
@@ -122,201 +63,6 @@
       success);
 }
 
-bool QuotaDatabaseMigrations::MigrateFromVersion7ToVersion8(
-    QuotaDatabase& quota_database) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_database.sequence_checker_);
-
-  sql::Database* db = quota_database.db_.get();
-  sql::Transaction transaction(db);
-  if (!transaction.Begin())
-    return false;
-
-  // Create new buckets table.
-  // clang-format off
-  static constexpr char kNewBucketsTableSql[] =
-      "CREATE TABLE IF NOT EXISTS new_buckets("
-        "id INTEGER PRIMARY KEY, "
-        "storage_key TEXT NOT NULL, "
-        "host TEXT NOT NULL, "
-        "type INTEGER NOT NULL, "
-        "name TEXT NOT NULL, "
-        "use_count INTEGER NOT NULL, "
-        "last_accessed INTEGER NOT NULL, "
-        "last_modified INTEGER NOT NULL, "
-        "expiration INTEGER NOT NULL, "
-        "quota INTEGER NOT NULL)";
-  // clang-format on
-  if (!db->Execute(kNewBucketsTableSql))
-    return false;
-
-  // clang-format off
-  static constexpr char kSelectBucketSql[] =
-      "SELECT "
-          "id,origin,type,name,use_count,last_accessed,last_modified,"
-          "expiration, quota "
-        "FROM buckets "
-        "WHERE id > ? "
-        "ORDER BY id "
-        "LIMIT 1";
-  // clang-format on
-  sql::Statement select_statement(
-      db->GetCachedStatement(SQL_FROM_HERE, kSelectBucketSql));
-
-  // clang-format off
-  static constexpr char kInsertBucketSql[] =
-      "INSERT into new_buckets("
-          "id,storage_key,host,type,name,use_count,last_accessed,"
-          "last_modified,expiration,quota) "
-        "VALUES(?,?,?,?,?,?,?,?,?,?)";
-  // clang-format on
-  sql::Statement insert_statement(
-      db->GetCachedStatement(SQL_FROM_HERE, kInsertBucketSql));
-
-  // Transfer bucket data to the new table one at a time with new host data.
-  BucketId last_bucket_id(0);
-  while (true) {
-    select_statement.BindInt64(0, last_bucket_id.value());
-    if (!select_statement.Step())
-      break;
-
-    // Populate bucket id.
-    BucketId bucket_id(select_statement.ColumnInt64(0));
-    insert_statement.BindInt64(0, bucket_id.value());
-
-    // Populate storage key and host.
-    std::string_view storage_key_string = select_statement.ColumnStringView(1);
-    insert_statement.BindString(1, storage_key_string);
-
-    std::optional<blink::StorageKey> storage_key =
-        blink::StorageKey::Deserialize(storage_key_string);
-    const std::string& host = storage_key.has_value()
-                                  ? storage_key.value().origin().host()
-                                  : std::string();
-    insert_statement.BindString(2, host);
-
-    // Populate type, name, use_count, last_accessed, last_modified,
-    // expiration and quota.
-    insert_statement.BindInt(3, select_statement.ColumnInt(2));
-    insert_statement.BindString(4, select_statement.ColumnStringView(3));
-    insert_statement.BindInt(5, select_statement.ColumnInt(4));
-    insert_statement.BindTime(6, select_statement.ColumnTime(5));
-    insert_statement.BindTime(7, select_statement.ColumnTime(6));
-    insert_statement.BindTime(8, select_statement.ColumnTime(7));
-    insert_statement.BindInt(9, select_statement.ColumnInt(8));
-
-    if (!insert_statement.Run())
-      return false;
-
-    select_statement.Reset(/*clear_bound_vars=*/true);
-    insert_statement.Reset(/*clear_bound_vars=*/true);
-    last_bucket_id = bucket_id;
-  }
-
-  if (!OverwriteBucketsTableSetUpIndexes(db))
-    return false;
-
-  // Mark database as up to date.
-  return quota_database.meta_table_->SetVersionNumber(8) &&
-         quota_database.meta_table_->SetCompatibleVersionNumber(8) &&
-         transaction.Commit();
-}
-
-bool QuotaDatabaseMigrations::MigrateFromVersion8ToVersion9(
-    QuotaDatabase& quota_database) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_database.sequence_checker_);
-
-  sql::Database* db = quota_database.db_.get();
-  sql::Transaction transaction(db);
-  if (!transaction.Begin())
-    return false;
-
-  // Create new buckets table.
-  // clang-format off
-  static constexpr char kNewBucketsTableSql[] =
-      "CREATE TABLE IF NOT EXISTS new_buckets("
-        "id INTEGER PRIMARY KEY AUTOINCREMENT, "
-        "storage_key TEXT NOT NULL, "
-        "host TEXT NOT NULL, "
-        "type INTEGER NOT NULL, "
-        "name TEXT NOT NULL, "
-        "use_count INTEGER NOT NULL, "
-        "last_accessed INTEGER NOT NULL, "
-        "last_modified INTEGER NOT NULL, "
-        "expiration INTEGER NOT NULL, "
-        "quota INTEGER NOT NULL, "
-        "persistent INTEGER NOT NULL, "
-        "durability INTEGER NOT NULL) "
-        "STRICT";
-  // clang-format on
-  if (!db->Execute(kNewBucketsTableSql))
-    return false;
-
-  // clang-format off
-  static constexpr char kSelectBucketSql[] =
-      "SELECT "
-          "id,storage_key,host,type,name,use_count,last_accessed,last_modified,"
-          "expiration,quota "
-        "FROM buckets "
-        "WHERE id > ? "
-        "ORDER BY id "
-        "LIMIT 1";
-  // clang-format on
-  sql::Statement select_statement(
-      db->GetCachedStatement(SQL_FROM_HERE, kSelectBucketSql));
-
-  // clang-format off
-  static constexpr char kInsertBucketSql[] =
-      "INSERT into new_buckets("
-          "id,storage_key,host,type,name,use_count,last_accessed,last_modified,"
-          "expiration,quota,persistent,durability)"
-        "VALUES(?,?,?,?,?,?,?,?,?,?,?,?)";
-  // clang-format on
-  sql::Statement insert_statement(
-      db->GetCachedStatement(SQL_FROM_HERE, kInsertBucketSql));
-
-  // Transfer bucket data to the new table one at a time with new
-  // persistence/durability (default) values.
-  BucketId last_bucket_id(0);
-  while (true) {
-    select_statement.BindInt64(0, last_bucket_id.value());
-    if (!select_statement.Step())
-      break;
-
-    last_bucket_id = BucketId(select_statement.ColumnInt64(0));
-
-    insert_statement.BindInt64(0, select_statement.ColumnInt64(0));
-    insert_statement.BindString(1, select_statement.ColumnStringView(1));
-    insert_statement.BindString(2, select_statement.ColumnStringView(2));
-    insert_statement.BindInt(3, select_statement.ColumnInt(3));
-    insert_statement.BindString(4, select_statement.ColumnStringView(4));
-    insert_statement.BindInt(5, select_statement.ColumnInt(5));
-    insert_statement.BindTime(6, select_statement.ColumnTime(6));
-    insert_statement.BindTime(7, select_statement.ColumnTime(7));
-    // Prior to version 9, the default value for `expiration` was
-    // base::Time::Max(), and the value was unused. For version 9+, the default
-    // value is base::Time(). Reset old values to the new default.
-    insert_statement.BindTime(8, base::Time());
-    insert_statement.BindInt(9, select_statement.ColumnInt(9));
-    insert_statement.BindBool(10, false);
-    insert_statement.BindInt(
-        11, static_cast<int>(blink::mojom::BucketDurability::kRelaxed));
-
-    if (!insert_statement.Run())
-      return false;
-
-    select_statement.Reset(/*clear_bound_vars=*/true);
-    insert_statement.Reset(/*clear_bound_vars=*/true);
-  }
-
-  if (!OverwriteBucketsTableSetUpIndexes(db))
-    return false;
-
-  // Mark database as up to date.
-  return quota_database.meta_table_->SetVersionNumber(9) &&
-         quota_database.meta_table_->SetCompatibleVersionNumber(9) &&
-         transaction.Commit();
-}
-
 bool QuotaDatabaseMigrations::MigrateFromVersion9ToVersion10(
     QuotaDatabase& quota_database) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(quota_database.sequence_checker_);
diff --git a/storage/browser/quota/quota_database_migrations_unittest.cc b/storage/browser/quota/quota_database_migrations_unittest.cc
index 331aa8dd..783a89c 100644
--- a/storage/browser/quota/quota_database_migrations_unittest.cc
+++ b/storage/browser/quota/quota_database_migrations_unittest.cc
@@ -117,158 +117,6 @@
   EXPECT_EQ(GetCurrentSchema(), GetQuotaDatabaseSchema());
 }
 
-TEST_F(QuotaDatabaseMigrationsTest, UpgradeSchemaFromV7) {
-  ASSERT_TRUE(LoadDatabase("version_7.sql", DbPath()));
-
-  {
-    sql::Database db(sql::test::kTestTag);
-    ASSERT_TRUE(db.Open(DbPath()));
-
-    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&db));
-    sql::MetaTable meta_table;
-    ASSERT_TRUE(
-        meta_table.Init(&db, kCurrentSchemaVersion, kCurrentCompatibleVersion));
-    ASSERT_EQ(meta_table.GetVersionNumber(), 7);
-    ASSERT_EQ(meta_table.GetCompatibleVersionNumber(), 7);
-
-    ASSERT_TRUE(db.DoesTableExist("quota"));
-    ASSERT_TRUE(db.DoesTableExist("buckets"));
-
-    // Check populated data.
-    EXPECT_EQ(
-        "1|http://a/|0|bucket_a|123|13260644621105493|13242931862595604|"
-        "9223372036854775807|0,"
-        "2|http://b/|0|bucket_b|111|13250042735631065|13260999511438890|"
-        "9223372036854775807|1000,"
-        "3|chrome-extension://abc/|2|default|321|13261163582572088|"
-        "13261079941303629|9223372036854775807|10000",
-        sql::test::ExecuteWithResults(
-            &db, "SELECT * FROM buckets ORDER BY id ASC", "|", ","));
-
-    EXPECT_EQ("a.com,b.com,c.com",
-              sql::test::ExecuteWithResults(
-                  &db, "SELECT host FROM quota ORDER BY host ASC", "|", ","));
-  }
-
-  MigrateDatabase();
-
-  // Verify upgraded schema.
-  {
-    sql::Database db(sql::test::kTestTag);
-    ASSERT_TRUE(db.Open(DbPath()));
-
-    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&db));
-    sql::MetaTable meta_table;
-    ASSERT_TRUE(
-        meta_table.Init(&db, kCurrentSchemaVersion, kCurrentCompatibleVersion));
-    ASSERT_EQ(meta_table.GetVersionNumber(), kCurrentSchemaVersion);
-    ASSERT_EQ(meta_table.GetCompatibleVersionNumber(), kCurrentSchemaVersion);
-
-    ASSERT_FALSE(db.DoesTableExist("quota"));
-    ASSERT_TRUE(db.DoesTableExist("buckets"));
-    ASSERT_FALSE(db.DoesTableExist("eviction_info"));
-
-    // Check that buckets data is still present.
-    EXPECT_EQ(
-        "1|http://a/|a|0|bucket_a|123|13260644621105493|13242931862595604|"
-        "0|0|0|0,"
-        "2|http://b/|b|0|bucket_b|111|13250042735631065|13260999511438890|"
-        "0|1000|0|0,"
-        "3|chrome-extension://abc/||2|_default|321|13261163582572088|"
-        "13261079941303629|0|10000|0|0",
-        sql::test::ExecuteWithResults(
-            &db, "SELECT * FROM buckets ORDER BY id ASC", "|", ","));
-
-    EXPECT_EQ(GetCurrentSchema(), RemoveQuotes(db.GetSchema()));
-
-    EXPECT_EQ(GetTotalHistogramCount(), 3u);
-    histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV7ToV8",
-                                   /*sample=*/true, /*expected_count=*/1);
-    histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV8ToV9",
-                                   /*sample=*/true, /*expected_count=*/1);
-    histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV9ToV10",
-                                   /*sample=*/true, /*expected_count=*/1);
-  }
-}
-
-TEST_F(QuotaDatabaseMigrationsTest, UpgradeSchemaFromV8) {
-  ASSERT_TRUE(LoadDatabase("version_8.sql", DbPath()));
-
-  {
-    sql::Database db(sql::test::kTestTag);
-    ASSERT_TRUE(db.Open(DbPath()));
-
-    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&db));
-    sql::MetaTable meta_table;
-    ASSERT_TRUE(
-        meta_table.Init(&db, kCurrentSchemaVersion, kCurrentCompatibleVersion));
-    ASSERT_EQ(meta_table.GetVersionNumber(), 8);
-    ASSERT_EQ(meta_table.GetCompatibleVersionNumber(), 8);
-
-    ASSERT_TRUE(db.DoesTableExist("quota"));
-    ASSERT_TRUE(db.DoesTableExist("buckets"));
-
-    // Check populated data.
-    EXPECT_EQ(
-        "1|http://a/|http://a/"
-        "|0|bucket_a|123|13260644621105493|13242931862595604|"
-        "9223372036854775807|0,"
-        "2|http://b/|http://b/"
-        "|0|bucket_b|111|13250042735631065|13260999511438890|"
-        "9223372036854775807|1000,"
-        "3|chrome-extension://abc/|chrome-extension://abc/"
-        "|2|default|321|13261163582572088|"
-        "13261079941303629|9223372036854775807|10000",
-        sql::test::ExecuteWithResults(
-            &db, "SELECT * FROM buckets ORDER BY id ASC", "|", ","));
-
-    EXPECT_EQ("a.com,b.com,c.com",
-              sql::test::ExecuteWithResults(
-                  &db, "SELECT host FROM quota ORDER BY host ASC", "|", ","));
-  }
-
-  MigrateDatabase();
-
-  // Verify upgraded schema.
-  {
-    sql::Database db(sql::test::kTestTag);
-    ASSERT_TRUE(db.Open(DbPath()));
-
-    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&db));
-    sql::MetaTable meta_table;
-    ASSERT_TRUE(
-        meta_table.Init(&db, kCurrentSchemaVersion, kCurrentCompatibleVersion));
-    EXPECT_EQ(meta_table.GetVersionNumber(), kCurrentSchemaVersion);
-    EXPECT_EQ(meta_table.GetCompatibleVersionNumber(), kCurrentSchemaVersion);
-
-    ASSERT_FALSE(db.DoesTableExist("quota"));
-    ASSERT_TRUE(db.DoesTableExist("buckets"));
-    ASSERT_FALSE(db.DoesTableExist("eviction_info"));
-
-    // Check that buckets data is still present.
-    EXPECT_EQ(
-        "1|http://a/|http://a/"
-        "|0|bucket_a|123|13260644621105493|13242931862595604|"
-        "0|0|0|0,"
-        "2|http://b/|http://b/"
-        "|0|bucket_b|111|13250042735631065|13260999511438890|"
-        "0|1000|0|0,"
-        "3|chrome-extension://abc/|chrome-extension://abc/"
-        "|2|_default|321|13261163582572088|"
-        "13261079941303629|0|10000|0|0",
-        sql::test::ExecuteWithResults(
-            &db, "SELECT * FROM buckets ORDER BY id ASC", "|", ","));
-
-    EXPECT_EQ(GetCurrentSchema(), RemoveQuotes(db.GetSchema()));
-
-    EXPECT_EQ(GetTotalHistogramCount(), 2u);
-    histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV8ToV9",
-                                   /*sample=*/true, /*expected_count=*/1);
-    histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV9ToV10",
-                                   /*sample=*/true, /*expected_count=*/1);
-  }
-}
-
 TEST_F(QuotaDatabaseMigrationsTest, UpgradeSchemaFromV9) {
   ASSERT_TRUE(LoadDatabase("version_9.sql", DbPath()));
 
diff --git a/storage/test/data/quota_database/version_7.sql b/storage/test/data/quota_database/version_7.sql
deleted file mode 100644
index 7a0c0fa2..0000000
--- a/storage/test/data/quota_database/version_7.sql
+++ /dev/null
@@ -1,34 +0,0 @@
-PRAGMA foreign_keys=OFF;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE quota(host TEXT NOT NULL, type INTEGER NOT NULL, quota INTEGER NOT NULL, PRIMARY KEY(host, type)) WITHOUT ROWID;
-
-CREATE TABLE buckets(id INTEGER PRIMARY KEY, origin TEXT NOT NULL, type INTEGER NOT NULL, name TEXT NOT NULL, use_count INTEGER NOT NULL, last_accessed INTEGER NOT NULL, last_modified INTEGER NOT NULL, expiration INTEGER NOT NULL, quota INTEGER NOT NULL);
-
-CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
-
-INSERT INTO meta VALUES ('mmap_status', '-1');
-INSERT INTO meta VALUES ('last_compatible_version', '7');
-INSERT INTO meta VALUES ('version', '7');
-
-INSERT INTO quota(host, type, quota)  VALUES('a.com', 0, 0);
-INSERT INTO quota(host, type, quota)  VALUES('b.com', 0, 1);
-INSERT INTO quota(host, type, quota)  VALUES('c.com', 1, 123);
-
-INSERT INTO buckets(origin, type, name, use_count, last_accessed, last_modified, expiration, quota)
-  VALUES('http://a/', 0, 'bucket_a', 123, 13260644621105493, 13242931862595604, 9223372036854775807, 0);
-INSERT INTO buckets(origin, type, name, use_count, last_accessed, last_modified, expiration, quota)
-  VALUES('http://b/', 0, 'bucket_b', 111, 13250042735631065, 13260999511438890, 9223372036854775807, 1000);
-INSERT INTO buckets(origin, type, name, use_count, last_accessed, last_modified, expiration, quota)
-  VALUES('chrome-extension://abc/', 2, 'default', 321, 13261163582572088, 13261079941303629, 9223372036854775807, 10000);
-
-CREATE UNIQUE INDEX buckets_by_storage_key ON buckets(origin, type, name);
-
-CREATE INDEX buckets_by_last_accessed ON buckets(type, last_accessed);
-
-CREATE INDEX buckets_by_last_modified ON buckets(type, last_modified);
-
-CREATE INDEX buckets_by_expiration ON buckets(expiration);
-
-COMMIT;
diff --git a/storage/test/data/quota_database/version_8.sql b/storage/test/data/quota_database/version_8.sql
deleted file mode 100644
index 1a5c97e..0000000
--- a/storage/test/data/quota_database/version_8.sql
+++ /dev/null
@@ -1,36 +0,0 @@
-PRAGMA foreign_keys=OFF;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE quota(host TEXT NOT NULL, type INTEGER NOT NULL, quota INTEGER NOT NULL, PRIMARY KEY(host, type)) WITHOUT ROWID;
-
-CREATE TABLE buckets(id INTEGER PRIMARY KEY, storage_key TEXT NOT NULL, host TEXT NOT NULL, type INTEGER NOT NULL, name TEXT NOT NULL, use_count INTEGER NOT NULL, last_accessed INTEGER NOT NULL, last_modified INTEGER NOT NULL, expiration INTEGER NOT NULL, quota INTEGER NOT NULL);
-
-CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
-
-INSERT INTO meta VALUES ('mmap_status', '-1');
-INSERT INTO meta VALUES ('last_compatible_version', '8');
-INSERT INTO meta VALUES ('version', '8');
-
-INSERT INTO quota(host, type, quota)  VALUES('a.com', 0, 0);
-INSERT INTO quota(host, type, quota)  VALUES('b.com', 0, 1);
-INSERT INTO quota(host, type, quota)  VALUES('c.com', 1, 123);
-
-INSERT INTO buckets(storage_key, host, type, name, use_count, last_accessed, last_modified, expiration, quota)
-  VALUES('http://a/', 'http://a/', 0, 'bucket_a', 123, 13260644621105493, 13242931862595604, 9223372036854775807, 0);
-INSERT INTO buckets(storage_key, host, type, name, use_count, last_accessed, last_modified, expiration, quota)
-  VALUES('http://b/', 'http://b/', 0, 'bucket_b', 111, 13250042735631065, 13260999511438890, 9223372036854775807, 1000);
-INSERT INTO buckets(storage_key, host, type, name, use_count, last_accessed, last_modified, expiration, quota)
-  VALUES('chrome-extension://abc/', 'chrome-extension://abc/', 2, 'default', 321, 13261163582572088, 13261079941303629, 9223372036854775807, 10000);
-
-CREATE UNIQUE INDEX buckets_by_storage_key ON buckets(storage_key, type, name);
-
-CREATE INDEX buckets_by_host ON buckets(host, type);
-
-CREATE INDEX buckets_by_last_accessed ON buckets(type, last_accessed);
-
-CREATE INDEX buckets_by_last_modified ON buckets(type, last_modified);
-
-CREATE INDEX buckets_by_expiration ON buckets(expiration);
-
-COMMIT;
diff --git a/storage/test/test_bundle_data.filelist b/storage/test/test_bundle_data.filelist
index e126cdd3..065daec 100644
--- a/storage/test/test_bundle_data.filelist
+++ b/storage/test/test_bundle_data.filelist
@@ -5,6 +5,4 @@
 #       If it requires updating, you should get a presubmit error with
 #       instructions on how to regenerate. Otherwise, do not edit.
 data/quota_database/version_10.sql
-data/quota_database/version_7.sql
-data/quota_database/version_8.sql
 data/quota_database/version_9.sql
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 5a7c74c..f225778 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -17358,21 +17358,6 @@
             ]
         }
     ],
-    "PartitionAllocFewerMemoryRegionsMac": [
-        {
-            "platforms": [
-                "mac"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "PartitionAllocFewerMemoryRegions"
-                    ]
-                }
-            ]
-        }
-    ],
     "PartitionAllocLargeThreadCacheSizeAndroid": [
         {
             "platforms": [
@@ -18892,40 +18877,13 @@
             ],
             "experiments": [
                 {
-                    "name": "5ms",
-                    "params": {
-                        "hover_dwell_time": "5ms"
-                    },
-                    "enable_features": [
-                        "PreloadingEagerHeuristics"
-                    ]
-                },
-                {
-                    "name": "10ms",
+                    "name": "Enabled",
                     "params": {
                         "hover_dwell_time": "10ms"
                     },
                     "enable_features": [
                         "PreloadingEagerHeuristics"
                     ]
-                },
-                {
-                    "name": "20ms",
-                    "params": {
-                        "hover_dwell_time": "20ms"
-                    },
-                    "enable_features": [
-                        "PreloadingEagerHeuristics"
-                    ]
-                },
-                {
-                    "name": "35ms",
-                    "params": {
-                        "hover_dwell_time": "35ms"
-                    },
-                    "enable_features": [
-                        "PreloadingEagerHeuristics"
-                    ]
                 }
             ]
         }
@@ -19338,6 +19296,21 @@
             ]
         }
     ],
+    "ProbeStylusWritingInBackground": [
+        {
+            "platforms": [
+                "android_webview"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ProbeStylusWritingInBackground"
+                    ]
+                }
+            ]
+        }
+    ],
     "ProcessBoundStringEncryption": [
         {
             "platforms": [
@@ -25595,6 +25568,25 @@
             ]
         }
     ],
+    "UnoPhase2Desktop": [
+        {
+            "platforms": [
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ForcedDiceMigration",
+                        "ReplaceSyncPromosWithSignInPromos",
+                        "SyncEnableContactInfoDataTypeForCustomPassphraseUsers"
+                    ]
+                }
+            ]
+        }
+    ],
     "UpdateStateBeforeUnbinding": [
         {
             "platforms": [
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
index adb1c8a..cebb1642 100644
--- a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
+++ b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
@@ -22,7 +22,7 @@
  */
 class ChromiumDepGraph {
 
-    private static final String DEFAULT_CIPD_SUFFIX = 'cr1'
+    private static final String DEFAULT_CIPD_SUFFIX = 'cr2'
 
     // Some libraries don't properly fill their POM with the appropriate licensing information. It is provided here from
     // manual lookups. Note that licenseUrl must provide textual content rather than be an html page.
@@ -631,7 +631,7 @@
                 children: Collections.unmodifiableList(new ArrayList<>(childModules)),
                 licenses: licenses,
                 directoryName: id.toLowerCase(),
-                fileName: artifact.file.name,
+                fileName: dependency.module.id.name + "." + artifact.extension,
                 fileUrl: fileUrl,
                 repoUrl: repoUrl,
                 description: description,
diff --git a/third_party/android_deps/fetch_common.py b/third_party/android_deps/fetch_common.py
index a1bd14ee..6617bf2f 100644
--- a/third_party/android_deps/fetch_common.py
+++ b/third_party/android_deps/fetch_common.py
@@ -58,7 +58,7 @@
                                                     spec.group_name,
                                                     spec.module_name, version,
                                                     spec.file_ext)
-    file_name = file_url.rsplit('/', 1)[-1]
+    file_name = f'{spec.module_name}.{spec.file_ext}'
 
     partial_manifest = {
         'url': [file_url],
diff --git a/third_party/android_deps/libs/com_android_support_support_annotations/3pp/fetch.py b/third_party/android_deps/libs/com_android_support_support_annotations/3pp/fetch.py
index 87ef99c..9ac9062 100755
--- a/third_party/android_deps/libs/com_android_support_support_annotations/3pp/fetch.py
+++ b/third_party/android_deps/libs/com_android_support_support_annotations/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='com/android/support',
                          module_name='support-annotations',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/com_android_support_support_annotations/cipd.yaml b/third_party/android_deps/libs/com_android_support_support_annotations/cipd.yaml
index 912bf09..630b230 100644
--- a/third_party/android_deps/libs/com_android_support_support_annotations/cipd.yaml
+++ b/third_party/android_deps/libs/com_android_support_support_annotations/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@28.0.0.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@28.0.0.cr2
 package: chromium/third_party/android_deps/libs/com_android_support_support_annotations
 description: "Android Support Library Annotations"
 data:
-- file: support-annotations-28.0.0.jar
+- file: support-annotations.jar
diff --git a/third_party/android_deps/libs/com_android_tools_common/3pp/fetch.py b/third_party/android_deps/libs/com_android_tools_common/3pp/fetch.py
index 9d27a3c4..6f5941d 100755
--- a/third_party/android_deps/libs/com_android_tools_common/3pp/fetch.py
+++ b/third_party/android_deps/libs/com_android_tools_common/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='com/android/tools',
                          module_name='common',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/com_android_tools_common/cipd.yaml b/third_party/android_deps/libs/com_android_tools_common/cipd.yaml
index fd26934..82640fb 100644
--- a/third_party/android_deps/libs/com_android_tools_common/cipd.yaml
+++ b/third_party/android_deps/libs/com_android_tools_common/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@30.2.0-beta01.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@30.2.0-beta01.cr2
 package: chromium/third_party/android_deps/libs/com_android_tools_common
 description: "com.android.tools.common"
 data:
-- file: common-30.2.0-beta01.jar
+- file: common.jar
diff --git a/third_party/android_deps/libs/com_android_tools_layoutlib_layoutlib_api/3pp/fetch.py b/third_party/android_deps/libs/com_android_tools_layoutlib_layoutlib_api/3pp/fetch.py
index 75622b67..d95e0918 100755
--- a/third_party/android_deps/libs/com_android_tools_layoutlib_layoutlib_api/3pp/fetch.py
+++ b/third_party/android_deps/libs/com_android_tools_layoutlib_layoutlib_api/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='com/android/tools/layoutlib',
                          module_name='layoutlib-api',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/com_android_tools_layoutlib_layoutlib_api/cipd.yaml b/third_party/android_deps/libs/com_android_tools_layoutlib_layoutlib_api/cipd.yaml
index 6b73b1f3..4e99c1c 100644
--- a/third_party/android_deps/libs/com_android_tools_layoutlib_layoutlib_api/cipd.yaml
+++ b/third_party/android_deps/libs/com_android_tools_layoutlib_layoutlib_api/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@30.2.0-beta01.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@30.2.0-beta01.cr2
 package: chromium/third_party/android_deps/libs/com_android_tools_layoutlib_layoutlib_api
 description: "Android Tools layoutlib-api"
 data:
-- file: layoutlib-api-30.2.0-beta01.jar
+- file: layoutlib-api.jar
diff --git a/third_party/android_deps/libs/com_android_tools_sdk_common/3pp/fetch.py b/third_party/android_deps/libs/com_android_tools_sdk_common/3pp/fetch.py
index 3848f6b..1f83485 100755
--- a/third_party/android_deps/libs/com_android_tools_sdk_common/3pp/fetch.py
+++ b/third_party/android_deps/libs/com_android_tools_sdk_common/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='com/android/tools',
                          module_name='sdk-common',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/com_android_tools_sdk_common/cipd.yaml b/third_party/android_deps/libs/com_android_tools_sdk_common/cipd.yaml
index 7cbcc77..23feed88 100644
--- a/third_party/android_deps/libs/com_android_tools_sdk_common/cipd.yaml
+++ b/third_party/android_deps/libs/com_android_tools_sdk_common/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@30.2.0-beta01.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@30.2.0-beta01.cr2
 package: chromium/third_party/android_deps/libs/com_android_tools_sdk_common
 description: "com.android.tools.sdk-common"
 data:
-- file: sdk-common-30.2.0-beta01.jar
+- file: sdk-common.jar
diff --git a/third_party/android_deps/libs/com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework/3pp/fetch.py b/third_party/android_deps/libs/com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework/3pp/fetch.py
index 4672e0e0..736522d 100755
--- a/third_party/android_deps/libs/com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework/3pp/fetch.py
+++ b/third_party/android_deps/libs/com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='com/google/android/apps/common/testing/accessibility/framework',
                          module_name='accessibility-test-framework',
                          file_ext='aar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework/cipd.yaml b/third_party/android_deps/libs/com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework/cipd.yaml
index 117445c1..f98b0ca3 100644
--- a/third_party/android_deps/libs/com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework/cipd.yaml
+++ b/third_party/android_deps/libs/com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@4.0.0.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@4.0.0.cr2
 package: chromium/third_party/android_deps/libs/com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework
 description: "Accessibility Test Framework"
 data:
-- file: accessibility-test-framework-4.0.0.aar
+- file: accessibility-test-framework.aar
diff --git a/third_party/android_deps/libs/com_googlecode_java_diff_utils_diffutils/3pp/fetch.py b/third_party/android_deps/libs/com_googlecode_java_diff_utils_diffutils/3pp/fetch.py
index c4b9ca3c..a40768f1 100755
--- a/third_party/android_deps/libs/com_googlecode_java_diff_utils_diffutils/3pp/fetch.py
+++ b/third_party/android_deps/libs/com_googlecode_java_diff_utils_diffutils/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='com/googlecode/java-diff-utils',
                          module_name='diffutils',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/com_googlecode_java_diff_utils_diffutils/cipd.yaml b/third_party/android_deps/libs/com_googlecode_java_diff_utils_diffutils/cipd.yaml
index 1f14f90..c99ca33 100644
--- a/third_party/android_deps/libs/com_googlecode_java_diff_utils_diffutils/cipd.yaml
+++ b/third_party/android_deps/libs/com_googlecode_java_diff_utils_diffutils/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@1.3.0.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@1.3.0.cr2
 package: chromium/third_party/android_deps/libs/com_googlecode_java_diff_utils_diffutils
 description: "java-diff-utils"
 data:
-- file: diffutils-1.3.0.jar
+- file: diffutils.jar
diff --git a/third_party/android_deps/libs/com_squareup_javapoet/3pp/fetch.py b/third_party/android_deps/libs/com_squareup_javapoet/3pp/fetch.py
index 7874d5f6..8e2b7b8 100755
--- a/third_party/android_deps/libs/com_squareup_javapoet/3pp/fetch.py
+++ b/third_party/android_deps/libs/com_squareup_javapoet/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='com/squareup',
                          module_name='javapoet',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/com_squareup_javapoet/cipd.yaml b/third_party/android_deps/libs/com_squareup_javapoet/cipd.yaml
index 1c8fb0d..b59e16a 100644
--- a/third_party/android_deps/libs/com_squareup_javapoet/cipd.yaml
+++ b/third_party/android_deps/libs/com_squareup_javapoet/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@1.13.0.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@1.13.0.cr2
 package: chromium/third_party/android_deps/libs/com_squareup_javapoet
 description: "JavaPoet"
 data:
-- file: javapoet-1.13.0.jar
+- file: javapoet.jar
diff --git a/third_party/android_deps/libs/net_bytebuddy_byte_buddy/3pp/fetch.py b/third_party/android_deps/libs/net_bytebuddy_byte_buddy/3pp/fetch.py
index 74cf1cf..9a9283f 100755
--- a/third_party/android_deps/libs/net_bytebuddy_byte_buddy/3pp/fetch.py
+++ b/third_party/android_deps/libs/net_bytebuddy_byte_buddy/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='net/bytebuddy',
                          module_name='byte-buddy',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/net_bytebuddy_byte_buddy/cipd.yaml b/third_party/android_deps/libs/net_bytebuddy_byte_buddy/cipd.yaml
index 18e7d58..953e9ef 100644
--- a/third_party/android_deps/libs/net_bytebuddy_byte_buddy/cipd.yaml
+++ b/third_party/android_deps/libs/net_bytebuddy_byte_buddy/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@1.17.6.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@1.17.6.cr2
 package: chromium/third_party/android_deps/libs/net_bytebuddy_byte_buddy
 description: "Byte Buddy (without dependencies)"
 data:
-- file: byte-buddy-1.17.6.jar
+- file: byte-buddy.jar
diff --git a/third_party/android_deps/libs/net_bytebuddy_byte_buddy_agent/3pp/fetch.py b/third_party/android_deps/libs/net_bytebuddy_byte_buddy_agent/3pp/fetch.py
index 8d11600..72ab3784 100755
--- a/third_party/android_deps/libs/net_bytebuddy_byte_buddy_agent/3pp/fetch.py
+++ b/third_party/android_deps/libs/net_bytebuddy_byte_buddy_agent/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='net/bytebuddy',
                          module_name='byte-buddy-agent',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/net_bytebuddy_byte_buddy_agent/cipd.yaml b/third_party/android_deps/libs/net_bytebuddy_byte_buddy_agent/cipd.yaml
index 47b05dc..1f36f9d5 100644
--- a/third_party/android_deps/libs/net_bytebuddy_byte_buddy_agent/cipd.yaml
+++ b/third_party/android_deps/libs/net_bytebuddy_byte_buddy_agent/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@1.17.6.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@1.17.6.cr2
 package: chromium/third_party/android_deps/libs/net_bytebuddy_byte_buddy_agent
 description: "Byte Buddy agent"
 data:
-- file: byte-buddy-agent-1.17.6.jar
+- file: byte-buddy-agent.jar
diff --git a/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup/3pp/fetch.py b/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup/3pp/fetch.py
index 72c4b21..cfe7e9d6 100755
--- a/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup/3pp/fetch.py
+++ b/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='org/ccil/cowan/tagsoup',
                          module_name='tagsoup',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup/cipd.yaml b/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup/cipd.yaml
index 211e72c..dafec701 100644
--- a/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup/cipd.yaml
+++ b/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@1.2.1.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@1.2.1.cr2
 package: chromium/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup
 description: "TagSoup"
 data:
-- file: tagsoup-1.2.1.jar
+- file: tagsoup.jar
diff --git a/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/3pp/fetch.py b/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/3pp/fetch.py
index 353ae22..b8729b3 100755
--- a/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/3pp/fetch.py
+++ b/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='org/checkerframework',
                          module_name='checker-compat-qual',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/cipd.yaml b/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/cipd.yaml
index cfaffdb..781b8b9 100644
--- a/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/cipd.yaml
+++ b/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@2.5.5.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@2.5.5.cr2
 package: chromium/third_party/android_deps/libs/org_checkerframework_checker_compat_qual
 description: "Checker Qual"
 data:
-- file: checker-compat-qual-2.5.5.jar
+- file: checker-compat-qual.jar
diff --git a/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_android_extensions_runtime/3pp/fetch.py b/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_android_extensions_runtime/3pp/fetch.py
index 7d5e697f..7143e215 100755
--- a/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_android_extensions_runtime/3pp/fetch.py
+++ b/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_android_extensions_runtime/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='org/jetbrains/kotlin',
                          module_name='kotlin-android-extensions-runtime',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_android_extensions_runtime/cipd.yaml b/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_android_extensions_runtime/cipd.yaml
index 9f76784..c589a463 100644
--- a/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_android_extensions_runtime/cipd.yaml
+++ b/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_android_extensions_runtime/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@1.9.22.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@1.9.22.cr2
 package: chromium/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_android_extensions_runtime
 description: "Kotlin Android Extensions Runtime"
 data:
-- file: kotlin-android-extensions-runtime-1.9.22.jar
+- file: kotlin-android-extensions-runtime.jar
diff --git a/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_parcelize_runtime/3pp/fetch.py b/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_parcelize_runtime/3pp/fetch.py
index ebd5c9f7..00a64b3d 100755
--- a/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_parcelize_runtime/3pp/fetch.py
+++ b/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_parcelize_runtime/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='org/jetbrains/kotlin',
                          module_name='kotlin-parcelize-runtime',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_parcelize_runtime/cipd.yaml b/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_parcelize_runtime/cipd.yaml
index fc52e08..59c9486 100644
--- a/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_parcelize_runtime/cipd.yaml
+++ b/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_parcelize_runtime/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@1.9.22.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@1.9.22.cr2
 package: chromium/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_parcelize_runtime
 description: "Parcelize Runtime"
 data:
-- file: kotlin-parcelize-runtime-1.9.22.jar
+- file: kotlin-parcelize-runtime.jar
diff --git a/third_party/android_deps/libs/org_jetbrains_kotlinx_atomicfu_jvm/3pp/fetch.py b/third_party/android_deps/libs/org_jetbrains_kotlinx_atomicfu_jvm/3pp/fetch.py
index 3429724f..853e8155 100755
--- a/third_party/android_deps/libs/org_jetbrains_kotlinx_atomicfu_jvm/3pp/fetch.py
+++ b/third_party/android_deps/libs/org_jetbrains_kotlinx_atomicfu_jvm/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='org/jetbrains/kotlinx',
                          module_name='atomicfu-jvm',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/org_jetbrains_kotlinx_atomicfu_jvm/cipd.yaml b/third_party/android_deps/libs/org_jetbrains_kotlinx_atomicfu_jvm/cipd.yaml
index ed9335c6..c841677 100644
--- a/third_party/android_deps/libs/org_jetbrains_kotlinx_atomicfu_jvm/cipd.yaml
+++ b/third_party/android_deps/libs/org_jetbrains_kotlinx_atomicfu_jvm/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@0.23.2.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@0.23.2.cr2
 package: chromium/third_party/android_deps/libs/org_jetbrains_kotlinx_atomicfu_jvm
 description: "atomicfu"
 data:
-- file: atomicfu-jvm-0.23.2.jar
+- file: atomicfu-jvm.jar
diff --git a/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_guava/3pp/fetch.py b/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_guava/3pp/fetch.py
index cc08f051..62bb88b 100755
--- a/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_guava/3pp/fetch.py
+++ b/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_guava/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='org/jetbrains/kotlinx',
                          module_name='kotlinx-coroutines-guava',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_guava/cipd.yaml b/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_guava/cipd.yaml
index 22c9799..18a8f26 100644
--- a/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_guava/cipd.yaml
+++ b/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_guava/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@1.8.1.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@1.8.1.cr2
 package: chromium/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_guava
 description: "kotlinx-coroutines-guava"
 data:
-- file: kotlinx-coroutines-guava-1.8.1.jar
+- file: kotlinx-coroutines-guava.jar
diff --git a/third_party/android_deps/libs/org_jsoup_jsoup/3pp/fetch.py b/third_party/android_deps/libs/org_jsoup_jsoup/3pp/fetch.py
index 0fb00215..3cd2195 100755
--- a/third_party/android_deps/libs/org_jsoup_jsoup/3pp/fetch.py
+++ b/third_party/android_deps/libs/org_jsoup_jsoup/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='org/jsoup',
                          module_name='jsoup',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/org_jsoup_jsoup/cipd.yaml b/third_party/android_deps/libs/org_jsoup_jsoup/cipd.yaml
index b390786..a379a807 100644
--- a/third_party/android_deps/libs/org_jsoup_jsoup/cipd.yaml
+++ b/third_party/android_deps/libs/org_jsoup_jsoup/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@1.15.1.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@1.15.1.cr2
 package: chromium/third_party/android_deps/libs/org_jsoup_jsoup
 description: "jsoup Java HTML Parser"
 data:
-- file: jsoup-1.15.1.jar
+- file: jsoup.jar
diff --git a/third_party/android_deps/libs/org_mockito_mockito_android/3pp/fetch.py b/third_party/android_deps/libs/org_mockito_mockito_android/3pp/fetch.py
index d96f3db0..2563c41 100755
--- a/third_party/android_deps/libs/org_mockito_mockito_android/3pp/fetch.py
+++ b/third_party/android_deps/libs/org_mockito_mockito_android/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='org/mockito',
                          module_name='mockito-android',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/org_mockito_mockito_android/cipd.yaml b/third_party/android_deps/libs/org_mockito_mockito_android/cipd.yaml
index 33c1b54..6fefb000e 100644
--- a/third_party/android_deps/libs/org_mockito_mockito_android/cipd.yaml
+++ b/third_party/android_deps/libs/org_mockito_mockito_android/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@5.19.0.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@5.19.0.cr2
 package: chromium/third_party/android_deps/libs/org_mockito_mockito_android
 description: "mockito-android"
 data:
-- file: mockito-android-5.19.0.jar
+- file: mockito-android.jar
diff --git a/third_party/android_deps/libs/org_mockito_mockito_core/3pp/fetch.py b/third_party/android_deps/libs/org_mockito_mockito_core/3pp/fetch.py
index 60dffc4c..13719b6 100755
--- a/third_party/android_deps/libs/org_mockito_mockito_core/3pp/fetch.py
+++ b/third_party/android_deps/libs/org_mockito_mockito_core/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='org/mockito',
                          module_name='mockito-core',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/org_mockito_mockito_core/cipd.yaml b/third_party/android_deps/libs/org_mockito_mockito_core/cipd.yaml
index 4ef02da8..93c1bd5 100644
--- a/third_party/android_deps/libs/org_mockito_mockito_core/cipd.yaml
+++ b/third_party/android_deps/libs/org_mockito_mockito_core/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@5.19.0.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@5.19.0.cr2
 package: chromium/third_party/android_deps/libs/org_mockito_mockito_core
 description: "mockito-core"
 data:
-- file: mockito-core-5.19.0.jar
+- file: mockito-core.jar
diff --git a/third_party/android_deps/libs/org_mockito_mockito_subclass/3pp/fetch.py b/third_party/android_deps/libs/org_mockito_mockito_subclass/3pp/fetch.py
index ee85e7a2..43aa864 100755
--- a/third_party/android_deps/libs/org_mockito_mockito_subclass/3pp/fetch.py
+++ b/third_party/android_deps/libs/org_mockito_mockito_subclass/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='org/mockito',
                          module_name='mockito-subclass',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/org_mockito_mockito_subclass/cipd.yaml b/third_party/android_deps/libs/org_mockito_mockito_subclass/cipd.yaml
index 6f6e274d..f5e36ab 100644
--- a/third_party/android_deps/libs/org_mockito_mockito_subclass/cipd.yaml
+++ b/third_party/android_deps/libs/org_mockito_mockito_subclass/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@5.19.0.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@5.19.0.cr2
 package: chromium/third_party/android_deps/libs/org_mockito_mockito_subclass
 description: "mockito-subclass"
 data:
-- file: mockito-subclass-5.19.0.jar
+- file: mockito-subclass.jar
diff --git a/third_party/android_deps/libs/org_objenesis_objenesis/3pp/fetch.py b/third_party/android_deps/libs/org_objenesis_objenesis/3pp/fetch.py
index 8135a838..692257c 100755
--- a/third_party/android_deps/libs/org_objenesis_objenesis/3pp/fetch.py
+++ b/third_party/android_deps/libs/org_objenesis_objenesis/3pp/fetch.py
@@ -18,7 +18,7 @@
                          group_name='org/objenesis',
                          module_name='objenesis',
                          file_ext='jar',
-                         patch_version='cr1',
+                         patch_version='cr2',
                          version_override=None,
                          version_filter=None)
 
diff --git a/third_party/android_deps/libs/org_objenesis_objenesis/cipd.yaml b/third_party/android_deps/libs/org_objenesis_objenesis/cipd.yaml
index d4eb96b..b383dbee 100644
--- a/third_party/android_deps/libs/org_objenesis_objenesis/cipd.yaml
+++ b/third_party/android_deps/libs/org_objenesis_objenesis/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@3.3.cr1
+# cipd create --pkg-def cipd.yaml -tag version:2@3.3.cr2
 package: chromium/third_party/android_deps/libs/org_objenesis_objenesis
 description: "Objenesis"
 data:
-- file: objenesis-3.3.jar
+- file: objenesis.jar
diff --git a/third_party/androidx/BUILD.gn b/third_party/androidx/BUILD.gn
index aa3ed21..e4b70e6 100644
--- a/third_party/androidx/BUILD.gn
+++ b/third_party/androidx/BUILD.gn
@@ -2130,6 +2130,7 @@
       ":androidx_savedstate_savedstate_compose_java",
       ":androidx_savedstate_savedstate_ktx_java",
       ":androidx_transition_transition_java",
+      ":androidx_window_window_java",
       "//third_party/android_deps:org_jetbrains_kotlinx_kotlinx_coroutines_android_java",
       "//third_party/android_deps:org_jetbrains_kotlinx_kotlinx_coroutines_core_java",
       "//third_party/android_deps:org_jspecify_jspecify_java",
diff --git a/third_party/androidx/build.gradle b/third_party/androidx/build.gradle
index 4208ee2..6b2b348 100644
--- a/third_party/androidx/build.gradle
+++ b/third_party/androidx/build.gradle
@@ -314,7 +314,7 @@
     google()
     maven {
         // This URL is generated by the fetch_all_androidx.py script.
-        url 'https://androidx.dev/snapshots/builds/14169252/artifacts/repository'
+        url 'https://androidx.dev/snapshots/builds/14172195/artifacts/repository'
     }
     mavenCentral()
 }
diff --git a/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium b/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium
index 787427f..d383d11 100644
--- a/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium
@@ -1,6 +1,6 @@
 Name: Activity
 Short Name: activity
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/activity/activity/1.12.0-SNAPSHOT/activity-1.12.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/activity/activity/1.12.0-SNAPSHOT/activity-1.12.0-20250926.054752-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium b/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium
index dd8a1aaa..db6ca01 100644
--- a/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium
@@ -1,6 +1,6 @@
 Name: Activity Compose
 Short Name: activity-compose
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/activity/activity-compose/1.12.0-SNAPSHOT/activity-compose-1.12.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/activity/activity-compose/1.12.0-SNAPSHOT/activity-compose-1.12.0-20250926.054752-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium
index 0585096..ffce9bb2 100644
--- a/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Activity Kotlin Extensions
 Short Name: activity-ktx
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/activity/activity-ktx/1.12.0-SNAPSHOT/activity-ktx-1.12.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/activity/activity-ktx/1.12.0-SNAPSHOT/activity-ktx-1.12.0-20250926.054752-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium b/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium
index 5cb6ae61..b653469 100644
--- a/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium
@@ -1,6 +1,6 @@
 Name: Experimental annotation
 Short Name: annotation-experimental
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/annotation/annotation-experimental/1.6.0-SNAPSHOT/annotation-experimental-1.6.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/annotation/annotation-experimental/1.6.0-SNAPSHOT/annotation-experimental-1.6.0-20250926.054752-1.aar
 Version: 1.6.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_annotation_annotation_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_annotation_annotation_jvm/README.chromium
index 746430f1..f3341ab 100644
--- a/third_party/androidx/committed/libs/androidx_annotation_annotation_jvm/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_annotation_annotation_jvm/README.chromium
@@ -1,6 +1,6 @@
 Name: Annotation
 Short Name: annotation-jvm
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/annotation/annotation-jvm/1.10.0-SNAPSHOT/annotation-jvm-1.10.0-20250925.204401-1.jar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/annotation/annotation-jvm/1.10.0-SNAPSHOT/annotation-jvm-1.10.0-20250926.054752-1.jar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium b/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium
index 79dde70..bd5eb03 100644
--- a/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium
@@ -1,6 +1,6 @@
 Name: AppCompat
 Short Name: appcompat
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/appcompat/appcompat/1.8.0-SNAPSHOT/appcompat-1.8.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/appcompat/appcompat/1.8.0-SNAPSHOT/appcompat-1.8.0-20250926.054752-1.aar
 Version: 1.8.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium b/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium
index 8a49051..1bcfadd 100644
--- a/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium
@@ -1,6 +1,6 @@
 Name: AppCompat Resources
 Short Name: appcompat-resources
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/appcompat/appcompat-resources/1.8.0-SNAPSHOT/appcompat-resources-1.8.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/appcompat/appcompat-resources/1.8.0-SNAPSHOT/appcompat-resources-1.8.0-20250926.054752-1.aar
 Version: 1.8.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium b/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium
index e2bf2f6..f223ac58 100644
--- a/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium
@@ -1,6 +1,6 @@
 Name: AppSearch
 Short Name: appsearch
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/appsearch/appsearch/1.2.0-SNAPSHOT/appsearch-1.2.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/appsearch/appsearch/1.2.0-SNAPSHOT/appsearch-1.2.0-20250926.054752-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium
index 52dcea6..f8f224f8 100644
--- a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium
@@ -1,6 +1,6 @@
 Name: AppSearch Builtin Types
 Short Name: appsearch-builtin-types
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/appsearch/appsearch-builtin-types/1.2.0-SNAPSHOT/appsearch-builtin-types-1.2.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/appsearch/appsearch-builtin-types/1.2.0-SNAPSHOT/appsearch-builtin-types-1.2.0-20250926.054752-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium
index 016bada..c79010c 100644
--- a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium
@@ -1,6 +1,6 @@
 Name: AppSearch Platform Storage
 Short Name: appsearch-platform-storage
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/appsearch/appsearch-platform-storage/1.2.0-SNAPSHOT/appsearch-platform-storage-1.2.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/appsearch/appsearch-platform-storage/1.2.0-SNAPSHOT/appsearch-platform-storage-1.2.0-20250926.054752-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium b/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium
index 246d07f..fd8115fa 100644
--- a/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium
@@ -1,6 +1,6 @@
 Name: Arch-Common
 Short Name: core-common
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/arch/core/core-common/2.3.0-SNAPSHOT/core-common-2.3.0-20250925.204401-1.jar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/arch/core/core-common/2.3.0-SNAPSHOT/core-common-2.3.0-20250926.054752-1.jar
 Version: 2.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium b/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium
index 7864d679..89dbd7fa 100644
--- a/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium
@@ -1,6 +1,6 @@
 Name: Arch-Runtime
 Short Name: core-runtime
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/arch/core/core-runtime/2.3.0-SNAPSHOT/core-runtime-2.3.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/arch/core/core-runtime/2.3.0-SNAPSHOT/core-runtime-2.3.0-20250926.054752-1.aar
 Version: 2.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium b/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium
index 4bf1fe35..4ab17448 100644
--- a/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium
@@ -1,6 +1,6 @@
 Name: Autofill
 Short Name: autofill
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/autofill/autofill/1.4.0-SNAPSHOT/autofill-1.4.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/autofill/autofill/1.4.0-SNAPSHOT/autofill-1.4.0-20250926.054752-1.aar
 Version: 1.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium
index fda22f8..d6e6b00 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark - Common
 Short Name: benchmark-common
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/benchmark/benchmark-common/1.5.0-SNAPSHOT/benchmark-common-1.5.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/benchmark/benchmark-common/1.5.0-SNAPSHOT/benchmark-common-1.5.0-20250926.054752-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium
index 4eaa7c0..c33529f 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark - JUnit4
 Short Name: benchmark-junit4
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/benchmark/benchmark-junit4/1.5.0-SNAPSHOT/benchmark-junit4-1.5.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/benchmark/benchmark-junit4/1.5.0-SNAPSHOT/benchmark-junit4-1.5.0-20250926.054752-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium
index c7741c5..51101721 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark - Macrobenchmark
 Short Name: benchmark-macro
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/benchmark/benchmark-macro/1.5.0-SNAPSHOT/benchmark-macro-1.5.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/benchmark/benchmark-macro/1.5.0-SNAPSHOT/benchmark-macro-1.5.0-20250926.054752-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium
index 65461cdf..d0c1006 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark - Macrobenchmark JUnit4
 Short Name: benchmark-macro-junit4
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/benchmark/benchmark-macro-junit4/1.5.0-SNAPSHOT/benchmark-macro-junit4-1.5.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/benchmark/benchmark-macro-junit4/1.5.0-SNAPSHOT/benchmark-macro-junit4-1.5.0-20250926.054752-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium
index 6819034..aed4e85 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark TraceProcessor
 Short Name: benchmark-traceprocessor-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/benchmark/benchmark-traceprocessor-android/1.5.0-SNAPSHOT/benchmark-traceprocessor-android-1.5.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/benchmark/benchmark-traceprocessor-android/1.5.0-SNAPSHOT/benchmark-traceprocessor-android-1.5.0-20250926.054752-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium b/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium
index 508fcec2..d4ff4f6f9 100644
--- a/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium
@@ -1,6 +1,6 @@
 Name: Biometric
 Short Name: biometric
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/biometric/biometric/1.4.0-SNAPSHOT/biometric-1.4.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/biometric/biometric/1.4.0-SNAPSHOT/biometric-1.4.0-20250926.054752-1.aar
 Version: 1.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_browser_browser/README.chromium b/third_party/androidx/committed/libs/androidx_browser_browser/README.chromium
index 512ea08..0aea4e2b 100644
--- a/third_party/androidx/committed/libs/androidx_browser_browser/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_browser_browser/README.chromium
@@ -1,6 +1,6 @@
 Name: Browser
 Short Name: browser
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/browser/browser/1.10.0-SNAPSHOT/browser-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/browser/browser/1.10.0-SNAPSHOT/browser-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium b/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium
index 02ba0299..6d2bfd96 100644
--- a/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium
@@ -1,6 +1,6 @@
 Name: CardView
 Short Name: cardview
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/cardview/cardview/1.1.0-SNAPSHOT/cardview-1.1.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/cardview/cardview/1.1.0-SNAPSHOT/cardview-1.1.0-20250926.054752-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium
index b9387c35..f24cfea 100644
--- a/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium
@@ -1,6 +1,6 @@
 Name: collections
 Short Name: collection-jvm
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/collection/collection-jvm/1.6.0-SNAPSHOT/collection-jvm-1.6.0-20250925.204401-1.jar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/collection/collection-jvm/1.6.0-SNAPSHOT/collection-jvm-1.6.0-20250926.054752-1.jar
 Version: 1.6.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium
index a47c0644..1bc9747 100644
--- a/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Collections Kotlin Extensions
 Short Name: collection-ktx
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/collection/collection-ktx/1.6.0-SNAPSHOT/collection-ktx-1.6.0-20250925.204401-1.jar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/collection/collection-ktx/1.6.0-SNAPSHOT/collection-ktx-1.6.0-20250926.054752-1.jar
 Version: 1.6.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium
index 40f60d9..df8cdd5a 100644
--- a/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Animation
 Short Name: animation-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/animation/animation-android/1.10.0-SNAPSHOT/animation-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/animation/animation-android/1.10.0-SNAPSHOT/animation-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium
index 3951484c..98d3197 100644
--- a/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Animation Core
 Short Name: animation-core-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/animation/animation-core-android/1.10.0-SNAPSHOT/animation-core-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/animation/animation-core-android/1.10.0-SNAPSHOT/animation-core-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium
index 38afb5c..1e05aa0 100644
--- a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Foundation
 Short Name: foundation-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/foundation/foundation-android/1.10.0-SNAPSHOT/foundation-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/foundation/foundation-android/1.10.0-SNAPSHOT/foundation-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium
index 675d876..f9fe6b9 100644
--- a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Layouts
 Short Name: foundation-layout-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/foundation/foundation-layout-android/1.10.0-SNAPSHOT/foundation-layout-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/foundation/foundation-layout-android/1.10.0-SNAPSHOT/foundation-layout-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium
index d41d58fc..7e6e916 100644
--- a/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Material3 Components
 Short Name: material3-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/material3/material3-android/1.5.0-SNAPSHOT/material3-android-1.5.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/material3/material3-android/1.5.0-SNAPSHOT/material3-android-1.5.0-20250926.054752-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium
index 088dc1f..e1847e4 100644
--- a/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Material Ripple
 Short Name: material-ripple-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/material/material-ripple-android/1.10.0-SNAPSHOT/material-ripple-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/material/material-ripple-android/1.10.0-SNAPSHOT/material-ripple-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium
index 8400488..47601318 100644
--- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Runtime
 Short Name: runtime-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/runtime/runtime-android/1.10.0-SNAPSHOT/runtime-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/runtime/runtime-android/1.10.0-SNAPSHOT/runtime-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium
index edd3950..cdf2e6e0 100644
--- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Runtime Annotation
 Short Name: runtime-annotation-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/runtime/runtime-annotation-android/1.10.0-SNAPSHOT/runtime-annotation-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/runtime/runtime-annotation-android/1.10.0-SNAPSHOT/runtime-annotation-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium
index 852cbcec..97701a8 100644
--- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Runtime Retain
 Short Name: runtime-retain-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/runtime/runtime-retain-android/1.10.0-SNAPSHOT/runtime-retain-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/runtime/runtime-retain-android/1.10.0-SNAPSHOT/runtime-retain-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium
index 30cade4..d1818089 100644
--- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Saveable
 Short Name: runtime-saveable-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/runtime/runtime-saveable-android/1.10.0-SNAPSHOT/runtime-saveable-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/runtime/runtime-saveable-android/1.10.0-SNAPSHOT/runtime-saveable-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium
index b7a2363..51d74c4 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose UI
 Short Name: ui-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/ui/ui-android/1.10.0-SNAPSHOT/ui-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/ui/ui-android/1.10.0-SNAPSHOT/ui-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium
index 90eea114..6312a455 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Geometry
 Short Name: ui-geometry-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/ui/ui-geometry-android/1.10.0-SNAPSHOT/ui-geometry-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/ui/ui-geometry-android/1.10.0-SNAPSHOT/ui-geometry-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium
index 5df711f..d5237ff 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Graphics
 Short Name: ui-graphics-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/ui/ui-graphics-android/1.10.0-SNAPSHOT/ui-graphics-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/ui/ui-graphics-android/1.10.0-SNAPSHOT/ui-graphics-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium
index 6521d8c..b784a563 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Testing
 Short Name: ui-test-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/ui/ui-test-android/1.10.0-SNAPSHOT/ui-test-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/ui/ui-test-android/1.10.0-SNAPSHOT/ui-test-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium
index b3375d1..eaa92146 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Testing for JUnit4
 Short Name: ui-test-junit4-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/ui/ui-test-junit4-android/1.10.0-SNAPSHOT/ui-test-junit4-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/ui/ui-test-junit4-android/1.10.0-SNAPSHOT/ui-test-junit4-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium
index 907103d9..e0fe9aa8 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Testing manifest dependency
 Short Name: ui-test-manifest
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/ui/ui-test-manifest/1.10.0-SNAPSHOT/ui-test-manifest-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/ui/ui-test-manifest/1.10.0-SNAPSHOT/ui-test-manifest-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium
index 30a5d4a1..fa29d94 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose UI Text
 Short Name: ui-text-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/ui/ui-text-android/1.10.0-SNAPSHOT/ui-text-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/ui/ui-text-android/1.10.0-SNAPSHOT/ui-text-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium
index 4574130..ba7af77 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Google Fonts integration
 Short Name: ui-text-google-fonts
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/ui/ui-text-google-fonts/1.10.0-SNAPSHOT/ui-text-google-fonts-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/ui/ui-text-google-fonts/1.10.0-SNAPSHOT/ui-text-google-fonts-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium
index ed4c9cd..90ec56d 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Unit
 Short Name: ui-unit-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/ui/ui-unit-android/1.10.0-SNAPSHOT/ui-unit-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/ui/ui-unit-android/1.10.0-SNAPSHOT/ui-unit-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium
index 7c6e310..da05fb7 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Util
 Short Name: ui-util-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/compose/ui/ui-util-android/1.10.0-SNAPSHOT/ui-util-android-1.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/compose/ui/ui-util-android/1.10.0-SNAPSHOT/ui-util-android-1.10.0-20250926.054752-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium
index 614c474a..0ef5012 100644
--- a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium
@@ -1,6 +1,6 @@
 Name: ConstraintLayout
 Short Name: constraintlayout
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/constraintlayout/constraintlayout/2.3.0-SNAPSHOT/constraintlayout-2.3.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/constraintlayout/constraintlayout/2.3.0-SNAPSHOT/constraintlayout-2.3.0-20250926.054752-1.aar
 Version: 2.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium
index 87c470e..a24fc658 100644
--- a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium
@@ -1,6 +1,6 @@
 Name: ConstraintLayout Core
 Short Name: constraintlayout-core
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/constraintlayout/constraintlayout-core/1.2.0-SNAPSHOT/constraintlayout-core-1.2.0-20250925.204401-1.jar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/constraintlayout/constraintlayout-core/1.2.0-SNAPSHOT/constraintlayout-core-1.2.0-20250926.054752-1.jar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_core_core/README.chromium b/third_party/androidx/committed/libs/androidx_core_core/README.chromium
index de6334b..6b50f0f7 100644
--- a/third_party/androidx/committed/libs/androidx_core_core/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_core_core/README.chromium
@@ -1,6 +1,6 @@
 Name: Core
 Short Name: core
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/core/core/1.18.0-SNAPSHOT/core-1.18.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/core/core/1.18.0-SNAPSHOT/core-1.18.0-20250926.054752-1.aar
 Version: 1.18.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium
index 8b88e91..51dd2317 100644
--- a/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Core Kotlin Extensions
 Short Name: core-ktx
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/core/core-ktx/1.18.0-SNAPSHOT/core-ktx-1.18.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/core/core-ktx/1.18.0-SNAPSHOT/core-ktx-1.18.0-20250926.054752-1.aar
 Version: 1.18.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium b/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium
index 9e1578b9..4467bb0 100644
--- a/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.core:core-viewtree
 Short Name: core-viewtree
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/core/core-viewtree/1.1.0-SNAPSHOT/core-viewtree-1.1.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/core/core-viewtree/1.1.0-SNAPSHOT/core-viewtree-1.1.0-20250926.054752-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium
index 4828a84..630e77a 100644
--- a/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium
@@ -1,6 +1,6 @@
 Name: Credentials
 Short Name: credentials
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/credentials/credentials/1.6.0-SNAPSHOT/credentials-1.6.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/credentials/credentials/1.6.0-SNAPSHOT/credentials-1.6.0-20250926.054752-1.aar
 Version: 1.6.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium
index 1524bfb..18f1006 100644
--- a/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium
@@ -1,6 +1,6 @@
 Name: Credentials Play Services Auth
 Short Name: credentials-play-services-auth
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/credentials/credentials-play-services-auth/1.6.0-SNAPSHOT/credentials-play-services-auth-1.6.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/credentials/credentials-play-services-auth/1.6.0-SNAPSHOT/credentials-play-services-auth-1.6.0-20250926.054752-1.aar
 Version: 1.6.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium
index da7b68f5..7f50a39 100644
--- a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.credentials.registry:registry-provider
 Short Name: registry-provider
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/credentials/registry/registry-provider/1.0.0-SNAPSHOT/registry-provider-1.0.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/credentials/registry/registry-provider/1.0.0-SNAPSHOT/registry-provider-1.0.0-20250926.054752-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium
index 3314572..348380b 100644
--- a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.credentials.registry:registry-provider-play-services
 Short Name: registry-provider-play-services
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/credentials/registry/registry-provider-play-services/1.0.0-SNAPSHOT/registry-provider-play-services-1.0.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/credentials/registry/registry-provider-play-services/1.0.0-SNAPSHOT/registry-provider-play-services-1.0.0-20250926.054752-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium b/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium
index f6aba2b..52bbb65 100644
--- a/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium
@@ -1,6 +1,6 @@
 Name: Cursor Adapter
 Short Name: cursoradapter
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/cursoradapter/cursoradapter/1.1.0-SNAPSHOT/cursoradapter-1.1.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/cursoradapter/cursoradapter/1.1.0-SNAPSHOT/cursoradapter-1.1.0-20250926.054752-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium
index fe0cfbb..dac20cb 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium
@@ -1,6 +1,6 @@
 Name: DataStore
 Short Name: datastore-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/datastore/datastore-android/1.2.0-SNAPSHOT/datastore-android-1.2.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/datastore/datastore-android/1.2.0-SNAPSHOT/datastore-android-1.2.0-20250926.054752-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium
index e7bc82d..43423a8 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium
@@ -1,6 +1,6 @@
 Name: DataStore Core
 Short Name: datastore-core-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/datastore/datastore-core-android/1.2.0-SNAPSHOT/datastore-core-android-1.2.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/datastore/datastore-core-android/1.2.0-SNAPSHOT/datastore-core-android-1.2.0-20250926.054752-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium
index f4f4104..f402e13 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium
@@ -1,6 +1,6 @@
 Name: DataStore Core Okio
 Short Name: datastore-core-okio-jvm
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/datastore/datastore-core-okio-jvm/1.2.0-SNAPSHOT/datastore-core-okio-jvm-1.2.0-20250925.204401-1.jar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/datastore/datastore-core-okio-jvm/1.2.0-SNAPSHOT/datastore-core-okio-jvm-1.2.0-20250926.054752-1.jar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium
index 424ec9ca..cecfdf0 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Preferences DataStore
 Short Name: datastore-preferences-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/datastore/datastore-preferences-android/1.2.0-SNAPSHOT/datastore-preferences-android-1.2.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/datastore/datastore-preferences-android/1.2.0-SNAPSHOT/datastore-preferences-android-1.2.0-20250926.054752-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium
index efb9a9b9..bef9698 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Preferences DataStore Core
 Short Name: datastore-preferences-core-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/datastore/datastore-preferences-core-android/1.2.0-SNAPSHOT/datastore-preferences-core-android-1.2.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/datastore/datastore-preferences-core-android/1.2.0-SNAPSHOT/datastore-preferences-core-android-1.2.0-20250926.054752-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium
index 2c4bd51..9d0c7ed7 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium
@@ -1,6 +1,6 @@
 Name: Preferences External Protobuf
 Short Name: datastore-preferences-external-protobuf
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/datastore/datastore-preferences-external-protobuf/1.2.0-SNAPSHOT/datastore-preferences-external-protobuf-1.2.0-20250925.204401-1.jar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/datastore/datastore-preferences-external-protobuf/1.2.0-SNAPSHOT/datastore-preferences-external-protobuf-1.2.0-20250926.054752-1.jar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: BSD-3-Clause
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium
index 444914a91..f2a51b5 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium
@@ -1,6 +1,6 @@
 Name: Preferences DataStore Proto
 Short Name: datastore-preferences-proto
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/datastore/datastore-preferences-proto/1.2.0-SNAPSHOT/datastore-preferences-proto-1.2.0-20250925.204401-1.jar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/datastore/datastore-preferences-proto/1.2.0-SNAPSHOT/datastore-preferences-proto-1.2.0-20250926.054752-1.jar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium b/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium
index b27a9b1e..68052f26f 100644
--- a/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium
@@ -1,6 +1,6 @@
 Name: Drawer Layout
 Short Name: drawerlayout
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/drawerlayout/drawerlayout/1.3.0-SNAPSHOT/drawerlayout-1.3.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/drawerlayout/drawerlayout/1.3.0-SNAPSHOT/drawerlayout-1.3.0-20250926.054752-1.aar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_emoji_emoji/README.chromium b/third_party/androidx/committed/libs/androidx_emoji_emoji/README.chromium
index ade6370..a05d84a 100644
--- a/third_party/androidx/committed/libs/androidx_emoji_emoji/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_emoji_emoji/README.chromium
@@ -1,6 +1,6 @@
 Name: Emoji
 Short Name: emoji
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/emoji/emoji/1.2.0-SNAPSHOT/emoji-1.2.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/emoji/emoji/1.2.0-SNAPSHOT/emoji-1.2.0-20250926.054752-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0, SIL Open Font License, Version 1.1, Unicode, Inc. License
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium
index 604ed39d..5f043874 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium
@@ -1,6 +1,6 @@
 Name: fragment
 Short Name: fragment
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/fragment/fragment/1.9.0-SNAPSHOT/fragment-1.9.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/fragment/fragment/1.9.0-SNAPSHOT/fragment-1.9.0-20250926.054752-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium
index 6ded157a..d741153 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium
@@ -1,6 +1,6 @@
 Name: Fragment Compose
 Short Name: fragment-compose
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/fragment/fragment-compose/1.9.0-SNAPSHOT/fragment-compose-1.9.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/fragment/fragment-compose/1.9.0-SNAPSHOT/fragment-compose-1.9.0-20250926.054752-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium
index 1935946..d4f71d81 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Fragment Kotlin Extensions
 Short Name: fragment-ktx
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/fragment/fragment-ktx/1.9.0-SNAPSHOT/fragment-ktx-1.9.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/fragment/fragment-ktx/1.9.0-SNAPSHOT/fragment-ktx-1.9.0-20250926.054752-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium
index 98e60a9..d1fdeeae 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium
@@ -1,6 +1,6 @@
 Name: Fragment Testing Extensions
 Short Name: fragment-testing
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/fragment/fragment-testing/1.9.0-SNAPSHOT/fragment-testing-1.9.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/fragment/fragment-testing/1.9.0-SNAPSHOT/fragment-testing-1.9.0-20250926.054752-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium
index d95e14cd..2a01890 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium
@@ -1,6 +1,6 @@
 Name: Fragment Testing Manifest dependency
 Short Name: fragment-testing-manifest
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/fragment/fragment-testing-manifest/1.9.0-SNAPSHOT/fragment-testing-manifest-1.9.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/fragment/fragment-testing-manifest/1.9.0-SNAPSHOT/fragment-testing-manifest-1.9.0-20250926.054752-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium b/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium
index 9370c06..721ce627 100644
--- a/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium
@@ -1,6 +1,6 @@
 Name: Android Graphics Path
 Short Name: graphics-path
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/graphics/graphics-path/1.1.0-SNAPSHOT/graphics-path-1.1.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/graphics/graphics-path/1.1.0-SNAPSHOT/graphics-path-1.1.0-20250926.054752-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_graphics_graphics_shapes_android/README.chromium b/third_party/androidx/committed/libs/androidx_graphics_graphics_shapes_android/README.chromium
index 129bdb5..27cfad1 100644
--- a/third_party/androidx/committed/libs/androidx_graphics_graphics_shapes_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_graphics_graphics_shapes_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Graphics Shapes
 Short Name: graphics-shapes-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/graphics/graphics-shapes-android/1.1.0-SNAPSHOT/graphics-shapes-android-1.1.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/graphics/graphics-shapes-android/1.1.0-SNAPSHOT/graphics-shapes-android-1.1.0-20250926.054752-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium b/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium
index 07c01fb0..2b0cf193 100644
--- a/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium
@@ -1,6 +1,6 @@
 Name: Interpolators
 Short Name: interpolator
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/interpolator/interpolator/1.1.0-SNAPSHOT/interpolator-1.1.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/interpolator/interpolator/1.1.0-SNAPSHOT/interpolator-1.1.0-20250926.054752-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium
index 7f35a01..69be6c23 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle-Common for Java 8
 Short Name: lifecycle-common-java8
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-common-java8/2.10.0-SNAPSHOT/lifecycle-common-java8-2.10.0-20250925.204401-1.jar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-common-java8/2.10.0-SNAPSHOT/lifecycle-common-java8-2.10.0-20250926.054752-1.jar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium
index 92df7a6b..953b62e 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle-Common
 Short Name: lifecycle-common-jvm
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-common-jvm/2.10.0-SNAPSHOT/lifecycle-common-jvm-2.10.0-20250925.204401-1.jar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-common-jvm/2.10.0-SNAPSHOT/lifecycle-common-jvm-2.10.0-20250926.054752-1.jar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium
index 4fc85adf..cd878ab2 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle LiveData
 Short Name: lifecycle-livedata
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-livedata/2.10.0-SNAPSHOT/lifecycle-livedata-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-livedata/2.10.0-SNAPSHOT/lifecycle-livedata-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium
index 1ff025ea..c061ef39 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle LiveData Core
 Short Name: lifecycle-livedata-core
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core/2.10.0-SNAPSHOT/lifecycle-livedata-core-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core/2.10.0-SNAPSHOT/lifecycle-livedata-core-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium
index a6cc4b7..40ffa04 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: LiveData Core Kotlin Extensions
 Short Name: lifecycle-livedata-core-ktx
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core-ktx/2.10.0-SNAPSHOT/lifecycle-livedata-core-ktx-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core-ktx/2.10.0-SNAPSHOT/lifecycle-livedata-core-ktx-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium
index 76ba107..6fc78f5 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: LiveData Kotlin Extensions
 Short Name: lifecycle-livedata-ktx
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-livedata-ktx/2.10.0-SNAPSHOT/lifecycle-livedata-ktx-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-livedata-ktx/2.10.0-SNAPSHOT/lifecycle-livedata-ktx-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium
index 41619d7c..a2805aa2 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Process
 Short Name: lifecycle-process
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-process/2.10.0-SNAPSHOT/lifecycle-process-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-process/2.10.0-SNAPSHOT/lifecycle-process-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium
index 5d367c11..cb5b6710 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Runtime
 Short Name: lifecycle-runtime-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-runtime-android/2.10.0-SNAPSHOT/lifecycle-runtime-android-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-runtime-android/2.10.0-SNAPSHOT/lifecycle-runtime-android-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium
index e349217..325fd26e 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Runtime Compose
 Short Name: lifecycle-runtime-compose-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-runtime-compose-android/2.10.0-SNAPSHOT/lifecycle-runtime-compose-android-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-runtime-compose-android/2.10.0-SNAPSHOT/lifecycle-runtime-compose-android-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium
index 8f7c4f542..27ad83b 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Kotlin Extensions
 Short Name: lifecycle-runtime-ktx-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-runtime-ktx-android/2.10.0-SNAPSHOT/lifecycle-runtime-ktx-android-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-runtime-ktx-android/2.10.0-SNAPSHOT/lifecycle-runtime-ktx-android-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium
index eb2beb3..e4ce45a 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Service
 Short Name: lifecycle-service
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-service/2.10.0-SNAPSHOT/lifecycle-service-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-service/2.10.0-SNAPSHOT/lifecycle-service-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium
index e0e62e0..57d862d 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle ViewModel
 Short Name: lifecycle-viewmodel-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-android/2.10.0-SNAPSHOT/lifecycle-viewmodel-android-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-android/2.10.0-SNAPSHOT/lifecycle-viewmodel-android-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium
index 737f9ab..2b31cbb3 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle ViewModel Compose
 Short Name: lifecycle-viewmodel-compose-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-compose-android/2.10.0-SNAPSHOT/lifecycle-viewmodel-compose-android-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-compose-android/2.10.0-SNAPSHOT/lifecycle-viewmodel-compose-android-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium
index 750a43e..59a39ba 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle ViewModel Kotlin Extensions
 Short Name: lifecycle-viewmodel-ktx
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-ktx/2.10.0-SNAPSHOT/lifecycle-viewmodel-ktx-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-ktx/2.10.0-SNAPSHOT/lifecycle-viewmodel-ktx-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium
index eee3cae..4bc9c4d 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle ViewModel with SavedState
 Short Name: lifecycle-viewmodel-savedstate-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-savedstate-android/2.10.0-SNAPSHOT/lifecycle-viewmodel-savedstate-android-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-savedstate-android/2.10.0-SNAPSHOT/lifecycle-viewmodel-savedstate-android-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium b/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium
index 34c6217b..3c1bfaf 100644
--- a/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium
@@ -1,6 +1,6 @@
 Name: loader
 Short Name: loader
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/loader/loader/1.2.0-SNAPSHOT/loader-1.2.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/loader/loader/1.2.0-SNAPSHOT/loader-1.2.0-20250926.054752-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_media_media/README.chromium b/third_party/androidx/committed/libs/androidx_media_media/README.chromium
index 84ca99e..9ab45d6f 100644
--- a/third_party/androidx/committed/libs/androidx_media_media/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_media_media/README.chromium
@@ -1,6 +1,6 @@
 Name: Media
 Short Name: media
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/media/media/1.8.0-SNAPSHOT/media-1.8.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/media/media/1.8.0-SNAPSHOT/media-1.8.0-20250926.054752-1.aar
 Version: 1.8.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium
index cf738cc..8c0eee4 100644
--- a/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Navigation Common
 Short Name: navigation-common-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/navigation/navigation-common-android/2.10.0-SNAPSHOT/navigation-common-android-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/navigation/navigation-common-android/2.10.0-SNAPSHOT/navigation-common-android-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium
index 5afe786e..767bb48 100644
--- a/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Navigation
 Short Name: navigation-compose-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/navigation/navigation-compose-android/2.10.0-SNAPSHOT/navigation-compose-android-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/navigation/navigation-compose-android/2.10.0-SNAPSHOT/navigation-compose-android-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium
index 81bb4e7..f952561a 100644
--- a/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Navigation Runtime
 Short Name: navigation-runtime-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/navigation/navigation-runtime-android/2.10.0-SNAPSHOT/navigation-runtime-android-2.10.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/navigation/navigation-runtime-android/2.10.0-SNAPSHOT/navigation-runtime-android-2.10.0-20250926.054752-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium
index 287a315c..2ff82cc 100644
--- a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Navigation Event
 Short Name: navigationevent-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/navigationevent/navigationevent-android/1.0.0-SNAPSHOT/navigationevent-android-1.0.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/navigationevent/navigationevent-android/1.0.0-SNAPSHOT/navigationevent-android-1.0.0-20250926.054752-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium
index b6fec1e..09de2021 100644
--- a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: NavigationEvent Compose
 Short Name: navigationevent-compose-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/navigationevent/navigationevent-compose-android/1.0.0-SNAPSHOT/navigationevent-compose-android-1.0.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/navigationevent/navigationevent-compose-android/1.0.0-SNAPSHOT/navigationevent-compose-android-1.0.0-20250926.054752-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium
index 2b5c4435..35825e87 100644
--- a/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Paging-Common
 Short Name: paging-common-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/paging/paging-common-android/3.4.0-SNAPSHOT/paging-common-android-3.4.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/paging/paging-common-android/3.4.0-SNAPSHOT/paging-common-android-3.4.0-20250926.054752-1.aar
 Version: 3.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium
index 0758d69..b92c83d 100644
--- a/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Paging-Common Kotlin Extensions
 Short Name: paging-common-ktx
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/paging/paging-common-ktx/3.4.0-SNAPSHOT/paging-common-ktx-3.4.0-20250925.204401-1.jar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/paging/paging-common-ktx/3.4.0-SNAPSHOT/paging-common-ktx-3.4.0-20250926.054752-1.jar
 Version: 3.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium
index ff642813..9a8e61cc 100644
--- a/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Paging-Compose
 Short Name: paging-compose-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/paging/paging-compose-android/3.4.0-SNAPSHOT/paging-compose-android-3.4.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/paging/paging-compose-android/3.4.0-SNAPSHOT/paging-compose-android-3.4.0-20250926.054752-1.aar
 Version: 3.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium
index cb85e13..c4725d6 100644
--- a/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium
@@ -1,6 +1,6 @@
 Name: Paging-Runtime
 Short Name: paging-runtime
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/paging/paging-runtime/3.4.0-SNAPSHOT/paging-runtime-3.4.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/paging/paging-runtime/3.4.0-SNAPSHOT/paging-runtime-3.4.0-20250926.054752-1.aar
 Version: 3.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium b/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium
index e80138c..c03644e1 100644
--- a/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium
@@ -1,6 +1,6 @@
 Name: Palette
 Short Name: palette
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/palette/palette/1.1.0-SNAPSHOT/palette-1.1.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/palette/palette/1.1.0-SNAPSHOT/palette-1.1.0-20250926.054752-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium b/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium
index 7f9ae4c..7cf8b0d 100644
--- a/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium
@@ -1,6 +1,6 @@
 Name: Preference
 Short Name: preference
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/preference/preference/1.3.0-SNAPSHOT/preference-1.3.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/preference/preference/1.3.0-SNAPSHOT/preference-1.3.0-20250926.054752-1.aar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium b/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium
index f964f71a..6202482 100644
--- a/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium
@@ -1,6 +1,6 @@
 Name: Profile Installer
 Short Name: profileinstaller
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/profileinstaller/profileinstaller/1.5.0-SNAPSHOT/profileinstaller-1.5.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/profileinstaller/profileinstaller/1.5.0-SNAPSHOT/profileinstaller-1.5.0-20250926.054752-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium b/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium
index 839b983..bbe0e01 100644
--- a/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium
@@ -1,6 +1,6 @@
 Name: RecyclerView
 Short Name: recyclerview
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/recyclerview/recyclerview/1.5.0-SNAPSHOT/recyclerview-1.5.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/recyclerview/recyclerview/1.5.0-SNAPSHOT/recyclerview-1.5.0-20250926.054752-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium b/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium
index f3e989fb..14b5f63 100644
--- a/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium
@@ -1,6 +1,6 @@
 Name: Resource Inspection - Annotations
 Short Name: resourceinspection-annotation
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/resourceinspection/resourceinspection-annotation/1.1.0-SNAPSHOT/resourceinspection-annotation-1.1.0-20250925.204401-1.jar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/resourceinspection/resourceinspection-annotation/1.1.0-SNAPSHOT/resourceinspection-annotation-1.1.0-20250926.054752-1.jar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium
index 063db74..c543c15d 100644
--- a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Saved State
 Short Name: savedstate-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/savedstate/savedstate-android/1.4.0-SNAPSHOT/savedstate-android-1.4.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/savedstate/savedstate-android/1.4.0-SNAPSHOT/savedstate-android-1.4.0-20250926.054752-1.aar
 Version: 1.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium
index 9c1b5da..06b1645 100644
--- a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Saved State Compose
 Short Name: savedstate-compose-android
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/savedstate/savedstate-compose-android/1.4.0-SNAPSHOT/savedstate-compose-android-1.4.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/savedstate/savedstate-compose-android/1.4.0-SNAPSHOT/savedstate-compose-android-1.4.0-20250926.054752-1.aar
 Version: 1.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium
index 4b521af..ac0afc95 100644
--- a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: SavedState Kotlin Extensions
 Short Name: savedstate-ktx
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/savedstate/savedstate-ktx/1.4.0-SNAPSHOT/savedstate-ktx-1.4.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/savedstate/savedstate-ktx/1.4.0-SNAPSHOT/savedstate-ktx-1.4.0-20250926.054752-1.aar
 Version: 1.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium b/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium
index 806063f7..bb1919f 100644
--- a/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium
@@ -1,6 +1,6 @@
 Name: Sliding Pane Layout
 Short Name: slidingpanelayout
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/slidingpanelayout/slidingpanelayout/1.3.0-SNAPSHOT/slidingpanelayout-1.3.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/slidingpanelayout/slidingpanelayout/1.3.0-SNAPSHOT/slidingpanelayout-1.3.0-20250926.054752-1.aar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_swiperefreshlayout_swiperefreshlayout/README.chromium b/third_party/androidx/committed/libs/androidx_swiperefreshlayout_swiperefreshlayout/README.chromium
index bfa6b98..a0ac40f 100644
--- a/third_party/androidx/committed/libs/androidx_swiperefreshlayout_swiperefreshlayout/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_swiperefreshlayout_swiperefreshlayout/README.chromium
@@ -1,6 +1,6 @@
 Name: Swipe Refresh Layout
 Short Name: swiperefreshlayout
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/swiperefreshlayout/swiperefreshlayout/1.2.0-SNAPSHOT/swiperefreshlayout-1.2.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/swiperefreshlayout/swiperefreshlayout/1.2.0-SNAPSHOT/swiperefreshlayout-1.2.0-20250926.054752-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium b/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium
index e932b38..3aea3e3 100644
--- a/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium
@@ -1,6 +1,6 @@
 Name: UIAutomator
 Short Name: uiautomator
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/test/uiautomator/uiautomator/2.4.0-SNAPSHOT/uiautomator-2.4.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/test/uiautomator/uiautomator/2.4.0-SNAPSHOT/uiautomator-2.4.0-20250926.054752-1.aar
 Version: 2.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_transition_transition/README.chromium b/third_party/androidx/committed/libs/androidx_transition_transition/README.chromium
index 14c4c99..ac8e30d8 100644
--- a/third_party/androidx/committed/libs/androidx_transition_transition/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_transition_transition/README.chromium
@@ -1,6 +1,6 @@
 Name: Transition
 Short Name: transition
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/transition/transition/1.7.0-SNAPSHOT/transition-1.7.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/transition/transition/1.7.0-SNAPSHOT/transition-1.7.0-20250926.054752-1.aar
 Version: 1.7.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium b/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium
index 4dc0cd5..542e781c 100644
--- a/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium
@@ -1,6 +1,6 @@
 Name: ViewPager2
 Short Name: viewpager2
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/viewpager2/viewpager2/1.2.0-SNAPSHOT/viewpager2-1.2.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/viewpager2/viewpager2/1.2.0-SNAPSHOT/viewpager2-1.2.0-20250926.054752-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium b/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium
index 7c462a5..4cd9db4 100644
--- a/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium
@@ -1,6 +1,6 @@
 Name: WebKit
 Short Name: webkit
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/webkit/webkit/1.15.0-SNAPSHOT/webkit-1.15.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/webkit/webkit/1.15.0-SNAPSHOT/webkit-1.15.0-20250926.054752-1.aar
 Version: 1.15.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium b/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium
index 8ee12e5..f45448a 100644
--- a/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium
@@ -1,6 +1,6 @@
 Name: WindowManager Sidecar
 Short Name: sidecar
-URL: https://androidx.dev/snapshots/builds/14169252/artifacts/repository/androidx/window/sidecar/sidecar/1.0.0-SNAPSHOT/sidecar-1.0.0-20250925.204401-1.aar
+URL: https://androidx.dev/snapshots/builds/14172195/artifacts/repository/androidx/window/sidecar/sidecar/1.0.0-SNAPSHOT/sidecar-1.0.0-20250926.054752-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/angle b/third_party/angle
index 1f40285..5e51d8e 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 1f40285c90cd45ae6238c57f30d0ec8c8d6e2e4b
+Subproject commit 5e51d8e71c08a84ad81080036c6b25cc36b53b1b
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index e39db15d..380d721 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -584,8 +584,6 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_texel_copy_texture_info.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_binding_layout.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_binding_layout.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_component_swizzle.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_component_swizzle.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_descriptor.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_descriptor.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_view_descriptor.cc",
@@ -1576,8 +1574,6 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_compare_function.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_compilation_message_type.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_compilation_message_type.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_component_swizzle.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_component_swizzle.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_cull_mode.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_cull_mode.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_device_lost_reason.cc",
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.cc b/third_party/blink/renderer/core/css/css_math_expression_node.cc
index 3585d85..7ebd676 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.cc
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.cc
@@ -342,10 +342,58 @@
   for (uint8_t type_index = 0u; type_index < BaseType::kNumTypes;
        ++type_index) {
     int8_t type_power = base_type_powers_[type_index];
-    types_sum += type_power;
-    if (type_power != 0) {
-      type = BaseType(type_index);
+    if (!type_power) {
+      continue;
     }
+    // If more than one base type has a non-zero power, it's intermediate.
+    if (types_sum != 0) {
+      if (type_index != kPercent) {
+        return kCalcIntermediate;
+      }
+      DCHECK(!percentage_hint_.has_value());
+      // Because we don't provide the percent hint from the "outside"
+      // as css-values expects [1], it's possible for the percentage
+      // hint to be unset even for expressions that involve percentages
+      // as well some other type, e.g.:
+      //
+      //  width: (1% * 1%) / 1px;
+      //
+      // The CSSMathType for the above expression would contain
+      // [percent -> 2, length -> -1] with a null percent hint.
+      // However, the expression is clearly valid for 'width', since
+      // the percentages resolve against lengths. To address this,
+      // we effectively deduce what the percent hint should have been
+      // from the other (non-percent) base type powers:
+      //
+      // If there is more than one non-percent base type with non-zero power,
+      // it's intermediate (handled by early-out above).
+      //
+      // Otherwise, if there is exactly *one* other (non-percent) base type
+      // with a non-zero power, then we assume that percentages were intended
+      // to resolve against that base type, and basically move the powers from
+      // "percent" to the other base type. Continuing the example from before:
+      //
+      //   [percent -> 2, length -> -1] => [percent -> 0, length -> 1]
+      //
+      // However, we do not actually need to modify `base_type_powers_`
+      // according to the above; the remainder of this function only
+      // checks `types_sum`, so it's enough to "pretend" that these numbers
+      // were combined all along.
+      //
+      // Note: This relies on kPercent appearing *last* in the enum.
+      //
+      // Note: Whether or not this deduced category is valid for
+      //       the relevant context will be determined by the call
+      //       site. For example, "width:(1% * 1%) / 1deg" would produce
+      //       kAngle for this algorithm, but ultimately be rejected.
+      //
+      // [1]
+      // https://drafts.csswg.org/css-values-4/#determine-the-type-of-a-calculation
+      types_sum += type_power;
+      break;
+    }
+    type = BaseType(type_index);
+    types_sum += type_power;
   }
   if (types_sum == 0) {
     return kCalcNumber;
@@ -508,6 +556,35 @@
   return type;
 }
 
+#if DCHECK_IS_ON()
+std::ostream& operator<<(std::ostream& os, const CSSMathType& type) {
+  if (!type.IsValid()) {
+    os << "InvalidType ";
+  }
+  os << "CSSMathType(";
+  bool first = true;
+  for (uint8_t type_index = 0; type_index < CSSMathType::BaseType::kNumTypes;
+       ++type_index) {
+    int8_t power = type.base_type_powers_[type_index];
+    if (power != 0) {
+      if (!first) {
+        os << ", ";
+      }
+      first = false;
+      os << static_cast<int>(type_index) << "^" << static_cast<int>(power);
+    }
+  }
+  if (type.percentage_hint_) {
+    if (!first) {
+      os << ", ";
+    }
+    os << "percent_hint=" << static_cast<int>(type.percentage_hint_.value());
+  }
+  os << ")";
+  return os;
+}
+#endif
+
 namespace {
 
 const PixelsAndPercent CreateClampedSamePixelsAndPercent(float value) {
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.h b/third_party/blink/renderer/core/css/css_math_expression_node.h
index 74ad970..975cc205 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.h
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.h
@@ -99,14 +99,16 @@
   DISALLOW_NEW();
 
   // https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-base-type
+  // kPercent should always be the last one as it's used to do a percent hint
+  // trick in Category().
   enum BaseType : uint8_t {
-    kPercent,
     kLength,
     kAngle,
     kTime,
     kFrequency,
     kResolution,
     kFlex,
+    kPercent,
     kNumTypes
   };
 
@@ -139,6 +141,9 @@
   CORE_EXPORT friend CSSMathType operator/(CSSMathType type1,
                                            CSSMathType type2);
   CORE_EXPORT CSSMathType operator-() const;
+#if DCHECK_IS_ON()
+  friend std::ostream& operator<<(std::ostream& os, const CSSMathType& type);
+#endif
 
  private:
   using BaseTypePowers =
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator.cc b/third_party/blink/renderer/core/css/media_query_evaluator.cc
index 8634b6b..3b2f093 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator.cc
@@ -1713,7 +1713,8 @@
   StyleResolverState state(container->GetDocument(), *container);
   PositionTryFallback query_fallback =
       StyleBuilderConverter::ConvertSinglePositionTryFallback(
-          state, value.GetCSSValue());
+          state, value.GetCSSValue(),
+          /*allow_any_keyword_in_position_area=*/true);
   query_fallback = ToPhysicalFallback(query_fallback, media_values);
   fallback = ToPhysicalFallback(fallback, media_values);
   return fallback.Matches(query_fallback);
diff --git a/third_party/blink/renderer/core/css/parser/css_at_rule_id.cc b/third_party/blink/renderer/core/css/parser/css_at_rule_id.cc
index e5f1c0f4..e2ad454 100644
--- a/third_party/blink/renderer/core/css/parser/css_at_rule_id.cc
+++ b/third_party/blink/renderer/core/css/parser/css_at_rule_id.cc
@@ -71,7 +71,8 @@
   if (EqualIgnoringASCIICase(name, "property")) {
     return CSSAtRuleID::kCSSAtRuleProperty;
   }
-  if (EqualIgnoringASCIICase(name, "route")) {
+  if (RuntimeEnabledFeatures::RouteMatchingEnabled() &&
+      EqualIgnoringASCIICase(name, "route")) {
     return CSSAtRuleID::kCSSAtRuleRoute;
   }
   if (EqualIgnoringASCIICase(name, "container")) {
diff --git a/third_party/blink/renderer/core/css/parser/css_tokenizer.cc b/third_party/blink/renderer/core/css/parser/css_tokenizer.cc
index be0208a9..1ca596c5 100644
--- a/third_party/blink/renderer/core/css/parser/css_tokenizer.cc
+++ b/third_party/blink/renderer/core/css/parser/css_tokenizer.cc
@@ -597,7 +597,7 @@
   unsigned size = 0;
 #if defined(__SSE2__) || defined(__ARM_NEON__)
   if (buffer.Is8Bit()) {
-    const LChar* ptr = UNSAFE_TODO(buffer.Characters8());
+    const LChar* ptr = buffer.Span8().data();
     while (size + 16 <= buffer.length()) {
       int8_t b __attribute__((vector_size(16)));
       UNSAFE_TODO(memcpy(&b, ptr + size, sizeof(b)));
diff --git a/third_party/blink/renderer/core/css/parser/css_tokenizer_input_stream.cc b/third_party/blink/renderer/core/css/parser/css_tokenizer_input_stream.cc
index e2303fe..6e51aef 100644
--- a/third_party/blink/renderer/core/css/parser/css_tokenizer_input_stream.cc
+++ b/third_party/blink/renderer/core/css/parser/css_tokenizer_input_stream.cc
@@ -14,13 +14,13 @@
 void CSSTokenizerInputStream::AdvanceUntilNonWhitespace() {
   // Using HTML space here rather than CSS space since we don't do preprocessing
   if (string_.Is8Bit()) {
-    const LChar* characters = UNSAFE_TODO(string_.Characters8());
+    const LChar* characters = string_.Span8().data();
     while (offset_ < string_length_ &&
            IsHTMLSpace(UNSAFE_TODO(characters[offset_]))) {
       ++offset_;
     }
   } else {
-    const UChar* characters = UNSAFE_TODO(string_.Characters16());
+    const UChar* characters = string_.Span16().data();
     while (offset_ < string_length_ &&
            IsHTMLSpace(UNSAFE_TODO(characters[offset_]))) {
       ++offset_;
@@ -50,7 +50,7 @@
   // complicated rounding machinery of CharactersToDouble(),
   // and can do with a much faster variant.
   if (start < end && string_.Is8Bit() && end - start <= 14) {
-    const LChar* ptr = UNSAFE_TODO(string_.Characters8() + offset_ + start);
+    const LChar* ptr = UNSAFE_TODO(string_.Span8().data() + offset_ + start);
     double result = ptr[0] - '0';
     for (unsigned i = 1; i < end - start; ++i) {
       result = result * 10 + (UNSAFE_TODO(ptr[i]) - '0');
diff --git a/third_party/blink/renderer/core/css/parser/css_tokenizer_input_stream.h b/third_party/blink/renderer/core/css/parser/css_tokenizer_input_stream.h
index 46992e2..c819d786 100644
--- a/third_party/blink/renderer/core/css/parser/css_tokenizer_input_stream.h
+++ b/third_party/blink/renderer/core/css/parser/css_tokenizer_input_stream.h
@@ -59,13 +59,13 @@
   template <bool characterPredicate(UChar)>
   unsigned SkipWhilePredicate(unsigned offset) {
     if (string_.Is8Bit()) {
-      const LChar* characters8 = UNSAFE_TODO(string_.Characters8());
+      const LChar* characters8 = string_.Span8().data();
       while ((offset_ + offset) < string_length_ &&
              characterPredicate(UNSAFE_TODO(characters8[offset_ + offset]))) {
         ++offset;
       }
     } else {
-      const UChar* characters16 = UNSAFE_TODO(string_.Characters16());
+      const UChar* characters16 = string_.Span16().data();
       while ((offset_ + offset) < string_length_ &&
              characterPredicate(UNSAFE_TODO(characters16[offset_ + offset]))) {
         ++offset;
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index 9ab2af8f..6c6c580 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -9253,12 +9253,14 @@
   if (first_value->GetValueID() == second_value->GetValueID()) {
     return first_value;
   }
-  if (first_value->GetValueID() == CSSValueID::kSpanAll &&
+  CSSValueID non_repeated_default =
+      allow_any_keyword ? CSSValueID::kAny : CSSValueID::kSpanAll;
+  if (first_value->GetValueID() == non_repeated_default &&
       !css_parsing_utils::IsRepeatedPositionAreaValue(
           second_value->GetValueID())) {
     return second_value;
   }
-  if (second_value->GetValueID() == CSSValueID::kSpanAll &&
+  if (second_value->GetValueID() == non_repeated_default &&
       !css_parsing_utils::IsRepeatedPositionAreaValue(
           first_value->GetValueID())) {
     return first_value;
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
index 02a72b17..aaf3b23 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
@@ -3837,7 +3837,8 @@
 
 PositionArea StyleBuilderConverter::ConvertPositionArea(
     StyleResolverState& state,
-    const CSSValue& value) {
+    const CSSValue& value,
+    bool allow_any_keyword) {
   auto extract_position_area_span = [](CSSValueID value)
       -> std::pair<PositionAreaRegion, PositionAreaRegion> {
     PositionAreaRegion start = PositionAreaRegion::kNone;
@@ -4026,18 +4027,21 @@
     return std::make_pair(start, end);
   };
 
-  if (const auto* first_value = DynamicTo<CSSIdentifierValue>(value)) {
-    CSSValueID first_keyword = first_value->GetValueID();
-    if (first_keyword == CSSValueID::kNone) {
+  if (const auto* single_keyword_value = DynamicTo<CSSIdentifierValue>(value)) {
+    CSSValueID single_keyword = single_keyword_value->GetValueID();
+    if (single_keyword == CSSValueID::kNone) {
       return PositionArea();
     }
     PositionAreaRegion span[2];
-    std::tie(span[0], span[1]) = extract_position_area_span(first_keyword);
-    if (css_parsing_utils::IsRepeatedPositionAreaValue(first_keyword)) {
+    std::tie(span[0], span[1]) = extract_position_area_span(single_keyword);
+    if (css_parsing_utils::IsRepeatedPositionAreaValue(single_keyword)) {
       return PositionArea(span[0], span[1], span[0], span[1]);
     } else {
-      return PositionArea(span[0], span[1], PositionAreaRegion::kAll,
-                          PositionAreaRegion::kAll);
+      PositionAreaRegion default_second_span = allow_any_keyword
+                                                   ? PositionAreaRegion::kAny
+                                                   : PositionAreaRegion::kAll;
+      return PositionArea(span[0], span[1], default_second_span,
+                          default_second_span);
     }
   }
 
@@ -4053,10 +4057,12 @@
 
 PositionTryFallback StyleBuilderConverter::ConvertSinglePositionTryFallback(
     StyleResolverState& state,
-    const CSSValue& value) {
+    const CSSValue& value,
+    bool allow_any_keyword_in_position_area) {
   // <'position-area'>
   if (IsA<CSSValuePair>(value) || IsA<CSSIdentifierValue>(value)) {
-    return PositionTryFallback(ConvertPositionArea(state, value));
+    return PositionTryFallback(
+        ConvertPositionArea(state, value, allow_any_keyword_in_position_area));
   }
   // [<dashed-ident> || <try-tactic>]
   const ScopedCSSName* scoped_name = nullptr;
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
index ea7ac20c..4b08af59 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
@@ -441,10 +441,13 @@
   static ScopedCSSNameList* ConvertTimelineScope(StyleResolverState&,
                                                  const CSSValue&);
 
-  static PositionArea ConvertPositionArea(StyleResolverState&, const CSSValue&);
+  static PositionArea ConvertPositionArea(StyleResolverState&,
+                                          const CSSValue&,
+                                          bool allow_any_keyword = false);
   static PositionTryFallback ConvertSinglePositionTryFallback(
       StyleResolverState&,
-      const CSSValue&);
+      const CSSValue&,
+      bool allow_any_keyword_in_position_area = false);
   static FitText ConvertFitText(StyleResolverState&, const CSSValue&);
   static TextOverflowData ConvertTextOverflow(StyleResolverState&,
                                               const CSSValue&);
diff --git a/third_party/blink/renderer/core/css/style_rule_test.cc b/third_party/blink/renderer/core/css/style_rule_test.cc
index d279b97e7..d32df86 100644
--- a/third_party/blink/renderer/core/css/style_rule_test.cc
+++ b/third_party/blink/renderer/core/css/style_rule_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
@@ -445,4 +446,14 @@
                 ->SelectorTextExpandingPseudoReferences(/*scope_id=*/0));
 }
 
+TEST_F(StyleRuleTest, RouteRuleDisabled) {
+  ScopedRouteMatchingForTest enabled(false);
+  // Test both old and new syntax.
+  StyleRuleBase* rule =
+      css_test_helpers::ParseRule(GetDocument(), "@route sixtysix {}");
+  EXPECT_FALSE(rule);
+  rule = css_test_helpers::ParseRule(GetDocument(), "@route (sixtysix) {}");
+  EXPECT_FALSE(rule);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 80bb246..db0cbec 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -9819,10 +9819,6 @@
     return true;
   }
 
-  if (!style->HasAnyPseudoElementStyles()) {
-    return false;
-  }
-
   for (PseudoElement* pseudo_element : rare_data->GetPseudoElements()) {
     if (pseudo_element->GetComputedStyle()->GetCounterDirectives()) {
       return true;
diff --git a/third_party/blink/renderer/core/dom/pseudo_element.cc b/third_party/blink/renderer/core/dom/pseudo_element.cc
index 8874e60..edd6794 100644
--- a/third_party/blink/renderer/core/dom/pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/pseudo_element.cc
@@ -412,6 +412,13 @@
   DCHECK(!nextSibling());
   DCHECK(!previousSibling());
 
+  if (const ComputedStyle* style = GetComputedStyle()) {
+    if (style->GetCounterDirectives() || style->ContainsStyle() ||
+        PseudoElementStylesAffectCounters()) {
+      GetDocument().GetStyleEngine().MarkCountersDirty();
+    }
+  }
+
   DetachLayoutTree();
   Element* parent = ParentOrShadowHostElement();
   GetDocument().AdoptIfNeeded(*this);
diff --git a/third_party/blink/renderer/core/layout/inline/caret_rect.cc b/third_party/blink/renderer/core/layout/inline/caret_rect.cc
index 319bcd14..56c7dbf 100644
--- a/third_party/blink/renderer/core/layout/inline/caret_rect.cc
+++ b/third_party/blink/renderer/core/layout/inline/caret_rect.cc
@@ -106,6 +106,11 @@
   if (offset < cursor.Current().TextEndOffset()) {
     cursor_inline_size = ComputeCharacterWidthAtOffset(
         cursor, offset - cursor.Current().TextStartOffset(), style);
+    // Fall back to 1ch.
+    if (cursor_inline_size == LayoutUnit()) {
+      cursor_inline_size =
+          LayoutUnit(style.GetFont()->PrimaryFont()->AvgCharWidth());
+    }
   } else {
     // If the next fragment is text, we need to get the width and height of
     // the first visible character in this fragment.
@@ -121,6 +126,10 @@
           {style_next.GetWritingMode(), ResolvedDirection(next)},
           next.Current().Size());
       cursor_inline_size = ComputeCharacterWidthAtOffset(next, 0, style_next);
+      if (cursor_inline_size == LayoutUnit()) {
+        cursor_inline_size =
+            LayoutUnit(style_next.GetFont()->PrimaryFont()->AvgCharWidth());
+      }
       cursor_block_size =
           converter_next.ToLogical(next.Current().Size()).block_size;
       switch (style.GetWritingMode()) {
@@ -139,16 +148,11 @@
           break;
       }
     } else {
-      // If there is no visible character after the insertion point, the UA must
-      // render the caret after the last visible character.
-      cursor_inline_size = ComputeCharacterWidthAtOffset(
-          cursor, offset - cursor.Current().TextStartOffset() - 1, style);
-    }
+      // The width of the block and underscore carets should be 1ch if
+      // this information is impractical to determine.
+      cursor_inline_size =
+          LayoutUnit(style.GetFont()->PrimaryFont()->AvgCharWidth());
   }
-  // When the inline size is zero, e.g. in the case that character is
-  // "&ZeroWidthSpace;", the inline size falls back to bar width.
-  if (cursor_inline_size == LayoutUnit()) {
-    cursor_inline_size = caret_width;
   }
   caret_rect.offset.block_offset = cursor_block_offset;
 
diff --git a/third_party/blink/renderer/core/svg/properties/svg_list_property_helper.h b/third_party/blink/renderer/core/svg/properties/svg_list_property_helper.h
index f2e820a..17742a5e3 100644
--- a/third_party/blink/renderer/core/svg/properties/svg_list_property_helper.h
+++ b/third_party/blink/renderer/core/svg/properties/svg_list_property_helper.h
@@ -54,8 +54,9 @@
     explicit const_iterator(SVGListPropertyBase::const_iterator wrapped)
         : wrapped_(wrapped) {}
 
-    const_iterator& operator++() {
-      UNSAFE_TODO(++wrapped_);
+    UNSAFE_BUFFER_USAGE const_iterator& operator++() {
+      // SAFETY: This function exposes this unsafety.
+      UNSAFE_BUFFERS(++wrapped_);
       return *this;
     }
     bool operator==(const const_iterator& other) const {
diff --git a/third_party/blink/renderer/core/svg/svg_poly_element.cc b/third_party/blink/renderer/core/svg/svg_poly_element.cc
index 297fb4b..3a7ba48 100644
--- a/third_party/blink/renderer/core/svg/svg_poly_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_poly_element.cc
@@ -56,14 +56,10 @@
   if (points_value->IsEmpty())
     return builder;
 
-  auto it = points_value->begin();
-  auto it_end = points_value->end();
-  DCHECK(it != it_end);
-  builder.MoveTo((*it)->Value());
-  ++it;
-
-  for (; it != it_end; ++it)
-    builder.LineTo((*it)->Value());
+  builder.MoveTo(points_value->at(0)->Value());
+  for (uint32_t i = 1; i < points_value->length(); ++i) {
+    builder.LineTo(points_value->at(i)->Value());
+  }
 
   return builder;
 }
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index c4cc4762..0abfa02 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -558,7 +558,6 @@
     "payments/payment_test_helper.h",
     "payments/payments_validators_test.cc",
     "payments/secure_payment_confirmation_helper_test.cc",
-    "peerconnection/byte_buffer_queue_test.cc",
     "peerconnection/media_stream_remote_video_source_test.cc",
     "peerconnection/media_stream_track_metrics_test.cc",
     "peerconnection/media_stream_video_webrtc_sink_test.cc",
diff --git a/third_party/blink/renderer/modules/mediastream/media_constraints.cc b/third_party/blink/renderer/modules/mediastream/media_constraints.cc
index a982c8b..6e4c556c9 100644
--- a/third_party/blink/renderer/modules/mediastream/media_constraints.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_constraints.cc
@@ -53,8 +53,9 @@
   if (builder.length() > 1) {
     builder.Append(", ");
   }
+  builder.Append("\"");
   builder.Append(name);
-  builder.Append(": ");
+  builder.Append("\": ");
   builder.AppendNumber(value);
 }
 
@@ -69,8 +70,9 @@
   if (builder.length() > 1) {
     builder.Append(", ");
   }
+  builder.Append("\"");
   builder.Append(name);
-  builder.Append(": ");
+  builder.Append("\": ");
   builder.Append(value);
 }
 
@@ -84,8 +86,9 @@
   if (builder.length() > 1) {
     builder.Append(", ");
   }
+  builder.Append("\"");
   builder.Append(name);
-  builder.Append(": ");
+  builder.Append("\": ");
   if (value) {
     builder.Append("true");
   } else {
@@ -163,7 +166,7 @@
       if (builder.length() > 1) {
         builder.Append(", ");
       }
-      builder.Append("advanced: [");
+      builder.Append("\"advanced\": [");
       bool first = true;
       for (const auto& constraint_set : Advanced()) {
         if (!first) {
@@ -371,7 +374,7 @@
   StringBuilder builder;
   builder.Append('{');
   if (!ideal_.empty()) {
-    builder.Append("ideal: [");
+    builder.Append("\"ideal\": [");
     bool first = true;
     for (const auto& iter : ideal_) {
       if (!first) {
@@ -388,7 +391,7 @@
     if (builder.length() > 1) {
       builder.Append(", ");
     }
-    builder.Append("exact: [");
+    builder.Append("\"exact\": [");
     bool first = true;
     for (const auto& iter : exact_) {
       if (!first) {
@@ -607,8 +610,9 @@
       if (!first) {
         builder.Append(", ");
       }
+      builder.Append("\"");
       builder.Append(constraint->GetName());
-      builder.Append(": ");
+      builder.Append("\": ");
       builder.Append(constraint->ToString());
       first = false;
     }
diff --git a/third_party/blink/renderer/modules/mediastream/media_constraints_test.cc b/third_party/blink/renderer/modules/mediastream/media_constraints_test.cc
index e72ee2e..958e360f 100644
--- a/third_party/blink/renderer/modules/mediastream/media_constraints_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_constraints_test.cc
@@ -113,10 +113,11 @@
   MediaTrackConstraintSetPlatform the_set;
   EXPECT_EQ("", the_set.ToString());
   the_set.width.SetMax(240);
-  EXPECT_EQ("width: {max: 240}", the_set.ToString().Utf8());
+  EXPECT_EQ("\"width\": {\"max\": 240}", the_set.ToString().Utf8());
   the_set.echo_cancellation.SetIdealBoolean(true);
-  EXPECT_EQ("width: {max: 240}, echoCancellation: {ideal: true}",
-            the_set.ToString().Utf8());
+  EXPECT_EQ(
+      "\"width\": {\"max\": 240}, \"echoCancellation\": {\"ideal\": true}",
+      the_set.ToString().Utf8());
 }
 
 TEST(MediaTrackConstraintsTest, ConstraintsToString) {
@@ -128,7 +129,8 @@
   advanced[0].echo_cancellation.SetExactBoolean(true);
   the_constraints.Initialize(basic, advanced);
   EXPECT_EQ(
-      "{width: {max: 240}, advanced: [{echoCancellation: {exact: true}}]}",
+      "{\"width\": {\"max\": 240}, \"advanced\": [{\"echoCancellation\": "
+      "{\"exact\": true}}]}",
       the_constraints.ToString().Utf8());
 
   MediaConstraints null_constraints;
@@ -140,7 +142,8 @@
   pan_basic.pan.SetIsPresent(false);
   pan_advanced[0].pan.SetIsPresent(true);
   pan_constraints.Initialize(pan_basic, pan_advanced);
-  EXPECT_EQ("{advanced: [{pan: {}}]}", pan_constraints.ToString().Utf8());
+  EXPECT_EQ("{\"advanced\": [{\"pan\": {}}]}",
+            pan_constraints.ToString().Utf8());
 
   MediaConstraints tilt_constraints;
   MediaTrackConstraintSetPlatform tilt_basic;
@@ -148,7 +151,8 @@
   tilt_basic.tilt.SetIsPresent(false);
   tilt_advanced[0].tilt.SetIsPresent(true);
   tilt_constraints.Initialize(tilt_basic, tilt_advanced);
-  EXPECT_EQ("{advanced: [{tilt: {}}]}", tilt_constraints.ToString().Utf8());
+  EXPECT_EQ("{\"advanced\": [{\"tilt\": {}}]}",
+            tilt_constraints.ToString().Utf8());
 
   MediaConstraints zoom_constraints;
   MediaTrackConstraintSetPlatform zoom_basic;
@@ -156,7 +160,8 @@
   zoom_basic.zoom.SetIsPresent(false);
   zoom_advanced[0].zoom.SetIsPresent(true);
   zoom_constraints.Initialize(zoom_basic, zoom_advanced);
-  EXPECT_EQ("{advanced: [{zoom: {}}]}", zoom_constraints.ToString().Utf8());
+  EXPECT_EQ("{\"advanced\": [{\"zoom\": {}}]}",
+            zoom_constraints.ToString().Utf8());
 
   // TODO(crbug.com/1086338): Test other constraints with IsPresent.
 }
diff --git a/third_party/blink/renderer/modules/peerconnection/BUILD.gn b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
index 1b83ddb..d8ef825 100644
--- a/third_party/blink/renderer/modules/peerconnection/BUILD.gn
+++ b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
@@ -20,8 +20,6 @@
     "adapters/sctp_transport_proxy.h",
     "adapters/web_rtc_cross_thread_copier.cc",
     "adapters/web_rtc_cross_thread_copier.h",
-    "byte_buffer_queue.cc",
-    "byte_buffer_queue.h",
     "dedicated_worker_global_scope_rtc_transform.h",
     "identifiability_metrics.cc",
     "identifiability_metrics.h",
diff --git a/third_party/blink/renderer/modules/peerconnection/byte_buffer_queue.cc b/third_party/blink/renderer/modules/peerconnection/byte_buffer_queue.cc
deleted file mode 100644
index e865711..0000000
--- a/third_party/blink/renderer/modules/peerconnection/byte_buffer_queue.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/modules/peerconnection/byte_buffer_queue.h"
-
-#include "base/compiler_specific.h"
-
-namespace blink {
-
-wtf_size_t ByteBufferQueue::ReadInto(base::span<uint8_t> buffer_out) {
-  wtf_size_t read_amount = 0;
-  while (!buffer_out.empty() && !deque_of_buffers_.empty()) {
-    base::span<const uint8_t> front_buffer =
-        base::span(deque_of_buffers_.front()).subspan(front_buffer_offset_);
-    DCHECK_GT(front_buffer.size(), 0u);
-    const wtf_size_t buffer_read_amount =
-        std::min(static_cast<wtf_size_t>(buffer_out.size()),
-                 static_cast<wtf_size_t>(front_buffer.size()));
-    buffer_out.take_first(buffer_read_amount)
-        .copy_from(front_buffer.first(buffer_read_amount));
-    read_amount += buffer_read_amount;
-    if (buffer_read_amount < front_buffer.size()) {
-      front_buffer_offset_ += buffer_read_amount;
-    } else {
-      deque_of_buffers_.pop_front();
-      front_buffer_offset_ = 0;
-    }
-  }
-  size_ -= read_amount;
-#if DCHECK_IS_ON()
-  CheckInvariants();
-#endif
-  return read_amount;
-}
-
-void ByteBufferQueue::Append(Vector<uint8_t> buffer) {
-  if (buffer.empty()) {
-    return;
-  }
-  size_ += buffer.size();
-  deque_of_buffers_.push_back(std::move(buffer));
-#if DCHECK_IS_ON()
-  CheckInvariants();
-#endif
-}
-
-void ByteBufferQueue::Clear() {
-  deque_of_buffers_.clear();
-  front_buffer_offset_ = 0;
-  size_ = 0;
-#if DCHECK_IS_ON()
-  CheckInvariants();
-#endif
-}
-
-#if DCHECK_IS_ON()
-void ByteBufferQueue::CheckInvariants() const {
-  wtf_size_t buffer_size_sum = 0;
-  for (const auto& buffer : deque_of_buffers_) {
-    DCHECK(!buffer.empty());
-    buffer_size_sum += buffer.size();
-  }
-  DCHECK_EQ(size_, buffer_size_sum - front_buffer_offset_);
-  if (deque_of_buffers_.empty()) {
-    DCHECK_EQ(front_buffer_offset_, 0u);
-  } else {
-    DCHECK_LT(front_buffer_offset_, deque_of_buffers_.front().size());
-  }
-}
-#endif
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/byte_buffer_queue.h b/third_party/blink/renderer/modules/peerconnection/byte_buffer_queue.h
deleted file mode 100644
index 52f99a6..0000000
--- a/third_party/blink/renderer/modules/peerconnection/byte_buffer_queue.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_BYTE_BUFFER_QUEUE_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_BYTE_BUFFER_QUEUE_H_
-
-#include "base/containers/span.h"
-#include "base/dcheck_is_on.h"
-#include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-#include "third_party/blink/renderer/platform/wtf/deque.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
-
-namespace blink {
-
-// A ByteBufferQueue is a byte buffer with O(1) append and O(n) read operations.
-// Clients can append entire byte buffers then copy data out across buffer
-// boundaries using |ReadInto|.
-class MODULES_EXPORT ByteBufferQueue final {
-  DISALLOW_NEW();
-
- public:
-  // Number of bytes that can be read.
-  wtf_size_t size() const { return size_; }
-
-  // True if size() == 0.
-  bool empty() const { return size_ == 0; }
-
-  // Copies data into the given byte span. This will cause bytes to be consumed
-  // so that the next call to ReadInto will return different bytes.
-  // Returns the number of bytes written to |buffer_out|.
-  wtf_size_t ReadInto(base::span<uint8_t> buffer_out);
-
-  // Appends the contents of a byte buffer. This takes ownership of the buffer.
-  void Append(Vector<uint8_t> buffer);
-
-  // Clear stored buffers.
-  void Clear();
-
- private:
-#if DCHECK_IS_ON()
-  void CheckInvariants() const;
-#endif
-
-  // Number of bytes that can be read.
-  // |Append()| adds to this number.
-  // |ReadInto()| subtracts from this number.
-  // Invariant: |size_| = sum of |deque_of_buffers_| element sizes -
-  //     |front_buffer_offset_|.
-  wtf_size_t size_ = 0;
-
-  // Double-ended queue of byte buffers.
-  // |Append()| pushes to the right.
-  // |ReadInto()| pops from the left (if an entire buffer has been read).
-  // Invariant: No element in |deque_of_buffers_| is empty.
-  Deque<Vector<uint8_t>> deque_of_buffers_;
-
-  // The offset from which to start reading the buffer at the front of
-  // |deque_of_buffers_|.
-  // Invariants:
-  // - If |deque_of_buffers_| is empty, |front_buffer_offset_| = 0.
-  // - Otherwise, |front_buffer_offset_| < |deque_of_buffers_|.front().size().
-  wtf_size_t front_buffer_offset_ = 0;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_BYTE_BUFFER_QUEUE_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/byte_buffer_queue_test.cc b/third_party/blink/renderer/modules/peerconnection/byte_buffer_queue_test.cc
deleted file mode 100644
index 5cc47e0..0000000
--- a/third_party/blink/renderer/modules/peerconnection/byte_buffer_queue_test.cc
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/modules/peerconnection/byte_buffer_queue.h"
-
-#include "testing/gmock/include/gmock/gmock.h"
-#include "third_party/blink/renderer/platform/testing/task_environment.h"
-
-namespace blink {
-
-using testing::ElementsAre;
-
-TEST(ByteBufferQueueTest, DefaultConstructor) {
-  test::TaskEnvironment task_environment;
-  ByteBufferQueue buffer_queue;
-  EXPECT_EQ(0u, buffer_queue.size());
-  EXPECT_TRUE(buffer_queue.empty());
-}
-
-TEST(ByteBufferQueueTest, AppendEmpty) {
-  test::TaskEnvironment task_environment;
-  ByteBufferQueue buffer_queue;
-  buffer_queue.Append({});
-  EXPECT_TRUE(buffer_queue.empty());
-}
-
-TEST(ByteBufferQueueTest, AppendOneSegment) {
-  test::TaskEnvironment task_environment;
-  ByteBufferQueue buffer_queue;
-  buffer_queue.Append({1, 2, 3});
-  EXPECT_EQ(3u, buffer_queue.size());
-}
-
-TEST(ByteBufferQueueTest, AppendTwoSegments) {
-  test::TaskEnvironment task_environment;
-  ByteBufferQueue buffer_queue;
-  buffer_queue.Append({1, 2, 3});
-  buffer_queue.Append({4, 5});
-  EXPECT_EQ(5u, buffer_queue.size());
-}
-
-TEST(ByteBufferQueueTest, ReadIntoEmpty) {
-  test::TaskEnvironment task_environment;
-  ByteBufferQueue buffer_queue;
-  Vector<uint8_t> data(100);
-  EXPECT_EQ(0u, buffer_queue.ReadInto(base::span(data)));
-}
-
-TEST(ByteBufferQueueTest, ReadIntoLessThanOneSegment) {
-  test::TaskEnvironment task_environment;
-  ByteBufferQueue buffer_queue;
-  buffer_queue.Append({1, 2, 3});
-  Vector<uint8_t> data(2);
-  EXPECT_EQ(2u, buffer_queue.ReadInto(base::span(data)));
-  EXPECT_EQ(1u, buffer_queue.size());
-  EXPECT_THAT(data, ElementsAre(1, 2));
-}
-
-TEST(ByteBufferQueueTest, ReadIntoExactOneSegmentSize) {
-  test::TaskEnvironment task_environment;
-  ByteBufferQueue buffer_queue;
-  buffer_queue.Append({1, 2, 3});
-  Vector<uint8_t> data(3);
-  EXPECT_EQ(3u, buffer_queue.ReadInto(base::span(data)));
-  EXPECT_EQ(0u, buffer_queue.size());
-  EXPECT_THAT(data, ElementsAre(1, 2, 3));
-}
-
-TEST(ByteBufferQueueTest, ReadIntoOverOneSegmentSize) {
-  test::TaskEnvironment task_environment;
-  ByteBufferQueue buffer_queue;
-  buffer_queue.Append({1, 2, 3});
-  Vector<uint8_t> data(5);
-  EXPECT_EQ(3u, buffer_queue.ReadInto(base::span(data)));
-  EXPECT_EQ(0u, buffer_queue.size());
-  EXPECT_THAT(data, ElementsAre(1, 2, 3, 0, 0));
-}
-
-TEST(ByteBufferQueueTest, ReadIntoEmptyData) {
-  test::TaskEnvironment task_environment;
-  ByteBufferQueue buffer_queue;
-  buffer_queue.Append({1, 2, 3});
-  Vector<uint8_t> data;
-  EXPECT_EQ(0u, buffer_queue.ReadInto(base::span(data)));
-  EXPECT_EQ(3u, buffer_queue.size());
-}
-
-TEST(ByteBufferQueueTest, ReadIntoExactlyTwoSegments) {
-  test::TaskEnvironment task_environment;
-  ByteBufferQueue buffer_queue;
-  buffer_queue.Append({1, 2, 3});
-  buffer_queue.Append({4, 5});
-  Vector<uint8_t> data(5);
-  EXPECT_EQ(5u, buffer_queue.ReadInto(base::span(data)));
-  EXPECT_EQ(0u, buffer_queue.size());
-  EXPECT_THAT(data, ElementsAre(1, 2, 3, 4, 5));
-}
-
-TEST(ByteBufferQueueTest, ReadIntoAcrossTwoSegmentsMisaligned) {
-  test::TaskEnvironment task_environment;
-  ByteBufferQueue buffer_queue;
-  buffer_queue.Append({1, 2, 3});
-  buffer_queue.Append({4, 5});
-
-  Vector<uint8_t> data(2);
-  EXPECT_EQ(2u, buffer_queue.ReadInto(base::span(data)));
-  EXPECT_THAT(data, ElementsAre(1, 2));
-
-  EXPECT_EQ(2u, buffer_queue.ReadInto(base::span(data)));
-  EXPECT_THAT(data, ElementsAre(3, 4));
-
-  EXPECT_EQ(1u, buffer_queue.ReadInto(base::span(data)));
-  EXPECT_THAT(data, ElementsAre(5, 4));
-}
-
-TEST(ByteBufferQueueTest, ClearEmptyBuffer) {
-  test::TaskEnvironment task_environment;
-  ByteBufferQueue buffer_queue;
-  buffer_queue.Clear();
-  EXPECT_EQ(0u, buffer_queue.size());
-  EXPECT_TRUE(buffer_queue.empty());
-}
-
-TEST(ByteBufferQueueTest, ReadIntoAfterClearThenAppend) {
-  test::TaskEnvironment task_environment;
-  ByteBufferQueue buffer_queue;
-
-  buffer_queue.Append({1, 2, 3});
-  Vector<uint8_t> data(2);
-  buffer_queue.ReadInto(base::span(data));
-
-  buffer_queue.Clear();
-  EXPECT_EQ(0u, buffer_queue.size());
-  EXPECT_EQ(0u, buffer_queue.ReadInto(base::span(data)));
-
-  buffer_queue.Append({4, 5});
-  EXPECT_EQ(2u, buffer_queue.ReadInto(base::span(data)));
-  EXPECT_THAT(data, ElementsAre(4, 5));
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.cc b/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.cc
index 9cc78d69..37ed944 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.cc
+++ b/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.cc
@@ -11,7 +11,6 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_buffer_binding_type.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_buffer_map_state.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_compare_function.h"
-#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_component_swizzle.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_cull_mode.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_error_filter.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_feature_name.h"
@@ -989,22 +988,23 @@
   NOTREACHED();
 }
 
-wgpu::ComponentSwizzle AsDawnEnum(const V8GPUComponentSwizzle& webgpu_enum) {
-  switch (webgpu_enum.AsEnum()) {
-    case V8GPUComponentSwizzle::Enum::kZero:
-      return wgpu::ComponentSwizzle::Zero;
-    case V8GPUComponentSwizzle::Enum::kOne:
-      return wgpu::ComponentSwizzle::One;
-    case V8GPUComponentSwizzle::Enum::kR:
+wgpu::ComponentSwizzle AsDawnEnum(const UChar c) {
+  switch (c) {
+    case 'r':
       return wgpu::ComponentSwizzle::R;
-    case V8GPUComponentSwizzle::Enum::kG:
+    case 'g':
       return wgpu::ComponentSwizzle::G;
-    case V8GPUComponentSwizzle::Enum::kB:
+    case 'b':
       return wgpu::ComponentSwizzle::B;
-    case V8GPUComponentSwizzle::Enum::kA:
+    case 'a':
       return wgpu::ComponentSwizzle::A;
+    case '0':
+      return wgpu::ComponentSwizzle::Zero;
+    case '1':
+      return wgpu::ComponentSwizzle::One;
+    default:
+      return wgpu::ComponentSwizzle::Undefined;
   }
-  NOTREACHED();
 }
 
 V8GPUBufferMapState FromDawnEnum(wgpu::BufferMapState dawn_enum) {
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.h b/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.h
index d0b35c0..53ffe3f 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.h
+++ b/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.h
@@ -2,6 +2,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_DAWN_ENUM_CONVERSIONS_H_
 
 #include "third_party/blink/renderer/platform/graphics/gpu/webgpu_cpp.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_uchar.h"
 
 namespace blink {
 
@@ -32,7 +33,6 @@
 class V8GPUFrontFace;
 class V8GPUTextureAspect;
 class V8GPUErrorFilter;
-class V8GPUComponentSwizzle;
 enum class PredefinedColorSpace;
 
 // Convert WebGPU bitfield values to Dawn enums. These have the same value.
@@ -70,7 +70,7 @@
 wgpu::FrontFace AsDawnEnum(const V8GPUFrontFace& webgpu_enum);
 wgpu::TextureAspect AsDawnEnum(const V8GPUTextureAspect& webgpu_enum);
 wgpu::ErrorFilter AsDawnEnum(const V8GPUErrorFilter& webgpu_enum);
-wgpu::ComponentSwizzle AsDawnEnum(const V8GPUComponentSwizzle& webgpu_enum);
+wgpu::ComponentSwizzle AsDawnEnum(const UChar c);
 
 // Convert Dawn enums to WebGPU IDL enums.
 V8GPUQueryType FromDawnEnum(wgpu::QueryType dawn_enum);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_texture.cc b/third_party/blink/renderer/modules/webgpu/gpu_texture.cc
index dc86440..b350e038 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_texture.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_texture.cc
@@ -6,7 +6,6 @@
 
 #include "base/containers/heap_array.h"
 #include "gpu/command_buffer/client/webgpu_interface.h"
-#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_component_swizzle.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_descriptor.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_view_descriptor.h"
 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h"
@@ -99,24 +98,49 @@
     dawn_desc_info->dawn_desc.usage =
         static_cast<wgpu::TextureUsage>(webgpu_desc->usage());
   }
-  auto* swizzle = webgpu_desc->getSwizzleOr(nullptr);
+  const auto& swizzle = webgpu_desc->swizzle();
   // Only pass the swizzle descriptor to Dawn if swizzle is non-default because
   // the C API will produce validation errors if a chained struct is passed
   // without its feature being enabled.
-  if (swizzle && (swizzle->r() != V8GPUComponentSwizzle::Enum::kR ||
-                  swizzle->g() != V8GPUComponentSwizzle::Enum::kG ||
-                  swizzle->b() != V8GPUComponentSwizzle::Enum::kB ||
-                  swizzle->a() != V8GPUComponentSwizzle::Enum::kA)) {
+  if (swizzle != "rgba") {
     dawn_desc_info->swizzle_desc =
         std::make_unique<wgpu::TextureComponentSwizzleDescriptor>();
-    dawn_desc_info->swizzle_desc->swizzle.r = AsDawnEnum(swizzle->r());
-    dawn_desc_info->swizzle_desc->swizzle.g = AsDawnEnum(swizzle->g());
-    dawn_desc_info->swizzle_desc->swizzle.b = AsDawnEnum(swizzle->b());
-    dawn_desc_info->swizzle_desc->swizzle.a = AsDawnEnum(swizzle->a());
+    dawn_desc_info->swizzle_desc->swizzle.r = AsDawnEnum(swizzle[0]);
+    dawn_desc_info->swizzle_desc->swizzle.g = AsDawnEnum(swizzle[1]);
+    dawn_desc_info->swizzle_desc->swizzle.b = AsDawnEnum(swizzle[2]);
+    dawn_desc_info->swizzle_desc->swizzle.a = AsDawnEnum(swizzle[3]);
     dawn_desc_info->dawn_desc.nextInChain = dawn_desc_info->swizzle_desc.get();
   }
 }
 
+// Validate swizzle must be a four-character string that only includes "r", "g",
+// "b", "a", "0", or "1".
+bool ValidateSwizzle(const String& swizzle, ExceptionState& exception_state) {
+  if (!RuntimeEnabledFeatures::WebGPUExperimentalFeaturesEnabled()) {
+    return true;
+  }
+
+  if (swizzle.length() != 4) {
+    exception_state.ThrowTypeError(String::Format(
+        "Swizzle ('%s') must be exactly a four-character string.",
+        swizzle.Utf8().c_str()));
+    return false;
+  }
+
+  if (AsDawnEnum(swizzle[0]) == wgpu::ComponentSwizzle::Undefined ||
+      AsDawnEnum(swizzle[1]) == wgpu::ComponentSwizzle::Undefined ||
+      AsDawnEnum(swizzle[2]) == wgpu::ComponentSwizzle::Undefined ||
+      AsDawnEnum(swizzle[3]) == wgpu::ComponentSwizzle::Undefined) {
+    exception_state.ThrowTypeError(String::Format(
+        "Swizzle ('%s') must contain only 'r', 'g', 'b', 'a', '0', "
+        "or '1' characters.",
+        swizzle.Utf8().c_str()));
+    return false;
+  }
+
+  return true;
+}
+
 // Dawn represents `undefined` as the special uint32_t value (0xFFFF'FFFF).
 // Blink must make sure that an actual value of 0xFFFF'FFFF coming in from JS
 // is not treated as the special `undefined` value, so it injects an error in
@@ -245,6 +269,10 @@
     return nullptr;
   }
 
+  if (!ValidateSwizzle(webgpu_desc->swizzle(), exception_state)) {
+    return nullptr;
+  }
+
   std::string error = ValidateTextureMipLevelAndArrayLayerCounts(webgpu_desc);
   if (!error.empty()) {
     device()->InjectError(wgpu::ErrorType::Validation, error.c_str());
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_texture_view_descriptor.idl b/third_party/blink/renderer/modules/webgpu/gpu_texture_view_descriptor.idl
index e48686a..cad0430e 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_texture_view_descriptor.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_texture_view_descriptor.idl
@@ -13,14 +13,7 @@
     GPUIntegerCoordinate baseArrayLayer = 0;
     GPUIntegerCoordinate arrayLayerCount;
     GPUTextureUsageFlags usage = 0;
-    [RuntimeEnabled=WebGPUExperimentalFeatures] GPUTextureComponentSwizzle swizzle;
-};
-
-dictionary GPUTextureComponentSwizzle {
-    GPUComponentSwizzle r = "r";
-    GPUComponentSwizzle g = "g";
-    GPUComponentSwizzle b = "b";
-    GPUComponentSwizzle a = "a";
+    [RuntimeEnabled=WebGPUExperimentalFeatures] DOMString swizzle = "rgba";
 };
 
 enum GPUTextureViewDimension {
@@ -37,12 +30,3 @@
     "stencil-only",
     "depth-only"
 };
-
-enum GPUComponentSwizzle {
-    "zero",
-    "one",
-    "r",
-    "g",
-    "b",
-    "a",
-};
diff --git a/third_party/blink/renderer/modules/xr/BUILD.gn b/third_party/blink/renderer/modules/xr/BUILD.gn
index df9844a..2b3c7c1 100644
--- a/third_party/blink/renderer/modules/xr/BUILD.gn
+++ b/third_party/blink/renderer/modules/xr/BUILD.gn
@@ -49,8 +49,8 @@
     "xr_frame_request_callback_collection.h",
     "xr_gpu_binding.cc",
     "xr_gpu_binding.h",
-    "xr_gpu_projection_layer.cc",
-    "xr_gpu_projection_layer.h",
+    "xr_gpu_drawing_context.cc",
+    "xr_gpu_drawing_context.h",
     "xr_gpu_sub_image.h",
     "xr_gpu_swap_chain.cc",
     "xr_gpu_swap_chain.h",
@@ -83,6 +83,7 @@
     "xr_joint_space.h",
     "xr_layer.cc",
     "xr_layer.h",
+    "xr_layer_drawing_context.h",
     "xr_layer_event.cc",
     "xr_layer_event.h",
     "xr_layer_shared_image_manager.cc",
@@ -150,11 +151,11 @@
     "xr_webgl_depth_information.h",
     "xr_webgl_drawing_buffer_swap_chain.cc",
     "xr_webgl_drawing_buffer_swap_chain.h",
+    "xr_webgl_drawing_context.cc",
+    "xr_webgl_drawing_context.h",
     "xr_webgl_layer.cc",
     "xr_webgl_layer.h",
     "xr_webgl_layer_client.h",
-    "xr_webgl_projection_layer.cc",
-    "xr_webgl_projection_layer.h",
     "xr_webgl_sub_image.cc",
     "xr_webgl_sub_image.h",
     "xr_webgl_swap_chain.cc",
diff --git a/third_party/blink/renderer/modules/xr/xr_composition_layer.cc b/third_party/blink/renderer/modules/xr/xr_composition_layer.cc
index 6238479a..8ad00569 100644
--- a/third_party/blink/renderer/modules/xr/xr_composition_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_composition_layer.cc
@@ -6,13 +6,24 @@
 
 #include "base/notimplemented.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_xr_layer_layout.h"
+#include "third_party/blink/renderer/modules/xr/xr_frame_provider.h"
 #include "third_party/blink/renderer/modules/xr/xr_graphics_binding.h"
+#include "third_party/blink/renderer/modules/xr/xr_layer_drawing_context.h"
+#include "third_party/blink/renderer/modules/xr/xr_projection_layer.h"
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
+#include "third_party/blink/renderer/modules/xr/xr_swap_chain.h"
+#include "third_party/blink/renderer/modules/xr/xr_system.h"
 
 namespace blink {
 
-XRCompositionLayer::XRCompositionLayer(XRGraphicsBinding* binding)
-    : XRLayer(binding->session()), binding_(binding) {}
+XRCompositionLayer::XRCompositionLayer(XRGraphicsBinding* binding,
+                                       XRLayerDrawingContext* drawing_context)
+    : XRLayer(binding->session()),
+      binding_(binding),
+      drawing_context_(drawing_context) {
+  CHECK(drawing_context_);
+  drawing_context_->SetCompositionLayer(this);
+}
 
 V8XRLayerLayout XRCompositionLayer::layout() const {
   return V8XRLayerLayout(V8XRLayerLayout::Enum::kDefault);
@@ -75,8 +86,40 @@
   mip_levels_ = mipLevels;
 }
 
+uint16_t XRCompositionLayer::textureWidth() const {
+  return drawing_context_->TextureWidth();
+}
+
+uint16_t XRCompositionLayer::textureHeight() const {
+  return drawing_context_->TextureHeight();
+}
+
+uint16_t XRCompositionLayer::textureArrayLength() const {
+  return drawing_context_->TextureWidth();
+}
+
+void XRCompositionLayer::OnFrameStart() {
+  drawing_context_->OnFrameStart();
+}
+
+void XRCompositionLayer::OnFrameEnd() {
+  drawing_context_->OnFrameEnd();
+
+  XRFrameProvider* frame_provider = session()->xr()->frameProvider();
+
+  if (IsModified()) {
+    if (XRProjectionLayer* layer = DynamicTo<XRProjectionLayer>(this); layer) {
+      frame_provider->UpdateLayerViewports(layer);
+      SetModified(false);
+    }
+  }
+
+  frame_provider->SubmitCompositionLayer(this);
+}
+
 void XRCompositionLayer::Trace(Visitor* visitor) const {
   visitor->Trace(binding_);
+  visitor->Trace(drawing_context_);
   XRLayer::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_composition_layer.h b/third_party/blink/renderer/modules/xr/xr_composition_layer.h
index 2186eaf..43a637db 100644
--- a/third_party/blink/renderer/modules/xr/xr_composition_layer.h
+++ b/third_party/blink/renderer/modules/xr/xr_composition_layer.h
@@ -16,12 +16,14 @@
 
 class V8XRLayerLayout;
 class XRGraphicsBinding;
+class XRLayerDrawingContext;
 
 class XRCompositionLayer : public XRLayer {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  explicit XRCompositionLayer(XRGraphicsBinding* binding);
+  XRCompositionLayer(XRGraphicsBinding* binding,
+                     XRLayerDrawingContext* drawing_context);
   ~XRCompositionLayer() override = default;
 
   XRGraphicsBinding* binding() const { return binding_.Get(); }
@@ -38,11 +40,17 @@
   bool needsRedraw() const;
   void destroy() const;
 
-  void Trace(Visitor*) const override;
+  uint16_t textureWidth() const;
+  uint16_t textureHeight() const;
+  uint16_t textureArrayLength() const;
 
-  virtual uint16_t textureWidth() const = 0;
-  virtual uint16_t textureHeight() const = 0;
-  virtual uint16_t textureArrayLength() const = 0;
+  void OnFrameStart() override;
+  void OnFrameEnd() override;
+  void OnResize() override {}
+
+  XRLayerDrawingContext* drawing_context() { return drawing_context_; }
+
+  void Trace(Visitor*) const override;
 
  protected:
   void SetNeedsRedraw(bool needsRedraw);
@@ -59,6 +67,8 @@
   float opacity_{1.0};
   uint16_t mip_levels_{1};
   bool needs_redraw_{false};
+
+  Member<XRLayerDrawingContext> drawing_context_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_cylinder_layer.cc b/third_party/blink/renderer/modules/xr/xr_cylinder_layer.cc
index 604d4c4e..0af70f7 100644
--- a/third_party/blink/renderer/modules/xr/xr_cylinder_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_cylinder_layer.cc
@@ -10,8 +10,9 @@
 
 namespace blink {
 XRCylinderLayer::XRCylinderLayer(const XRCylinderLayerInit* init,
-                                 XRGraphicsBinding* binding)
-    : XRShapedLayer(init, binding),
+                                 XRGraphicsBinding* binding,
+                                 XRLayerDrawingContext* drawing_context)
+    : XRShapedLayer(init, binding, drawing_context),
       radius_(init->radius()),
       central_angle_(init->centralAngle()),
       aspect_ratio_(init->aspectRatio()) {
@@ -23,25 +24,29 @@
   }
 }
 
+XRLayerType XRCylinderLayer::LayerType() const {
+  return XRLayerType::kCylinderLayer;
+}
+
 void XRCylinderLayer::setRadius(float radius) {
   radius_ = radius;
-  OnUpdateLayerData();
+  SetModified(true);
 }
 
 void XRCylinderLayer::setCentralAngle(float central_angle) {
   central_angle_ = central_angle;
-  OnUpdateLayerData();
+  SetModified(true);
 }
 
 void XRCylinderLayer::setAspectRatio(float aspect_ratio) {
   aspect_ratio_ = aspect_ratio;
-  OnUpdateLayerData();
+  SetModified(true);
 }
 
 void XRCylinderLayer::setTransform(XRRigidTransform* value) {
   if (transform_ != value) {
     transform_ = value;
-    OnUpdateLayerData();
+    SetModified(true);
   }
 }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_cylinder_layer.h b/third_party/blink/renderer/modules/xr/xr_cylinder_layer.h
index b0fbafd..ca10eff 100644
--- a/third_party/blink/renderer/modules/xr/xr_cylinder_layer.h
+++ b/third_party/blink/renderer/modules/xr/xr_cylinder_layer.h
@@ -19,10 +19,13 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  explicit XRCylinderLayer(const XRCylinderLayerInit* init,
-                           XRGraphicsBinding* binding);
+  XRCylinderLayer(const XRCylinderLayerInit* init,
+                  XRGraphicsBinding* binding,
+                  XRLayerDrawingContext* drawing_context);
   ~XRCylinderLayer() override = default;
 
+  XRLayerType LayerType() const override;
+
   XRRigidTransform* transform() const { return transform_.Get(); }
   void setTransform(XRRigidTransform* value);
   float radius() const { return radius_; }
diff --git a/third_party/blink/renderer/modules/xr/xr_equirect_layer.cc b/third_party/blink/renderer/modules/xr/xr_equirect_layer.cc
index c97519c..51fd109c 100644
--- a/third_party/blink/renderer/modules/xr/xr_equirect_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_equirect_layer.cc
@@ -10,8 +10,9 @@
 
 namespace blink {
 XREquirectLayer::XREquirectLayer(const XREquirectLayerInit* init,
-                                 XRGraphicsBinding* binding)
-    : XRShapedLayer(init, binding),
+                                 XRGraphicsBinding* binding,
+                                 XRLayerDrawingContext* drawing_context)
+    : XRShapedLayer(init, binding, drawing_context),
       radius_(init->radius()),
       central_horizontal_angle_(init->centralHorizontalAngle()),
       upper_vertical_angle_(init->upperVerticalAngle()),
@@ -24,30 +25,34 @@
   }
 }
 
+XRLayerType XREquirectLayer::LayerType() const {
+  return XRLayerType::kEquirectLayer;
+}
+
 void XREquirectLayer::setRadius(float radius) {
   radius_ = radius;
-  OnUpdateLayerData();
+  SetModified(true);
 }
 
 void XREquirectLayer::setCentralHorizontalAngle(float angle) {
   central_horizontal_angle_ = angle;
-  OnUpdateLayerData();
+  SetModified(true);
 }
 
 void XREquirectLayer::setUpperVerticalAngle(float angle) {
   upper_vertical_angle_ = angle;
-  OnUpdateLayerData();
+  SetModified(true);
 }
 
 void XREquirectLayer::setLowerVerticalAngle(float angle) {
   lower_vertical_angle_ = angle;
-  OnUpdateLayerData();
+  SetModified(true);
 }
 
 void XREquirectLayer::setTransform(XRRigidTransform* value) {
   if (transform_ != value) {
     transform_ = value;
-    OnUpdateLayerData();
+    SetModified(true);
   }
 }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_equirect_layer.h b/third_party/blink/renderer/modules/xr/xr_equirect_layer.h
index a9700d9d..3d8ac7e0 100644
--- a/third_party/blink/renderer/modules/xr/xr_equirect_layer.h
+++ b/third_party/blink/renderer/modules/xr/xr_equirect_layer.h
@@ -21,10 +21,13 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  explicit XREquirectLayer(const XREquirectLayerInit* init,
-                           XRGraphicsBinding* binding);
+  XREquirectLayer(const XREquirectLayerInit* init,
+                  XRGraphicsBinding* binding,
+                  XRLayerDrawingContext* drawing_context);
   ~XREquirectLayer() override = default;
 
+  XRLayerType LayerType() const override;
+
   XRRigidTransform* transform() const { return transform_.Get(); }
   void setTransform(XRRigidTransform* value);
   float radius() const { return radius_; }
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
index 00391ae3..bd043b8 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
@@ -20,16 +20,16 @@
 #include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_texture.h"
 #include "third_party/blink/renderer/modules/xr/xr_gpu_binding.h"
-#include "third_party/blink/renderer/modules/xr/xr_gpu_projection_layer.h"
+#include "third_party/blink/renderer/modules/xr/xr_gpu_drawing_context.h"
 #include "third_party/blink/renderer/modules/xr/xr_gpu_swap_chain.h"
 #include "third_party/blink/renderer/modules/xr/xr_graphics_binding.h"
 #include "third_party/blink/renderer/modules/xr/xr_projection_layer.h"
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
 #include "third_party/blink/renderer/modules/xr/xr_system.h"
 #include "third_party/blink/renderer/modules/xr/xr_viewport.h"
+#include "third_party/blink/renderer/modules/xr/xr_webgl_drawing_context.h"
 #include "third_party/blink/renderer/modules/xr/xr_webgl_layer.h"
 #include "third_party/blink/renderer/modules/xr/xr_webgl_layer_client.h"
-#include "third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "ui/display/display.h"
@@ -640,11 +640,24 @@
   return high_res_now_ms;
 }
 
+void XRFrameProvider::SubmitCompositionLayer(XRCompositionLayer* layer) {
+  CHECK(layer);
+  CHECK(layer->drawing_context());
+
+  if (layer->drawing_context()->GraphicsApi() ==
+      XRGraphicsBinding::Api::kWebGL) {
+    auto* drawing_context =
+        static_cast<XRWebGLDrawingContext*>(layer->drawing_context());
+    SubmitWebGLLayer(drawing_context, drawing_context->TextureWasQueried());
+  } else {
+    SubmitWebGPULayer(layer);
+  }
+}
+
 void XRFrameProvider::SubmitWebGLLayer(XRWebGLLayerClient* layer_client,
                                        bool was_changed) {
   CHECK(layer_client);
   CHECK(immersive_session_);
-
   const XRLayer* layer = layer_client->layer();
   CHECK(layer);
 
@@ -759,8 +772,7 @@
       frame_id_, left_coords, right_coords, gfx::Size(width, height));
 }
 
-void XRFrameProvider::SubmitWebGPULayer(XRGPUProjectionLayer* layer,
-                                        bool was_queried) {
+void XRFrameProvider::SubmitWebGPULayer(XRCompositionLayer* layer) {
   CHECK(layer);
   CHECK(immersive_session_);
   CHECK_EQ(layer->session(), immersive_session_);
@@ -769,11 +781,18 @@
     return;
   }
 
+  // A static_cast is safe here because the drawing context type was already
+  // checked by SubmitCompositionLayer.
+  auto* drawing_context =
+      static_cast<XRGPUDrawingContext*>(layer->drawing_context());
+  CHECK(drawing_context);
+
+  bool was_queried = drawing_context->TextureWasQueried();
+
   TRACE_EVENT1("gpu", "XRFrameProvider::SubmitWebGPULayer", "frame", frame_id_);
   DVLOG(3) << __func__ << ": frame=" << frame_id_;
 
-  XRGPUBinding* webgpu_binding = static_cast<XRGPUBinding*>(layer->binding());
-  GPUDevice* device = webgpu_binding->device();
+  GPUDevice* device = drawing_context->device();
 
   if (frame_id_ < 0) {
     // There is no valid frame_id_, and the browser side is not currently
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.h b/third_party/blink/renderer/modules/xr/xr_frame_provider.h
index 750296d..ea437b4 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.h
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.h
@@ -25,12 +25,12 @@
 
 class LocalDOMWindow;
 class XRFrameTransport;
-class XRGPUProjectionLayer;
 class XRProjectionLayer;
 class XRSession;
 class XRSystem;
 class XRWebGLLayer;
 class XRWebGLLayerClient;
+class XRCompositionLayer;
 
 // This class manages requesting and dispatching frame updates, which includes
 // pose information for a given XRDevice.
@@ -62,11 +62,11 @@
 
   void OnNonImmersiveVSync(double high_res_now_ms);
 
+  void SubmitCompositionLayer(XRCompositionLayer*);
+
   void SubmitWebGLLayer(XRWebGLLayerClient*, bool was_changed);
   void UpdateWebGLLayerViewports(XRWebGLLayer*);
 
-  void SubmitWebGPULayer(XRGPUProjectionLayer*, bool was_queried);
-
   // Used for both WebGPU and WebGL layers.
   void UpdateLayerViewports(XRProjectionLayer*);
 
@@ -91,6 +91,8 @@
     kImmersive = 1,
   };
 
+  void SubmitWebGPULayer(XRCompositionLayer*);
+
   void OnImmersiveFrameData(device::mojom::blink::XRFrameDataPtr data);
   void OnNonImmersiveFrameData(XRSession* session,
                                device::mojom::blink::XRFrameDataPtr data);
diff --git a/third_party/blink/renderer/modules/xr/xr_gpu_binding.cc b/third_party/blink/renderer/modules/xr/xr_gpu_binding.cc
index 0e5be45..ef1e615 100644
--- a/third_party/blink/renderer/modules/xr/xr_gpu_binding.cc
+++ b/third_party/blink/renderer/modules/xr/xr_gpu_binding.cc
@@ -14,10 +14,11 @@
 #include "third_party/blink/renderer/modules/webgpu/gpu_supported_limits.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_texture.h"
 #include "third_party/blink/renderer/modules/xr/xr_frame_provider.h"
-#include "third_party/blink/renderer/modules/xr/xr_gpu_projection_layer.h"
+#include "third_party/blink/renderer/modules/xr/xr_gpu_drawing_context.h"
 #include "third_party/blink/renderer/modules/xr/xr_gpu_sub_image.h"
 #include "third_party/blink/renderer/modules/xr/xr_gpu_swap_chain.h"
 #include "third_party/blink/renderer/modules/xr/xr_gpu_texture_array_swap_chain.h"
+#include "third_party/blink/renderer/modules/xr/xr_projection_layer.h"
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
 #include "third_party/blink/renderer/modules/xr/xr_system.h"
 #include "third_party/blink/renderer/modules/xr/xr_view.h"
@@ -160,8 +161,10 @@
         MakeGarbageCollected<XRGPUStaticSwapChain>(device_, depth_stencil_desc);
   }
 
-  return MakeGarbageCollected<XRGPUProjectionLayer>(this, wrapped_swap_chain,
-                                                    depth_stencil_swap_chain);
+  auto* drawing_context = MakeGarbageCollected<XRGPUDrawingContext>(
+      this, wrapped_swap_chain, depth_stencil_swap_chain);
+
+  return MakeGarbageCollected<XRProjectionLayer>(this, drawing_context);
 }
 
 XRGPUSubImage* XRGPUBinding::getViewSubImage(XRProjectionLayer* layer,
@@ -181,21 +184,24 @@
     return nullptr;
   }
 
-  XRGPUProjectionLayer* gpu_layer = static_cast<XRGPUProjectionLayer*>(layer);
+  // The layer passed the OwnsLayer check, confirming it can only contain
+  // a GPU drawing context. This makes the static_cast safe.
+  XRGPUDrawingContext* drawing_context =
+      static_cast<XRGPUDrawingContext*>(layer->drawing_context());
 
-  GPUTexture* color_texture =
-      gpu_layer->color_swap_chain()->GetCurrentTexture();
+  XRGPUSwapChain* color_swap_chain = drawing_context->color_swap_chain();
+  GPUTexture* color_texture = color_swap_chain->GetCurrentTexture();
 
   GPUTexture* depth_stencil_texture = nullptr;
   XRGPUSwapChain* depth_stencil_swap_chain =
-      gpu_layer->depth_stencil_swap_chain();
+      drawing_context->depth_stencil_swap_chain();
   if (depth_stencil_swap_chain) {
     depth_stencil_texture = depth_stencil_swap_chain->GetCurrentTexture();
   }
 
   XRViewData* viewData = view->ViewData();
   if (viewData->ApplyViewportScaleForFrame()) {
-    gpu_layer->MarkViewportUpdated();
+    layer->SetModified(true);
   }
 
   gfx::Rect viewport = GetViewportForView(layer, viewData);
diff --git a/third_party/blink/renderer/modules/xr/xr_gpu_projection_layer.cc b/third_party/blink/renderer/modules/xr/xr_gpu_drawing_context.cc
similarity index 63%
rename from third_party/blink/renderer/modules/xr/xr_gpu_projection_layer.cc
rename to third_party/blink/renderer/modules/xr/xr_gpu_drawing_context.cc
index aeb2b2e..7d63e4c2 100644
--- a/third_party/blink/renderer/modules/xr/xr_gpu_projection_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_gpu_drawing_context.cc
@@ -1,8 +1,8 @@
-// Copyright 2024 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.
 
-#include "third_party/blink/renderer/modules/xr/xr_gpu_projection_layer.h"
+#include "third_party/blink/renderer/modules/xr/xr_gpu_drawing_context.h"
 
 #include "third_party/blink/renderer/bindings/modules/v8/v8_xr_gpu_projection_layer_init.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
@@ -18,64 +18,62 @@
 
 namespace blink {
 
-XRGPUProjectionLayer::XRGPUProjectionLayer(
+XRGPUDrawingContext::XRGPUDrawingContext(
     XRGPUBinding* binding,
     XRGPUSwapChain* color_swap_chain,
     XRGPUSwapChain* depth_stencil_swap_chain)
-    : XRProjectionLayer(binding),
-      device_(binding->device()),
+    : device_(binding->device()),
       color_swap_chain_(color_swap_chain),
       depth_stencil_swap_chain_(depth_stencil_swap_chain) {
   CHECK(color_swap_chain_);
-  color_swap_chain_->SetLayer(this);
-  if (depth_stencil_swap_chain_) {
-    depth_stencil_swap_chain_->SetLayer(this);
-  }
 }
 
-uint16_t XRGPUProjectionLayer::textureWidth() const {
+enum XRGraphicsBinding::Api XRGPUDrawingContext::GraphicsApi() const {
+  return XRGraphicsBinding::Api::kWebGPU;
+}
+
+uint16_t XRGPUDrawingContext::TextureWidth() const {
   return color_swap_chain_->descriptor().size.width;
 }
 
-uint16_t XRGPUProjectionLayer::textureHeight() const {
+uint16_t XRGPUDrawingContext::TextureHeight() const {
   return color_swap_chain_->descriptor().size.height;
 }
 
-uint16_t XRGPUProjectionLayer::textureArrayLength() const {
+uint16_t XRGPUDrawingContext::TextureArrayLength() const {
   return color_swap_chain_->descriptor().size.depthOrArrayLayers;
 }
 
-void XRGPUProjectionLayer::OnFrameStart() {
+bool XRGPUDrawingContext::TextureWasQueried() const {
+  return color_swap_chain_->texture_was_queried();
+}
+
+void XRGPUDrawingContext::SetCompositionLayer(XRCompositionLayer* layer) {
+  color_swap_chain_->SetLayer(layer);
+  if (depth_stencil_swap_chain_) {
+    depth_stencil_swap_chain_->SetLayer(layer);
+  }
+}
+
+void XRGPUDrawingContext::OnFrameStart() {
   color_swap_chain_->OnFrameStart();
   if (depth_stencil_swap_chain_) {
     depth_stencil_swap_chain_->OnFrameStart();
   }
 }
 
-void XRGPUProjectionLayer::OnFrameEnd() {
+void XRGPUDrawingContext::OnFrameEnd() {
   color_swap_chain_->OnFrameEnd();
   if (depth_stencil_swap_chain_) {
     depth_stencil_swap_chain_->OnFrameEnd();
   }
-
-  XRFrameProvider* frame_provider = session()->xr()->frameProvider();
-
-  if (viewport_updated_) {
-    frame_provider->UpdateLayerViewports(this);
-    viewport_updated_ = false;
-  }
-
-  frame_provider->SubmitWebGPULayer(this,
-                                    color_swap_chain_->texture_was_queried());
 }
 
-void XRGPUProjectionLayer::OnResize() {}
-
-void XRGPUProjectionLayer::Trace(Visitor* visitor) const {
+void XRGPUDrawingContext::Trace(Visitor* visitor) const {
   visitor->Trace(device_);
   visitor->Trace(color_swap_chain_);
   visitor->Trace(depth_stencil_swap_chain_);
-  XRProjectionLayer::Trace(visitor);
+  XRLayerDrawingContext::Trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_gpu_drawing_context.h b/third_party/blink/renderer/modules/xr/xr_gpu_drawing_context.h
new file mode 100644
index 0000000..5b0341a
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_gpu_drawing_context.h
@@ -0,0 +1,54 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_GPU_DRAWING_CONTEXT_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_GPU_DRAWING_CONTEXT_H_
+
+#include "third_party/blink/renderer/modules/xr/xr_layer_drawing_context.h"
+
+namespace blink {
+
+class GPUDevice;
+class XRCompositionLayer;
+class XRGPUBinding;
+class XRGPUSwapChain;
+
+class XRGPUDrawingContext final : public XRLayerDrawingContext {
+ public:
+  XRGPUDrawingContext(XRGPUBinding*,
+                      XRGPUSwapChain* color_swap_chain,
+                      XRGPUSwapChain* depth_stencil_swap_chain);
+  ~XRGPUDrawingContext() = default;
+
+  enum XRGraphicsBinding::Api GraphicsApi() const override;
+
+  uint16_t TextureWidth() const override;
+  uint16_t TextureHeight() const override;
+  uint16_t TextureArrayLength() const override;
+
+  void SetCompositionLayer(XRCompositionLayer* layer) override;
+
+  void OnFrameStart() override;
+  void OnFrameEnd() override;
+
+  bool TextureWasQueried() const override;
+
+  GPUDevice* device() { return device_; }
+
+  XRGPUSwapChain* color_swap_chain() { return color_swap_chain_.Get(); }
+  XRGPUSwapChain* depth_stencil_swap_chain() {
+    return depth_stencil_swap_chain_.Get();
+  }
+
+  void Trace(Visitor*) const override;
+
+ private:
+  Member<GPUDevice> device_;
+  Member<XRGPUSwapChain> color_swap_chain_;
+  Member<XRGPUSwapChain> depth_stencil_swap_chain_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_GPU_DRAWING_CONTEXT_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_gpu_projection_layer.h b/third_party/blink/renderer/modules/xr/xr_gpu_projection_layer.h
deleted file mode 100644
index 78f48ae..0000000
--- a/third_party/blink/renderer/modules/xr/xr_gpu_projection_layer.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_GPU_PROJECTION_LAYER_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_GPU_PROJECTION_LAYER_H_
-
-#include "third_party/blink/renderer/modules/xr/xr_projection_layer.h"
-
-namespace blink {
-
-class GPUDevice;
-class XRGPUBinding;
-class XRGPUSwapChain;
-
-class XRGPUProjectionLayer final : public XRProjectionLayer {
- public:
-  XRGPUProjectionLayer(XRGPUBinding*,
-                       XRGPUSwapChain* color_swap_chain,
-                       XRGPUSwapChain* depth_stencil_swap_chain);
-  ~XRGPUProjectionLayer() override = default;
-
-  uint16_t textureWidth() const override;
-  uint16_t textureHeight() const override;
-  uint16_t textureArrayLength() const override;
-
-  void OnFrameStart() override;
-  void OnFrameEnd() override;
-  void OnResize() override;
-
-  XRGPUSwapChain* color_swap_chain() { return color_swap_chain_.Get(); }
-  XRGPUSwapChain* depth_stencil_swap_chain() {
-    return depth_stencil_swap_chain_.Get();
-  }
-
-  void MarkViewportUpdated() { viewport_updated_ = true; }
-
-  void Trace(Visitor*) const override;
-
- private:
-  Member<GPUDevice> device_;
-  Member<XRGPUSwapChain> color_swap_chain_;
-  Member<XRGPUSwapChain> depth_stencil_swap_chain_;
-  bool viewport_updated_ = true;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_GPU_PROJECTION_LAYER_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_layer.cc b/third_party/blink/renderer/modules/xr/xr_layer.cc
index 4bd1c54..8cb638a 100644
--- a/third_party/blink/renderer/modules/xr/xr_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_layer.cc
@@ -29,6 +29,14 @@
   return session_->LayerSharedImageManager().HasLayerSharedImage(layer_id_);
 }
 
+void XRLayer::SetModified(bool is_modified) {
+  is_modified_ = is_modified;
+}
+
+bool XRLayer::IsModified() const {
+  return is_modified_;
+}
+
 void XRLayer::Trace(Visitor* visitor) const {
   visitor->Trace(session_);
   EventTarget::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/xr/xr_layer.h b/third_party/blink/renderer/modules/xr/xr_layer.h
index 872d63b..8ad9866 100644
--- a/third_party/blink/renderer/modules/xr/xr_layer.h
+++ b/third_party/blink/renderer/modules/xr/xr_layer.h
@@ -15,6 +15,14 @@
 class XRSession;
 struct XRSharedImageData;
 
+enum class XRLayerType {
+  kWebGLLayer,
+  kProjectionLayer,
+  kQuadLayer,
+  kCylinderLayer,
+  kEquirectLayer
+};
+
 class XRLayer : public EventTarget {
   DEFINE_WRAPPERTYPEINFO();
 
@@ -33,15 +41,20 @@
   const AtomicString& InterfaceName() const override;
 
   uint32_t layer_id() const { return layer_id_; }
+  virtual XRLayerType LayerType() const = 0;
 
   const XRSharedImageData& SharedImage() const;
   bool HasSharedImage() const;
 
+  void SetModified(bool modified);
+  bool IsModified() const;
+
   void Trace(Visitor*) const override;
 
  private:
   const Member<XRSession> session_;
   const uint32_t layer_id_;
+  bool is_modified_{false};
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_layer_drawing_context.h b/third_party/blink/renderer/modules/xr/xr_layer_drawing_context.h
new file mode 100644
index 0000000..23f6375
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_layer_drawing_context.h
@@ -0,0 +1,36 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_LAYER_DRAWING_CONTEXT_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_LAYER_DRAWING_CONTEXT_H_
+
+#include "third_party/blink/renderer/modules/xr/xr_graphics_binding.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+
+namespace blink {
+
+class XRCompositionLayer;
+
+class XRLayerDrawingContext : public GarbageCollected<XRLayerDrawingContext> {
+ public:
+  virtual enum XRGraphicsBinding::Api GraphicsApi() const = 0;
+
+  virtual void OnFrameStart() = 0;
+  virtual void OnFrameEnd() = 0;
+
+  virtual void SetCompositionLayer(XRCompositionLayer* layer) = 0;
+
+  virtual uint16_t TextureWidth() const = 0;
+  virtual uint16_t TextureHeight() const = 0;
+  virtual uint16_t TextureArrayLength() const = 0;
+
+  virtual bool TextureWasQueried() const = 0;
+
+  virtual void Trace(Visitor* visitor) const {}
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_LAYER_DRAWING_CONTEXT_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_projection_layer.cc b/third_party/blink/renderer/modules/xr/xr_projection_layer.cc
index 3a6429d2..e65a0c3 100644
--- a/third_party/blink/renderer/modules/xr/xr_projection_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_projection_layer.cc
@@ -9,8 +9,13 @@
 
 namespace blink {
 
-XRProjectionLayer::XRProjectionLayer(XRGraphicsBinding* binding)
-    : XRCompositionLayer(binding) {}
+XRProjectionLayer::XRProjectionLayer(XRGraphicsBinding* binding,
+                                     XRLayerDrawingContext* drawing_context)
+    : XRCompositionLayer(binding, drawing_context) {}
+
+XRLayerType XRProjectionLayer::LayerType() const {
+  return XRLayerType::kProjectionLayer;
+}
 
 bool XRProjectionLayer::ignoreDepthValues() const {
   return ignore_depth_values_;
diff --git a/third_party/blink/renderer/modules/xr/xr_projection_layer.h b/third_party/blink/renderer/modules/xr/xr_projection_layer.h
index 49fe986..dd2cb97 100644
--- a/third_party/blink/renderer/modules/xr/xr_projection_layer.h
+++ b/third_party/blink/renderer/modules/xr/xr_projection_layer.h
@@ -8,6 +8,7 @@
 #include <optional>
 
 #include "third_party/blink/renderer/modules/xr/xr_composition_layer.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 
 namespace blink {
 
@@ -17,9 +18,12 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  explicit XRProjectionLayer(XRGraphicsBinding* binding);
+  XRProjectionLayer(XRGraphicsBinding* binding,
+                    XRLayerDrawingContext* drawing_context);
   ~XRProjectionLayer() override = default;
 
+  XRLayerType LayerType() const override;
+
   bool ignoreDepthValues() const;
   std::optional<float> fixedFoveation() const;
   void setFixedFoveation(std::optional<float> value);
@@ -34,6 +38,13 @@
   Member<XRRigidTransform> delta_pose_{nullptr};
 };
 
+template <>
+struct DowncastTraits<XRProjectionLayer> {
+  static bool AllowFrom(const XRCompositionLayer& layer) {
+    return layer.LayerType() == XRLayerType::kProjectionLayer;
+  }
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_PROJECTION_LAYER_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_quad_layer.cc b/third_party/blink/renderer/modules/xr/xr_quad_layer.cc
index 950504d..90f2baf 100644
--- a/third_party/blink/renderer/modules/xr/xr_quad_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_quad_layer.cc
@@ -14,8 +14,9 @@
 namespace blink {
 
 XRQuadLayer::XRQuadLayer(const XRQuadLayerInit* init,
-                         XRGraphicsBinding* binding)
-    : XRShapedLayer(init, binding),
+                         XRGraphicsBinding* binding,
+                         XRLayerDrawingContext* drawing_context)
+    : XRShapedLayer(init, binding, drawing_context),
       width_(init->width()),
       height_(init->height()) {
   if (init->hasTransform()) {
@@ -26,20 +27,24 @@
   }
 }
 
+XRLayerType XRQuadLayer::LayerType() const {
+  return XRLayerType::kQuadLayer;
+}
+
 void XRQuadLayer::setWidth(float width) {
   width_ = width;
-  OnUpdateLayerData();
+  SetModified(true);
 }
 
 void XRQuadLayer::setHeight(float height) {
   height_ = height;
-  OnUpdateLayerData();
+  SetModified(true);
 }
 
 void XRQuadLayer::setTransform(XRRigidTransform* value) {
   if (transform_ != value) {
     transform_ = value;
-    OnUpdateLayerData();
+    SetModified(true);
   }
 }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_quad_layer.h b/third_party/blink/renderer/modules/xr/xr_quad_layer.h
index e174093..4de73d9b6 100644
--- a/third_party/blink/renderer/modules/xr/xr_quad_layer.h
+++ b/third_party/blink/renderer/modules/xr/xr_quad_layer.h
@@ -18,9 +18,13 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  explicit XRQuadLayer(const XRQuadLayerInit* init, XRGraphicsBinding* binding);
+  XRQuadLayer(const XRQuadLayerInit* init,
+              XRGraphicsBinding* binding,
+              XRLayerDrawingContext* drawing_context);
   ~XRQuadLayer() override = default;
 
+  XRLayerType LayerType() const override;
+
   XRRigidTransform* transform() const { return transform_.Get(); }
   void setTransform(XRRigidTransform* value);
 
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index 3db417b..f13efd86 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -78,7 +78,7 @@
     "This device does not support the requested reference space type.";
 
 const char kIncompatibleLayer[] =
-    "XRWebGLLayer was created with a different session.";
+    "XRLayer was created with a different session.";
 
 const char kBaseLayerAndLayers[] =
     "Both baseLayer and layers should not be set at the same time when "
diff --git a/third_party/blink/renderer/modules/xr/xr_shaped_layer.cc b/third_party/blink/renderer/modules/xr/xr_shaped_layer.cc
index c5d6397..570c159 100644
--- a/third_party/blink/renderer/modules/xr/xr_shaped_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_shaped_layer.cc
@@ -11,8 +11,9 @@
 namespace blink {
 
 XRShapedLayer::XRShapedLayer(const XRLayerInit* init,
-                             XRGraphicsBinding* binding)
-    : XRCompositionLayer(binding),
+                             XRGraphicsBinding* binding,
+                             XRLayerDrawingContext* drawing_context)
+    : XRCompositionLayer(binding, drawing_context),
       xr_space_(init->space()),
       texture_width_(init->viewPixelWidth()),
       texture_height_(init->viewPixelHeight()),
@@ -28,15 +29,7 @@
 
 void XRShapedLayer::setSpace(XRSpace* space) {
   xr_space_ = space;
-  OnUpdateLayerData();
-}
-
-uint16_t XRShapedLayer::textureWidth() const {
-  return texture_width_;
-}
-
-uint16_t XRShapedLayer::textureHeight() const {
-  return texture_height_;
+  SetModified(true);
 }
 
 void XRShapedLayer::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/modules/xr/xr_shaped_layer.h b/third_party/blink/renderer/modules/xr/xr_shaped_layer.h
index bcd1cb6..a3c956d 100644
--- a/third_party/blink/renderer/modules/xr/xr_shaped_layer.h
+++ b/third_party/blink/renderer/modules/xr/xr_shaped_layer.h
@@ -15,7 +15,9 @@
 
 class XRShapedLayer : public XRCompositionLayer {
  public:
-  explicit XRShapedLayer(const XRLayerInit* init, XRGraphicsBinding* binding);
+  XRShapedLayer(const XRLayerInit* init,
+                XRGraphicsBinding* binding,
+                XRLayerDrawingContext* drawing_context);
   ~XRShapedLayer() override = default;
 
   // onredraw event handler
@@ -29,10 +31,6 @@
   bool isStatic() const { return is_static_; }
   bool clearOnAccess() const { return clear_on_access_; }
 
-  uint16_t textureWidth() const override;
-  uint16_t textureHeight() const override;
-  uint16_t textureArrayLength() const override { return 1; }
-
   // TODO(crbug.com/443963000): Initialize mojom backend.
   bool InitializeLayer() const;
   // TODO(crbug.com/443963000): Send data the mojom backend.
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc b/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc
index df36026..7347c91 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc
@@ -29,8 +29,8 @@
 #include "third_party/blink/renderer/modules/xr/xr_utils.h"
 #include "third_party/blink/renderer/modules/xr/xr_viewer_pose.h"
 #include "third_party/blink/renderer/modules/xr/xr_webgl_drawing_buffer_swap_chain.h"
+#include "third_party/blink/renderer/modules/xr/xr_webgl_drawing_context.h"
 #include "third_party/blink/renderer/modules/xr/xr_webgl_layer.h"
-#include "third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.h"
 #include "third_party/blink/renderer/modules/xr/xr_webgl_sub_image.h"
 #include "third_party/blink/renderer/modules/xr/xr_webgl_swap_chain.h"
 #include "third_party/blink/renderer/modules/xr/xr_webgl_texture_array_swap_chain.h"
@@ -105,6 +105,29 @@
   return false;
 }
 
+XRWebGLSwapChain* XRWebGLBinding::CreateColorSwapchain(GLenum layer_format,
+                                                       gfx::Size texture_size) {
+  XRWebGLSwapChain::Descriptor color_desc = {};
+  color_desc.format = FormatForLayerFormat(layer_format);
+  color_desc.internal_format = InternalFormatForLayerFormat(layer_format);
+  color_desc.type = TypeForLayerFormat(layer_format);
+  color_desc.attachment_target = GL_COLOR_ATTACHMENT0;
+  color_desc.width = static_cast<uint32_t>(texture_size.width());
+  color_desc.height = static_cast<uint32_t>(texture_size.height());
+  color_desc.layers = 1;
+
+  XRWebGLSwapChain* color_swap_chain;
+  if (session()->xr()->frameProvider()->DrawingIntoSharedBuffer()) {
+    color_swap_chain = MakeGarbageCollected<XRWebGLSharedImageSwapChain>(
+        webgl_context_, color_desc, webgl2_);
+  } else {
+    color_swap_chain = MakeGarbageCollected<XRWebGLDrawingBufferSwapChain>(
+        webgl_context_, color_desc, webgl2_);
+  }
+
+  return color_swap_chain;
+}
+
 XRProjectionLayer* XRWebGLBinding::createProjectionLayer(
     const XRProjectionLayerInit* init,
     ExceptionState& exception_state) {
@@ -153,24 +176,8 @@
 
   gfx::Size texture_size = gfx::ToFlooredSize(scaled_size);
 
-  XRWebGLSwapChain::Descriptor color_desc = {};
-  color_desc.format = FormatForLayerFormat(init->colorFormat());
-  color_desc.internal_format =
-      InternalFormatForLayerFormat(init->colorFormat());
-  color_desc.type = TypeForLayerFormat(init->colorFormat());
-  color_desc.attachment_target = GL_COLOR_ATTACHMENT0;
-  color_desc.width = static_cast<uint32_t>(texture_size.width());
-  color_desc.height = static_cast<uint32_t>(texture_size.height());
-  color_desc.layers = 1;
-
-  XRWebGLSwapChain* color_swap_chain;
-  if (session()->xr()->frameProvider()->DrawingIntoSharedBuffer()) {
-    color_swap_chain = MakeGarbageCollected<XRWebGLSharedImageSwapChain>(
-        webgl_context_, color_desc, webgl2_);
-  } else {
-    color_swap_chain = MakeGarbageCollected<XRWebGLDrawingBufferSwapChain>(
-        webgl_context_, color_desc, webgl2_);
-  }
+  XRWebGLSwapChain* color_swap_chain =
+      CreateColorSwapchain(init->colorFormat(), texture_size);
 
   if (is_texture_array) {
     // If a texture-array was requested, create a texture array wrapper for the
@@ -207,8 +214,10 @@
         webgl_context_, depth_stencil_desc, webgl2_);
   }
 
-  return MakeGarbageCollected<XRWebGLProjectionLayer>(this, color_swap_chain,
-                                                      depth_stencil_swap_chain);
+  auto* drawing_context = MakeGarbageCollected<XRWebGLDrawingContext>(
+      this, color_swap_chain, depth_stencil_swap_chain);
+
+  return MakeGarbageCollected<XRProjectionLayer>(this, drawing_context);
 }
 
 XRQuadLayer* XRWebGLBinding::createQuadLayer(const XRQuadLayerInit* init,
@@ -286,22 +295,21 @@
     return nullptr;
   }
 
-  // Because we have validated that this is a layer owned by this binding we
-  // know that it is a XRWebGLProjectionLayer, because that's the only type of
-  // projection layer that this class returns.
-  XRWebGLProjectionLayer* gl_layer =
-      static_cast<XRWebGLProjectionLayer*>(layer);
-
   XRViewData* viewData = view->ViewData();
   if (viewData->ApplyViewportScaleForFrame()) {
-    gl_layer->MarkViewportUpdated();
+    layer->SetModified(true);
   }
 
   gfx::Rect viewport = GetViewportForView(layer, viewData);
 
+  // The layer passed the OwnsLayer check, confirming it can only contain
+  // a WebGL drawing context. This makes the static_cast safe.
+  auto* drawing_context =
+      static_cast<XRWebGLDrawingContext*>(layer->drawing_context());
+
   return MakeGarbageCollected<XRWebGLSubImage>(
-      viewport, viewData->index(), gl_layer->color_swap_chain(),
-      gl_layer->depth_stencil_swap_chain(), nullptr);
+      viewport, viewData->index(), drawing_context->color_swap_chain(),
+      drawing_context->depth_stencil_swap_chain(), nullptr);
 }
 
 XRWebGLSubImage* XRWebGLBinding::getSubImage(XRCompositionLayer* layer,
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_binding.h b/third_party/blink/renderer/modules/xr/xr_webgl_binding.h
index 58bddd07..330755d 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_binding.h
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_binding.h
@@ -98,6 +98,8 @@
   GLenum InternalFormatForLayerFormat(GLenum format);
   GLenum TypeForLayerFormat(GLenum format);
 
+  XRWebGLSwapChain* CreateColorSwapchain(GLenum layer_format,
+                                         gfx::Size layer_size);
   XRWebGLSwapChain* GetSwapchainForLayer(XRCompositionLayer* layer);
 
   Member<WebGLRenderingContextBase> webgl_context_;
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.cc b/third_party/blink/renderer/modules/xr/xr_webgl_drawing_context.cc
similarity index 61%
rename from third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.cc
rename to third_party/blink/renderer/modules/xr/xr_webgl_drawing_context.cc
index 88ac5810..1dffcce 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_drawing_context.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.h"
+#include "third_party/blink/renderer/modules/xr/xr_webgl_drawing_context.h"
 
 #include "third_party/blink/renderer/bindings/modules/v8/v8_xr_gpu_projection_layer_init.h"
 #include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h"
@@ -15,69 +15,71 @@
 
 namespace blink {
 
-XRWebGLProjectionLayer::XRWebGLProjectionLayer(
+XRWebGLDrawingContext::XRWebGLDrawingContext(
     XRWebGLBinding* binding,
     XRWebGLSwapChain* color_swap_chain,
     XRWebGLSwapChain* depth_stencil_swap_chain)
-    : XRProjectionLayer(binding),
-      webgl_context_(binding->context()),
+    : webgl_context_(binding->context()),
       color_swap_chain_(color_swap_chain),
       depth_stencil_swap_chain_(depth_stencil_swap_chain) {
   CHECK(color_swap_chain_);
-  color_swap_chain_->SetLayer(this);
+}
+
+enum XRGraphicsBinding::Api XRWebGLDrawingContext::GraphicsApi() const {
+  return XRGraphicsBinding::Api::kWebGL;
+}
+
+void XRWebGLDrawingContext::SetCompositionLayer(XRCompositionLayer* layer) {
+  color_swap_chain_->SetLayer(layer);
   if (depth_stencil_swap_chain_) {
-    depth_stencil_swap_chain_->SetLayer(this);
+    depth_stencil_swap_chain_->SetLayer(layer);
   }
 }
 
-uint16_t XRWebGLProjectionLayer::textureWidth() const {
+uint16_t XRWebGLDrawingContext::TextureWidth() const {
   return color_swap_chain_->descriptor().width;
 }
 
-uint16_t XRWebGLProjectionLayer::textureHeight() const {
+uint16_t XRWebGLDrawingContext::TextureHeight() const {
   return color_swap_chain_->descriptor().height;
 }
 
-uint16_t XRWebGLProjectionLayer::textureArrayLength() const {
+uint16_t XRWebGLDrawingContext::TextureArrayLength() const {
   return color_swap_chain_->descriptor().layers;
 }
 
-void XRWebGLProjectionLayer::OnFrameStart() {
+bool XRWebGLDrawingContext::TextureWasQueried() const {
+  return color_swap_chain_->texture_was_queried();
+}
+
+void XRWebGLDrawingContext::OnFrameStart() {
   color_swap_chain_->OnFrameStart();
   if (depth_stencil_swap_chain_) {
     depth_stencil_swap_chain_->OnFrameStart();
   }
 }
 
-void XRWebGLProjectionLayer::OnFrameEnd() {
+void XRWebGLDrawingContext::OnFrameEnd() {
   color_swap_chain_->OnFrameEnd();
   if (depth_stencil_swap_chain_) {
     depth_stencil_swap_chain_->OnFrameEnd();
   }
+}
 
-  XRFrameProvider* frame_provider = session()->xr()->frameProvider();
-
-  if (viewport_updated_) {
-    frame_provider->UpdateLayerViewports(this);
-    viewport_updated_ = false;
-  }
-
-  frame_provider->SubmitWebGLLayer(this,
-                                   color_swap_chain_->texture_was_queried());
+const XRLayer* XRWebGLDrawingContext::layer() const {
+  return color_swap_chain_->layer();
 }
 
 scoped_refptr<StaticBitmapImage>
-XRWebGLProjectionLayer::TransferToStaticBitmapImage() {
+XRWebGLDrawingContext::TransferToStaticBitmapImage() {
   return color_swap_chain_->TransferToStaticBitmapImage();
 }
 
-void XRWebGLProjectionLayer::OnResize() {}
-
-void XRWebGLProjectionLayer::Trace(Visitor* visitor) const {
+void XRWebGLDrawingContext::Trace(Visitor* visitor) const {
   visitor->Trace(webgl_context_);
   visitor->Trace(color_swap_chain_);
   visitor->Trace(depth_stencil_swap_chain_);
-  XRProjectionLayer::Trace(visitor);
+  XRLayerDrawingContext::Trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_drawing_context.h b/third_party/blink/renderer/modules/xr/xr_webgl_drawing_context.h
new file mode 100644
index 0000000..9bcfe43
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_drawing_context.h
@@ -0,0 +1,61 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_DRAWING_CONTEXT_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_DRAWING_CONTEXT_H_
+
+#include "third_party/blink/renderer/modules/xr/xr_layer_drawing_context.h"
+#include "third_party/blink/renderer/modules/xr/xr_webgl_layer_client.h"
+
+namespace blink {
+
+class WebGLRenderingContextBase;
+class XRWebGLBinding;
+class XRWebGLSwapChain;
+class XRCompositionLayer;
+
+class XRWebGLDrawingContext final : public XRLayerDrawingContext,
+                                    public XRWebGLLayerClient {
+ public:
+  XRWebGLDrawingContext(XRWebGLBinding*,
+                        XRWebGLSwapChain* color_swap_chain,
+                        XRWebGLSwapChain* depth_stencil_swap_chain);
+  ~XRWebGLDrawingContext() = default;
+
+  enum XRGraphicsBinding::Api GraphicsApi() const override;
+
+  // XRWebGLLayerClient implementation.
+  const XRLayer* layer() const override;
+  WebGLRenderingContextBase* context() const override {
+    return webgl_context_.Get();
+  }
+  scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage() override;
+
+  uint16_t TextureWidth() const override;
+  uint16_t TextureHeight() const override;
+  uint16_t TextureArrayLength() const override;
+
+  bool TextureWasQueried() const override;
+
+  void SetCompositionLayer(XRCompositionLayer* layer) override;
+
+  void OnFrameStart() override;
+  void OnFrameEnd() override;
+
+  XRWebGLSwapChain* color_swap_chain() const { return color_swap_chain_.Get(); }
+  XRWebGLSwapChain* depth_stencil_swap_chain() const {
+    return depth_stencil_swap_chain_.Get();
+  }
+
+  void Trace(Visitor*) const override;
+
+ private:
+  Member<WebGLRenderingContextBase> webgl_context_;
+  Member<XRWebGLSwapChain> color_swap_chain_;
+  Member<XRWebGLSwapChain> depth_stencil_swap_chain_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_DRAWING_CONTEXT_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
index 826f173a0..55aba0a 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
@@ -182,6 +182,10 @@
   }
 }
 
+XRLayerType XRWebGLLayer::LayerType() const {
+  return XRLayerType::kWebGLLayer;
+}
+
 uint32_t XRWebGLLayer::framebufferWidth() const {
   if (drawing_buffer_) {
     return drawing_buffer_->size().width();
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.h b/third_party/blink/renderer/modules/xr/xr_webgl_layer.h
index c6aac3b..bb952bd 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.h
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.h
@@ -82,6 +82,8 @@
   void OnFrameEnd() override;
   void OnResize() override;
 
+  XRLayerType LayerType() const override;
+
   void Trace(Visitor*) const override;
 
  private:
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.h b/third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.h
deleted file mode 100644
index 3106b6e..0000000
--- a/third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_PROJECTION_LAYER_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_PROJECTION_LAYER_H_
-
-#include "third_party/blink/renderer/modules/xr/xr_projection_layer.h"
-#include "third_party/blink/renderer/modules/xr/xr_webgl_layer_client.h"
-
-namespace blink {
-
-class WebGLRenderingContextBase;
-class XRWebGLBinding;
-class XRWebGLSwapChain;
-
-class XRWebGLProjectionLayer final : public XRProjectionLayer,
-                                     public XRWebGLLayerClient {
- public:
-  XRWebGLProjectionLayer(XRWebGLBinding*,
-                         XRWebGLSwapChain* color_swap_chain,
-                         XRWebGLSwapChain* depth_stencil_swap_chain);
-  ~XRWebGLProjectionLayer() override = default;
-
-  // XRWebGLLayerClient implementation
-  const XRLayer* layer() const override { return this; }
-  WebGLRenderingContextBase* context() const override {
-    return webgl_context_.Get();
-  }
-  scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage() override;
-
-  uint16_t textureWidth() const override;
-  uint16_t textureHeight() const override;
-  uint16_t textureArrayLength() const override;
-
-  void OnFrameStart() override;
-  void OnFrameEnd() override;
-  void OnResize() override;
-
-  XRWebGLSwapChain* color_swap_chain() const { return color_swap_chain_.Get(); }
-  XRWebGLSwapChain* depth_stencil_swap_chain() const {
-    return depth_stencil_swap_chain_.Get();
-  }
-
-  void MarkViewportUpdated() { viewport_updated_ = true; }
-
-  void Trace(Visitor*) const override;
-
- private:
-  Member<WebGLRenderingContextBase> webgl_context_;
-  Member<XRWebGLSwapChain> color_swap_chain_;
-  Member<XRWebGLSwapChain> depth_stencil_swap_chain_;
-  bool viewport_updated_ = true;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_PROJECTION_LAYER_H_
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc
index da7448d..59869a8 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc
@@ -118,7 +118,7 @@
   unsigned size = to - from;
   Vector<uint32_t, 256> pending_utf8_character_index_from_character_index(size);
   if (current_text_.Is8Bit()) {
-    const LChar* latin1 = UNSAFE_TODO(current_text_.Characters8());
+    const LChar* latin1 = current_text_.Span8().data();
     wtf_size_t utf8_size = pending_utf8_.size();
     for (unsigned i = from; i < to;) {
       pending_utf8_character_index_from_character_index[i - from] = utf8_size;
@@ -128,7 +128,7 @@
       UNSAFE_TODO(U8_APPEND_UNSAFE(pending_utf8_.begin(), utf8_size, cp));
     }
   } else {
-    const UChar* utf16 = UNSAFE_TODO(current_text_.Characters16());
+    const UChar* utf16 = current_text_.Span16().data();
     wtf_size_t utf8_size = pending_utf8_.size();
     for (unsigned i = from; i < to;) {
       pending_utf8_character_index_from_character_index[i - from] = utf8_size;
diff --git a/third_party/blink/renderer/platform/wtf/pod_arena.h b/third_party/blink/renderer/platform/wtf/pod_arena.h
index f00de5e4..abbe23a 100644
--- a/third_party/blink/renderer/platform/wtf/pod_arena.h
+++ b/third_party/blink/renderer/platform/wtf/pod_arena.h
@@ -23,18 +23,15 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_POD_ARENA_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_POD_ARENA_H_
 
 #include <stdint.h>
+
 #include <memory>
 #include <utility>
 
+#include "base/compiler_specific.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
@@ -149,8 +146,10 @@
     // Allocates a block of memory of the given size from the passed
     // Allocator.
     Chunk(Allocator* allocator, size_t size)
-        : allocator_(allocator), size_(size), current_offset_(0) {
-      base_ = static_cast<uint8_t*>(allocator_->Allocate(size));
+        : allocator_(allocator), current_offset_(0) {
+      uint8_t* allocated = static_cast<uint8_t*>(allocator_->Allocate(size));
+      // SAFETY: Allocate() ensures `allocated` has `size` bytes.
+      base_ = UNSAFE_BUFFERS(base::span<uint8_t>(allocated, allocated + size));
     }
 
     Chunk(const Chunk&) = delete;
@@ -158,7 +157,7 @@
 
     // Frees the memory allocated from the Allocator in the
     // constructor.
-    ~Chunk() { allocator_->Free(base_); }
+    ~Chunk() { allocator_->Free(base_.data()); }
 
     // Returns a pointer to "size" bytes of storage, or 0 if this
     // Chunk could not satisfy the allocation.
@@ -167,18 +166,18 @@
       if (current_offset_ + size < current_offset_)
         return nullptr;
 
-      if (current_offset_ + size > size_)
+      if (current_offset_ + size > base_.size()) {
         return nullptr;
+      }
 
-      void* result = base_ + current_offset_;
+      void* result = base_.subspan(current_offset_, size).data();
       current_offset_ += size;
       return result;
     }
 
    protected:
     Allocator* allocator_;
-    uint8_t* base_;
-    size_t size_;
+    base::span<uint8_t> base_;
     size_t current_offset_;
   };
 
diff --git a/third_party/blink/renderer/platform/wtf/text/string_view.h b/third_party/blink/renderer/platform/wtf/text/string_view.h
index b44b310f..302da50 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_view.h
+++ b/third_party/blink/renderer/platform/wtf/text/string_view.h
@@ -199,12 +199,6 @@
   }
 
   // Use Span16() instead.
-  UNSAFE_BUFFER_USAGE const LChar* Characters8() const {
-    DCHECK(Is8Bit());
-    return static_cast<const LChar*>(bytes_);
-  }
-
-  // Use Span16() instead.
   UNSAFE_BUFFER_USAGE const UChar* Characters16() const {
     DCHECK(!Is8Bit());
     return static_cast<const UChar*>(bytes_);
@@ -328,9 +322,9 @@
   // SAFETY: Invariants are checked last two line.
   UNSAFE_BUFFERS({
     if (Is8Bit()) {
-      bytes_ = view.Characters8() + offset;
+      bytes_ = view.Span8().data() + offset;
     } else {
-      bytes_ = view.Characters16() + offset;
+      bytes_ = view.Span16().data() + offset;
     }
   });
 }
diff --git a/third_party/blink/renderer/platform/wtf/text/string_view_test.cc b/third_party/blink/renderer/platform/wtf/text/string_view_test.cc
index 9a10a38a..4f7f44c 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_view_test.cc
+++ b/third_party/blink/renderer/platform/wtf/text/string_view_test.cc
@@ -12,6 +12,18 @@
 
 namespace blink {
 
+const LChar* Address8(const StringImpl& impl, size_t offset = 0) {
+  return offset ? impl.Span8().subspan(offset).data() : impl.Span8().data();
+}
+
+const LChar* Address8(const String& str, size_t offset = 0) {
+  return offset ? str.Span8().subspan(offset).data() : str.Span8().data();
+}
+
+const LChar* Address8(StringView view, size_t offset = 0) {
+  return offset ? view.Span8().subspan(offset).data() : view.Span8().data();
+}
+
 const char kChars[] = "12345";
 const char16_t kCharsU[] = u"12345";
 const LChar* const kChars8 = reinterpret_cast<const LChar*>(kChars);
@@ -24,16 +36,14 @@
   // StringView(StringImpl*);
   ASSERT_TRUE(StringView(impl8_bit.get()).Is8Bit());
   EXPECT_FALSE(StringView(impl8_bit.get()).IsNull());
-  UNSAFE_TODO(EXPECT_EQ(impl8_bit->Characters8(),
-                        StringView(impl8_bit.get()).Characters8()));
+  EXPECT_EQ(Address8(*impl8_bit), Address8(StringView(impl8_bit.get())));
   EXPECT_EQ(impl8_bit->length(), StringView(impl8_bit.get()).length());
   EXPECT_EQ(kChars, StringView(impl8_bit.get()));
 
   // StringView(StringImpl*, unsigned offset);
   ASSERT_TRUE(StringView(impl8_bit.get(), 2).Is8Bit());
   EXPECT_FALSE(StringView(impl8_bit.get(), 2).IsNull());
-  UNSAFE_TODO(EXPECT_EQ(impl8_bit->Characters8() + 2,
-                        StringView(impl8_bit.get(), 2).Characters8()));
+  EXPECT_EQ(Address8(*impl8_bit, 2u), Address8(StringView(impl8_bit.get(), 2)));
   EXPECT_EQ(3u, StringView(impl8_bit.get(), 2).length());
   EXPECT_EQ(StringView("345"), StringView(impl8_bit.get(), 2));
   EXPECT_EQ("345", StringView(impl8_bit.get(), 2));
@@ -41,8 +51,8 @@
   // StringView(StringImpl*, unsigned offset, unsigned length);
   ASSERT_TRUE(StringView(impl8_bit.get(), 2, 1).Is8Bit());
   EXPECT_FALSE(StringView(impl8_bit.get(), 2, 1).IsNull());
-  UNSAFE_TODO(EXPECT_EQ(impl8_bit->Characters8() + 2,
-                        StringView(impl8_bit.get(), 2, 1).Characters8()));
+  EXPECT_EQ(Address8(*impl8_bit, 2u),
+            Address8(StringView(impl8_bit.get(), 2, 1)));
   EXPECT_EQ(1u, StringView(impl8_bit.get(), 2, 1).length());
   EXPECT_EQ(StringView("3"), StringView(impl8_bit.get(), 2, 1));
   EXPECT_EQ("3", StringView(impl8_bit.get(), 2, 1));
@@ -86,16 +96,14 @@
   // StringView(StringImpl&);
   ASSERT_TRUE(StringView(*impl8_bit).Is8Bit());
   EXPECT_FALSE(StringView(*impl8_bit).IsNull());
-  UNSAFE_TODO(EXPECT_EQ(impl8_bit->Characters8(),
-                        StringView(*impl8_bit).Characters8()));
+  EXPECT_EQ(Address8(*impl8_bit), Address8(StringView(*impl8_bit)));
   EXPECT_EQ(impl8_bit->length(), StringView(*impl8_bit).length());
   EXPECT_EQ(kChars, StringView(*impl8_bit));
 
   // StringView(StringImpl&, unsigned offset);
   ASSERT_TRUE(StringView(*impl8_bit, 2).Is8Bit());
   EXPECT_FALSE(StringView(*impl8_bit, 2).IsNull());
-  UNSAFE_TODO(EXPECT_EQ(impl8_bit->Characters8() + 2,
-                        StringView(*impl8_bit, 2).Characters8()));
+  EXPECT_EQ(Address8(*impl8_bit, 2u), Address8(StringView(*impl8_bit, 2)));
   EXPECT_EQ(3u, StringView(*impl8_bit, 2).length());
   EXPECT_EQ(StringView("345"), StringView(*impl8_bit, 2));
   EXPECT_EQ("345", StringView(*impl8_bit, 2));
@@ -103,8 +111,7 @@
   // StringView(StringImpl&, unsigned offset, unsigned length);
   ASSERT_TRUE(StringView(*impl8_bit, 2, 1).Is8Bit());
   EXPECT_FALSE(StringView(*impl8_bit, 2, 1).IsNull());
-  UNSAFE_TODO(EXPECT_EQ(impl8_bit->Characters8() + 2,
-                        StringView(*impl8_bit, 2, 1).Characters8()));
+  EXPECT_EQ(Address8(*impl8_bit, 2u), Address8(StringView(*impl8_bit, 2, 1)));
   EXPECT_EQ(1u, StringView(*impl8_bit, 2, 1).length());
   EXPECT_EQ(StringView("3"), StringView(*impl8_bit, 2, 1));
   EXPECT_EQ("3", StringView(*impl8_bit, 2, 1));
@@ -147,16 +154,14 @@
   // StringView(const String&);
   ASSERT_TRUE(StringView(string8_bit).Is8Bit());
   EXPECT_FALSE(StringView(string8_bit).IsNull());
-  UNSAFE_TODO(EXPECT_EQ(string8_bit.Characters8(),
-                        StringView(string8_bit).Characters8()));
+  EXPECT_EQ(Address8(string8_bit), Address8(StringView(string8_bit)));
   EXPECT_EQ(string8_bit.length(), StringView(string8_bit).length());
   EXPECT_EQ(kChars, StringView(string8_bit));
 
   // StringView(const String&, unsigned offset);
   ASSERT_TRUE(StringView(string8_bit, 2).Is8Bit());
   EXPECT_FALSE(StringView(string8_bit, 2).IsNull());
-  UNSAFE_TODO(EXPECT_EQ(string8_bit.Characters8() + 2,
-                        StringView(string8_bit, 2).Characters8()));
+  EXPECT_EQ(Address8(string8_bit, 2u), Address8(StringView(string8_bit, 2)));
   EXPECT_EQ(3u, StringView(string8_bit, 2).length());
   EXPECT_EQ(StringView("345"), StringView(string8_bit, 2));
   EXPECT_EQ("345", StringView(string8_bit, 2));
@@ -164,8 +169,7 @@
   // StringView(const String&, unsigned offset, unsigned length);
   ASSERT_TRUE(StringView(string8_bit, 2, 1).Is8Bit());
   EXPECT_FALSE(StringView(string8_bit, 2, 1).IsNull());
-  UNSAFE_TODO(EXPECT_EQ(string8_bit.Characters8() + 2,
-                        StringView(string8_bit, 2, 1).Characters8()));
+  EXPECT_EQ(Address8(string8_bit, 2u), Address8(StringView(string8_bit, 2, 1)));
   EXPECT_EQ(1u, StringView(string8_bit, 2, 1).length());
   EXPECT_EQ(StringView("3"), StringView(string8_bit, 2, 1));
   EXPECT_EQ("3", StringView(string8_bit, 2, 1));
@@ -268,16 +272,14 @@
   // StringView(StringView&);
   ASSERT_TRUE(StringView(view8_bit).Is8Bit());
   EXPECT_FALSE(StringView(view8_bit).IsNull());
-  UNSAFE_TODO(
-      EXPECT_EQ(view8_bit.Characters8(), StringView(view8_bit).Characters8()));
+  EXPECT_EQ(Address8(view8_bit), Address8(StringView(view8_bit)));
   EXPECT_EQ(view8_bit.length(), StringView(view8_bit).length());
   EXPECT_EQ(kChars, StringView(view8_bit));
 
   // StringView(const StringView&, unsigned offset);
   ASSERT_TRUE(StringView(view8_bit, 2).Is8Bit());
   EXPECT_FALSE(StringView(view8_bit, 2).IsNull());
-  UNSAFE_TODO(EXPECT_EQ(view8_bit.Characters8() + 2,
-                        StringView(view8_bit, 2).Characters8()));
+  EXPECT_EQ(Address8(view8_bit, 2u), Address8(StringView(view8_bit, 2)));
   EXPECT_EQ(3u, StringView(view8_bit, 2).length());
   EXPECT_EQ(StringView("345"), StringView(view8_bit, 2));
   EXPECT_EQ("345", StringView(view8_bit, 2));
@@ -285,8 +287,7 @@
   // StringView(const StringView&, unsigned offset, unsigned length);
   ASSERT_TRUE(StringView(view8_bit, 2, 1).Is8Bit());
   EXPECT_FALSE(StringView(view8_bit, 2, 1).IsNull());
-  UNSAFE_TODO(EXPECT_EQ(view8_bit.Characters8() + 2,
-                        StringView(view8_bit, 2, 1).Characters8()));
+  EXPECT_EQ(Address8(view8_bit, 2u), Address8(StringView(view8_bit, 2, 1)));
   EXPECT_EQ(1u, StringView(view8_bit, 2, 1).length());
   EXPECT_EQ(StringView("3"), StringView(view8_bit, 2, 1));
   EXPECT_EQ("3", StringView(view8_bit, 2, 1));
@@ -343,7 +344,7 @@
   // StringView(const char* chars);
   ASSERT_TRUE(StringView(kChars).Is8Bit());
   EXPECT_FALSE(StringView(kChars).IsNull());
-  UNSAFE_TODO(EXPECT_EQ(kChars8, StringView(kChars).Characters8()));
+  EXPECT_EQ(kChars8, Address8(StringView(kChars)));
   EXPECT_EQ(5u, StringView(kChars).length());
   EXPECT_EQ(kChars, StringView(kChars));
 
@@ -380,7 +381,7 @@
   const auto kCharsSpan8 = base::byte_span_from_cstring(kChars);
   ASSERT_TRUE(StringView(kCharsSpan8).Is8Bit());
   EXPECT_FALSE(StringView(kCharsSpan8).IsNull());
-  UNSAFE_TODO(EXPECT_EQ(kChars8, StringView(kCharsSpan8).Characters8()));
+  EXPECT_EQ(kChars8, Address8(StringView(kCharsSpan8)));
   EXPECT_EQ(5u, StringView(kCharsSpan8).length());
   EXPECT_EQ(kChars, StringView(kCharsSpan8));
 }
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index b5ed90d..a168f3c 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2973,19 +2973,6 @@
 external/wpt/css/css-shadow-parts/exportparts-layered.html [ Failure ]
 external/wpt/css/css-text/text-autospace/text-autospace-elements-007.html [ Failure ]
 external/wpt/css/css-text/text-autospace/text-autospace-transform-full-width-001.tentative.html [ Failure ]
-external/wpt/css/css-overflow/line-clamp/block-ellipsis-007.html [ Failure ]
-external/wpt/css/css-overflow/line-clamp/block-ellipsis-008.html [ Failure ]
-external/wpt/css/css-overflow/line-clamp/block-ellipsis-009.html [ Failure ]
-external/wpt/css/css-overflow/line-clamp/block-ellipsis-012.html [ Failure ]
-external/wpt/css/css-overflow/line-clamp/block-ellipsis-015.tentative.html [ Failure ]
-external/wpt/css/css-overflow/line-clamp/block-ellipsis-019.html [ Failure ]
-external/wpt/css/css-overflow/line-clamp/block-ellipsis-020.html [ Failure ]
-external/wpt/css/css-overflow/line-clamp/block-ellipsis-021.html [ Failure ]
-external/wpt/css/css-overflow/line-clamp/block-ellipsis-027.html [ Failure ]
-external/wpt/css/css-overflow/line-clamp/block-ellipsis-028.html [ Failure ]
-external/wpt/css/css-overflow/line-clamp/line-clamp-auto-009.html [ Failure ]
-external/wpt/css/css-overflow/line-clamp/line-clamp-auto-040.html [ Failure ]
-external/wpt/css/css-overflow/line-clamp/line-clamp-with-abspos-017.html [ Failure ]
 external/wpt/navigation-api/navigation-methods/return-value/navigate-javascript-url.html [ Timeout ]
 
 # Flaky UUID in assertion failure
@@ -8709,23 +8696,27 @@
 crbug.com/351117372 external/wpt/css/css-writing-modes/vertical-alignment-slr-035.xht [ Crash Pass Timeout ]
 
 # line-clamp
-crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/line-clamp-auto-009.tentative.html [ Failure ]
-crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/line-clamp-with-abspos-017.tentative.html [ Failure ]
+crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/line-clamp-auto-009.html [ Failure ]
+crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/line-clamp-auto-040.html [ Failure ]
+crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/line-clamp-with-abspos-017.html [ Failure ]
 crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-001.html [ Failure ]
-crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-007.tentative.html [ Failure ]
-crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-008.tentative.html [ Failure ]
-crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-009.tentative.html [ Failure ]
-crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-012.tentative.html [ Failure ]
+crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-007.html [ Failure ]
+crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-008.html [ Failure ]
+crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-009.html [ Failure ]
+crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-012.html [ Failure ]
 crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-013.html [ Failure ]
 crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-014.html [ Failure ]
+crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-015.tentative.html [ Failure ]
 crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-016.html [ Failure ]
 crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-017.html [ Failure ]
 crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-018.html [ Failure ]
-crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-019.tentative.html [ Failure ]
-crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-020.tentative.html [ Failure ]
-crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-021.tentative.html [ Failure ]
+crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-019.html [ Failure ]
+crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-020.html [ Failure ]
+crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-021.html [ Failure ]
 crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-024.html [ Failure ]
 crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-025.html [ Failure ]
+crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-027.html [ Failure ]
+crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-028.html [ Failure ]
 crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-003.html [ Failure ]
 crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-036.html [ Failure ]
 crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-051.html [ Failure ]
@@ -8735,21 +8726,18 @@
 crbug.com/40336192 external/wpt/css/css-text/white-space/text-wrap-balance-line-clamp-003.html [ Failure ]
 crbug.com/40336192 external/wpt/css/css-text/white-space/text-wrap-balance-line-clamp-004.html [ Failure ]
 # css-line-clamp-line-breaking-ellipsis
-crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/line-clamp-auto-009.tentative.html [ Pass ]
-crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/line-clamp-with-abspos-017.tentative.html [ Pass ]
+crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/line-clamp-auto-009.html [ Pass ]
+crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/line-clamp-with-abspos-017.html [ Pass ]
 crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-001.html [ Pass ]
-crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-007.tentative.html [ Pass ]
-crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-008.tentative.html [ Pass ]
-crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-009.tentative.html [ Pass ]
-crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-012.tentative.html [ Pass ]
-crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-013.html [ Failure ]
-crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-014.html [ Failure ]
+crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-007.html [ Pass ]
+crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-008.html [ Pass ]
+crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-009.html [ Pass ]
+crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-012.html [ Pass ]
 crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-016.html [ Pass ]
-crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-017.html [ Failure ]
 crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-018.html [ Pass ]
-crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-019.tentative.html [ Pass ]
-crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-020.tentative.html [ Pass ]
-crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-021.tentative.html [ Pass ]
+crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-019.html [ Pass ]
+crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-020.html [ Pass ]
+crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-021.html [ Pass ]
 crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-025.html [ Pass ]
 crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-003.html [ Pass ]
 crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-036.html [ Pass ]
@@ -8775,9 +8763,6 @@
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-040.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-047.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-050.html [ Failure ]
-crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-051.html [ Failure ]
-crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-052.html [ Failure ]
-crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-053.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-with-max-height.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-001.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-004.html [ Failure ]
@@ -8813,7 +8798,6 @@
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-auto-003.tentative.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-auto-004.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-auto-005.html [ Failure ]
-crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-auto-009.tentative.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-auto-011.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-auto-013.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-auto-014.html [ Failure ]
@@ -8862,7 +8846,6 @@
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-with-abspos-014.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-with-abspos-015.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-with-abspos-016.html [ Failure ]
-crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-with-abspos-017.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-with-abspos-018.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-with-abspos-019.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-with-abspos-020.html [ Failure ]
@@ -8896,23 +8879,17 @@
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-with-floats-008.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-with-floats-009.tentative.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-with-floats-010.tentative.html [ Failure ]
-crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-001.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-002.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-003.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-004.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-005.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-006.html [ Failure ]
-crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-007.tentative.html [ Failure ]
-crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-008.tentative.html [ Failure ]
-crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-009.tentative.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-010.html [ Failure ]
-crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-015.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-022.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-023.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-026.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-001.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-002.html [ Failure ]
-crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-003.html [ Failure ]
 crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-004.html [ Failure ]
 
 # backdrop-filter-mirror-edge
diff --git a/third_party/blink/web_tests/editing/caret/caret-shape-block-text-svg-circle.html b/third_party/blink/web_tests/editing/caret/caret-shape-block-text-svg-circle.html
new file mode 100644
index 0000000..567e76d
--- /dev/null
+++ b/third_party/blink/web_tests/editing/caret/caret-shape-block-text-svg-circle.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>caret-shape block width falls back to 1ch when impractical to determine</title>
+  <link rel="help" href="https://drafts.csswg.org/css-ui/#caret-shape">
+  <meta name="assert" content="Test checks that caret width for block shape should fall back to 1ch when it's impractical to determine.">
+  <script src="../../resources/testharness.js"></script>
+  <script src="../../resources/testharnessreport.js"></script>
+  <script src="../../resources/ahem.js"></script>
+<style>
+    div {
+    font-family: Ahem;
+    font-size: 10px;
+    caret-shape: block;
+    }
+  </style>
+</head>
+
+<body>
+<div id="test" contenteditable >XX<svg height="50" width="50">
+<circle r="25" cx="25" cy="25" fill="green" /></svg></div>
+</body>
+<script>
+   test(function () {
+    const test = document.getElementById("test").firstChild;
+    if (window.internals) {
+      getSelection().collapse(test, 2);
+      const caret_width = internals.absoluteCaretBounds().width;
+      assert_approx_equals(10, caret_width, 1);
+     }
+    }, "The width of the block caret should be 1ch if this information is impractical to determine.");
+</script>
+
diff --git a/third_party/blink/web_tests/editing/caret/caret-shape-block-width-for-space-style.html b/third_party/blink/web_tests/editing/caret/caret-shape-block-width-for-space-style.html
index 7a07f223..a9751c0 100644
--- a/third_party/blink/web_tests/editing/caret/caret-shape-block-width-for-space-style.html
+++ b/third_party/blink/web_tests/editing/caret/caret-shape-block-width-for-space-style.html
@@ -37,6 +37,8 @@
     <div id="hair" contenteditable>X&#x200a;X</div>
     <!-- zero width space -->
     <div id="zero" contenteditable>X&ZeroWidthSpace;X</div>
+    <!-- zero width space in the following segment-->
+    <div id="zeroNext" contenteditable>X<span>&ZeroWidthSpace;X></span></div>
   </div>
 </body>
 <script>
@@ -82,10 +84,15 @@
     const hairWidth = getCaretRectWidth(hair);
     assert_approx_equals(emWidth / 6, hairWidth, 1);
 
-    // zero - fall back to bar width.
+    // zero - fall back to 1ch width.
     const zero = document.getElementById("zero").firstChild;
     const zeroWidth = getCaretRectWidth(zero);
-    assert_equals(1, zeroWidth);
+    assert_equals(10, zeroWidth);
+    
+    // The following segment starts with a zero space character.
+    const zeroNext = document.getElementById("zeroNext").firstChild;
+    const nextWidth = getCaretRectWidth(zeroNext);
+    assert_equals(10, zeroWidth);
   }, "caret-shape block width respects the space stylings");
 </script>
 </body>
diff --git a/third_party/blink/web_tests/editing/caret/caret-shape-underscore-text-svg-circle.html b/third_party/blink/web_tests/editing/caret/caret-shape-underscore-text-svg-circle.html
new file mode 100644
index 0000000..55290b9
--- /dev/null
+++ b/third_party/blink/web_tests/editing/caret/caret-shape-underscore-text-svg-circle.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>caret-shape underscore width falls back to 1ch when impractical to determine</title>
+  <link rel="help" href="https://drafts.csswg.org/css-ui/#caret-shape">
+  <meta name="assert" content="Test checks that caret width for underscore shape should fall back to 1ch when it's impractical to determine.">
+  <script src="../../resources/testharness.js"></script>
+  <script src="../../resources/testharnessreport.js"></script>
+  <script src="../../resources/ahem.js"></script>
+<style>
+    div {
+    font-family: Ahem;
+    font-size: 10px;
+    caret-shape: underscore;
+    }
+  </style>
+</head>
+
+<body>
+<div id="test" contenteditable >XX<svg height="50" width="50">
+<circle r="25" cx="25" cy="25" fill="green" /></svg></div>
+</body>
+<script>
+   test(function () {
+    const test = document.getElementById("test").firstChild;
+    if (window.internals) {
+      getSelection().collapse(test, 2);
+      const caret_width = internals.absoluteCaretBounds().width;
+      assert_approx_equals(10, caret_width, 1);
+     }
+    }, "The width of the underscore caret should be 1ch if this information is impractical to determine.");
+</script>
+
diff --git a/third_party/blink/web_tests/editing/caret/caret-shape-underscore-width-for-space-style.html b/third_party/blink/web_tests/editing/caret/caret-shape-underscore-width-for-space-style.html
index d400da5f..b8d8bb6 100644
--- a/third_party/blink/web_tests/editing/caret/caret-shape-underscore-width-for-space-style.html
+++ b/third_party/blink/web_tests/editing/caret/caret-shape-underscore-width-for-space-style.html
@@ -37,6 +37,9 @@
     <div id="hair" contenteditable>X&#x200a;X</div>
     <!-- zero width space -->
     <div id="zero" contenteditable>X&ZeroWidthSpace;X</div>
+    <!-- zero width space in the following segment-->
+    <div id="zeroNext" contenteditable>X<span>&ZeroWidthSpace;X></span></div>
+  </div>
   </div>
 </body>
 <script>
@@ -85,7 +88,12 @@
     // zero - fall back to bar width.
     const zero = document.getElementById("zero").firstChild;
     var zeroWidth = getCaretRectWidth(zero);
-    assert_equals(1, zeroWidth);
+    assert_equals(10, zeroWidth);
+
+    // The following segment starts with a zero space character.
+    const zeroNext = document.getElementById("zeroNext").firstChild;
+    const nextWidth = getCaretRectWidth(zeroNext);
+    assert_equals(10, zeroWidth);
   }, "caret-shape underscore width respects the space stylings");
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/anchored-fallback-implicit-any.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/anchored-fallback-implicit-any.html
new file mode 100644
index 0000000..e92e7ed
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/anchored-fallback-implicit-any.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<title>CSS Conditional Test: @container anchored(fallback) matching implicit 'any' &lt;position-area&gt;</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-2/#anchored-container-queries">
+<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>
+  #anchor {
+    anchor-name: --a;
+    width: 100px;
+    height: 100px;
+  }
+  #anchored {
+    position: absolute;
+    position-anchor: --a;
+    position-area: top;
+    position-try-fallbacks: right span-bottom;
+    width: 100px;
+    /* Too tall to fit over the anchor to trigger fallback */
+    height: 100px;
+    container-type: anchored;
+  }
+  #target {
+    /* Implicit 'any' for the horizontal part of the query */
+    @container anchored(fallback: span-bottom) { --span-bottom: yes; }
+    /* Implicit 'any' for the vertical part of the query */
+    @container anchored(fallback: right) { --right: yes; }
+  }
+</style>
+<div id="anchor"></div>
+<div id="anchored">
+  <div id="target"></div>
+</div>
+<script>
+  test(() => {
+    const style = getComputedStyle(target);
+    assert_equals(style.getPropertyValue("--span-bottom"), "yes");
+    assert_equals(style.getPropertyValue("--right"), "yes");
+  }, "@container anchored(fallback: <position-area>) querying single axis with implicit 'any'");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/anchored-fallback-position-area-any.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/anchored-fallback-position-area-any.html
similarity index 95%
rename from third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/anchored-fallback-position-area-any.tentative.html
rename to third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/anchored-fallback-position-area-any.html
index 51ed84fd3..321d2f1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/anchored-fallback-position-area-any.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/anchored-fallback-position-area-any.html
@@ -1,7 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Conditional Test: @container anchored(fallback) matching 'any' &lt;position-area&gt;</title>
 <link rel="help" href="https://drafts.csswg.org/css-anchor-position-2/#anchored-container-queries">
-<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/12610">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/at-container-anchored-parsing-any.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/at-container-anchored-parsing-any.html
similarity index 92%
rename from third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/at-container-anchored-parsing-any.tentative.html
rename to third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/at-container-anchored-parsing-any.html
index b4582e1..ef85057 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/at-container-anchored-parsing-any.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/container-queries/at-container-anchored-parsing-any.html
@@ -1,7 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Conditional Test: @container anchored query parsing - 'any' position-area</title>
 <link rel="help" href="https://drafts.csswg.org/css-anchor-position-2/#anchored-container-queries">
-<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/12610">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/deep-pseudo-element-remove-update-ref.html b/third_party/blink/web_tests/external/wpt/css/css-lists/deep-pseudo-element-remove-update-ref.html
new file mode 100644
index 0000000..595b27a6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/deep-pseudo-element-remove-update-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<div>1A</div>
+<div>2B</div>
+<div>3C</div>
+<div>4E</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/deep-pseudo-element-remove-update.html b/third_party/blink/web_tests/external/wpt/css/css-lists/deep-pseudo-element-remove-update.html
new file mode 100644
index 0000000..709dae62
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/deep-pseudo-element-remove-update.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>CSS Lists Test: counters updated when parent element of element with counter operation on its pseudo element is removed</title>
+<link rel="help" href="https://drafts.csswg.org/css-lists/#inheriting-counters">
+<link rel="match" href="deep-pseudo-element-remove-update-ref.html">
+<style>
+  .container {
+    counter-reset: plm-table 0;
+  }
+
+  .numbered span::after {
+    content: counter(plm-table);
+    counter-increment: plm-table;
+  }
+</style>
+<div class="container">
+  <div class="numbered a"><span></span>A</div>
+  <div class="numbered b"><span></span>B</div>
+  <div id="target" class="numbered d"><span></span>D</div>
+  <div class="numbered c"><span></span>C</div>
+  <div class="numbered e"><span></span>E</div>
+</div>
+<script>
+  document.documentElement.offsetTop;
+  target.remove();
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/caret-eol-001.html b/third_party/blink/web_tests/external/wpt/css/css-ui/caret-eol-001.html
index 3eb1d8c..46c1510 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/caret-eol-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/caret-eol-001.html
@@ -4,7 +4,7 @@
 <title>CSS Basic User Interface Test: end-of-line carets should be visible</title>
 <link rel="author" title="Florian Rivoal" href="mailto:florian@rivoal.net">
 <link rel="help" href="http://www.w3.org/TR/css4-ui/#caret-shape">
-<link rel=match href=reference/caret-eol-001.html>
+<link rel="mismatch" href=reference/caret-eol-001.html>
 <meta name="assert" content="Checks that carets positioned at the end of the line are shown even if they overflow.">
 <style>
 div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/caret-eol-002.html b/third_party/blink/web_tests/external/wpt/css/css-ui/caret-eol-002.html
index 2506c6d..e7ba4e9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/caret-eol-002.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/caret-eol-002.html
@@ -4,7 +4,7 @@
 <title>CSS Basic User Interface Test: end-of-line carets should be visible</title>
 <link rel="author" title="Florian Rivoal" href="mailto:florian@rivoal.net">
 <link rel="help" href="http://www.w3.org/TR/css4-ui/#caret-shape">
-<link rel=match href=reference/caret-eol-001.html>
+<link rel="mismatch" href=reference/caret-eol-001.html>
 <meta name="assert" content="Checks that carets positioned at the end of the line are shown even if they overflow, even if the box has hidden overflow.">
 <style>
 div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/caret-eol-003.html b/third_party/blink/web_tests/external/wpt/css/css-ui/caret-eol-003.html
index 61e9c50..7b562e8 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/caret-eol-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/caret-eol-003.html
@@ -1,10 +1,10 @@
-<!DOCTYPE html>
+<!DOCTYPE html>
 <html lang=en>
 <meta charset="utf-8">
 <title>CSS Basic User Interface Test: end-of-line carets should be visible</title>
 <link rel="author" title="Florian Rivoal" href="mailto:florian@rivoal.net">
 <link rel="help" href="http://www.w3.org/TR/css4-ui/#caret-shape">
-<link rel=match href=reference/caret-eol-001.html>
+<link rel="mismatch" href=reference/caret-eol-001.html>
 <meta name="assert" content="Checks that carets positioned at the end of the line are shown even if they overflow, even if the box has overflow: clip.">
 <style>
 div {
@@ -28,4 +28,4 @@
 const t = document.querySelector("#test");
 window.getSelection().selectAllChildren(t);
 window.getSelection().collapseToEnd();
-</script>
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/reference/caret-eol-001.html b/third_party/blink/web_tests/external/wpt/css/css-ui/reference/caret-eol-001.html
index dc8f37f..c3db995 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/reference/caret-eol-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/reference/caret-eol-001.html
@@ -7,6 +7,7 @@
 div {
     font-size: 40px;
     font-family: monospace;
+    width: 5ch;
     caret-color: orange;
     caret-shape: block;
     caret-animation: manual;
@@ -17,7 +18,7 @@
 
 <p>Test passes if you see a orange text insertion caret at the end of the line below.
 
-<div id=test contenteditable spellcheck="false">abcde</div>
+<div id=test spellcheck="false">abcde</div>
 
 <script>
 const t = document.querySelector("#test");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/typed-arithmetic-different-categories-crash.html b/third_party/blink/web_tests/external/wpt/css/css-values/typed-arithmetic-different-categories-crash.html
new file mode 100644
index 0000000..e3dd6bb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/typed-arithmetic-different-categories-crash.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<title>CSS Values Test: typed arithmetic with different categories crash</title>
+<link rel="help" href="https://issues.chromium.org/issues/445824614">
+<style>
+  body {
+    stroke-dasharray: calc(0.5px * 2 / -4500000000kHz);
+  }
+</style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/typed_arithmetic-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-values/typed_arithmetic-expected.txt
new file mode 100644
index 0000000..8be5b859
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/typed_arithmetic-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+[FAIL] calc(10% / 1px) should be used-value-equivalent to 1
+  assert_equals: calc(10% / 1px) and 1 serialize to the same thing in used values. expected "10px" but got "100px"
+[FAIL] calc(10% * 10% / 1px * 10deg / 1deg / 10px) should be used-value-equivalent to 1
+  assert_equals: calc(10% * 10% / 1px * 10deg / 1deg / 10px) and 1 serialize to the same thing in used values. expected "10px" but got "1000px"
+[FAIL] calc(10% * 10% / 1px * 1deg / 1deg) should be used-value-equivalent to 1px
+  assert_equals: calc(10% * 10% / 1px * 1deg / 1deg) and 1px serialize to the same thing in used values. expected "1px" but got "100px"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/typed_arithmetic.html b/third_party/blink/web_tests/external/wpt/css/css-values/typed_arithmetic.html
index f4258c33..37f560d2 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-values/typed_arithmetic.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/typed_arithmetic.html
@@ -5,6 +5,7 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/numeric-testcommon.js"></script>
 <script src="/css/support/computed-testcommon.js"></script>
+<script src="../support/parsing-testcommon.js"></script>
 <style>
 :root {
   font-size: 10px;
@@ -41,6 +42,26 @@
 test_math_used("calc(10em / 1px)", "100", {"prop": "z-index"});
 test_math_used("calc(1px / 10em * NaN)", "0", {"prop": "z-index"});
 
+// 10% -> 1px; 1px / 1px -> 1.
+test_math_used("calc(10% / 1px)", "1", {"prop": "line-height"});
+// 1% * 100% / 10% -> 10%.
+test_math_used("calc(1% * 100% / 10%)", "10%", {"prop": "line-height"});
+// 10% / 10% -> 1.
+test_math_used("calc(10% / 10%)", "1", {"prop": "line-height"});
+// 10% -> 1px; 1% -> 0.1px; 1px / 0.1px / 1px -> 10px.
+test_math_used("calc((10% * 1%) / 1px)", "10px");
+// 10% -> 1px; 1px * 1px / 1px * 10deg / 1deg / 10px -> 1.
+test_math_used("calc(10% * 10% / 1px * 10deg / 1deg / 10px)", "1", {"prop": "line-height"});
+// 10% -> 1px; 1px * 1px / 1px * 1deg / 1deg -> 1px.
+test_math_used("calc(10% * 10% / 1px * 1deg / 1deg)", "1px", {"prop": "line-height"});
+// 1px * 2deg / 1deg -> 2px.
+test_math_used("calc(1px * 2deg / 1deg)", "2px", {"prop": "line-height"});
+// 1px * 3deg / 1deg / 1px -> 3.
+test_math_used("calc(1px * 3deg / 1deg / 1px)", "3", {"prop": "line-height"});
+
+test_invalid_value("width", "calc((1% * 1deg) / 1px)");
+test_invalid_value("width", "calc((1% * 1% * 1%) / 1px)");
+
 testComputedValueGreaterOrLowerThan("width", "calc(1px * 10em / 0em)", REALLY_LARGE);
 testComputedValueGreaterOrLowerThan("width", "calc(1px / 1px * 10em * infinity)", REALLY_LARGE);
 testComputedValueGreaterOrLowerThan("margin-left", "calc(1px * 10em / -0em)", REALLY_LARGE_NEGATIVE);
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCRtpSender-getParameters.html b/third_party/blink/web_tests/fast/peerconnection/RTCRtpSender-getParameters.html
index 112f3db..168df5e 100644
--- a/third_party/blink/web_tests/fast/peerconnection/RTCRtpSender-getParameters.html
+++ b/third_party/blink/web_tests/fast/peerconnection/RTCRtpSender-getParameters.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<meta name="timeout" content="long">
 <html>
 <head>
 <title>RTCRtpSender.getParameters</title>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-ui/caret-eol-part-clip.html b/third_party/blink/web_tests/wpt_internal/css/css-ui/caret-eol-part-clip.html
new file mode 100644
index 0000000..662bee1
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-ui/caret-eol-part-clip.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Basic User Interface Test: end-of-line carets should be visible</title>
+<link rel="help" href="http://www.w3.org/TR/css4-ui/#caret-shape">
+<link rel="match" href="caret-eol-part-ref.html">
+<link rel="match" href="caret-eol-part-mac-ref.html">
+<meta name="assert"
+    content="Checks that carets positioned at the end of the line are partially shown to fill the rest of the available space when the box has clip overflow with caret-shape: block.">
+<style>
+    div {
+        font-size: 40px;
+        font-family: monospace;
+        width: 5.5ch;
+        caret-color: orange;
+        caret-shape: block;
+        caret-animation: manual;
+        outline: none;
+        overflow: clip;
+        white-space: pre;
+    }
+</style>
+
+<p>Test passes if you see a orange text insertion caret at the end of the line below.
+
+<div id=test contenteditable spellcheck="false">abcde</div>
+
+<script>
+    const t = document.querySelector("#test");
+    window.getSelection().selectAllChildren(t);
+    window.getSelection().collapseToEnd();
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-ui/caret-eol-part-hidden.html b/third_party/blink/web_tests/wpt_internal/css/css-ui/caret-eol-part-hidden.html
new file mode 100644
index 0000000..7bd76f20
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-ui/caret-eol-part-hidden.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Basic User Interface Test: end-of-line carets should be visible</title>
+<link rel="help" href="http://www.w3.org/TR/css4-ui/#caret-shape">
+<link rel="match" href="caret-eol-part-ref.html">
+<link rel="match" href="caret-eol-part-mac-ref.html">
+<meta name="assert"
+    content="Checks that carets positioned at the end of the line are partially shown to fill the rest of the available space when the box has hidden overflow with caret-shape: block.">
+<style>
+    div {
+        font-size: 40px;
+        font-family: monospace;
+        width: 5.5ch;
+        caret-color: orange;
+        caret-shape: block;
+        caret-animation: manual;
+        outline: none;
+        overflow: hidden;
+        white-space: pre;
+    }
+</style>
+
+<p>Test passes if you see a orange text insertion caret at the end of the line below.
+
+<div id=test contenteditable spellcheck="false">abcde</div>
+
+<script>
+    const t = document.querySelector("#test");
+    window.getSelection().selectAllChildren(t);
+    window.getSelection().collapseToEnd();
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-ui/caret-eol-part-mac-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-ui/caret-eol-part-mac-ref.html
new file mode 100644
index 0000000..e476cbe
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-ui/caret-eol-part-mac-ref.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Basic User Interface Test Reference</title>
+<style>
+div {
+    font-size: 40px;
+    font-family: monospace;
+    caret-color: orange;
+    caret-animation: manual;
+    caret-shape: block;
+    width: 5ch;
+    outline: none;
+    white-space: pre;
+    display: inline-block;
+}
+
+#div2 {
+   width: 1ch;
+   position: relative;
+   left: -6px;
+   transform: scaleX(0.5);
+}
+
+</style>
+
+<p>Test passes if you see a orange text insertion caret at the end of the line below.</p>
+
+<div id=test contenteditable spellcheck="false">abcde</div><div id=div2 contenteditable></div>
+
+<script>
+  window.onload = function () {
+    document.getElementById("div2").focus();
+  }
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-ui/caret-eol-part-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-ui/caret-eol-part-ref.html
new file mode 100644
index 0000000..dc45537
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-ui/caret-eol-part-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Basic User Interface Test Reference</title>
+<style>
+div {
+    font-size: 40px;
+    font-family: monospace;
+    caret-color: orange;
+    caret-animation: manual;
+    caret-shape: block;
+    width: 5ch;
+    outline: none;
+    white-space: pre;
+    display: inline-block;
+}
+
+#div2 {
+   position: relative;
+   left: -1.25ch;
+   transform: scaleX(0.5);
+}
+
+</style>
+
+<p>Test passes if you see a orange text insertion caret at the end of the line below.</p>
+
+<div id=test contenteditable spellcheck="false">abcde</div><div id=div2 contenteditable></div>
+
+<script>
+  window.onload = function () {
+    document.getElementById("div2").focus();
+  }
+</script>
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 2d6e3ee..0ef5b063 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 2d6e3ee0f3197f192fe4d759e8eb6934dea46069
+Subproject commit 0ef5b0631522c714639536c36dabf98a1802bf1f
diff --git a/third_party/omnibox_proto/README.chromium b/third_party/omnibox_proto/README.chromium
index 93e21f475..21707de 100644
--- a/third_party/omnibox_proto/README.chromium
+++ b/third_party/omnibox_proto/README.chromium
@@ -1,9 +1,8 @@
 Name: Omnibox Protos
 Short Name: omnibox_proto
-URL: Google Internal
-Version: 810525975
-Date: 2025-09-23
-Update Mechanism: Manual (https://crbug.com/422306307)
+URL: This is the canonical public repository
+Version: 811469245
+Date: 2025-09-25
 License: BSD-3-Clause
 License File: LICENSE
 Shipped: yes
diff --git a/third_party/omnibox_proto/aim_eligibility_response.proto b/third_party/omnibox_proto/aim_eligibility_response.proto
index 5e4318a6..55ad6b2 100644
--- a/third_party/omnibox_proto/aim_eligibility_response.proto
+++ b/third_party/omnibox_proto/aim_eligibility_response.proto
@@ -11,8 +11,10 @@
 package omnibox;
 
 // AIM Eligibility Response.
-// Next ID: 3
+// Next ID: 5
 message AimEligibilityResponse {
   optional bool is_eligible = 1;
   optional bool is_pdf_upload_eligible = 2;
+  optional bool is_deep_search_eligible = 3;
+  optional bool is_canvas_eligible = 4;
 }
diff --git a/third_party/perfetto b/third_party/perfetto
index 95317c9..c65beb1 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 95317c91aadc41a24abc2a671b211baec3d6d749
+Subproject commit c65beb1b174079f116a0d31729c43e6cbfacfc85
diff --git a/third_party/skia b/third_party/skia
index b54e106..2b871d6 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit b54e1064ccae404b7a7bff410585fef1c5381241
+Subproject commit 2b871d6b99caae5e3990351b91606e9a957f196f
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index 2458bba..5bc0a41 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit 2458bba6f0d087f6ad588af68bcb4075c1d0701b
+Subproject commit 5bc0a41ae522a65e91c77b043bce45637a1a2fde
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src
index 102c306..c352f86 160000
--- a/third_party/vulkan-validation-layers/src
+++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@
-Subproject commit 102c30690b1e84e58ad659a249dbb819dcc7f926
+Subproject commit c352f86877474dbc7294125d31523e8c74918c76
diff --git a/third_party/webrtc b/third_party/webrtc
index 9607577..ba391e6 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 96075773302f4a4b7265052c37a793486355aef9
+Subproject commit ba391e6893da350a48a7895a0bff8f033369a729
diff --git a/tools/clang/spanify/Spanifier.cpp b/tools/clang/spanify/Spanifier.cpp
index 30809f9c..d471384 100644
--- a/tools/clang/spanify/Spanifier.cpp
+++ b/tools/clang/spanify/Spanifier.cpp
@@ -674,8 +674,8 @@
                             const clang::ASTContext& ast_context) {
   clang::PrintingPolicy printing_policy(ast_context.getLangOpts());
   printing_policy.SuppressScope = 0;
+  printing_policy.SuppressTagKeyword = 0;
   printing_policy.SuppressUnwrittenScope = 1;
-  printing_policy.SuppressElaboration = 0;
   printing_policy.SuppressInlineNamespace = 1;
   printing_policy.SuppressDefaultTemplateArgs = 1;
   printing_policy.PrintAsCanonical = 0;
@@ -751,36 +751,23 @@
   assert(false && "Unexpected match in getSourceRange()");
 }
 
-// Unwraps typedef type locs and/or elaborated type locs, and returns the body
-// type loc.
+// Unwraps typedef type locs and returns the body type loc.
 //
 // Note that using-declared types are also represented with typedef types in
 // clang, so this function works for both 'typedef' and 'using' declarations.
 //
 // Example TypeLoc structures:
 //     // Given T2 where typedef int T1; using T2 = T1;
-//     ElaboratedTypeLoc('T2')
-//       --(getNamedTypeLoc)--> TypedefTypeLoc('T2')
-//         --(getTypedefNameDecl)--> ElaboratedTypeLoc('T1')
-//           --(getNamedTypeLoc)--> TypedefTypeLoc('T1')
-//             --(getTypedefNameDecl)--> BuiltinTypeLoc('int')
+//     TypedefTypeLoc('T2')
+//       --(getDecl)--> TypedefTypeLoc('T1')
+//         --(getDecl)--> BuiltinTypeLoc('int')
 //     => returns BuiltinTypeLoc('int').
-//
-//     // Given base::raw_ptr<int>,
-//     ElaboratedTypeLoc('base::raw_ptr<int>')
-//       --(getNamedTypeLoc)--> TemplateSpecializationTypeLoc('raw_ptr<int>')
-//         --(getArgLoc)--> TemplateArgumentLoc('int')
-//     => returns TemplateSpecializationTypeLoc('raw_ptr<int>').
 clang::TypeLoc UnwrapTypedefTypeLoc(clang::TypeLoc type_loc) {
-  while (const clang::ElaboratedTypeLoc elaborated_type_loc =
-             type_loc.getAs<clang::ElaboratedTypeLoc>()) {
-    type_loc = elaborated_type_loc.getNamedTypeLoc();
-    if (const clang::TypedefTypeLoc typedef_type_loc =
-            type_loc.getAs<clang::TypedefTypeLoc>()) {
-      const clang::TypedefNameDecl* typedef_name_decl =
-          typedef_type_loc.getTypedefNameDecl();
-      type_loc = typedef_name_decl->getTypeSourceInfo()->getTypeLoc();
-    }
+  while (const clang::TypedefTypeLoc typedef_type_loc =
+             type_loc.getAs<clang::TypedefTypeLoc>()) {
+    const clang::TypedefNameDecl* typedef_name_decl =
+        typedef_type_loc.getDecl();
+    type_loc = typedef_name_decl->getTypeSourceInfo()->getTypeLoc();
   }
   return type_loc;
 }
@@ -2517,23 +2504,15 @@
   if (!unnamed_class.empty()) {
     element_type_as_string = unnamed_class;
   } else if (original_element_type->isElaboratedTypeSpecifier()) {
-    // If the `original_element_type` is an elaborated type with a keyword, i.e.
-    // `struct`, `class`, `union`, we will create another ElaboratedType
-    // without the keyword. So `struct funcHasName` will be `funcHasHame`.
-    auto* original_type = new_element_type->getAs<clang::ElaboratedType>();
-
-    // Create a new ElaboratedType without 'struct', 'class', 'union'
-    // keywords.
-    auto new_element_type = ast_context.getElaboratedType(
-        // Use `None` to suppress tag names.
-        clang::ElaboratedTypeKeyword::None,
-        // Keep the same as the original.
-        original_type->getQualifier(),
-        // Keep the same as the original.
-        original_type->getNamedType(),
-        // Remove `OwnedTagDecl`. We don't need IncludeTagDefinition.
-        nullptr);
-    element_type_as_string = GetTypeAsString(new_element_type, ast_context);
+    // `GetTypeAsString` doesn't remove a tag keyword (struct, class, enum, or
+    // union), but we'd like to remove the tag keyword here.
+    clang::PrintingPolicy printing_policy(ast_context.getLangOpts());
+    printing_policy.SuppressTagKeyword = 1;
+    printing_policy.SuppressUnwrittenScope = 1;
+    printing_policy.SuppressInlineNamespace = 1;
+    printing_policy.SuppressDefaultTemplateArgs = 1;
+    printing_policy.PrintAsCanonical = 1;
+    element_type_as_string = new_element_type.getAsString(printing_policy);
   } else {
     element_type_as_string = RewriteCArrayToStdArray(
         new_element_type, array_type_loc.getElementLoc(), source_manager,
diff --git a/tools/clang/spanify/tests/func-ptr-var-expected.cc b/tools/clang/spanify/tests/func-ptr-var-expected.cc
index 53218a2..6b9b571d 100644
--- a/tools/clang/spanify/tests/func-ptr-var-expected.cc
+++ b/tools/clang/spanify/tests/func-ptr-var-expected.cc
@@ -42,10 +42,10 @@
 // Expected rewrite:
 // base::span<int> FuncAll(base::span<int, 3> arg1,
 //                         base::span<int> arg2,
-//                         base::base::raw_span<int> arg3) {
+//                         base::raw_span<int> arg3) {
 base::span<int> FuncAll(base::span<int, 3> arg1,
                         base::span<int> arg2,
-                        base::base::raw_span<int> arg3) {
+                        base::raw_span<int> arg3) {
   arg1[UnsafeIndex()] = 3;
   arg2[UnsafeIndex()] = 3;
   arg3[UnsafeIndex()] = 3;
@@ -185,10 +185,10 @@
     // Expected rewrite:
     // typedef base::span<int> (*AllType1)(base::span<int, 3> arg1,
     //                                     base::span<int> arg2,
-    //                                     base::base::raw_span<int> arg3);
+    //                                     base::raw_span<int> arg3);
     typedef base::span<int> (*AllType1)(base::span<int, 3> arg1,
                                         base::span<int> arg2,
-                                        base::base::raw_span<int> arg3);
+                                        base::raw_span<int> arg3);
     using AllType2 = AllType1;
     AllType2 p_all = FuncAll;
     int* ret_all = p_all(arr, arr, arr);
diff --git a/tools/clang/spanify/tests/func-ptr-var-original.cc b/tools/clang/spanify/tests/func-ptr-var-original.cc
index fcaefcf..718519c 100644
--- a/tools/clang/spanify/tests/func-ptr-var-original.cc
+++ b/tools/clang/spanify/tests/func-ptr-var-original.cc
@@ -40,7 +40,7 @@
 // Expected rewrite:
 // base::span<int> FuncAll(base::span<int, 3> arg1,
 //                         base::span<int> arg2,
-//                         base::base::raw_span<int> arg3) {
+//                         base::raw_span<int> arg3) {
 int* FuncAll(int arg1[3], int* arg2, base::raw_ptr<int> arg3) {
   arg1[UnsafeIndex()] = 3;
   arg2[UnsafeIndex()] = 3;
@@ -181,7 +181,7 @@
     // Expected rewrite:
     // typedef base::span<int> (*AllType1)(base::span<int, 3> arg1,
     //                                     base::span<int> arg2,
-    //                                     base::base::raw_span<int> arg3);
+    //                                     base::raw_span<int> arg3);
     typedef int* (*AllType1)(int arg1[3], int* arg2, base::raw_ptr<int> arg3);
     using AllType2 = AllType1;
     AllType2 p_all = FuncAll;
diff --git a/tools/clang/spanify/tests/var-construction-expected.cc b/tools/clang/spanify/tests/var-construction-expected.cc
index ba0f1a5..1f2f998 100644
--- a/tools/clang/spanify/tests/var-construction-expected.cc
+++ b/tools/clang/spanify/tests/var-construction-expected.cc
@@ -134,7 +134,7 @@
   // Expected rewrite:
   // base::raw_span<char> buf4 = buf3;
   // base::PostIncrementSpan(buf4);
-  base::base::raw_span<char> buf4 = buf3;
+  base::raw_span<char> buf4 = buf3;
   base::PostIncrementSpan(buf4);
 
   // Expected rewrite:
diff --git a/tools/licenses/licenses.py b/tools/licenses/licenses.py
index 61a91637..a8afd4c 100755
--- a/tools/licenses/licenses.py
+++ b/tools/licenses/licenses.py
@@ -77,7 +77,10 @@
     os.path.join('third_party', 'gperf'),
     os.path.join('third_party', 'lighttpd'),
     os.path.join('third_party', 'llvm'),
+    os.path.join('third_party', 'llvm-bootstrap'),
+    os.path.join('third_party', 'llvm-bootstrap-install'),
     os.path.join('third_party', 'llvm-build'),
+    os.path.join('third_party', 'llvm-build-tools'),
     os.path.join('third_party', 'mingw-w64'),
     os.path.join('third_party', 'nacl_sdk_binaries'),
     os.path.join('third_party', 'pefile'),
@@ -86,6 +89,8 @@
     os.path.join('third_party', 'pyelftools'),
     os.path.join('third_party', 'pylib'),
     os.path.join('third_party', 'pywebsocket'),
+    os.path.join('third_party', 'rust-src'),
+    os.path.join('third_party', 'rust-toolchain-intermediate'),
     os.path.join('third_party', 'syzygy'),
 
     # Stuff pulled in from chrome-internal for official builds/tools.
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 262acfd..5232ab8 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -37168,6 +37168,16 @@
   </description>
 </action>
 
+<action name="Settings.DeleteBrowsingData.GeminiAppsActivityLinkClick">
+  <owner>rainhard@chromium.com</owner>
+  <owner>zalmashni@chromium.org</owner>
+  <owner>chrome-browser-privacy-team@google.com</owner>
+  <description>
+    User clicks on the &quot;Gemini Apps Activity&quot; link in the &quot;Other
+    Google data&quot; dialog.
+  </description>
+</action>
+
 <action name="Settings.DeleteBrowsingData.GoogleSearchHistoryLinkClick">
   <owner>hkamila@google.com</owner>
   <owner>zalmashni@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml_files.gni b/tools/metrics/histograms/histograms_xml_files.gni
index 443a7c5..dd083489 100644
--- a/tools/metrics/histograms/histograms_xml_files.gni
+++ b/tools/metrics/histograms/histograms_xml_files.gni
@@ -135,6 +135,8 @@
   "//tools/metrics/histograms/metadata/invalidation/histograms.xml",
   "//tools/metrics/histograms/metadata/ios/enums.xml",
   "//tools/metrics/histograms/metadata/ios/histograms.xml",
+  "//tools/metrics/histograms/metadata/ip_protection/enums.xml",
+  "//tools/metrics/histograms/metadata/ip_protection/histograms.xml",
   "//tools/metrics/histograms/metadata/kerberos/histograms.xml",
   "//tools/metrics/histograms/metadata/kiosk/histograms.xml",
   "//tools/metrics/histograms/metadata/language/enums.xml",
diff --git a/tools/metrics/histograms/metadata/enterprise/enums.xml b/tools/metrics/histograms/metadata/enterprise/enums.xml
index ccdd848..795bb4d 100644
--- a/tools/metrics/histograms/metadata/enterprise/enums.xml
+++ b/tools/metrics/histograms/metadata/enterprise/enums.xml
@@ -60,6 +60,7 @@
   <int value="9" label="Load Key Failed"/>
   <int value="10" label="Invalid Final Identity Name"/>
   <int value="11" label="Identity Not Found"/>
+  <int value="12" label="Delete Identity Failed"/>
 </enum>
 
 <enum name="CertificateUploadClientError">
@@ -2290,6 +2291,7 @@
   <int value="1395"
       label="ExtensionForceInstallWithNonMalwareViolationsEnabled"/>
   <int value="1396" label="CacheEncryptionEnabled"/>
+  <int value="1397" label="SilentPrintingEnabled"/>
 </enum>
 
 <enum name="EnterprisePoliciesSources">
diff --git a/tools/metrics/histograms/metadata/ios/enums.xml b/tools/metrics/histograms/metadata/ios/enums.xml
index 24fdcfb..1f3d636 100644
--- a/tools/metrics/histograms/metadata/ios/enums.xml
+++ b/tools/metrics/histograms/metadata/ios/enums.xml
@@ -1684,6 +1684,19 @@
 
 <!-- LINT.ThenChange(/ios/chrome/browser/reader_mode/model/constants.h:ReaderModeAccessPoint) -->
 
+<!-- LINT.IfChange(ReaderModeAccessPointWithMode) -->
+
+<enum name="ReaderModeAccessPointWithMode">
+  <int value="0" label="Contextual Chip in Regular"/>
+  <int value="1" label="Contextual Chip in Incognito"/>
+  <int value="2" label="Tools Menu in Regular"/>
+  <int value="3" label="Tools Menu in Incognito"/>
+  <int value="4" label="AI Hub in Regular"/>
+  <int value="5" label="AI Hub in Incognito"/>
+</enum>
+
+<!-- LINT.ThenChange(/ios/chrome/browser/reader_mode/model/constants.h:ReaderModeAccessPointWithMode) -->
+
 <!-- LINT.IfChange(ReaderModeCustomizationType) -->
 
 <enum name="ReaderModeCustomizationType">
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index acb8376..ce54396 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -4984,6 +4984,19 @@
   </summary>
 </histogram>
 
+<histogram name="IOS.ReaderMode.AccessPointWithMode"
+    enum="ReaderModeAccessPointWithMode" expires_after="2026-03-22">
+  <owner>fernandex@google.com</owner>
+  <owner>qpubert@google.com</owner>
+  <owner>bling-squid-squad@google.com</owner>
+  <summary>
+    Logs the access point where the user enabled Reading Mode UI on iOS, this
+    does not include situations when the user changes web state (e.g. by
+    switching tabs or closing the browser). The results are further divided by
+    the application mode (Incognito or Regular).
+  </summary>
+</histogram>
+
 <histogram name="IOS.ReaderMode.Customization"
     enum="ReaderModeCustomizationType" expires_after="2026-02-22">
   <owner>fernandex@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/ip_protection/OWNERS b/tools/metrics/histograms/metadata/ip_protection/OWNERS
new file mode 100644
index 0000000..3d5fd33
--- /dev/null
+++ b/tools/metrics/histograms/metadata/ip_protection/OWNERS
@@ -0,0 +1,4 @@
+per-file OWNERS=file://tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
+
+ortuno@chromium.org
+awillia@chromium.org
diff --git a/tools/metrics/histograms/metadata/ip_protection/enums.xml b/tools/metrics/histograms/metadata/ip_protection/enums.xml
new file mode 100644
index 0000000..89027d8
--- /dev/null
+++ b/tools/metrics/histograms/metadata/ip_protection/enums.xml
@@ -0,0 +1,257 @@
+<!--
+Copyright 2025 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<!--
+This file describes the enumerations referenced by entries in histograms.xml for
+this directory. Some enums may instead be listed in the central enums.xml file
+at src/tools/metrics/histograms/enums.xml when multiple files use them.
+
+For best practices on writing enumerations descriptions, see
+https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#Enum-Histograms
+
+Please follow the instructions in the OWNERS file in this directory to find a
+reviewer. If no OWNERS file exists, please consider signing up at
+go/reviewing-metrics (Googlers only), as all subdirectories are expected to
+have an OWNERS file. As a last resort you can send the CL to
+chromium-metrics-reviews@google.com.
+-->
+
+<histogram-configuration>
+
+<!-- Enum types -->
+
+<enums>
+
+<!-- LINT.IfChange(IpProtectionGetAuthTokenResultForGeo) -->
+
+<enum name="IpProtectionGetAuthTokenResultForGeo">
+  <int value="0" label="Auth token unavailable and cache is empty"/>
+  <int value="1" label="Auth token unavailable but cache contains tokens"/>
+  <int value="2" label="Auth token available for current geo"/>
+  <int value="3" label="Auth token available for other geo"/>
+</enum>
+
+<!-- LINT.ThenChange(//components/ip_protection/common/ip_protection_telemetry.h:AuthTokenResultForGeo) -->
+
+<enum name="IpProtectionGetProxyListResult">
+  <int value="0" label="Failed to acquire a list"/>
+  <int value="1" label="Got a list without any valid entries"/>
+  <int value="2" label="Got a list with at least one valid entry"/>
+</enum>
+
+<enum name="IpProtectionJobResult">
+  <int value="0" label="IP Protection was not attempted"/>
+  <int value="1"
+      label="The request was IP Protected and carried via IP Protection
+             proxies or, if the direct-only parameter is true, made directly"/>
+  <int value="2" label="The request was IP Protected, but fell back to direct"/>
+</enum>
+
+<enum name="IpProtectionProxyChainId">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Chain 1"/>
+  <int value="2" label="Chain 2"/>
+  <int value="3" label="Chain 3"/>
+</enum>
+
+<enum name="IpProtectionProxyLayer">
+  <int value="0" label="Proxy A"/>
+  <int value="1" label="Proxy B"/>
+</enum>
+
+<enum name="IpProtectionProxyResolutionResult">
+  <int value="0"
+      label="The MDL is not populated, so an eligility decision could not be
+             made"/>
+  <int value="1" label="The request did not match the MDL"/>
+  <int value="2"
+      label="DEPRECATED: The EnableIpProtectionProxy feature is not enabled"/>
+  <int value="3" label="The IP Protection setting is disabled"/>
+  <int value="4" label="Proxy List is unavailable"/>
+  <int value="5"
+      label="Proxy List is available but tokens have never been available"/>
+  <int value="6"
+      label="Proxy List is available but tokens in the cache have been
+             exhausted"/>
+  <int value="7" label="The request was resolved to the IP Protection proxies"/>
+  <int value="8"
+      label="A site exception created by User Bypass disables protections"/>
+  <int value="9"
+      label="The request bypassed the IP Protection proxies through DevTools"/>
+</enum>
+
+<enum name="IpProtectionTokenBatchRequestError">
+<!-- The following errors are omitted because they have many possible suffixes:
+  "Failed to create RSA public key: <error>"
+  "Failed to decode extensions: <error>"
+  "Failed to validate extensions: <error>"
+  "Failed to parse expiration timestamp: <error>"
+  "Failed to parse geo hint: <error>"
+  "Failed to parse use case: <error>"
+  "Failed to create Privacy Pass client: <error>"
+  "Failed to create ExtendedTokenRequest: <error>"
+
+  Additionally, errors marked [DEPRECATED] are no longer emitted as of M141 (crrev.com/c/6865774).
+ -->
+
+  <int value="4948469" label="AuthAndSign failed: 404 (NOT_FOUND)"/>
+  <int value="149981085" label="GetInitialData failed: 401 (UNAUTHENTICATED)"/>
+  <int value="150736087" label="GetInitialData failed: 499 (CANCELLED)"/>
+  <int value="159430335"
+      label="[DEPRECATED] GetInitialDataRequest failed: 499 (CANCELLED)"/>
+  <int value="203200113" label="AuthAndSign failed: 409 (ABORTED)"/>
+  <int value="219311180" label="AuthAndSign failed: invalid response"/>
+  <int value="247849994" label="GetInitialData failed: 501 (UNIMPLEMENTED)"/>
+  <int value="774920404"
+      label="[DEPRECATED] GetInitialDataRequest failed: 5xx (INTERNAL)"/>
+  <int value="776650315"
+      label="GetInitialData failed: 4xx (FAILED_PRECONDITION)"/>
+  <int value="788650824"
+      label="[DEPRECATED] GetInitialDataRequest failed: 409 (ABORTED)"/>
+  <int value="882579398"
+      label="[DEPRECATED] AuthAndSign failed: 504 (DEADLINE_EXCEEDED)"/>
+  <int value="1009665478" label="GetInitialData failed: 409 (ABORTED)"/>
+  <int value="1324062375"
+      label="[DEPRECATED] AuthAndSign failed: 403 (PERMISSION_DENIED)"/>
+  <int value="1359797045"
+      label="[DEPRECATED] AuthAndSign failed: 416 (OUT_OF_RANGE)"/>
+  <int value="1392994811"
+      label="[DEPRECATED] GetInitialDataRequest failed: 4xx
+             (FAILED_PRECONDITION)"/>
+  <int value="1560305329"
+      label="[DEPRECATED] GetInitialDataRequest failed: invalid response"/>
+  <int value="1570178381"
+      label="[DEPRECATED] GetInitialDataRequest failed: 429
+             (RESOURCE_EXHAUSTED)"/>
+  <int value="1578860000" label="GetInitialData failed: 404 (NOT_FOUND)"/>
+  <int value="1632191523"
+      label="[DEPRECATED] GetInitialDataRequest failed: 1xx/3xx (UNKNOWN)"/>
+  <int value="1632242186"
+      label="[DEPRECATED] AuthAndSign failed: 429 (RESOURCE_EXHAUSTED)"/>
+  <int value="1637757426"
+      label="[DEPRECATED] GetInitialDataRequest failed: 401 (UNAUTHENTICATED)"/>
+  <int value="1684860881"
+      label="GetInitialData failed: 429 (RESOURCE_EXHAUSTED)"/>
+  <int value="1731128661" label="AuthAndSign failed: 401 (UNAUTHENTICATED)"/>
+  <int value="1741288262"
+      label="[DEPRECATED] AuthAndSign failed: 499 (CANCELLED)"/>
+  <int value="1782033339"
+      label="[DEPRECATED] AuthAndSign failed: invalid response"/>
+  <int value="1869144073"
+      label="GetInitialData failed: 504 (DEADLINE_EXCEEDED)"/>
+  <int value="1916829389"
+      label="[DEPRECATED] GetInitialDataRequest failed: 404 (NOT_FOUND)"/>
+  <int value="2040698924"
+      label="[DEPRECATED] AuthAndSign failed: 404 (NOT_FOUND)"/>
+  <int value="2073947853"
+      label="[DEPRECATED] AuthAndSign failed: 401 (UNAUTHENTICATED)"/>
+  <int value="2141269466" label="[DEPRECATED] Failed to validate extensions"/>
+  <int value="2143022301" label="AuthAndSign failed: 429 (RESOURCE_EXHAUSTED)"/>
+  <int value="2152182465"
+      label="GetInitialData failed: 403 (PERMISSION_DENIED)"/>
+  <int value="2218632443" label="GetInitialData failed: invalid response"/>
+  <int value="2339897637"
+      label="[DEPRECATED] GetInitialDataRequest failed: 503 (UNAVAILABLE)"/>
+  <int value="2353899066" label="AuthAndSign failed: 416 (OUT_OF_RANGE)"/>
+  <int value="2408268044" label="AuthAndSign failed: 400 (INVALID_ARGUMENT)"/>
+  <int value="2420519214" label="GetInitialData failed: 1xx/3xx (UNKNOWN)"/>
+  <int value="2528366448" label="Failed to parse GetInitialDataResponse"/>
+  <int value="2528411010"
+      label="[DEPRECATED] GetInitialDataRequest failed: 416 (OUT_OF_RANGE)"/>
+  <int value="2594877687"
+      label="[DEPRECATED] GetInitialDataRequest failed: 504
+             (DEADLINE_EXCEEDED)"/>
+  <int value="2596810654" label="Failed to marshal token"/>
+  <int value="2766613933"
+      label="[DEPRECATED] AuthAndSign failed: 503 (UNAVAILABLE)"/>
+  <int value="2848390062" label="Failed to parse AuthAndSignResponse"/>
+  <int value="2906092890"
+      label="[DEPRECATED] GetInitialDataRequest failed: 501 (UNIMPLEMENTED)"/>
+  <int value="2958424397" label="GetInitialData failed: 416 (OUT_OF_RANGE)"/>
+  <int value="3024523150"
+      label="[DEPRECATED] AuthAndSign failed: 5xx (INTERNAL)"/>
+  <int value="3118017018"
+      label="[DEPRECATED] AuthAndSign failed: 501 (UNIMPLEMENTED)"/>
+  <int value="3172166369"
+      label="[DEPRECATED] AuthAndSign failed: 1xx/3xx (UNKNOWN)"/>
+  <int value="3240481601" label="AuthAndSign failed: 504 (DEADLINE_EXCEEDED)"/>
+  <int value="3317564205"
+      label="AuthAndSign failed: 4xx (FAILED_PRECONDITION)"/>
+  <int value="3321526425" label="AuthAndSign failed: 501 (UNIMPLEMENTED)"/>
+  <int value="3329637962" label="AuthAndSign failed: 1xx/3xx (UNKNOWN)"/>
+  <int value="3336018440"
+      label="Non-Privacy Pass tokens are no longer supported"/>
+  <int value="3359657202"
+      label="[DEPRECATED] GetInitialDataRequest failed: 400
+             (INVALID_ARGUMENT)"/>
+  <int value="3426382554" label="AuthAndSign failed: 403 (PERMISSION_DENIED)"/>
+  <int value="3496444457" label="AuthAndSign failed: 5xx (INTERNAL)"/>
+  <int value="3506031840"
+      label="[DEPRECATED] AuthAndSign failed: 400 (INVALID_ARGUMENT)"/>
+  <int value="3554182688" label="Failed to unescape blinded signature"/>
+  <int value="3582010778" label="GetInitialData failed: 5xx (INTERNAL)"/>
+  <int value="3613919248" label="Failed to finalize token"/>
+  <int value="3831168552" label="AuthAndSign failed: 499 (CANCELLED)"/>
+  <int value="3831429430"
+      label="[DEPRECATED] AuthAndSign failed: 409 (ABORTED)"/>
+  <int value="3915930013" label="Failed to parse Privacy Pass public key"/>
+  <int value="4031088688" label="GetInitialData failed: 503 (UNAVAILABLE)"/>
+  <int value="4053847635"
+      label="GetInitialData failed: 400 (INVALID_ARGUMENT)"/>
+  <int value="4107556447"
+      label="[DEPRECATED] AuthAndSign failed: 4xx (FAILED_PRECONDITION)"/>
+  <int value="4124254920" label="AuthAndSign failed: 503 (UNAVAILABLE)"/>
+  <int value="4124458081" label="Failed to marshal token challenge"/>
+  <int value="4144593304"
+      label="Number of signatures is greater than the number of Privacy Pass
+             tokens sent"/>
+  <int value="4198175771"
+      label="[DEPRECATED] GetInitialDataRequest failed: 403
+             (PERMISSION_DENIED)"/>
+  <int value="4205984674"
+      label="[DEPRECATED] Failed to parse expiration timestamp"/>
+</enum>
+
+<enum name="IpProtectionTokenBatchRequestResult">
+  <int value="0" label="Success"/>
+  <int value="1" label="Failed - No Account"/>
+  <int value="2" label="Failed - Not Eligible"/>
+  <int value="3" label="Deprecated"/>
+  <int value="4" label="Failed - BSA Error 400"/>
+  <int value="5" label="Failed - BSA Error 401"/>
+  <int value="6" label="Failed - BSA Error 403"/>
+  <int value="7" label="Failed - BSA Error Other"/>
+  <int value="8" label="Transient OAuth Token Failure"/>
+  <int value="9" label="Persistent OAuth Token Failure"/>
+  <int value="10" label="Disabled by User"/>
+</enum>
+
+<!-- LINT.IfChange(ProbabilisticRevealTokensResult) -->
+
+<enum name="ProbabilisticRevealTokensResult">
+  <int value="0" label="Success"/>
+  <int value="1" label="Net Not Ok"/>
+  <int value="2" label="Net Ok Null Response"/>
+  <int value="3" label="Null Response"/>
+  <int value="4" label="Response Parsing Failed"/>
+  <int value="5" label="Invalid Token Version"/>
+  <int value="6" label="Invalid Token Size"/>
+  <int value="7" label="Too Few Tokens"/>
+  <int value="8" label="Too Many Tokens"/>
+  <int value="9" label="Expiration Too Soon"/>
+  <int value="10" label="Expiration Too Late"/>
+  <int value="11" label="Invalid Public Key"/>
+  <int value="12" label="Invalid Num Tokens With Signal"/>
+  <int value="13" label="Request Backed Off"/>
+  <int value="14" label="No Google Chrome Branding"/>
+  <int value="15" label="Invalid Epoch ID Size"/>
+</enum>
+
+<!-- LINT.ThenChange(//components/ip_protection/common/ip_protection_data_types.h:TryGetProbabilisticRevealTokensStatus) -->
+
+</enums>
+
+</histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/ip_protection/histograms.xml b/tools/metrics/histograms/metadata/ip_protection/histograms.xml
new file mode 100644
index 0000000..7a04b6d
--- /dev/null
+++ b/tools/metrics/histograms/metadata/ip_protection/histograms.xml
@@ -0,0 +1,774 @@
+<!--
+Copyright 2025 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<!--
+This file is used to generate a comprehensive list of IpProtection histograms
+along with a detailed description for each histogram.
+
+For best practices on writing histogram descriptions, see
+https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md
+
+Please follow the instructions in the OWNERS file in this directory to find a
+reviewer. If no OWNERS file exists, please consider signing up at
+go/reviewing-metrics (Googlers only), as all subdirectories are expected to
+have an OWNERS file. As a last resort you can send the CL to
+chromium-metrics-reviews@google.com.
+-->
+
+<histogram-configuration>
+
+<histograms>
+
+<histogram name="Net.HttpJob.IpProtection.AllowListMatch.BytesReceived2"
+    units="bytes" expires_after="2026-03-08">
+  <owner>aakallam@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Total bytes received over the network for an HttpJob request that would be
+    covered by IP Protection based on match with respect to the masked domain
+    list alone. This is irrespective of token and proxy availability. Any bypass
+    logic (e.g. first party to top level frame) will be considered, if set.
+    These bytes will be recorded even if `EnableIpPrivacyProxy` is `false`. This
+    is measured when the HttpJob is completed.
+
+    There is no previous version of this metric. It is numbered to be in sync
+    with similar metrics. Unlike PrefilterBytesRead, this metric accounts for
+    all HTTP stream bytes (not just the content body), counts bytes for aborted
+    jobs, and network bytes in 304 Not Modified exchanges (rather than cached
+    content bodies).
+  </summary>
+</histogram>
+
+<histogram name="Net.HttpJob.IpProtection.AllowListMatch.BytesSent2"
+    units="bytes" expires_after="2026-03-08">
+  <owner>aakallam@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Total bytes sent over the network for an HttpJob request that would be
+    covered by IP Protection based on match with respect to the masked domain
+    list alone. This is irrespective of token and proxy availability. Any bypass
+    logic (e.g. first party to top level frame) will be considered, if set.
+    These bytes will be recorded even if `EnableIpPrivacyProxy` is `false`. This
+    is measured when the HttpJob is completed.
+
+    This version improves on BytesSent by counting bytes for aborted jobs and
+    network bytes in 304 Not Modified exchanges.
+  </summary>
+</histogram>
+
+<histogram
+    name="Net.HttpJob.IpProtection.AllowListMatch.PrefilterBytesRead.Net"
+    units="bytes" expires_after="2026-01-07">
+  <owner>aakallam@chromium.org</owner>
+  <owner>abhijithnair@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Total prefilter (e.g., before decompression) bytes read for an HttpJob
+    request that would be covered by IP Protection and served from the network
+    based on match with respect to the masked domain list alone. This is
+    irrespective of token and proxy availability. Any bypass logic (e.g. first
+    party to top level frame) will be considered, if set. These bytes will be
+    recorded even if `EnableIpPrivacyProxy` is `false`. This is measured when
+    the HttpJob is completed.
+  </summary>
+</histogram>
+
+<histogram name="Net.HttpJob.IpProtection.BytesSent" units="bytes"
+    expires_after="2026-03-08">
+  <owner>aakallam@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Total bytes sent over the network for an HttpJob request that is covered by
+    IP Protection. These bytes will be recorded even if the IP Protection proxy
+    is configured as `direct://` (i.e. an IP Protection proxy is configured but
+    doesn't actually proxy any traffic).
+  </summary>
+</histogram>
+
+<histogram name="Net.HttpJob.IpProtection.BytesSent2" units="bytes"
+    expires_after="2026-06-21">
+  <owner>dschinazi@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Total bytes sent over the network for an HttpJob request that is covered by
+    IP Protection. These bytes will be recorded even if the IP Protection proxy
+    is configured as `direct://` (i.e. an IP Protection proxy is configured but
+    doesn't actually proxy any traffic). It is also recorded if IP Protection
+    failed and we fell back to going direct.
+  </summary>
+</histogram>
+
+<histogram name="Net.HttpJob.IpProtection.Fallback.BytesSent" units="bytes"
+    expires_after="2026-06-21">
+  <owner>dschinazi@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Total bytes sent over the network for an HttpJob request that is covered by
+    IP Protection. These bytes will only be recorded if IP Protection failed and
+    we fell back to going direct.
+  </summary>
+</histogram>
+
+<histogram name="Net.HttpJob.IpProtection.Fallback.PrefilterBytesRead.Net"
+    units="bytes" expires_after="2026-06-21">
+  <owner>dschinazi@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Total prefilter (e.g., before decompression) bytes read for an HttpJob
+    request that is covered by IP Protection and served from the network. These
+    bytes will only be recorded if IP Protection failed and we fell back to
+    going direct.
+  </summary>
+</histogram>
+
+<histogram name="Net.HttpJob.IpProtection.Fallback.TotalTimeNotCached2"
+    units="ms" expires_after="2026-06-21">
+  <owner>dschinazi@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Time it takes to complete an HttpJob that attempted IP Protection, from
+    starting the transaction until we are done reading, for jobs not served from
+    the cache. This time is only recorded if IP Protection failed and we fell
+    back to going direct.
+  </summary>
+</histogram>
+
+<histogram name="Net.HttpJob.IpProtection.JobResult"
+    enum="IpProtectionJobResult" expires_after="never">
+<!-- expires-never: This is used for dashboard metrics (internal: go/ipp-e2e-slos) -->
+
+  <owner>dschinazi@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Result of a UrlRequestHttpJob with respect to IP Protection: not protected,
+    successfully protected, or fallback on protection failure. This does not
+    measure the result of the request to the destination, so for example a 500
+    from the destination is still considered success.
+
+    When either of the `MaskedDomainList` or `EnableIpPrivacyProxy` features are
+    disabled, this will always be `kProtectionNotAttempted`.
+
+    This histogram is emitted for every request handled by `UrlRequestHttpJob`
+    that is not aborted.
+  </summary>
+</histogram>
+
+<histogram name="Net.HttpJob.IpProtection.JobResult.{Chain}"
+    enum="IpProtectionJobResult" expires_after="2026-06-21">
+  <owner>dschinazi@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Result of a UrlRequestHttpJob with respect to IP Protection: successfully
+    protected or fallback on protection failure. This is not logged when IP
+    Protection is not attempted. This does not measure the result of the request
+    to the destination, so for example a 500 from the destination is still
+    considered success. When either of the `MaskedDomainList` or
+    `EnableIpPrivacyProxy` features are disabled, this will not be logged.
+  </summary>
+  <token key="Chain">
+    <variant name="Chain0"/>
+    <variant name="Chain1"/>
+    <variant name="Chain2"/>
+    <variant name="Chain3"/>
+  </token>
+</histogram>
+
+<histogram name="Net.HttpJob.IpProtection.PrefilterBytesRead.Net" units="bytes"
+    expires_after="2026-03-08">
+  <owner>aakallam@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Total prefilter (e.g., before decompression) bytes read for an HttpJob
+    request that is covered by IP Protection and served from the network. These
+    bytes will be recorded even if the IP Protection proxy is configured as
+    `direct://` (i.e. an IP Protection proxy is configured but doesn't actually
+    proxy any traffic).
+  </summary>
+</histogram>
+
+<histogram name="Net.HttpJob.IpProtection.PrefilterBytesRead.Net2"
+    units="bytes" expires_after="2026-06-21">
+  <owner>dschinazi@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Total prefilter (e.g., before decompression) bytes read for an HttpJob
+    request that is covered by IP Protection and served from the network. These
+    bytes will be recorded even if the IP Protection proxy is configured as
+    `direct://` (i.e. an IP Protection proxy is configured but doesn't actually
+    proxy any traffic). It is also recorded if IP Protection failed and we fell
+    back to going direct.
+  </summary>
+</histogram>
+
+<histogram name="Net.HttpJob.IpProtection.TotalTimeNotCached" units="ms"
+    expires_after="2026-06-21">
+  <owner>dschinazi@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Time it takes to complete an HttpJob that is covered by IP Protection, from
+    starting the transaction until we are done reading, for jobs not served from
+    the cache. This time will be recorded even if the IP Protection proxy is
+    configured as `direct://` (i.e. an IP Protection proxy is configured but
+    doesn't actually proxy any traffic).
+  </summary>
+</histogram>
+
+<histogram name="Net.HttpJob.IpProtection.TotalTimeNotCached.{Chain}"
+    units="ms" expires_after="2026-06-21">
+  <owner>dschinazi@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Similar to Net.HttpJob.IpProtection.TotalTimeNotCached, but only chains with
+    the given chain_id, as provided in the GetProxyInfo RPC response.
+  </summary>
+  <token key="Chain">
+    <variant name="Chain1"/>
+    <variant name="Chain2"/>
+    <variant name="Chain3"/>
+  </token>
+</histogram>
+
+<histogram name="Net.HttpJob.IpProtection.TotalTimeNotCached3" units="ms"
+    expires_after="2026-06-21">
+  <owner>dschinazi@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Time it takes to complete an HttpJob that attempted IP Protection, from
+    starting the transaction until we are done reading, for jobs not served from
+    the cache. This time will be recorded even if the IP Protection proxy is
+    configured as `direct://` (i.e. an IP Protection proxy is configured but
+    doesn't actually proxy any traffic). It is also recorded if IP Protection
+    failed and we fell back to going direct.
+  </summary>
+</histogram>
+
+<histogram name="Net.IpProtection.CanFalloverToNextProxy2.Error.{Chain}"
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2026-02-22">
+  <owner>rsailer@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records the error code passed to CanFalloverToNextProxy() for IP Protection
+    proxy chains. This function is called when a proxy fails, and the error code
+    is used to determine whether to try the next proxy in the chain or not.
+  </summary>
+  <token key="Chain">
+    <variant name="Chain0"/>
+    <variant name="Chain1"/>
+    <variant name="Chain2"/>
+    <variant name="Chain3"/>
+  </token>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.EmptyTokenCache2"
+    enum="IpProtectionProxyLayer" expires_after="2026-03-08">
+  <owner>awillia@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records which token cache (if any) was empty when calling
+    `IpProtectionConfigCache::AreAuthTokensAvailable()`. This metric is NOT
+    emitted if the cache has never been filled.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.GeoChangeTokenPresence"
+    enum="BooleanAvailable" expires_after="2026-03-08">
+  <owner>awillia@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    This histogram is emitted to when changing to a new geo, and tracks whether
+    the token cache already contains tokens for the new geo.
+
+    `IpProtectionConfigCache::SetCurrentGeo(geo_id)` is called when a geo is
+    observed in the `IpProtectionProxyListManager` or
+    `IpProtectionTokenCacheManager`. This metric is useful to help understand if
+    token caching by geo is useful in providing tokens for when a geo shifts
+    back to a previous geo that is contained within the cache.
+
+    If the value is `Available`, it means that when the geo change was observed,
+    the token cache contained some number of tokens for the new geo from a
+    previous refill.
+
+    If the value is `Not Available`, it means that the a new geo was observed,
+    but there was no tokens already in the cache that matched this new geo.
+
+    This histogram will only be emitted if the MaskedDomainList and
+    EnableIpPrivacyProxy features are enabled and a platform-dependent sign-in
+    is complete.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.GetAuthTokenResult"
+    enum="BooleanSuccess" expires_after="2026-03-08">
+  <owner>awillia@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    The result of a call to `IpProtectionConfigCache::GetAuthToken()`, which
+    will not happen unless `IpProtectionConfigCache::AreAuthTokensAvailable()`
+    is true. If this fails, it is because the cache was empty when a new
+    connection to a proxy was initiated.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.GetAuthTokenResultForGeo"
+    enum="IpProtectionGetAuthTokenResultForGeo" expires_after="2026-01-07">
+  <owner>awillia@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    This metric tracks the effectiveness of token caching in IpProtection,
+    especially during network transitions or non-ideal conditions. It records
+    the outcome of `IpProtectionConfigCache::GetAuthToken()` requests, focusing
+    on token availability and its alignment with the current or a previously
+    cached geographical location. The four possible outcomes indicate successful
+    token retrieval (for current or other geo), or unavailability with or
+    without other cached tokens. This metric is only measured when geo caching
+    is enabled.
+
+    This histogram will only be emitted if the MaskedDomainList and
+    EnableIpPrivacyProxy features are enabled as well as the feature parameter,
+    IpPrivacyCacheTokensByGeo. Some kind of platform-dependent signin is also
+    required.
+  </summary>
+</histogram>
+
+<histogram
+    name="NetworkService.IpProtection.GetProbabilisticRevealTokensResult"
+    enum="ProbabilisticRevealTokensResult" expires_after="2026-03-08">
+  <owner>ryankalla@google.com</owner>
+  <owner>kiln-eng@google.com</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    The result of a call to `IpProtectionProbabilisticRevealTokenFetcher::
+    TryGetProbabilisticRevealTokens()`.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.GetProxyListResult"
+    enum="IpProtectionGetProxyListResult" expires_after="2026-03-08">
+  <owner>awillia@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Whether a call to `IpProtectionConfigCache::GetProxyList()` resulted in
+    obtaining a proxy list. This is measured in
+    `IpProtectionProxyListManagerImpl::OnGotProxyList`.
+
+    If a proxy list was obtained, this histogram also records whether it had at
+    least one valid entry. (It is possible to obtain an empty list from the
+    server, or the list from the server could contain only invalid entries that
+    are rejected by the client.)
+
+    This histogram will only be emitted if the MaskedDomainList and
+    EnableIpPrivacyProxy features are enabled. Some kind of platform-dependent
+    signin is also required.
+  </summary>
+</histogram>
+
+<histogram
+    name="NetworkService.IpProtection.IsProbabilisticRevealTokenAvailableOnInitialRequest"
+    enum="BooleanSuccess" expires_after="2026-03-08">
+  <owner>ryankalla@google.com</owner>
+  <owner>kiln-eng@google.com</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    The result of a call to
+    `IpProtectionProbabilisticRevealTokenManager::IsTokenAvailable()` when
+    `IpProtectionProbabilisticRevealTokenManager::GetToken()` is determining if
+    the token cache contains any non-expired tokens. If this fails, the request
+    will not include a probabilistic reveal token.
+
+    This histogram is only emitted the first time `GetToken()` is called after
+    the PRT manager is constructed.
+  </summary>
+</histogram>
+
+<histogram
+    name="NetworkService.IpProtection.IsProbabilisticRevealTokenAvailableOnSubsequentRequest"
+    enum="BooleanSuccess" expires_after="2026-03-08">
+  <owner>ryankalla@google.com</owner>
+  <owner>kiln-eng@google.com</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    The result of a call to
+    `IpProtectionProbabilisticRevealTokenManager::IsTokenAvailable()` when
+    `IpProtectionProbabilisticRevealTokenManager::GetToken()` is determining if
+    the token cache contains any non-expired tokens. If this fails, the request
+    will not include a probabilistic reveal token.
+
+    This histogram is not emitted the first time `GetToken()` is called after
+    the PRT manager is constructed, but is emitted on all subsequent calls.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.OAuthTokenFetchTime" units="ms"
+    expires_after="2026-03-08">
+  <owner>awillia@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records the elapsed time for successful requests by IpProtectionConfigGetter
+    for an OAuth token.
+  </summary>
+</histogram>
+
+<histogram
+    name="NetworkService.IpProtection.ProbabilisticRevealTokenRandomizationTime"
+    units="ms" expires_after="2026-03-08">
+  <owner>ryankalla@google.com</owner>
+  <owner>kiln-eng@google.com</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records the elapsed time for successfully re-randomizing a probabilistic
+    reveal token.
+  </summary>
+</histogram>
+
+<histogram
+    name="NetworkService.IpProtection.ProbabilisticRevealTokensRequestTime"
+    units="ms" expires_after="2026-03-08">
+  <owner>ryankalla@google.com</owner>
+  <owner>kiln-eng@google.com</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records the elapsed time for successful requests by
+    IpProtectionProbabilisticRevealTokenFetcher for a batch of probabilistic
+    reveal tokens. This represents the total time taken from the moment we call
+    the TokenFetcher until PRTs are available in the browser. This includes the
+    IPC between the browser and the network service, the network request itself
+    (including any scheduling/queuing delay), and the token validation checks.
+  </summary>
+</histogram>
+
+<histogram
+    name="NetworkService.IpProtection.ProxyAllowList.FlatbufferBuildTime"
+    units="ms" expires_after="2026-03-08">
+  <owner>aakallam@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records the time it takes to build the MaskedDomainList flatbuffer data
+    structure. This is recorded once for each successful call to
+    `BuildFromProto`.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.ProxyAllowList.UpdateSuccess"
+    enum="BooleanSuccess" expires_after="2026-03-08">
+  <owner>aakallam@chromium.org</owner>
+  <owner>abhijithnair@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records the result of an attempt to update the MaskedDomainListManager with
+    the contents of the Masked Domain List when it is received from Component
+    Updater.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.ProxyChainFallback"
+    enum="IpProtectionProxyChainId" expires_after="2026-03-08">
+  <owner>awillia@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Logged when a proxy chain failure is detected in the proxy delegate's
+    `OnFallback` method, with values defined by the `chain_id` value given in
+    the GetProxyInfo RPC response.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.ProxyListRefreshTime" units="ms"
+    expires_after="2026-01-07">
+  <owner>awillia@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Time taken to successfully refresh the IP Protection proxy list, measured
+    from IpProtectionProxyListManagerImpl::RefreshProxyList to
+    IpProtectionProxyListManagerImpl::OnGotProxyList.
+
+    A successful refresh is anything where OnGotProxyList receives a non-nullopt
+    proxy_list, which may include cases where an empty proxy list or a proxy
+    list with only unsuitable entries was fetched over the network. Outright
+    failures and refreshes which were inhibited due to client-side rate limiting
+    will not be measured.
+
+    This histogram will only be emitted if the MaskedDomainList and
+    EnableIpPrivacyProxy features are enabled. Some kind of platform-dependent
+    signin is also required.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.ProxyResolution"
+    enum="IpProtectionProxyResolutionResult" expires_after="never">
+<!-- expires-never: This is used for dashboard metrics (internal: go/ipp-e2e-slos) -->
+
+  <owner>awillia@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    The result of determining whether a request should be proxied (as
+    implemented in IpProtectionProxyDelegate::ClassifyRequest). The enum values
+    are in the order the checks occur, so for example, if the MaskedDomainList
+    feature is disabled, &quot;The MDL is not populated&quot; value will be
+    emitted because that is checked first. If that feature is enabled and the
+    request does not match the MDL, then the &quot;The request did not match the
+    MDL&quot; value will be recorded. &quot;The IP Protection setting is
+    disabled&quot; value will be recorded if the feature is enabled and the
+    request matched the MDL, but IP Protection was disabled via user settings or
+    enterprise policy, and so on.
+
+    This histogram is emitted for every request that undergoes proxy resolution.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.QuicProxiesFailed"
+    units="requests" expires_after="2026-03-08">
+  <owner>awillia@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Recorded when a connection to a QUIC proxy for IP Protection failed and the
+    HTTPS fallback succeeds, recording the number of requests begun before this
+    failure occurred. In this situation, Chrome falls back to exclusively HTTPS
+    proxies until the network changes, so this metric provides a way to
+    determine what proportion of clients are experiencing this failure, and how
+    quickly they experience the failure. Note that the metric counts requests
+    *begun*, so this value may be greater than one even if the first request
+    fails. This metric is not recorded for clients not configured to use QUIC to
+    connect to IP Protection proxies.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.TokenBatchGenerationTime"
+    units="ms" expires_after="2026-03-08">
+  <owner>awillia@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records the time taken for a successful attempt to generate auth tokens.
+    This only measures the time across a single attempt (not across retries,
+    which may be delayed by a variable backoff).
+
+    This measures the whole token batch generation process, from an
+    IpProtectionTokenCacheManagerImpl's perspective, from just before calling
+    IpProtectionConfigGetter::TryGetAuthTokens until OnGotAuthTokens. Note that
+    if OnGotAuthTokens receives a non-nullopt but empty vector of tokens this is
+    considered a success by this metric.
+
+    Note that if multiple token caches exist (one for each proxy layer), the
+    attempts in each token cache are timed independently, but they will all feed
+    into the same histogram.
+
+    This histogram will only be emitted if the MaskedDomainList and
+    EnableIpPrivacyProxy features are enabled. Some kind of platform-dependent
+    signin is also required.
+  </summary>
+</histogram>
+
+<histogram
+    name="NetworkService.IpProtection.TokenBatchGenerationTime.{BlindSignAuthPhase}"
+    units="ms" expires_after="2026-01-07">
+  <owner>jtoohill@google.com</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records the time taken for the {BlindSignAuthPhase} step of generating auth
+    tokens. This only measures the time across a single attempt (not across
+    retries, which may be delayed by a variable backoff).
+
+    Note that if multiple token caches exist (one for each proxy layer), the
+    attempts in each token cache are timed independently, but they will all feed
+    into the same histogram.
+
+    This histogram will be emitted during a GetTokens call into the BSA library,
+    which is only called if the MaskedDomainList and EnableIpPrivacyProxy
+    features are enabled. Some kind of platform-dependent signin is also
+    required.
+  </summary>
+<!-- LINT.IfChange(BlindSignAuthPhase) -->
+
+  <token key="BlindSignAuthPhase">
+    <variant name="AuthAndSign"/>
+    <variant name="GenerateBlindedTokenRequests"/>
+    <variant name="GetInitialData"/>
+    <variant name="UnblindTokens"/>
+  </token>
+<!-- LINT.ThenChange(/components/ip_protection/common/ip_protection_telemetry.h:BlindSignAuthPhase) -->
+
+</histogram>
+
+<histogram name="NetworkService.IpProtection.TokenBatchRequestTime" units="ms"
+    expires_after="2026-03-08">
+  <owner>awillia@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records the elapsed time for successful requests by IpProtectionConfigGetter
+    for blind-signed tokens from BSA.
+
+    This metric only measures part of the Chrome-specific blind-signing
+    implementation and does not encompass the full token batch generation
+    process. See NetworkService.IpProtection.TokenBatchGenerationTime for a
+    generic measurement of the full token batch generation process.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.TokenDemandDuringBatchGeneration"
+    units="tokens" expires_after="2026-01-07">
+  <owner>jtoohill@google.com</owner>
+  <owner>awillia@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    An upper bound on the number of tokens that might have been spent while a
+    call to TryGetAuthTokens was in flight. This includes both tokens that were
+    successfully spent from the cache, and requests that were not proxied
+    because the cache was empty. Emitted when TryGetAuthTokens finishes.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.TryGetAuthTokensErrors"
+    enum="IpProtectionTokenBatchRequestError" expires_after="2026-03-08">
+  <owner>linxinan@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    This histogram is emitted when any error returned by GetTokens call into the
+    BSA library. The error message is hashed into unsigned 32 bit integer. For
+    more information on the possible errors from BSA see
+    https://docs.google.com/document/d/1zv3AqLkALZGsvCKYd1M3UytNpyY0ecK55LdsioubmC4.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.TryGetAuthTokensResult2"
+    enum="IpProtectionTokenBatchRequestResult" expires_after="2026-03-22">
+  <owner>awillia@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    The result of handling a request by the network process to the browser
+    process for a batch of blind-signed auth tokens.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.{ProxyLayer}.TokenCount.{Event}"
+    units="tokens" expires_after="2026-03-22">
+  <owner>awillia@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records the number of IP Protection auth tokens involved in a specific event
+    for {ProxyLayer}. The histogram indicates that {Event}. Recorded when tokens
+    are successfully issued, spent, expired, or orphaned. Expected counts are
+    typically 1 for Spent, and potentially higher for other events (up to batch
+    size or cache size).
+  </summary>
+  <token key="ProxyLayer">
+    <variant name="ProxyA"/>
+    <variant name="ProxyB"/>
+  </token>
+  <token key="Event">
+    <variant name="Expired" summary="Token removed from cache due to expiry."/>
+    <variant name="Issued" summary="Tokens successfully fetched and cached."/>
+    <variant name="Orphaned"
+        summary="Tokens discarded (neither spent nor expired) on network
+                 context destruction."/>
+    <variant name="Recycled"
+        summary="Previously orphaned tokens added to the cache"/>
+    <variant name="Spent" summary="Token used for a proxy connection."/>
+  </token>
+</histogram>
+
+<histogram name="NetworkService.IpProtection.{ProxyLayer}.TokenExpirationRate"
+    units="tokens" expires_after="2026-03-08">
+  <owner>awillia@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Number of auth tokens for {ProxyLayer} that expired before they were used,
+    as a rate per hour, up to 100,000. This value is measured approximately
+    every 5 minutes.
+  </summary>
+  <token key="ProxyLayer">
+    <variant name="ProxyA"/>
+    <variant name="ProxyB"/>
+  </token>
+</histogram>
+
+<histogram name="NetworkService.MaskedDomainList.DiskUsage" units="KB"
+    expires_after="2026-03-15">
+  <owner>djmitche@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records the on-disk size of each MDL, when stored on disk (in the flatbuffer
+    format). A single execution of the browser may generate multiple MDLs, such
+    as for multiple experiments; each of these will be a distinct record. This
+    record is emitted when the flatbuffer MDL is generated. The generated
+    flatbuffer MDL may be cached and reused for multiple network service
+    instances, or may be re-generated for each one.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.MaskedDomainList.EstimatedMemoryUsage"
+    units="KB" expires_after="2025-12-14">
+  <owner>aakallam@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records the estimated memory usage of the MaskedDomainListManager every time
+    the Masked Domain List is updated.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.MaskedDomainList.FirstUpdateTime" units="ms"
+    expires_after="2026-01-11">
+  <owner>aakallam@chromium.org</owner>
+  <owner>ashleynewson@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Delay between a MaskedDomainListManager being constructed and its
+    UpdateMaskedDomainList method first being called (measured on entry).
+
+    This effectively measures the duration for which the MDL is not populated
+    and thus requests' eligibilities for IP Protection remain unknown.
+
+    This histogram is only emitted when the MaskedDomainList feature is enabled.
+    The histogram will not be emitted if the MDL never becomes available. It is
+    emitted at most once per Network Service startup.
+
+    This histogram can easily overflow its maximum (10 second) bucket if the
+    component that stores the MDL data is not already available on disk. This
+    histogram is primarily focused on the (more typical) case where such data is
+    already available.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.MaskedDomainList.MatchesTime"
+    units="microseconds" expires_after="2026-03-22">
+  <owner>djmitche@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records the time required to call the `Matches` method on the MDL. This is a
+    sync call that accesses a large data structure so it may require additional
+    optimization.
+
+    Note that clients without high-resolution clocks will report 0 for very
+    short times.
+  </summary>
+</histogram>
+
+<histogram name="NetworkService.MaskedDomainList.Size2" units="KB"
+    expires_after="2026-02-22">
+  <owner>aakallam@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Record the size of the Masked Domain List proto in KB. Emitted when the raw
+    proto bytes are received from Component Updater and parsed. The previous
+    version of this histogram sometimes recorded the value in MB and was not
+    usable.
+  </summary>
+</histogram>
+
+</histograms>
+
+</histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/net/enums.xml b/tools/metrics/histograms/metadata/net/enums.xml
index 5b05c501..6d58fa59 100644
--- a/tools/metrics/histograms/metadata/net/enums.xml
+++ b/tools/metrics/histograms/metadata/net/enums.xml
@@ -655,14 +655,6 @@
   <int value="3" label="3G"/>
 </enum>
 
-<enum name="IpProtectionJobResult">
-  <int value="0" label="IP Protection was not attempted"/>
-  <int value="1"
-      label="The request was IP Protected and carried via IP Protection
-             proxies or, if the direct-only parameter is true, made directly"/>
-  <int value="2" label="The request was IP Protected, but fell back to direct"/>
-</enum>
-
 <enum name="JobProtocolErrorLocation">
   <int value="0" label="kSessionStartReadingFailedAsync"/>
   <int value="1" label="kSessionStartReadingFailedSync"/>
@@ -2998,6 +2990,19 @@
 
 <!-- LINT.ThenChange(//net/disk_cache/sql/sql_backend_impl.h:FakeIndexFileError) -->
 
+<!-- LINT.IfChange(SqlDiskCacheIndexMismatchLocation) -->
+
+<enum name="SqlDiskCacheIndexMismatchLocation">
+  <int value="0" label="OpenOrCreateEntry"/>
+  <int value="1" label="CreateEntry"/>
+  <int value="2" label="DoomEntry"/>
+  <int value="3" label="StartEviction"/>
+  <int value="4" label="DeleteLiveEntry"/>
+  <int value="5" label="DeleteLiveEntriesBetween"/>
+</enum>
+
+<!-- LINT.ThenChange(//net/disk_cache/sql/sql_persistent_store.cc:IndexMismatchLocation) -->
+
 <!-- LINT.IfChange(SqlDiskCacheStoreError) -->
 
 <enum name="SqlDiskCacheStoreError">
@@ -3017,6 +3022,7 @@
   <int value="13" label="NotFound"/>
   <int value="14" label="InvalidArgument"/>
   <int value="15" label="BodyEndMismatch"/>
+  <int value="16" label="FailedForTesting"/>
 </enum>
 
 <!-- LINT.ThenChange(//net/disk_cache/sql/sql_persistent_store.h:SqlDiskCacheStoreError) -->
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index 8d0eea81..b9ae865 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -3036,236 +3036,6 @@
   </summary>
 </histogram>
 
-<histogram name="Net.HttpJob.IpProtection.AllowListMatch.BytesReceived2"
-    units="bytes" expires_after="2026-03-08">
-  <owner>aakallam@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Total bytes received over the network for an HttpJob request that would be
-    covered by IP Protection based on match with respect to the masked domain
-    list alone. This is irrespective of token and proxy availability. Any bypass
-    logic (e.g. first party to top level frame) will be considered, if set.
-    These bytes will be recorded even if `EnableIpPrivacyProxy` is `false`. This
-    is measured when the HttpJob is completed.
-
-    There is no previous version of this metric. It is numbered to be in sync
-    with similar metrics. Unlike PrefilterBytesRead, this metric accounts for
-    all HTTP stream bytes (not just the content body), counts bytes for aborted
-    jobs, and network bytes in 304 Not Modified exchanges (rather than cached
-    content bodies).
-  </summary>
-</histogram>
-
-<histogram name="Net.HttpJob.IpProtection.AllowListMatch.BytesSent2"
-    units="bytes" expires_after="2026-03-08">
-  <owner>aakallam@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Total bytes sent over the network for an HttpJob request that would be
-    covered by IP Protection based on match with respect to the masked domain
-    list alone. This is irrespective of token and proxy availability. Any bypass
-    logic (e.g. first party to top level frame) will be considered, if set.
-    These bytes will be recorded even if `EnableIpPrivacyProxy` is `false`. This
-    is measured when the HttpJob is completed.
-
-    This version improves on BytesSent by counting bytes for aborted jobs and
-    network bytes in 304 Not Modified exchanges.
-  </summary>
-</histogram>
-
-<histogram
-    name="Net.HttpJob.IpProtection.AllowListMatch.PrefilterBytesRead.Net"
-    units="bytes" expires_after="2026-01-07">
-  <owner>aakallam@chromium.org</owner>
-  <owner>abhijithnair@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Total prefilter (e.g., before decompression) bytes read for an HttpJob
-    request that would be covered by IP Protection and served from the network
-    based on match with respect to the masked domain list alone. This is
-    irrespective of token and proxy availability. Any bypass logic (e.g. first
-    party to top level frame) will be considered, if set. These bytes will be
-    recorded even if `EnableIpPrivacyProxy` is `false`. This is measured when
-    the HttpJob is completed.
-  </summary>
-</histogram>
-
-<histogram name="Net.HttpJob.IpProtection.BytesSent" units="bytes"
-    expires_after="2026-03-08">
-  <owner>aakallam@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Total bytes sent over the network for an HttpJob request that is covered by
-    IP Protection. These bytes will be recorded even if the IP Protection proxy
-    is configured as `direct://` (i.e. an IP Protection proxy is configured but
-    doesn't actually proxy any traffic).
-  </summary>
-</histogram>
-
-<histogram name="Net.HttpJob.IpProtection.BytesSent2" units="bytes"
-    expires_after="2026-06-21">
-  <owner>dschinazi@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Total bytes sent over the network for an HttpJob request that is covered by
-    IP Protection. These bytes will be recorded even if the IP Protection proxy
-    is configured as `direct://` (i.e. an IP Protection proxy is configured but
-    doesn't actually proxy any traffic). It is also recorded if IP Protection
-    failed and we fell back to going direct.
-  </summary>
-</histogram>
-
-<histogram name="Net.HttpJob.IpProtection.Fallback.BytesSent" units="bytes"
-    expires_after="2026-06-21">
-  <owner>dschinazi@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Total bytes sent over the network for an HttpJob request that is covered by
-    IP Protection. These bytes will only be recorded if IP Protection failed and
-    we fell back to going direct.
-  </summary>
-</histogram>
-
-<histogram name="Net.HttpJob.IpProtection.Fallback.PrefilterBytesRead.Net"
-    units="bytes" expires_after="2026-06-21">
-  <owner>dschinazi@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Total prefilter (e.g., before decompression) bytes read for an HttpJob
-    request that is covered by IP Protection and served from the network. These
-    bytes will only be recorded if IP Protection failed and we fell back to
-    going direct.
-  </summary>
-</histogram>
-
-<histogram name="Net.HttpJob.IpProtection.Fallback.TotalTimeNotCached2"
-    units="ms" expires_after="2026-06-21">
-  <owner>dschinazi@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Time it takes to complete an HttpJob that attempted IP Protection, from
-    starting the transaction until we are done reading, for jobs not served from
-    the cache. This time is only recorded if IP Protection failed and we fell
-    back to going direct.
-  </summary>
-</histogram>
-
-<histogram name="Net.HttpJob.IpProtection.JobResult"
-    enum="IpProtectionJobResult" expires_after="never">
-<!-- expires-never: This is used for dashboard metrics (internal: go/ipp-e2e-slos) -->
-
-  <owner>dschinazi@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Result of a UrlRequestHttpJob with respect to IP Protection: not protected,
-    successfully protected, or fallback on protection failure. This does not
-    measure the result of the request to the destination, so for example a 500
-    from the destination is still considered success.
-
-    When either of the `MaskedDomainList` or `EnableIpPrivacyProxy` features are
-    disabled, this will always be `kProtectionNotAttempted`.
-
-    This histogram is emitted for every request handled by `UrlRequestHttpJob`
-    that is not aborted.
-  </summary>
-</histogram>
-
-<histogram name="Net.HttpJob.IpProtection.JobResult.{Chain}"
-    enum="IpProtectionJobResult" expires_after="2026-06-21">
-  <owner>dschinazi@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Result of a UrlRequestHttpJob with respect to IP Protection: successfully
-    protected or fallback on protection failure. This is not logged when IP
-    Protection is not attempted. This does not measure the result of the request
-    to the destination, so for example a 500 from the destination is still
-    considered success. When either of the `MaskedDomainList` or
-    `EnableIpPrivacyProxy` features are disabled, this will not be logged.
-  </summary>
-  <token key="Chain">
-    <variant name="Chain0"/>
-    <variant name="Chain1"/>
-    <variant name="Chain2"/>
-    <variant name="Chain3"/>
-  </token>
-</histogram>
-
-<histogram name="Net.HttpJob.IpProtection.PrefilterBytesRead.Net" units="bytes"
-    expires_after="2026-03-08">
-  <owner>aakallam@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Total prefilter (e.g., before decompression) bytes read for an HttpJob
-    request that is covered by IP Protection and served from the network. These
-    bytes will be recorded even if the IP Protection proxy is configured as
-    `direct://` (i.e. an IP Protection proxy is configured but doesn't actually
-    proxy any traffic).
-  </summary>
-</histogram>
-
-<histogram name="Net.HttpJob.IpProtection.PrefilterBytesRead.Net2"
-    units="bytes" expires_after="2026-06-21">
-  <owner>dschinazi@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Total prefilter (e.g., before decompression) bytes read for an HttpJob
-    request that is covered by IP Protection and served from the network. These
-    bytes will be recorded even if the IP Protection proxy is configured as
-    `direct://` (i.e. an IP Protection proxy is configured but doesn't actually
-    proxy any traffic). It is also recorded if IP Protection failed and we fell
-    back to going direct.
-  </summary>
-</histogram>
-
-<histogram name="Net.HttpJob.IpProtection.TotalTimeNotCached" units="ms"
-    expires_after="2026-06-21">
-  <owner>dschinazi@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Time it takes to complete an HttpJob that is covered by IP Protection, from
-    starting the transaction until we are done reading, for jobs not served from
-    the cache. This time will be recorded even if the IP Protection proxy is
-    configured as `direct://` (i.e. an IP Protection proxy is configured but
-    doesn't actually proxy any traffic).
-  </summary>
-</histogram>
-
-<histogram name="Net.HttpJob.IpProtection.TotalTimeNotCached.{Chain}"
-    units="ms" expires_after="2026-06-21">
-  <owner>dschinazi@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Similar to Net.HttpJob.IpProtection.TotalTimeNotCached, but only chains with
-    the given chain_id, as provided in the GetProxyInfo RPC response.
-  </summary>
-  <token key="Chain">
-    <variant name="Chain1"/>
-    <variant name="Chain2"/>
-    <variant name="Chain3"/>
-  </token>
-</histogram>
-
-<histogram name="Net.HttpJob.IpProtection.TotalTimeNotCached3" units="ms"
-    expires_after="2026-06-21">
-  <owner>dschinazi@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Time it takes to complete an HttpJob that attempted IP Protection, from
-    starting the transaction until we are done reading, for jobs not served from
-    the cache. This time will be recorded even if the IP Protection proxy is
-    configured as `direct://` (i.e. an IP Protection proxy is configured but
-    doesn't actually proxy any traffic). It is also recorded if IP Protection
-    failed and we fell back to going direct.
-  </summary>
-</histogram>
-
 <histogram name="Net.HttpJob.MainJobWaitTimeWithAvailableSpdySession"
     units="ms" expires_after="2026-02-22">
   <owner>fayang@chromium.org</owner>
@@ -3794,23 +3564,6 @@
   </token>
 </histogram>
 
-<histogram name="Net.IpProtection.CanFalloverToNextProxy2.Error.{Chain}"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2026-02-22">
-  <owner>rsailer@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Records the error code passed to CanFalloverToNextProxy() for IP Protection
-    proxy chains. This function is called when a proxy fails, and the error code
-    is used to determine whether to try the next proxy in the chain or not.
-  </summary>
-  <token key="Chain">
-    <variant name="Chain0"/>
-    <variant name="Chain1"/>
-    <variant name="Chain2"/>
-    <variant name="Chain3"/>
-  </token>
-</histogram>
-
 <histogram name="Net.MultiThreadedCertVerifier.RequestDuration{HostType}"
     units="ms" expires_after="2026-03-01">
   <owner>nidhijaju@chromium.org</owner>
@@ -7716,6 +7469,30 @@
   </summary>
 </histogram>
 
+<histogram name="Net.SqlDiskCache.Backend.IndexMismatch"
+    enum="SqlDiskCacheIndexMismatchLocation" expires_after="2026-03-22">
+  <owner>horo@chromium.org</owner>
+  <owner>src/net/OWNERS</owner>
+  <summary>
+    Records the location where an in-memory index mismatch is detected in the
+    SQL-based disk cache. This helps identify the operations that are most prone
+    to causing inconsistencies between the in-memory index and the on-disk
+    database.
+  </summary>
+</histogram>
+
+<histogram name="Net.SqlDiskCache.Backend.LoadInMemoryIndexTime"
+    units="microseconds" expires_after="2025-12-09">
+  <owner>horo@chromium.org</owner>
+  <owner>src/net/OWNERS</owner>
+  <summary>
+    Measures the time taken to load the in memory index of the SQL-based disk
+    cache backend. This metric is recorded immediately after the in-memory index
+    is loaded from the database. The histogram is not reported on Windows
+    machines without high resolution clocks.
+  </summary>
+</histogram>
+
 <histogram name="Net.SqlDiskCache.Backend.MaxSize" units="MB"
     expires_after="2026-03-22">
   <owner>horo@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/network/enums.xml b/tools/metrics/histograms/metadata/network/enums.xml
index c76dc71..d65aed6 100644
--- a/tools/metrics/histograms/metadata/network/enums.xml
+++ b/tools/metrics/histograms/metadata/network/enums.xml
@@ -635,208 +635,6 @@
   <int value="5" label="kInstallationFailed"/>
 </enum>
 
-<enum name="IpProtectionEligibility">
-  <int value="0" label="Unknown"/>
-  <int value="1" label="Ineligible"/>
-  <int value="2" label="Eligible"/>
-</enum>
-
-<!-- LINT.IfChange(IpProtectionGetAuthTokenResultForGeo) -->
-
-<enum name="IpProtectionGetAuthTokenResultForGeo">
-  <int value="0" label="Auth token unavailable and cache is empty"/>
-  <int value="1" label="Auth token unavailable but cache contains tokens"/>
-  <int value="2" label="Auth token available for current geo"/>
-  <int value="3" label="Auth token available for other geo"/>
-</enum>
-
-<!-- LINT.ThenChange(//services/network/ip_protection/ip_protection_token_cache_manager_impl.h:AuthTokenResultForGeo) -->
-
-<enum name="IpProtectionGetProxyListResult">
-  <int value="0" label="Failed to acquire a list"/>
-  <int value="1" label="Got a list without any valid entries"/>
-  <int value="2" label="Got a list with at least one valid entry"/>
-</enum>
-
-<enum name="IpProtectionProxyChainId">
-  <int value="0" label="Unknown"/>
-  <int value="1" label="Chain 1"/>
-  <int value="2" label="Chain 2"/>
-  <int value="3" label="Chain 3"/>
-</enum>
-
-<enum name="IpProtectionProxyLayer">
-  <int value="0" label="Proxy A"/>
-  <int value="1" label="Proxy B"/>
-</enum>
-
-<enum name="IpProtectionProxyResolutionResult">
-  <int value="0"
-      label="The MDL is not populated, so an eligility decision could not be
-             made"/>
-  <int value="1" label="The request did not match the MDL"/>
-  <int value="2"
-      label="DEPRECATED: The EnableIpProtectionProxy feature is not enabled"/>
-  <int value="3" label="The IP Protection setting is disabled"/>
-  <int value="4" label="Proxy List is unavailable"/>
-  <int value="5"
-      label="Proxy List is available but tokens have never been available"/>
-  <int value="6"
-      label="Proxy List is available but tokens in the cache have been
-             exhausted"/>
-  <int value="7" label="The request was resolved to the IP Protection proxies"/>
-  <int value="8"
-      label="A site exception created by User Bypass disables protections"/>
-  <int value="9"
-      label="The request bypassed the IP Protection proxies through DevTools"/>
-</enum>
-
-<enum name="IpProtectionTokenBatchRequestError">
-<!-- The following errors are omitted because they have many possible suffixes:
-  "Failed to create RSA public key: <error>"
-  "Failed to decode extensions: <error>"
-  "Failed to validate extensions: <error>"
-  "Failed to parse expiration timestamp: <error>"
-  "Failed to parse geo hint: <error>"
-  "Failed to parse use case: <error>"
-  "Failed to create Privacy Pass client: <error>"
-  "Failed to create ExtendedTokenRequest: <error>"
-
-  Additionally, errors marked [DEPRECATED] are no longer emitted as of M141 (crrev.com/c/6865774).
- -->
-
-  <int value="4948469" label="AuthAndSign failed: 404 (NOT_FOUND)"/>
-  <int value="149981085" label="GetInitialData failed: 401 (UNAUTHENTICATED)"/>
-  <int value="150736087" label="GetInitialData failed: 499 (CANCELLED)"/>
-  <int value="159430335"
-      label="[DEPRECATED] GetInitialDataRequest failed: 499 (CANCELLED)"/>
-  <int value="203200113" label="AuthAndSign failed: 409 (ABORTED)"/>
-  <int value="219311180" label="AuthAndSign failed: invalid response"/>
-  <int value="247849994" label="GetInitialData failed: 501 (UNIMPLEMENTED)"/>
-  <int value="774920404"
-      label="[DEPRECATED] GetInitialDataRequest failed: 5xx (INTERNAL)"/>
-  <int value="776650315"
-      label="GetInitialData failed: 4xx (FAILED_PRECONDITION)"/>
-  <int value="788650824"
-      label="[DEPRECATED] GetInitialDataRequest failed: 409 (ABORTED)"/>
-  <int value="882579398"
-      label="[DEPRECATED] AuthAndSign failed: 504 (DEADLINE_EXCEEDED)"/>
-  <int value="1009665478" label="GetInitialData failed: 409 (ABORTED)"/>
-  <int value="1324062375"
-      label="[DEPRECATED] AuthAndSign failed: 403 (PERMISSION_DENIED)"/>
-  <int value="1359797045"
-      label="[DEPRECATED] AuthAndSign failed: 416 (OUT_OF_RANGE)"/>
-  <int value="1392994811"
-      label="[DEPRECATED] GetInitialDataRequest failed: 4xx
-             (FAILED_PRECONDITION)"/>
-  <int value="1560305329"
-      label="[DEPRECATED] GetInitialDataRequest failed: invalid response"/>
-  <int value="1570178381"
-      label="[DEPRECATED] GetInitialDataRequest failed: 429
-             (RESOURCE_EXHAUSTED)"/>
-  <int value="1578860000" label="GetInitialData failed: 404 (NOT_FOUND)"/>
-  <int value="1632191523"
-      label="[DEPRECATED] GetInitialDataRequest failed: 1xx/3xx (UNKNOWN)"/>
-  <int value="1632242186"
-      label="[DEPRECATED] AuthAndSign failed: 429 (RESOURCE_EXHAUSTED)"/>
-  <int value="1637757426"
-      label="[DEPRECATED] GetInitialDataRequest failed: 401 (UNAUTHENTICATED)"/>
-  <int value="1684860881"
-      label="GetInitialData failed: 429 (RESOURCE_EXHAUSTED)"/>
-  <int value="1731128661" label="AuthAndSign failed: 401 (UNAUTHENTICATED)"/>
-  <int value="1741288262"
-      label="[DEPRECATED] AuthAndSign failed: 499 (CANCELLED)"/>
-  <int value="1782033339"
-      label="[DEPRECATED] AuthAndSign failed: invalid response"/>
-  <int value="1869144073"
-      label="GetInitialData failed: 504 (DEADLINE_EXCEEDED)"/>
-  <int value="1916829389"
-      label="[DEPRECATED] GetInitialDataRequest failed: 404 (NOT_FOUND)"/>
-  <int value="2040698924"
-      label="[DEPRECATED] AuthAndSign failed: 404 (NOT_FOUND)"/>
-  <int value="2073947853"
-      label="[DEPRECATED] AuthAndSign failed: 401 (UNAUTHENTICATED)"/>
-  <int value="2141269466" label="[DEPRECATED] Failed to validate extensions"/>
-  <int value="2143022301" label="AuthAndSign failed: 429 (RESOURCE_EXHAUSTED)"/>
-  <int value="2152182465"
-      label="GetInitialData failed: 403 (PERMISSION_DENIED)"/>
-  <int value="2218632443" label="GetInitialData failed: invalid response"/>
-  <int value="2339897637"
-      label="[DEPRECATED] GetInitialDataRequest failed: 503 (UNAVAILABLE)"/>
-  <int value="2353899066" label="AuthAndSign failed: 416 (OUT_OF_RANGE)"/>
-  <int value="2408268044" label="AuthAndSign failed: 400 (INVALID_ARGUMENT)"/>
-  <int value="2420519214" label="GetInitialData failed: 1xx/3xx (UNKNOWN)"/>
-  <int value="2528366448" label="Failed to parse GetInitialDataResponse"/>
-  <int value="2528411010"
-      label="[DEPRECATED] GetInitialDataRequest failed: 416 (OUT_OF_RANGE)"/>
-  <int value="2594877687"
-      label="[DEPRECATED] GetInitialDataRequest failed: 504
-             (DEADLINE_EXCEEDED)"/>
-  <int value="2596810654" label="Failed to marshal token"/>
-  <int value="2766613933"
-      label="[DEPRECATED] AuthAndSign failed: 503 (UNAVAILABLE)"/>
-  <int value="2848390062" label="Failed to parse AuthAndSignResponse"/>
-  <int value="2906092890"
-      label="[DEPRECATED] GetInitialDataRequest failed: 501 (UNIMPLEMENTED)"/>
-  <int value="2958424397" label="GetInitialData failed: 416 (OUT_OF_RANGE)"/>
-  <int value="3024523150"
-      label="[DEPRECATED] AuthAndSign failed: 5xx (INTERNAL)"/>
-  <int value="3118017018"
-      label="[DEPRECATED] AuthAndSign failed: 501 (UNIMPLEMENTED)"/>
-  <int value="3172166369"
-      label="[DEPRECATED] AuthAndSign failed: 1xx/3xx (UNKNOWN)"/>
-  <int value="3240481601" label="AuthAndSign failed: 504 (DEADLINE_EXCEEDED)"/>
-  <int value="3317564205"
-      label="AuthAndSign failed: 4xx (FAILED_PRECONDITION)"/>
-  <int value="3321526425" label="AuthAndSign failed: 501 (UNIMPLEMENTED)"/>
-  <int value="3329637962" label="AuthAndSign failed: 1xx/3xx (UNKNOWN)"/>
-  <int value="3336018440"
-      label="Non-Privacy Pass tokens are no longer supported"/>
-  <int value="3359657202"
-      label="[DEPRECATED] GetInitialDataRequest failed: 400
-             (INVALID_ARGUMENT)"/>
-  <int value="3426382554" label="AuthAndSign failed: 403 (PERMISSION_DENIED)"/>
-  <int value="3496444457" label="AuthAndSign failed: 5xx (INTERNAL)"/>
-  <int value="3506031840"
-      label="[DEPRECATED] AuthAndSign failed: 400 (INVALID_ARGUMENT)"/>
-  <int value="3554182688" label="Failed to unescape blinded signature"/>
-  <int value="3582010778" label="GetInitialData failed: 5xx (INTERNAL)"/>
-  <int value="3613919248" label="Failed to finalize token"/>
-  <int value="3831168552" label="AuthAndSign failed: 499 (CANCELLED)"/>
-  <int value="3831429430"
-      label="[DEPRECATED] AuthAndSign failed: 409 (ABORTED)"/>
-  <int value="3915930013" label="Failed to parse Privacy Pass public key"/>
-  <int value="4031088688" label="GetInitialData failed: 503 (UNAVAILABLE)"/>
-  <int value="4053847635"
-      label="GetInitialData failed: 400 (INVALID_ARGUMENT)"/>
-  <int value="4107556447"
-      label="[DEPRECATED] AuthAndSign failed: 4xx (FAILED_PRECONDITION)"/>
-  <int value="4124254920" label="AuthAndSign failed: 503 (UNAVAILABLE)"/>
-  <int value="4124458081" label="Failed to marshal token challenge"/>
-  <int value="4144593304"
-      label="Number of signatures is greater than the number of Privacy Pass
-             tokens sent"/>
-  <int value="4198175771"
-      label="[DEPRECATED] GetInitialDataRequest failed: 403
-             (PERMISSION_DENIED)"/>
-  <int value="4205984674"
-      label="[DEPRECATED] Failed to parse expiration timestamp"/>
-</enum>
-
-<enum name="IpProtectionTokenBatchRequestResult">
-  <int value="0" label="Success"/>
-  <int value="1" label="Failed - No Account"/>
-  <int value="2" label="Failed - Not Eligible"/>
-  <int value="3" label="Deprecated"/>
-  <int value="4" label="Failed - BSA Error 400"/>
-  <int value="5" label="Failed - BSA Error 401"/>
-  <int value="6" label="Failed - BSA Error 403"/>
-  <int value="7" label="Failed - BSA Error Other"/>
-  <int value="8" label="Transient OAuth Token Failure"/>
-  <int value="9" label="Persistent OAuth Token Failure"/>
-  <int value="10" label="Disabled by User"/>
-</enum>
-
 <enum name="ManagedApnMigrationType">
   <int value="0" label="Matches selected APN"/>
   <int value="1" label="Does not match selected APN"/>
@@ -1679,29 +1477,6 @@
 
 <!-- LINT.ThenChange(//services/network/prefetch_matches.cc:FieldsForUma) -->
 
-<!-- LINT.IfChange(ProbabilisticRevealTokensResult) -->
-
-<enum name="ProbabilisticRevealTokensResult">
-  <int value="0" label="Success"/>
-  <int value="1" label="Net Not Ok"/>
-  <int value="2" label="Net Ok Null Response"/>
-  <int value="3" label="Null Response"/>
-  <int value="4" label="Response Parsing Failed"/>
-  <int value="5" label="Invalid Token Version"/>
-  <int value="6" label="Invalid Token Size"/>
-  <int value="7" label="Too Few Tokens"/>
-  <int value="8" label="Too Many Tokens"/>
-  <int value="9" label="Expiration Too Soon"/>
-  <int value="10" label="Expiration Too Late"/>
-  <int value="11" label="Invalid Public Key"/>
-  <int value="12" label="Invalid Num Tokens With Signal"/>
-  <int value="13" label="Request Backed Off"/>
-  <int value="14" label="No Google Chrome Branding"/>
-  <int value="15" label="Invalid Epoch ID Size"/>
-</enum>
-
-<!-- LINT.ThenChange(//components/ip_protection/common/ip_protection_data_types.h:TryGetProbabilisticRevealTokensStatus) -->
-
 <enum name="PSimSetupFlowResult">
   <int value="0" label="SUCCESS"/>
   <int value="1" label="CANCELLED"/>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml
index e82dcb0..bcd4e942 100644
--- a/tools/metrics/histograms/metadata/network/histograms.xml
+++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -5110,506 +5110,6 @@
   </summary>
 </histogram>
 
-<histogram name="NetworkService.IpProtection.EmptyTokenCache2"
-    enum="IpProtectionProxyLayer" expires_after="2026-03-08">
-  <owner>awillia@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Records which token cache (if any) was empty when calling
-    `IpProtectionConfigCache::AreAuthTokensAvailable()`. This metric is NOT
-    emitted if the cache has never been filled.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.GeoChangeTokenPresence"
-    enum="BooleanAvailable" expires_after="2026-03-08">
-  <owner>awillia@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    This histogram is emitted to when changing to a new geo, and tracks whether
-    the token cache already contains tokens for the new geo.
-
-    `IpProtectionConfigCache::SetCurrentGeo(geo_id)` is called when a geo is
-    observed in the `IpProtectionProxyListManager` or
-    `IpProtectionTokenCacheManager`. This metric is useful to help understand if
-    token caching by geo is useful in providing tokens for when a geo shifts
-    back to a previous geo that is contained within the cache.
-
-    If the value is `Available`, it means that when the geo change was observed,
-    the token cache contained some number of tokens for the new geo from a
-    previous refill.
-
-    If the value is `Not Available`, it means that the a new geo was observed,
-    but there was no tokens already in the cache that matched this new geo.
-
-    This histogram will only be emitted if the MaskedDomainList and
-    EnableIpPrivacyProxy features are enabled and a platform-dependent sign-in
-    is complete.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.GetAuthTokenResult"
-    enum="BooleanSuccess" expires_after="2026-03-08">
-  <owner>awillia@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    The result of a call to `IpProtectionConfigCache::GetAuthToken()`, which
-    will not happen unless `IpProtectionConfigCache::AreAuthTokensAvailable()`
-    is true. If this fails, it is because the cache was empty when a new
-    connection to a proxy was initiated.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.GetAuthTokenResultForGeo"
-    enum="IpProtectionGetAuthTokenResultForGeo" expires_after="2026-01-07">
-  <owner>awillia@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    This metric tracks the effectiveness of token caching in IpProtection,
-    especially during network transitions or non-ideal conditions. It records
-    the outcome of `IpProtectionConfigCache::GetAuthToken()` requests, focusing
-    on token availability and its alignment with the current or a previously
-    cached geographical location. The four possible outcomes indicate successful
-    token retrieval (for current or other geo), or unavailability with or
-    without other cached tokens. This metric is only measured when geo caching
-    is enabled.
-
-    This histogram will only be emitted if the MaskedDomainList and
-    EnableIpPrivacyProxy features are enabled as well as the feature parameter,
-    IpPrivacyCacheTokensByGeo. Some kind of platform-dependent signin is also
-    required.
-  </summary>
-</histogram>
-
-<histogram
-    name="NetworkService.IpProtection.GetProbabilisticRevealTokensResult"
-    enum="ProbabilisticRevealTokensResult" expires_after="2026-03-08">
-  <owner>ryankalla@google.com</owner>
-  <owner>kiln-eng@google.com</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    The result of a call to `IpProtectionProbabilisticRevealTokenFetcher::
-    TryGetProbabilisticRevealTokens()`.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.GetProxyListResult"
-    enum="IpProtectionGetProxyListResult" expires_after="2026-03-08">
-  <owner>awillia@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Whether a call to `IpProtectionConfigCache::GetProxyList()` resulted in
-    obtaining a proxy list. This is measured in
-    `IpProtectionProxyListManagerImpl::OnGotProxyList`.
-
-    If a proxy list was obtained, this histogram also records whether it had at
-    least one valid entry. (It is possible to obtain an empty list from the
-    server, or the list from the server could contain only invalid entries that
-    are rejected by the client.)
-
-    This histogram will only be emitted if the MaskedDomainList and
-    EnableIpPrivacyProxy features are enabled. Some kind of platform-dependent
-    signin is also required.
-  </summary>
-</histogram>
-
-<histogram
-    name="NetworkService.IpProtection.IsProbabilisticRevealTokenAvailableOnInitialRequest"
-    enum="BooleanSuccess" expires_after="2026-03-08">
-  <owner>ryankalla@google.com</owner>
-  <owner>kiln-eng@google.com</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    The result of a call to
-    `IpProtectionProbabilisticRevealTokenManager::IsTokenAvailable()` when
-    `IpProtectionProbabilisticRevealTokenManager::GetToken()` is determining if
-    the token cache contains any non-expired tokens. If this fails, the request
-    will not include a probabilistic reveal token.
-
-    This histogram is only emitted the first time `GetToken()` is called after
-    the PRT manager is constructed.
-  </summary>
-</histogram>
-
-<histogram
-    name="NetworkService.IpProtection.IsProbabilisticRevealTokenAvailableOnSubsequentRequest"
-    enum="BooleanSuccess" expires_after="2026-03-08">
-  <owner>ryankalla@google.com</owner>
-  <owner>kiln-eng@google.com</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    The result of a call to
-    `IpProtectionProbabilisticRevealTokenManager::IsTokenAvailable()` when
-    `IpProtectionProbabilisticRevealTokenManager::GetToken()` is determining if
-    the token cache contains any non-expired tokens. If this fails, the request
-    will not include a probabilistic reveal token.
-
-    This histogram is not emitted the first time `GetToken()` is called after
-    the PRT manager is constructed, but is emitted on all subsequent calls.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.OAuthTokenFetchTime" units="ms"
-    expires_after="2026-03-08">
-  <owner>awillia@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Records the elapsed time for successful requests by IpProtectionConfigGetter
-    for an OAuth token.
-  </summary>
-</histogram>
-
-<histogram
-    name="NetworkService.IpProtection.ProbabilisticRevealTokenRandomizationTime"
-    units="ms" expires_after="2026-03-08">
-  <owner>ryankalla@google.com</owner>
-  <owner>kiln-eng@google.com</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Records the elapsed time for successfully re-randomizing a probabilistic
-    reveal token.
-  </summary>
-</histogram>
-
-<histogram
-    name="NetworkService.IpProtection.ProbabilisticRevealTokensRequestTime"
-    units="ms" expires_after="2026-03-08">
-  <owner>ryankalla@google.com</owner>
-  <owner>kiln-eng@google.com</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Records the elapsed time for successful requests by
-    IpProtectionProbabilisticRevealTokenFetcher for a batch of probabilistic
-    reveal tokens. This represents the total time taken from the moment we call
-    the TokenFetcher until PRTs are available in the browser. This includes the
-    IPC between the browser and the network service, the network request itself
-    (including any scheduling/queuing delay), and the token validation checks.
-  </summary>
-</histogram>
-
-<histogram
-    name="NetworkService.IpProtection.ProxyAllowList.FlatbufferBuildTime"
-    units="ms" expires_after="2026-03-08">
-  <owner>aakallam@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Records the time it takes to build the MaskedDomainList flatbuffer data
-    structure. This is recorded once for each successful call to
-    `BuildFromProto`.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.ProxyAllowList.UpdateSuccess"
-    enum="BooleanSuccess" expires_after="2026-03-08">
-  <owner>aakallam@chromium.org</owner>
-  <owner>abhijithnair@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Records the result of an attempt to update the MaskedDomainListManager with
-    the contents of the Masked Domain List when it is received from Component
-    Updater.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.ProxyChainFallback"
-    enum="IpProtectionProxyChainId" expires_after="2026-03-08">
-  <owner>awillia@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Logged when a proxy chain failure is detected in the proxy delegate's
-    `OnFallback` method, with values defined by the `chain_id` value given in
-    the GetProxyInfo RPC response.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.ProxyListRefreshTime" units="ms"
-    expires_after="2026-01-07">
-  <owner>awillia@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Time taken to successfully refresh the IP Protection proxy list, measured
-    from IpProtectionProxyListManagerImpl::RefreshProxyList to
-    IpProtectionProxyListManagerImpl::OnGotProxyList.
-
-    A successful refresh is anything where OnGotProxyList receives a non-nullopt
-    proxy_list, which may include cases where an empty proxy list or a proxy
-    list with only unsuitable entries was fetched over the network. Outright
-    failures and refreshes which were inhibited due to client-side rate limiting
-    will not be measured.
-
-    This histogram will only be emitted if the MaskedDomainList and
-    EnableIpPrivacyProxy features are enabled. Some kind of platform-dependent
-    signin is also required.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.ProxyResolution"
-    enum="IpProtectionProxyResolutionResult" expires_after="never">
-<!-- expires-never: This is used for dashboard metrics (internal: go/ipp-e2e-slos) -->
-
-  <owner>awillia@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    The result of determining whether a request should be proxied (as
-    implemented in IpProtectionProxyDelegate::ClassifyRequest). The enum values
-    are in the order the checks occur, so for example, if the MaskedDomainList
-    feature is disabled, &quot;The MDL is not populated&quot; value will be
-    emitted because that is checked first. If that feature is enabled and the
-    request does not match the MDL, then the &quot;The request did not match the
-    MDL&quot; value will be recorded. &quot;The IP Protection setting is
-    disabled&quot; value will be recorded if the feature is enabled and the
-    request matched the MDL, but IP Protection was disabled via user settings or
-    enterprise policy, and so on.
-
-    This histogram is emitted for every request that undergoes proxy resolution.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.QuicProxiesFailed"
-    units="requests" expires_after="2026-03-08">
-  <owner>awillia@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Recorded when a connection to a QUIC proxy for IP Protection failed and the
-    HTTPS fallback succeeds, recording the number of requests begun before this
-    failure occurred. In this situation, Chrome falls back to exclusively HTTPS
-    proxies until the network changes, so this metric provides a way to
-    determine what proportion of clients are experiencing this failure, and how
-    quickly they experience the failure. Note that the metric counts requests
-    *begun*, so this value may be greater than one even if the first request
-    fails. This metric is not recorded for clients not configured to use QUIC to
-    connect to IP Protection proxies.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.TokenBatchGenerationTime"
-    units="ms" expires_after="2026-03-08">
-  <owner>awillia@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Records the time taken for a successful attempt to generate auth tokens.
-    This only measures the time across a single attempt (not across retries,
-    which may be delayed by a variable backoff).
-
-    This measures the whole token batch generation process, from an
-    IpProtectionTokenCacheManagerImpl's perspective, from just before calling
-    IpProtectionConfigGetter::TryGetAuthTokens until OnGotAuthTokens. Note that
-    if OnGotAuthTokens receives a non-nullopt but empty vector of tokens this is
-    considered a success by this metric.
-
-    Note that if multiple token caches exist (one for each proxy layer), the
-    attempts in each token cache are timed independently, but they will all feed
-    into the same histogram.
-
-    This histogram will only be emitted if the MaskedDomainList and
-    EnableIpPrivacyProxy features are enabled. Some kind of platform-dependent
-    signin is also required.
-  </summary>
-</histogram>
-
-<histogram
-    name="NetworkService.IpProtection.TokenBatchGenerationTime.{BlindSignAuthPhase}"
-    units="ms" expires_after="2026-01-07">
-  <owner>jtoohill@google.com</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Records the time taken for the {BlindSignAuthPhase} step of generating auth
-    tokens. This only measures the time across a single attempt (not across
-    retries, which may be delayed by a variable backoff).
-
-    Note that if multiple token caches exist (one for each proxy layer), the
-    attempts in each token cache are timed independently, but they will all feed
-    into the same histogram.
-
-    This histogram will be emitted during a GetTokens call into the BSA library,
-    which is only called if the MaskedDomainList and EnableIpPrivacyProxy
-    features are enabled. Some kind of platform-dependent signin is also
-    required.
-  </summary>
-<!-- LINT.IfChange(BlindSignAuthPhase) -->
-
-  <token key="BlindSignAuthPhase">
-    <variant name="AuthAndSign"/>
-    <variant name="GenerateBlindedTokenRequests"/>
-    <variant name="GetInitialData"/>
-    <variant name="UnblindTokens"/>
-  </token>
-<!-- LINT.ThenChange(/components/ip_protection/common/ip_protection_telemetry.h:BlindSignAuthPhase) -->
-
-</histogram>
-
-<histogram name="NetworkService.IpProtection.TokenBatchRequestTime" units="ms"
-    expires_after="2026-03-08">
-  <owner>awillia@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Records the elapsed time for successful requests by IpProtectionConfigGetter
-    for blind-signed tokens from BSA.
-
-    This metric only measures part of the Chrome-specific blind-signing
-    implementation and does not encompass the full token batch generation
-    process. See NetworkService.IpProtection.TokenBatchGenerationTime for a
-    generic measurement of the full token batch generation process.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.TokenDemandDuringBatchGeneration"
-    units="tokens" expires_after="2026-01-07">
-  <owner>jtoohill@google.com</owner>
-  <owner>awillia@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    An upper bound on the number of tokens that might have been spent while a
-    call to TryGetAuthTokens was in flight. This includes both tokens that were
-    successfully spent from the cache, and requests that were not proxied
-    because the cache was empty. Emitted when TryGetAuthTokens finishes.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.TryGetAuthTokensErrors"
-    enum="IpProtectionTokenBatchRequestError" expires_after="2026-03-08">
-  <owner>linxinan@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    This histogram is emitted when any error returned by GetTokens call into the
-    BSA library. The error message is hashed into unsigned 32 bit integer. For
-    more information on the possible errors from BSA see
-    https://docs.google.com/document/d/1zv3AqLkALZGsvCKYd1M3UytNpyY0ecK55LdsioubmC4.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.TryGetAuthTokensResult2"
-    enum="IpProtectionTokenBatchRequestResult" expires_after="2026-03-22">
-  <owner>awillia@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    The result of handling a request by the network process to the browser
-    process for a batch of blind-signed auth tokens.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.{ProxyLayer}.TokenCount.{Event}"
-    units="tokens" expires_after="2026-03-22">
-  <owner>awillia@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Records the number of IP Protection auth tokens involved in a specific event
-    for {ProxyLayer}. The histogram indicates that {Event}. Recorded when tokens
-    are successfully issued, spent, expired, or orphaned. Expected counts are
-    typically 1 for Spent, and potentially higher for other events (up to batch
-    size or cache size).
-  </summary>
-  <token key="ProxyLayer">
-    <variant name="ProxyA"/>
-    <variant name="ProxyB"/>
-  </token>
-  <token key="Event">
-    <variant name="Expired" summary="Token removed from cache due to expiry."/>
-    <variant name="Issued" summary="Tokens successfully fetched and cached."/>
-    <variant name="Orphaned"
-        summary="Tokens discarded (neither spent nor expired) on network
-                 context destruction."/>
-    <variant name="Recycled"
-        summary="Previously orphaned tokens added to the cache"/>
-    <variant name="Spent" summary="Token used for a proxy connection."/>
-  </token>
-</histogram>
-
-<histogram name="NetworkService.IpProtection.{ProxyLayer}.TokenExpirationRate"
-    units="tokens" expires_after="2026-03-08">
-  <owner>awillia@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Number of auth tokens for {ProxyLayer} that expired before they were used,
-    as a rate per hour, up to 100,000. This value is measured approximately
-    every 5 minutes.
-  </summary>
-  <token key="ProxyLayer">
-    <variant name="ProxyA"/>
-    <variant name="ProxyB"/>
-  </token>
-</histogram>
-
-<histogram name="NetworkService.MaskedDomainList.DiskUsage" units="KB"
-    expires_after="2026-03-15">
-  <owner>djmitche@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Records the on-disk size of each MDL, when stored on disk (in the flatbuffer
-    format). A single execution of the browser may generate multiple MDLs, such
-    as for multiple experiments; each of these will be a distinct record. This
-    record is emitted when the flatbuffer MDL is generated. The generated
-    flatbuffer MDL may be cached and reused for multiple network service
-    instances, or may be re-generated for each one.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.MaskedDomainList.EstimatedMemoryUsage"
-    units="KB" expires_after="2025-12-14">
-  <owner>aakallam@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Records the estimated memory usage of the MaskedDomainListManager every time
-    the Masked Domain List is updated.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.MaskedDomainList.FirstUpdateTime" units="ms"
-    expires_after="2026-01-11">
-  <owner>aakallam@chromium.org</owner>
-  <owner>ashleynewson@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Delay between a MaskedDomainListManager being constructed and its
-    UpdateMaskedDomainList method first being called (measured on entry).
-
-    This effectively measures the duration for which the MDL is not populated
-    and thus requests' eligibilities for IP Protection remain unknown.
-
-    This histogram is only emitted when the MaskedDomainList feature is enabled.
-    The histogram will not be emitted if the MDL never becomes available. It is
-    emitted at most once per Network Service startup.
-
-    This histogram can easily overflow its maximum (10 second) bucket if the
-    component that stores the MDL data is not already available on disk. This
-    histogram is primarily focused on the (more typical) case where such data is
-    already available.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.MaskedDomainList.MatchesTime"
-    units="microseconds" expires_after="2026-03-22">
-  <owner>djmitche@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Records the time required to call the `Matches` method on the MDL. This is a
-    sync call that accesses a large data structure so it may require additional
-    optimization.
-
-    Note that clients without high-resolution clocks will report 0 for very
-    short times.
-  </summary>
-</histogram>
-
-<histogram name="NetworkService.MaskedDomainList.Size2" units="KB"
-    expires_after="2026-02-22">
-  <owner>aakallam@chromium.org</owner>
-  <owner>src/components/ip_protection/OWNERS</owner>
-  <summary>
-    Record the size of the Masked Domain List proto in KB. Emitted when the raw
-    proto bytes are received from Component Updater and parsed. The previous
-    version of this histogram sometimes recorded the value in MB and was not
-    usable.
-  </summary>
-</histogram>
-
 <histogram name="NetworkService.NetworkLoaderCompletionTime2.{Source}"
     units="ms" expires_after="2026-03-01">
   <owner>hayato@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/quota/histograms.xml b/tools/metrics/histograms/metadata/quota/histograms.xml
index 807573c..a903108 100644
--- a/tools/metrics/histograms/metadata/quota/histograms.xml
+++ b/tools/metrics/histograms/metadata/quota/histograms.xml
@@ -73,8 +73,6 @@
     attempting to migrate the database.
   </summary>
   <token key="VersionUpgrades">
-    <variant name="FromV7ToV8"/>
-    <variant name="FromV8ToV9"/>
     <variant name="FromV9ToV10"/>
   </token>
 </histogram>
diff --git a/tools/metrics/histograms/metadata/search/enums.xml b/tools/metrics/histograms/metadata/search/enums.xml
index d969dc0..1ad69f6 100644
--- a/tools/metrics/histograms/metadata/search/enums.xml
+++ b/tools/metrics/histograms/metadata/search/enums.xml
@@ -436,9 +436,8 @@
     currently enabled only in the First Run Experience.
   </int>
   <int value="21" label="Managed">
-    The profile is managed, and the regional program does not treat managed
-    profiles as eligible for the choice screen. This management could be at the
-    device or profile level.
+    There is device-level management, and the regional program does not treat
+    managed profiles as eligible for the choice screen.
   </int>
 </enum>
 
diff --git a/tools/metrics/histograms/metadata/signin/histograms.xml b/tools/metrics/histograms/metadata/signin/histograms.xml
index 8256dc9..8b9cf37 100644
--- a/tools/metrics/histograms/metadata/signin/histograms.xml
+++ b/tools/metrics/histograms/metadata/signin/histograms.xml
@@ -3285,7 +3285,7 @@
 </histogram>
 
 <histogram name="Signin.SignoutAndClearDataFromManagedAccount"
-    enum="BooleanSignoutFromManagedAccount" expires_after="2025-10-12">
+    enum="BooleanSignoutFromManagedAccount" expires_after="2026-01-12">
   <owner>esalma@google.com</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sync/enums.xml b/tools/metrics/histograms/metadata/sync/enums.xml
index 7ef105c..383ec1e 100644
--- a/tools/metrics/histograms/metadata/sync/enums.xml
+++ b/tools/metrics/histograms/metadata/sync/enums.xml
@@ -258,6 +258,23 @@
 
 <!-- LINT.ThenChange(/components/sync_bookmarks/bookmark_model_merger.cc:RemoteBookmarkUpdateError) -->
 
+<!-- LINT.IfChange(SessionSpecificsInvalidReason) -->
+
+<enum name="SessionSpecificsInvalidReason">
+  <summary>
+    The validity (or reason for non-validity) of a SessionSpecifics.
+  </summary>
+  <int value="0" label="Missing session_tag"/>
+  <int value="1" label="Both header and tab specified"/>
+  <int value="2" label="Neither header nor tab specified"/>
+  <int value="3" label="Tab with invalid tab_node_id"/>
+  <int value="4" label="Tab with invalid tab_id"/>
+  <int value="5" label="Header with duplicate tab IDs"/>
+  <int value="6" label="Header with tab_node_id"/>
+</enum>
+
+<!-- LINT.ThenChange(/components/sync_sessions/session_store.h:SessionSpecificsInvalidReason) -->
+
 <enum name="SyncablePref">
 <!-- LINT.IfChange(CommonSyncablePref) -->
 
@@ -359,6 +376,7 @@
   <int value="94" label="AutofillNameAndEmailProfileNotSelectedCounter"/>
   <int value="96" label="AutofillAiLastVersionDeduped"/>
   <int value="97" label="CrossDeviceOmniboxIsInBottomPosition"/>
+  <int value="98" label="AutofillWasNameAndEmailProfileUsed"/>
 <!-- LINT.ThenChange(/components/sync_preferences/common_syncable_prefs_database.cc:CommonSyncablePref)-->
 
 <!-- LINT.IfChange(ChromeSyncablePref) -->
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index e07c2658..0260ff2 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -500,6 +500,17 @@
   </summary>
 </histogram>
 
+<histogram name="Sync.ClearMetadataDueToEmptyStorageKey" enum="SyncDataTypes"
+    expires_after="2026-03-30">
+  <owner>treib@chromium.org</owner>
+  <owner>src/components/sync/OWNERS</owner>
+  <summary>
+    Records when a data type's metadata is cleared due to empty/missing storage
+    keys in the persisted metadata. This will result in a re-download of all
+    data of this type.
+  </summary>
+</histogram>
+
 <histogram name="Sync.ClearMetadataWhileStopped{ClearTime}"
     enum="SyncDataTypes" expires_after="2026-05-13">
   <owner>ankushkush@google.com</owner>
@@ -1339,6 +1350,17 @@
   </summary>
 </histogram>
 
+<histogram name="Sync.InvalidSessionHeader.AssociateWindows"
+    enum="SessionSpecificsInvalidReason" expires_after="2026-03-30">
+  <owner>treib@chromium.org</owner>
+  <owner>src/components/sync/OWNERS</owner>
+  <summary>
+    Recorded if the SessionHeader specifics produced at the end of an
+    AssociateWindows() call was invalid (and thus not actually sent to the sync
+    machinery).
+  </summary>
+</histogram>
+
 <histogram name="Sync.KeystoreDecryptionFailed"
     enum="SyncKeystoreDecryptionFailure" expires_after="2026-07-05">
   <owner>mmoskvitin@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml
index 21d685b..a917970 100644
--- a/tools/metrics/histograms/metadata/tab/histograms.xml
+++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -2934,6 +2934,22 @@
   <token key="SplitViewMenuSource" variants="SplitViewMenuSource"/>
 </histogram>
 
+<histogram name="Tabs.Startup.PinnedTabCount.{TabModelSelectorType}.{TabType}"
+    units="count" expires_after="2026-09-15">
+  <owner>madhavpruthi@google.com</owner>
+  <owner>ckitagawa@chromium.org</owner>
+  <owner>clank-tab-dev@google.com</owner>
+  <summary>
+    (Android only) Records the number of {TabType} pinned Tabs loaded on startup
+    per {TabModelSelectorType}.
+  </summary>
+  <token key="TabModelSelectorType" variants="TabModelSelectorType"/>
+  <token key="TabType">
+    <variant name="Incognito" summary="Incognito"/>
+    <variant name="Regular" summary="Regular"/>
+  </token>
+</histogram>
+
 <histogram name="Tabs.Startup.RestoreDuration.{TabModelSelectorType}"
     units="ms" expires_after="2026-02-15">
   <owner>ckitagawa@chromium.org</owner>
diff --git a/tools/metrics/histograms/validate_format.py b/tools/metrics/histograms/validate_format.py
index 39ee0f4a..cebfffb 100755
--- a/tools/metrics/histograms/validate_format.py
+++ b/tools/metrics/histograms/validate_format.py
@@ -23,7 +23,7 @@
 # split across multiple files.
 _NAMESPACES_IN_MULTIPLE_FILES = [
     'ash', 'autocomplete', 'chromeos', 'fcminvalidations', 'graphics', 'launch',
-    'usereducation'
+    'net', 'networkservice', 'usereducation'
 ]
 
 
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index ac1d226..29b237df 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/ebf44e57a3b734c5281bdff53d9945805486004e/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "a6ee4e46272bdb4fa2327658c4c2b3800d96128e",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/95317c91aadc41a24abc2a671b211baec3d6d749/trace_processor_shell"
+            "hash": "82ea8949e97dc6edd053c9fa7dc37603804f4db9",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/34d969360658ee7c0505e4e34d5687f0ea55aaa0/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/v8 b/v8
index 4cfbba3..ca4e6e3 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 4cfbba3684e7d04a5d46c9f9a2117f84e02ef5ca
+Subproject commit ca4e6e3b342f30687ca4d36fda90be00be782331