diff --git a/DEPS b/DEPS
index b45f6b4..2ba9ca65 100644
--- a/DEPS
+++ b/DEPS
@@ -304,7 +304,7 @@
   # 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': '5f4740b998b39a53127873ed1798c739229b5027',
+  'skia_revision': '06721a72483137ffea1d51309bf681020b5e9e57',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -312,7 +312,7 @@
   # 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': '7b0212b337ff1c6bec9dea0a69f4ca42a19a37d7',
+  'angle_revision': '7c811715508175171303b305bc6891f8be9b0567',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -380,7 +380,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
-  'crossbench_revision': '18c0bc491b2e3c71d910377b3656560a0a204db4',
+  'crossbench_revision': '64f705bef1cbf31c84d6a779e4939c8b79edecde',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -388,7 +388,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling fuzztest
   # and whatever else without interference from each other.
-  'fuzztest_revision': 'a29e31cb00ec9b123dec5a0c6b8d4bc12c2480c8',
+  'fuzztest_revision': 'f0177b98d4d5f17a0bd448852d66d3e9a2c8fe86',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling domato
   # and whatever else without interference from each other.
@@ -396,7 +396,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': '862905a4376db9c2964d5178b30d11bf80c6cfdd',
+  'devtools_frontend_revision': '986c1da746d466f1079e62047f7a176e244b51f4',
   # 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.
@@ -484,7 +484,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'libcxxabi_revision':    'd0c57cb48fb5709f02096f411d65a11cbc722944',
+  'libcxxabi_revision':    '3c26edd0c36a294c74800ac48c17b18d0045e3b6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1295,7 +1295,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'fae8cf6f28d863b3e263c7fd30baed82ea532082',
+    '16d508806ccde9d81dbeae737909d2e181df48e1',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1774,7 +1774,7 @@
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '984e509da297e8ac3366d6579379a1294af73771',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '3ed9eeaf4c42ea7a10232f1df5dcd94ea68452b5',
     'condition': 'checkout_src_internal',
   },
 
@@ -2420,7 +2420,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': 'fPtOVFF2pWq045gaoD8_EIc8Kg9zbgBc6ydnAE7ATicC',
+              'version': 'LHNTk_-ajFbwrjtRsvuqgsLQGXD5dbTlWyV1MJ4_u2EC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -2547,14 +2547,14 @@
       'packages': [
           {
               'package': 'chromium/third_party/turbine',
-              'version': 'jgDuyha_VOnbWcvPhoSVjeQWyIfb_WwGGzpBQOhrFZsC',
+              'version': 'vSia3h9tzpwpP_goLj4HMdl7_FEB5iVCv9nU5ZXOfIMC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@50ad0c468c61f59b5a5bd6b869c44fc8d45cd51d',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@223523f05dc03140b40cf71915f74311207c3a6e',
   'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@46ef757e048e760b46601e6e77ae0cb72c97bd2f',
   '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@ec59c77a3bb5c747a369931ef101ac7c14823f2f',
@@ -2563,7 +2563,7 @@
   'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@56dd9f2344a7be1d218a54549a4a5e39c1e85073',
   'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@2020cec4111c87d85b167d583180b839f0c736c5',
   'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@a71907b8577c994b1c69adfa7315d55bbe79656e',
-  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@b840f9e619bc367959bd246d685f4af6b92ae161',
+  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@8bb1ad96a6204dd85b4af293eddc76a6f6ea3a54',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '56300b29fbfcc693ee6609ddad3fdd5b7a449a21',
@@ -2603,7 +2603,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '3f6f6b75c81701a4cbe893c4ff1ec3165aeb83d2',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '4ab100d4a6c3b7848ae33b4c88fdf995da2f8922',
+    Var('webrtc_git') + '/src.git' + '@' + '95e697190f50f9fe1dce12e9d49cda165efa29b0',
 
   # 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.
@@ -2787,7 +2787,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'YdvrpItxP8SHr17oBxcWSkWt-yqa9Rxm4H7FE4kdRF8C',
+        'version': 'mrVb9ia0bLjcFOnkdAMuqmv-ScUVpd3IEDoxXOhsaCQC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4385,7 +4385,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '55fb27a6b3296b3bb716819231e89fa2e2850dc7',
+        'b8eee2f52e65c9a92408c29c8b514414566f7337',
       '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 b99c017..4d608e5 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
@@ -817,10 +817,6 @@
                 ContentFeatures.ACCESSIBILITY_MANAGE_BROADCAST_RECEIVER_ON_BACKGROUND,
                 "Register, un-register Accessibility broadcast receiver on a background thread."),
         Flag.baseFeature(
-                VizFeatures.INVALIDATE_LOCAL_SURFACE_ID_PRE_COMMIT,
-                "When enabled, invalidates the LocalSurfaceId of the DelegatedFrameHostAndroid when"
-                        + " the old page is about to be unloaded."),
-        Flag.baseFeature(
                 BlinkFeatures.INCREMENT_LOCAL_SURFACE_ID_FOR_MAINFRAME_SAME_DOC_NAVIGATION,
                 "When enabled, every mainframe same-doc navigation will increment the"
                         + " `viz::LocalSurfaceId` from the impl thread."),
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwUncaughtExceptionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwUncaughtExceptionTest.java
index 8506c3c..72dc5c5 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwUncaughtExceptionTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwUncaughtExceptionTest.java
@@ -29,6 +29,7 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.base.test.util.DoNotBatch;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 
 import java.lang.reflect.Field;
@@ -280,6 +281,7 @@
     @Test
     @MediumTest
     @Feature({"AndroidWebView"})
+    @DisabledTest(message = "https://crbug.com/370449449")
     public void testShouldOverrideUrlLoading() throws InterruptedException {
         final CountDownLatch latch = new CountDownLatch(1);
         final String msg = "dies.";
diff --git a/android_webview/test/shared.gni b/android_webview/test/shared.gni
index 1738ded..c23a5ec 100644
--- a/android_webview/test/shared.gni
+++ b/android_webview/test/shared.gni
@@ -39,6 +39,7 @@
              "//components/policy/android:policy_java_test_support",
              "//content/public/android:content_java",
              "//third_party/androidx:androidx_annotation_annotation_java",
+             "//third_party/androidx:androidx_core_core_java",
              "//third_party/androidx:androidx_test_monitor_java",
              "//third_party/androidx_javascriptengine:javascriptengine_java",
              "//third_party/jni_zero:jni_zero_java",
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestRunnerActivity.java b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestRunnerActivity.java
index 8f52d098..32b53ad9 100644
--- a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestRunnerActivity.java
+++ b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestRunnerActivity.java
@@ -11,6 +11,10 @@
 import android.view.ViewGroup.LayoutParams;
 import android.widget.LinearLayout;
 
+import androidx.core.graphics.Insets;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
+
 import org.chromium.android_webview.AwBrowserProcess;
 import org.chromium.android_webview.shell.AwShellResourceProvider;
 import org.chromium.base.StrictModeContext;
@@ -41,6 +45,7 @@
         hideActionBarIfNecessary();
 
         setContentView(mLinearLayout);
+        setupEdgeToEdge();
     }
 
     private void hideActionBarIfNecessary() {
@@ -86,4 +91,23 @@
     public void setIgnoreStartActivity(boolean ignore) {
         mIgnoreStartActivity = ignore;
     }
+
+    private void setupEdgeToEdge() {
+        ViewCompat.setOnApplyWindowInsetsListener(
+                mLinearLayout,
+                (v, windowInsets) -> {
+                    Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
+                    // Ensure the LinearLayout (container) view does not overlap with the system
+                    // status bar by adjusting its top padding based on the system window insets.
+                    v.setPadding(
+                            v.getPaddingLeft(),
+                            insets.top,
+                            v.getPaddingRight(),
+                            v.getPaddingBottom());
+
+                    // Return CONSUMED to indicate we have handled the insets for this view
+                    // and don't want them to be passed down to descendant views.
+                    return WindowInsetsCompat.CONSUMED;
+                });
+    }
 }
diff --git a/ash/webui/recorder_app_ui/resources/components/recording-title.ts b/ash/webui/recorder_app_ui/resources/components/recording-title.ts
index 4af666b..647d6f6 100644
--- a/ash/webui/recorder_app_ui/resources/components/recording-title.ts
+++ b/ash/webui/recorder_app_ui/resources/components/recording-title.ts
@@ -230,7 +230,10 @@
 
   private onChangeTitle(ev: Event) {
     const target = assertInstanceof(ev.target, Textfield);
-    this.setTitle(target.value);
+    // Change title when the text field is not empty.
+    if (target.value !== '') {
+      this.setTitle(target.value);
+    }
   }
 
   private onSuggestTitle(ev: CustomEvent<string>) {
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 9b52a9b..8cf35e71 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -294,6 +294,7 @@
     "export_template.h",
     "feature_list.cc",
     "feature_list.h",
+    "feature_visitor.h",
     "features.cc",
     "features.h",
     "file_version_info.h",
@@ -1002,10 +1003,6 @@
     ]
   }
 
-  if (is_chromeos_ash) {
-    sources += [ "feature_visitor.h" ]
-  }
-
   if (is_linux || is_chromeos || is_android) {
     sources += [
       "files/file_path_watcher_inotify.cc",
diff --git a/base/feature_list.cc b/base/feature_list.cc
index e981c804..75dc0b1f 100644
--- a/base/feature_list.cc
+++ b/base/feature_list.cc
@@ -2,24 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/location.h"
 #ifdef UNSAFE_BUFFERS_BUILD
 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
 #pragma allow_unsafe_buffers
 #endif
 
-#include "base/feature_list.h"
-
 #include <stddef.h>
 
+#include <algorithm>
 #include <string>
 #include <string_view>
 #include <tuple>
 
 #include "base/base_switches.h"
+#include "base/check_is_test.h"
 #include "base/containers/contains.h"
 #include "base/containers/span.h"
 #include "base/debug/crash_logging.h"
 #include "base/debug/dump_without_crashing.h"
+#include "base/feature_list.h"
+#include "base/feature_visitor.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
@@ -37,10 +40,6 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "base/feature_visitor.h"
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
 namespace base {
 
 namespace {
@@ -687,14 +686,39 @@
   allowed_feature_names_.insert(std::move(feature_name));
 }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
 // static
-void FeatureList::VisitFeaturesAndParams(FeatureVisitor& visitor) {
-  CHECK(g_feature_list_instance);
+void FeatureList::VisitFeaturesAndParams(FeatureVisitor& visitor,
+                                         std::string_view filter_prefix) {
+  // If there is no feature list, there are no overrides. This should only happen
+  // in tests.
+  // TODO(leszeks): Add a CHECK_IS_TEST() to verify the above.
+  if (!g_feature_list_instance) {
+    return;
+  }
+
   FieldTrialParamAssociator* params_associator =
       FieldTrialParamAssociator::GetInstance();
 
-  for (auto& feature_override : g_feature_list_instance->overrides_) {
+  using FeatureOverride = std::pair<std::string, OverrideEntry>;
+  base::span<FeatureOverride> filtered_overrides(
+      g_feature_list_instance->overrides_);
+  if (!filter_prefix.empty()) {
+    // If there is a filter prefix, then change the begin/end range to be the
+    // range where the values are prefixed with the given prefix (overrides are
+    // lexically sorted, so this will be a continuous range). This is
+    // implemented as a binary search of the upper and lower bounds of the
+    // override iterator, projecting each iterator value to just the
+    // key, trimmed to the length of the prefix.
+    DCHECK(std::ranges::is_sorted(
+        filtered_overrides, std::less<>(),
+        [](const FeatureOverride& entry) { return entry.first; }));
+    filtered_overrides = std::ranges::equal_range(
+        filtered_overrides, filter_prefix, std::less<>(),
+        [filter_prefix](const FeatureOverride& entry) {
+          return std::string_view(entry.first).substr(0, filter_prefix.size());
+        });
+  }
+  for (const FeatureOverride& feature_override : filtered_overrides) {
     FieldTrial* field_trial = feature_override.second.field_trial;
 
     std::string trial_name;
@@ -712,7 +736,6 @@
                   group_name);
   }
 }
-#endif  // BULDFLAG(IS_CHROMEOS_ASH)
 
 void FeatureList::FinalizeInitialization() {
   DCHECK(!initialized_);
diff --git a/base/feature_list.h b/base/feature_list.h
index 602fbce..8e59551 100644
--- a/base/feature_list.h
+++ b/base/feature_list.h
@@ -26,17 +26,13 @@
 #include "base/memory/raw_ptr.h"
 #include "base/synchronization/lock.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 
 namespace base {
 
 class FieldTrial;
 class FieldTrialList;
 class PersistentMemoryAllocator;
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
 class FeatureVisitor;
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 // Specifies whether a given feature is enabled or disabled by default.
 // NOTE: The actual runtime state may be different, due to a field trial or a
@@ -579,15 +575,15 @@
   // only be called on a FeatureList that was set with SetEarlyAccessInstance().
   void AddEarlyAllowedFeatureForTesting(std::string feature_name);
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
   // Allows a visitor to record override state, parameters, and field trial
-  // associated with each feature.
+  // associated with each feature. Optionally, provide a prefix which filters
+  // the visited features.
   //
   // NOTE: This is intended only for the special case of needing to get all
-  // overrides. This use case is specific to CrOS-Ash. Most users should call
-  // IsEnabled() to query a feature's state.
-  static void VisitFeaturesAndParams(FeatureVisitor& visitor);
-#endif  // BULDFLAG(IS_CHROMEOS_ASH)
+  // overrides. This use case is specific to CrOS-Ash and V8. Most users should
+  // call IsEnabled() to query a feature's state.
+  static void VisitFeaturesAndParams(FeatureVisitor& visitor,
+                                     std::string_view filter_prefix = "");
 
  private:
   FRIEND_TEST_ALL_PREFIXES(FeatureListTest, CheckFeatureIdentity);
diff --git a/base/feature_list_unittest.cc b/base/feature_list_unittest.cc
index 2bb02ed..618078b 100644
--- a/base/feature_list_unittest.cc
+++ b/base/feature_list_unittest.cc
@@ -19,6 +19,7 @@
 #include <vector>
 
 #include "base/feature_list_buildflags.h"
+#include "base/feature_visitor.h"
 #include "base/format_macros.h"
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/metrics/field_trial.h"
@@ -32,10 +33,6 @@
 #include "build/chromeos_buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "base/feature_visitor.h"
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
 namespace base {
 
 namespace {
@@ -851,7 +848,6 @@
   }
 }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
 // Test only class to verify correctness of
 // FeatureList::VisitFeaturesAndParams().
 class TestFeatureVisitor : public FeatureVisitor {
@@ -979,6 +975,35 @@
 
   EXPECT_EQ(actual_feature_state, expected_feature_state);
 }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+TEST(TestFeatureVisitor, FeatureWithPrefix) {
+  base::test::ScopedFeatureList outer_scope;
+  outer_scope.InitWithEmptyFeatureAndFieldTrialLists();
+
+  base::test::ScopedFeatureList initialized_feature_list;
+
+  initialized_feature_list.InitFromCommandLine(
+      /*enable_features=*/
+      "AFeature,AnotherFeature,TestFeature,TestFeature2,PrefixedFeature,"
+      "PrefixedFeature2",
+      /*disable_features=*/"");
+
+  TestFeatureVisitor visitor;
+  base::FeatureList::VisitFeaturesAndParams(visitor, "Prefixed");
+  std::multiset<TestFeatureVisitor::VisitedFeatureState> actual_feature_state =
+      visitor.feature_state();
+
+  std::multiset<TestFeatureVisitor::VisitedFeatureState>
+      expected_feature_state = {
+          {"PrefixedFeature",
+           FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE,
+           FieldTrialParams{}, "", ""},
+          {"PrefixedFeature2",
+           FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE,
+           FieldTrialParams{}, "", ""},
+      };
+
+  EXPECT_EQ(actual_feature_state, expected_feature_state);
+}
 
 }  // namespace base
diff --git a/base/feature_visitor.h b/base/feature_visitor.h
index b85440d..47732397 100644
--- a/base/feature_visitor.h
+++ b/base/feature_visitor.h
@@ -9,21 +9,21 @@
 #include <string>
 
 #include "base/feature_list.h"
-#include "build/chromeos_buildflags.h"
-
-static_assert(BUILDFLAG(IS_CHROMEOS_ASH), "For ChromeOS ash-chrome only");
 
 namespace variations::cros_early_boot::evaluate_seed {
 class EarlyBootFeatureVisitor;
 }
 
+namespace gin {
+class V8FeatureVisitor;
+}
+
 namespace base {
 
 class TestFeatureVisitor;
 
-// An interface for EarlyBootFeatureVisitor that provides a method to
-// iterate over a feature's name, override state, parameters, and associated
-// field trial.
+// An interface for FeatureList that provides a method to iterate over a
+// feature's name, override state, parameters, and associated field trial.
 //
 // NOTE: This is intended only for the special case of needing to get all
 // feature overrides. Most users should call FeatureList::IsEnabled() to query
@@ -45,6 +45,7 @@
 
  private:
   friend variations::cros_early_boot::evaluate_seed::EarlyBootFeatureVisitor;
+  friend gin::V8FeatureVisitor;
   friend TestFeatureVisitor;
 
   // The constructor is private so only friend classes can inherit from this
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index 0cddd96..965206e 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -247,12 +247,12 @@
 class ScopedAllowWaitForDebugURL;
 class ServiceWorkerContextClient;
 class ShellPathProvider;
+class SlowWebPreferenceCache;
 class SynchronousCompositor;
 class SynchronousCompositorHost;
 class SynchronousCompositorSyncCallBridge;
 class ScopedAllowBlockingForViewAura;
 class TextInputClientMac;
-class WebContentsImpl;
 class WebContentsViewMac;
 base::File CreateFileForDrop(base::FilePath*);
 }  // namespace content
@@ -658,7 +658,7 @@
 #endif
 #if BUILDFLAG(IS_WIN)
   friend class base::win::OSInfo;
-  friend class content::WebContentsImpl;  // http://crbug.com/1262162
+  friend class content::SlowWebPreferenceCache;  // http://crbug.com/1262162
   friend class media::GpuMojoMediaClientWin;  // https://crbug.com/360642944
 #endif
 #if BUILDFLAG(IS_IOS)
diff --git a/base/tracing/protos/chrome_track_event.proto b/base/tracing/protos/chrome_track_event.proto
index badb31d..79972c2e 100644
--- a/base/tracing/protos/chrome_track_event.proto
+++ b/base/tracing/protos/chrome_track_event.proto
@@ -1466,6 +1466,10 @@
   // `viz::BeginFrameAck.trace_id`s for frames aggregated at this step.
   // Used with `STEP_SURFACE_AGGREGATION` only.
   repeated int64 aggregated_frames_ids = 8;
+
+  // List of LatencyInfo.trace_ids associated with this frame, which
+  // should allow us to match input to frame production.
+  repeated int64 latency_ids = 9;
 };
 
 message LibunwindstackUnwinder {
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index db98b23..c0951f6 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2775,7 +2775,7 @@
 
 viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame(
     FrameData* frame) {
-  TRACE_EVENT(
+  TRACE_EVENT_BEGIN(
       "viz,benchmark,graphics.pipeline", "Graphics.Pipeline",
       perfetto::Flow::Global(CurrentBeginFrameArgs().trace_id),
       [&](perfetto::EventContext ctx) {
@@ -3033,6 +3033,20 @@
         total_quad_count);
   }
 
+  // TODO(b/368050735): future-proof this event against early returns.
+  TRACE_EVENT_END("viz,benchmark,graphics.pipeline",
+                  perfetto::Flow::Global(CurrentBeginFrameArgs().trace_id),
+                  [&](perfetto::EventContext ctx) {
+                    auto* event =
+                        ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
+                    auto* data = event->set_chrome_graphics_pipeline();
+
+                    for (const ui::LatencyInfo& latency :
+                         compositor_frame.metadata.latency_info) {
+                      data->add_latency_ids(latency.trace_id());
+                    }
+                  });
+
   return compositor_frame;
 }
 
diff --git a/chrome/VERSION b/chrome/VERSION
index 8b5b22a..de39e62c 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=131
 MINOR=0
-BUILD=6749
+BUILD=6750
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 94aedf5..215c326 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1037,6 +1037,7 @@
       "//chrome/browser/device:java",
       "//chrome/browser/device:junit",
       "//chrome/browser/device_reauth/android:java",
+      "//chrome/browser/device_reauth/android:junit",
       "//chrome/browser/download/android:java",
       "//chrome/browser/download/android:junit_tests",
       "//chrome/browser/download/internal/android:junit",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
index f1d7d2e1..62a9946 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
@@ -72,7 +72,6 @@
 import org.chromium.chrome.browser.translate.TranslateBridge;
 import org.chromium.content_public.browser.Visibility;
 import org.chromium.content_public.browser.WebContents;
-import org.chromium.net.NetId;
 import org.chromium.ui.base.ActivityWindowAndroid;
 
 import java.lang.annotation.Retention;
@@ -497,13 +496,10 @@
             return webContents;
         }
 
-        // Check if any available target network specified via customTabsIntent before creating
-        // web contents, and we only use the spare web contents if the provided network is the
-        // default one.
+        // Multi-network CCT does not support spare web contents.
         // TODO: this check can be removed once the spare web contents can be created with a
         // particular target network as well, e.g. via {@link CustomTabsSession#mayLaunchUrl}.
-        long targetNetwork = mIntentDataProvider.getTargetNetwork();
-        if (targetNetwork == NetId.INVALID) {
+        if (!mIntentDataProvider.hasTargetNetwork()) {
             webContents =
                     mWarmupManager.takeSpareWebContents(
                             mIntentDataProvider.isOffTheRecord(), /* initiallyHidden= */ false);
@@ -518,12 +514,18 @@
                 ProfileProvider.getOrCreateProfile(
                         mProfileProviderSupplier.get(), mIntentDataProvider.isOffTheRecord()),
                 /* initiallyHidden= */ false,
-                targetNetwork);
+                mIntentDataProvider.getTargetNetwork());
     }
 
     private @Nullable WebContents takeAsyncWebContents() {
         // Async WebContents are not supported for Incognito/Ephemeral CCT.
         if (mIntentDataProvider.isOffTheRecord()) return null;
+        // Async WebContents are not supported for multi-network CCT. In this case it's better to
+        // always create WebContents from scratch, otherwise we might break the "WebContents
+        // associated with a CCT tab targeting a network will always have
+        // WebContents::GetTargetNetwork == that target network" invariant (see
+        // WebContentsImpl::CreateWithOpener for more info).
+        if (mIntentDataProvider.hasTargetNetwork()) return null;
         int assignedTabId = IntentHandler.getTabId(mIntent);
         AsyncTabParams asyncParams = mAsyncTabParamsManager.get().remove(assignedTabId);
         if (asyncParams == null) return null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
index 5ab6bef..ee1f2fa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
@@ -855,6 +855,7 @@
                 .hasPrimaryAccount(ConsentLevel.SYNC)) {
             return;
         }
+        // TODO: crbug.com/343933167 - Stop suppressing the snackbar.
         SignOutCoordinator.startSignOutFlow(
                 requireContext(),
                 getProfile(),
@@ -863,7 +864,8 @@
                 mSnackbarManagerSupplier.get(),
                 SignoutReason.USER_CLICKED_SIGNOUT_SETTINGS,
                 /* showConfirmDialog= */ false,
-                () -> {});
+                () -> {},
+                /* suppressSnackbar= */ true);
     }
 
     private void onTurnOffSyncClicked() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java
index ca9311e..48717bb 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java
@@ -10,6 +10,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.AdditionalMatchers.not;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -199,15 +200,14 @@
     }
 
     @Test
-    public void usesWebContentsCreatedWithWarmRenderer_basedOnParticularNetworkHandle() {
-        WebContents webContents = mock(WebContents.class);
+    public void propagatesTargetNetworkCorrectly_whenIntentDataProviderTargetsNetwork() {
         when(env.intentDataProvider.getTargetNetwork()).thenReturn(TEST_TARGET_NETWORK);
-        when(env.webContentsFactory.createWebContentsWithWarmRenderer(
-                        any(), anyBoolean(), eq(TEST_TARGET_NETWORK)))
-                .thenReturn(webContents);
         env.reachNativeInit(mTabController);
-        verify(env.warmupManager, never()).takeSpareWebContents(env.isOffTheRecord, false);
-        assertEquals(webContents, env.webContentsCaptor.getValue());
+        verify(env.webContentsFactory, never())
+                .createWebContentsWithWarmRenderer(
+                        any(), anyBoolean(), not(eq(TEST_TARGET_NETWORK)));
+        verify(env.webContentsFactory)
+                .createWebContentsWithWarmRenderer(any(), anyBoolean(), eq(TEST_TARGET_NETWORK));
     }
 
     @Test
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 13aa73f2..b8940c7 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -1501,6 +1501,9 @@
       <message name="IDS_SYNC_ERROR_PASSPHRASE_USER_MENU_TITLE_SIGNED_IN_ONLY" desc="Title of the sync/signin error header of desktop user menu. Shown for users who are signed-in but not syncing.">
         To use and save Chromium data in your Google Account, enter your passphrase
       </message>
+       <message name="IDS_SYNC_ERROR_UPGRADE_CLIENT_USER_MENU_TITLE" desc="Error message to resolve sync protocol error for out-of-date client.">
+        To use and save Chromium data in your Google Account, update Chromium
+      </message>
 
       <!-- Sync errors. Android uses native UI to handle Sync settings -->
       <if expr="not is_android">
diff --git a/chrome/app/chromium_strings_grd/IDS_SYNC_ERROR_UPGRADE_CLIENT_USER_MENU_TITLE.png.sha1 b/chrome/app/chromium_strings_grd/IDS_SYNC_ERROR_UPGRADE_CLIENT_USER_MENU_TITLE.png.sha1
new file mode 100644
index 0000000..b538191
--- /dev/null
+++ b/chrome/app/chromium_strings_grd/IDS_SYNC_ERROR_UPGRADE_CLIENT_USER_MENU_TITLE.png.sha1
@@ -0,0 +1 @@
+3f5d0127030800e176430520f3e8dc408dac91b9
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 189f399..df86b00 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -1662,6 +1662,9 @@
       <message name="IDS_SYNC_ERROR_PASSPHRASE_USER_MENU_TITLE_SIGNED_IN_ONLY" desc="Title of the sync/signin error header of desktop user menu. Shown for users who are signed-in but not syncing.">
         To use and save Chrome data in your Google Account, enter your passphrase
       </message>
+      <message name="IDS_SYNC_ERROR_UPGRADE_CLIENT_USER_MENU_TITLE" desc="Error message to resolve sync protocol error for out-of-date client.">
+        To use and save Chrome data in your Google Account, update Chrome
+      </message>
 
       <!-- Sync errors. Android uses native UI to handle Sync settings -->
       <if expr="not is_android">
diff --git a/chrome/app/google_chrome_strings_grd/IDS_SYNC_ERROR_UPGRADE_CLIENT_USER_MENU_TITLE.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_SYNC_ERROR_UPGRADE_CLIENT_USER_MENU_TITLE.png.sha1
new file mode 100644
index 0000000..b538191
--- /dev/null
+++ b/chrome/app/google_chrome_strings_grd/IDS_SYNC_ERROR_UPGRADE_CLIENT_USER_MENU_TITLE.png.sha1
@@ -0,0 +1 @@
+3f5d0127030800e176430520f3e8dc408dac91b9
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 64a2cf1..847f4e5 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2848,6 +2848,12 @@
   <message name="IDS_SITE_SETTINGS_SMART_CARDS_BLOCKED" desc="Option to block Isolated Web Apps from controlling smart card readers.">
     Isolated Web Apps cannot control smart card readers. Apps won't gain access to any smart card.
   </message>
+  <message name="IDS_SITE_SETTINGS_NO_SMART_CARD_READERS_FOUND" desc="No smart card readers were found.">
+    No smart card readers found.
+  </message>
+  <message name="IDS_SITE_SETTINGS_RESET_SMART_CARD_CONFIRMATION" desc="Confirmation for resetting smart card reader permissions.">
+    Reset all smart card reader permissions?
+  </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_IMAGES_DESCRIPTION" desc="Description of the images content setting.">
     Sites usually show images to provide illustration, like photos for online stores or news articles
   </message>
diff --git a/chrome/app/settings_strings_grdp/IDS_SITE_SETTINGS_NO_SMART_CARD_READERS_FOUND.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SITE_SETTINGS_NO_SMART_CARD_READERS_FOUND.png.sha1
new file mode 100644
index 0000000..702c61e
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SITE_SETTINGS_NO_SMART_CARD_READERS_FOUND.png.sha1
@@ -0,0 +1 @@
+63326cc9bad2ccae4ab45ae7160dda40afac07a5
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SITE_SETTINGS_RESET_SMART_CARD_CONFIRMATION.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SITE_SETTINGS_RESET_SMART_CARD_CONFIRMATION.png.sha1
new file mode 100644
index 0000000..401cc0f
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SITE_SETTINGS_RESET_SMART_CARD_CONFIRMATION.png.sha1
@@ -0,0 +1 @@
+67e7e1e5cb6787dadcd139b823c75c9481094e38
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index bde76bd..f82a791 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1974,6 +1974,7 @@
     "//chrome/browser/ui/prefs:impl",
     "//chrome/browser/ui/safety_hub",
     "//chrome/browser/ui/safety_hub:impl",
+    "//chrome/browser/ui/startup:startup_tab",
     "//chrome/browser/ui/tabs",
     "//chrome/browser/ui/webui",
     "//chrome/browser/ui/webui:configs",
@@ -4253,6 +4254,7 @@
       "//chrome/browser/ui/tabs:tabs",
       "//chrome/browser/ui/toasts",
       "//chrome/browser/ui/toasts/api:toasts",
+      "//chrome/browser/ui/toolbar/cast:impl",
       "//chrome/browser/ui/user_education",
       "//chrome/browser/ui/views/side_panel",
       "//chrome/browser/ui/views/toolbar",
@@ -4391,6 +4393,10 @@
       # gets modularized.
       "//chrome/browser/ui/global_media_controls:impl",
 
+      # TODO(crbug.com/369794212): Remove this circular dependency when //c/b/signin/identity_manager_factory.h
+      # gets modularized.
+      "//chrome/browser/ui/toolbar/cast:impl",
+
       "//chrome/browser/task_manager:impl",
       "//chrome/browser/ui/exclusive_access",
       "//chrome/browser/ui/views/toolbar",
diff --git a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java
index f00811a..a2f969f7 100644
--- a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java
+++ b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java
@@ -647,13 +647,22 @@
     }
 
     /**
-     * Return the target network that should be used from this intent, the default value to be used
-     * when a network has not been explicitly set via intent.
+     * Return the target network handle {@link android.net.Network#getNetworkHandle} that loads
+     * associated with this intent must use. Defaults to {@code NetId.INVALID}, in which case we let
+     * the underlying system make this choice.
      */
     public long getTargetNetwork() {
         return NetId.INVALID;
     }
 
+    /**
+     * Return whether this intent has a target network. Certain optimizations or features are not
+     * support for tabs targeting a network. This helper is useful for handling those scenarios.
+     */
+    public boolean hasTargetNetwork() {
+        return getTargetNetwork() != NetId.INVALID;
+    }
+
     /** Return {@code true} if the service was launched for authentication. */
     public boolean isAuthTab() {
         return false;
diff --git a/chrome/browser/ash/app_restore/BUILD.gn b/chrome/browser/ash/app_restore/BUILD.gn
index fc75ce54..9fe2dd3 100644
--- a/chrome/browser/ash/app_restore/BUILD.gn
+++ b/chrome/browser/ash/app_restore/BUILD.gn
@@ -94,6 +94,7 @@
     "//chrome/browser/ash/policy/scheduled_task_handler",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/chromeos/full_restore",
+    "//chrome/browser/ui/startup:startup_tab",
     "//chrome/browser/web_applications",
     "//chrome/common:chrome_features",
     "//chrome/common:constants",
diff --git a/chrome/browser/ash/arc/util/arc_window_watcher.cc b/chrome/browser/ash/arc/util/arc_window_watcher.cc
index b289b722..a0038851 100644
--- a/chrome/browser/ash/arc/util/arc_window_watcher.cc
+++ b/chrome/browser/ash/arc/util/arc_window_watcher.cc
@@ -20,18 +20,9 @@
 
 namespace {
 
-bool IgnoreWindow(aura::Window* window) {
-  // Lacros browser windows:
-  if (crosapi::browser_util::IsLacrosWindow(window)) {
-    return true;
-  }
-
-  // Ash browser windows:
-  if (chrome::FindBrowserWithWindow(window)) {
-    return true;
-  }
-
-  return false;
+bool ShouldIgnoreWindow(aura::Window* window) {
+  // ArcWindowWatcher doesn't interested in Ash browser windows.
+  return chrome::FindBrowserWithWindow(window);
 }
 
 class Tracker : public aura::WindowObserver {
@@ -83,7 +74,7 @@
       return;
     }
 
-    if (IgnoreWindow(window)) {
+    if (ShouldIgnoreWindow(window)) {
       ash::ArcWindowWatcher::instance()->OnTrackerRemoved(this, nullptr);
       // WARNING: this is deleted here - must return immediately.
       return;
@@ -147,7 +138,7 @@
     return;
   }
 
-  if (IgnoreWindow(window)) {
+  if (ShouldIgnoreWindow(window)) {
     return;
   }
 
diff --git a/chrome/browser/ash/login/enrollment/enrollment_screen.cc b/chrome/browser/ash/login/enrollment/enrollment_screen.cc
index e81db6f..aa20053 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_screen.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_screen.cc
@@ -180,32 +180,28 @@
 void EnrollmentScreen::SetEnrollmentConfig(
     const policy::EnrollmentConfig& enrollment_config) {
   prescribed_config_ = enrollment_config;
-  switch (prescribed_config_.auth_mechanism) {
-    case EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE:
+  if (prescribed_config_.is_mode_oauth()) {
+    current_auth_ = AUTH_OAUTH;
+    next_auth_ = AUTH_OAUTH;
+  } else if (prescribed_config_.is_mode_attestation()) {
+    if (TestForcesManualEnrollment()) {
       current_auth_ = AUTH_OAUTH;
       next_auth_ = AUTH_OAUTH;
-      break;
-    case EnrollmentConfig::AUTH_MECHANISM_ATTESTATION:
+    } else if (prescribed_config_.is_mode_with_manual_fallback()) {
+      current_auth_ = AUTH_ATTESTATION;
+      next_auth_ = AUTH_OAUTH;
+    } else {
       current_auth_ = AUTH_ATTESTATION;
       next_auth_ = AUTH_ATTESTATION;
-      break;
-    case EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED:
-      if (TestForcesManualEnrollment()) {
-        current_auth_ = AUTH_OAUTH;
-        next_auth_ = AUTH_OAUTH;
-        break;
-      }
-      current_auth_ = AUTH_ATTESTATION;
-      next_auth_ = AUTH_OAUTH;
-      break;
-    case EnrollmentConfig::AUTH_MECHANISM_TOKEN_PREFERRED:
-      current_auth_ = AUTH_ENROLLMENT_TOKEN;
-      next_auth_ = AUTH_OAUTH;
-      break;
-    default:
-      NOTREACHED_IN_MIGRATION();
-      break;
+    }
+  } else if (prescribed_config_.is_mode_token()) {
+    current_auth_ = AUTH_ENROLLMENT_TOKEN;
+    next_auth_ = AUTH_OAUTH;
+  } else {
+    NOTREACHED() << "EnrollmentConfig does not match any auth: "
+                 << prescribed_config_;
   }
+
   SetConfig();
 }
 
diff --git a/chrome/browser/ash/login/enrollment/enrollment_screen_browsertest.cc b/chrome/browser/ash/login/enrollment/enrollment_screen_browsertest.cc
index 9eca6657..3b61132f 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_screen_browsertest.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_screen_browsertest.cc
@@ -123,12 +123,9 @@
     return signin_artifacts;
   }
 
-  policy::EnrollmentConfig CreateConfig(
-      policy::EnrollmentConfig::Mode mode,
-      policy::EnrollmentConfig::AuthMechanism auth_mechanism) {
+  policy::EnrollmentConfig CreateConfig(policy::EnrollmentConfig::Mode mode) {
     policy::EnrollmentConfig config;
     config.mode = mode;
-    config.auth_mechanism = auth_mechanism;
     return config;
   }
 
@@ -176,8 +173,6 @@
   policy::EnrollmentConfig enrollment_config;
   enrollment_config.mode =
       policy::EnrollmentConfig::MODE_ATTESTATION_ROLLBACK_FORCED;
-  enrollment_config.auth_mechanism =
-      policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED;
 
   enrollment_helper_.ExpectEnrollmentMode(
       policy::EnrollmentConfig::MODE_ATTESTATION_ROLLBACK_FORCED);
@@ -201,8 +196,6 @@
   policy::EnrollmentConfig enrollment_config;
   enrollment_config.mode =
       policy::EnrollmentConfig::MODE_ATTESTATION_ROLLBACK_FORCED;
-  enrollment_config.auth_mechanism =
-      policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED;
 
   enrollment_helper_.ExpectEnrollmentMode(
       policy::EnrollmentConfig::MODE_ATTESTATION_ROLLBACK_FORCED);
@@ -248,8 +241,6 @@
   policy::EnrollmentConfig enrollment_config;
   enrollment_config.mode =
       policy::EnrollmentConfig::MODE_ATTESTATION_ROLLBACK_FORCED;
-  enrollment_config.auth_mechanism =
-      policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED;
 
   enrollment_helper_.ExpectEnrollmentMode(
       policy::EnrollmentConfig::MODE_ATTESTATION_ROLLBACK_FORCED);
@@ -291,8 +282,6 @@
   policy::EnrollmentConfig enrollment_config;
   enrollment_config.mode =
       policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED;
-  enrollment_config.auth_mechanism =
-      policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED;
 
   enrollment_helper_.ExpectEnrollmentMode(
       policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED);
@@ -315,8 +304,6 @@
   policy::EnrollmentConfig enrollment_config;
   enrollment_config.mode =
       policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED;
-  enrollment_config.auth_mechanism =
-      policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED;
 
   enrollment_helper_.ExpectEnrollmentMode(
       policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED);
@@ -359,8 +346,6 @@
   enrollment_ui_.SetExitHandler();
   policy::EnrollmentConfig enrollment_config;
   enrollment_config.mode = policy::EnrollmentConfig::MODE_MANUAL;
-  enrollment_config.auth_mechanism =
-      policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE;
 
   enrollment_config.is_license_packaged_with_device = true;
 
@@ -390,8 +375,6 @@
 
   // Set enrollment config to Forced Re-enrollment.
   enrollment_config.mode = policy::EnrollmentConfig::MODE_LOCAL_FORCED;
-  enrollment_config.auth_mechanism =
-      policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE;
 
   enrollment_config.is_license_packaged_with_device = true;
 
@@ -418,8 +401,6 @@
   enrollment_ui_.SetExitHandler();
   policy::EnrollmentConfig enrollment_config;
   enrollment_config.mode = policy::EnrollmentConfig::MODE_MANUAL;
-  enrollment_config.auth_mechanism =
-      policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE;
 
   enrollment_config.is_license_packaged_with_device = true;
 
@@ -450,8 +431,6 @@
   enrollment_ui_.SetExitHandler();
   policy::EnrollmentConfig enrollment_config;
   enrollment_config.mode = policy::EnrollmentConfig::MODE_MANUAL;
-  enrollment_config.auth_mechanism =
-      policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE;
 
   enrollment_config.is_license_packaged_with_device = false;
 
@@ -505,9 +484,8 @@
   WizardContext context;
   enrollment_helper_.ExpectAttestationEnrollmentSuccess();
   enrollment_helper_.DisableAttributePromptUpdate();
-  enrollment_screen()->SetEnrollmentConfig(CreateConfig(
-      policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED,
-      policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED));
+  enrollment_screen()->SetEnrollmentConfig(
+      CreateConfig(policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED));
   enrollment_screen()->Show(&context);
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
 
@@ -524,8 +502,7 @@
                        ShowsWorkingStepOnAttestationFlow) {
   WizardContext context;
   enrollment_screen()->SetEnrollmentConfig(
-      CreateConfig(policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED,
-                   policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION));
+      CreateConfig(policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED));
 
   enrollment_screen()->Show(&context);
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepWorking);
@@ -536,11 +513,10 @@
   WizardContext context;
   enrollment_helper_.ExpectAttestationEnrollmentError(
       policy::EnrollmentStatus::ForRegistrationError(
-          policy::DeviceManagementStatus::DM_STATUS_SERVICE_DEVICE_NOT_FOUND));
+          policy::DeviceManagementStatus::DM_STATUS_TEMPORARY_UNAVAILABLE));
   enrollment_helper_.SetupClearAuth();
   enrollment_screen()->SetEnrollmentConfig(
-      CreateConfig(policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED,
-                   policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION));
+      CreateConfig(policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED));
   enrollment_screen()->Show(&context);
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
 
@@ -554,8 +530,6 @@
   policy::EnrollmentConfig enrollment_config;
   enrollment_config.mode =
       policy::EnrollmentConfig::MODE_ENROLLMENT_TOKEN_INITIAL_SERVER_FORCED;
-  enrollment_config.auth_mechanism =
-      policy::EnrollmentConfig::AUTH_MECHANISM_TOKEN_PREFERRED;
   enrollment_config.enrollment_token = kTestEnrollmentToken;
 
   enrollment_helper_.ExpectEnrollmentTokenConfig(kTestEnrollmentToken);
@@ -580,8 +554,6 @@
   policy::EnrollmentConfig enrollment_config;
   enrollment_config.mode =
       policy::EnrollmentConfig::MODE_ENROLLMENT_TOKEN_INITIAL_SERVER_FORCED;
-  enrollment_config.auth_mechanism =
-      policy::EnrollmentConfig::AUTH_MECHANISM_TOKEN_PREFERRED;
   enrollment_config.enrollment_token = kTestEnrollmentToken;
 
   enrollment_helper_.ExpectEnrollmentTokenConfig(kTestEnrollmentToken);
@@ -624,7 +596,6 @@
 
 struct EnrollmentErrorScreenTestParams {
   policy::EnrollmentConfig::Mode enrollment_mode;
-  policy::EnrollmentConfig::AuthMechanism enrollment_auth_mechanism;
 };
 
 class EnrollmentErrorScreenTest
@@ -634,7 +605,6 @@
   policy::EnrollmentConfig GetEnrollmentConfigParam() {
     policy::EnrollmentConfig config;
     config.mode = GetParam().enrollment_mode;
-    config.auth_mechanism = GetParam().enrollment_auth_mechanism;
     return config;
   }
 
@@ -755,31 +725,22 @@
     ForcedEnrollment,
     ManualEnrollmentErrorScreenTest,
     testing::ValuesIn(std::vector<EnrollmentErrorScreenTestParams>{
-        {policy::EnrollmentConfig::MODE_LOCAL_FORCED,
-         policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE},
-        {policy::EnrollmentConfig::MODE_SERVER_FORCED,
-         policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE},
-        {policy::EnrollmentConfig::MODE_RECOVERY,
-         policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE},
-        {policy::EnrollmentConfig::MODE_ATTESTATION_MANUAL_FALLBACK,
-         policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE},
-        {policy::EnrollmentConfig::MODE_ATTESTATION_INITIAL_MANUAL_FALLBACK,
-         policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE},
-        {policy::EnrollmentConfig::MODE_ATTESTATION_ROLLBACK_MANUAL_FALLBACK,
-         policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE}}));
+        {policy::EnrollmentConfig::MODE_LOCAL_FORCED},
+        {policy::EnrollmentConfig::MODE_SERVER_FORCED},
+        {policy::EnrollmentConfig::MODE_RECOVERY},
+        {policy::EnrollmentConfig::MODE_ATTESTATION_MANUAL_FALLBACK},
+        {policy::EnrollmentConfig::MODE_ATTESTATION_INITIAL_MANUAL_FALLBACK},
+        {policy::EnrollmentConfig::
+             MODE_ATTESTATION_ROLLBACK_MANUAL_FALLBACK}}));
 
 INSTANTIATE_TEST_SUITE_P(
     NotForcedEnrollment,
     ManualEnrollmentErrorScreenTest,
     testing::ValuesIn(std::vector<EnrollmentErrorScreenTestParams>{
-        {policy::EnrollmentConfig::MODE_MANUAL,
-         policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE},
-        {policy::EnrollmentConfig::MODE_MANUAL_REENROLLMENT,
-         policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE},
-        {policy::EnrollmentConfig::MODE_LOCAL_ADVERTISED,
-         policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE},
-        {policy::EnrollmentConfig::MODE_SERVER_ADVERTISED,
-         policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE}}));
+        {policy::EnrollmentConfig::MODE_MANUAL},
+        {policy::EnrollmentConfig::MODE_MANUAL_REENROLLMENT},
+        {policy::EnrollmentConfig::MODE_LOCAL_ADVERTISED},
+        {policy::EnrollmentConfig::MODE_SERVER_ADVERTISED}}));
 
 using AttestationEnrollmentErrorScreenTest = EnrollmentErrorScreenTest;
 
@@ -787,9 +748,7 @@
                        AttestationEnrollmentErrorAndScreenData) {
   enrollment_ui_.SetExitHandler();
   const policy::EnrollmentConfig enrollment_config = GetEnrollmentConfigParam();
-  ASSERT_NE(
-      enrollment_config.auth_mechanism,
-      policy::EnrollmentConfig::AuthMechanism::AUTH_MECHANISM_INTERACTIVE);
+  ASSERT_TRUE(enrollment_config.is_automatic_enrollment());
   ASSERT_TRUE(enrollment_config.is_mode_attestation());
 
   // The test expects the error screen to be shown. Avoid automatic fallback
@@ -855,21 +814,16 @@
     ForcedEnrollment,
     AttestationEnrollmentErrorScreenTest,
     testing::ValuesIn(std::vector<EnrollmentErrorScreenTestParams>{
-        {policy::EnrollmentConfig::MODE_ATTESTATION_LOCAL_FORCED,
-         policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION},
-        {policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED,
-         policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED},
-        {policy::EnrollmentConfig::MODE_ATTESTATION_ROLLBACK_FORCED,
-         policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED},
-        {policy::EnrollmentConfig::MODE_ATTESTATION_INITIAL_SERVER_FORCED,
-         policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED}}));
+        {policy::EnrollmentConfig::MODE_ATTESTATION_LOCAL_FORCED},
+        {policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED},
+        {policy::EnrollmentConfig::MODE_ATTESTATION_ROLLBACK_FORCED},
+        {policy::EnrollmentConfig::MODE_ATTESTATION_INITIAL_SERVER_FORCED}}));
 
 INSTANTIATE_TEST_SUITE_P(
     NotForcedEnrollment,
     AttestationEnrollmentErrorScreenTest,
     testing::ValuesIn(std::vector<EnrollmentErrorScreenTestParams>{
-        {policy::EnrollmentConfig::MODE_ATTESTATION,
-         policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED}}));
+        {policy::EnrollmentConfig::MODE_ATTESTATION}}));
 
 // Class to test TPM pre-enrollment check that happens only with
 // --tpm-is-dynamic switch enabled. Test parameter represents take TPM
diff --git a/chrome/browser/ash/login/enrollment/enrollment_screen_unittest.cc b/chrome/browser/ash/login/enrollment/enrollment_screen_unittest.cc
index c68652c..f4c6794 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_screen_unittest.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_screen_unittest.cc
@@ -260,25 +260,18 @@
             });
   }
 
-  void ExpectEnrollmentConfig(policy::EnrollmentConfig::Mode mode,
-                              policy::EnrollmentConfig::AuthMechanism auth) {
-    EXPECT_CALL(
-        mock_view_,
-        SetEnrollmentConfig(testing::AllOf(
-            testing::Field(&policy::EnrollmentConfig::mode, mode),
-            testing::Field(&policy::EnrollmentConfig::auth_mechanism, auth))));
+  void ExpectEnrollmentConfig(policy::EnrollmentConfig::Mode mode) {
+    EXPECT_CALL(mock_view_, SetEnrollmentConfig(testing::AllOf(testing::Field(
+                                &policy::EnrollmentConfig::mode, mode))));
   }
 
   void ExpectEnrollmentConfig(policy::EnrollmentConfig::Mode mode,
-                              policy::EnrollmentConfig::AuthMechanism auth,
                               std::string enrollment_token) {
-    EXPECT_CALL(
-        mock_view_,
-        SetEnrollmentConfig(testing::AllOf(
-            testing::Field(&policy::EnrollmentConfig::mode, mode),
-            testing::Field(&policy::EnrollmentConfig::auth_mechanism, auth),
-            testing::Field(&policy::EnrollmentConfig::enrollment_token,
-                           enrollment_token))));
+    EXPECT_CALL(mock_view_,
+                SetEnrollmentConfig(testing::AllOf(
+                    testing::Field(&policy::EnrollmentConfig::mode, mode),
+                    testing::Field(&policy::EnrollmentConfig::enrollment_token,
+                                   enrollment_token))));
   }
 
   void ExpectShowView() { EXPECT_CALL(mock_view_, Show()); }
@@ -426,8 +419,6 @@
   policy::EnrollmentConfig GetEnrollmentConfig() {
     policy::EnrollmentConfig config;
     config.mode = GetParamEnrollmentMode();
-    config.auth_mechanism =
-        policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE;
     DCHECK(!config.is_mode_attestation())
         << "Config must not be attestation: " << config;
 
@@ -453,7 +444,7 @@
 TEST_P(EnrollmentScreenManualFlowTest, ShouldFinishEnrollmentScreen) {
   const policy::EnrollmentConfig config = GetEnrollmentConfig();
 
-  ExpectEnrollmentConfig(config.mode, config.auth_mechanism);
+  ExpectEnrollmentConfig(config.mode);
 
   ExpectShowViewWithLogin();
   ExpectManualEnrollmentAndReportEnrolled();
@@ -471,7 +462,7 @@
 TEST_P(EnrollmentScreenManualFlowTest, ShouldNotAutomaticallyRetryEnrollment) {
   const policy::EnrollmentConfig config = GetEnrollmentConfig();
 
-  ExpectEnrollmentConfig(config.mode, config.auth_mechanism);
+  ExpectEnrollmentConfig(config.mode);
 
   ExpectShowViewWithLogin();
   ExpectManualEnrollmentAndReportFailure();
@@ -493,7 +484,7 @@
   {
     testing::InSequence s;
     // First view is shown for attestation-based failure.
-    ExpectEnrollmentConfig(config.mode, config.auth_mechanism);
+    ExpectEnrollmentConfig(config.mode);
     ExpectShowViewWithLogin();
     ExpectManualEnrollmentAndReportFailure();
     ExpectErrorScreen();
@@ -558,8 +549,6 @@
   policy::EnrollmentConfig GetManualConfig() {
     policy::EnrollmentConfig config;
     config.mode = policy::EnrollmentConfig::MODE_MANUAL;
-    config.auth_mechanism =
-        policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE;
     return config;
   }
 
@@ -692,8 +681,6 @@
   policy::EnrollmentConfig GetEnrollmentConfig() {
     policy::EnrollmentConfig config;
     config.mode = GetParam();
-    config.auth_mechanism =
-        policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED;
     DCHECK(config.is_mode_attestation())
         << "Config must be attestation: " << config;
 
@@ -708,7 +695,7 @@
 TEST_P(EnrollmentScreenAttestationFlowTest, ShouldFinishEnrollmentScreen) {
   const policy::EnrollmentConfig config = GetEnrollmentConfig();
 
-  ExpectEnrollmentConfig(config.mode, config.auth_mechanism);
+  ExpectEnrollmentConfig(config.mode);
 
   ExpectAttestationBasedEnrollmentAndReportEnrolled();
   ExpectGetDeviceAttributeUpdatePermission(/*permission_granted=*/false);
@@ -728,7 +715,7 @@
        ShouldNotAutomaticallyRetryEnrollment) {
   const policy::EnrollmentConfig config = GetEnrollmentConfig();
 
-  ExpectEnrollmentConfig(config.mode, config.auth_mechanism);
+  ExpectEnrollmentConfig(config.mode);
   ExpectAttestationBasedEnrollmentAndReportFailure();
   ExpectErrorScreen();
   ExpectClearAuth();
@@ -748,7 +735,7 @@
   {
     testing::InSequence s;
     // First view is shown for attestation-based failure.
-    ExpectEnrollmentConfig(config.mode, config.auth_mechanism);
+    ExpectEnrollmentConfig(config.mode);
     ExpectShowView();
     ExpectAttestationBasedEnrollmentAndReportFailure();
     ExpectErrorScreen();
@@ -786,7 +773,7 @@
 
   const policy::EnrollmentConfig config = GetEnrollmentConfig();
 
-  ExpectEnrollmentConfig(config.mode, config.auth_mechanism);
+  ExpectEnrollmentConfig(config.mode);
 
   ExpectAttestationBasedEnrollmentAndReportEnrolled();
   ExpectGetDeviceAttributeUpdatePermission(/*permission_granted=*/false);
@@ -828,13 +815,12 @@
   {
     testing::InSequence s;
     // First view is shown for attestation-based failure.
-    ExpectEnrollmentConfig(initial_config.mode, initial_config.auth_mechanism);
+    ExpectEnrollmentConfig(initial_config.mode);
     ExpectShowView();
     ExpectAttestationBasedEnrollmentAndReportFailureWithAutomaticFallback();
 
     // Second view is shown for manual fallback.
-    ExpectEnrollmentConfig(fallback_config.mode,
-                           fallback_config.auth_mechanism);
+    ExpectEnrollmentConfig(fallback_config.mode);
     ExpectShowViewWithLogin();
     ExpectManualEnrollmentAndReportEnrolled();
     ExpectGetDeviceAttributeUpdatePermission(/*permission_granted=*/false);
@@ -860,15 +846,14 @@
   {
     testing::InSequence s;
     // First view is shown for attestation-based failure.
-    ExpectEnrollmentConfig(initial_config.mode, initial_config.auth_mechanism);
+    ExpectEnrollmentConfig(initial_config.mode);
     ExpectShowView();
     ExpectAttestationBasedEnrollmentAndReportFailure();
     ExpectErrorScreen();
 
     // Second view is shown for manual fallback. This should be triggered after
     // user decides to fallback.
-    ExpectEnrollmentConfig(fallback_config.mode,
-                           fallback_config.auth_mechanism);
+    ExpectEnrollmentConfig(fallback_config.mode);
     ExpectShowViewWithLogin();
     ExpectManualEnrollmentAndReportEnrolled();
     ExpectGetDeviceAttributeUpdatePermission(/*permission_granted=*/false);
@@ -901,14 +886,12 @@
 class EnrollmentScreenTokenBasedEnrollmentTest
     : public EnrollmentScreenBaseTest {
  protected:
-  EnrollmentScreenTokenBasedEnrollmentTest() {}
+  EnrollmentScreenTokenBasedEnrollmentTest() = default;
 
   policy::EnrollmentConfig GetEnrollmentConfig() {
     policy::EnrollmentConfig config;
     config.mode =
         policy::EnrollmentConfig::MODE_ENROLLMENT_TOKEN_INITIAL_SERVER_FORCED;
-    config.auth_mechanism =
-        policy::EnrollmentConfig::AUTH_MECHANISM_TOKEN_PREFERRED;
     // The token isn't used directly by EnrollmentScreen, but let's set it here
     // for realism.
     config.enrollment_token = policy::test::kEnrollmentToken;
@@ -928,8 +911,7 @@
 TEST_F(EnrollmentScreenTokenBasedEnrollmentTest, ShouldFinishEnrollmentScreen) {
   const policy::EnrollmentConfig config = GetEnrollmentConfig();
 
-  ExpectEnrollmentConfig(config.mode, config.auth_mechanism,
-                         config.enrollment_token);
+  ExpectEnrollmentConfig(config.mode, config.enrollment_token);
 
   ExpectTokenBasedEnrollmentAndReportEnrolled();
   ExpectGetDeviceAttributeUpdatePermission(false);
@@ -956,8 +938,7 @@
 
   const policy::EnrollmentConfig config = GetEnrollmentConfig();
 
-  ExpectEnrollmentConfig(config.mode, config.auth_mechanism,
-                         config.enrollment_token);
+  ExpectEnrollmentConfig(config.mode, config.enrollment_token);
 
   ExpectTokenBasedEnrollmentAndReportEnrolled();
   ExpectGetDeviceAttributeUpdatePermission(false);
@@ -980,8 +961,7 @@
   {
     testing::InSequence s;
     // First view is shown for token-based enrollment failure.
-    ExpectEnrollmentConfig(config.mode, config.auth_mechanism,
-                           config.enrollment_token);
+    ExpectEnrollmentConfig(config.mode, config.enrollment_token);
     ExpectShowView();
     ExpectTokenBasedEnrollmentAndReportFailure();
     ExpectErrorScreen();
@@ -1010,7 +990,7 @@
        ShouldNotAutomaticallyRetryEnrollment) {
   const policy::EnrollmentConfig config = GetEnrollmentConfig();
 
-  ExpectEnrollmentConfig(config.mode, config.auth_mechanism);
+  ExpectEnrollmentConfig(config.mode);
   ExpectTokenBasedEnrollmentAndReportFailure();
   ExpectErrorScreen();
   ExpectClearAuth();
@@ -1038,8 +1018,7 @@
   {
     testing::InSequence s;
     // First view is shown for attestation-based failure.
-    ExpectEnrollmentConfig(config.mode, config.auth_mechanism,
-                           config.enrollment_token);
+    ExpectEnrollmentConfig(config.mode, config.enrollment_token);
     ExpectShowView();
     ExpectTokenBasedEnrollmentAndReportFailure(
         policy::EnrollmentStatus::ForRegistrationError(
@@ -1067,7 +1046,7 @@
   {
     testing::InSequence s;
     // First view is shown for token-based failure.
-    ExpectEnrollmentConfig(initial_config.mode, initial_config.auth_mechanism,
+    ExpectEnrollmentConfig(initial_config.mode,
                            initial_config.enrollment_token);
     ExpectShowView();
     ExpectTokenBasedEnrollmentAndReportFailure();
@@ -1075,7 +1054,7 @@
 
     // Second view is shown for manual fallback. This should be triggered after
     // user decides to fallback.
-    ExpectEnrollmentConfig(fallback_config.mode, fallback_config.auth_mechanism,
+    ExpectEnrollmentConfig(fallback_config.mode,
                            fallback_config.enrollment_token);
     ExpectShowViewWithLogin();
     ExpectManualEnrollmentAndReportEnrolled();
@@ -1111,7 +1090,6 @@
        UserCanChooseLicenseTypeOnManualEnrollment) {
   policy::EnrollmentConfig config;
   config.mode = policy::EnrollmentConfig::MODE_MANUAL;
-  config.auth_mechanism = policy::EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE;
   config.license_type = policy::LicenseType::kEducation;
 
   ExpectShowViewWithLogin(policy::LicenseType::kTerminal);
@@ -1128,8 +1106,6 @@
        AttestationBasedEnrollmentPicksPrescribedLicense) {
   policy::EnrollmentConfig config;
   config.mode = policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED;
-  config.auth_mechanism =
-      policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED;
   config.license_type = policy::LicenseType::kTerminal;
 
   ExpectLicense(policy::LicenseType::kTerminal);
@@ -1145,8 +1121,6 @@
   policy::EnrollmentConfig config;
   config.mode =
       policy::EnrollmentConfig::MODE_ENROLLMENT_TOKEN_INITIAL_SERVER_FORCED;
-  config.auth_mechanism =
-      policy::EnrollmentConfig::AUTH_MECHANISM_TOKEN_PREFERRED;
   config.enrollment_token = policy::test::kEnrollmentToken;
   config.license_type = policy::LicenseType::kEnterprise;
 
diff --git a/chrome/browser/ash/login/reset_browsertest.cc b/chrome/browser/ash/login/reset_browsertest.cc
index 14484ff..f35a760 100644
--- a/chrome/browser/ash/login/reset_browsertest.cc
+++ b/chrome/browser/ash/login/reset_browsertest.cc
@@ -472,7 +472,9 @@
 
   InvokeResetAccelerator();
   ClickToConfirmButton();
+  WaitForConfirmationDialogToOpen();
   ClickResetButton();
+  WaitForConfirmationDialogToClose();
 
   EXPECT_EQ(
       0, chromeos::FakePowerManagerClient::Get()->num_request_restart_calls());
diff --git a/chrome/browser/ash/login/screens/pin_setup_screen_browsertest.cc b/chrome/browser/ash/login/screens/pin_setup_screen_browsertest.cc
index 0bc1844..18cb196 100644
--- a/chrome/browser/ash/login/screens/pin_setup_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/pin_setup_screen_browsertest.cc
@@ -176,12 +176,7 @@
 
   void TapSkipButton() { test::OobeJS().TapOnPath(kSkipButton); }
 
-  void TapNextButton() {
-    test::OobeJS().TapOnPath(kNextButton);
-    // Wait until the back button is visible to ensure that the UI is showing
-    // the 'confirmation' step.
-    test::OobeJS().CreateVisibilityWaiter(true, kBackButton)->Wait();
-  }
+  void TapNextButton() { test::OobeJS().TapOnPath(kNextButton); }
 
   void TapDoneButton() {
     test::OobeJS()
@@ -382,6 +377,9 @@
 
   EnterPin();
   TapNextButton();
+  // Wait until the back button is visible to ensure that the UI is showing
+  // the 'confirmation' step.
+  test::OobeJS().CreateVisibilityWaiter(true, kBackButton)->Wait();
 
   TapSkipButton();
   WaitForScreenExit();
@@ -396,6 +394,9 @@
 
   EnterPin();
   TapNextButton();
+  // Wait until the back button is visible to ensure that the UI is showing
+  // the 'confirmation' step.
+  test::OobeJS().CreateVisibilityWaiter(true, kBackButton)->Wait();
   EnterPin();
   TapNextButton();
 
diff --git a/chrome/browser/ash/login/wizard_controller_browsertest.cc b/chrome/browser/ash/login/wizard_controller_browsertest.cc
index c3de1b3..2e49dde 100644
--- a/chrome/browser/ash/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/ash/login/wizard_controller_browsertest.cc
@@ -153,12 +153,6 @@
 using ::testing::Mock;
 using ::testing::NotNull;
 
-const char kUnifiedStateDeterminationKillSwitchConfigURL[] =
-    "https://www.gstatic.com/chromeos-usd-experiment/v1.json";
-const char kUnifiedStateDeterminationKillSwitchConfigResponseBody[] = R"({
-  "disable_up_to_version": 0
-})";
-
 const char kDMServerURLPrefix[] =
     "https://m.google.com/devicemanagement/data/api";
 
@@ -1123,10 +1117,6 @@
                                                   "2000-01");
     fake_statistics_provider_.SetVpdStatus(
         system::StatisticsProvider::VpdStatus::kValid);
-    // Simulate disabled kill-switch for unified state determination.
-    test_url_loader_factory_.AddResponse(
-        GURL(kUnifiedStateDeterminationKillSwitchConfigURL).spec(),
-        kUnifiedStateDeterminationKillSwitchConfigResponseBody);
     // Make all requests to DMServer fail with net::ERR_CONNECTION_REFUSED.
     test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting(
         [&](const network::ResourceRequest& request) {
@@ -1205,7 +1195,7 @@
 
   content::RunAllTasksUntilIdle();
 
-  EXPECT_TRUE(policy::AutoEnrollmentTypeChecker::Initialized());
+  EXPECT_TRUE(auto_enrollment_controller()->IsInProgress());
 }
 
 IN_PROC_BROWSER_TEST_F(WizardControllerDeviceStateTest,
diff --git a/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc b/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc
index 17dc7de..ad33154 100644
--- a/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc
+++ b/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc
@@ -751,8 +751,6 @@
     ASSERT_TRUE(owner_settings_service);
 
     EnrollmentConfig enrollment_config;
-    enrollment_config.auth_mechanism =
-        EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED;
     enrollment_config.mode = with_cert ? EnrollmentConfig::MODE_ATTESTATION
                                        : EnrollmentConfig::MODE_MANUAL;
     DMAuth auth =
diff --git a/chrome/browser/ash/policy/enrollment/auto_enrollment_controller.cc b/chrome/browser/ash/policy/enrollment/auto_enrollment_controller.cc
index bcde418..a801c94 100644
--- a/chrome/browser/ash/policy/enrollment/auto_enrollment_controller.cc
+++ b/chrome/browser/ash/policy/enrollment/auto_enrollment_controller.cc
@@ -245,17 +245,6 @@
     network_state_observation_.Observe(network_state_handler_);
   }
 
-  if (!AutoEnrollmentTypeChecker::Initialized()) {
-    if (!auto_enrollment_check_type_init_started_) {
-      auto_enrollment_check_type_init_started_ = true;
-      AutoEnrollmentTypeChecker::Initialize(
-          shared_url_loader_factory_,
-          base::BindOnce(&AutoEnrollmentController::Start,
-                         weak_ptr_factory_.GetWeakPtr()));
-    }
-    return;
-  }
-
   if (IsInProgress()) {
     return;
   }
diff --git a/chrome/browser/ash/policy/enrollment/auto_enrollment_controller.h b/chrome/browser/ash/policy/enrollment/auto_enrollment_controller.h
index 8daa992a..f00b991 100644
--- a/chrome/browser/ash/policy/enrollment/auto_enrollment_controller.h
+++ b/chrome/browser/ash/policy/enrollment/auto_enrollment_controller.h
@@ -109,6 +109,9 @@
   // Retry checking.
   void Retry();
 
+  // Returns true if auto-enrollment check is running.
+  bool IsInProgress() const;
+
   // Registers a callback to invoke on state changes.
   base::CallbackListSubscription RegisterProgressCallback(
       const ProgressCallbackList::CallbackType& callback);
@@ -230,8 +233,6 @@
   // Handles timeout of the safeguard timer and stops waiting for a result.
   void Timeout();
 
-  bool IsInProgress() const;
-
   // Used for checking ownership.
   raw_ptr<ash::DeviceSettingsService> device_settings_service_;
 
diff --git a/chrome/browser/ash/policy/enrollment/auto_enrollment_controller_unittest.cc b/chrome/browser/ash/policy/enrollment/auto_enrollment_controller_unittest.cc
index 561d028..d71bc46a 100644
--- a/chrome/browser/ash/policy/enrollment/auto_enrollment_controller_unittest.cc
+++ b/chrome/browser/ash/policy/enrollment/auto_enrollment_controller_unittest.cc
@@ -255,11 +255,6 @@
 
 class AutoEnrollmentControllerBaseTest : public testing::Test {
  protected:
-  ~AutoEnrollmentControllerBaseTest() override {
-    AutoEnrollmentTypeChecker::
-        ClearUnifiedStateDeterminationKillSwitchForTesting();
-  }
-
   AutoEnrollmentControllerForTesting CreateController() {
     return AutoEnrollmentControllerForTesting(
         &mock_device_settings_service_, &fake_dm_service_,
@@ -305,10 +300,6 @@
     command_line_.GetProcessCommandLine()->AppendSwitchASCII(
         ash::switches::kEnterpriseEnableUnifiedStateDetermination,
         switch_value);
-
-    const bool is_killed = !enabled;
-    AutoEnrollmentTypeChecker::SetUnifiedStateDeterminationKillSwitchForTesting(
-        is_killed);
   }
 
   void SetupForcedReenrollmentCheckType() {
diff --git a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.cc b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.cc
index 46e43289..d091065 100644
--- a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.cc
+++ b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.cc
@@ -94,158 +94,9 @@
   return IsOfficialGoogleChrome() || IsFlexAndFREOnFlexIsEnabled();
 }
 
-// Kill switch config request parameters.
-constexpr net::NetworkTrafficAnnotationTag kKSConfigTrafficAnnotation =
-    net::DefineNetworkTrafficAnnotation(
-        "unified_state_determination_kill_switch",
-        R"(
-            semantics {
-              sender: "Unified State Determination"
-              description:
-                "Communication with the backend used to check whether "
-                "unified state determination should be enabled."
-              trigger: "Open device for the first time, powerwash the device."
-              data: "A simple GET HTTP request without user data."
-              destination: GOOGLE_OWNED_SERVICE
-              internal {
-                contacts {
-                  email: "sergiyb@google.com"
-                }
-                contacts {
-                  email: "chromeos-commercial-remote-management@google.com"
-                }
-              }
-              user_data {
-                type: NONE
-              }
-              last_reviewed: "2023-05-16"
-            }
-            policy {
-              cookies_allowed: NO
-              setting: "This feature cannot be controlled by Chrome settings."
-              chrome_policy {}
-            })");
-constexpr char kKSConfigUrl[] =
-    "https://www.gstatic.com/chromeos-usd-experiment/v1.json";
-constexpr base::TimeDelta kKSConfigFetchTimeout = base::Seconds(1);
-constexpr int kKSConfigFetchTries = 4;
-constexpr size_t kKSConfigMaxSize = 1024;  // 1KB
-constexpr char kKSConfigFetchMethod[] = "GET";
-constexpr char kKSConfigDisableUpToVersionKey[] = "disable_up_to_version";
-constexpr int kUMAKSFetchNumTriesMinValue = 1;
-constexpr int kUMAKSFetchNumTriesExclusiveMaxValue = 51;
-constexpr int kUMAKSFetchNumTriesBuckets =
-    kUMAKSFetchNumTriesExclusiveMaxValue - kUMAKSFetchNumTriesMinValue;
-
-// This value represents current version of the code. After we have enabled kill
-// switch for a particular version, we can increment it after fixing the logic.
-// The devices running new code will not be affected by kill switch and we can
-// test our fixes.
-const int kCodeVersion = 1;
-
-// When set to true, unified state determination is disabled.
-std::optional<bool> g_unified_state_determination_kill_switch;
-
-void ReportKillSwitchFetchTries(int tries) {
-  base::UmaHistogramCustomCounts(kUMAStateDeterminationKillSwitchFetchNumTries,
-                                 tries, kUMAKSFetchNumTriesMinValue,
-                                 kUMAKSFetchNumTriesExclusiveMaxValue,
-                                 kUMAKSFetchNumTriesBuckets);
-}
-
-void ParseKSConfig(base::OnceClosure init_callback,
-                   const std::string& response) {
-  std::optional<base::Value> config = base::JSONReader::Read(response);
-  if (!config || !config->is_dict()) {
-    LOG(ERROR) << "Kill switch config is not valid JSON or not a dict";
-    std::move(init_callback).Run();
-    return;
-  }
-
-  std::optional<int> disable_up_to_version =
-      config->GetDict().FindInt(kKSConfigDisableUpToVersionKey);
-  if (!disable_up_to_version) {
-    LOG(ERROR) << "Kill switch config is missing disable_up_to_version key or "
-                  "it is not an int";
-    std::move(init_callback).Run();
-    return;
-  }
-
-  g_unified_state_determination_kill_switch =
-      kCodeVersion <= disable_up_to_version;
-  std::move(init_callback).Run();
-}
-
-void FetchKSConfig(
-    scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
-    base::OnceClosure init_callback,
-    int tries_left,
-    std::unique_ptr<network::SimpleURLLoader> loader = nullptr,
-    std::unique_ptr<std::string> response = nullptr) {
-  if (loader) {
-    base::UmaHistogramSparse(
-        kUMAStateDeterminationKillSwitchFetchNetworkErrorCode,
-        -loader->NetError());
-  }
-
-  if (!response && tries_left) {
-    auto request = std::make_unique<network::ResourceRequest>();
-    request->url = GURL(kKSConfigUrl);
-    request->method = kKSConfigFetchMethod;
-    request->load_flags = net::LOAD_DISABLE_CACHE;
-    request->credentials_mode = network::mojom::CredentialsMode::kOmit;
-    VLOG(1) << "Sending kill switch config request to " << request->url;
-    loader = network::SimpleURLLoader::Create(std::move(request),
-                                              kKSConfigTrafficAnnotation);
-    loader->SetTimeoutDuration(kKSConfigFetchTimeout);
-    // Use the raw pointer to avoid calling on empty `loader` after std::move.
-    network::SimpleURLLoader* loader_ptr = loader.get();
-    loader_ptr->DownloadToString(
-        loader_factory.get(),
-        base::BindOnce(FetchKSConfig, loader_factory, std::move(init_callback),
-                       tries_left - 1, std::move(loader)),
-        kKSConfigMaxSize);
-    return;
-  }
-
-  // On any errors, assume kill switch is enabled and fallback to old logic.
-  g_unified_state_determination_kill_switch = true;
-  if (!response) {
-    LOG(ERROR) << "Kill switch config request failed with code "
-               << loader->NetError();
-    ReportKillSwitchFetchTries(kKSConfigFetchTries);
-    std::move(init_callback).Run();
-    return;
-  }
-
-  VLOG(1) << "Received kill switch config response after "
-          << (kKSConfigFetchTries - tries_left) << " tries: " << *response;
-  ReportKillSwitchFetchTries(kKSConfigFetchTries - tries_left);
-  ParseKSConfig(std::move(init_callback), *response);
-}
-
-bool IsUnifiedStateDeterminationDisabledByKillSwitch() {
-  // If AutoEnrollmentTypeChecker is not initialized, assume the kill switch is
-  // enabled. This is for legacy code that doesn't know about unified state
-  // determination. New code should wait for init to complete.
-  return g_unified_state_determination_kill_switch.value_or(true);
-}
-
 }  // namespace
 
 // static
-void AutoEnrollmentTypeChecker::Initialize(
-    scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
-    base::OnceClosure init_callback) {
-  FetchKSConfig(loader_factory, std::move(init_callback), kKSConfigFetchTries);
-}
-
-// static
-bool AutoEnrollmentTypeChecker::Initialized() {
-  return g_unified_state_determination_kill_switch.has_value();
-}
-
-// static
 bool AutoEnrollmentTypeChecker::IsUnifiedStateDeterminationEnabled() {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   std::string command_line_mode = command_line->GetSwitchValueASCII(
@@ -260,11 +111,6 @@
                                   USDStatus::kDisabledViaNeverSwitch);
     return false;
   }
-  if (IsUnifiedStateDeterminationDisabledByKillSwitch()) {
-    base::UmaHistogramEnumeration(kUMAStateDeterminationStatus,
-                                  USDStatus::kDisabledViaKillSwitch);
-    return false;
-  }
 
 #if !BUILDFLAG(GOOGLE_CHROME_BRANDING)
   base::UmaHistogramEnumeration(kUMAStateDeterminationStatus,
@@ -599,22 +445,4 @@
   return CheckType::kNone;
 }
 
-// static
-void AutoEnrollmentTypeChecker::
-    SetUnifiedStateDeterminationKillSwitchForTesting(bool is_killed) {
-  g_unified_state_determination_kill_switch = is_killed;
-}
-
-// static
-void AutoEnrollmentTypeChecker::
-    ClearUnifiedStateDeterminationKillSwitchForTesting() {
-  g_unified_state_determination_kill_switch.reset();
-}
-
-// static
-bool AutoEnrollmentTypeChecker::
-    IsUnifiedStateDeterminationDisabledByKillSwitchForTesting() {
-  return IsUnifiedStateDeterminationDisabledByKillSwitch();
-}
-
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.h b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.h
index 9e11d8a4..428ba50 100644
--- a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.h
+++ b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.h
@@ -7,13 +7,6 @@
 
 #include "base/functional/callback_forward.h"
 
-template <class T>
-class scoped_refptr;
-
-namespace network {
-class SharedURLLoaderFactory;
-}
-
 namespace ash::system {
 class StatisticsProvider;
 }
@@ -73,7 +66,7 @@
     // These values are persisted to logs. Entries should not be renumbered and
     // numeric values should never be reused.
     kDisabledViaNeverSwitch = 0,
-    kDisabledViaKillSwitch = 1,
+    // Deprecated: kDisabledViaKillSwitch = 1,
     kDisabledOnUnbrandedBuild = 2,
     kDisabledOnNonChromeDevice = 3,
     kEnabledOnOfficialGoogleChrome = 4,
@@ -82,16 +75,6 @@
     kMaxValue = kEnabledViaAlwaysSwitch
   };
 
-  // Returns true when class has been initialized.
-  static bool Initialized();
-
-  // Perform async initialization of this class, which requires access to the
-  // network. Users must call this method and wait until `init_callback` has
-  // been invoked before calling any other non-testing functions below.
-  static void Initialize(
-      scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
-      base::OnceClosure init_callback);
-
   // Returns true when unified state determination is enabled based on
   // command-line switch, official build status and server-based kill-switch.
   static bool IsUnifiedStateDeterminationEnabled();
@@ -142,17 +125,6 @@
       ash::system::StatisticsProvider* statistics_provider,
       bool dev_disable_boot);
 
-  // Allows to configure unified state determination kill switch. Used for
-  // testing.
-  static void SetUnifiedStateDeterminationKillSwitchForTesting(bool is_killed);
-
-  // Clears unified state determination kill switch. Used for testing.
-  static void ClearUnifiedStateDeterminationKillSwitchForTesting();
-
-  // Checks if unified state determination is disabled using the server-based
-  // kill-switch. Used for testing.
-  static bool IsUnifiedStateDeterminationDisabledByKillSwitchForTesting();
-
  private:
   // Requirement for initial state determination.
   enum class InitialStateDeterminationRequirement {
diff --git a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc
index 6faad17..a22c69cf 100644
--- a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc
+++ b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc
@@ -390,8 +390,10 @@
                                                 kSerialNumberValue);
   fake_statistics_provider_.SetMachineStatistic(ash::system::kRlzBrandCodeKey,
                                                 kBrandCodeValue);
-  AutoEnrollmentTypeChecker::SetUnifiedStateDeterminationKillSwitchForTesting(
-      true);
+  command_line_.GetProcessCommandLine()->AppendSwitchASCII(
+      ash::switches::kEnterpriseEnableUnifiedStateDetermination,
+      AutoEnrollmentTypeChecker::AutoEnrollmentTypeChecker::
+          kUnifiedStateDeterminationNever);
 
   EXPECT_EQ(AutoEnrollmentTypeChecker::IsInitialEnrollmentEnabled(),
             is_google_branded_);
@@ -418,8 +420,10 @@
                                                 kSerialNumberValue);
   fake_statistics_provider_.SetMachineStatistic(ash::system::kRlzBrandCodeKey,
                                                 kBrandCodeValue);
-  AutoEnrollmentTypeChecker::SetUnifiedStateDeterminationKillSwitchForTesting(
-      true);
+  command_line_.GetProcessCommandLine()->AppendSwitchASCII(
+      ash::switches::kEnterpriseEnableUnifiedStateDetermination,
+      AutoEnrollmentTypeChecker::AutoEnrollmentTypeChecker::
+          kUnifiedStateDeterminationNever);
 
   EXPECT_EQ(AutoEnrollmentTypeChecker::IsInitialEnrollmentEnabled(),
             is_google_branded_);
@@ -443,8 +447,10 @@
                                                 kSerialNumberValue);
   fake_statistics_provider_.SetMachineStatistic(ash::system::kRlzBrandCodeKey,
                                                 kBrandCodeValue);
-  AutoEnrollmentTypeChecker::SetUnifiedStateDeterminationKillSwitchForTesting(
-      true);
+  command_line_.GetProcessCommandLine()->AppendSwitchASCII(
+      ash::switches::kEnterpriseEnableUnifiedStateDetermination,
+      AutoEnrollmentTypeChecker::AutoEnrollmentTypeChecker::
+          kUnifiedStateDeterminationNever);
 
   EXPECT_EQ(AutoEnrollmentTypeChecker::IsInitialEnrollmentEnabled(),
             is_google_branded_);
@@ -470,8 +476,10 @@
                                                 kSerialNumberValue);
   fake_statistics_provider_.SetMachineStatistic(ash::system::kRlzBrandCodeKey,
                                                 kBrandCodeValue);
-  AutoEnrollmentTypeChecker::SetUnifiedStateDeterminationKillSwitchForTesting(
-      true);
+  command_line_.GetProcessCommandLine()->AppendSwitchASCII(
+      ash::switches::kEnterpriseEnableUnifiedStateDetermination,
+      AutoEnrollmentTypeChecker::AutoEnrollmentTypeChecker::
+          kUnifiedStateDeterminationNever);
 
   EXPECT_EQ(AutoEnrollmentTypeChecker::IsInitialEnrollmentEnabled(),
             is_google_branded_);
@@ -482,210 +490,8 @@
   EXPECT_EQ(check_type, AutoEnrollmentTypeChecker::CheckType::kNone);
 }
 
-class AutoEnrollmentTypeCheckerInitializationTest
-    : public AutoEnrollmentTypeCheckerTest {
- public:
-  void SetUp() override {
-    AutoEnrollmentTypeCheckerTest::SetUp();
-    AutoEnrollmentTypeChecker::
-        ClearUnifiedStateDeterminationKillSwitchForTesting();
-    test_shared_loader_factory_ = test_url_loader_factory_.GetSafeWeakWrapper();
-  }
-
- protected:
-  network::TestURLLoaderFactory test_url_loader_factory_;
-  scoped_refptr<network::WeakWrapperSharedURLLoaderFactory>
-      test_shared_loader_factory_;
-
- private:
-  base::test::SingleThreadTaskEnvironment task_environment_;
-};
-
-TEST_F(AutoEnrollmentTypeCheckerInitializationTest, Request) {
-  base::test::TestFuture<void> future;
-
-  AutoEnrollmentTypeChecker::Initialize(test_shared_loader_factory_,
-                                        future.GetCallback());
-
-  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
-  auto request = test_url_loader_factory_.GetPendingRequest(0)->request;
-  EXPECT_EQ(request.url,
-            "https://www.gstatic.com/chromeos-usd-experiment/v1.json");
-  EXPECT_EQ(request.method, "GET");
-  EXPECT_TRUE(request.load_flags & net::LOAD_DISABLE_CACHE);
-  EXPECT_EQ(request.credentials_mode, network::mojom::CredentialsMode::kOmit);
-}
-
-TEST_F(AutoEnrollmentTypeCheckerInitializationTest, RetriesAllErrors) {
-  base::test::TestFuture<void> future;
-  network::URLLoaderCompletionStatus status;
-
-  AutoEnrollmentTypeChecker::Initialize(test_shared_loader_factory_,
-                                        future.GetCallback());
-
-  // Network changed.
-  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
-  status.error_code = net::ERR_NETWORK_CHANGED;
-  test_url_loader_factory_.SimulateResponseForPendingRequest(
-      GURL("https://www.gstatic.com/chromeos-usd-experiment/v1.json"), status,
-      network::mojom::URLResponseHead::New(), "");
-
-  // DNS failed.
-  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
-  status.error_code = net::ERR_TIMED_OUT;
-  test_url_loader_factory_.SimulateResponseForPendingRequest(
-      GURL("https://www.gstatic.com/chromeos-usd-experiment/v1.json"), status,
-      network::mojom::URLResponseHead::New(), "");
-
-  // HTTP 5xx error.
-  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
-  test_url_loader_factory_.SimulateResponseForPendingRequest(
-      "https://www.gstatic.com/chromeos-usd-experiment/v1.json", "",
-      net::HTTP_SERVICE_UNAVAILABLE);
-
-  EXPECT_EQ(test_url_loader_factory_.NumPending(), 1);
-  EXPECT_FALSE(future.IsReady());
-  EXPECT_FALSE(AutoEnrollmentTypeChecker::Initialized());
-}
-
-TEST_F(AutoEnrollmentTypeCheckerInitializationTest, UmaHistograms) {
-  base::HistogramTester histograms;
-  base::test::TestFuture<void> future;
-  network::URLLoaderCompletionStatus status;
-
-  AutoEnrollmentTypeChecker::Initialize(test_shared_loader_factory_,
-                                        future.GetCallback());
-
-  status.error_code = net::ERR_NETWORK_CHANGED;
-  test_url_loader_factory_.SimulateResponseForPendingRequest(
-      GURL("https://www.gstatic.com/chromeos-usd-experiment/v1.json"), status,
-      network::mojom::URLResponseHead::New(), "");
-  test_url_loader_factory_.AddResponse(
-      "https://www.gstatic.com/chromeos-usd-experiment/v1.json",
-      R"({"disable_up_to_version": 1})", net::HTTP_OK);
-
-  ASSERT_TRUE(future.Wait());
-  histograms.ExpectTotalCount(
-      kUMAStateDeterminationKillSwitchFetchNetworkErrorCode, 2);
-  histograms.ExpectBucketCount(
-      kUMAStateDeterminationKillSwitchFetchNetworkErrorCode,
-      -net::ERR_NETWORK_CHANGED, 1);
-  histograms.ExpectBucketCount(
-      kUMAStateDeterminationKillSwitchFetchNetworkErrorCode, -net::OK, 1);
-  histograms.ExpectUniqueSample(kUMAStateDeterminationKillSwitchFetchNumTries,
-                                2, 1);
-}
-
-TEST_F(AutoEnrollmentTypeCheckerInitializationTest, KilledBeforeInitStarted) {
-  EXPECT_FALSE(AutoEnrollmentTypeChecker::Initialized());
-  EXPECT_TRUE(AutoEnrollmentTypeChecker::
-                  IsUnifiedStateDeterminationDisabledByKillSwitchForTesting());
-}
-
-TEST_F(AutoEnrollmentTypeCheckerInitializationTest, KilledBeforeInitCompleted) {
-  base::test::TestFuture<void> future;
-
-  AutoEnrollmentTypeChecker::Initialize(test_shared_loader_factory_,
-                                        future.GetCallback());
-
-  EXPECT_FALSE(future.IsReady());
-  EXPECT_FALSE(AutoEnrollmentTypeChecker::Initialized());
-  EXPECT_TRUE(AutoEnrollmentTypeChecker::
-                  IsUnifiedStateDeterminationDisabledByKillSwitchForTesting());
-}
-
-TEST_F(AutoEnrollmentTypeCheckerInitializationTest, KilledOnTimeout) {
-  base::test::TestFuture<void> future;
-
-  AutoEnrollmentTypeChecker::Initialize(test_shared_loader_factory_,
-                                        future.GetCallback());
-  network::SimpleURLLoader::SetTimeoutTickClockForTest(nullptr);
-
-  ASSERT_TRUE(future.Wait());
-  EXPECT_TRUE(AutoEnrollmentTypeChecker::Initialized());
-  EXPECT_TRUE(AutoEnrollmentTypeChecker::
-                  IsUnifiedStateDeterminationDisabledByKillSwitchForTesting());
-}
-
-TEST_F(AutoEnrollmentTypeCheckerInitializationTest, KilledOnInvalidJSON) {
-  base::test::TestFuture<void> future;
-  test_url_loader_factory_.AddResponse(
-      "https://www.gstatic.com/chromeos-usd-experiment/v1.json", "!!!",
-      net::HTTP_OK);
-
-  AutoEnrollmentTypeChecker::Initialize(test_shared_loader_factory_,
-                                        future.GetCallback());
-
-  ASSERT_TRUE(future.Wait());
-  EXPECT_TRUE(AutoEnrollmentTypeChecker::
-                  IsUnifiedStateDeterminationDisabledByKillSwitchForTesting());
-}
-
-TEST_F(AutoEnrollmentTypeCheckerInitializationTest, KilledOnNonDict) {
-  base::test::TestFuture<void> future;
-  test_url_loader_factory_.AddResponse(
-      "https://www.gstatic.com/chromeos-usd-experiment/v1.json", "42",
-      net::HTTP_OK);
-
-  AutoEnrollmentTypeChecker::Initialize(test_shared_loader_factory_,
-                                        future.GetCallback());
-
-  ASSERT_TRUE(future.Wait());
-  EXPECT_TRUE(AutoEnrollmentTypeChecker::
-                  IsUnifiedStateDeterminationDisabledByKillSwitchForTesting());
-}
-
-TEST_F(AutoEnrollmentTypeCheckerInitializationTest, KilledOnMissingKey) {
-  base::test::TestFuture<void> future;
-  test_url_loader_factory_.AddResponse(
-      "https://www.gstatic.com/chromeos-usd-experiment/v1.json", "{}",
-      net::HTTP_OK);
-
-  AutoEnrollmentTypeChecker::Initialize(test_shared_loader_factory_,
-                                        future.GetCallback());
-
-  ASSERT_TRUE(future.Wait());
-  EXPECT_TRUE(AutoEnrollmentTypeChecker::
-                  IsUnifiedStateDeterminationDisabledByKillSwitchForTesting());
-}
-
-TEST_F(AutoEnrollmentTypeCheckerInitializationTest, KilledVersion) {
-  base::test::TestFuture<void> future;
-  test_url_loader_factory_.AddResponse(
-      "https://www.gstatic.com/chromeos-usd-experiment/v1.json",
-      R"({"disable_up_to_version": 1})", net::HTTP_OK);
-
-  AutoEnrollmentTypeChecker::Initialize(test_shared_loader_factory_,
-                                        future.GetCallback());
-
-  ASSERT_TRUE(future.Wait());
-  EXPECT_TRUE(AutoEnrollmentTypeChecker::
-                  IsUnifiedStateDeterminationDisabledByKillSwitchForTesting());
-}
-
-TEST_F(AutoEnrollmentTypeCheckerInitializationTest, ActiveVersion) {
-  base::test::TestFuture<void> future;
-  test_url_loader_factory_.AddResponse(
-      "https://www.gstatic.com/chromeos-usd-experiment/v1.json",
-      R"({"disable_up_to_version": 0})", net::HTTP_OK);
-
-  AutoEnrollmentTypeChecker::Initialize(test_shared_loader_factory_,
-                                        future.GetCallback());
-
-  ASSERT_TRUE(future.Wait());
-  EXPECT_FALSE(AutoEnrollmentTypeChecker::
-                   IsUnifiedStateDeterminationDisabledByKillSwitchForTesting());
-}
-
 class AutoEnrollmentTypeCheckerUSDStatusTest
     : public AutoEnrollmentTypeCheckerTest {
- public:
-  void SetUp() override {
-    AutoEnrollmentTypeCheckerTest::SetUp();
-    AutoEnrollmentTypeChecker::SetUnifiedStateDeterminationKillSwitchForTesting(
-        false);
-  }
-
  protected:
   base::HistogramTester histograms_;
 };
@@ -731,16 +537,6 @@
                                  USDStatus::kDisabledViaNeverSwitch, 1);
 }
 
-TEST_F(AutoEnrollmentTypeCheckerUSDStatusTest, KillSwitch) {
-  AutoEnrollmentTypeChecker::SetUnifiedStateDeterminationKillSwitchForTesting(
-      true);
-
-  AutoEnrollmentTypeChecker::IsUnifiedStateDeterminationEnabled();
-
-  histograms_.ExpectUniqueSample(kUMAStateDeterminationStatus,
-                                 USDStatus::kDisabledViaKillSwitch, 1);
-}
-
 TEST_F(AutoEnrollmentTypeCheckerUSDStatusTest, NonChrome) {
   enrollment_test_helper_.SetUpNonchromeDevice();
 
@@ -767,10 +563,10 @@
   FlexWithFRE = 3,
 };
 
-// This is parameterized by device OS and USD kill switch enablement.
+// This is parameterized by device OS.
 class AutoEnrollmentTypeCheckerUnifiedStateDeterminationTestP
     : public AutoEnrollmentTypeCheckerTest,
-      public testing::WithParamInterface<std::tuple<DeviceOs, bool>> {
+      public testing::WithParamInterface<std::tuple<DeviceOs>> {
  protected:
   void SetUp() override {
     AutoEnrollmentTypeCheckerTest::SetUp();
@@ -781,8 +577,6 @@
     } else if (device_os_ == DeviceOs::FlexWithFRE) {
       SetUpFlexDeviceWithFREOnFlexEnabled();
     }
-    AutoEnrollmentTypeChecker::SetUnifiedStateDeterminationKillSwitchForTesting(
-        kill_switch_enabled_);
   }
 
   bool IsFRESupportedByDevice() {
@@ -797,7 +591,6 @@
   }
 
   const DeviceOs device_os_ = std::get<0>(GetParam());
-  const bool kill_switch_enabled_ = std::get<1>(GetParam());
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   const bool google_branded_ = true;
 #else
@@ -807,7 +600,7 @@
 
 TEST_P(AutoEnrollmentTypeCheckerUnifiedStateDeterminationTestP, Default) {
   EXPECT_EQ(AutoEnrollmentTypeChecker::IsUnifiedStateDeterminationEnabled(),
-            !kill_switch_enabled_ && IsOfficialGoogleOS());
+            IsOfficialGoogleOS());
 }
 
 TEST_P(AutoEnrollmentTypeCheckerUnifiedStateDeterminationTestP, OfficialBuild) {
@@ -816,7 +609,7 @@
       AutoEnrollmentTypeChecker::kUnifiedStateDeterminationOfficialBuild);
 
   EXPECT_EQ(AutoEnrollmentTypeChecker::IsUnifiedStateDeterminationEnabled(),
-            !kill_switch_enabled_ && IsOfficialGoogleOS());
+            IsOfficialGoogleOS());
 }
 
 TEST_P(AutoEnrollmentTypeCheckerUnifiedStateDeterminationTestP, Never) {
@@ -881,11 +674,10 @@
 INSTANTIATE_TEST_SUITE_P(
     AutoEnrollmentTypeCheckerUnifiedStateDeterminationTestSuite,
     AutoEnrollmentTypeCheckerUnifiedStateDeterminationTestP,
-    testing::Combine(testing::Values(DeviceOs::Chrome,
-                                     DeviceOs::Nonchrome,
-                                     DeviceOs::FlexWithoutFRE,
-                                     DeviceOs::FlexWithFRE),
-                     testing::Bool()));
+    testing::Values(DeviceOs::Chrome,
+                    DeviceOs::Nonchrome,
+                    DeviceOs::FlexWithoutFRE,
+                    DeviceOs::FlexWithFRE));
 
 // This is parametrized with dev_disable_boot.
 class AutoEnrollmentTypeCheckerTestP
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_config.cc b/chrome/browser/ash/policy/enrollment/enrollment_config.cc
index fbbedcbd..8fa288f 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_config.cc
+++ b/chrome/browser/ash/policy/enrollment/enrollment_config.cc
@@ -108,31 +108,12 @@
 #undef CASE
 }
 
-std::string_view ToStringView(EnrollmentConfig::AuthMechanism auth) {
-#define CASE(_name)                            \
-  case EnrollmentConfig::AuthMechanism::_name: \
-    return #_name;
-
-  switch (auth) {
-    CASE(AUTH_MECHANISM_INTERACTIVE);
-    CASE(AUTH_MECHANISM_ATTESTATION);
-    CASE(AUTH_MECHANISM_ATTESTATION_PREFERRED);
-    CASE(AUTH_MECHANISM_TOKEN_PREFERRED);
-  }
-
-  NOTREACHED();
-#undef CASE
-}
-
 EnrollmentConfig GetPrescribedRecoveryConfig(
     PrefService* local_state,
     const ash::InstallAttributes& install_attributes,
     ash::system::StatisticsProvider* statistics_provider) {
   EnrollmentConfig recovery_config;
 
-  // Regardless what mode is applicable, auth mechanism must be manual one.
-  recovery_config.auth_mechanism = EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE;
-
   // Regardless what mode is applicable, the enrollment domain is fixed.
   recovery_config.management_domain = install_attributes.GetDomain();
 
@@ -214,11 +195,10 @@
 }  // namespace
 
 struct EnrollmentConfig::PrescribedConfig {
-  EnrollmentConfig::Mode mode;
-  EnrollmentConfig::AuthMechanism auth_mechanism;
+  EnrollmentConfig::Mode mode = MODE_NONE;
   std::string management_domain;
   std::string enrollment_token;
-  OOBEConfigSource oobe_config_source;
+  OOBEConfigSource oobe_config_source = OOBEConfigSource::kNone;
 
   static PrescribedConfig GetPrescribedConfig(
       PrefService* local_state,
@@ -236,9 +216,7 @@
     const ash::OobeConfiguration* oobe_configuration) {
   // Decide enrollment mode. Give precedence to forced variants.
   if (IsEnrollingAfterRollback()) {
-    return {.mode = EnrollmentConfig::MODE_ATTESTATION_ROLLBACK_FORCED,
-            .auth_mechanism =
-                EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED};
+    return {.mode = EnrollmentConfig::MODE_ATTESTATION_ROLLBACK_FORCED};
   }
 
   const std::string device_state_mode =
@@ -248,27 +226,21 @@
 
   if (device_state_mode == kDeviceStateRestoreModeReEnrollmentEnforced) {
     return {.mode = EnrollmentConfig::MODE_SERVER_FORCED,
-            .auth_mechanism = EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
             .management_domain = device_state_management_domain};
   }
 
   if (device_state_mode == kDeviceStateInitialModeEnrollmentEnforced) {
     return {.mode = EnrollmentConfig::MODE_INITIAL_SERVER_FORCED,
-            .auth_mechanism = EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
             .management_domain = device_state_management_domain};
   }
 
   if (device_state_mode == kDeviceStateRestoreModeReEnrollmentZeroTouch) {
     return {.mode = EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED,
-            .auth_mechanism =
-                EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED,
             .management_domain = device_state_management_domain};
   }
 
   if (device_state_mode == kDeviceStateInitialModeEnrollmentZeroTouch) {
     return {.mode = EnrollmentConfig::MODE_ATTESTATION_INITIAL_SERVER_FORCED,
-            .auth_mechanism =
-                EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED,
             .management_domain = device_state_management_domain};
   }
 
@@ -283,7 +255,6 @@
               ash::configuration::kSource);
       return {
           .mode = EnrollmentConfig::MODE_ENROLLMENT_TOKEN_INITIAL_SERVER_FORCED,
-          .auth_mechanism = EnrollmentConfig::AUTH_MECHANISM_TOKEN_PREFERRED,
           .enrollment_token = std::move(enrollment_token.value()),
           .oobe_config_source = ConvertToOOBEConfigSource(oobe_config_source)};
     } else {
@@ -303,8 +274,7 @@
 
   if (pref_enrollment_auto_start_present && pref_enrollment_auto_start &&
       pref_enrollment_can_exit_present && !pref_enrollment_can_exit) {
-    return {.mode = EnrollmentConfig::MODE_LOCAL_FORCED,
-            .auth_mechanism = EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE};
+    return {.mode = EnrollmentConfig::MODE_LOCAL_FORCED};
   }
 
   const bool oem_is_managed = ash::system::StatisticsProvider::FlagValueToBool(
@@ -318,8 +288,7 @@
           /*default_value=*/true);
 
   if (oem_is_managed && !oem_can_exit_enrollment) {
-    return {.mode = EnrollmentConfig::MODE_LOCAL_FORCED,
-            .auth_mechanism = EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE};
+    return {.mode = EnrollmentConfig::MODE_LOCAL_FORCED};
   }
 
   if (local_state->GetBoolean(ash::prefs::kOobeComplete)) {
@@ -330,27 +299,25 @@
 
   if (device_state_mode == kDeviceStateRestoreModeReEnrollmentRequested) {
     return {.mode = EnrollmentConfig::MODE_SERVER_ADVERTISED,
-            .auth_mechanism = EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
             .management_domain = device_state_management_domain};
   }
 
   if (pref_enrollment_auto_start_present && pref_enrollment_auto_start) {
-    return {.mode = EnrollmentConfig::MODE_LOCAL_ADVERTISED,
-            .auth_mechanism = EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE};
+    return {.mode = EnrollmentConfig::MODE_LOCAL_ADVERTISED};
   }
 
   if (oem_is_managed) {
-    return {.mode = EnrollmentConfig::MODE_LOCAL_ADVERTISED,
-            .auth_mechanism = EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE};
+    return {.mode = EnrollmentConfig::MODE_LOCAL_ADVERTISED};
   }
 
   return {.mode = EnrollmentConfig::MODE_NONE};
 }
 
 struct EnrollmentConfig::PrescribedLicense {
-  bool is_license_packaged_with_device;
-  EnrollmentConfig::AssignedUpgradeType assigned_upgrade_type;
-  LicenseType license_type;
+  bool is_license_packaged_with_device = false;
+  EnrollmentConfig::AssignedUpgradeType assigned_upgrade_type =
+      AssignedUpgradeType::kAssignedUpgradeTypeUnspecified;
+  LicenseType license_type = LicenseType::kNone;
 
   static PrescribedLicense GetPrescribedLicense(
       const base::Value::Dict& device_state);
@@ -398,7 +365,6 @@
 EnrollmentConfig::EnrollmentConfig(PrescribedConfig prescribed_config,
                                    PrescribedLicense prescribed_license)
     : mode(prescribed_config.mode),
-      auth_mechanism(prescribed_config.auth_mechanism),
       management_domain(std::move(prescribed_config.management_domain)),
       is_license_packaged_with_device(
           prescribed_license.is_license_packaged_with_device),
@@ -443,11 +409,10 @@
 
 // static
 EnrollmentConfig EnrollmentConfig::GetDemoModeEnrollmentConfig() {
-  policy::EnrollmentConfig config;
-  config.mode = policy::EnrollmentConfig::MODE_ATTESTATION;
-  config.auth_mechanism = AUTH_MECHANISM_ATTESTATION;
-  config.management_domain = policy::kDemoModeDomain;
-  config.license_type = policy::LicenseType::kEnterprise;
+  EnrollmentConfig config;
+  config.mode = EnrollmentConfig::MODE_ATTESTATION;
+  config.management_domain = kDemoModeDomain;
+  config.license_type = LicenseType::kEnterprise;
   return config;
 }
 
@@ -462,7 +427,6 @@
   // needs to be deprecated.
   manual_config.mode =
       management_domain.empty() ? MODE_MANUAL : MODE_MANUAL_REENROLLMENT;
-  manual_config.auth_mechanism = AUTH_MECHANISM_INTERACTIVE;
   return manual_config;
 }
 
@@ -474,8 +438,6 @@
 
   EnrollmentConfig manual_fallback_config = *this;
   manual_fallback_config.mode = GetManualFallbackMode(mode);
-  manual_fallback_config.auth_mechanism =
-      EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE;
 
   return manual_fallback_config;
 }
@@ -484,14 +446,8 @@
   return os << ToStringView(mode);
 }
 
-std::ostream& operator<<(std::ostream& os,
-                         const EnrollmentConfig::AuthMechanism& auth) {
-  return os << ToStringView(auth);
-}
-
 std::ostream& operator<<(std::ostream& os, const EnrollmentConfig& config) {
-  return os << "EnrollmentConfig(" << config.mode << ", "
-            << config.auth_mechanism << ")";
+  return os << "EnrollmentConfig(" << config.mode << ")";
 }
 
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_config.h b/chrome/browser/ash/policy/enrollment/enrollment_config.h
index dcacc1c..a5c8119f 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_config.h
+++ b/chrome/browser/ash/policy/enrollment/enrollment_config.h
@@ -108,27 +108,6 @@
     MODE_ENROLLMENT_TOKEN_INITIAL_MANUAL_FALLBACK = 20,
   };
 
-  // An enumeration of authentication mechanisms that can be used for
-  // enrollment.
-  enum AuthMechanism {
-    // Interactive authentication.
-    // Note: the entry is used as both a signal for interactive enrollment and
-    // as a default value (e.g. in `should_enroll_with_attestation()`).
-    // TODO(b/332529631): Introduce kNone entry to represent default config
-    // that does not require enrollment.
-    AUTH_MECHANISM_INTERACTIVE = 0,
-    // Automatic authentication relying on the attestation process.
-    AUTH_MECHANISM_ATTESTATION = 1,
-    // Prefer to use attestation enrollment, falling back to manual enrollment
-    // if attestation fails or manual enrollment is forced on the system.
-    AUTH_MECHANISM_ATTESTATION_PREFERRED = 2,
-    // Prefer to use automatic enrollment-token-based authentication relying on
-    // the device. If token-based auth fails, fall back to manual enrollment.
-    // As of writing, token-based-enrollment on ChromeOS only happens for Flex
-    // Auto Enrollment.
-    AUTH_MECHANISM_TOKEN_PREFERRED = 3,
-  };
-
   // An enumeration of assigned upgrades that a device can after initial
   // enrollment.
   enum class AssignedUpgradeType {
@@ -207,25 +186,28 @@
            mode == MODE_ATTESTATION_ROLLBACK_FORCED;
   }
 
-  // Whether this configuration is an automatic enrollment mode that has a
-  // manual fallback. I.e. after a failed attempt at automatic enrolling,
-  // manual enrollment will be triggered.
-  bool is_mode_with_manual_fallback() const {
-    return is_mode_attestation_server() ||
-           mode == MODE_ATTESTATION_ROLLBACK_FORCED ||
-           mode == MODE_ENROLLMENT_TOKEN_INITIAL_SERVER_FORCED;
-  }
-
   // Whether this configuration is in attestation mode.
   bool is_mode_attestation() const {
     return is_mode_attestation_client() || is_mode_attestation_server();
   }
 
+  // Whether this configuration is in token-based mode.
+  bool is_mode_token() const {
+    return mode == MODE_ENROLLMENT_TOKEN_INITIAL_SERVER_FORCED;
+  }
+
   // Whether this configuration's mode causes the device to automatically
   // enroll without user interaction.
   bool is_automatic_enrollment() const {
-    return is_mode_attestation() ||
-           mode == MODE_ENROLLMENT_TOKEN_INITIAL_SERVER_FORCED;
+    return is_mode_attestation() || is_mode_token();
+  }
+
+  // Whether this configuration is an automatic enrollment mode that has a
+  // manual fallback. I.e. after a failed attempt at automatic enrolling,
+  // manual enrollment will be triggered.
+  bool is_mode_with_manual_fallback() const {
+    return is_mode_attestation_server() ||
+           mode == MODE_ATTESTATION_ROLLBACK_FORCED || is_mode_token();
   }
 
   // Whether this configuration is in OAuth mode.
@@ -248,10 +230,6 @@
   // Indicates the enrollment flow variant to trigger during OOBE.
   Mode mode = MODE_NONE;
 
-  // The authentication mechanism to use.
-  // TODO(drcrash): Change to best available once ZTE is everywhere.
-  AuthMechanism auth_mechanism = AUTH_MECHANISM_INTERACTIVE;
-
   // The domain to enroll the device to, if applicable. If this is not set, the
   // device may be enrolled to any domain. Note that for the case where the
   // device is not already locked to a certain domain, this value is used for
@@ -282,7 +260,7 @@
 
   // Source of OOBE config, if the device has an OOBE configuration file and
   // that config influences enrollment.
-  OOBEConfigSource oobe_config_source;
+  OOBEConfigSource oobe_config_source = OOBEConfigSource::kNone;
 
  private:
   // Hold fields to be filled corresponding to ones in `EnrollmentConfig`.
@@ -295,9 +273,6 @@
 
 std::ostream& operator<<(std::ostream& os, const EnrollmentConfig::Mode& mode);
 
-std::ostream& operator<<(std::ostream& os,
-                         const EnrollmentConfig::AuthMechanism& auth);
-
 std::ostream& operator<<(std::ostream& os, const EnrollmentConfig& config);
 
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_config_unittest.cc b/chrome/browser/ash/policy/enrollment/enrollment_config_unittest.cc
index 7f7341fa..fffb8bc 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_config_unittest.cc
+++ b/chrome/browser/ash/policy/enrollment/enrollment_config_unittest.cc
@@ -142,8 +142,8 @@
     const auto config = GetPrescribedConfig();
     EXPECT_EQ(EnrollmentConfig::MODE_NONE, config.mode);
     EXPECT_TRUE(config.management_domain.empty());
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              config.auth_mechanism);
+    EXPECT_FALSE(config.is_automatic_enrollment());
+    EXPECT_FALSE(config.is_mode_oauth());
   }
 
   // Set signals in increasing order of precedence, check results.
@@ -155,8 +155,7 @@
     const auto config = GetPrescribedConfig();
     EXPECT_EQ(EnrollmentConfig::MODE_LOCAL_ADVERTISED, config.mode);
     EXPECT_TRUE(config.management_domain.empty());
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              config.auth_mechanism);
+    EXPECT_TRUE(config.is_mode_oauth());
     EXPECT_CHECK_DEATH(config.GetManualFallbackConfig());
   }
 
@@ -170,8 +169,7 @@
     const auto config = GetPrescribedConfig();
     EXPECT_EQ(EnrollmentConfig::MODE_LOCAL_ADVERTISED, config.mode);
     EXPECT_TRUE(config.management_domain.empty());
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              config.auth_mechanism);
+    EXPECT_TRUE(config.is_mode_oauth());
     EXPECT_CHECK_DEATH(config.GetManualFallbackConfig());
   }
 
@@ -185,8 +183,7 @@
     const auto config = GetPrescribedConfig();
     EXPECT_EQ(EnrollmentConfig::MODE_SERVER_ADVERTISED, config.mode);
     EXPECT_EQ(kTestDomain, config.management_domain);
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              config.auth_mechanism);
+    EXPECT_TRUE(config.is_mode_oauth());
     EXPECT_CHECK_DEATH(config.GetManualFallbackConfig());
   }
 
@@ -199,8 +196,7 @@
     const auto config = GetPrescribedConfig();
     EXPECT_EQ(EnrollmentConfig::MODE_LOCAL_FORCED, config.mode);
     EXPECT_TRUE(config.management_domain.empty());
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              config.auth_mechanism);
+    EXPECT_TRUE(config.is_mode_oauth());
     EXPECT_CHECK_DEATH(config.GetManualFallbackConfig());
   }
 
@@ -214,8 +210,7 @@
     const auto config = GetPrescribedConfig();
     EXPECT_EQ(EnrollmentConfig::MODE_LOCAL_FORCED, config.mode);
     EXPECT_TRUE(config.management_domain.empty());
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              config.auth_mechanism);
+    EXPECT_TRUE(config.is_mode_oauth());
     EXPECT_CHECK_DEATH(config.GetManualFallbackConfig());
   }
 
@@ -230,15 +225,14 @@
     EXPECT_EQ(EnrollmentConfig::MODE_ATTESTATION_INITIAL_SERVER_FORCED,
               config.mode);
     EXPECT_EQ(kTestDomain, config.management_domain);
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED,
-              config.auth_mechanism);
+    EXPECT_TRUE(config.is_automatic_enrollment());
+    EXPECT_TRUE(config.is_mode_attestation());
 
     const auto manual_fallback_config = config.GetManualFallbackConfig();
     EXPECT_TRUE(manual_fallback_config.is_manual_fallback());
     EXPECT_EQ(EnrollmentConfig::MODE_ATTESTATION_INITIAL_MANUAL_FALLBACK,
               manual_fallback_config.mode);
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              manual_fallback_config.auth_mechanism);
+    EXPECT_TRUE(manual_fallback_config.is_mode_oauth());
   }
 
   // Server-backed state: forced attestation-based re-enrollment.
@@ -251,15 +245,14 @@
     const auto config = GetPrescribedConfig();
     EXPECT_EQ(EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED, config.mode);
     EXPECT_EQ(kTestDomain, config.management_domain);
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED,
-              config.auth_mechanism);
+    EXPECT_TRUE(config.is_automatic_enrollment());
+    EXPECT_TRUE(config.is_mode_attestation());
 
     const auto manual_fallback_config = config.GetManualFallbackConfig();
     EXPECT_TRUE(manual_fallback_config.is_manual_fallback());
     EXPECT_EQ(EnrollmentConfig::MODE_ATTESTATION_MANUAL_FALLBACK,
               manual_fallback_config.mode);
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              manual_fallback_config.auth_mechanism);
+    EXPECT_TRUE(manual_fallback_config.is_mode_oauth());
   }
 
   // Server-backed state: forced initial enrollment.
@@ -272,8 +265,7 @@
     const auto config = GetPrescribedConfig();
     EXPECT_EQ(EnrollmentConfig::MODE_INITIAL_SERVER_FORCED, config.mode);
     EXPECT_EQ(kTestDomain, config.management_domain);
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              config.auth_mechanism);
+    EXPECT_TRUE(config.is_mode_oauth());
     EXPECT_CHECK_DEATH(config.GetManualFallbackConfig());
   }
 
@@ -287,8 +279,7 @@
     const auto config = GetPrescribedConfig();
     EXPECT_EQ(EnrollmentConfig::MODE_SERVER_FORCED, config.mode);
     EXPECT_EQ(kTestDomain, config.management_domain);
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              config.auth_mechanism);
+    EXPECT_TRUE(config.is_mode_oauth());
     EXPECT_CHECK_DEATH(config.GetManualFallbackConfig());
   }
 
@@ -299,15 +290,14 @@
     const auto config = GetPrescribedConfig();
     EXPECT_EQ(EnrollmentConfig::MODE_ATTESTATION_ROLLBACK_FORCED, config.mode);
     EXPECT_TRUE(config.management_domain.empty());
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED,
-              config.auth_mechanism);
+    EXPECT_TRUE(config.is_automatic_enrollment());
+    EXPECT_TRUE(config.is_mode_attestation());
 
     const auto manual_fallback_config = config.GetManualFallbackConfig();
     EXPECT_TRUE(manual_fallback_config.is_manual_fallback());
     EXPECT_EQ(EnrollmentConfig::MODE_ATTESTATION_ROLLBACK_MANUAL_FALLBACK,
               manual_fallback_config.mode);
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              manual_fallback_config.auth_mechanism);
+    EXPECT_TRUE(manual_fallback_config.is_mode_oauth());
   }
 }
 
@@ -320,8 +310,7 @@
     const auto config = GetPrescribedConfig();
     EXPECT_EQ(EnrollmentConfig::MODE_NONE, config.mode);
     EXPECT_TRUE(config.management_domain.empty());
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              config.auth_mechanism);
+    EXPECT_FALSE(config.should_enroll());
   }
 
   // Advertised enrollment gets ignored.
@@ -332,8 +321,7 @@
     const auto config = GetPrescribedConfig();
     EXPECT_EQ(EnrollmentConfig::MODE_NONE, config.mode);
     EXPECT_TRUE(config.management_domain.empty());
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              config.auth_mechanism);
+    EXPECT_FALSE(config.should_enroll());
   }
 
   // If the device is enterprise-managed, the management domain gets pulled from
@@ -343,8 +331,7 @@
     const auto config = GetPrescribedConfig();
     EXPECT_EQ(EnrollmentConfig::MODE_NONE, config.mode);
     EXPECT_EQ(kTestDomain, config.management_domain);
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              config.auth_mechanism);
+    EXPECT_FALSE(config.should_enroll());
   }
 
   // If enrollment recovery is on, this is signaled in |config.mode|.
@@ -353,8 +340,7 @@
     const auto config = GetPrescribedConfig();
     EXPECT_EQ(EnrollmentConfig::MODE_RECOVERY, config.mode);
     EXPECT_EQ(kTestDomain, config.management_domain);
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              config.auth_mechanism);
+    EXPECT_TRUE(config.is_mode_oauth());
     EXPECT_CHECK_DEATH(config.GetManualFallbackConfig());
   }
 }
@@ -364,16 +350,14 @@
 
   EXPECT_EQ(EnrollmentConfig::MODE_ATTESTATION, config.mode);
   EXPECT_EQ(policy::kDemoModeDomain, config.management_domain);
-  EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_ATTESTATION,
-            config.auth_mechanism);
+  EXPECT_TRUE(config.is_automatic_enrollment());
+  EXPECT_TRUE(config.is_mode_attestation());
   EXPECT_CHECK_DEATH(config.GetManualFallbackConfig());
 }
 
-TEST_F(EnrollmentConfigTest, DISABLED_GetEffectivePrescribedEnrollmentConfig) {
+TEST_F(EnrollmentConfigTest, GetEffectivePrescribedEnrollmentConfig) {
   EnrollmentConfig config;
   config.mode = EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED;
-  config.auth_mechanism =
-      EnrollmentConfig::AUTH_MECHANISM_ATTESTATION_PREFERRED;
   config.management_domain = kTestDomain;
 
   ASSERT_TRUE(config.should_enroll());
@@ -390,9 +374,8 @@
     const auto manual_config = config.GetEffectiveConfig();
 
     EXPECT_EQ(EnrollmentConfig::MODE_MANUAL, manual_config.mode);
-    EXPECT_TRUE(config.management_domain.empty());
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              manual_config.auth_mechanism);
+    EXPECT_TRUE(manual_config.management_domain.empty());
+    EXPECT_TRUE(manual_config.is_mode_oauth());
     EXPECT_EQ(LicenseType::kNone, manual_config.license_type);
     EXPECT_CHECK_DEATH(manual_config.GetManualFallbackConfig());
   }
@@ -410,9 +393,8 @@
     const auto manual_config = config.GetEffectiveConfig();
 
     EXPECT_EQ(EnrollmentConfig::MODE_MANUAL, manual_config.mode);
-    EXPECT_TRUE(config.management_domain.empty());
-    EXPECT_EQ(EnrollmentConfig::AUTH_MECHANISM_INTERACTIVE,
-              manual_config.auth_mechanism);
+    EXPECT_TRUE(manual_config.management_domain.empty());
+    EXPECT_TRUE(manual_config.is_mode_oauth());
     EXPECT_EQ(LicenseType::kEducation, manual_config.license_type);
     EXPECT_CHECK_DEATH(manual_config.GetManualFallbackConfig());
   }
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_handler.cc b/chrome/browser/ash/policy/enrollment/enrollment_handler.cc
index a7708d8..3a09d648 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_handler.cc
+++ b/chrome/browser/ash/policy/enrollment/enrollment_handler.cc
@@ -207,9 +207,7 @@
   CHECK(!client_->is_registered());
   CHECK_EQ(DM_STATUS_SUCCESS, client_->last_dm_status());
   CHECK_EQ(dm_auth_.empty(), enrollment_config_.is_mode_attestation());
-  CHECK(enrollment_config_.auth_mechanism !=
-            EnrollmentConfig::AUTH_MECHANISM_ATTESTATION ||
-        attestation_flow_);
+  CHECK(enrollment_config_.is_mode_attestation() || attestation_flow_);
   register_params_ =
       std::make_unique<CloudPolicyClient::RegistrationParameters>(
           em::DeviceRegisterRequest::DEVICE,
diff --git a/chrome/browser/ash/power/idle_action_warning_observer.cc b/chrome/browser/ash/power/idle_action_warning_observer.cc
index 26f967d..57e5d51 100644
--- a/chrome/browser/ash/power/idle_action_warning_observer.cc
+++ b/chrome/browser/ash/power/idle_action_warning_observer.cc
@@ -4,15 +4,23 @@
 
 #include "chrome/browser/ash/power/idle_action_warning_observer.h"
 
+#include <memory>
+
 #include "ash/constants/ash_pref_names.h"
+#include "base/memory/raw_ptr.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/scoped_observation.h"
 #include "base/time/time.h"
 #include "chrome/browser/ash/login/demo_mode/demo_session.h"
 #include "chrome/browser/ash/power/idle_action_warning_dialog_view.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/power/power_policy_controller.h"
+#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
 #include "components/prefs/pref_service.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
 
 namespace ash {
 namespace {
@@ -25,33 +33,78 @@
 };
 
 void ReportMetricsForDemoMode(IdleLogoutWarningEvent event) {
-  if (DemoSession::IsDeviceInDemoMode())
+  if (DemoSession::IsDeviceInDemoMode()) {
     UMA_HISTOGRAM_ENUMERATION("DemoMode.IdleLogoutWarningEvent", event);
+  }
 }
 
 chromeos::PowerPolicyController::Action GetIdleAction(bool on_battery_power) {
   PrefService* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
   int action;
-  if (on_battery_power)
+  if (on_battery_power) {
     action = prefs->GetInteger(ash::prefs::kPowerBatteryIdleAction);
-  else
+  } else {
     action = prefs->GetInteger(ash::prefs::kPowerAcIdleAction);
+  }
   return static_cast<chromeos::PowerPolicyController::Action>(action);
 }
 
 }  // namespace
 
+class IdleActionWarningObserver::PowerManagerObserver
+    : public chromeos::PowerManagerClient::Observer {
+ public:
+  PowerManagerObserver(IdleActionWarningObserver* idle_action_warning_observer,
+                       chromeos::PowerManagerClient* client)
+      : idle_action_warning_observer_(idle_action_warning_observer) {
+    observation_.Observe(client);
+  }
+
+  void IdleActionImminent(base::TimeDelta time_until_idle_action) override {
+    idle_action_warning_observer_->IdleActionImminent(time_until_idle_action);
+  }
+  void IdleActionDeferred() override {
+    idle_action_warning_observer_->IdleActionDeferred();
+  }
+  void PowerChanged(
+      const power_manager::PowerSupplyProperties& proto) override {
+    idle_action_warning_observer_->PowerChanged(proto);
+  }
+
+ private:
+  base::ScopedObservation<chromeos::PowerManagerClient, PowerManagerObserver>
+      observation_{this};
+  raw_ptr<IdleActionWarningObserver> idle_action_warning_observer_;
+};
+
+class IdleActionWarningObserver::WidgetObserver : public views::WidgetObserver {
+ public:
+  WidgetObserver(IdleActionWarningObserver* idle_action_warning_observer,
+                 views::Widget* widget)
+      : idle_action_warning_observer_(idle_action_warning_observer) {
+    observation_.Observe(widget);
+  }
+
+  void OnWidgetDestroying(views::Widget* widget) override {
+    idle_action_warning_observer_->OnWidgetDestroying(widget);
+  }
+
+ private:
+  base::ScopedObservation<views::Widget, WidgetObserver> observation_{this};
+  raw_ptr<IdleActionWarningObserver> idle_action_warning_observer_;
+};
+
 IdleActionWarningObserver::IdleActionWarningObserver() {
-  chromeos::PowerManagerClient::Get()->AddObserver(this);
+  power_manager_observer_ = std::make_unique<PowerManagerObserver>(
+      this, chromeos::PowerManagerClient::Get());
 }
 
 IdleActionWarningObserver::~IdleActionWarningObserver() {
-  chromeos::PowerManagerClient::Get()->RemoveObserver(this);
+  power_manager_observer_.reset();
+  widged_observer_.reset();
   if (warning_dialog_) {
-    warning_dialog_->GetWidget()->RemoveObserver(this);
     warning_dialog_->CloseDialog();
   }
-  CHECK(!IsInObserverList());
 }
 
 void IdleActionWarningObserver::IdleActionImminent(
@@ -71,7 +124,8 @@
     warning_dialog_->Update(idle_action_time);
   } else {
     warning_dialog_ = new IdleActionWarningDialogView(idle_action_time);
-    warning_dialog_->GetWidget()->AddObserver(this);
+    widged_observer_ =
+        std::make_unique<WidgetObserver>(this, warning_dialog_->GetWidget());
     ReportMetricsForDemoMode(IdleLogoutWarningEvent::kShown);
   }
 }
@@ -90,7 +144,7 @@
 void IdleActionWarningObserver::OnWidgetDestroying(views::Widget* widget) {
   DCHECK(warning_dialog_);
   DCHECK_EQ(widget, warning_dialog_->GetWidget());
-  warning_dialog_->GetWidget()->RemoveObserver(this);
+  widged_observer_.reset();
   warning_dialog_ = nullptr;
 }
 
diff --git a/chrome/browser/ash/power/idle_action_warning_observer.h b/chrome/browser/ash/power/idle_action_warning_observer.h
index 74a1c81..030abd6 100644
--- a/chrome/browser/ash/power/idle_action_warning_observer.h
+++ b/chrome/browser/ash/power/idle_action_warning_observer.h
@@ -5,9 +5,15 @@
 #ifndef CHROME_BROWSER_ASH_POWER_IDLE_ACTION_WARNING_OBSERVER_H_
 #define CHROME_BROWSER_ASH_POWER_IDLE_ACTION_WARNING_OBSERVER_H_
 
+#include <memory>
+
 #include "base/memory/raw_ptr.h"
-#include "chromeos/dbus/power/power_manager_client.h"
-#include "ui/views/widget/widget_observer.h"
+#include "base/time/time.h"
+#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
+
+namespace views {
+class Widget;
+}
 
 namespace ash {
 
@@ -15,8 +21,7 @@
 
 // Listens for notifications that the idle action is imminent and shows a
 // warning dialog to the user.
-class IdleActionWarningObserver : public chromeos::PowerManagerClient::Observer,
-                                  public views::WidgetObserver {
+class IdleActionWarningObserver {
  public:
   IdleActionWarningObserver();
 
@@ -24,19 +29,25 @@
   IdleActionWarningObserver& operator=(const IdleActionWarningObserver&) =
       delete;
 
-  ~IdleActionWarningObserver() override;
-
-  // PowerManagerClient::Observer:
-  void IdleActionImminent(base::TimeDelta time_until_idle_action) override;
-  void IdleActionDeferred() override;
-  void PowerChanged(const power_manager::PowerSupplyProperties& proto) override;
+  ~IdleActionWarningObserver();
 
  private:
+  class PowerManagerObserver;
+  class WidgetObserver;
+
+  // PowerManagerClient::Observer:
+  void IdleActionImminent(base::TimeDelta time_until_idle_action);
+  void IdleActionDeferred();
+  void PowerChanged(const power_manager::PowerSupplyProperties& proto);
+
   // views::WidgetObserver:
-  void OnWidgetDestroying(views::Widget* widget) override;
+  void OnWidgetDestroying(views::Widget* widget);
 
   void HideDialogIfPresent();
 
+  std::unique_ptr<PowerManagerObserver> power_manager_observer_;
+  std::unique_ptr<WidgetObserver> widged_observer_;
+
   raw_ptr<IdleActionWarningDialogView> warning_dialog_ = nullptr;  // Not owned.
 
   // Used to derive the correct idle action (IdleActionAC/IdleActionBattery).
diff --git a/chrome/browser/banners/app_banner_manager_browsertest.cc b/chrome/browser/banners/app_banner_manager_browsertest.cc
index c2e8732..ecfae0a 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest.cc
+++ b/chrome/browser/banners/app_banner_manager_browsertest.cc
@@ -696,7 +696,13 @@
                                 1);
 }
 
-IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, PreferRelatedAppUnknown) {
+// Flaky on Android. crbug.com/369804412
+#if BUILDFLAG(IS_ANDROID)
+#define MAYBE_PreferRelatedAppUnknown DISABLED_PreferRelatedAppUnknown
+#else
+#define MAYBE_PreferRelatedAppUnknown PreferRelatedAppUnknown
+#endif
+IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, MAYBE_PreferRelatedAppUnknown) {
   std::unique_ptr<AppBannerManagerTest> manager(CreateAppBannerManager());
 
   GURL test_url = embedded_test_server()->GetURL(
@@ -713,7 +719,6 @@
 #else
 #define MAYBE_PreferRelatedChromeApp PreferRelatedChromeApp
 #endif
-
 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, MAYBE_PreferRelatedChromeApp) {
   std::unique_ptr<AppBannerManagerTest> manager(CreateAppBannerManager());
   base::HistogramTester histograms;
@@ -1086,7 +1091,13 @@
             InstallableWebAppCheckResult::kYes_Promotable);
 }
 
-IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, ImplicitName) {
+// Flaky on Android. crbug.com/369804412
+#if BUILDFLAG(IS_ANDROID)
+#define MAYBE_ImplicitName DISABLED_ImplicitName
+#else
+#define MAYBE_ImplicitName ImplicitName
+#endif
+IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, MAYBE_ImplicitName) {
   std::unique_ptr<AppBannerManagerTest> manager(CreateAppBannerManager());
 
   GURL test_url = embedded_test_server()->GetURL(
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 95ef0f3..d7e9610 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -191,6 +191,7 @@
 #include "chrome/browser/ui/prefs/pref_watcher.h"
 #include "chrome/browser/ui/tab_contents/chrome_web_contents_view_delegate.h"
 #include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/web_applications/navigation_capturing_redirection_throttle.h"
 #include "chrome/browser/ui/webid/identity_dialog_controller.h"
 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
 #include "chrome/browser/ui/webui/log_web_ui_url.h"
@@ -199,7 +200,6 @@
 #include "chrome/browser/usb/chrome_usb_delegate.h"
 #include "chrome/browser/vr/vr_tab_helper.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
-#include "chrome/browser/web_applications/link_capturing_redirect_navigation_throttle.h"
 #include "chrome/browser/webapps/web_app_offline.h"
 #include "chrome/browser/webauthn/webauthn_pref_names.h"
 #include "chrome/common/buildflags.h"
@@ -5427,10 +5427,10 @@
   }
 
   std::unique_ptr<content::NavigationThrottle>
-      link_capturing_redirect_nav_throttle =
-          web_app::LinkCapturingRedirectNavigationThrottle::MaybeCreate(handle);
-  if (link_capturing_redirect_nav_throttle) {
-    throttles.push_back(std::move(link_capturing_redirect_nav_throttle));
+      navigation_capturing_redirection_throttle =
+          web_app::NavigationCapturingRedirectionThrottle::MaybeCreate(handle);
+  if (navigation_capturing_redirection_throttle) {
+    throttles.push_back(std::move(navigation_capturing_redirection_throttle));
   }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
diff --git a/chrome/browser/chromeos/policy/default_notifications_setting_browsertest.cc b/chrome/browser/chromeos/policy/default_notifications_setting_browsertest.cc
index 0abcfc456..ac7011e3 100644
--- a/chrome/browser/chromeos/policy/default_notifications_setting_browsertest.cc
+++ b/chrome/browser/chromeos/policy/default_notifications_setting_browsertest.cc
@@ -9,7 +9,6 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/permissions/features.h"
 #include "components/policy/policy_constants.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -32,8 +31,6 @@
       public testing::WithParamInterface<int> {
  public:
   void SetUpInProcessBrowserTestFixture() override {
-    feature_list_.InitAndDisableFeature(
-        permissions::features::kPermissionDedicatedCpssSetting);
     policy::PolicyTest::SetUpInProcessBrowserTestFixture();
 
     // Use param 0 to test the policy unset case.
@@ -95,7 +92,7 @@
       "  querySelector('settings-main').shadowRoot."
       "  querySelector('settings-basic-page').shadowRoot."
       "  querySelector('settings-privacy-page').shadowRoot."
-      "  querySelectorAll('settings-collapse-radio-button');";
+      "  querySelectorAll('cr-radio-button');";
   std::string kGetRadiosChecked = kGetRadios +
                                   "let radiosChecked = [];"
                                   "radiosChecked.push(radios[0].checked);"
@@ -123,28 +120,28 @@
       // Policy not set.
       EXPECT_TRUE(radios_checked_list[0].GetBool());
       EXPECT_FALSE(radios_checked_list[1].GetBool());
-      EXPECT_FALSE(radios_checked_list[2].GetBool());
+      EXPECT_TRUE(radios_checked_list[2].GetBool());
       EXPECT_TRUE(radios_enabled_list[0].GetBool());
       EXPECT_TRUE(radios_enabled_list[1].GetBool());
       EXPECT_TRUE(radios_enabled_list[2].GetBool());
       break;
     case 1:
       // Allow sites to show desktop notifications.
-      EXPECT_TRUE(radios_checked_list[0].GetBool());
+      EXPECT_FALSE(radios_checked_list[0].GetBool());
       EXPECT_FALSE(radios_checked_list[1].GetBool());
       EXPECT_FALSE(radios_checked_list[2].GetBool());
       EXPECT_TRUE(radios_enabled_list[0].GetBool());
       EXPECT_TRUE(radios_enabled_list[1].GetBool());
-      EXPECT_FALSE(radios_enabled_list[2].GetBool());
+      EXPECT_TRUE(radios_enabled_list[2].GetBool());
       break;
     case 2:
       // Don't allow sites to show desktop notifications.
       EXPECT_FALSE(radios_checked_list[0].GetBool());
       EXPECT_FALSE(radios_checked_list[1].GetBool());
-      EXPECT_TRUE(radios_checked_list[2].GetBool());
-      EXPECT_FALSE(radios_enabled_list[0].GetBool());
-      EXPECT_FALSE(radios_enabled_list[1].GetBool());
-      EXPECT_FALSE(radios_enabled_list[2].GetBool());
+      EXPECT_FALSE(radios_checked_list[2].GetBool());
+      EXPECT_TRUE(radios_enabled_list[0].GetBool());
+      EXPECT_TRUE(radios_enabled_list[1].GetBool());
+      EXPECT_TRUE(radios_enabled_list[2].GetBool());
       break;
     case 3:
       // Ask every time a site wants to show desktop notifications.
@@ -153,7 +150,7 @@
       EXPECT_FALSE(radios_checked_list[2].GetBool());
       EXPECT_TRUE(radios_enabled_list[0].GetBool());
       EXPECT_TRUE(radios_enabled_list[1].GetBool());
-      EXPECT_FALSE(radios_enabled_list[2].GetBool());
+      EXPECT_TRUE(radios_enabled_list[2].GetBool());
       break;
   }
 }
diff --git a/chrome/browser/content_settings/BUILD.gn b/chrome/browser/content_settings/BUILD.gn
index 482a28a3..f3cb178 100644
--- a/chrome/browser/content_settings/BUILD.gn
+++ b/chrome/browser/content_settings/BUILD.gn
@@ -37,7 +37,6 @@
   } else {
     sources += [
       "generated_cookie_prefs.h",
-      "generated_notification_pref.h",
       "generated_permission_prompting_behavior_pref.h",
     ]
 
@@ -93,7 +92,6 @@
   } else {
     sources += [
       "generated_cookie_prefs.cc",
-      "generated_notification_pref.cc",
       "generated_permission_prompting_behavior_pref.cc",
     ]
 
@@ -293,10 +291,7 @@
   ]
 
   if (!is_android) {
-    sources += [
-      "generated_cookie_prefs_unittest.cc",
-      "generated_notification_pref_unittest.cc",
-    ]
+    sources += [ "generated_cookie_prefs_unittest.cc" ]
 
     deps += [
       "//chrome/browser/extensions",
diff --git a/chrome/browser/content_settings/generated_notification_pref.cc b/chrome/browser/content_settings/generated_notification_pref.cc
deleted file mode 100644
index c031a764..0000000
--- a/chrome/browser/content_settings/generated_notification_pref.cc
+++ /dev/null
@@ -1,233 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/content_settings/generated_notification_pref.h"
-
-#include "chrome/browser/content_settings/generated_permission_prompting_behavior_pref.h"
-#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/common/extensions/api/settings_private.h"
-#include "components/content_settings/core/browser/content_settings_utils.h"
-#include "components/content_settings/core/common/content_settings.h"
-#include "components/content_settings/core/common/pref_names.h"
-
-namespace settings_api = extensions::api::settings_private;
-typedef extensions::settings_private::GeneratedPref GeneratedPref;
-
-namespace content_settings {
-
-namespace {
-
-bool IsDefaultNotificationContentSettingUserControlled(
-    HostContentSettingsMap* map) {
-  content_settings::ProviderType content_setting_provider;
-  map->GetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS,
-                                &content_setting_provider);
-  auto content_setting_source =
-      content_settings::GetSettingSourceFromProviderType(
-          content_setting_provider);
-  return content_setting_source == SettingSource::kUser;
-}
-
-}  // namespace
-
-GeneratedNotificationPref::GeneratedNotificationPref(Profile* profile)
-    : profile_(profile) {
-  user_prefs_registrar_.Init(profile->GetPrefs());
-  user_prefs_registrar_.Add(
-      prefs::kEnableQuietNotificationPermissionUi,
-      base::BindRepeating(
-          &GeneratedNotificationPref::OnNotificationPreferencesChanged,
-          base::Unretained(this)));
-
-  host_content_settings_map_ =
-      HostContentSettingsMapFactory::GetForProfile(profile_);
-  content_setting_observation_.Observe(host_content_settings_map_.get());
-}
-
-GeneratedNotificationPref::~GeneratedNotificationPref() = default;
-
-void GeneratedNotificationPref::OnContentSettingChanged(
-    const ContentSettingsPattern& primary_pattern,
-    const ContentSettingsPattern& secondary_pattern,
-    ContentSettingsTypeSet content_type_set) {
-  if (content_type_set.Contains(ContentSettingsType::NOTIFICATIONS)) {
-    NotifyObservers(kGeneratedNotificationPref);
-  }
-}
-
-void GeneratedNotificationPref::OnNotificationPreferencesChanged() {
-  NotifyObservers(kGeneratedNotificationPref);
-}
-
-extensions::settings_private::SetPrefResult GeneratedNotificationPref::SetPref(
-    const base::Value* value) {
-  if (!value->is_int()) {
-    return extensions::settings_private::SetPrefResult::PREF_TYPE_MISMATCH;
-  }
-
-  auto selection = static_cast<SettingsState>(value->GetInt());
-
-  if (selection != SettingsState::kCanPromptWithAlwaysLoudUI &&
-      selection != SettingsState::kCanPromptWithAlwaysQuietUI &&
-      selection != SettingsState::kBlocked) {
-    return extensions::settings_private::SetPrefResult::PREF_TYPE_MISMATCH;
-  }
-
-  auto current_content_setting =
-      host_content_settings_map_->GetDefaultContentSetting(
-          ContentSettingsType::NOTIFICATIONS);
-  auto new_content_setting = selection == SettingsState::kBlocked
-                                 ? ContentSetting::CONTENT_SETTING_BLOCK
-                                 : ContentSetting::CONTENT_SETTING_ASK;
-
-  // Do not modify content setting if its setting source is not user.
-  // If there's no actual change, the check doesn't apply.
-  if (current_content_setting != new_content_setting &&
-      !IsDefaultNotificationContentSettingUserControlled(
-          host_content_settings_map_)) {
-    return extensions::settings_private::SetPrefResult::PREF_NOT_MODIFIABLE;
-  }
-  if (selection != SettingsState::kBlocked) {
-    const PrefService::Preference* quieter_pref =
-        profile_->GetPrefs()->FindPreference(
-            prefs::kEnableQuietNotificationPermissionUi);
-    bool quieter_value = quieter_pref->GetValue()->GetBool();
-    bool new_quieter_value =
-        selection != SettingsState::kCanPromptWithAlwaysLoudUI;
-
-    // Do not modify the preference value if the user is unable to change its
-    // value. If there's no actual change, this check doesn't apply.
-    if (quieter_value != new_quieter_value &&
-        !quieter_pref->IsUserModifiable()) {
-      return extensions::settings_private::SetPrefResult::PREF_NOT_MODIFIABLE;
-    }
-    profile_->GetPrefs()->SetBoolean(
-        prefs::kEnableQuietNotificationPermissionUi, new_quieter_value);
-  }
-
-  host_content_settings_map_->SetDefaultContentSetting(
-      ContentSettingsType::NOTIFICATIONS, new_content_setting);
-  return extensions::settings_private::SetPrefResult::SUCCESS;
-}
-
-settings_api::PrefObject GeneratedNotificationPref::GetPrefObject() const {
-  settings_api::PrefObject pref_object;
-  pref_object.key = kGeneratedNotificationPref;
-  pref_object.type = settings_api::PrefType::kNumber;
-
-  const auto quieter_pref_enabled =
-      profile_->GetPrefs()
-          ->FindPreference(prefs::kEnableQuietNotificationPermissionUi)
-          ->GetValue()
-          ->GetBool();
-  const auto notification_content_setting =
-      host_content_settings_map_->GetDefaultContentSetting(
-          ContentSettingsType::NOTIFICATIONS);
-  const auto notification_content_setting_enabled =
-      notification_content_setting != ContentSetting::CONTENT_SETTING_BLOCK;
-
-  if (notification_content_setting_enabled && quieter_pref_enabled) {
-    pref_object.value = base::Value(
-        static_cast<int>(SettingsState::kCanPromptWithAlwaysQuietUI));
-  } else if (notification_content_setting_enabled) {
-    pref_object.value = base::Value(
-        static_cast<int>(SettingsState::kCanPromptWithAlwaysLoudUI));
-  } else {
-    DCHECK_EQ(ContentSetting::CONTENT_SETTING_BLOCK,
-              notification_content_setting);
-    pref_object.value = base::Value(static_cast<int>(SettingsState::kBlocked));
-  }
-
-  ApplyNotificationManagementState(*profile_, pref_object);
-
-  return pref_object;
-}
-
-/* static */
-void GeneratedNotificationPref::ApplyNotificationManagementState(
-    Profile& profile,
-    settings_api::PrefObject& pref_object) {
-  HostContentSettingsMap* map =
-      HostContentSettingsMapFactory::GetForProfile(&profile);
-  content_settings::ProviderType content_setting_provider;
-  auto content_setting = map->GetDefaultContentSetting(
-      ContentSettingsType::NOTIFICATIONS, &content_setting_provider);
-  auto content_setting_source =
-      content_settings::GetSettingSourceFromProviderType(
-          content_setting_provider);
-  bool content_setting_enforced =
-      content_setting_source != content_settings::SettingSource::kUser;
-
-  const PrefService::Preference* quieter_ui_pref =
-      profile.GetPrefs()->FindPreference(
-          prefs::kEnableQuietNotificationPermissionUi);
-  auto quieter_ui_on = quieter_ui_pref->GetValue()->GetBool();
-  auto quieter_ui_enforced = !quieter_ui_pref->IsUserModifiable();
-  auto quieter_ui_recommended =
-      quieter_ui_pref->GetRecommendedValue() != nullptr;
-  auto quieter_ui_recommended_on =
-      (quieter_ui_recommended &&
-       quieter_ui_pref->GetRecommendedValue()->GetBool());
-
-  if (!content_setting_enforced && !quieter_ui_enforced &&
-      !quieter_ui_recommended) {
-    // No notification setting is enforced or recommended.
-    return;
-  }
-
-  if (content_setting_enforced) {
-    pref_object.enforcement = settings_api::Enforcement::kEnforced;
-
-    if (content_setting == CONTENT_SETTING_BLOCK) {
-      // Preference is fully managed by the content setting.
-      GeneratedPref::ApplyControlledByFromContentSettingSource(
-          &pref_object, content_setting_source);
-      return;
-    } else if (quieter_ui_enforced) {
-      // Preference is fully managed by the content setting and quieter ui pref.
-      GeneratedPref::ApplyControlledByFromPref(&pref_object, quieter_ui_pref);
-      return;
-    }
-    GeneratedPref::ApplyControlledByFromContentSettingSource(
-        &pref_object, content_setting_source);
-
-    DCHECK(content_setting_enforced && !quieter_ui_enforced);
-    // Since content setting is enforced but the quieter ui pref is not,
-    // user can choose from 2 options.
-    GeneratedPref::AddUserSelectableValue(
-        &pref_object,
-        static_cast<int>(SettingsState::kCanPromptWithAlwaysLoudUI));
-    GeneratedPref::AddUserSelectableValue(
-        &pref_object,
-        static_cast<int>(SettingsState::kCanPromptWithAlwaysQuietUI));
-
-    if (quieter_ui_recommended) {
-      pref_object.recommended_value = base::Value(
-          static_cast<int>(quieter_ui_recommended_on
-                               ? SettingsState::kCanPromptWithAlwaysQuietUI
-                               : SettingsState::kCanPromptWithAlwaysLoudUI));
-    }
-    return;
-  }
-
-  if (quieter_ui_enforced) {
-    // Quieter ui pref is enforced, but the content setting is not, so the user
-    // can choose from 2 options
-    pref_object.enforcement = settings_api::Enforcement::kEnforced;
-    GeneratedPref::ApplyControlledByFromPref(&pref_object, quieter_ui_pref);
-    GeneratedPref::AddUserSelectableValue(
-        &pref_object,
-        static_cast<int>(quieter_ui_on
-                             ? SettingsState::kCanPromptWithAlwaysQuietUI
-                             : SettingsState::kCanPromptWithAlwaysLoudUI));
-    GeneratedPref::AddUserSelectableValue(
-        &pref_object, static_cast<int>(SettingsState::kBlocked));
-  }
-  // If neither of notification content setting nor quieter ui preference is
-  // enforced, but quieter ui preference is recommended, then recommended value
-  // is ignored since it's ambiguous given that notification content setting
-  // can be blocked.
-}
-
-}  // namespace content_settings
diff --git a/chrome/browser/content_settings/generated_notification_pref.h b/chrome/browser/content_settings/generated_notification_pref.h
deleted file mode 100644
index 98b32f3..0000000
--- a/chrome/browser/content_settings/generated_notification_pref.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CONTENT_SETTINGS_GENERATED_NOTIFICATION_PREF_H_
-#define CHROME_BROWSER_CONTENT_SETTINGS_GENERATED_NOTIFICATION_PREF_H_
-
-#include "base/memory/raw_ptr.h"
-#include "chrome/browser/extensions/api/settings_private/generated_pref.h"
-
-#include "base/scoped_observation.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/content_settings/core/browser/content_settings_observer.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "components/prefs/pref_change_registrar.h"
-
-namespace content_settings {
-
-// A generated preference which represents the effective Notification setting
-// state based on the Notification content setting and quieter UI user
-// preference.
-// This class will be removed when kPermissionDedicatedCpssSetting feature is
-// enabled by default.
-class GeneratedNotificationPref
-    : public extensions::settings_private::GeneratedPref,
-      public content_settings::Observer {
- public:
-  explicit GeneratedNotificationPref(Profile* profile);
-
-  ~GeneratedNotificationPref() override;
-
-  // Generated Preference Interface.
-  extensions::settings_private::SetPrefResult SetPref(
-      const base::Value* value) override;
-  extensions::api::settings_private::PrefObject GetPrefObject() const override;
-
-  void OnContentSettingChanged(
-      const ContentSettingsPattern& primary_pattern,
-      const ContentSettingsPattern& secondary_pattern,
-      ContentSettingsTypeSet content_type_set) override;
-
-  void OnNotificationPreferencesChanged();
-
- private:
-  static void ApplyNotificationManagementState(
-      Profile& profile,
-      extensions::api::settings_private::PrefObject& pref_object);
-
-  const raw_ptr<Profile> profile_;
-  raw_ptr<HostContentSettingsMap> host_content_settings_map_;
-  base::ScopedObservation<HostContentSettingsMap, content_settings::Observer>
-      content_setting_observation_{this};
-  PrefChangeRegistrar user_prefs_registrar_;
-};
-
-}  // namespace content_settings
-
-#endif  // CHROME_BROWSER_CONTENT_SETTINGS_GENERATED_NOTIFICATION_PREF_H_
diff --git a/chrome/browser/content_settings/generated_notification_pref_unittest.cc b/chrome/browser/content_settings/generated_notification_pref_unittest.cc
deleted file mode 100644
index d97167c..0000000
--- a/chrome/browser/content_settings/generated_notification_pref_unittest.cc
+++ /dev/null
@@ -1,395 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/content_settings/generated_notification_pref.h"
-
-#include "base/ranges/algorithm.h"
-#include "chrome/browser/content_settings/generated_permission_prompting_behavior_pref.h"
-#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/extensions/api/settings_private/generated_pref_test_base.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/content_settings/core/common/content_settings.h"
-#include "components/content_settings/core/common/content_settings_types.h"
-#include "components/content_settings/core/common/pref_names.h"
-#include "components/content_settings/core/test/content_settings_mock_provider.h"
-#include "components/content_settings/core/test/content_settings_test_utils.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace settings_api = extensions::api::settings_private;
-namespace settings_private = extensions::settings_private;
-
-namespace content_settings {
-
-namespace {
-
-// Sets the value of |generated_pref| to |pref_value| and then ensures
-// that the notification content settings and preferences match
-// |expected_content_setting| and |expected_quieter_ui|. The value of
-// the new PrefObject returned by the |generated_pref| is then checked
-// against |pref_value|.
-void ValidateGeneratedPrefSetting(
-    HostContentSettingsMap* map,
-    sync_preferences::TestingPrefServiceSyncable* prefs,
-    GeneratedNotificationPref* generated_pref,
-    SettingsState pref_value,
-    ContentSetting expected_content_setting,
-    bool expected_quieter_ui) {
-  EXPECT_EQ(
-      generated_pref->SetPref(
-          std::make_unique<base::Value>(static_cast<int>(pref_value)).get()),
-      settings_private::SetPrefResult::SUCCESS);
-  EXPECT_EQ(map->GetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS),
-            expected_content_setting);
-
-  EXPECT_EQ(prefs->GetUserPref(prefs::kEnableQuietNotificationPermissionUi)
-                ->GetBool(),
-            expected_quieter_ui);
-  EXPECT_EQ(static_cast<SettingsState>(
-                generated_pref->GetPrefObject().value->GetInt()),
-            pref_value);
-}
-
-const SettingsState kNoRecommendedValue = static_cast<SettingsState>(-1);
-
-// Represents a set of settings, preferences and the associated expected
-// fields for the returned preference object.
-struct NotificationSettingManagedTestCase {
-  ContentSetting default_content_setting;
-  content_settings::SettingSource default_content_setting_source;
-  settings_private::PrefSetting quieter_ui;
-  settings_private::PrefSource quieter_ui_source;
-  settings_api::ControlledBy expected_controlled_by;
-  settings_api::Enforcement expected_enforcement;
-  SettingsState expected_recommended_value;
-  std::vector<SettingsState> expected_user_selectable_values;
-};
-
-static const std::vector<NotificationSettingManagedTestCase> managed_test_cases{
-    {CONTENT_SETTING_ASK,
-     SettingSource::kUser,
-     settings_private::PrefSetting::kNotSet,
-     settings_private::PrefSource::kNone,
-     settings_api::ControlledBy::kNone,
-     settings_api::Enforcement::kNone,
-     kNoRecommendedValue,
-     {}},
-    {CONTENT_SETTING_ASK,
-     SettingSource::kExtension,
-     settings_private::PrefSetting::kNotSet,
-     settings_private::PrefSource::kNone,
-     settings_api::ControlledBy::kExtension,
-     settings_api::Enforcement::kEnforced,
-     kNoRecommendedValue,
-     {SettingsState::kCanPromptWithAlwaysLoudUI,
-      SettingsState::kCanPromptWithAlwaysQuietUI}},
-    {CONTENT_SETTING_ASK,
-     SettingSource::kUser,
-     settings_private::PrefSetting::kEnforcedOn,
-     settings_private::PrefSource::kDevicePolicy,
-     settings_api::ControlledBy::kDevicePolicy,
-     settings_api::Enforcement::kEnforced,
-     kNoRecommendedValue,
-     {SettingsState::kCanPromptWithAlwaysQuietUI, SettingsState::kBlocked}},
-    {CONTENT_SETTING_ASK,
-     SettingSource::kUser,
-     settings_private::PrefSetting::kEnforcedOff,
-     settings_private::PrefSource::kExtension,
-     settings_api::ControlledBy::kExtension,
-     settings_api::Enforcement::kEnforced,
-     kNoRecommendedValue,
-     {SettingsState::kCanPromptWithAlwaysLoudUI, SettingsState::kBlocked}},
-    {CONTENT_SETTING_ASK,
-     SettingSource::kPolicy,
-     settings_private::PrefSetting::kEnforcedOn,
-     settings_private::PrefSource::kDevicePolicy,
-     settings_api::ControlledBy::kDevicePolicy,
-     settings_api::Enforcement::kEnforced,
-     kNoRecommendedValue,
-     {}},
-    {CONTENT_SETTING_ASK,
-     SettingSource::kSupervised,
-     settings_private::PrefSetting::kRecommendedOn,
-     settings_private::PrefSource::kRecommended,
-     settings_api::ControlledBy::kChildRestriction,
-     settings_api::Enforcement::kEnforced,
-     SettingsState::kCanPromptWithAlwaysQuietUI,
-     {SettingsState::kCanPromptWithAlwaysLoudUI,
-      SettingsState::kCanPromptWithAlwaysQuietUI}},
-    {CONTENT_SETTING_ASK,
-     SettingSource::kExtension,
-     settings_private::PrefSetting::kRecommendedOff,
-     settings_private::PrefSource::kRecommended,
-     settings_api::ControlledBy::kExtension,
-     settings_api::Enforcement::kEnforced,
-     SettingsState::kCanPromptWithAlwaysLoudUI,
-     {SettingsState::kCanPromptWithAlwaysLoudUI,
-      SettingsState::kCanPromptWithAlwaysQuietUI}},
-    {CONTENT_SETTING_BLOCK,
-     SettingSource::kExtension,
-     settings_private::PrefSetting::kRecommendedOn,
-     settings_private::PrefSource::kRecommended,
-     settings_api::ControlledBy::kExtension,
-     settings_api::Enforcement::kEnforced,
-     kNoRecommendedValue,
-     {}},
-    {CONTENT_SETTING_BLOCK,
-     SettingSource::kExtension,
-     settings_private::PrefSetting::kRecommendedOff,
-     settings_private::PrefSource::kRecommended,
-     settings_api::ControlledBy::kExtension,
-     settings_api::Enforcement::kEnforced,
-     kNoRecommendedValue,
-     {}},
-    {CONTENT_SETTING_BLOCK,
-     SettingSource::kExtension,
-     settings_private::PrefSetting::kEnforcedOn,
-     settings_private::PrefSource::kRecommended,
-     settings_api::ControlledBy::kExtension,
-     settings_api::Enforcement::kEnforced,
-     kNoRecommendedValue,
-     {}},
-    {CONTENT_SETTING_BLOCK,
-     SettingSource::kExtension,
-     settings_private::PrefSetting::kEnforcedOff,
-     settings_private::PrefSource::kRecommended,
-     settings_api::ControlledBy::kExtension,
-     settings_api::Enforcement::kEnforced,
-     kNoRecommendedValue,
-     {}},
-    {CONTENT_SETTING_BLOCK,
-     SettingSource::kExtension,
-     settings_private::PrefSetting::kNotSet,
-     settings_private::PrefSource::kRecommended,
-     settings_api::ControlledBy::kExtension,
-     settings_api::Enforcement::kEnforced,
-     kNoRecommendedValue,
-     {}},
-};
-
-void SetupManagedTestConditions(
-    HostContentSettingsMap* map,
-    sync_preferences::TestingPrefServiceSyncable* prefs,
-    const NotificationSettingManagedTestCase& test_case) {
-  auto provider = std::make_unique<content_settings::MockProvider>();
-  provider->SetWebsiteSetting(
-      ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
-      ContentSettingsType::NOTIFICATIONS,
-      base::Value(test_case.default_content_setting), /*constraints=*/{},
-      content_settings::PartitionKey::GetDefaultForTesting());
-  ProviderType provider_type;
-  switch (test_case.default_content_setting_source) {
-    case SettingSource::kPolicy:
-      provider_type = ProviderType::kPolicyProvider;
-      break;
-    case SettingSource::kExtension:
-      provider_type = ProviderType::kCustomExtensionProvider;
-      break;
-    case SettingSource::kSupervised:
-      provider_type = ProviderType::kSupervisedProvider;
-      break;
-    case SettingSource::kUser:
-    case SettingSource::kNone:
-    case SettingSource::kAllowList:
-    case SettingSource::kTpcdGrant:
-    case SettingSource::kInstalledWebApp:
-      provider_type = content_settings::ProviderType::kDefaultProvider;
-  }
-  content_settings::TestUtils::OverrideProvider(map, std::move(provider),
-                                                provider_type);
-
-  settings_private::SetPrefFromSource(
-      prefs, prefs::kEnableQuietNotificationPermissionUi, test_case.quieter_ui,
-      test_case.quieter_ui_source);
-}
-
-void ValidateManagedPreference(
-    settings_api::PrefObject& pref,
-    const NotificationSettingManagedTestCase& test_case) {
-  if (test_case.expected_controlled_by != settings_api::ControlledBy::kNone) {
-    EXPECT_EQ(pref.controlled_by, test_case.expected_controlled_by);
-  }
-
-  if (test_case.expected_enforcement != settings_api::Enforcement::kNone) {
-    EXPECT_EQ(pref.enforcement, test_case.expected_enforcement);
-  }
-
-  if (test_case.expected_recommended_value != kNoRecommendedValue) {
-    EXPECT_EQ(static_cast<SettingsState>(pref.recommended_value->GetInt()),
-              test_case.expected_recommended_value);
-  }
-
-  // Ensure user selectable values are as expected. Ordering is enforced here
-  // despite not being required by the SettingsPrivate API.
-  // First convert std::vector<std::unique_ptr<base::value(T)>> to
-  // std::vector<T> for easier comparison.
-  std::vector<SettingsState> pref_user_selectable_values;
-  if (pref.user_selectable_values) {
-    for (const auto& value : *pref.user_selectable_values) {
-      pref_user_selectable_values.push_back(
-          static_cast<SettingsState>(value.GetInt()));
-    }
-  }
-
-  EXPECT_TRUE(base::ranges::equal(pref_user_selectable_values,
-                                  test_case.expected_user_selectable_values));
-}
-
-}  // namespace
-
-typedef settings_private::GeneratedPrefTestBase GeneratedNotificationPrefTest;
-
-TEST_F(GeneratedNotificationPrefTest, UpdatePreference) {
-  auto pref = std::make_unique<GeneratedNotificationPref>(profile());
-  HostContentSettingsMap* map =
-      HostContentSettingsMapFactory::GetForProfile(profile());
-
-  // Setup a baseline content setting and preference state.
-  map->SetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS,
-                                ContentSetting::CONTENT_SETTING_ASK);
-  prefs()->SetUserPref(prefs::kEnableQuietNotificationPermissionUi,
-                       std::make_unique<base::Value>(false));
-
-  // Check that each of the three possible preference values sets the correct
-  // state and is correctly reflected in a newly returned PrefObject.
-  ValidateGeneratedPrefSetting(map, prefs(), pref.get(),
-                               SettingsState::kBlocked,
-                               ContentSetting::CONTENT_SETTING_BLOCK, false);
-  ValidateGeneratedPrefSetting(map, prefs(), pref.get(),
-                               SettingsState::kCanPromptWithAlwaysLoudUI,
-                               ContentSetting::CONTENT_SETTING_ASK, false);
-  ValidateGeneratedPrefSetting(map, prefs(), pref.get(),
-                               SettingsState::kCanPromptWithAlwaysQuietUI,
-                               ContentSetting::CONTENT_SETTING_ASK, true);
-  // Setting notification content setting to
-  // |ContentSetting::CONTENT_SETTING_BLOCK| should not change the quieter UI
-  // pref.
-  ValidateGeneratedPrefSetting(map, prefs(), pref.get(),
-                               SettingsState::kBlocked,
-                               ContentSetting::CONTENT_SETTING_BLOCK, true);
-}
-
-TEST_F(GeneratedNotificationPrefTest, UpdatePreferenceInvalidAction) {
-  auto pref = std::make_unique<GeneratedNotificationPref>(profile());
-  HostContentSettingsMap* map =
-      HostContentSettingsMapFactory::GetForProfile(profile());
-
-  // Setup a baseline content setting and preference state.
-  map->SetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS,
-                                ContentSetting::CONTENT_SETTING_BLOCK);
-  prefs()->SetUserPref(prefs::kEnableQuietNotificationPermissionUi,
-                       std::make_unique<base::Value>(false));
-
-  // Confirm that a type mismatch is reported as such.
-  EXPECT_EQ(pref->SetPref(std::make_unique<base::Value>(true).get()),
-            settings_private::SetPrefResult::PREF_TYPE_MISMATCH);
-  // Check a numerical value outside of the acceptable range.
-  EXPECT_EQ(pref->SetPref(std::make_unique<base::Value>(
-                              static_cast<int>(SettingsState::kBlocked) + 1)
-                              .get()),
-            settings_private::SetPrefResult::PREF_TYPE_MISMATCH);
-
-  // Make quieter UI preference not user modifiable.
-  settings_private::SetPrefFromSource(
-      prefs(), prefs::kEnableQuietNotificationPermissionUi,
-      settings_private::PrefSetting::kEnforcedOff,
-      settings_private::PrefSource::kDevicePolicy);
-
-  // Confirm that quieter UI preference isn't modified when it's enforced.
-  EXPECT_EQ(pref->SetPref(std::make_unique<base::Value>(
-                              static_cast<int>(
-                                  SettingsState::kCanPromptWithAlwaysQuietUI))
-                              .get()),
-            settings_private::SetPrefResult::PREF_NOT_MODIFIABLE);
-
-  // Confirm the neither value was modified.
-  EXPECT_EQ(map->GetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS),
-            ContentSetting::CONTENT_SETTING_BLOCK);
-  EXPECT_FALSE(prefs()
-                   ->FindPreference(prefs::kEnableQuietNotificationPermissionUi)
-                   ->GetValue()
-                   ->GetBool());
-
-  // Make notification content setting not user modifiable.
-  auto provider = std::make_unique<content_settings::MockProvider>();
-  provider->SetWebsiteSetting(
-      ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
-      ContentSettingsType::NOTIFICATIONS,
-      base::Value(ContentSetting::CONTENT_SETTING_ASK), /*constraints=*/{},
-      content_settings::PartitionKey::GetDefaultForTesting());
-
-  content_settings::TestUtils::OverrideProvider(map, std::move(provider),
-                                                ProviderType::kPolicyProvider);
-
-  // Confirm that notification content setting isn't modified when it's
-  // enforced.
-  EXPECT_EQ(pref->SetPref(std::make_unique<base::Value>(
-                              static_cast<int>(
-                                  SettingsState::kCanPromptWithAlwaysQuietUI))
-                              .get()),
-            settings_private::SetPrefResult::PREF_NOT_MODIFIABLE);
-
-  // Confirm the neither value was modified.
-  EXPECT_EQ(map->GetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS),
-            ContentSetting::CONTENT_SETTING_ASK);
-  EXPECT_FALSE(prefs()
-                   ->FindPreference(prefs::kEnableQuietNotificationPermissionUi)
-                   ->GetValue()
-                   ->GetBool());
-
-  // Confirm that notification content setting isn't modified when it's
-  // enforced.
-  EXPECT_EQ(pref->SetPref(std::make_unique<base::Value>(
-                              static_cast<int>(SettingsState::kBlocked))
-                              .get()),
-            settings_private::SetPrefResult::PREF_NOT_MODIFIABLE);
-
-  // Confirm the neither value was modified.
-  EXPECT_EQ(map->GetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS),
-            ContentSetting::CONTENT_SETTING_ASK);
-  EXPECT_FALSE(prefs()
-                   ->FindPreference(prefs::kEnableQuietNotificationPermissionUi)
-                   ->GetValue()
-                   ->GetBool());
-}
-
-TEST_F(GeneratedNotificationPrefTest, NotifyPrefUpdates) {
-  // Update source Notification preferences and ensure an observer is fired.
-  auto pref = std::make_unique<GeneratedNotificationPref>(profile());
-
-  settings_private::TestGeneratedPrefObserver test_observer;
-  pref->AddObserver(&test_observer);
-
-  prefs()->SetUserPref(prefs::kEnableQuietNotificationPermissionUi,
-                       std::make_unique<base::Value>(true));
-  EXPECT_EQ(test_observer.GetUpdatedPrefName(), kGeneratedNotificationPref);
-  test_observer.Reset();
-
-  HostContentSettingsMap* map =
-      HostContentSettingsMapFactory::GetForProfile(profile());
-  map->SetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS,
-                                ContentSetting::CONTENT_SETTING_BLOCK);
-
-  EXPECT_EQ(test_observer.GetUpdatedPrefName(), kGeneratedNotificationPref);
-}
-
-TEST_F(GeneratedNotificationPrefTest, ManagedState) {
-  for (const auto& test_case : managed_test_cases) {
-    TestingProfile profile;
-    HostContentSettingsMap* map =
-        HostContentSettingsMapFactory::GetForProfile(&profile);
-
-    testing::Message scope_message;
-    scope_message << "Content Setting:" << test_case.default_content_setting
-                  << " Quieter UI:" << static_cast<int>(test_case.quieter_ui);
-    SCOPED_TRACE(scope_message);
-    SetupManagedTestConditions(map, profile.GetTestingPrefService(), test_case);
-    auto pref =
-        std::make_unique<content_settings::GeneratedNotificationPref>(&profile);
-    auto pref_object = pref->GetPrefObject();
-    ValidateManagedPreference(pref_object, test_case);
-  }
-}
-
-}  // namespace content_settings
diff --git a/chrome/browser/data_sharing/BUILD.gn b/chrome/browser/data_sharing/BUILD.gn
index 425eb2b1..8d36d3ca 100644
--- a/chrome/browser/data_sharing/BUILD.gn
+++ b/chrome/browser/data_sharing/BUILD.gn
@@ -181,6 +181,7 @@
       "//third_party/androidx:androidx_test_core_java",
       "//third_party/junit:junit",
       "//third_party/mockito:mockito_java",
+      "//ui/android:ui_java_test_support",
       "//ui/android:ui_no_recycler_view_java",
       "//url:url_java",
     ]
diff --git a/chrome/browser/data_sharing/android/java/res/drawable/round_image_filled.xml b/chrome/browser/data_sharing/android/java/res/drawable/round_image_filled.xml
index 7ba5da7..6036db2 100644
--- a/chrome/browser/data_sharing/android/java/res/drawable/round_image_filled.xml
+++ b/chrome/browser/data_sharing/android/java/res/drawable/round_image_filled.xml
@@ -10,5 +10,5 @@
   <solid android:color="@macro/default_bg_color" />
   <corners android:radius="@dimen/shared_image_tiles_icon_radius" />
   <!-- Note: This is for testing purposes only. -->
-  <stroke android:width="1dp" android:color="@color/black_alpha_5"/>
+  <stroke android:width="@dimen/shared_image_tiles_icon_border" android:color="@macro/default_bg_color"/>
 </shape>
diff --git a/chrome/browser/data_sharing/android/java/res/layout/shared_image_tiles.xml b/chrome/browser/data_sharing/android/java/res/layout/shared_image_tiles.xml
index 43801ed..fce178e 100644
--- a/chrome/browser/data_sharing/android/java/res/layout/shared_image_tiles.xml
+++ b/chrome/browser/data_sharing/android/java/res/layout/shared_image_tiles.xml
@@ -19,9 +19,9 @@
     <LinearLayout
         android:id="@+id/last_tile_container"
         android:layout_width="wrap_content"
-        android:layout_height="@dimen/shared_image_tiles_icon_height"
+        android:layout_height="@dimen/shared_image_tiles_icon_total_height"
         android:layout_marginEnd="@dimen/shared_image_tiles_overlap_margin_negative"
-        android:minWidth="@dimen/shared_image_tiles_icon_height"
+        android:minWidth="@dimen/shared_image_tiles_icon_total_height"
         android:orientation="vertical"
         android:background="@drawable/round_image_filled"
         android:focusable="false"
diff --git a/chrome/browser/data_sharing/android/java/res/layout/shared_image_tiles_icon.xml b/chrome/browser/data_sharing/android/java/res/layout/shared_image_tiles_icon.xml
index 9043db1..e7662e5 100644
--- a/chrome/browser/data_sharing/android/java/res/layout/shared_image_tiles_icon.xml
+++ b/chrome/browser/data_sharing/android/java/res/layout/shared_image_tiles_icon.xml
@@ -8,7 +8,7 @@
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="@dimen/shared_image_tiles_icon_height"
-    android:layout_height="@dimen/shared_image_tiles_icon_height"
+    android:layout_width="@dimen/shared_image_tiles_icon_total_height"
+    android:layout_height="@dimen/shared_image_tiles_icon_total_height"
     android:background="@drawable/round_image_filled"
     android:layout_marginEnd="@dimen/shared_image_tiles_overlap_margin_negative"/>
diff --git a/chrome/browser/data_sharing/android/java/res/values/dimens.xml b/chrome/browser/data_sharing/android/java/res/values/dimens.xml
index ab4fcf5..ddf0d498 100644
--- a/chrome/browser/data_sharing/android/java/res/values/dimens.xml
+++ b/chrome/browser/data_sharing/android/java/res/values/dimens.xml
@@ -7,8 +7,9 @@
 
 <resources xmlns:tools="http://schemas.android.com/tools">
     <!-- Dimens for shared_image_tiles view -->
-    <dimen name="shared_image_tiles_icon_height">28dp</dimen>
+    <dimen name="shared_image_tiles_icon_border">2dp</dimen>
     <dimen name="shared_image_tiles_icon_radius">14dp</dimen>
+    <dimen name="shared_image_tiles_icon_total_height">32dp</dimen>
     <dimen name="shared_image_tiles_overlap_margin">8dp</dimen>
     <dimen name="shared_image_tiles_overlap_margin_negative">-6dp</dimen>
 
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinator.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinator.java
index 74ca065..3ffd014 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinator.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinator.java
@@ -9,16 +9,19 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.annotation.ColorInt;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Callback;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.data_sharing.DataSharingService;
 import org.chromium.components.data_sharing.DataSharingUIDelegate;
 import org.chromium.components.data_sharing.GroupData;
 import org.chromium.components.data_sharing.GroupMember;
 import org.chromium.components.data_sharing.PeopleGroupActionFailure;
+import org.chromium.components.data_sharing.configs.AvatarConfig;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
@@ -43,9 +46,10 @@
     private final PropertyModel mModel;
     private final SharedImageTilesView mView;
     private final @SharedImageTilesType int mType;
+    private final @SharedImageTilesColor int mColor;
     private final @NonNull DataSharingService mDataSharingService;
     private @NonNull String mCollaborationId;
-    private int mAvailableTileCount;
+    private int mAvailableMemberCount;
     private int mIconTilesCount;
 
     /**
@@ -67,6 +71,7 @@
                         .build();
         mContext = context;
         mType = type;
+        mColor = color;
         mDataSharingService = dataSharingService;
 
         mView =
@@ -88,7 +93,7 @@
     public void updateCollaborationId(@Nullable String collaborationId) {
         mCollaborationId = collaborationId;
         if (mCollaborationId == null) {
-            updateTilesCount(0);
+            updateMembersCount(0);
             return;
         }
 
@@ -98,7 +103,7 @@
                 (result) -> {
                     if (result.actionFailure != PeopleGroupActionFailure.UNKNOWN) {
                         // Error occurred. Remove all view.
-                        updateTilesCount(0);
+                        updateMembersCount(0);
                         return;
                     }
 
@@ -114,7 +119,7 @@
                 emails.add(member.email);
             }
         }
-        updateTilesCount(emails.size());
+        updateMembersCount(emails.size());
 
         // Let the UI delegate draw the icon tiles.
         DataSharingUIDelegate dataSharingUiDelegate = mDataSharingService.getUIDelegate();
@@ -123,30 +128,57 @@
         Callback<Boolean> successCallback =
                 (success) -> {
                     if (!success) {
-                        updateTilesCount(0);
+                        assert false;
                     }
                 };
 
+        List<ViewGroup> iconViews = getAllIconViews();
+        AvatarConfig config =
+                new AvatarConfig.Builder()
+                        .setAvatarSizeInPixels(getAvatarSizeInPixels())
+                        .setAvatarBackgroundColor(getAvatarBackgroundColor())
+                        .setBorderColor(getBorderColor())
+                        .setBorderWidthInPixels(getBorderWidthInPixels())
+                        .build();
+
         dataSharingUiDelegate.showAvatars(
                 mContext,
-                getAllIconViews(),
-                emails,
+                iconViews,
+                emails.subList(0, iconViews.size()),
                 /* success= */ successCallback,
-                /* config= */ null);
+                config);
+    }
+
+    private int getAvatarSizeInPixels() {
+        return mContext.getResources()
+                .getDimensionPixelSize(R.dimen.shared_image_tiles_icon_total_height);
+    }
+
+    private @ColorInt int getAvatarBackgroundColor() {
+        return SemanticColorUtils.getColorPrimaryContainer(mContext);
+    }
+
+    private @ColorInt int getBorderColor() {
+        return SemanticColorUtils.getDefaultBgColor(mContext);
+    }
+
+    private int getBorderWidthInPixels() {
+        return mContext.getResources()
+                .getDimensionPixelSize(R.dimen.shared_image_tiles_icon_border);
     }
 
     /** Populate the shared_image_tiles container with the specific icons. */
     private void initializeSharedImageTiles() {
-        if (mAvailableTileCount == 0) {
+        if (mAvailableMemberCount == 0) {
             return;
         }
 
         int maxTilesToShowWithNumberTile = MAX_TILES_UI_LIMIT - 1;
-        boolean showNumberTile = mAvailableTileCount > MAX_TILES_UI_LIMIT;
+        boolean showNumberTile = mAvailableMemberCount > MAX_TILES_UI_LIMIT;
         mIconTilesCount =
                 showNumberTile
                         ? MAX_TILES_UI_LIMIT - 1
-                        : Math.min(mAvailableTileCount, MAX_TILES_UI_LIMIT);
+                        : Math.min(mAvailableMemberCount, MAX_TILES_UI_LIMIT);
 
         // Add icon tile(s).
         mModel.set(SharedImageTilesProperties.ICON_TILES, mIconTilesCount);
@@ -156,7 +188,7 @@
             // Compute a count bubble.
             mModel.set(
                     SharedImageTilesProperties.REMAINING_TILES,
-                    mAvailableTileCount - maxTilesToShowWithNumberTile);
+                    mAvailableMemberCount - maxTilesToShowWithNumberTile);
         }
     }
 
@@ -182,10 +214,8 @@
     }
 
     @VisibleForTesting
-    void updateTilesCount(int count) {
-        // TODO(b/325533985): |mAvailableTileCount| should be replace by the actual number of icons
-        // needed.
-        mAvailableTileCount = count;
+    void updateMembersCount(int count) {
+        mAvailableMemberCount = count;
         mModel.set(SharedImageTilesProperties.REMAINING_TILES, 0);
         mModel.set(SharedImageTilesProperties.ICON_TILES, 0);
         initializeSharedImageTiles();
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinatorUnitTest.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinatorUnitTest.java
index c8821a5..79591fb 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinatorUnitTest.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinatorUnitTest.java
@@ -12,12 +12,10 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 
-import android.content.Context;
+import android.app.Activity;
 import android.view.View;
 import android.widget.TextView;
 
-import androidx.test.core.app.ApplicationProvider;
-
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -25,6 +23,7 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
+import org.robolectric.Robolectric;
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
@@ -34,6 +33,7 @@
 import org.chromium.components.data_sharing.GroupData;
 import org.chromium.components.data_sharing.GroupMember;
 import org.chromium.components.data_sharing.PeopleGroupActionFailure;
+import org.chromium.ui.base.TestActivity;
 
 import java.util.Arrays;
 
@@ -46,22 +46,22 @@
     private static final String EMAIL = "test@test.com";
 
     @Mock private DataSharingService mDataSharingService;
-    @Mock private DataSharingUIDelegate mDataSharingUIDelegate;
+    @Mock private DataSharingUIDelegate mDataSharingUiDelegate;
 
-    private Context mContext;
+    private Activity mActivity;
     private SharedImageTilesCoordinator mSharedImageTilesCoordinator;
     private SharedImageTilesView mView;
     private TextView mCountTileView;
 
     @Before
     public void setUp() {
-        mContext = ApplicationProvider.getApplicationContext();
+        mActivity = Robolectric.buildActivity(TestActivity.class).setup().get();
         initialize(SharedImageTilesType.DEFAULT, SharedImageTilesColor.DEFAULT);
     }
 
     private void initialize(@SharedImageTilesType int type, @SharedImageTilesColor int color) {
         mSharedImageTilesCoordinator =
-                new SharedImageTilesCoordinator(mContext, type, color, mDataSharingService);
+                new SharedImageTilesCoordinator(mActivity, type, color, mDataSharingService);
         mView = mSharedImageTilesCoordinator.getView();
         mCountTileView = mView.findViewById(R.id.tiles_count);
     }
@@ -87,16 +87,16 @@
         // etc
         verifyViews(View.GONE, /* iconViewCount= */ 0);
 
-        mSharedImageTilesCoordinator.updateTilesCount(1);
+        mSharedImageTilesCoordinator.updateMembersCount(1);
         verifyViews(View.GONE, /* iconViewCount= */ 1);
 
-        mSharedImageTilesCoordinator.updateTilesCount(2);
+        mSharedImageTilesCoordinator.updateMembersCount(2);
         verifyViews(View.GONE, /* iconViewCount= */ 2);
 
-        mSharedImageTilesCoordinator.updateTilesCount(3);
+        mSharedImageTilesCoordinator.updateMembersCount(3);
         verifyViews(View.GONE, /* iconViewCount= */ 3);
 
-        mSharedImageTilesCoordinator.updateTilesCount(4);
+        mSharedImageTilesCoordinator.updateMembersCount(4);
         verifyViews(View.VISIBLE, /* iconViewCount= */ 2);
     }
 
@@ -144,11 +144,11 @@
                         })
                 .when(mDataSharingService)
                 .readGroup(eq(COLLABORATION_ID), any(Callback.class));
-        doReturn(mDataSharingUIDelegate).when(mDataSharingService).getUIDelegate();
+        doReturn(mDataSharingUiDelegate).when(mDataSharingService).getUIDelegate();
 
         mSharedImageTilesCoordinator.updateCollaborationId(COLLABORATION_ID);
 
-        verify(mDataSharingUIDelegate)
+        verify(mDataSharingUiDelegate)
                 .showAvatars(any(), any(), eq(Arrays.asList(memberValid.email)), any(), any());
     }
 }
diff --git a/chrome/browser/device_reauth/android/BUILD.gn b/chrome/browser/device_reauth/android/BUILD.gn
index 944dbb9..7521b8e 100644
--- a/chrome/browser/device_reauth/android/BUILD.gn
+++ b/chrome/browser/device_reauth/android/BUILD.gn
@@ -8,6 +8,7 @@
 
 android_library("java") {
   deps = [
+    ":java_resources",
     "//base:base_java",
     "//build/android:build_java",
     "//chrome/browser/flags:java",
@@ -29,6 +30,9 @@
 
   sources = [
     "java/src/org/chromium/chrome/browser/device_reauth/AndroidxDeviceAuthenticatorControllerImpl.java",
+    "java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogController.java",
+    "java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogProperties.java",
+    "java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogViewBinder.java",
     "java/src/org/chromium/chrome/browser/device_reauth/DeviceAuthenticatorBridge.java",
     "java/src/org/chromium/chrome/browser/device_reauth/DeviceAuthenticatorController.java",
     "java/src/org/chromium/chrome/browser/device_reauth/DeviceAuthenticatorControllerImpl.java",
@@ -49,3 +53,29 @@
 java_cpp_enum("device_reauth_java_enums_srcjar") {
   sources = [ "device_authenticator_bridge.h" ]
 }
+
+android_resources("java_resources") {
+  sources = [ "java/res/layout/biometric_auth_error_dialog.xml" ]
+
+  deps = [ "//components/browser_ui/styles/android:java_resources" ]
+}
+
+robolectric_library("junit") {
+  resources_package = "org.chromium.chrome.browser.device_reauth"
+
+  testonly = true
+  sources = [ "java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogUnitTest.java" ]
+
+  deps = [
+    ":java",
+    ":java_resources",
+    "//base:base_java",
+    "//base:base_java_test_support",
+    "//base:base_junit_test_support",
+    "//chrome/browser/ui/android/strings:ui_strings_grd",
+    "//components/browser_ui/theme/android:java_resources",
+    "//third_party/junit",
+    "//ui/android:ui_java_test_support",
+    "//ui/android:ui_no_recycler_view_java",
+  ]
+}
diff --git a/chrome/browser/device_reauth/android/java/res/layout/biometric_auth_error_dialog.xml b/chrome/browser/device_reauth/android/java/res/layout/biometric_auth_error_dialog.xml
new file mode 100644
index 0000000..a6409a9
--- /dev/null
+++ b/chrome/browser/device_reauth/android/java/res/layout/biometric_auth_error_dialog.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingLeft="24dp"
+    android:paddingRight="24dp">
+  <TextView
+      android:id="@+id/error_dialog_title"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:gravity="center_horizontal"
+      android:textAppearance="@style/TextAppearance.Headline.Primary"
+      android:paddingTop="16dp"/>
+
+  <TextView
+      android:id="@+id/description"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
+      android:paddingTop="16dp"/>
+  <TextView
+      android:id="@+id/more_details"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
+      android:paddingTop="16dp"/>
+</LinearLayout>
diff --git a/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogController.java b/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogController.java
new file mode 100644
index 0000000..fe63ec0
--- /dev/null
+++ b/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogController.java
@@ -0,0 +1,93 @@
+// 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.
+
+package org.chromium.chrome.browser.device_reauth;
+
+import static org.chromium.chrome.browser.device_reauth.BiometricErrorDialogProperties.DESCRIPTION;
+import static org.chromium.chrome.browser.device_reauth.BiometricErrorDialogProperties.MORE_DETAILS;
+import static org.chromium.chrome.browser.device_reauth.BiometricErrorDialogProperties.TITLE;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.text.SpannableString;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modaldialog.SimpleModalDialogController;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.text.NoUnderlineClickableSpan;
+import org.chromium.ui.text.SpanApplier;
+
+/**
+ * Shows a modal dialog describing the error encountered during mandatory biometric auth and steps
+ * to solve it.
+ */
+public class BiometricErrorDialogController {
+    private final Activity mActivity;
+    private final ModalDialogManager mModalDialogManager;
+
+    BiometricErrorDialogController(Activity activity, ModalDialogManager modalDialogManager) {
+        mActivity = activity;
+        mModalDialogManager = modalDialogManager;
+    }
+
+    void showLockoutErrorDialog() {
+        View dialogCustomView =
+                LayoutInflater.from(mActivity).inflate(R.layout.biometric_auth_error_dialog, null);
+        Resources resources = mActivity.getResources();
+        // TODO(crbug.com/3367923668): Add icon at the top of the title. This is the only reason to
+        // use a custom view.
+        SpannableString moreDetails =
+                getLockoutErrorDetails(
+                        resources.getString(R.string.identity_check_lockout_error_more_details));
+        PropertyModel customViewModel =
+                new PropertyModel.Builder(BiometricErrorDialogProperties.ALL_KEYS)
+                        .with(
+                                TITLE,
+                                resources.getString(R.string.identity_check_lockout_error_title))
+                        .with(
+                                DESCRIPTION,
+                                resources.getString(
+                                        R.string.identity_check_lockout_error_description))
+                        .with(MORE_DETAILS, moreDetails)
+                        .build();
+
+        // TODO(crbug.com/367922864): Add controller that handles the click on the "Lock screen"
+        // button.
+        PropertyModelChangeProcessor.create(
+                customViewModel, dialogCustomView, BiometricErrorDialogViewBinder::bind);
+        PropertyModel modalDialogModel =
+                new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
+                        .with(
+                                ModalDialogProperties.CONTROLLER,
+                                new SimpleModalDialogController(
+                                        mModalDialogManager, (Integer dismissalCause) -> {}))
+                        .with(ModalDialogProperties.CUSTOM_VIEW, dialogCustomView)
+                        .with(
+                                ModalDialogProperties.POSITIVE_BUTTON_TEXT,
+                                resources,
+                                R.string.lock_screen)
+                        .with(
+                                ModalDialogProperties.NEGATIVE_BUTTON_TEXT,
+                                resources,
+                                R.string.cancel)
+                        .build();
+
+        mModalDialogManager.showDialog(modalDialogModel, ModalDialogType.TAB);
+    }
+
+    private SpannableString getLockoutErrorDetails(String text) {
+        // TODO(crbug.com/367922864): Link this to the Identity Check settings.
+        return SpanApplier.applySpans(
+                text,
+                new SpanApplier.SpanInfo(
+                        "<link>",
+                        "</link>",
+                        new NoUnderlineClickableSpan(mActivity, (View unused) -> {})));
+    }
+}
diff --git a/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogProperties.java b/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogProperties.java
new file mode 100644
index 0000000..2377317
--- /dev/null
+++ b/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogProperties.java
@@ -0,0 +1,23 @@
+// 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.
+package org.chromium.chrome.browser.device_reauth;
+
+import android.text.SpannableString;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel.ReadableObjectPropertyKey;
+
+public class BiometricErrorDialogProperties {
+    private BiometricErrorDialogProperties() {}
+
+    static final PropertyModel.ReadableObjectPropertyKey<String> TITLE =
+            new ReadableObjectPropertyKey<>("dialog title");
+    static final PropertyModel.ReadableObjectPropertyKey<String> DESCRIPTION =
+            new ReadableObjectPropertyKey<>("description");
+    static final PropertyModel.ReadableObjectPropertyKey<SpannableString> MORE_DETAILS =
+            new ReadableObjectPropertyKey("more details");
+
+    static final PropertyKey[] ALL_KEYS = {TITLE, DESCRIPTION, MORE_DETAILS};
+}
diff --git a/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogUnitTest.java b/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogUnitTest.java
new file mode 100644
index 0000000..f00a8d9
--- /dev/null
+++ b/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogUnitTest.java
@@ -0,0 +1,86 @@
+// 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.
+
+package org.chromium.chrome.browser.device_reauth;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.text.SpannableString;
+import android.view.View;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.test.util.modaldialog.FakeModalDialogManager;
+import org.chromium.ui.text.NoUnderlineClickableSpan;
+import org.chromium.ui.text.SpanApplier;
+
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class BiometricErrorDialogUnitTest {
+    private FakeModalDialogManager mModalDialogManager =
+            new FakeModalDialogManager(ModalDialogManager.ModalDialogType.APP);
+    private Activity mActivity;
+
+    @Before
+    public void setUp() {
+        mActivity = Robolectric.buildActivity(Activity.class).create().start().resume().get();
+        mActivity.setTheme(R.style.Theme_BrowserUI_DayNight);
+    }
+
+    @Test
+    public void testShowsAndHidesLockoutErrorDialog() {
+        BiometricErrorDialogController controller =
+                new BiometricErrorDialogController(mActivity, mModalDialogManager);
+        controller.showLockoutErrorDialog();
+        PropertyModel mDialogModel = mModalDialogManager.getShownDialogModel();
+        assertNotNull(mDialogModel);
+
+        mModalDialogManager.clickNegativeButton();
+        assertNull(mModalDialogManager.getShownDialogModel());
+    }
+
+    @Test
+    public void testLockoutErrorDialogProperties() {
+        BiometricErrorDialogController controller =
+                new BiometricErrorDialogController(mActivity, mModalDialogManager);
+        controller.showLockoutErrorDialog();
+        PropertyModel mDialogModel = mModalDialogManager.getShownDialogModel();
+        View dialogContentsView = mDialogModel.get(ModalDialogProperties.CUSTOM_VIEW);
+
+        assertNotNull(dialogContentsView);
+
+        Resources resources = mActivity.getResources();
+        TextView title = dialogContentsView.findViewById(R.id.error_dialog_title);
+        assertEquals(
+                resources.getString(R.string.identity_check_lockout_error_title), title.getText());
+
+        TextView description = dialogContentsView.findViewById(R.id.description);
+        assertEquals(
+                resources.getString(R.string.identity_check_lockout_error_description),
+                description.getText());
+
+        TextView moreDetails = dialogContentsView.findViewById(R.id.more_details);
+        SpannableString expected =
+                SpanApplier.applySpans(
+                        resources.getString(R.string.identity_check_lockout_error_more_details),
+                        new SpanApplier.SpanInfo(
+                                "<link>",
+                                "</link>",
+                                new NoUnderlineClickableSpan(mActivity, (unusedView) -> {})));
+        assertEquals(expected.toString(), moreDetails.getText().toString());
+    }
+}
diff --git a/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogViewBinder.java b/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogViewBinder.java
new file mode 100644
index 0000000..622ae56
--- /dev/null
+++ b/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/BiometricErrorDialogViewBinder.java
@@ -0,0 +1,36 @@
+// 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.
+
+package org.chromium.chrome.browser.device_reauth;
+
+import static org.chromium.chrome.browser.device_reauth.BiometricErrorDialogProperties.DESCRIPTION;
+import static org.chromium.chrome.browser.device_reauth.BiometricErrorDialogProperties.MORE_DETAILS;
+import static org.chromium.chrome.browser.device_reauth.BiometricErrorDialogProperties.TITLE;
+
+import android.view.View;
+import android.widget.TextView;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * Maps {@link BiometricErrorDialogProperties changes in a {@link PropertyModel} to
+ * {@link BiometricErrorDialogController}'s modal dialog custom view.
+ */
+class BiometricErrorDialogViewBinder {
+    private BiometricErrorDialogViewBinder() {}
+    ;
+
+    static void bind(PropertyModel model, View view, PropertyKey key) {
+        if (key == TITLE) {
+            ((TextView) view.findViewById(R.id.error_dialog_title)).setText(model.get(TITLE));
+        } else if (key == DESCRIPTION) {
+            ((TextView) view.findViewById(R.id.description)).setText(model.get(DESCRIPTION));
+        } else if (key == MORE_DETAILS) {
+            ((TextView) view.findViewById(R.id.more_details)).setText(model.get(MORE_DETAILS));
+        } else {
+            assert false : "Property " + key.toString() + " not handled in the binder";
+        }
+    }
+}
diff --git a/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/ReauthenticatorBridge.java b/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/ReauthenticatorBridge.java
index 4fa1799..9f8ac1da 100644
--- a/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/ReauthenticatorBridge.java
+++ b/chrome/browser/device_reauth/android/java/src/org/chromium/chrome/browser/device_reauth/ReauthenticatorBridge.java
@@ -13,6 +13,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.ResettersForTesting;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.ui.modaldialog.ModalDialogManager;
 
 /**
  * Class handling the communication with the C++ part of the reauthentication based on device lock.
@@ -25,7 +26,10 @@
     private Callback<Boolean> mAuthResultCallback;
 
     private ReauthenticatorBridge(
-            Activity activity, Profile profile, @DeviceAuthSource int source) {
+            Activity activity,
+            Profile profile,
+            ModalDialogManager modalDialogManager,
+            @DeviceAuthSource int source) {
         mNativeReauthenticatorBridge =
                 ReauthenticatorBridgeJni.get().create(this, activity, profile, source);
     }
@@ -87,13 +91,32 @@
     /**
      * Create an instance of {@link ReauthenticatorBridge} based on the provided {@link
      * DeviceAuthSource}.
+     *
+     * @param activity Used to display the biometric prompt and modal dialogs.
+     * @param profile The profile to which the device authenticator service belongs.
+     * @param modalDialogManager Used to display error dialogs during mandatory auth steps.
+     * @param source The feature invoking the authentication.
      */
     public static ReauthenticatorBridge create(
-            Activity activity, Profile profile, @DeviceAuthSource int source) {
+            Activity activity,
+            Profile profile,
+            ModalDialogManager modalDialogManager,
+            @DeviceAuthSource int source) {
         if (sReauthenticatorBridgeForTesting != null) {
             return sReauthenticatorBridgeForTesting;
         }
-        return new ReauthenticatorBridge(activity, profile, source);
+        return new ReauthenticatorBridge(activity, profile, modalDialogManager, source);
+    }
+
+    /**
+     * Create an instance of {@link ReauthenticatorBridge} based on the provided {@link
+     * DeviceAuthSource}.
+     *
+     * <p>// TODO(crbug.com/370467784) Remove once all callers have switched to the one above.
+     */
+    public static ReauthenticatorBridge create(
+            Activity activity, Profile profile, @DeviceAuthSource int source) {
+        return ReauthenticatorBridge.create(activity, profile, null, source);
     }
 
     /** For testing only. */
diff --git a/chrome/browser/extensions/api/settings_private/generated_prefs.cc b/chrome/browser/extensions/api/settings_private/generated_prefs.cc
index 733e49b..179a144 100644
--- a/chrome/browser/extensions/api/settings_private/generated_prefs.cc
+++ b/chrome/browser/extensions/api/settings_private/generated_prefs.cc
@@ -9,7 +9,6 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/content_settings/generated_cookie_prefs.h"
-#include "chrome/browser/content_settings/generated_notification_pref.h"
 #include "chrome/browser/content_settings/generated_permission_prompting_behavior_pref.h"
 #include "chrome/browser/extensions/api/settings_private/generated_pref.h"
 #include "chrome/browser/extensions/api/settings_private/prefs_util_enums.h"
@@ -102,19 +101,12 @@
       std::make_unique<GeneratedPasswordLeakDetectionPref>(profile_);
   prefs_[safe_browsing::kGeneratedSafeBrowsingPref] =
       std::make_unique<safe_browsing::GeneratedSafeBrowsingPref>(profile_);
-  if (base::FeatureList::IsEnabled(
-          permissions::features::kPermissionDedicatedCpssSetting)) {
-    prefs_[content_settings::kGeneratedNotificationPref] = std::make_unique<
-        content_settings::GeneratedPermissionPromptingBehaviorPref>(
-        profile_, ContentSettingsType::NOTIFICATIONS);
-    prefs_[content_settings::kGeneratedGeolocationPref] = std::make_unique<
-        content_settings::GeneratedPermissionPromptingBehaviorPref>(
-        profile_, ContentSettingsType::GEOLOCATION);
-  } else {
-    prefs_[content_settings::kGeneratedNotificationPref] =
-        std::make_unique<content_settings::GeneratedNotificationPref>(profile_);
-    prefs_[content_settings::kGeneratedGeolocationPref] = nullptr;
-  }
+  prefs_[content_settings::kGeneratedNotificationPref] = std::make_unique<
+      content_settings::GeneratedPermissionPromptingBehaviorPref>(
+      profile_, ContentSettingsType::NOTIFICATIONS);
+  prefs_[content_settings::kGeneratedGeolocationPref] = std::make_unique<
+      content_settings::GeneratedPermissionPromptingBehaviorPref>(
+      profile_, ContentSettingsType::GEOLOCATION);
   prefs_[kGeneratedHttpsFirstModePref] =
       std::make_unique<GeneratedHttpsFirstModePref>(profile_);
 }
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 67c7ebb..66fff50 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/content_settings/generated_cookie_prefs.h"
-#include "chrome/browser/content_settings/generated_notification_pref.h"
 #include "chrome/browser/content_settings/generated_permission_prompting_behavior_pref.h"
 #include "chrome/browser/extensions/api/settings_private/generated_prefs.h"
 #include "chrome/browser/extensions/api/settings_private/generated_prefs_factory.h"
diff --git a/chrome/browser/extensions/api/socket/socket_apitest.cc b/chrome/browser/extensions/api/socket/socket_apitest.cc
index 3ced4af..058aedb 100644
--- a/chrome/browser/extensions/api/socket/socket_apitest.cc
+++ b/chrome/browser/extensions/api/socket/socket_apitest.cc
@@ -14,6 +14,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/test/browser_test.h"
 #include "extensions/browser/api/socket/socket_api.h"
+#include "extensions/browser/api/socket/write_quota_checker.h"
 #include "extensions/browser/api/sockets_udp/test_udp_echo_server.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
@@ -139,3 +140,62 @@
 
   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
+
+IN_PROC_BROWSER_TEST_F(SocketApiTest, TCPSocketWriteQuota) {
+  extensions::WriteQuotaChecker* write_quota_checker =
+      extensions::WriteQuotaChecker::Get(profile());
+  constexpr size_t kBytesLimit = 1;
+  extensions::WriteQuotaChecker::ScopedBytesLimitForTest scoped_quota(
+      write_quota_checker, kBytesLimit);
+
+  net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTP);
+  test_server.AddDefaultHandlers();
+  EXPECT_TRUE(test_server.Start());
+
+  net::HostPortPair host_port_pair = test_server.host_port_pair();
+  int port = host_port_pair.port();
+  ASSERT_GT(port, 0);
+
+  ResultCatcher catcher;
+  catcher.RestrictToBrowserContext(browser()->profile());
+
+  ExtensionTestMessageListener listener("info_please",
+                                        ReplyBehavior::kWillReply);
+
+  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("socket/api")));
+  EXPECT_TRUE(listener.WaitUntilSatisfied());
+  listener.Reply(base::StringPrintf("tcp_write_quota:%s:%d",
+                                    host_port_pair.host().c_str(), port));
+
+  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
+
+IN_PROC_BROWSER_TEST_F(SocketApiTest, UDPSocketWriteQuota) {
+  extensions::WriteQuotaChecker* write_quota_checker =
+      extensions::WriteQuotaChecker::Get(profile());
+  constexpr size_t kBytesLimit = 1;
+  extensions::WriteQuotaChecker::ScopedBytesLimitForTest scoped_quota(
+      write_quota_checker, kBytesLimit);
+
+  extensions::TestUdpEchoServer udp_echo_server;
+  net::HostPortPair host_port_pair;
+  ASSERT_TRUE(udp_echo_server.Start(
+      profile()->GetDefaultStoragePartition()->GetNetworkContext(),
+      &host_port_pair));
+
+  int port = host_port_pair.port();
+  ASSERT_GT(port, 0);
+
+  ResultCatcher catcher;
+  catcher.RestrictToBrowserContext(browser()->profile());
+
+  ExtensionTestMessageListener listener("info_please",
+                                        ReplyBehavior::kWillReply);
+
+  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("socket/api")));
+  EXPECT_TRUE(listener.WaitUntilSatisfied());
+  listener.Reply(base::StringPrintf("udp_sendTo_quota:%s:%d",
+                                    host_port_pair.host().c_str(), port));
+
+  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 3a39dae1..d61d464a 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -4114,11 +4114,6 @@
     "expiry_milestone": 120
   },
   {
-    "name": "enable-tflite-language-detection-ignore",
-    "owners": [ "mcrouse@chromium.org", "chrome-intelligence-core@google.com" ],
-    "expiry_milestone": 120
-  },
-  {
     "name": "enable-tls13-early-data",
     "owners": [ "davidben@chromium.org", "svaldez@chromium.org", "bashi@chromium.org" ],
     "expiry_milestone": 160
diff --git a/chrome/browser/loading_modal/android/java/src/org/chromium/chrome/browser/loading_modal/LoadingModalDialogMediator.java b/chrome/browser/loading_modal/android/java/src/org/chromium/chrome/browser/loading_modal/LoadingModalDialogMediator.java
index 2b23b522..07ca152 100644
--- a/chrome/browser/loading_modal/android/java/src/org/chromium/chrome/browser/loading_modal/LoadingModalDialogMediator.java
+++ b/chrome/browser/loading_modal/android/java/src/org/chromium/chrome/browser/loading_modal/LoadingModalDialogMediator.java
@@ -43,6 +43,8 @@
     private boolean mSkipDelay;
     private boolean mDisableTimeout;
 
+    private Runnable mShowingTask = this::onShowDelayPassed;
+
     /** ModalDialogProperties.Controller implementation */
     @Override
     public void onClick(PropertyModel model, @ButtonType int buttonType) {
@@ -117,7 +119,7 @@
         mDialogManager = dialogManager;
         mModel = model;
         mState = LoadingModalDialogCoordinator.State.PENDING;
-        postDelayed(this::onShowDelayPassed, SHOW_DELAY_TIME_MS);
+        postDelayed(mShowingTask, SHOW_DELAY_TIME_MS);
     }
 
     /**
@@ -128,7 +130,7 @@
      */
     void dismiss() {
         if (mState == LoadingModalDialogCoordinator.State.PENDING) {
-            mHandler.removeCallbacks(this::onShowDelayPassed);
+            mHandler.removeCallbacks(mShowingTask);
         }
 
         mState = LoadingModalDialogCoordinator.State.FINISHED;
diff --git a/chrome/browser/net/net_error_diagnostics_dialog_win.cc b/chrome/browser/net/net_error_diagnostics_dialog_win.cc
index cff2f0f7..4a87962 100644
--- a/chrome/browser/net/net_error_diagnostics_dialog_win.cc
+++ b/chrome/browser/net/net_error_diagnostics_dialog_win.cc
@@ -67,6 +67,10 @@
   }
 
  private:
+// TODO(crbug.com/370065739): The Ndf* functions here have been deprecated.
+// Update this function and then remove these pragmas.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
   void ShowDialogOnPrivateThread(HWND parent, const std::string& failed_url) {
     NDFHANDLE incident_handle;
     std::wstring failed_url_wide = base::UTF8ToWide(failed_url);
@@ -77,6 +81,7 @@
     NdfExecuteDiagnosis(incident_handle, parent);
     NdfCloseIncident(incident_handle);
   }
+#pragma clang diagnostic pop
 
   void DiagnosticsDone(std::unique_ptr<RunState> run_state,
                        base::OnceClosure callback) {
diff --git a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupClientHelper.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupClientHelper.java
index f411b53..17b188e 100644
--- a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupClientHelper.java
+++ b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupClientHelper.java
@@ -121,4 +121,34 @@
             String accountName,
             Callback<Integer> successCallback,
             Callback<Exception> failureCallback) {}
+
+    /**
+     * Asynchronously returns the number of weak credentials for the provided account.
+     *
+     * @param referrer the place that requested number of weak credentials.
+     * @param accountName the account name that is syncing passwords. If no value was provided local
+     *     account will be used.
+     * @param successCallback callback called with the number of weak passwords.
+     * @param failureCallback callback called if encountered an error.
+     */
+    default void getWeakCredentialsCount(
+            @PasswordCheckReferrer int referrer,
+            String accountName,
+            Callback<Integer> successCallback,
+            Callback<Exception> failureCallback) {}
+
+    /**
+     * Asynchronously returns the number of reused credentials for the provided account.
+     *
+     * @param referrer the place that requested number of reused credentials.
+     * @param accountName the account name that is syncing passwords. If no value was provided local
+     *     account will be used.
+     * @param successCallback callback called with the number of reused passwords.
+     * @param failureCallback callback called if encountered an error.
+     */
+    default void getReusedCredentialsCount(
+            @PasswordCheckReferrer int referrer,
+            String accountName,
+            Callback<Integer> successCallback,
+            Callback<Exception> failureCallback) {}
 }
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 1a9678a..9628e59 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -59,6 +59,7 @@
 #include "components/autofill/core/common/password_generation_util.h"
 #include "components/back_forward_cache/back_forward_cache_disable.h"
 #include "components/browsing_data/content/browsing_data_helper.h"
+#include "components/device_reauth/device_authenticator.h"
 #include "components/no_state_prefetch/browser/no_state_prefetch_contents.h"
 #include "components/password_manager/content/browser/bad_message.h"
 #include "components/password_manager/content/browser/content_password_manager_driver.h"
@@ -607,17 +608,24 @@
   if (!authenticator || !GetPrefs()) {
     return false;
   }
-  bool can_authenticate =
-      authenticator->CanAuthenticateWithBiometricOrScreenLock();
+  device_reauth::BiometricStatus biometric_status =
+      authenticator->GetBiometricAvailabilityStatus();
   base::UmaHistogramBoolean(
       "PasswordManager.BiometricAuthPwdFillAndroid."
       "CanAuthenticateWithBiometricOrScreenLock",
-      can_authenticate);
-  return can_authenticate &&
-         base::FeatureList::IsEnabled(
-             password_manager::features::kBiometricTouchToFill) &&
-         GetPrefs()->GetBoolean(
-             password_manager::prefs::kBiometricAuthenticationBeforeFilling);
+      biometric_status != device_reauth::BiometricStatus::kUnavailable);
+  switch (biometric_status) {
+    case device_reauth::BiometricStatus::kRequired:
+      return true;
+    case device_reauth::BiometricStatus::kBiometricsAvailable:
+    case device_reauth::BiometricStatus::kOnlyLskfAvailable:
+      return base::FeatureList::IsEnabled(
+                 password_manager::features::kBiometricTouchToFill) &&
+             GetPrefs()->GetBoolean(password_manager::prefs::
+                                        kBiometricAuthenticationBeforeFilling);
+    case device_reauth::BiometricStatus::kUnavailable:
+      return false;
+  }
 #else
   return false;
 #endif
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index f22c9a5..9e967c16 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -52,11 +52,13 @@
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/signatures.h"
 #include "components/autofill/core/common/unique_ids.h"
+#include "components/device_reauth/device_authenticator.h"
 #include "components/device_reauth/mock_device_authenticator.h"
 #include "components/password_manager/content/browser/content_password_manager_driver.h"
 #include "components/password_manager/content/browser/password_manager_log_router_factory.h"
 #include "components/password_manager/core/browser/credential_cache.h"
 #include "components/password_manager/core/browser/credentials_filter.h"
+#include "components/password_manager/core/browser/features/password_features.h"
 #include "components/password_manager/core/browser/mock_password_manager_settings_service.h"
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/browser/password_manager.h"
@@ -140,6 +142,7 @@
 
 #if BUILDFLAG(IS_ANDROID)
 using base::android::BuildInfo;
+using device_reauth::BiometricStatus;
 using password_manager::CredentialCache;
 using password_manager::MockPasswordStoreInterface;
 #endif
@@ -853,14 +856,14 @@
     GTEST_SKIP();
   }
   device_reauth::MockDeviceAuthenticator authenticator;
-  ON_CALL(authenticator, CanAuthenticateWithBiometricOrScreenLock)
-      .WillByDefault(Return(true));
+  ON_CALL(authenticator, GetBiometricAvailabilityStatus)
+      .WillByDefault(Return(BiometricStatus::kBiometricsAvailable));
   EXPECT_FALSE(GetClient()->IsReauthBeforeFillingRequired(&authenticator));
 }
 
 // Test that authentication is not possible if the
-// `CanAuthenticateWithBiometrics` returns `false` when `kBiometricTouchToFill`
-// is enabled.
+// `GetBiometricAvailabilityStatus` returns `kUnavailable` when
+// `kBiometricTouchToFill` is enabled.
 TEST_F(ChromePasswordManagerClientTest,
        CanUseBiometricAuthAndroidAuthDisabled) {
   // Authentication is always available for automotive.
@@ -871,8 +874,8 @@
   base::test::ScopedFeatureList enabled_features(
       password_manager::features::kBiometricTouchToFill);
   device_reauth::MockDeviceAuthenticator authenticator;
-  ON_CALL(authenticator, CanAuthenticateWithBiometricOrScreenLock)
-      .WillByDefault(Return(false));
+  ON_CALL(authenticator, GetBiometricAvailabilityStatus)
+      .WillByDefault(Return(BiometricStatus::kUnavailable));
   EXPECT_FALSE(GetClient()->IsReauthBeforeFillingRequired(&authenticator));
   histogram_tester.ExpectUniqueSample(
       "PasswordManager.BiometricAuthPwdFillAndroid."
@@ -881,7 +884,7 @@
 }
 
 // Test that authentication is not possible if the
-// `CanAuthenticateWithBiometrics` returns `true`, but the
+// `GetBiometricAvailabilityStatus` returns `kBiometricsAvailable`, but the
 // `kBiometricReauthBeforePwdFilling` pref is set to false when
 // `kBiometricTouchToFill` is enabled.
 TEST_F(ChromePasswordManagerClientTest,
@@ -893,14 +896,14 @@
   base::test::ScopedFeatureList enabled_features(
       password_manager::features::kBiometricTouchToFill);
   device_reauth::MockDeviceAuthenticator authenticator;
-  ON_CALL(authenticator, CanAuthenticateWithBiometricOrScreenLock)
-      .WillByDefault(Return(true));
+  ON_CALL(authenticator, GetBiometricAvailabilityStatus)
+      .WillByDefault(Return(BiometricStatus::kBiometricsAvailable));
   EXPECT_FALSE(GetClient()->IsReauthBeforeFillingRequired(&authenticator));
 }
 
-// Test that authentication is possible if the `CanAuthenticateWithBiometrics`
-// returns `true` and the `kBiometricReauthBeforePwdFilling` pref is set to true
-// when `kBiometricTouchToFill` is enabled.
+// Test that authentication is possible if the `GetBiometricAvailabilityStatus`
+// returns `kOnlyLskfAvailable` and the `kBiometricReauthBeforePwdFilling`
+// pref is set to true when `kBiometricTouchToFill` is enabled.
 TEST_F(ChromePasswordManagerClientTest, CanUseBiometricAuthAndroidAuthEnabled) {
   // Authentication is always available for automotive.
   if (base::android::BuildInfo::GetInstance()->is_automotive()) {
@@ -913,8 +916,8 @@
   device_reauth::MockDeviceAuthenticator authenticator;
   profile()->GetTestingPrefService()->SetBoolean(
       password_manager::prefs::kBiometricAuthenticationBeforeFilling, true);
-  ON_CALL(authenticator, CanAuthenticateWithBiometricOrScreenLock)
-      .WillByDefault(Return(true));
+  ON_CALL(authenticator, GetBiometricAvailabilityStatus)
+      .WillByDefault(Return(BiometricStatus::kOnlyLskfAvailable));
   EXPECT_TRUE(GetClient()->IsReauthBeforeFillingRequired(&authenticator));
   histogram_tester.ExpectUniqueSample(
       "PasswordManager.BiometricAuthPwdFillAndroid."
@@ -922,8 +925,8 @@
       true, 1);
 }
 
-// Test that authentication is possible if the `CanAuthenticateWithBiometrics`
-// returns `true` on auto regardless of the pref and flag value.
+// Test that authentication is possible if the `GetBiometricAvailabilityStatus`
+// returns `kBiometricsAvailable` on auto regardless of the pref and flag value.
 TEST_F(ChromePasswordManagerClientTest,
        CanUseBiometricAuthAndroidAlwaysTrueOnAutomotive) {
   // Authentication is always available for automotive.
@@ -931,8 +934,23 @@
     GTEST_SKIP();
   }
   device_reauth::MockDeviceAuthenticator authenticator;
-  ON_CALL(authenticator, CanAuthenticateWithBiometricOrScreenLock)
-      .WillByDefault(Return(true));
+  ON_CALL(authenticator, GetBiometricAvailabilityStatus)
+      .WillByDefault(Return(BiometricStatus::kBiometricsAvailable));
+  EXPECT_TRUE(GetClient()->IsReauthBeforeFillingRequired(&authenticator));
+}
+
+// Test that `IsReauthBeforeFillingRequired` always returns true for mandatory
+// biometric auth.
+TEST_F(ChromePasswordManagerClientTest, MandatoryBiometricEnabled) {
+  // Authentication is always available for automotive.
+  if (base::android::BuildInfo::GetInstance()->is_automotive()) {
+    GTEST_SKIP();
+  }
+  base::test::ScopedFeatureList enabled_features(
+      password_manager::features::kBiometricAuthIdentityCheck);
+  device_reauth::MockDeviceAuthenticator authenticator;
+  ON_CALL(authenticator, GetBiometricAvailabilityStatus)
+      .WillByDefault(Return(BiometricStatus::kRequired));
   EXPECT_TRUE(GetClient()->IsReauthBeforeFillingRequired(&authenticator));
 }
 
diff --git a/chrome/browser/permissions/adaptive_quiet_notification_permission_ui_enabler.cc b/chrome/browser/permissions/adaptive_quiet_notification_permission_ui_enabler.cc
index 8c44485..b857755 100644
--- a/chrome/browser/permissions/adaptive_quiet_notification_permission_ui_enabler.cc
+++ b/chrome/browser/permissions/adaptive_quiet_notification_permission_ui_enabler.cc
@@ -269,10 +269,6 @@
 
 void AdaptiveQuietNotificationPermissionUiEnabler::
     MigrateAdaptiveNotificationQuietingToCPSS() {
-  if (!base::FeatureList::IsEnabled(
-          permissions::features::kPermissionDedicatedCpssSetting)) {
-    return;
-  }
   if (profile_->GetPrefs()->GetBoolean(
           prefs::kDidMigrateAdaptiveNotifiationQuietingToCPSS)) {
     return;
diff --git a/chrome/browser/permissions/adaptive_quiet_notification_permission_ui_enabler_unittest.cc b/chrome/browser/permissions/adaptive_quiet_notification_permission_ui_enabler_unittest.cc
index a42fc866..6454b0448 100644
--- a/chrome/browser/permissions/adaptive_quiet_notification_permission_ui_enabler_unittest.cc
+++ b/chrome/browser/permissions/adaptive_quiet_notification_permission_ui_enabler_unittest.cc
@@ -61,10 +61,6 @@
       {false, std::nullopt, true, false},
   };
 
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(
-      permissions::features::kPermissionDedicatedCpssSetting);
-
   auto* permission_ui_enabler =
       AdaptiveQuietNotificationPermissionUiEnabler::GetForProfile(profile());
 
diff --git a/chrome/browser/permissions/permission_settings_page_browsertest.cc b/chrome/browser/permissions/permission_settings_page_browsertest.cc
index 7ba2457..ce3b31a 100644
--- a/chrome/browser/permissions/permission_settings_page_browsertest.cc
+++ b/chrome/browser/permissions/permission_settings_page_browsertest.cc
@@ -15,7 +15,6 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/content_settings/core/common/pref_names.h"
-#include "components/permissions/features.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/dns/mock_host_resolver.h"
@@ -53,10 +52,6 @@
 
 class PredictionSettingsPageBrowserTest : public InteractiveBrowserTest {
  public:
-  PredictionSettingsPageBrowserTest() {
-    scoped_feature_list_.InitWithFeaturesAndParameters(
-        {{permissions::features::kPermissionDedicatedCpssSetting, {}}}, {});
-  }
 
   ~PredictionSettingsPageBrowserTest() override = default;
 
@@ -288,9 +283,6 @@
             }))
         .Build();
   }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(PredictionSettingsPageBrowserTest,
diff --git a/chrome/browser/permissions/pref_based_quiet_permission_ui_selector.cc b/chrome/browser/permissions/pref_based_quiet_permission_ui_selector.cc
index 15168a8..10f995c83 100644
--- a/chrome/browser/permissions/pref_based_quiet_permission_ui_selector.cc
+++ b/chrome/browser/permissions/pref_based_quiet_permission_ui_selector.cc
@@ -52,8 +52,7 @@
   if (request_type == permissions::RequestType::kNotifications) {
     return true;
   } else if (request_type == permissions::RequestType::kGeolocation) {
-    return base::FeatureList::IsEnabled(
-        permissions::features::kPermissionDedicatedCpssSetting);
+    return true;
   } else {
     return false;
   }
diff --git a/chrome/browser/profiles/profile_keyed_service_browsertest.cc b/chrome/browser/profiles/profile_keyed_service_browsertest.cc
index c3dbf9f..9da6a70b 100644
--- a/chrome/browser/profiles/profile_keyed_service_browsertest.cc
+++ b/chrome/browser/profiles/profile_keyed_service_browsertest.cc
@@ -592,6 +592,7 @@
     "WebRequestEventRouter",
     "WebRtcEventLogManagerKeyedService",
     "WebrtcAudioPrivateEventService",
+    "WriteQuotaChecker",
     "feedback::FeedbackUploaderChrome",
     "sct_reporting::Factory",
     "ZeroSuggestCacheServiceFactory",
diff --git a/chrome/browser/resources/discards/discards_tab.html b/chrome/browser/resources/discards/discards_tab.html
index d18b3df..066d0ac 100644
--- a/chrome/browser/resources/discards/discards_tab.html
+++ b/chrome/browser/resources/discards/discards_tab.html
@@ -212,6 +212,14 @@
                 </div>
               </div>
             </th>
+            <th data-sort-key="canDiscard" on-click="onSortClick">
+              <div class="header-cell-container">
+                <div>
+                  <div>Is</div>
+                  <div>Discardable</div>
+                </div>
+              </div>
+            </th>
             <th data-sort-key="discardCount" on-click="onSortClick">
               <div class="header-cell-container">
                 <div>
@@ -258,6 +266,14 @@
               <td>[[visibilityToString_(item.visibility)]]</td>
               <td>[[loadingStateToString_(item.loadingState)]]</td>
               <td>[[getLifeCycleState_(item)]]</td>
+              <td class="boolean-cell">
+                <div>[[boolToString_(item.canDiscard)]]</div>
+                <div is="action-link" class="tooltip-container"
+                  disabled$="[[!shouldShowCannotDiscardReason_(item)]]">
+                  [View Reason]
+                  <div class="tooltip">[[item.cannotDiscardReasons]]<div>
+                </div>
+              </td>
               <td>[[item.discardCount]]</td>
               <td class="boolean-cell">
                 <div>[[boolToString_(item.isAutoDiscardable)]]</div>
diff --git a/chrome/browser/resources/discards/discards_tab.ts b/chrome/browser/resources/discards/discards_tab.ts
index ed0fe59..8dd2017 100644
--- a/chrome/browser/resources/discards/discards_tab.ts
+++ b/chrome/browser/resources/discards/discards_tab.ts
@@ -356,6 +356,16 @@
   }
 
   /**
+   * Tests whether an item should show the reason why it cannot be discarded.
+   * @param item The item in question.
+   * @return true iff the item should show the reason why it cannot be
+   *     discarded.
+   */
+  private shouldShowCannotDiscardReason_(item: TabDiscardsInfo): boolean {
+    return !item.canDiscard && item.state !== LifecycleUnitState.DISCARDED;
+  }
+
+  /**
    * Event handler that toggles the auto discardable flag on an item.
    * @param e The event.
    */
diff --git a/chrome/browser/resources/search_engine_choice/app.ts b/chrome/browser/resources/search_engine_choice/app.ts
index 0fc6092..71bc84d 100644
--- a/chrome/browser/resources/search_engine_choice/app.ts
+++ b/chrome/browser/resources/search_engine_choice/app.ts
@@ -3,10 +3,9 @@
 // found in the LICENSE file.
 
 import 'chrome://resources/cr_components/localized_link/localized_link.js';
+import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 import 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
-import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 import 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.js';
 import 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.js';
 import './strings.m.js';
diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resources/settings/BUILD.gn
index 85cde67..a239452 100644
--- a/chrome/browser/resources/settings/BUILD.gn
+++ b/chrome/browser/resources/settings/BUILD.gn
@@ -206,6 +206,7 @@
     "site_settings_page/site_settings_page.ts",
     "site_settings_page/unused_site_permissions.ts",
     "site_settings/smart_card_readers_page.ts",
+    "site_settings/smart_card_reader_origin_entry.ts",
     "site_settings/pdf_documents.ts",
     "site_settings/protocol_handlers.ts",
     "site_settings/settings_category_default_radio_group.ts",
diff --git a/chrome/browser/resources/settings/lazy_load.ts b/chrome/browser/resources/settings/lazy_load.ts
index 7b586d0..6e9e469 100644
--- a/chrome/browser/resources/settings/lazy_load.ts
+++ b/chrome/browser/resources/settings/lazy_load.ts
@@ -246,7 +246,9 @@
 export {SiteEntryElement} from './site_settings/site_entry.js';
 export {SiteListElement} from './site_settings/site_list.js';
 export {SiteListEntryElement} from './site_settings/site_list_entry.js';
-export {ChooserException, DefaultContentSetting, DefaultSettingSource, FileSystemGrant, OriginFileSystemGrants, OriginInfo, RawChooserException, RawSiteException, RecentSitePermissions, SiteException, SiteGroup, SiteSettingsPrefsBrowserProxy, SiteSettingsPrefsBrowserProxyImpl, StorageAccessEmbeddingException, StorageAccessSiteException, ZoomLevelEntry} from './site_settings/site_settings_prefs_browser_proxy.js';
+export {ChooserException, DefaultContentSetting, DefaultSettingSource, FileSystemGrant, OriginFileSystemGrants, OriginInfo, RawChooserException, RawSiteException, RecentSitePermissions, SiteException, SiteGroup, SiteSettingsPrefsBrowserProxy, SiteSettingsPrefsBrowserProxyImpl, SmartCardReaderGrants, StorageAccessEmbeddingException, StorageAccessSiteException, ZoomLevelEntry} from './site_settings/site_settings_prefs_browser_proxy.js';
+export {SmartCardReaderOriginEntryElement} from './site_settings/smart_card_reader_origin_entry.js';
+export {SettingsSmartCardReadersPageElement} from './site_settings/smart_card_readers_page.js';
 export {StorageAccessSiteListElement} from './site_settings/storage_access_site_list.js';
 export {StorageAccessSiteListEntryElement} from './site_settings/storage_access_site_list_entry.js';
 export {StorageAccessStaticSiteListEntry, StorageAccessStaticSiteListEntryElement} from './site_settings/storage_access_static_site_list_entry.js';
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html
index c966007..0cff7d2 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -495,67 +495,52 @@
             $i18n{siteSettingsLocationDescription}
           </div>
           <div id="locationRadioGroup" class="radio-group">
-            <template is="dom-if" if="[[!showDedicatedCpssSetting_]]">
-              <settings-category-default-radio-group
-                  category="[[contentSettingsTypesEnum_.GEOLOCATION]]"
-                  allow-option-label=
-                        "$i18n{siteSettingsLocationAllowed}"
-                  allow-option-icon="settings:location-on"
-                  block-option-label="$i18n{siteSettingsLocationBlocked}"
-                  block-option-sub-label=
-                      "$i18n{siteSettingsLocationBlockedSubLabel}"
-                  block-option-icon="privacy:location-off">
-              </settings-category-default-radio-group>
-            </template>
-
-            <template is="dom-if" if="[[showDedicatedCpssSetting_]]">
-              <h2>$i18n{siteSettingsDefaultBehavior}</h2>
-              <div id="geolocationSubHeading"
+            <h2>$i18n{siteSettingsDefaultBehavior}</h2>
+            <div id="geolocationSubHeading"
                 class="secondary radio-sub-heading">
                 $i18n{siteSettingsDefaultBehaviorDescription}
-              </div>
+            </div>
 
-              <cr-radio-group
-                  on-selected-changed="onLocationTopLevelRadioChanged_">
-                <cr-radio-button no-collapse
-                    name="location-ask-radio-button"
-                    checked$="[[isLocationAllowed_]]">
-                  <cr-icon icon="settings:location-on"></cr-icon>
-                  $i18n{siteSettingsLocationAllowed}
+            <cr-radio-group
+                on-selected-changed="onLocationTopLevelRadioChanged_">
+              <cr-radio-button no-collapse
+                  name="location-ask-radio-button"
+                  checked$="[[isLocationAllowed_]]">
+                <cr-icon icon="settings:location-on"></cr-icon>
+                $i18n{siteSettingsLocationAllowed}
+              </cr-radio-button>
+
+              <settings-radio-group
+                  pref="{{prefs.generated.geolocation}}"
+                  selectable-elements="cr-radio-button"
+                  hidden$="[[!isLocationAllowed_]]">
+                <cr-radio-button class="padded-radio-section"
+                    name="[[settingsStateEnum_.QUIET]]"
+                    pref="[[prefs.generated.geolocation]]"
+                    label="$i18n{siteSettingsLocationAskQuiet}">
                 </cr-radio-button>
 
-                <settings-radio-group
-                    pref="{{prefs.generated.geolocation}}"
-                    selectable-elements="cr-radio-button"
-                    hidden$="[[!isLocationAllowed_]]">
-                  <cr-radio-button class="padded-radio-section"
-                      name="[[settingsStateEnum_.QUIET]]"
-                      pref="[[prefs.generated.geolocation]]"
-                      label="$i18n{siteSettingsLocationAskQuiet}">
-                  </cr-radio-button>
-
-                  <cr-radio-button class="padded-radio-section"
-                      name="[[settingsStateEnum_.CPSS]]"
-                      pref="[[prefs.generated.geolocation]]"
-                      label="$i18n{siteSettingsLocationAskCPSS}">
-                  </cr-radio-button>
-
-                  <cr-radio-button class="padded-radio-section"
-                      name="[[settingsStateEnum_.LOUD]]"
-                      pref="[[prefs.generated.geolocation]]"
-                      label="$i18n{siteSettingsLocationAskLoud}">
-                  </cr-radio-button>
-                </settings-radio-group>
-
-                <cr-radio-button class="two-line"
-                    name="location-block-radio-button"
-                    sub-label="$i18n{siteSettingsLocationBlockedSubLabel}"
-                    checked$="[[!isLocationAllowed_]]">
-                  <cr-icon icon="privacy:location-off"></cr-icon>
-                  $i18n{siteSettingsLocationBlocked}
+                <cr-radio-button class="padded-radio-section"
+                    name="[[settingsStateEnum_.CPSS]]"
+                    pref="[[prefs.generated.geolocation]]"
+                    label="$i18n{siteSettingsLocationAskCPSS}">
                 </cr-radio-button>
-              </cr-radio-group>
-            </template>
+
+                <cr-radio-button class="padded-radio-section"
+                    name="[[settingsStateEnum_.LOUD]]"
+                    pref="[[prefs.generated.geolocation]]"
+                    label="$i18n{siteSettingsLocationAskLoud}">
+                </cr-radio-button>
+              </settings-radio-group>
+
+              <cr-radio-button class="two-line"
+                  name="location-block-radio-button"
+                  sub-label="$i18n{siteSettingsLocationBlockedSubLabel}"
+                  checked$="[[!isLocationAllowed_]]">
+                <cr-icon icon="privacy:location-off"></cr-icon>
+                $i18n{siteSettingsLocationBlocked}
+              </cr-radio-button>
+            </cr-radio-group>
           </div>
 
           <category-setting-exceptions
@@ -753,83 +738,51 @@
               $i18n{siteSettingsNotificationsDefaultBehaviorDescription}
             </div>
 
-            <template is="dom-if" if="[[!showDedicatedCpssSetting_]]">
+            <cr-radio-group
+                on-selected-changed="onNotificationTopLevelRadioChanged_">
+              <cr-radio-button
+                  id="notification-ask-radio-button"
+                  name="notification-ask-radio-button"
+                  checked$="[[isNotificationAllowed_]]">
+                <cr-icon icon="privacy:notifications"></cr-icon>
+                $i18n{siteSettingsNotificationsAskState}
+              </cr-radio-button>
+
               <settings-radio-group
                   pref="{{prefs.generated.notification}}"
-                  selectable-elements="settings-collapse-radio-button">
-                <settings-collapse-radio-button no-collapse
-                    name="[[settingsStateEnum_.LOUD]]"
-                    pref="[[prefs.generated.notification]]"
-                    label="$i18n{siteSettingsNotificationsAllowed}"
-                    icon="privacy:notifications">
-                </settings-collapse-radio-button>
-
-                <settings-collapse-radio-button no-collapse class="two-line"
+                  selectable-elements="cr-radio-button"
+                  hidden$="[[!isNotificationAllowed_]]">
+                <cr-radio-button class="padded-radio-section"
+                    id="notification-ask-quiet"
                     name="[[settingsStateEnum_.QUIET]]"
                     pref="[[prefs.generated.notification]]"
-                    label="$i18n{siteSettingsNotificationsPartial}"
-                    sub-label=
-                        "$i18n{siteSettingsNotificationsPartialSubLabel}"
-                    icon="privacy:notifications">
-                </settings-collapse-radio-button>
+                    label="$i18n{siteSettingsNotificationsAskQuiet}">
+                </cr-radio-button>
 
-                <settings-collapse-radio-button no-collapse class="two-line"
-                    name="[[settingsStateEnum_.BLOCK]]"
+                <cr-radio-button class="padded-radio-section"
+                    id="notification-ask-cpss"
+                    name="[[settingsStateEnum_.CPSS]]"
                     pref="[[prefs.generated.notification]]"
-                    label="$i18n{siteSettingsNotificationsBlocked}"
-                    sub-label="$i18n{siteSettingsNotificationsBlockedSubLabel}"
-                    icon="privacy:notifications-off">
-                </settings-collapse-radio-button>
+                    label="$i18n{siteSettingsNotificationsAskCPSS}">
+                </cr-radio-button>
+
+                <cr-radio-button class="padded-radio-section"
+                    id="notification-ask-loud"
+                    name="[[settingsStateEnum_.LOUD]]"
+                    pref="[[prefs.generated.notification]]"
+                    label="$i18n{siteSettingsNotificationsAskLoud}">
+                </cr-radio-button>
               </settings-radio-group>
-            </template>
 
-            <template is="dom-if" if="[[showDedicatedCpssSetting_]]">
-              <cr-radio-group
-                  on-selected-changed="onNotificationTopLevelRadioChanged_">
-                <cr-radio-button
-                    id="notification-ask-radio-button"
-                    name="notification-ask-radio-button"
-                    checked$="[[isNotificationAllowed_]]">
-                  <cr-icon icon="privacy:notifications"></cr-icon>
-                  $i18n{siteSettingsNotificationsAskState}
-                </cr-radio-button>
-
-                <settings-radio-group
-                    pref="{{prefs.generated.notification}}"
-                    selectable-elements="cr-radio-button"
-                    hidden$="[[!isNotificationAllowed_]]">
-                  <cr-radio-button class="padded-radio-section"
-                      id="notification-ask-quiet"
-                      name="[[settingsStateEnum_.QUIET]]"
-                      pref="[[prefs.generated.notification]]"
-                      label="$i18n{siteSettingsNotificationsAskQuiet}">
-                  </cr-radio-button>
-
-                  <cr-radio-button class="padded-radio-section"
-                      id="notification-ask-cpss"
-                      name="[[settingsStateEnum_.CPSS]]"
-                      pref="[[prefs.generated.notification]]"
-                      label="$i18n{siteSettingsNotificationsAskCPSS}">
-                  </cr-radio-button>
-
-                  <cr-radio-button class="padded-radio-section"
-                      id="notification-ask-loud"
-                      name="[[settingsStateEnum_.LOUD]]"
-                      pref="[[prefs.generated.notification]]"
-                      label="$i18n{siteSettingsNotificationsAskLoud}">
-                  </cr-radio-button>
-                </settings-radio-group>
-
-                <cr-radio-button class="two-line"
-                    id="notification-block"
-                    name="notification-block-radio-button"
-                    sub-label="$i18n{siteSettingsNotificationsBlockedSubLabel}"
-                    checked$="[[!isNotificationAllowed_]]">
-                  <cr-icon icon="privacy:notifications-off"></cr-icon>
-                  $i18n{siteSettingsNotificationsBlocked}
-                </cr-radio-button>
-              </cr-radio-group>
-            </template>
+              <cr-radio-button class="two-line"
+                  id="notification-block"
+                  name="notification-block-radio-button"
+                  sub-label="$i18n{siteSettingsNotificationsBlockedSubLabel}"
+                  checked$="[[!isNotificationAllowed_]]">
+                <cr-icon icon="privacy:notifications-off"></cr-icon>
+                $i18n{siteSettingsNotificationsBlocked}
+              </cr-radio-button>
+            </cr-radio-group>
           </div>
           <category-setting-exceptions
               category="[[contentSettingsTypesEnum_.NOTIFICATIONS]]"
@@ -1412,7 +1365,9 @@
       </template>
       <template is="dom-if" if="[[enableSmartCardReadersContentSetting_]]">
         <template is="dom-if" route-path="/content/smartCardReaders" no-search>
-          <settings-subpage page-title="$i18n{siteSettingsSmartCardReaders}">
+          <settings-subpage page-title="$i18n{siteSettingsSmartCardReaders}"
+              search-label="$i18n{siteSettingsAllSitesSearch}"
+              search-term="{{searchFilter_}}">
             <settings-smart-card-readers-page filter="[[searchFilter_]]">
             </settings-smart-card-readers-page>
           </settings-subpage>
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.ts b/chrome/browser/resources/settings/privacy_page/privacy_page.ts
index 2dc0c13..ead2033a 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.ts
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.ts
@@ -328,13 +328,6 @@
         },
       },
 
-      showDedicatedCpssSetting_: {
-        type: Boolean,
-        value() {
-          return loadTimeData.getBoolean('permissionDedicatedCpssSettings');
-        },
-      },
-
       // <if expr="chrome_root_store_cert_management_ui">
       enableCertManagementUIV2_: {
         type: Boolean,
@@ -404,7 +397,6 @@
       SafetyHubBrowserProxyImpl.getInstance();
   private isNotificationAllowed_: boolean;
   private isLocationAllowed_: boolean;
-  private showDedicatedCpssSetting_: boolean;
   // <if expr="chrome_root_store_cert_management_ui">
   private enableCertManagementUIV2_: boolean;
   // </if>
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.ts b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.ts
index 0f111a1..308ede26 100644
--- a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.ts
+++ b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.ts
@@ -196,6 +196,11 @@
   editGrants: FileSystemGrant[];
 }
 
+export interface SmartCardReaderGrants {
+  readerName: string;
+  origins: string[];
+}
+
 export interface SiteSettingsPrefsBrowserProxy {
   /**
    * Sets the default value for a site settings category.
@@ -268,6 +273,24 @@
   revokeFileSystemGrants(origin: string): void;
 
   /**
+   * Gets the persistent Smart Card Reader permission grants, grouped by reader
+   * name.
+   */
+  getSmartCardReaderGrants(): Promise<SmartCardReaderGrants[]>;
+
+  /**
+   * Revokes all Smart Card Reader permission grants.
+   */
+  revokeAllSmartCardReadersGrants(): void;
+
+  /**
+   * Revokes a particular persistent Smart Card Reader permission grant.
+   * @param reader The smart card reader name.
+   * @param origin URL of the site that was granted the permission.
+   */
+  revokeSmartCardReaderGrant(reader: string, origin: string): void;
+
+  /**
    * Gets a list of category permissions for a given origin. Note that this
    * may be different to the results retrieved by getExceptionList(), since it
    * combines different sources of data to get a permission's value.
@@ -554,6 +577,18 @@
     chrome.send('revokeFileSystemGrants', [origin]);
   }
 
+  getSmartCardReaderGrants() {
+    return sendWithPromise('getSmartCardReaderGrants');
+  }
+
+  revokeAllSmartCardReadersGrants() {
+    chrome.send('revokeAllSmartCardReadersGrants');
+  }
+
+  revokeSmartCardReaderGrant(reader: string, origin: string) {
+    chrome.send('revokeSmartCardReaderGrant', [reader, origin]);
+  }
+
   getOriginPermissions(origin: string, contentTypes: ContentSettingsTypes[]) {
     return sendWithPromise('getOriginPermissions', origin, contentTypes);
   }
diff --git a/chrome/browser/resources/settings/site_settings/smart_card_reader_origin_entry.html b/chrome/browser/resources/settings/site_settings/smart_card_reader_origin_entry.html
new file mode 100644
index 0000000..edc7644
--- /dev/null
+++ b/chrome/browser/resources/settings/site_settings/smart_card_reader_origin_entry.html
@@ -0,0 +1,36 @@
+<style include="cr-shared-style settings-shared">
+  .origin {
+    flex-grow: 1;
+    padding-inline-start: 20px;
+  }
+
+  .origin-row {
+    align-items: center;
+    display: flex;
+    padding-bottom: 5px;
+    padding-inline-end: 10px;
+    padding-top: 5px;
+  }
+
+  .subpage-arrow {
+    margin-inline-end: 2px;
+  }
+
+  .separator {
+    padding-inline-end: 25px;
+  }
+</style>
+
+<div class="list-frame">
+  <div class="origin-row">
+    <site-favicon url="[[origin]]"></site-favicon>
+    <div class="origin">[[origin]]</div>
+    <div class="separator"></div>
+    <cr-icon-button
+        class="icon-delete-gray"
+        id="removeOrigin"
+        on-click="onRemoveOriginClick_"
+        aria-label$="[[origin]]">
+    </cr-icon-button>
+  </div>
+</div>
diff --git a/chrome/browser/resources/settings/site_settings/smart_card_reader_origin_entry.ts b/chrome/browser/resources/settings/site_settings/smart_card_reader_origin_entry.ts
new file mode 100644
index 0000000..3deff62
--- /dev/null
+++ b/chrome/browser/resources/settings/site_settings/smart_card_reader_origin_entry.ts
@@ -0,0 +1,59 @@
+// 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.
+
+/**
+ * @fileoverview
+ * 'file-system-site-entry' is an element representing a single origin's
+ * permission grant(s), granted via the File System Access API.
+ */
+import 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.js';
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import 'chrome://resources/cr_elements/cr_shared_style.css.js';
+import './file_system_site_entry_item.js';
+import '../settings_shared.css.js';
+import '../site_favicon.js';
+
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {SiteSettingsMixin} from './site_settings_mixin.js';
+import {getTemplate} from './smart_card_reader_origin_entry.html.js';
+
+const SmartCardReaderOriginEntryElementBase = SiteSettingsMixin(PolymerElement);
+
+export class SmartCardReaderOriginEntryElement extends
+    SmartCardReaderOriginEntryElementBase {
+  static get is() {
+    return 'smart-card-reader-origin-entry';
+  }
+
+  static get template() {
+    return getTemplate();
+  }
+
+  static get properties() {
+    return {
+      smartCardReaderName: {
+        type: String,
+      },
+      origin: {
+        type: String,
+      },
+    };
+  }
+  smartCardReaderName: string;
+  origin: string;
+
+  private onRemoveOriginClick_() {
+    this.browserProxy.revokeSmartCardReaderGrant(
+        this.smartCardReaderName, this.origin);
+  }
+}
+declare global {
+  interface HTMLElementTagNameMap {
+    'smart-card-reader-origin-entry': SmartCardReaderOriginEntryElement;
+  }
+}
+
+customElements.define(
+    SmartCardReaderOriginEntryElement.is, SmartCardReaderOriginEntryElement);
diff --git a/chrome/browser/resources/settings/site_settings/smart_card_readers_page.html b/chrome/browser/resources/settings/site_settings/smart_card_readers_page.html
index 93681b2..1392041 100644
--- a/chrome/browser/resources/settings/site_settings/smart_card_readers_page.html
+++ b/chrome/browser/resources/settings/site_settings/smart_card_readers_page.html
@@ -3,11 +3,19 @@
   .radio-group {
     padding: 0 var(--cr-section-padding);
   }
+  #resetButton {
+    margin-top: 24px;
+  }
+  .smart-card-reader-entry {
+    margin-top: 30px;
+    margin-bottom: 16px;
+  }
 </style>
 
 <div class="content-settings-header secondary">
   $i18n{siteSettingsSmartCardReadersDescription}
 </div>
+
 <settings-category-default-radio-group
     category="[[contentSettingsType_]]"
     description=
@@ -18,3 +26,44 @@
     block-option-label="$i18n{siteSettingsSmartCardReadersBlocked}"
     block-option-icon="privacy:smart-card-reader-off">
 </settings-category-default-radio-group>
+
+<div id="readersNotFound"
+  class="content-settings-header secondary smart-card-reader-entry"
+  hidden$="[[hasReadersWithGrants_(readersWithGrants_.*)]]">
+  $i18n{siteSettingsNoSmartCardReadersFound}
+</div>
+
+<div class="cr-row first"
+  hidden$="[[!hasReadersWithGrants_(readersWithGrants_.*)]]">
+  <cr-button id="resetButton" class="header-aligned-button"
+      role="button" aria-disabled="false"
+      on-click="onClickShowResetConfirmDialog_">
+    $i18n{siteSettingsReset}
+  </cr-button>
+</div>
+
+<template is="dom-repeat" items="[[readersWithGrants_]]" as="reader">
+  <div class="content-settings-header secondary smart-card-reader-entry">
+    [[reader.readerName]]
+  </div>
+  <template is="dom-repeat" items="[[reader.origins]]" as="origin">
+    <smart-card-reader-origin-entry
+      smart-card-reader-name="[[reader.readerName]]"
+      origin="[[origin]]">
+    </smart-card-reader-origin-entry>
+  </template>
+</template>
+
+<!-- Confirm reset settings dialog. -->
+<cr-dialog id="confirmReset" close-text="$i18n{close}"
+    on-close="onResetDialogClosed_">
+  <div slot="title">$i18n{siteSettingsResetSmartCardConfirmation}</div>
+  <div slot="button-container">
+    <cr-button class="cancel-button" on-click="onCloseDialog_">
+      $i18n{cancel}
+    </cr-button>
+    <cr-button class="action-button" on-click="onClickReset_">
+      $i18n{siteSettingsSiteResetAll}
+    </cr-button>
+  </div>
+</cr-dialog>
diff --git a/chrome/browser/resources/settings/site_settings/smart_card_readers_page.ts b/chrome/browser/resources/settings/site_settings/smart_card_readers_page.ts
index 44f6bdc..0f5d8cc 100644
--- a/chrome/browser/resources/settings/site_settings/smart_card_readers_page.ts
+++ b/chrome/browser/resources/settings/site_settings/smart_card_readers_page.ts
@@ -3,13 +3,37 @@
 // found in the LICENSE file.
 
 import '../controls/settings_toggle_button.js';
+import './smart_card_reader_origin_entry.js';
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
 
+import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
+import {assert} from 'chrome://resources/js/assert.js';
+import {focusWithoutInk} from 'chrome://resources/js/focus_without_ink.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {CrDialogElement, SmartCardReaderGrants} from 'lazy_load.js';
+
+import {routes} from '../route.js';
+import {RouteObserverMixin} from '../router.js';
+import type {Route} from '../router.js';
 
 import {ContentSettingsTypes} from './constants.js';
+import {SiteSettingsMixin} from './site_settings_mixin.js';
 import {getTemplate} from './smart_card_readers_page.html.js';
 
-export class SettingsSmartCardReadersPageElement extends PolymerElement {
+export interface SettingsSmartCardReadersPageElement {
+  $: {
+    confirmReset: CrDialogElement,
+    resetButton: CrButtonElement,
+  };
+}
+
+const SettingsSmartCardReadersPageElementBase =
+    SiteSettingsMixin(RouteObserverMixin(WebUiListenerMixin(PolymerElement)));
+
+export class SettingsSmartCardReadersPageElement extends
+    SettingsSmartCardReadersPageElementBase {
   static get is() {
     return 'settings-smart-card-readers-page';
   }
@@ -24,8 +48,62 @@
         type: ContentSettingsTypes,
         value: ContentSettingsTypes.SMART_CARD_READERS,
       },
+      readersWithGrants_: {
+        type: Array,
+        value: () => [],
+      },
     };
   }
+
+  private readersWithGrants_: SmartCardReaderGrants[];
+
+  override connectedCallback() {
+    super.connectedCallback();
+
+    this.addWebUiListener(
+        'contentSettingChooserPermissionChanged',
+        (category: ContentSettingsTypes) => {
+          if (category === ContentSettingsTypes.SMART_CARD_READERS) {
+            this.populateList_();
+          }
+        });
+  }
+
+  private onResetSettingsDialogClosed_() {
+    const toFocus = this.$.resetButton;
+    assert(toFocus);
+    focusWithoutInk(toFocus);
+  }
+
+  private onCloseDialog_(_e: Event) {
+    this.$.confirmReset.close();
+  }
+
+  private onClickReset_(e: Event) {
+    this.browserProxy.revokeAllSmartCardReadersGrants();
+    this.onCloseDialog_(e);
+  }
+
+  private onClickShowResetConfirmDialog_(e: Event) {
+    e.preventDefault();
+    this.$.confirmReset.showModal();
+  }
+
+  private async populateList_() {
+    await this.browserProxy.getSmartCardReaderGrants().then(
+        (grants) => this.set('readersWithGrants_', grants));
+  }
+
+  private hasReadersWithGrants_(): boolean {
+    return this.readersWithGrants_.length > 0;
+  }
+
+  override currentRouteChanged(currentRoute: Route, oldRoute?: Route) {
+    if (currentRoute === routes.SITE_SETTINGS_SMART_CARD_READERS &&
+        currentRoute !== oldRoute) {
+      this.populateList_();
+    }
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_list.ts b/chrome/browser/resources/settings/site_settings_page/site_settings_list.ts
index 9dc2ee9..6c565fc 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_list.ts
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_list.ts
@@ -229,9 +229,6 @@
    * description changes.
    */
   private updateLocationLabel_() {
-    if (!loadTimeData.getBoolean('permissionDedicatedCpssSettings')) {
-      return;
-    }
     const state = this.getPref('generated.geolocation').value;
     const index = this.categoryList.map(e => e.id).indexOf(
         ContentSettingsTypes.GEOLOCATION);
diff --git a/chrome/browser/search_engine_choice/search_engine_choice_dialog_browsertest.cc b/chrome/browser/search_engine_choice/search_engine_choice_dialog_browsertest.cc
index fadf5f1..660f75d 100644
--- a/chrome/browser/search_engine_choice/search_engine_choice_dialog_browsertest.cc
+++ b/chrome/browser/search_engine_choice/search_engine_choice_dialog_browsertest.cc
@@ -134,10 +134,10 @@
 class SearchEngineChoiceDialogBrowserTest : public InProcessBrowserTest {
  public:
   explicit SearchEngineChoiceDialogBrowserTest(bool use_spy_service = true)
-      : use_spy_service_(use_spy_service) {
-  }
+      : use_spy_service_(use_spy_service) {}
 
-  SearchEngineChoiceDialogBrowserTest(const SearchEngineChoiceDialogBrowserTest&) = delete;
+  SearchEngineChoiceDialogBrowserTest(
+      const SearchEngineChoiceDialogBrowserTest&) = delete;
   SearchEngineChoiceDialogBrowserTest& operator=(
       const SearchEngineChoiceDialogBrowserTest&) = delete;
 
@@ -265,6 +265,7 @@
     profiles::SwitchToGuestProfile(browser_future.GetCallback());
     Browser* guest_browser = browser_future.Get();
     CHECK(guest_browser);
+    EXPECT_TRUE(guest_browser->profile()->IsGuestSession());
     content::WebContents* ntp_contents =
         guest_browser->tab_strip_model()->GetActiveWebContents();
     content::WaitForLoadStop(ntp_contents);
@@ -278,6 +279,8 @@
       SearchEngineChoiceDialogServiceFactory::
           ScopedChromeBuildOverrideForTesting(
               /*force_chrome_build=*/true);
+  base::test::ScopedFeatureList feature_list_{
+      switches::kSearchEngineChoiceGuestExperience};
   bool use_spy_service_;
   base::CallbackListSubscription create_services_subscription_;
   base::HistogramTester histogram_tester_;
@@ -676,6 +679,7 @@
 
   Browser* first_guest_session = CreateGuestBrowserAndLoadNTP();
   EXPECT_EQ(BrowserList::GetInstance()->size(), 2u);
+
   auto* first_service = static_cast<MockSearchEngineChoiceDialogService*>(
       SearchEngineChoiceDialogServiceFactory::GetForProfile(
           first_guest_session->profile()));
@@ -703,6 +707,61 @@
       TemplateURLPrepopulateData::bing.id, /*save_guest_mode_selection=*/false,
       SearchEngineChoiceDialogService::EntryPoint::kDialog);
   EXPECT_FALSE(second_service->IsShowingDialog(*second_guest_session));
+
+  CheckNavigationConditionRecorded(
+      search_engines::SearchEngineChoiceScreenConditions::
+          kUsingPersistedGuestSessionChoice,
+      0);
+}
+
+IN_PROC_BROWSER_TEST_F(SearchEngineChoiceDialogBrowserTest,
+                       PRE_SearchEngineIsSavedBetweenGuestSessionsIfNeeded) {
+  // Initial browser
+  EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
+
+  Browser* guest_session = CreateGuestBrowserAndLoadNTP();
+  EXPECT_EQ(BrowserList::GetInstance()->size(), 2u);
+  auto* first_service = static_cast<MockSearchEngineChoiceDialogService*>(
+      SearchEngineChoiceDialogServiceFactory::GetForProfile(
+          guest_session->profile()));
+
+  // Complete the choice for the first guest profile and choose to save the
+  // choice between guest sessions.
+  first_service->NotifyChoiceMade(
+      TemplateURLPrepopulateData::bing.id, /*save_guest_mode_selection=*/true,
+      SearchEngineChoiceDialogService::EntryPoint::kDialog);
+  EXPECT_FALSE(first_service->IsShowingDialog(*guest_session));
+
+  CloseBrowserSynchronously(guest_session);
+  EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
+}
+
+IN_PROC_BROWSER_TEST_F(SearchEngineChoiceDialogBrowserTest,
+                       SearchEngineIsSavedBetweenGuestSessionsIfNeeded) {
+#if !BUILDFLAG(IS_MAC)
+  // This initial browser is sometimes missing on mac. We don't really need that
+  // browser, so if the guest browser works, then the test might still succeed.
+  EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
+#endif
+
+  Browser* guest_session = CreateGuestBrowserAndLoadNTP();
+#if !BUILDFLAG(IS_MAC)
+  EXPECT_EQ(BrowserList::GetInstance()->size(), 2u);
+#endif
+  auto* second_service = static_cast<MockSearchEngineChoiceDialogService*>(
+      SearchEngineChoiceDialogServiceFactory::GetForProfile(
+          guest_session->profile()));
+
+  // The search engine choice dialog doesn't get displayed for the second guest
+  // profile and the previously chosen default search engine is used.
+  EXPECT_FALSE(second_service->IsShowingDialog(*guest_session));
+  EXPECT_EQ(g_browser_process->local_state()->GetInt64(
+                prefs::kDefaultSearchProviderGuestModePrepopulatedId),
+            TemplateURLPrepopulateData::bing.id);
+  CheckNavigationConditionRecorded(
+      search_engines::SearchEngineChoiceScreenConditions::
+          kUsingPersistedGuestSessionChoice,
+      1);
 }
 #endif
 
diff --git a/chrome/browser/search_engine_choice/search_engine_choice_dialog_service.cc b/chrome/browser/search_engine_choice/search_engine_choice_dialog_service.cc
index e92a03b..b71d2c9 100644
--- a/chrome/browser/search_engine_choice/search_engine_choice_dialog_service.cc
+++ b/chrome/browser/search_engine_choice/search_engine_choice_dialog_service.cc
@@ -195,11 +195,11 @@
 
     NOTREACHED(base::NotFatalUntil::M127);
   } else {
-    if (base::FeatureList::IsEnabled(
-            switches::kSearchEngineChoiceGuestExperience) &&
-        profile_->IsGuestSession() && save_guest_mode_selection) {
-      g_browser_process->local_state()->SetInt64(
-          prefs::kDefaultSearchProviderGuestModePrepopulatedId, prepopulate_id);
+    if (search_engine_choice_service_
+            ->IsProfileEligibleForDseGuestPropagation() &&
+        save_guest_mode_selection) {
+      search_engine_choice_service_->PropagateSearchEngineBetweenGuestSessions(
+          prepopulate_id);
     }
     template_url_service_->SetUserSelectedDefaultSearchProvider(
         selected_engine, search_engines::ChoiceMadeLocation::kChoiceScreen);
@@ -336,6 +336,13 @@
         kAlreadyCompleted;
   }
 
+  if (search_engine_choice_service_
+          ->IsProfileEligibleForDseGuestPropagation() &&
+      search_engine_choice_service_->ShouldPropagateDseBetweenGuestSessions()) {
+    return search_engines::SearchEngineChoiceScreenConditions::
+        kUsingPersistedGuestSessionChoice;
+  }
+
   if (web_app::AppBrowserController::IsWebApp(&browser)) {
     // Showing a Chrome-specific search engine dialog on top of a window
     // dedicated to a specific web app is a horrible UX, we suppress it for this
diff --git a/chrome/browser/search_engine_choice/search_engine_choice_dialog_service_unittest.cc b/chrome/browser/search_engine_choice/search_engine_choice_dialog_service_unittest.cc
index a6dbdd5..03eb1f2 100644
--- a/chrome/browser/search_engine_choice/search_engine_choice_dialog_service_unittest.cc
+++ b/chrome/browser/search_engine_choice/search_engine_choice_dialog_service_unittest.cc
@@ -15,8 +15,10 @@
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/dialog_test_browser_window.h"
 #include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
 #include "components/country_codes/country_codes.h"
 #include "components/search_engines/prepopulated_engines.h"
+#include "components/search_engines/search_engine_choice/search_engine_choice_service.h"
 #include "components/search_engines/search_engine_choice/search_engine_choice_utils.h"
 #include "components/search_engines/search_engine_utils.h"
 #include "components/search_engines/search_engines_pref_names.h"
@@ -346,10 +348,16 @@
   EXPECT_FALSE(g_browser_process->local_state()->HasPrefPath(
       prefs::kDefaultSearchProviderGuestModePrepopulatedId));
 
-  SearchEngineChoiceDialogService* search_engine_choice_dialog_service =
-      SearchEngineChoiceDialogServiceFactory::GetForProfile(profile());
-  profile()->SetGuestSession(/*guest=*/true);
+  TestingProfile* parent_guest = profile_manager()->CreateGuestProfile();
+  Profile* child_guest = parent_guest->GetOffTheRecordProfile(
+      Profile::OTRProfileID::PrimaryID(), false);
 
+  TemplateURLServiceFactory::GetInstance()->SetTestingFactory(
+      parent_guest,
+      base::BindRepeating(&TemplateURLServiceFactory::BuildInstanceFor));
+
+  SearchEngineChoiceDialogService* search_engine_choice_dialog_service =
+      SearchEngineChoiceDialogServiceFactory::GetForProfile(child_guest);
   const int kPrepopulatedId =
       search_engine_choice_dialog_service->GetSearchEngines()
           .at(0)
@@ -371,10 +379,16 @@
   EXPECT_FALSE(g_browser_process->local_state()->HasPrefPath(
       prefs::kDefaultSearchProviderGuestModePrepopulatedId));
 
-  SearchEngineChoiceDialogService* search_engine_choice_dialog_service =
-      SearchEngineChoiceDialogServiceFactory::GetForProfile(profile());
-  profile()->SetGuestSession(/*guest=*/true);
+  TestingProfile* parent_guest = profile_manager()->CreateGuestProfile();
+  Profile* child_guest = parent_guest->GetOffTheRecordProfile(
+      Profile::OTRProfileID::PrimaryID(), false);
 
+  TemplateURLServiceFactory::GetInstance()->SetTestingFactory(
+      parent_guest,
+      base::BindRepeating(&TemplateURLServiceFactory::BuildInstanceFor));
+
+  SearchEngineChoiceDialogService* search_engine_choice_dialog_service =
+      SearchEngineChoiceDialogServiceFactory::GetForProfile(child_guest);
   const int kPrepopulatedId =
       search_engine_choice_dialog_service->GetSearchEngines()
           .at(0)
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 13d95b17..e60c9bc3 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
@@ -6,10 +6,12 @@
 
 #include "base/check_deref.h"
 #include "base/check_is_test.h"
+#include "base/feature_list.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/search_engines/search_engine_choice/search_engine_choice_service.h"
+#include "components/search_engines/search_engines_switches.h"
 #include "components/variations/service/variations_service.h"
 
 namespace search_engines {
@@ -17,8 +19,19 @@
 std::unique_ptr<KeyedService> BuildSearchEngineChoiceService(
     content::BrowserContext* context) {
   Profile& profile = CHECK_DEREF(Profile::FromBrowserContext(context));
+
+#if !BUILDFLAG(IS_ANDROID)
+  const bool is_profile_elibile_for_dse_guest_propagation =
+      base::FeatureList::IsEnabled(
+          switches::kSearchEngineChoiceGuestExperience) &&
+      profile.IsGuestSession();
+#endif
+
   return std::make_unique<SearchEngineChoiceService>(
       CHECK_DEREF(profile.GetPrefs()), g_browser_process->local_state(),
+#if !BUILDFLAG(IS_ANDROID)
+      is_profile_elibile_for_dse_guest_propagation,
+#endif
       g_browser_process->variations_service());
 }
 }  // namespace
diff --git a/chrome/browser/search_engines/template_url_service_test_util.cc b/chrome/browser/search_engines/template_url_service_test_util.cc
index 7a31311d..52ca1470 100644
--- a/chrome/browser/search_engines/template_url_service_test_util.cc
+++ b/chrome/browser/search_engines/template_url_service_test_util.cc
@@ -167,7 +167,8 @@
 
   search_engine_choice_service_ =
       std::make_unique<search_engines::SearchEngineChoiceService>(
-          *profile_->GetPrefs(), local_state_);
+          *profile_->GetPrefs(), local_state_,
+          /*is_profile_eligible_for_dse_guest_propagation=*/false);
 
   ResetModel(false);
 }
diff --git a/chrome/browser/signin/chrome_signin_client.cc b/chrome/browser/signin/chrome_signin_client.cc
index 63e6d76..f8da7af 100644
--- a/chrome/browser/signin/chrome_signin_client.cc
+++ b/chrome/browser/signin/chrome_signin_client.cc
@@ -384,6 +384,7 @@
     signin::PrimaryAccountChangeEvent event_details) {
   for (signin::ConsentLevel consent_level :
        {signin::ConsentLevel::kSignin, signin::ConsentLevel::kSync}) {
+    // Only record metrics when setting the primary account.
     switch (event_details.GetEventTypeFor(consent_level)) {
       case signin::PrimaryAccountChangeEvent::Type::kNone:
       case signin::PrimaryAccountChangeEvent::Type::kCleared:
@@ -393,7 +394,6 @@
         signin_metrics::AccessPoint access_point =
             event_details.GetSetPrimaryAccountAccessPoint().value();
 
-        // Only record metrics when setting the primary account.
         std::optional<size_t> all_bookmarks_count = GetAllBookmarksCount();
         std::optional<size_t> bar_bookmarks_count =
             GetBookmarkBarBookmarksCount();
@@ -403,6 +403,7 @@
                                 all_bookmarks_count.value(),
                                 bar_bookmarks_count.value());
         }
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
         std::optional<size_t> extensions_count = GetExtensionsCount();
         if (extensions_count.has_value()) {
@@ -410,6 +411,8 @@
                                  extensions_count.value());
         }
 #endif
+
+        RecordOpenTabCount(access_point, consent_level);
     }
   }
 }
@@ -578,6 +581,26 @@
 }
 #endif
 
+void ChromeSigninClient::RecordOpenTabCount(
+    signin_metrics::AccessPoint access_point,
+    signin::ConsentLevel consent_level) {
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+  size_t tabs_count = 0;
+
+  for (Browser* browser : *BrowserList::GetInstance()) {
+    if (browser->profile() != profile_) {
+      continue;
+    }
+    if (TabStripModel* tab_strip_model = browser->tab_strip_model()) {
+      tabs_count += tab_strip_model->count();
+    }
+  }
+
+  signin_metrics::RecordOpenTabCountOnSignin(access_point, consent_level,
+                                             tabs_count);
+#endif
+}
+
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 // Returns the account that must be auto-signed-in to the Main Profile in
 // Lacros.
diff --git a/chrome/browser/signin/chrome_signin_client.h b/chrome/browser/signin/chrome_signin_client.h
index d9e7d09..77bbfd7 100644
--- a/chrome/browser/signin/chrome_signin_client.h
+++ b/chrome/browser/signin/chrome_signin_client.h
@@ -134,6 +134,9 @@
   virtual std::optional<size_t> GetExtensionsCount();
 #endif
 
+  void RecordOpenTabCount(signin_metrics::AccessPoint access_point,
+                          signin::ConsentLevel consent_level);
+
   const std::unique_ptr<WaitForNetworkCallbackHelper>
       wait_for_network_callback_helper_;
   raw_ptr<Profile, DanglingUntriaged> profile_;
diff --git a/chrome/browser/smart_card/smart_card_permission_context.cc b/chrome/browser/smart_card/smart_card_permission_context.cc
index fff767e..ce247443 100644
--- a/chrome/browser/smart_card/smart_card_permission_context.cc
+++ b/chrome/browser/smart_card/smart_card_permission_context.cc
@@ -4,10 +4,15 @@
 
 #include "chrome/browser/smart_card/smart_card_permission_context.h"
 
+#include <algorithm>
+#include <vector>
+
+#include "base/containers/to_vector.h"
 #include "base/power_monitor/power_monitor.h"
 #include "base/power_monitor/power_observer.h"
 #include "base/scoped_observation.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/permissions/one_time_permissions_tracker.h"
 #include "chrome/browser/permissions/one_time_permissions_tracker_factory.h"
@@ -19,6 +24,7 @@
 #include "components/permissions/permission_request_manager.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
+#include "url/origin.h"
 
 namespace {
 constexpr char kReaderNameKey[] = "reader-name";
@@ -296,6 +302,47 @@
   StopObserving();
 }
 
+void SmartCardPermissionContext::RevokeAllPermissions() {
+  for (auto& origin : GetOriginsWithGrants()) {
+    RevokeObjectPermissions(origin);
+  }
+  RevokeEphemeralPermissions();
+}
+
+void SmartCardPermissionContext::RevokePersistentPermission(
+    const std::string& reader_name,
+    const url::Origin& origin) {
+  RevokeObjectPermission(origin, ReaderNameToValue(reader_name));
+}
+
+SmartCardPermissionContext::ReaderGrants::ReaderGrants(
+    const std::string& reader_name,
+    const std::vector<url::Origin>& origins)
+    : reader_name(reader_name), origins(origins) {}
+SmartCardPermissionContext::ReaderGrants::~ReaderGrants() = default;
+SmartCardPermissionContext::ReaderGrants::ReaderGrants(
+    const ReaderGrants& other) = default;
+bool SmartCardPermissionContext::ReaderGrants::operator==(
+    const ReaderGrants& other) const = default;
+
+std::vector<SmartCardPermissionContext::ReaderGrants>
+SmartCardPermissionContext::GetPersistentReaderGrants() {
+  std::map<std::string, std::set<url::Origin>> reader_grants;
+  for (const auto& object : GetAllGrantedObjects()) {
+    const base::Value::Dict& reader_value = object->value;
+
+    CHECK(IsValidObject(reader_value));
+
+    reader_grants[*reader_value.FindString(kReaderNameKey)].insert(
+        url::Origin::Create(object->origin));
+  }
+
+  return base::ToVector(
+      reader_grants, [](const auto& reader_grants) -> ReaderGrants {
+        return {reader_grants.first, base::ToVector(reader_grants.second)};
+      });
+}
+
 void SmartCardPermissionContext::OnTrackingStarted(
     std::optional<std::vector<SmartCardReaderTracker::ReaderInfo>> info_list) {
   if (!info_list) {
diff --git a/chrome/browser/smart_card/smart_card_permission_context.h b/chrome/browser/smart_card/smart_card_permission_context.h
index de3fe39..635fcc74 100644
--- a/chrome/browser/smart_card/smart_card_permission_context.h
+++ b/chrome/browser/smart_card/smart_card_permission_context.h
@@ -7,6 +7,8 @@
 
 #include <map>
 #include <set>
+#include <string>
+#include <vector>
 
 #include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
@@ -46,6 +48,24 @@
   std::u16string GetObjectDisplayName(const base::Value::Dict& object) override;
 
   void RevokeEphemeralPermissions();
+  void RevokeAllPermissions();
+  void RevokePersistentPermission(const std::string& reader_name,
+                                  const url::Origin& origin);
+
+  struct ReaderGrants {
+    ReaderGrants(const std::string& reader_name,
+                 const std::vector<url::Origin>& origins);
+    ~ReaderGrants();
+
+    ReaderGrants(const ReaderGrants& other);
+    bool operator==(const ReaderGrants& other) const;
+
+    std::string reader_name;
+    std::vector<url::Origin> origins;
+  };
+
+  // Returns persistent grants, grouped by reader.
+  std::vector<ReaderGrants> GetPersistentReaderGrants();
 
  private:
   friend class SmartCardPermissionContextTest;
diff --git a/chrome/browser/smart_card/smart_card_permission_context_unittest.cc b/chrome/browser/smart_card/smart_card_permission_context_unittest.cc
index d92977c..31fe749 100644
--- a/chrome/browser/smart_card/smart_card_permission_context_unittest.cc
+++ b/chrome/browser/smart_card/smart_card_permission_context_unittest.cc
@@ -22,6 +22,7 @@
 namespace {
 
 constexpr char kDummyReader[] = "dummy reader";
+constexpr char kDummyReader2[] = "dummy reader 2";
 
 class FakeOneTimePermissionsTracker : public OneTimePermissionsTracker {
  public:
@@ -279,3 +280,92 @@
 
   permission_context.RevokeObjectPermissions(foo_origin);
 }
+
+TEST_F(SmartCardPermissionContextTest, RevokeAllPermissions) {
+  auto foo_origin = url::Origin::Create(GURL("https://foo.com/"));
+  auto bar_origin = url::Origin::Create(GURL("https://bar.com/"));
+  auto cthulhu_origin = url::Origin::Create(GURL("https://cthulhu.rlyeh/"));
+
+  SmartCardPermissionContext permission_context(&profile_);
+
+  GrantPersistentReaderPermission(permission_context, foo_origin, kDummyReader);
+  GrantEphemeralReaderPermission(permission_context, bar_origin, kDummyReader);
+  GrantPersistentReaderPermission(permission_context, cthulhu_origin,
+                                  kDummyReader2);
+
+  EXPECT_TRUE(
+      HasReaderPermission(permission_context, foo_origin, kDummyReader));
+  EXPECT_TRUE(
+      HasReaderPermission(permission_context, bar_origin, kDummyReader));
+  EXPECT_TRUE(
+      HasReaderPermission(permission_context, cthulhu_origin, kDummyReader2));
+
+  permission_context.RevokeAllPermissions();
+
+  // should reset permissions of all types
+  EXPECT_FALSE(
+      HasReaderPermission(permission_context, foo_origin, kDummyReader));
+  EXPECT_FALSE(
+      HasReaderPermission(permission_context, bar_origin, kDummyReader));
+  EXPECT_FALSE(
+      HasReaderPermission(permission_context, cthulhu_origin, kDummyReader2));
+}
+
+TEST_F(SmartCardPermissionContextTest, RevokePersistentPermission) {
+  auto foo_origin = url::Origin::Create(GURL("https://foo.com/"));
+  auto bar_origin = url::Origin::Create(GURL("https://bar.com/"));
+  auto cthulhu_origin = url::Origin::Create(GURL("https://cthulhu.rlyeh/"));
+
+  SmartCardPermissionContext permission_context(&profile_);
+
+  GrantPersistentReaderPermission(permission_context, foo_origin, kDummyReader);
+  GrantEphemeralReaderPermission(permission_context, bar_origin, kDummyReader);
+  GrantPersistentReaderPermission(permission_context, cthulhu_origin,
+                                  kDummyReader2);
+
+  EXPECT_TRUE(
+      HasReaderPermission(permission_context, foo_origin, kDummyReader));
+  EXPECT_TRUE(
+      HasReaderPermission(permission_context, bar_origin, kDummyReader));
+  EXPECT_TRUE(
+      HasReaderPermission(permission_context, cthulhu_origin, kDummyReader2));
+
+  permission_context.RevokePersistentPermission(kDummyReader, foo_origin);
+  permission_context.RevokePersistentPermission(kDummyReader, bar_origin);
+  permission_context.RevokePersistentPermission(kDummyReader2, cthulhu_origin);
+
+  // should reset permissions only of the persistent type
+  EXPECT_FALSE(
+      HasReaderPermission(permission_context, foo_origin, kDummyReader));
+  EXPECT_TRUE(
+      HasReaderPermission(permission_context, bar_origin, kDummyReader));
+  EXPECT_FALSE(
+      HasReaderPermission(permission_context, cthulhu_origin, kDummyReader2));
+
+  permission_context.RevokeAllPermissions();
+}
+
+TEST_F(SmartCardPermissionContextTest, GetPersistentReaderGrants) {
+  auto foo_origin = url::Origin::Create(GURL("https://foo.com/"));
+  auto bar_origin = url::Origin::Create(GURL("https://bar.com/"));
+  auto cthulhu_origin = url::Origin::Create(GURL("https://cthulhu.rlyeh/"));
+
+  SmartCardPermissionContext permission_context(&profile_);
+
+  GrantPersistentReaderPermission(permission_context, foo_origin, kDummyReader);
+  GrantEphemeralReaderPermission(permission_context, bar_origin, kDummyReader);
+  GrantPersistentReaderPermission(permission_context, cthulhu_origin,
+                                  kDummyReader2);
+
+  std::vector<SmartCardPermissionContext::ReaderGrants> grants =
+      permission_context.GetPersistentReaderGrants();
+
+  // should return only persistent grants
+  ASSERT_THAT(grants,
+              testing::ElementsAre(SmartCardPermissionContext::ReaderGrants(
+                                       kDummyReader, {foo_origin}),
+                                   SmartCardPermissionContext::ReaderGrants(
+                                       kDummyReader2, {cthulhu_origin})));
+
+  permission_context.RevokeAllPermissions();
+}
diff --git a/chrome/browser/supervised_user/classify_url_navigation_throttle.cc b/chrome/browser/supervised_user/classify_url_navigation_throttle.cc
index dcabec8..f658208 100644
--- a/chrome/browser/supervised_user/classify_url_navigation_throttle.cc
+++ b/chrome/browser/supervised_user/classify_url_navigation_throttle.cc
@@ -217,6 +217,24 @@
       break;
     }
     case SupervisedUserNavigationThrottle::kCancelWithInterstitial: {
+      CHECK(navigation_handle());
+// LINT.IfChange(cancel_with_interstitial)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+      if (ShouldShowReAuthInterstitial(*navigation_handle(), is_main_frame)) {
+        // Show the re-authentication interstitial if the user signed out of
+        // the content area, as parent's approval requires authentication.
+        // This interstitial is only available on Linux/Mac/Windows as
+        // ChromeOS and Android have different re-auth mechanisms.
+        CancelDeferredNavigation(
+            content::NavigationThrottle::ThrottleCheckResult(
+                CANCEL, net::ERR_BLOCKED_BY_CLIENT,
+                CreateReauthenticationInterstitial(
+                    *navigation_handle(),
+                    GetVerificationPurposeFromFilteringReason(result.reason))));
+        return;
+      }
+#endif
+      // LINT.ThenChange(//chrome/browser/supervised_user/supervised_user_navigation_throttle.cc:cancel_with_interstitial)
       Profile* profile = Profile::FromBrowserContext(
           navigation_handle()->GetWebContents()->GetBrowserContext());
       std::string interstitial_html =
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc b/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc
index 16dd8fc..a236392 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc
+++ b/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc
@@ -201,26 +201,6 @@
   }
 }
 
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-SupervisedUserVerificationPage::VerificationPurpose
-GetVerificationPurposeFromFilteringReason(
-    supervised_user::FilteringBehaviorReason reason) {
-  switch (reason) {
-    case supervised_user::FilteringBehaviorReason::DEFAULT:
-      return SupervisedUserVerificationPage::VerificationPurpose::
-          DEFAULT_BLOCKED_SITE;
-    case supervised_user::FilteringBehaviorReason::ASYNC_CHECKER:
-      return SupervisedUserVerificationPage::VerificationPurpose::
-          SAFE_SITES_BLOCKED_SITE;
-    case supervised_user::FilteringBehaviorReason::MANUAL:
-      return SupervisedUserVerificationPage::VerificationPurpose::
-          MANUAL_BLOCKED_SITE;
-    default:
-      NOTREACHED_NORETURN();
-  }
-}
-#endif
-
 void SupervisedUserNavigationThrottle::OnInterstitialResult(
     CallbackActions action,
     bool already_sent_request,
@@ -232,21 +212,10 @@
     }
     case kCancelWithInterstitial: {
       CHECK(navigation_handle());
-      Profile* profile = Profile::FromBrowserContext(
-          navigation_handle()->GetWebContents()->GetBrowserContext());
-
+// LINT.IfChange(cancel_with_interstitial)
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-      supervised_user::ChildAccountService* child_account_service =
-          ChildAccountServiceFactory::GetForProfile(profile);
-      if (base::FeatureList::IsEnabled(
-              supervised_user::
-                  kForceSupervisedUserReauthenticationForBlockedSites) &&
-          SupervisedUserVerificationPage::ShouldShowPage(
-              *child_account_service) &&
-          (is_main_frame ||
-           base::FeatureList::IsEnabled(
-               supervised_user::
-                   kAllowSupervisedUserReauthenticationForSubframes))) {
+      if (supervised_user::ShouldShowReAuthInterstitial(*navigation_handle(),
+                                                        is_main_frame)) {
         // Show the re-authentication interstitial if the user signed out of
         // the content area, as parent's approval requires authentication.
         // This interstitial is only available on Linux/Mac/Windows as
@@ -260,7 +229,9 @@
         return;
       }
 #endif
-
+      // LINT.ThenChange(//chrome/browser/supervised_user/classify_url_navigation_throttle.cc:cancel_with_interstitial)
+      Profile* profile = Profile::FromBrowserContext(
+          navigation_handle()->GetWebContents()->GetBrowserContext());
       std::string interstitial_html =
           supervised_user::SupervisedUserInterstitial::GetHTMLContents(
               SupervisedUserServiceFactory::GetForProfile(profile),
@@ -271,3 +242,41 @@
     }
   }
 }
+
+namespace supervised_user {
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+SupervisedUserVerificationPage::VerificationPurpose
+GetVerificationPurposeFromFilteringReason(FilteringBehaviorReason reason) {
+  switch (reason) {
+    case FilteringBehaviorReason::DEFAULT:
+      return SupervisedUserVerificationPage::VerificationPurpose::
+          DEFAULT_BLOCKED_SITE;
+    case FilteringBehaviorReason::ASYNC_CHECKER:
+      return SupervisedUserVerificationPage::VerificationPurpose::
+          SAFE_SITES_BLOCKED_SITE;
+    case FilteringBehaviorReason::MANUAL:
+      return SupervisedUserVerificationPage::VerificationPurpose::
+          MANUAL_BLOCKED_SITE;
+    default:
+      NOTREACHED_NORETURN();
+  }
+}
+
+bool ShouldShowReAuthInterstitial(content::NavigationHandle& navigation_handle,
+                                  bool is_main_frame) {
+  Profile* profile = Profile::FromBrowserContext(
+      navigation_handle.GetWebContents()->GetBrowserContext());
+  supervised_user::ChildAccountService* child_account_service =
+      ChildAccountServiceFactory::GetForProfile(profile);
+  return base::FeatureList::IsEnabled(
+             kForceSupervisedUserReauthenticationForBlockedSites) &&
+         SupervisedUserVerificationPage::ShouldShowPage(
+             *child_account_service) &&
+         (is_main_frame ||
+          base::FeatureList::IsEnabled(
+              supervised_user::
+                  kAllowSupervisedUserReauthenticationForSubframes));
+}
+
+#endif
+}  // namespace supervised_user
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_throttle.h b/chrome/browser/supervised_user/supervised_user_navigation_throttle.h
index 5f62968e..628d8270 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_throttle.h
+++ b/chrome/browser/supervised_user/supervised_user_navigation_throttle.h
@@ -11,14 +11,26 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/elapsed_timer.h"
+#include "chrome/browser/supervised_user/supervised_user_verification_page.h"
 #include "components/supervised_user/core/browser/supervised_user_error_page.h"
 #include "components/supervised_user/core/browser/supervised_user_url_filter.h"
 #include "components/supervised_user/core/browser/supervised_user_utils.h"
 #include "components/supervised_user/core/common/supervised_users.h"
+#include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/navigation_throttle.h"
 
 class Profile;
 
+namespace supervised_user {
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+SupervisedUserVerificationPage::VerificationPurpose
+GetVerificationPurposeFromFilteringReason(FilteringBehaviorReason reason);
+
+bool ShouldShowReAuthInterstitial(content::NavigationHandle& navigation_handle,
+                                  bool is_main_frame);
+#endif
+}  // namespace supervised_user
+
 class SupervisedUserNavigationThrottle : public content::NavigationThrottle {
  public:
   enum CallbackActions { kCancelNavigation = 0, kCancelWithInterstitial };
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc b/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc
index 837d0b8..50467d0f 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc
+++ b/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc
@@ -63,12 +63,6 @@
 #include "ui/events/test/event_generator.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if (BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN))
-#include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h"
-#include "chrome/test/supervised_user/google_auth_state_waiter_mixin.h"
-#include "components/supervised_user/core/browser/child_account_service.h"
-#endif  // BUILDFLAG(IS_WIN)
-
 namespace {
 
 using content::NavigationController;
@@ -824,13 +818,6 @@
 
 IN_PROC_BROWSER_TEST_P(SupervisedUserIframeFilterTest,
                        TestBackButtonMainFrame) {
-#if (BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN))
-  // TODO(crbug.com/368578425): handle this in SupervisionMixin.
-  supervised_user::GoogleAuthStateWaiterMixin::WaitForGoogleAuthState(
-      ChildAccountServiceFactory::GetForProfile(browser()->profile()),
-      supervised_user::ChildAccountService::AuthState::AUTHENTICATED);
-#endif
-
   BlockHost(kExampleHost);
 
   GURL allowed_url_with_iframes = embedded_test_server()->GetURL(
@@ -875,13 +862,6 @@
 IN_PROC_BROWSER_TEST_P(
     SupervisedUserIframeFilterTest,
     MAYBE_BlockedMainFrameFromClassifyUrlForUnstripedHostIsStrippedInRemoteApproval) {
-#if (BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN))
-  // TODO(crbug.com/368578425): handle this in SupervisionMixin.
-  supervised_user::GoogleAuthStateWaiterMixin::WaitForGoogleAuthState(
-      ChildAccountServiceFactory::GetForProfile(browser()->profile()),
-      supervised_user::ChildAccountService::AuthState::AUTHENTICATED);
-#endif
-
   // Classify url blocks the navigation to the target url.
   // No matching blocklist entry exists for the host of the target url.
   kids_management_api_mock().RestrictSubsequentClassifyUrl();
@@ -915,13 +895,6 @@
 IN_PROC_BROWSER_TEST_P(
     SupervisedUserIframeFilterTest,
     BlockedMainFrameFromBlockListIsStrippedInRemoteApproval) {
-#if (BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN))
-  // TODO(crbug.com/368578425): handle this in SupervisionMixin.
-  supervised_user::GoogleAuthStateWaiterMixin::WaitForGoogleAuthState(
-      ChildAccountServiceFactory::GetForProfile(browser()->profile()),
-      supervised_user::ChildAccountService::AuthState::AUTHENTICATED);
-#endif
-
   // Manual parental blocklist entry blocks the navigation to the target url.
   BlockHost("*.example.*");
   kids_management_api_mock().AllowSubsequentClassifyUrl();
@@ -954,13 +927,6 @@
 IN_PROC_BROWSER_TEST_P(
     SupervisedUserIframeFilterTest,
     BlockedMainFrameFromBlockListForUnstripedHostSkipsStrippingInRemoteApproval) {
-#if (BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN))
-  // TODO(crbug.com/368578425): handle this in SupervisionMixin.
-  supervised_user::GoogleAuthStateWaiterMixin::WaitForGoogleAuthState(
-      ChildAccountServiceFactory::GetForProfile(browser()->profile()),
-      supervised_user::ChildAccountService::AuthState::AUTHENTICATED);
-#endif
-
   // Manual parental blocklist entry for the unstriped url blocks the
   // navigation to the target url.
   BlockHost(kExampleHost);
@@ -1010,13 +976,6 @@
 
 IN_PROC_BROWSER_TEST_P(SupervisedUserIframeFilterTest,
                        RememberAlreadyRequestedHosts) {
-#if (BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN))
-  // TODO(crbug.com/368578425): handle this in SupervisionMixin.
-  supervised_user::GoogleAuthStateWaiterMixin::WaitForGoogleAuthState(
-      ChildAccountServiceFactory::GetForProfile(browser()->profile()),
-      supervised_user::ChildAccountService::AuthState::AUTHENTICATED);
-#endif
-
   BlockHost(kExampleHost);
 
   GURL blocked_url = embedded_test_server()->GetURL(
diff --git a/chrome/browser/supervised_user/supervised_user_pending_state_navigation_browsertest.cc b/chrome/browser/supervised_user/supervised_user_pending_state_navigation_browsertest.cc
index 5b1d894..b180c12af 100644
--- a/chrome/browser/supervised_user/supervised_user_pending_state_navigation_browsertest.cc
+++ b/chrome/browser/supervised_user/supervised_user_pending_state_navigation_browsertest.cc
@@ -44,12 +44,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
-#if (BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN))
-#include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h"
-#include "chrome/test/supervised_user/google_auth_state_waiter_mixin.h"
-#include "components/supervised_user/core/browser/child_account_service.h"
-#endif
-
 namespace {
 
 static constexpr std::string_view kUmaReauthenticationHistogramName =
@@ -106,14 +100,6 @@
     supervision_mixin_.SignIn(
         supervised_user::SupervisionMixin::SignInMode::kSupervised);
 
-#if (BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN))
-    // TODO(crbug.com/368578425): handle this in SupervisionMixin.
-    supervised_user::GoogleAuthStateWaiterMixin::WaitForGoogleAuthState(
-        ChildAccountServiceFactory::GetForProfile(browser()->profile()),
-        supervised_user::SupervisionMixin::GetExpectedAuthState(
-            supervised_user::SupervisionMixin::SignInMode::kSupervised));
-#endif
-
     ASSERT_FALSE(
         identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState(
             identity_manager()->GetPrimaryAccountId(
diff --git a/chrome/browser/supervised_user/supervised_user_regional_url_filter_browsertest.cc b/chrome/browser/supervised_user/supervised_user_regional_url_filter_browsertest.cc
index cdc6762..bf17b93d 100644
--- a/chrome/browser/supervised_user/supervised_user_regional_url_filter_browsertest.cc
+++ b/chrome/browser/supervised_user/supervised_user_regional_url_filter_browsertest.cc
@@ -47,17 +47,6 @@
 using ::testing::NiceMock;
 using ::testing::Pointee;
 
-// Surprisingly, we don't have proto-comparators from gtest available. Remove
-// once they're available.
-MATCHER_P(EqualsProto,
-          message,
-          "Match a proto Message equal to the matcher's argument.") {
-  std::string expected_serialized, actual_serialized;
-  message.SerializeToString(&expected_serialized);
-  arg.SerializeToString(&actual_serialized);
-  return expected_serialized == actual_serialized;
-}
-
 // Wrapper class; introducing fluent aliases for test parameters.
 class TestCase {
  public:
diff --git a/chrome/browser/supervised_user/supervised_user_service_browsertest.cc b/chrome/browser/supervised_user/supervised_user_service_browsertest.cc
index 2b15d6b..eae19768 100644
--- a/chrome/browser/supervised_user/supervised_user_service_browsertest.cc
+++ b/chrome/browser/supervised_user/supervised_user_service_browsertest.cc
@@ -24,12 +24,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if (BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN))
-#include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h"
-#include "chrome/test/supervised_user/google_auth_state_waiter_mixin.h"
-#include "components/supervised_user/core/browser/child_account_service.h"
-#endif
-
 namespace {
 
 class SupervisedUserServiceBrowserTest
@@ -55,13 +49,6 @@
   Profile* profile = browser()->profile();
   PrefService* prefs = profile->GetPrefs();
 
-#if (BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN))
-  // TODO(crbug.com/368578425): handle this in SupervisionMixin.
-  supervised_user::GoogleAuthStateWaiterMixin::WaitForGoogleAuthState(
-      ChildAccountServiceFactory::GetForProfile(profile),
-      supervised_user::SupervisionMixin::GetExpectedAuthState(GetSignInMode()));
-#endif
-
   if (GetSignInMode() ==
       supervised_user::SupervisionMixin::SignInMode::kSupervised) {
     EXPECT_FALSE(
diff --git a/chrome/browser/sync/sync_ui_util.cc b/chrome/browser/sync/sync_ui_util.cc
index be9be9b..0cf34c8 100644
--- a/chrome/browser/sync/sync_ui_util.cc
+++ b/chrome/browser/sync/sync_ui_util.cc
@@ -255,10 +255,15 @@
     // because the setup incomplete case is treated separately below. See the
     // comment in ShouldRequestSyncConfirmation() about dashboard resets.
 
-    if (switches::IsImprovedSigninUIOnDesktopEnabled() &&
-        service->GetUserSettings()
-            ->IsPassphraseRequiredForPreferredDataTypes()) {
-      return AvatarSyncErrorType::kPassphraseError;
+    if (switches::IsImprovedSigninUIOnDesktopEnabled()) {
+      if (service->RequiresClientUpgrade()) {
+        return AvatarSyncErrorType::kUpgradeClientError;
+      }
+
+      if (service->GetUserSettings()
+              ->IsPassphraseRequiredForPreferredDataTypes()) {
+        return AvatarSyncErrorType::kPassphraseError;
+      }
     }
 
     return GetTrustedVaultError(service);
@@ -326,12 +331,20 @@
             is_sync_feature_enabled
                 ? IDS_SYNC_STATUS_NEEDS_PASSWORD
                 : IDS_SYNC_ERROR_PASSPHRASE_USER_MENU_TITLE_SIGNED_IN_ONLY);
+      } else {
+        return l10n_util::GetStringUTF16(IDS_SYNC_ERROR_USER_MENU_TITLE);
       }
-      [[fallthrough]];
+    case AvatarSyncErrorType::kUpgradeClientError:
+      if (switches::IsImprovedSigninUIOnDesktopEnabled() &&
+          !is_sync_feature_enabled) {
+        return l10n_util::GetStringUTF16(
+            IDS_SYNC_ERROR_UPGRADE_CLIENT_USER_MENU_TITLE);
+      } else {
+        return l10n_util::GetStringUTF16(IDS_SYNC_ERROR_USER_MENU_TITLE);
+      }
     case AvatarSyncErrorType::kSettingsUnconfirmedError:
     case AvatarSyncErrorType::kManagedUserUnrecoverableError:
     case AvatarSyncErrorType::kUnrecoverableError:
-    case AvatarSyncErrorType::kUpgradeClientError:
     case AvatarSyncErrorType::kTrustedVaultKeyMissingForEverythingError:
       return l10n_util::GetStringUTF16(IDS_SYNC_ERROR_USER_MENU_TITLE);
   }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index c1a782d..08fdd5b 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1216,8 +1216,6 @@
       "startup/startup_browser_creator.h",
       "startup/startup_browser_creator_impl.cc",
       "startup/startup_browser_creator_impl.h",
-      "startup/startup_tab.cc",
-      "startup/startup_tab.h",
       "startup/startup_tab_provider.cc",
       "startup/startup_tab_provider.h",
       "startup/startup_types.h",
@@ -1388,8 +1386,6 @@
       "toolbar/back_forward_menu_model.h",
       "toolbar/bookmark_sub_menu_model.cc",
       "toolbar/bookmark_sub_menu_model.h",
-      "toolbar/cast/cast_contextual_menu.cc",
-      "toolbar/cast/cast_toolbar_button_controller.cc",
       "toolbar/pinned_toolbar/pinned_toolbar_actions_model.cc",
       "toolbar/pinned_toolbar/pinned_toolbar_actions_model.h",
       "toolbar/pinned_toolbar/pinned_toolbar_actions_model_factory.cc",
@@ -1801,6 +1797,7 @@
       "//chrome/browser/safe_browsing:advanced_protection",
       "//chrome/browser/screen_ai:screen_ai_install_state",
       "//chrome/browser/screen_ai:screen_ai_service_router_factory",
+      "//chrome/browser/smart_card:smart_card",
       "//chrome/browser/tab_group_sync:utils",
       "//chrome/browser/task_manager:impl",
       "//chrome/browser/themes",
@@ -1824,6 +1821,7 @@
       "//chrome/browser/ui/permission_bubble",
       "//chrome/browser/ui/signin",
       "//chrome/browser/ui/signin:impl",
+      "//chrome/browser/ui/startup:startup_tab",
       "//chrome/browser/ui/tabs",
       "//chrome/browser/ui/tabs:tab_strip_model_observer",
       "//chrome/browser/ui/tabs:tab_strip_model_observer_impl",
@@ -1831,6 +1829,7 @@
       "//chrome/browser/ui/toasts:toasts",
       "//chrome/browser/ui/toasts/api:toasts",
       "//chrome/browser/ui/toolbar/cast",
+      "//chrome/browser/ui/toolbar/cast:impl",
       "//chrome/browser/ui/user_education",
       "//chrome/browser/ui/user_education:impl",
       "//chrome/browser/ui/views/side_panel:side_panel_enums",
@@ -1995,6 +1994,7 @@
       "//chrome/browser/ui/window_sizer:impl",
       "//chrome/browser/ui/webui/settings:impl",
       "//chrome/browser/ui/global_media_controls:impl",
+      "//chrome/browser/ui/toolbar/cast:impl",
     ]
 
     if (is_mac) {
@@ -5572,6 +5572,8 @@
       "web_applications/diagnostics/app_type_initialized_event.h",
       "web_applications/diagnostics/web_app_icon_health_checks.cc",
       "web_applications/diagnostics/web_app_icon_health_checks.h",
+      "web_applications/navigation_capturing_redirection_throttle.cc",
+      "web_applications/navigation_capturing_redirection_throttle.h",
       "web_applications/share_target_utils.cc",
       "web_applications/share_target_utils.h",
       "web_applications/sub_apps_install_dialog_controller.cc",
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SignOutCoordinator.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SignOutCoordinator.java
index a00460a..89f9700 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SignOutCoordinator.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SignOutCoordinator.java
@@ -66,6 +66,53 @@
             @SignoutReason int signOutReason,
             boolean showConfirmDialog,
             Runnable onSignOut) {
+        startSignOutFlow(
+                context,
+                profile,
+                fragmentManager,
+                dialogManager,
+                snackbarManager,
+                signOutReason,
+                showConfirmDialog,
+                onSignOut,
+                false);
+    }
+
+    // TODO: crbug.com/343933167 - The @param suppressSnackbar removed upon being able to show the
+    // settings
+    // signout snackbar from here, which means after fixing b/343933167.
+    /**
+     * Starts the sign-out flow. The caller must verify existence of a signed-in account and whether
+     * sign-out is allowed before calling. Child users may only call this method if there is an
+     * account with {@link ConsentLevel#SYNC}. It can show three different UIs depending on user
+     * state:
+     * <li>A snackbar indicating user has signed-out.
+     * <li>A confirmation dialog indicating user has unsaved data.
+     * <li>A confirmation dialog indicating that user may be signed-out as a side-effect of some
+     *     action (e.g., toggling 'Allow Chrome sign-in').
+     *
+     * @param context Context to create the view.
+     * @param profile The Profile to sign out of.
+     * @param fragmentManager FragmentManager used by {@link SignOutDialogCoordinator}.
+     * @param dialogManager A ModalDialogManager that manages the dialog.
+     * @param signOutReason The access point to sign out from.
+     * @param showConfirmDialog Whether a confirm dialog should be shown before sign-out.
+     * @param onSignOut A {@link Runnable} to run when the user presses the confirm button. Will be
+     *     called on the UI thread when the sign-out flow finishes. If sign-out fails it will not be
+     *     called.
+     * @param suppressSnackbar Indicates whether the snackbar should be suppressed.
+     */
+    @MainThread
+    public static void startSignOutFlow(
+            Context context,
+            Profile profile,
+            FragmentManager fragmentManager,
+            ModalDialogManager dialogManager,
+            SnackbarManager snackbarManager,
+            @SignoutReason int signOutReason,
+            boolean showConfirmDialog,
+            Runnable onSignOut,
+            boolean suppressSnackbar) {
         ThreadUtils.assertOnUiThread();
         assert snackbarManager != null;
         assert onSignOut != null;
@@ -88,7 +135,8 @@
                                 signinManager,
                                 syncService,
                                 signOutReason,
-                                onSignOut);
+                                onSignOut,
+                                suppressSnackbar);
                         case UiState.UNSAVED_DATA -> showUnsavedDataDialog(
                                 context, dialogManager, signinManager, signOutReason, onSignOut);
                         case UiState.SHOW_CONFIRM_DIALOG -> showConfirmDialog(
@@ -286,7 +334,8 @@
             SigninManager signinManager,
             SyncService syncService,
             @SignoutReason int signOutReason,
-            Runnable onSignOut) {
+            Runnable onSignOut,
+            boolean supressSnackbar) {
         signOut(
                 signinManager,
                 signOutReason,
@@ -294,7 +343,9 @@
                     PostTask.runOrPostTask(
                             TaskTraits.UI_DEFAULT,
                             () -> {
-                                showSnackbar(context, snackbarManager, syncService);
+                                if (!supressSnackbar) {
+                                    showSnackbar(context, snackbarManager, syncService);
+                                }
                                 onSignOut.run();
                             });
                 });
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 3f8db54f..40d7c6a 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -6825,7 +6825,21 @@
         You can use Chrome any time you tap links in messages, documents and other apps
       </message>
       <message name="IDS_DEFAULT_BROWSER_PROMO_OPEN_SETTINGS_LABEL" desc="Text that displayed on button that allows the user to open Android settings to set a default browser.">
-        Open Settings
+          Open Settings
+      </message>
+
+       <!-- Mandatory biometric auth errors -->
+      <message name="IDS_IDENTITY_CHECK_LOCKOUT_ERROR_TITLE" desc="Title for the error dialog shown to the user when Identity Check cannot authenticate them because the maximum number of attempts to authenticate with biometrics has been exceeded and biometrics have been locked out.">
+        Identity Check is on and can’t verify it’s you
+      </message>
+      <message name="IDS_IDENTITY_CHECK_LOCKOUT_ERROR_DESCRIPTION" desc="Description for the error dialog shown to the user when the maximum number of attempts to authenticate with biometrics has been exceeded and biometrics have been locked out, but Identity Check is on so verification is mandatory.">
+        Biometrics failed too many times. Lock and unlock your device to retry.
+      </message>
+      <message name="IDS_IDENTITY_CHECK_LOCKOUT_ERROR_MORE_DETAILS" desc="Second paragraph of the description for the lockout error, explaining to the user how they can manage Identity Check.">
+        You can manage Identity Check in theft protection settings. <ph name="BEGIN_LINK">&lt;link&gt;</ph>Go to settings<ph name="END_LINK">&lt;/link&gt;</ph>
+      </message>
+      <message name="IDS_LOCK_SCREEN" desc="Title for the button that the user can tap to lock the device screen. This is the only way to exit a biometric lockout status.">
+        Lock screen
       </message>
     </messages>
   </release>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_IDENTITY_CHECK_LOCKOUT_ERROR_DESCRIPTION.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_IDENTITY_CHECK_LOCKOUT_ERROR_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..61d098a
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_IDENTITY_CHECK_LOCKOUT_ERROR_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+b706f3e1039f5581d4e65081552ee95da3c3e2f8
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_IDENTITY_CHECK_LOCKOUT_ERROR_MORE_DETAILS.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_IDENTITY_CHECK_LOCKOUT_ERROR_MORE_DETAILS.png.sha1
new file mode 100644
index 0000000..61d098a
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_IDENTITY_CHECK_LOCKOUT_ERROR_MORE_DETAILS.png.sha1
@@ -0,0 +1 @@
+b706f3e1039f5581d4e65081552ee95da3c3e2f8
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_IDENTITY_CHECK_LOCKOUT_ERROR_TITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_IDENTITY_CHECK_LOCKOUT_ERROR_TITLE.png.sha1
new file mode 100644
index 0000000..61d098a
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_IDENTITY_CHECK_LOCKOUT_ERROR_TITLE.png.sha1
@@ -0,0 +1 @@
+b706f3e1039f5581d4e65081552ee95da3c3e2f8
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LOCK_SCREEN.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LOCK_SCREEN.png.sha1
new file mode 100644
index 0000000..61d098a
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LOCK_SCREEN.png.sha1
@@ -0,0 +1 @@
+b706f3e1039f5581d4e65081552ee95da3c3e2f8
\ No newline at end of file
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index 4ba6fe2..baa70e6 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -41,6 +41,7 @@
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/browser/ui/web_applications/web_app_tabbed_utils.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
+#include "chrome/browser/web_applications/web_app_tab_helper.h"
 #include "chrome/common/url_constants.h"
 #include "components/captive_portal/core/buildflags.h"
 #include "components/constrained_window/constrained_window_views.h"
@@ -989,10 +990,18 @@
 // At this point, the `params->navigated_or_inserted_contents` is guaranteed to
 // be non null, so perform tasks if the navigation has been captured by a web
 // app, like enqueueing launch params.
+// The `params->browser` might change as a result of a navigation, so obtain the
+// `source_app_id` from the source browser, which retains a reference.
 #if !BUILDFLAG(IS_ANDROID)
+  // TODO(crbug.com/336371044): Accessing the app_id from the WebAppTabHelper
+  // does not work here. Investigate why to support apps that open in a new tab.
+  std::optional<webapps::AppId> source_app_id;
+  if (source_browser && source_browser->app_controller()) {
+    source_app_id = source_browser->app_controller()->app_id();
+  }
   web_app::OnWebAppNavigationAfterWebContentsCreation(
       std::move(app_navigation_result), *params, navigation_handle,
-      original_disposition);
+      original_disposition, source_app_id);
 #endif  // !BUILDFLAG(IS_ANDROID)
   return navigation_handle;
 }
diff --git a/chrome/browser/ui/startup/BUILD.gn b/chrome/browser/ui/startup/BUILD.gn
index 96925c3..61a544f1 100644
--- a/chrome/browser/ui/startup/BUILD.gn
+++ b/chrome/browser/ui/startup/BUILD.gn
@@ -13,3 +13,13 @@
   header = "buildflags.h"
   flags = [ "CAN_TEST_GCPW_SIGNIN_STARTUP=$can_test_gcpw_signin_startup" ]
 }
+
+# TODO(crbug.com/369883710): modularize all the rest.
+source_set("startup_tab") {
+  sources = [
+    "startup_tab.cc",
+    "startup_tab.h",
+  ]
+
+  public_deps = [ "//url" ]
+}
diff --git a/chrome/browser/ui/toolbar/cast/BUILD.gn b/chrome/browser/ui/toolbar/cast/BUILD.gn
index f2c3105..23cbc9b 100644
--- a/chrome/browser/ui/toolbar/cast/BUILD.gn
+++ b/chrome/browser/ui/toolbar/cast/BUILD.gn
@@ -20,3 +20,59 @@
     "//ui/base",
   ]
 }
+
+source_set("impl") {
+  sources = [
+    "cast_contextual_menu.cc",
+    "cast_toolbar_button_controller.cc",
+  ]
+  deps = [
+    ":cast",
+    "//base",
+    "//chrome/app:branded_strings",
+    "//chrome/app:command_ids",
+    "//chrome/app:generated_resources",
+    "//chrome/browser/media/router:media_router_feature",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui:ui_features",
+    "//chrome/browser/ui/views/toolbar",
+    "//chrome/common",
+    "//chrome/common:constants",
+    "//components/media_router/common",
+    "//components/signin/public/identity_manager",
+    "//components/strings:components_strings",
+    "//components/vector_icons",
+    "//content/public/browser",
+    "//extensions/common:common_constants",
+    "//ui/actions:actions_headers",
+    "//ui/gfx",
+    "//ui/gfx:color_utils",
+  ]
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "cast_contextual_menu_unittest.cc",
+    "cast_toolbar_button_controller_unittest.cc",
+    "mock_cast_toolbar_button_controller.cc",
+    "mock_cast_toolbar_button_controller.h",
+  ]
+  deps = [
+    ":cast",
+    "//base/test:test_support",
+    "//chrome/app:branded_strings",
+    "//chrome/app:command_ids",
+    "//chrome/app:generated_resources",
+    "//chrome/browser/extensions:test_support",
+    "//chrome/browser/media/router",
+    "//chrome/browser/media/router:media_router_feature",
+    "//chrome/browser/ui/media_router",
+    "//chrome/common:constants",
+    "//chrome/test:test_support",
+    "//components/media_router/browser:test_support",
+    "//components/media_router/common",
+    "//components/signin/public/identity_manager",
+  ]
+}
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 ec45ae4..9082c78 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
@@ -412,6 +412,14 @@
     GetTestSyncService()->FireStateChanged();
   }
 
+  void SimulateUpgradeClientError() {
+    syncer::SyncStatus sync_status;
+    sync_status.sync_protocol_error.action = syncer::UPGRADE_CLIENT;
+    GetTestSyncService()->SetDetailedSyncStatus(true, sync_status);
+    GetTestSyncService()->FireStateChanged();
+    ASSERT_TRUE(GetTestSyncService()->RequiresClientUpgrade());
+  }
+
  private:
   void SetTestingFactories(content::BrowserContext* context) {
     SyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
@@ -1622,4 +1630,14 @@
                                    IDS_SYNC_ERROR_USER_MENU_PASSPHRASE_BUTTON));
 }
 
+IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonWithImprovedSigninUIBrowserTest,
+                       UpgradeClientError) {
+  AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser());
+  EnableSyncWithImageAndClearGreeting(avatar, u"test@gmail.com");
+  ASSERT_EQ(avatar->GetText(), std::u16string());
+  SimulateUpgradeClientError();
+  EXPECT_EQ(avatar->GetText(),
+            l10n_util::GetStringUTF16(IDS_SYNC_ERROR_USER_MENU_UPGRADE_BUTTON));
+}
+
 #endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
index 4efe7cc..a7a98f4d 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
@@ -140,6 +140,7 @@
   kShowIdentityName,
   kSigninPending,
   kSyncPaused,
+  kUpgradeClientError,
   kPassphraseError,
   // Catch-all for remaining errors in sync-the-feature or sync-the-transport.
   kSyncError,
@@ -984,6 +985,10 @@
               /*state_observer=*/*this, *profile, avatar_toolbar_button_.get());
 
       if (switches::IsImprovedSigninUIOnDesktopEnabled()) {
+        states_[ButtonState::kUpgradeClientError] =
+            std::make_unique<SyncErrorStateProvider>(
+                /*state_observer=*/*this, *profile,
+                AvatarSyncErrorType::kUpgradeClientError);
         states_[ButtonState::kPassphraseError] =
             std::make_unique<SyncErrorStateProvider>(
                 /*state_observer=*/*this, *profile,
@@ -1357,6 +1362,10 @@
       color = color_provider->GetColor(kColorAvatarButtonHighlightSyncPaused);
       text = l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SYNC_PAUSED);
       break;
+    case ButtonState::kUpgradeClientError:
+      color = color_provider->GetColor(kColorAvatarButtonHighlightSyncPaused);
+      text = l10n_util::GetStringUTF16(IDS_SYNC_ERROR_USER_MENU_UPGRADE_BUTTON);
+      break;
     case ButtonState::kPassphraseError:
       color = color_provider->GetColor(kColorAvatarButtonHighlightSyncPaused);
       text =
@@ -1438,6 +1447,7 @@
     case ButtonState::kShowIdentityName:
     case ButtonState::kIncognitoProfile:
     case ButtonState::kManagement:
+    case ButtonState::kUpgradeClientError:
     case ButtonState::kPassphraseError:
     case ButtonState::kSyncError:
     case ButtonState::kSyncPaused:
@@ -1482,6 +1492,7 @@
           kColorAvatarButtonHighlightSyncErrorForeground);
     case ButtonState::kManagement:
     case ButtonState::kSigninPending:
+    case ButtonState::kUpgradeClientError:
     case ButtonState::kPassphraseError:
       return color_provider->GetColor(
           kColorAvatarButtonHighlightNormalForeground);
@@ -1502,6 +1513,7 @@
       return l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_GUEST_TOOLTIP);
     case ButtonState::kShowIdentityName:
       return GetShortProfileName();
+    case ButtonState::kUpgradeClientError:
     case ButtonState::kPassphraseError:
     case ButtonState::kSyncPaused:
     case ButtonState::kSyncError: {
@@ -1546,6 +1558,7 @@
       case ButtonState::kManagement:
       case ButtonState::kSigninPending:
       case ButtonState::kSyncPaused:
+      case ButtonState::kUpgradeClientError:
       case ButtonState::kPassphraseError:
         ripple_color_id = kColorAvatarButtonNormalRipple;
         break;
@@ -1579,6 +1592,7 @@
           GetProfileAvatarImage(icon_size), icon_size, icon_size,
           profiles::SHAPE_CIRCLE));
     case ButtonState::kPassphraseError:
+    case ButtonState::kUpgradeClientError:
     case ButtonState::kSigninPending:
       // First shrink the icon from it's regular size in order to accommodate
       // for the dotted circle that is drawn around it in `PaintIcon()`.
@@ -1607,6 +1621,7 @@
     case ButtonState::kExplicitTextShowing:
     case ButtonState::kManagement:
     case ButtonState::kSigninPending:
+    case ButtonState::kUpgradeClientError:
     case ButtonState::kPassphraseError:
     case ButtonState::kSyncPaused:
     case ButtonState::kSyncError:
@@ -1686,6 +1701,7 @@
     case ButtonState::kSyncError:
       return;
     case ButtonState::kSigninPending:
+    case ButtonState::kUpgradeClientError:
     case ButtonState::kPassphraseError:
       // Paints the dotted circle around the shrunk icon (from
       // `GetAvatarIcon()`).
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
index c9f11a75..ea6e921 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -632,8 +632,6 @@
 
   // The |edit_button| is on the right and has fixed width.
   if (edit_button) {
-    edit_button->SetProperty(views::kBoxLayoutFlexKey,
-                             views::BoxLayoutFlexSpecification());
     views::View* edit_button_container =
         profile_background_container_->AddChildView(
             std::make_unique<views::View>());
@@ -642,8 +640,6 @@
         views::BoxLayout::CrossAxisAlignment::kCenter,
         gfx::Insets::TLBR(0, 0, kHalfOfAvatarImageViewSize + kDefaultMargin,
                           0)));
-    // TODO(crbug.com/40232718): See View::SetLayoutManagerUseConstrainedSpace.
-    edit_button_container->SetLayoutManagerUseConstrainedSpace(false);
     edit_button_container->AddChildView(std::move(edit_button));
   }
 }
@@ -690,7 +686,8 @@
     heading_label->SetHorizontalAlignment(gfx::ALIGN_CENTER);
     heading_label->SetProperty(
         views::kFlexBehaviorKey,
-        views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
+        views::FlexSpecification(views::LayoutOrientation::kHorizontal,
+                                 views::MinimumFlexSizeRule::kScaleToZero,
                                  views::MaximumFlexSizeRule::kUnbounded));
     if (avatar_header_art.empty()) {
       heading_label->SetAutoColorReadabilityEnabled(false);
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
index 7a41019..7420a728 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
@@ -2526,8 +2526,16 @@
   ExpectLastCommittedUrl(app_url());
 }
 
+// TODO(https://crbug.com/361839153): This test fails on ChromeOS builds.
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_WebAppOriginTextAccessibleProperties \
+  DISABLED_WebAppOriginTextAccessibleProperties
+#else
+#define MAYBE_WebAppOriginTextAccessibleProperties \
+  WebAppOriginTextAccessibleProperties
+#endif
 IN_PROC_BROWSER_TEST_P(WebAppFrameToolbarBrowserTest_OriginText,
-                       WebAppOriginTextAccessibleProperties) {
+                       MAYBE_WebAppOriginTextAccessibleProperties) {
   InstallAndLaunchWebApp();
   auto* origin_text = helper()->origin_text_view();
   ui::AXNodeData data;
diff --git a/chrome/browser/ui/web_applications/navigation_capturing_redirection_throttle.cc b/chrome/browser/ui/web_applications/navigation_capturing_redirection_throttle.cc
new file mode 100644
index 0000000..cbf6b9ed
--- /dev/null
+++ b/chrome/browser/ui/web_applications/navigation_capturing_redirection_throttle.cc
@@ -0,0 +1,51 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/web_applications/navigation_capturing_redirection_throttle.h"
+
+#include "chrome/browser/web_applications/navigation_capturing_information_forwarder.h"
+#include "chrome/browser/web_applications/navigation_capturing_navigation_handle_user_data.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_handle.h"
+#include "ui/base/window_open_disposition.h"
+
+namespace web_app {
+
+namespace {
+
+using ThrottleCheckResult = content::NavigationThrottle::ThrottleCheckResult;
+
+}  // namespace
+
+// static
+std::unique_ptr<content::NavigationThrottle>
+NavigationCapturingRedirectionThrottle::MaybeCreate(
+    content::NavigationHandle* handle) {
+  return base::WrapUnique(new NavigationCapturingRedirectionThrottle(handle));
+}
+
+NavigationCapturingRedirectionThrottle::
+    ~NavigationCapturingRedirectionThrottle() = default;
+
+const char* NavigationCapturingRedirectionThrottle::GetNameForLogging() {
+  return "NavigationCapturingWebAppRedirectThrottle";
+}
+
+ThrottleCheckResult
+NavigationCapturingRedirectionThrottle::WillProcessResponse() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  return HandleRequest();
+}
+
+ThrottleCheckResult NavigationCapturingRedirectionThrottle::HandleRequest() {
+  // TODO(crbug.com/351775835): This is where the final response of a navigation
+  // will be handled.
+  return content::NavigationThrottle::PROCEED;
+}
+
+NavigationCapturingRedirectionThrottle::NavigationCapturingRedirectionThrottle(
+    content::NavigationHandle* navigation_handle)
+    : content::NavigationThrottle(navigation_handle) {}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/link_capturing_redirect_navigation_throttle.h b/chrome/browser/ui/web_applications/navigation_capturing_redirection_throttle.h
similarity index 60%
rename from chrome/browser/web_applications/link_capturing_redirect_navigation_throttle.h
rename to chrome/browser/ui/web_applications/navigation_capturing_redirection_throttle.h
index 7a60a6d..c674448 100644
--- a/chrome/browser/web_applications/link_capturing_redirect_navigation_throttle.h
+++ b/chrome/browser/ui/web_applications/navigation_capturing_redirection_throttle.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_WEB_APPLICATIONS_LINK_CAPTURING_REDIRECT_NAVIGATION_THROTTLE_H_
-#define CHROME_BROWSER_WEB_APPLICATIONS_LINK_CAPTURING_REDIRECT_NAVIGATION_THROTTLE_H_
+#ifndef CHROME_BROWSER_UI_WEB_APPLICATIONS_NAVIGATION_CAPTURING_REDIRECTION_THROTTLE_H_
+#define CHROME_BROWSER_UI_WEB_APPLICATIONS_NAVIGATION_CAPTURING_REDIRECTION_THROTTLE_H_
 
 #include <memory>
 
@@ -14,7 +14,7 @@
 
 // Navigation throttle used to handle navigation capturing at the end of a
 // redirect chain.
-class LinkCapturingRedirectNavigationThrottle
+class NavigationCapturingRedirectionThrottle
     : public content::NavigationThrottle {
  public:
   using ThrottleCheckResult = content::NavigationThrottle::ThrottleCheckResult;
@@ -22,11 +22,11 @@
   static std::unique_ptr<content::NavigationThrottle> MaybeCreate(
       content::NavigationHandle* handle);
 
-  LinkCapturingRedirectNavigationThrottle(
-      const LinkCapturingRedirectNavigationThrottle&) = delete;
-  LinkCapturingRedirectNavigationThrottle& operator=(
-      const LinkCapturingRedirectNavigationThrottle&) = delete;
-  ~LinkCapturingRedirectNavigationThrottle() override;
+  NavigationCapturingRedirectionThrottle(
+      const NavigationCapturingRedirectionThrottle&) = delete;
+  NavigationCapturingRedirectionThrottle& operator=(
+      const NavigationCapturingRedirectionThrottle&) = delete;
+  ~NavigationCapturingRedirectionThrottle() override;
 
   // content::NavigationHandle overrides:
   const char* GetNameForLogging() override;
@@ -36,7 +36,7 @@
   ThrottleCheckResult WillProcessResponse() override;
 
  private:
-  explicit LinkCapturingRedirectNavigationThrottle(
+  explicit NavigationCapturingRedirectionThrottle(
       content::NavigationHandle* navigation_handle);
 
   ThrottleCheckResult HandleRequest();
@@ -44,4 +44,4 @@
 
 }  // namespace web_app
 
-#endif  // CHROME_BROWSER_WEB_APPLICATIONS_LINK_CAPTURING_REDIRECT_NAVIGATION_THROTTLE_H_
+#endif  // CHROME_BROWSER_UI_WEB_APPLICATIONS_NAVIGATION_CAPTURING_REDIRECTION_THROTTLE_H_
diff --git a/chrome/browser/ui/web_applications/web_app_launch_utils.cc b/chrome/browser/ui/web_applications/web_app_launch_utils.cc
index d171bff..cc6522e 100644
--- a/chrome/browser/ui/web_applications/web_app_launch_utils.cc
+++ b/chrome/browser/ui/web_applications/web_app_launch_utils.cc
@@ -1004,9 +1004,12 @@
       Browser* app_window = CreateWebAppWindowFromNavigationParams(
           *current_browser_app_id, params,
           params.disposition == WindowOpenDisposition::NEW_POPUP);
-      return {AppNavigationResult{app_window, -1,
-                                  /*enqueue_launch_params=*/false,
-                                  /*show_iph=*/false, std::move(debug_data)}};
+      return {AppNavigationResult{
+          app_window, -1,
+          /*enqueue_launch_params=*/false,
+          /*show_iph=*/false,
+          NavigationHandlingInitialResult::kAppWindowAuxContext,
+          std::move(debug_data)}};
     }
     return std::nullopt;
   }
@@ -1021,9 +1024,12 @@
           OpensInStandaloneExperience(*controlling_app_id)) {
         Browser* app_window =
             CreateWebAppWindowFromNavigationParams(*controlling_app_id, params);
-        return {AppNavigationResult{app_window, -1,
-                                    /*enqueue_launch_params=*/true,
-                                    /*show_iph=*/true, std::move(debug_data)}};
+        return {AppNavigationResult{
+            app_window, -1,
+            /*enqueue_launch_params=*/true,
+            /*show_iph=*/true,
+            NavigationHandlingInitialResult::kAppWindowLaunchHandling,
+            std::move(debug_data)}};
       }
 
       const webapps::AppId& current_app_id = *current_browser_app_id;
@@ -1036,16 +1042,21 @@
         if (!params.browser->app_controller()->ShouldHideNewTabButton()) {
           // Apps that support tabbed mode can open a new tab in the current app
           // browser itself.
-          return {AppNavigationResult{params.browser, -1,
-                                      /*enqueue_launch_params=*/true,
-                                      /*show_iph=*/true,
-                                      std::move(debug_data)}};
+          return {AppNavigationResult{
+              params.browser, -1,
+              /*enqueue_launch_params=*/true,
+              /*show_iph=*/true,
+              NavigationHandlingInitialResult::kAppWindowLaunchHandling,
+              std::move(debug_data)}};
         }
         Browser* app_window =
             CreateWebAppWindowFromNavigationParams(current_app_id, params);
-        return {AppNavigationResult{app_window, -1,
-                                    /*enqueue_launch_params=*/true,
-                                    /*show_iph=*/true, std::move(debug_data)}};
+        return {AppNavigationResult{
+            app_window, -1,
+            /*enqueue_launch_params=*/true,
+            /*show_iph=*/true,
+            NavigationHandlingInitialResult::kAppWindowLaunchHandling,
+            std::move(debug_data)}};
       }
     }
     return std::nullopt;
@@ -1118,9 +1129,10 @@
         RecordLaunchMetrics(
             app_id, apps::LaunchContainer::kLaunchContainerWindow,
             apps::LaunchSource::kFromNavigationCapturing, params.url, contents);
-        return {AppNavigationResult{/*browser= */ nullptr, -1,
-                                    /*enqueue_launch_params=*/false,
-                                    /*show_iph=*/true}};
+        return {AppNavigationResult{
+            /*browser= */ nullptr, -1,
+            /*enqueue_launch_params=*/false, /*show_iph=*/true,
+            NavigationHandlingInitialResult::kAppWindowNavigationCaptured}};
       }
 
       // Fallback to creating a new instance.
@@ -1131,10 +1143,12 @@
     // Navigate existing.
     if (client_mode == LaunchHandler::ClientMode::kNavigateExisting) {
       if (existing_browser_and_tab) {
-        return {AppNavigationResult{existing_browser_and_tab->first,
-                                    existing_browser_and_tab->second,
-                                    /*enqueue_launch_params=*/true,
-                                    /*show_iph=*/true, std::move(debug_data)}};
+        return {AppNavigationResult{
+            existing_browser_and_tab->first, existing_browser_and_tab->second,
+            /*enqueue_launch_params=*/true,
+            /*show_iph=*/true,
+            NavigationHandlingInitialResult::kAppWindowNavigationCaptured,
+            std::move(debug_data)}};
       }
       client_mode = LaunchHandler::ClientMode::kNavigateNew;
       debug_data.Set("client_mode", base::ToString(client_mode));
@@ -1158,9 +1172,12 @@
     // TODO(crbug.com/359224477): In all but one case we set `show_iph` to the
     // same value as `enqueue_launch_params`. Maybe there is an opportunity to
     // simplify this once the WebAppLaunchProcess logic has been fixed.
-    return {AppNavigationResult{app_window, -1,
-                                /*enqueue_launch_params=*/true,
-                                /*show_iph=*/true, std::move(debug_data)}};
+    return {AppNavigationResult{
+        app_window, -1,
+        /*enqueue_launch_params=*/true,
+        /*show_iph=*/true,
+        NavigationHandlingInitialResult::kAppWindowNavigationCaptured,
+        std::move(debug_data)}};
   }
   return std::nullopt;
 }
@@ -1182,17 +1199,26 @@
     std::optional<web_app::AppNavigationResult> app_navigation_result,
     const NavigateParams& params,
     base::WeakPtr<content::NavigationHandle> navigation_handle,
-    WindowOpenDisposition original_disposition) {
-  // Store the original disposition with the NavigationHandle or WebContents, in
-  // order to make behavior decisions after a redirection has happened for the
-  // current navigation.
+    WindowOpenDisposition original_disposition,
+    std::optional<webapps::AppId> source_app_id) {
+  // Construct the information necessary to handle redirections and store it
+  // with the NavigationHandle or WebContents, in order to make behavior
+  // decisions later on inside the `NavigationCapturingRedirectionThrottle`.
+  NavigationCapturingRedirectionInfo redirection_info;
+  redirection_info.app_id_initial_browser = source_app_id;
+  redirection_info.disposition = original_disposition;
+  redirection_info.initial_nav_handling_result =
+      app_navigation_result ? app_navigation_result->initial_result
+                            : NavigationHandlingInitialResult::kBrowserTab;
+
   if (navigation_handle) {
     web_app::NavigationCapturingNavigationHandleUserData::
-        CreateForNavigationHandle(*navigation_handle, original_disposition);
+        CreateForNavigationHandle(*navigation_handle,
+                                  std::move(redirection_info));
   } else {
     CHECK(params.navigated_or_inserted_contents);
     web_app::NavigationCapturingInformationForwarder::CreateForWebContents(
-        params.navigated_or_inserted_contents, original_disposition);
+        params.navigated_or_inserted_contents, std::move(redirection_info));
   }
 
   if (!app_navigation_result.has_value() ||
diff --git a/chrome/browser/ui/web_applications/web_app_launch_utils.h b/chrome/browser/ui/web_applications/web_app_launch_utils.h
index 55bbde7..6943832d 100644
--- a/chrome/browser/ui/web_applications/web_app_launch_utils.h
+++ b/chrome/browser/ui/web_applications/web_app_launch_utils.h
@@ -16,6 +16,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/web_applications/navigation_capturing_navigation_handle_user_data.h"
 #include "chrome/browser/web_applications/web_app_ui_manager.h"
 #include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "components/webapps/common/web_app_id.h"
@@ -52,10 +53,22 @@
 // Returns information useful for the browser to show UI affordances, provided a
 // web app handles the navigation.
 struct AppNavigationResult {
+  // The browser instance to perform navigation in.
   Browser* browser = nullptr;
+  // If navigation needs to happen in a tab inside the browser, this stores the
+  // index of the tab.
   int tab_index = -1;
+  // Whether launch params need to be enqueued for the navigation.
   bool enqueue_launch_params = false;
+  // Whether the IPH bubble for navigation handling needs to be showed to the
+  // end user.
   bool show_iph = false;
+  // The result of navigation handling by the web app system. Useful for making
+  // future decisions if the navigation redirects.
+  NavigationHandlingInitialResult initial_result =
+      NavigationHandlingInitialResult::kBrowserTab;
+
+  // Debug information persisted to chrome://web-app-internals.
   base::Value::Dict debug_value;
 
   STACK_ALLOCATED();
@@ -170,7 +183,8 @@
     std::optional<web_app::AppNavigationResult> app_navigation_result,
     const NavigateParams& params,
     base::WeakPtr<content::NavigationHandle> navigation_handle,
-    WindowOpenDisposition original_disposition);
+    WindowOpenDisposition original_disposition,
+    std::optional<webapps::AppId> source_app_id);
 
 }  // namespace web_app
 
diff --git a/chrome/browser/ui/webui/discards/discards_ui.cc b/chrome/browser/ui/webui/discards/discards_ui.cc
index edc11c8..dd1ffc5 100644
--- a/chrome/browser/ui/webui/discards/discards_ui.cc
+++ b/chrome/browser/ui/webui/discards/discards_ui.cc
@@ -147,6 +147,8 @@
       info->loading_state = lifecycle_unit->GetLoadingState();
       info->state = lifecycle_unit->GetState();
       resource_coordinator::DecisionDetails discard_details;
+      info->can_discard = lifecycle_unit->CanDiscard(
+          ::mojom::LifecycleUnitDiscardReason::PROACTIVE, &discard_details);
       info->cannot_discard_reasons = discard_details.GetFailureReasonStrings();
       info->discard_reason = lifecycle_unit->GetDiscardReason();
       info->discard_count = lifecycle_unit->GetDiscardCount();
diff --git a/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.cc b/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.cc
index 3fe311f..86c1f22 100644
--- a/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.cc
+++ b/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.cc
@@ -10,7 +10,6 @@
 #include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.h"
 
 #include "base/check_deref.h"
-#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_forward.h"
 #include "base/json/json_writer.h"
@@ -39,13 +38,11 @@
 #include "content/public/browser/web_ui_data_source.h"
 
 namespace {
-std::string GetChoiceListJSON(Profile& profile) {
+std::string GetChoiceListJSON(
+    SearchEngineChoiceDialogService& search_engine_choice_dialog_service) {
   base::Value::List choice_value_list;
-  SearchEngineChoiceDialogService* search_engine_choice_dialog_service =
-      SearchEngineChoiceDialogServiceFactory::GetForProfile(&profile);
-  CHECK(search_engine_choice_dialog_service);
   const TemplateURL::TemplateURLVector choices =
-      search_engine_choice_dialog_service->GetSearchEngines();
+      search_engine_choice_dialog_service.GetSearchEngines();
 
   for (const auto& choice : choices) {
     base::Value::Dict choice_value;
@@ -121,11 +118,18 @@
                           IDR_SIGNIN_TANGIBLE_SYNC_STYLE_SHARED_LIT_CSS_JS);
   source->AddResourcePath("signin_vars.css.js", IDR_SIGNIN_SIGNIN_VARS_CSS_JS);
 
-  source->AddString("choiceList", GetChoiceListJSON(profile_.get()));
-  source->AddBoolean("showGuestCheckbox",
-                     base::FeatureList::IsEnabled(
-                         switches::kSearchEngineChoiceGuestExperience) &&
-                         profile_->IsGuestSession());
+  SearchEngineChoiceDialogService* search_engine_choice_dialog_service =
+      SearchEngineChoiceDialogServiceFactory::GetForProfile(&profile_.get());
+  source->AddString(
+      "choiceList",
+      GetChoiceListJSON(CHECK_DEREF(search_engine_choice_dialog_service)));
+
+  search_engines::SearchEngineChoiceService* search_engine_choice_service =
+      search_engines::SearchEngineChoiceServiceFactory::GetForProfile(
+          &profile_.get());
+  source->AddBoolean(
+      "showGuestCheckbox",
+      search_engine_choice_service->IsProfileEligibleForDseGuestPropagation());
 
   webui::SetupWebUIDataSource(
       source,
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index bb2595f..23b5d97 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -3297,7 +3297,11 @@
       {"siteSettingsSmartCardReadersAllowed",
        IDS_SITE_SETTINGS_SMART_CARDS_ALLOWED},
       {"siteSettingsSmartCardReadersBlocked",
-       IDS_SITE_SETTINGS_SMART_CARDS_BLOCKED}};
+       IDS_SITE_SETTINGS_SMART_CARDS_BLOCKED},
+      {"siteSettingsNoSmartCardReadersFound",
+       IDS_SITE_SETTINGS_NO_SMART_CARD_READERS_FOUND},
+      {"siteSettingsResetSmartCardConfirmation",
+       IDS_SITE_SETTINGS_RESET_SMART_CARD_CONFIRMATION}};
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
   // Tracking protection learn more links.
@@ -3375,11 +3379,6 @@
       base::FeatureList::IsEnabled(
           features::kFileSystemAccessPersistentPermissions));
 
-  html_source->AddBoolean(
-      "permissionDedicatedCpssSettings",
-      base::FeatureList::IsEnabled(
-          permissions::features::kPermissionDedicatedCpssSetting));
-
   // The exception placeholder should not be translated. See
   // crbug.com/1095878.
   html_source->AddString("addSiteExceptionPlaceholder", "[*.]example.com");
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index fee3f9f8..77f614d 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -14,6 +14,7 @@
 #include "base/check_deref.h"
 #include "base/containers/contains.h"
 #include "base/containers/flat_set.h"
+#include "base/containers/to_value_list.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
@@ -49,6 +50,10 @@
 #include "chrome/browser/privacy_sandbox/privacy_sandbox_service_factory.h"
 #include "chrome/browser/serial/serial_chooser_context.h"
 #include "chrome/browser/serial/serial_chooser_context_factory.h"
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/smart_card/smart_card_permission_context.h"
+#include "chrome/browser/smart_card/smart_card_permission_context_factory.h"
+#endif
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/page_info/page_info_infobar_delegate.h"
@@ -106,6 +111,7 @@
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
 #include "storage/common/file_system/file_system_util.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/page/page_zoom.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/text/bytes_formatting.h"
@@ -716,6 +722,20 @@
       base::BindRepeating(&SiteSettingsHandler::HandleRevokeFileSystemGrants,
                           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
+      "getSmartCardReaderGrants",
+      base::BindRepeating(&SiteSettingsHandler::HandleGetSmartCardReaderGrants,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "revokeAllSmartCardReadersGrants",
+      base::BindRepeating(
+          &SiteSettingsHandler::HandleRevokeAllSmartCardReaderGrants,
+          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "revokeSmartCardReaderGrant",
+      base::BindRepeating(
+          &SiteSettingsHandler::HandleRevokeSmartCardReaderGrant,
+          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
       "getChooserExceptionList",
       base::BindRepeating(&SiteSettingsHandler::HandleGetChooserExceptionList,
                           base::Unretained(this)));
@@ -1545,6 +1565,65 @@
   permission_context->RevokeGrants(origin);
 }
 
+void SiteSettingsHandler::HandleGetSmartCardReaderGrants(
+    const base::Value::List& args) {
+  DCHECK(base::FeatureList::IsEnabled(blink::features::kSmartCard));
+
+  CHECK_EQ(1U, args.size());
+  AllowJavascript();
+
+  const base::Value& callback_id = args[0];
+  base::Value::List reader_names;
+#if BUILDFLAG(IS_CHROMEOS)
+  SmartCardPermissionContext& permission_context =
+      SmartCardPermissionContextFactory::GetForProfile(*profile_);
+
+  reader_names = base::ToValueList(
+      permission_context.GetPersistentReaderGrants(),
+      [](const SmartCardPermissionContext::ReaderGrants& reader_grant) {
+        return base::Value::Dict()
+            .Set(site_settings::kReaderName, reader_grant.reader_name)
+            .Set(site_settings::kOrigins,
+                 base::ToValueList(reader_grant.origins,
+                                   &url::Origin::Serialize));
+      });
+#endif
+  ResolveJavascriptCallback(callback_id, reader_names);
+}
+
+void SiteSettingsHandler::HandleRevokeAllSmartCardReaderGrants(
+    const base::Value::List& args) {
+  DCHECK(base::FeatureList::IsEnabled(blink::features::kSmartCard));
+
+  CHECK(args.empty());
+  AllowJavascript();
+#if BUILDFLAG(IS_CHROMEOS)
+  SmartCardPermissionContext& permission_context =
+      SmartCardPermissionContextFactory::GetForProfile(*profile_);
+
+  permission_context.RevokeAllPermissions();
+#endif
+}
+
+void SiteSettingsHandler::HandleRevokeSmartCardReaderGrant(
+    const base::Value::List& args) {
+  DCHECK(base::FeatureList::IsEnabled(blink::features::kSmartCard));
+
+  CHECK_EQ(2U, args.size());
+  AllowJavascript();
+#if BUILDFLAG(IS_CHROMEOS)
+
+  auto reader_name = args[0].GetString();
+  auto url = GURL(args[1].GetString());
+  DCHECK(url.is_valid());
+  const url::Origin& origin = url::Origin::Create(url);
+
+  SmartCardPermissionContext& permission_context =
+      SmartCardPermissionContextFactory::GetForProfile(*profile_);
+  permission_context.RevokePersistentPermission(reader_name, origin);
+#endif
+}
+
 void SiteSettingsHandler::HandleSetOriginPermissions(
     const base::Value::List& args) {
   CHECK_EQ(3U, args.size());
@@ -2171,6 +2250,16 @@
           file_system_access_permission_context);
     }
   }
+
+#if BUILDFLAG(IS_CHROMEOS)
+  if (base::FeatureList::IsEnabled(blink::features::kSmartCard)) {
+    auto& smart_card_context =
+        SmartCardPermissionContextFactory::GetForProfile(*profile);
+    if (!chooser_observations_.IsObservingSource(&smart_card_context)) {
+      chooser_observations_.AddObservation(&smart_card_context);
+    }
+  }
+#endif
   observed_profiles_.AddObservation(profile);
 }
 
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.h b/chrome/browser/ui/webui/settings/site_settings_handler.h
index ff86df7..5b1d33b 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.h
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.h
@@ -182,6 +182,10 @@
   // Revokes all of the File System Access permissions for a given origin.
   void HandleRevokeFileSystemGrants(const base::Value::List& args);
 
+  void HandleGetSmartCardReaderGrants(const base::Value::List& args);
+  void HandleRevokeAllSmartCardReaderGrants(const base::Value::List& args);
+  void HandleRevokeSmartCardReaderGrant(const base::Value::List& args);
+
   // Gets and sets a list of ContentSettingTypes for an origin.
   // TODO(crbug.com/40528601): Investigate replacing the
   // '*CategoryPermissionForPattern' equivalents below with these methods.
diff --git a/chrome/browser/ui/webui/settings/site_settings_helper.h b/chrome/browser/ui/webui/settings/site_settings_helper.h
index a699eee3..5052df1ec 100644
--- a/chrome/browser/ui/webui/settings/site_settings_helper.h
+++ b/chrome/browser/ui/webui/settings/site_settings_helper.h
@@ -110,9 +110,11 @@
 constexpr char kObject[] = "object";
 constexpr char kOpenDescription[] = "openDescription";
 constexpr char kOrigin[] = "origin";
+constexpr char kOrigins[] = "origins";
 constexpr char kOriginForFavicon[] = "originForFavicon";
 constexpr char kPermissions[] = "permissions";
 constexpr char kPolicyIndicator[] = "indicator";
+constexpr char kReaderName[] = "readerName";
 constexpr char kRecentPermissions[] = "recentPermissions";
 constexpr char kSetting[] = "setting";
 constexpr char kSites[] = "sites";
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index b8350b5..276539f 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -184,8 +184,6 @@
     "jobs/uninstall/uninstall_job.cc",
     "jobs/uninstall/uninstall_job.h",
     "jobs/uninstall/web_app_uninstall_and_replace_job.h",
-    "link_capturing_redirect_navigation_throttle.cc",
-    "link_capturing_redirect_navigation_throttle.h",
     "locks/all_apps_lock.cc",
     "locks/all_apps_lock.h",
     "locks/app_lock.cc",
diff --git a/chrome/browser/web_applications/link_capturing_redirect_navigation_throttle.cc b/chrome/browser/web_applications/link_capturing_redirect_navigation_throttle.cc
deleted file mode 100644
index 1f35b289..0000000
--- a/chrome/browser/web_applications/link_capturing_redirect_navigation_throttle.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/web_applications/link_capturing_redirect_navigation_throttle.h"
-
-#include "chrome/browser/web_applications/navigation_capturing_information_forwarder.h"
-#include "chrome/browser/web_applications/navigation_capturing_navigation_handle_user_data.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/navigation_handle.h"
-#include "ui/base/window_open_disposition.h"
-
-namespace web_app {
-
-namespace {
-
-using ThrottleCheckResult = content::NavigationThrottle::ThrottleCheckResult;
-
-}  // namespace
-
-// static
-std::unique_ptr<content::NavigationThrottle>
-LinkCapturingRedirectNavigationThrottle::MaybeCreate(
-    content::NavigationHandle* handle) {
-  return base::WrapUnique(new LinkCapturingRedirectNavigationThrottle(handle));
-}
-
-LinkCapturingRedirectNavigationThrottle::
-    ~LinkCapturingRedirectNavigationThrottle() = default;
-
-const char* LinkCapturingRedirectNavigationThrottle::GetNameForLogging() {
-  return "LinkCapturingRedirectNavigationThrottle";
-}
-
-ThrottleCheckResult
-LinkCapturingRedirectNavigationThrottle::WillProcessResponse() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return HandleRequest();
-}
-
-ThrottleCheckResult LinkCapturingRedirectNavigationThrottle::HandleRequest() {
-  // TODO(crbug.com/351775835): This is where the final response of a navigation
-  // will be handled.
-  return content::NavigationThrottle::PROCEED;
-}
-
-LinkCapturingRedirectNavigationThrottle::
-    LinkCapturingRedirectNavigationThrottle(
-        content::NavigationHandle* navigation_handle)
-    : content::NavigationThrottle(navigation_handle) {}
-
-}  // namespace web_app
diff --git a/chrome/browser/web_applications/navigation_capturing_data_transfer_browsertest.cc b/chrome/browser/web_applications/navigation_capturing_data_transfer_browsertest.cc
index 5143c1a1..1ac26b92 100644
--- a/chrome/browser/web_applications/navigation_capturing_data_transfer_browsertest.cc
+++ b/chrome/browser/web_applications/navigation_capturing_data_transfer_browsertest.cc
@@ -51,17 +51,17 @@
     auto* navigation_handle_data =
         web_app::NavigationCapturingNavigationHandleUserData::
             GetForNavigationHandle(*handle);
-    disposition_in_handle_ = navigation_handle_data
-                                 ? navigation_handle_data->disposition()
-                                 : WindowOpenDisposition::UNKNOWN;
+    if (navigation_handle_data) {
+      redirection_info_ = navigation_handle_data->redirection_info();
+    }
     ConditionMet();
   }
 
   void AwaitNavigationCompletion() { Wait(); }
 
-  WindowOpenDisposition GetDispositionForNavigation() {
-    CHECK(disposition_in_handle_.has_value());
-    return disposition_in_handle_.value();
+  NavigationCapturingRedirectionInfo GetRedirectionInfoForNavigation() {
+    CHECK(redirection_info_.has_value());
+    return redirection_info_.value();
   }
 
   // AllTabsObserver override:
@@ -77,7 +77,7 @@
   }
 
  private:
-  std::optional<WindowOpenDisposition> disposition_in_handle_;
+  std::optional<NavigationCapturingRedirectionInfo> redirection_info_;
 };
 
 class NavigationCapturingDataTransferBrowserTest
@@ -181,8 +181,14 @@
   nav_awaiter.AwaitNavigationCompletion();
   ASSERT_NE(nullptr, app_browser);
 
-  EXPECT_EQ(nav_awaiter.GetDispositionForNavigation(),
-            WindowOpenDisposition::NEW_FOREGROUND_TAB);
+  NavigationCapturingRedirectionInfo redirection_info =
+      std::move(nav_awaiter.GetRedirectionInfoForNavigation());
+  // Triggered from a tab and not an app window.
+  EXPECT_FALSE(redirection_info.app_id_initial_browser.has_value());
+  EXPECT_EQ(NavigationHandlingInitialResult::kAppWindowNavigationCaptured,
+            redirection_info.initial_nav_handling_result);
+  EXPECT_EQ(WindowOpenDisposition::NEW_FOREGROUND_TAB,
+            redirection_info.disposition);
 
   // Post navigation, the WebContentsUserData instances should be cleaned up.
   EXPECT_THAT(GetForwarderForWebContents(
@@ -199,8 +205,15 @@
                                test::ClickMethod::kLeftClick);
   nav_awaiter.AwaitNavigationCompletion();
 
-  EXPECT_EQ(nav_awaiter.GetDispositionForNavigation(),
-            WindowOpenDisposition::NEW_FOREGROUND_TAB);
+  NavigationCapturingRedirectionInfo redirection_info =
+      std::move(nav_awaiter.GetRedirectionInfoForNavigation());
+
+  // Triggered from a tab and not an app window.
+  EXPECT_FALSE(redirection_info.app_id_initial_browser.has_value());
+  EXPECT_EQ(NavigationHandlingInitialResult::kBrowserTab,
+            redirection_info.initial_nav_handling_result);
+  EXPECT_EQ(WindowOpenDisposition::NEW_FOREGROUND_TAB,
+            redirection_info.disposition);
 
   // Post navigation, the WebContentsUserData instances should be cleaned up.
   EXPECT_THAT(GetForwarderForWebContents(contents), testing::IsNull());
@@ -220,8 +233,19 @@
   nav_awaiter.AwaitNavigationCompletion();
   ASSERT_NE(nullptr, app_browser);
 
-  EXPECT_EQ(nav_awaiter.GetDispositionForNavigation(),
-            WindowOpenDisposition::NEW_WINDOW);
+  NavigationCapturingRedirectionInfo redirection_info =
+      std::move(nav_awaiter.GetRedirectionInfoForNavigation());
+
+  // Triggered from an app window for app_id_a.
+  EXPECT_TRUE(redirection_info.app_id_initial_browser.has_value());
+  EXPECT_EQ(app_id_a, redirection_info.app_id_initial_browser.value());
+  // Navigation capturing only extends to left clicks creating a capturable
+  // experience, and does not extend to user modified clicks like shift and
+  // middle clicks.
+  EXPECT_EQ(NavigationHandlingInitialResult::kAppWindowLaunchHandling,
+            redirection_info.initial_nav_handling_result);
+  EXPECT_EQ(WindowOpenDisposition::NEW_WINDOW, redirection_info.disposition);
+
   EXPECT_THAT(GetForwarderForWebContents(
                   app_browser->tab_strip_model()->GetActiveWebContents()),
               testing::IsNull());
@@ -240,8 +264,17 @@
   nav_awaiter.AwaitNavigationCompletion();
   ASSERT_NE(nullptr, app_browser);
 
-  EXPECT_EQ(nav_awaiter.GetDispositionForNavigation(),
-            WindowOpenDisposition::NEW_BACKGROUND_TAB);
+  NavigationCapturingRedirectionInfo redirection_info =
+      std::move(nav_awaiter.GetRedirectionInfoForNavigation());
+
+  // Triggered from an app window for app_id.
+  EXPECT_TRUE(redirection_info.app_id_initial_browser.has_value());
+  EXPECT_EQ(app_id, redirection_info.app_id_initial_browser.value());
+  EXPECT_EQ(NavigationHandlingInitialResult::kAppWindowLaunchHandling,
+            redirection_info.initial_nav_handling_result);
+  EXPECT_EQ(WindowOpenDisposition::NEW_BACKGROUND_TAB,
+            redirection_info.disposition);
+
   EXPECT_THAT(GetForwarderForWebContents(
                   app_browser->tab_strip_model()->GetActiveWebContents()),
               testing::IsNull());
diff --git a/chrome/browser/web_applications/navigation_capturing_information_forwarder.cc b/chrome/browser/web_applications/navigation_capturing_information_forwarder.cc
index fc83e24..9d2a393b 100644
--- a/chrome/browser/web_applications/navigation_capturing_information_forwarder.cc
+++ b/chrome/browser/web_applications/navigation_capturing_information_forwarder.cc
@@ -16,12 +16,13 @@
     ~NavigationCapturingInformationForwarder() = default;
 
 NavigationCapturingInformationForwarder::
-    NavigationCapturingInformationForwarder(content::WebContents* contents,
-                                            WindowOpenDisposition disposition)
+    NavigationCapturingInformationForwarder(
+        content::WebContents* contents,
+        NavigationCapturingRedirectionInfo redirection_info)
     : content::WebContentsObserver(contents),
       content::WebContentsUserData<NavigationCapturingInformationForwarder>(
           *contents),
-      disposition_(disposition) {}
+      redirection_info_(std::move(redirection_info)) {}
 
 void NavigationCapturingInformationForwarder::SelfDestruct() {
   GetWebContents().RemoveUserData(UserDataKey());
@@ -29,9 +30,9 @@
 
 void NavigationCapturingInformationForwarder::DidStartNavigation(
     content::NavigationHandle* navigation_handle) {
-  CHECK(disposition_ != WindowOpenDisposition::UNKNOWN);
   web_app::NavigationCapturingNavigationHandleUserData::
-      CreateForNavigationHandle(*navigation_handle, disposition_);
+      CreateForNavigationHandle(*navigation_handle,
+                                std::move(redirection_info_));
   SelfDestruct();
 }
 
diff --git a/chrome/browser/web_applications/navigation_capturing_information_forwarder.h b/chrome/browser/web_applications/navigation_capturing_information_forwarder.h
index f26d062..5747ddaa 100644
--- a/chrome/browser/web_applications/navigation_capturing_information_forwarder.h
+++ b/chrome/browser/web_applications/navigation_capturing_information_forwarder.h
@@ -5,10 +5,10 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_NAVIGATION_CAPTURING_INFORMATION_FORWARDER_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_NAVIGATION_CAPTURING_INFORMATION_FORWARDER_H_
 
+#include "chrome/browser/web_applications/navigation_capturing_navigation_handle_user_data.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
-#include "ui/base/window_open_disposition.h"
 
 namespace content {
 class WebContents;
@@ -38,11 +38,12 @@
       content::NavigationHandle* navigation_handle) override;
 
  private:
-  NavigationCapturingInformationForwarder(content::WebContents* contents,
-                                          WindowOpenDisposition disposition);
+  NavigationCapturingInformationForwarder(
+      content::WebContents* contents,
+      NavigationCapturingRedirectionInfo redirection_info);
   friend WebContentsUserData;
 
-  WindowOpenDisposition disposition_ = WindowOpenDisposition::UNKNOWN;
+  NavigationCapturingRedirectionInfo redirection_info_;
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 };
 
diff --git a/chrome/browser/web_applications/navigation_capturing_navigation_handle_user_data.cc b/chrome/browser/web_applications/navigation_capturing_navigation_handle_user_data.cc
index 8b3e35ca..83d08799 100644
--- a/chrome/browser/web_applications/navigation_capturing_navigation_handle_user_data.cc
+++ b/chrome/browser/web_applications/navigation_capturing_navigation_handle_user_data.cc
@@ -10,14 +10,21 @@
 
 namespace web_app {
 
+NavigationCapturingRedirectionInfo::NavigationCapturingRedirectionInfo() =
+    default;
+NavigationCapturingRedirectionInfo::~NavigationCapturingRedirectionInfo() =
+    default;
+NavigationCapturingRedirectionInfo::NavigationCapturingRedirectionInfo(
+    const NavigationCapturingRedirectionInfo& navigation_info) = default;
+
 NavigationCapturingNavigationHandleUserData::
     ~NavigationCapturingNavigationHandleUserData() = default;
 
 NavigationCapturingNavigationHandleUserData::
     NavigationCapturingNavigationHandleUserData(
         content::NavigationHandle& navigation_handle,
-        WindowOpenDisposition disposition)
-    : disposition_(disposition) {}
+        NavigationCapturingRedirectionInfo redirection_info)
+    : redirection_info_(std::move(redirection_info)) {}
 
 NAVIGATION_HANDLE_USER_DATA_KEY_IMPL(
     NavigationCapturingNavigationHandleUserData);
diff --git a/chrome/browser/web_applications/navigation_capturing_navigation_handle_user_data.h b/chrome/browser/web_applications/navigation_capturing_navigation_handle_user_data.h
index 652b8e6..96a3c050 100644
--- a/chrome/browser/web_applications/navigation_capturing_navigation_handle_user_data.h
+++ b/chrome/browser/web_applications/navigation_capturing_navigation_handle_user_data.h
@@ -5,6 +5,10 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_NAVIGATION_CAPTURING_NAVIGATION_HANDLE_USER_DATA_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_NAVIGATION_CAPTURING_NAVIGATION_HANDLE_USER_DATA_H_
 
+#include <optional>
+
+#include "base/memory/raw_ptr.h"
+#include "components/webapps/common/web_app_id.h"
 #include "content/public/browser/navigation_handle_user_data.h"
 #include "ui/base/window_open_disposition.h"
 
@@ -12,10 +16,52 @@
 class NavigationHandle;
 }  // namespace content
 
+class Browser;
+
 namespace web_app {
 
+// TODO(crbug.com/336371044): Support web apps that open in a new tab.
+// The initial result of navigation handling, stored as an enum to prevent
+// transferring a `Browser` instance everywhere. The possible use-cases are:
+// 1. The web app system does not handle the navigation, so a new browser tab
+// opens.
+// 2. The web app system handles the navigation and captures it as part of a
+// left click with a new top level browsing context. Launch parameters are
+// enqueued.
+// 3. The web app system handles the navigation and launches a new app, but it
+// wasn't captured as it was triggered by a shift or middle click. Launch
+// parameters are enqueued.
+// 4. The web app system handles the navigation and opens a new app window as
+// part of a navigation that created an auxiliary browsing context. This is not
+// an app launch, and as such, launch parameters are not enqueued.
+enum class NavigationHandlingInitialResult {
+  kBrowserTab = 0,
+  kAppWindowNavigationCaptured = 1,
+  kAppWindowLaunchHandling = 2,
+  kAppWindowAuxContext = 3,
+  kMaxValue = kAppWindowAuxContext
+};
+
+// Information that will be used to make decisions regarding redirection.
+// Includes:
+// 1. The app_id of the source app browser if the navigation was triggered from
+// an app browser window or from a web app that is set to open in a new tab,
+// std::nullopt otherwise.
+// 2. The initial result of navigation handling by the web app system.
+// 3. The initial `WindowOpenDisposition` of the navigation.
+struct NavigationCapturingRedirectionInfo {
+  NavigationCapturingRedirectionInfo();
+  ~NavigationCapturingRedirectionInfo();
+  NavigationCapturingRedirectionInfo(
+      const NavigationCapturingRedirectionInfo& navigation_info);
+
+  std::optional<webapps::AppId> app_id_initial_browser;
+  NavigationHandlingInitialResult initial_nav_handling_result;
+  WindowOpenDisposition disposition = WindowOpenDisposition::UNKNOWN;
+};
+
 // Data that is tied to the NavigationHandle. Used in the
-// LinkCapturingRedirectNavigationThrottle to make final decisions on what the
+// `NavigationCapturingRedirectionThrottle` to make final decisions on what the
 // outcome of navigation capturing on a redirected navigation should be.
 class NavigationCapturingNavigationHandleUserData
     : public content::NavigationHandleUserData<
@@ -23,20 +69,20 @@
  public:
   ~NavigationCapturingNavigationHandleUserData() override;
 
-  // The initial disposition of the navigation (before any normalization) that
-  // is currently being controlled by the NavigationHandle. This is set in
-  // `Navigate()` and is used in the LinkCapturingRedirectNavigationThrottle to
-  // determine how to handle redirections if any.
-  WindowOpenDisposition disposition() { return disposition_; }
+  // Information necessary to perform different actions based on multiple
+  // redirects.
+  NavigationCapturingRedirectionInfo redirection_info() {
+    return redirection_info_;
+  }
 
  private:
   NavigationCapturingNavigationHandleUserData(
       content::NavigationHandle& navigation_handle,
-      WindowOpenDisposition disposition);
+      NavigationCapturingRedirectionInfo redirection_info);
 
   friend NavigationHandleUserData;
 
-  WindowOpenDisposition disposition_ = WindowOpenDisposition::UNKNOWN;
+  NavigationCapturingRedirectionInfo redirection_info_;
   NAVIGATION_HANDLE_USER_DATA_KEY_DECL();
 };
 
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index af1a01b..ac83ae0 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1727675811-37c7bb096dfd87a78112e56878ee4ca0053d0501-ef80dc69fbf8ca4cb2725267e33dbcd540bb833d.profdata
+chrome-mac-arm-main-1727704514-48cc36aa2f6acacaf0de3fc7cfc6a378b8ca6c9c-b36250d13e3c66af226ace3b6348fb0f4d3a7eed.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 6f6acf7..6bf43ab 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1727654373-ae54f0c72e9cf0bdcebdcc7826a483052b9b9775-9e533d09cb79991d491e58cfbcebcff288329d39.profdata
+chrome-mac-main-1727697472-c47b3850e96cd549ed93cbabebcd798e502f4e4a-306b38a11005d8104469667f442d189b81d82954.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 49d73931..1853706 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1727654373-b1ab1b6f9ea22fbef6053997ebc91077af345483-9e533d09cb79991d491e58cfbcebcff288329d39.profdata
+chrome-win32-main-1727686732-567e49f9cdf2439fe4f3a73c25130f657dba6878-9760916a5ae2bbe4c7a3e7eb761d9fa108bed338.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 12305a96..9edde3659 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1727654373-0931e6b390b6a8ff7a4e0b41e90d783dd8349786-9e533d09cb79991d491e58cfbcebcff288329d39.profdata
+chrome-win64-main-1727686732-a38dd97daf1f235352b3d4a9687fb9d9982d7280-9760916a5ae2bbe4c7a3e7eb761d9fa108bed338.profdata
diff --git a/chrome/installer/mac/keystone_install.sh b/chrome/installer/mac/keystone_install.sh
index b40d6de..9d7d5934 100755
--- a/chrome/installer/mac/keystone_install.sh
+++ b/chrome/installer/mac/keystone_install.sh
@@ -606,10 +606,13 @@
   if [[ ${EUID} -eq 0 ]]; then
     readonly RSYNC_FLAGS="--ignore-times --links --perms --recursive --times"
   else
-    # When non-root, omit dir times, since rsync can't update them if the
-    # directories are owned by a different user.
+    # When non-root, omit link and dir times, since rsync can't update them if
+    # the directories are owned by a different user. Also avoid --perms, since
+    # in some cases users can't change the permissions of files owned by other
+    # users.
     readonly RSYNC_FLAGS=\
-"--ignore-times --links --perms --recursive --times --omit-dir-times"
+"--ignore-times --links --no-perms --executability --chmod=u=rwX,go=rX\
+ --recursive"
   fi
 
   # It's difficult to get GOOGLE_CHROME_UPDATER_DEBUG set in the environment
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b31c7d5..2436794 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -197,6 +197,8 @@
     "../browser/tpcd/experiment/mock_experiment_manager.h",
     "../browser/tpcd/support/trial_test_utils.cc",
     "../browser/tpcd/support/trial_test_utils.h",
+    "../test/base/menu_model_test.cc",
+    "../test/base/menu_model_test.h",
     "base/chrome_render_view_host_test_harness.cc",
     "base/chrome_render_view_host_test_harness.h",
     "base/chrome_render_view_test.cc",
@@ -679,6 +681,7 @@
       ":test_support_ui",
       "//chrome/app:test_support",
       "//chrome/browser:test_support",
+      "//chrome/browser/ui/startup:startup_tab",
       "//chrome/browser/ui/views/toolbar",
       "//chrome/browser/web_applications",
       "//components/crx_file",
@@ -2002,6 +2005,7 @@
       "//chrome/browser/ui/safety_hub:test_support",
       "//chrome/browser/ui/signin",
       "//chrome/browser/ui/signin:browser_tests",
+      "//chrome/browser/ui/startup:startup_tab",
       "//chrome/browser/ui/tabs",
       "//chrome/browser/ui/tabs:tab_enums",
       "//chrome/browser/ui/toasts",
@@ -6344,8 +6348,6 @@
     "../renderer/media/flash_embed_rewrite_unittest.cc",
     "../renderer/net/net_error_helper_core_unittest.cc",
     "../renderer/v8_unwinder_unittest.cc",
-    "../test/base/menu_model_test.cc",
-    "../test/base/menu_model_test.h",
   ]
 
   if (build_with_tflite_lib) {
@@ -7844,10 +7846,6 @@
       "../browser/ui/toolbar/app_menu_icon_controller_unittest.cc",
       "../browser/ui/toolbar/app_menu_model_unittest.cc",
       "../browser/ui/toolbar/back_forward_menu_model_unittest.cc",
-      "../browser/ui/toolbar/cast/cast_contextual_menu_unittest.cc",
-      "../browser/ui/toolbar/cast/cast_toolbar_button_controller_unittest.cc",
-      "../browser/ui/toolbar/cast/mock_cast_toolbar_button_controller.cc",
-      "../browser/ui/toolbar/cast/mock_cast_toolbar_button_controller.h",
       "../browser/ui/toolbar/chrome_labs/chrome_labs_model_unittest.cc",
       "../browser/ui/toolbar/chrome_location_bar_model_delegate_unittest.cc",
       "../browser/ui/toolbar/location_bar_model_unittest.cc",
@@ -8003,6 +8001,7 @@
 
     deps += [
       "//chrome/browser/ui/safety_hub",
+      "//chrome/browser/ui/startup:startup_tab",
       "//chrome/browser/ui/webui/settings",
     ]
 
@@ -8216,9 +8215,7 @@
 
       # TODO(crbug.com/369756821): Remove when the modularization of //c/b/ui/global_media_controls happens.
       "//chrome/browser/ui/media_router",
-
-      # TODO(crbug.com/369794212): Remove when modularization of //c/b/ui/toolbar/cast happens.
-      "//chrome/browser/ui/toolbar/cast",
+      "//chrome/browser/ui/toolbar/cast:unit_tests",
       "//chrome/browser/ui/views/bubble",
       "//chrome/browser/ui/webui/cr_components/theme_color_picker",
       "//chrome/browser/ui/webui/discards:mojo_bindings",
@@ -10969,6 +10966,7 @@
       "//chrome/browser/ui/lens:interactive_ui_tests",
       "//chrome/browser/ui/omnibox",
       "//chrome/browser/ui/page_action:icon_type",
+      "//chrome/browser/ui/startup:startup_tab",
       "//chrome/browser/ui/toasts",
       "//chrome/browser/ui/toasts:interactive_ui_tests",
       "//chrome/browser/ui/views/bubble",
diff --git a/chrome/test/data/extensions/api_test/socket/api/background.js b/chrome/test/data/extensions/api_test/socket/api/background.js
index c8bb0a8..4f1bcf4 100644
--- a/chrome/test/data/extensions/api_test/socket/api/background.js
+++ b/chrome/test/data/extensions/api_test/socket/api/background.js
@@ -416,6 +416,48 @@
   }
 };
 
+var testWriteQuota = function() {
+  console.log("calling create, protoocol=", protocol);
+  chrome.socket.create(protocol, {}, onCreate);
+
+  function onCreate(createInfo) {
+    chrome.test.assertTrue(createInfo.socketId > 0, "failed to create socket");
+    socketId = createInfo.socketId;
+    if (protocol == "tcp") {
+      console.log("calling connect");
+      chrome.socket.connect(socketId, address, port, onConnectOrBindComplete);
+    } else {
+      console.log("calling bind");
+      socket.bind(socketId, "0.0.0.0", 0, onConnectOrBindComplete);
+    }
+  }
+
+  function onConnectOrBindComplete(result) {
+    chrome.test.assertEq(0, result, "failed to connect");
+    console.log("Socket connect: result=" + result, chrome.runtime.lastError);
+
+    string2ArrayBuffer(request, function (arrayBuffer) {
+      if (protocol == "tcp") {
+        chrome.socket.write(socketId, arrayBuffer, onComplete);
+      } else {
+        socket.sendTo(socketId, arrayBuffer, address, port,
+                      onComplete);
+      }
+    });
+  }
+
+  function onComplete(result) {
+    console.log("onComplete: result=" + result, chrome.runtime.lastError);
+    if (chrome.runtime.lastError &&
+        chrome.runtime.lastError.message == "Exceeded write quota.") {
+      chrome.test.succeed();
+      return;
+    }
+
+    chrome.test.fail('Write quota not enforced');
+  }
+}
+
 var onMessageReply = function(message) {
   var parts = message.split(":");
   var test_type = parts[0];
@@ -431,6 +473,14 @@
   } else if (test_type == 'multicast') {
     console.log("Running multicast tests");
     chrome.test.runTests([ testMulticast ]);
+  } else if (test_type == 'tcp_write_quota') {
+    console.log("Running TCP write quota tests");
+    protocol = "tcp";
+    chrome.test.runTests([ testWriteQuota ]);
+  } else if (test_type == 'udp_sendTo_quota') {
+    console.log("Running UDP sendTo write quota tests");
+    protocol = "udp";
+    chrome.test.runTests([ testWriteQuota ]);
   } else {
     protocol = test_type;
     if (protocol == "udp") {
diff --git a/chrome/test/data/webui/settings/BUILD.gn b/chrome/test/data/webui/settings/BUILD.gn
index 5fac10b4..2180440b 100644
--- a/chrome/test/data/webui/settings/BUILD.gn
+++ b/chrome/test/data/webui/settings/BUILD.gn
@@ -155,6 +155,8 @@
     "unused_site_permissions_test.ts",
     "unused_site_permissions_interactive_ui_test.ts",
     "zoom_levels_test.ts",
+    "smart_card_readers_page_test.ts",
+    "smart_card_reader_origin_entry_test.ts",
   ]
 
   if (is_chromeos_ash) {
diff --git a/chrome/test/data/webui/settings/settings_browsertest.cc b/chrome/test/data/webui/settings/settings_browsertest.cc
index d771885..2c2eb3fa 100644
--- a/chrome/test/data/webui/settings/settings_browsertest.cc
+++ b/chrome/test/data/webui/settings/settings_browsertest.cc
@@ -467,6 +467,14 @@
   RunTest("settings/settings_slider_test.js", "mocha.run()");
 }
 
+IN_PROC_BROWSER_TEST_F(SettingsTest, SmartCardReadersPage) {
+  RunTest("settings/smart_card_readers_page_test.js", "mocha.run()");
+}
+
+IN_PROC_BROWSER_TEST_F(SettingsTest, SmartCardReaderOriginEntry) {
+  RunTest("settings/smart_card_reader_origin_entry_test.js", "mocha.run()");
+}
+
 IN_PROC_BROWSER_TEST_F(SettingsTest, SpeedPage) {
   RunTest("settings/speed_page_test.js", "mocha.run()");
 }
diff --git a/chrome/test/data/webui/settings/smart_card_reader_origin_entry_test.ts b/chrome/test/data/webui/settings/smart_card_reader_origin_entry_test.ts
new file mode 100644
index 0000000..ca7f364
--- /dev/null
+++ b/chrome/test/data/webui/settings/smart_card_reader_origin_entry_test.ts
@@ -0,0 +1,48 @@
+// 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.
+
+// clang-format off
+import 'chrome://settings/lazy_load.js';
+
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {SmartCardReaderOriginEntryElement} from 'chrome://settings/lazy_load.js';
+import {SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
+import type {CrIconButtonElement} from 'chrome://settings/lazy_load.js';
+import {CrSettingsPrefs} from 'chrome://settings/settings.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+
+import {TestSiteSettingsPrefsBrowserProxy} from './test_site_settings_prefs_browser_proxy.js';
+
+// clang-format on
+suite('FileSystemSettings_EnablePersistentPermissions', function() {
+  let testElement: SmartCardReaderOriginEntryElement;
+  let browserProxy: TestSiteSettingsPrefsBrowserProxy;
+
+  suiteSetup(function() {
+    CrSettingsPrefs.setInitialized();
+  });
+
+  setup(function() {
+    document.body.innerHTML = window.trustedTypes!.emptyHTML;
+    browserProxy = new TestSiteSettingsPrefsBrowserProxy();
+    SiteSettingsPrefsBrowserProxyImpl.setInstance(browserProxy);
+    testElement = document.createElement('smart-card-reader-origin-entry');
+    assertTrue(!!testElement);
+    testElement.smartCardReaderName = 'reader';
+    testElement.origin = 'foo.com';
+    document.body.appendChild(testElement);
+  });
+
+  test('SmartCardReadersPage_IsPopulated', async function() {
+    const buttons = testElement.shadowRoot!.querySelectorAll('#removeOrigin');
+    assertEquals(buttons.length, 1);
+    (buttons[0] as CrIconButtonElement).click();
+    await browserProxy.whenCalled('revokeSmartCardReaderGrant');
+    flush();
+    assertEquals(browserProxy.getCallCount('revokeSmartCardReaderGrant'), 1);
+    const lastArgs = browserProxy.getArgs('revokeSmartCardReaderGrant')[0];
+    assertEquals(lastArgs[0], 'reader');
+    assertEquals(lastArgs[1], 'foo.com');
+  });
+});
diff --git a/chrome/test/data/webui/settings/smart_card_readers_page_test.ts b/chrome/test/data/webui/settings/smart_card_readers_page_test.ts
new file mode 100644
index 0000000..bc86b35
--- /dev/null
+++ b/chrome/test/data/webui/settings/smart_card_readers_page_test.ts
@@ -0,0 +1,102 @@
+// 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.
+
+// clang-format off
+import 'chrome://settings/lazy_load.js';
+
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {SettingsSmartCardReadersPageElement} from 'chrome://settings/lazy_load.js';
+import {SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
+import {CrSettingsPrefs, loadTimeData, routes, resetRouterForTesting} from 'chrome://settings/settings.js';
+import {assertEquals, assertTrue, assertFalse} from 'chrome://webui-test/chai_assert.js';
+
+import {TestSiteSettingsPrefsBrowserProxy} from './test_site_settings_prefs_browser_proxy.js';
+
+// clang-format on
+suite('FileSystemSettings_EnablePersistentPermissions', function() {
+  let testElement: SettingsSmartCardReadersPageElement;
+  let browserProxy: TestSiteSettingsPrefsBrowserProxy;
+
+  suiteSetup(function() {
+    CrSettingsPrefs.setInitialized();
+
+    loadTimeData.overrideValues({
+      enableSmartCardReadersContentSetting: true,
+    });
+    resetRouterForTesting();
+  });
+
+  // Initialize the settings-smart-card-readers-page element.
+  setup(function() {
+    document.body.innerHTML = window.trustedTypes!.emptyHTML;
+    browserProxy = new TestSiteSettingsPrefsBrowserProxy();
+    SiteSettingsPrefsBrowserProxyImpl.setInstance(browserProxy);
+    testElement = document.createElement('settings-smart-card-readers-page');
+    assertTrue(!!testElement);
+    document.body.appendChild(testElement);
+  });
+
+  test('SmartCardReadersPage_IsPopulated', async function() {
+    browserProxy.setSmartCardReaderGrants([
+      {
+        readerName: 'reader',
+        origins: [
+          'foo.com',
+        ],
+      },
+      {
+        readerName: 'reader',
+        origins: [
+          'bar.com',
+        ],
+      },
+      {
+        readerName: 'reader 2',
+        origins: [
+          'bar.com',
+        ],
+      },
+    ]);
+
+    testElement.currentRouteChanged(routes.SITE_SETTINGS_SMART_CARD_READERS);
+    await browserProxy.whenCalled('getSmartCardReaderGrants');
+    flush();
+    const originEntries = testElement.shadowRoot!.querySelectorAll(
+        'smart-card-reader-origin-entry');
+    assertEquals(originEntries.length, 3);
+    assertEquals(browserProxy.getCallCount('getSmartCardReaderGrants'), 1);
+
+    const emptyHeaders =
+        testElement.shadowRoot!.querySelectorAll('#readersNotFound');
+    assertEquals(emptyHeaders.length, 1);
+    assertFalse(emptyHeaders[0]!.checkVisibility());
+
+    const resetButtons =
+        testElement.shadowRoot!.querySelectorAll('#resetButton');
+    assertEquals(resetButtons.length, 1);
+    assertTrue(resetButtons[0]!.checkVisibility());
+  });
+
+  test('SmartCardReadersPage_IsPopulatedEmpty', async function() {
+    browserProxy.setSmartCardReaderGrants([]);
+
+    testElement.currentRouteChanged(routes.SITE_SETTINGS_SMART_CARD_READERS);
+    await browserProxy.whenCalled('getSmartCardReaderGrants');
+    flush();
+    const originEntries = testElement.shadowRoot!.querySelectorAll(
+        'smart-card-reader-origin-entry');
+    assertEquals(originEntries.length, 0);
+    assertEquals(browserProxy.getCallCount('getSmartCardReaderGrants'), 1);
+
+    const emptyHeaders =
+        testElement.shadowRoot!.querySelectorAll('#readersNotFound');
+    assertEquals(emptyHeaders.length, 1);
+    assertTrue(emptyHeaders[0]!.checkVisibility());
+
+    const resetButtons =
+        testElement.shadowRoot!.querySelectorAll('#resetButton');
+    assertEquals(resetButtons.length, 1);
+    assertFalse(resetButtons[0]!.checkVisibility());
+  });
+});
diff --git a/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.ts b/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.ts
index e8ea438..30a2f45 100644
--- a/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.ts
+++ b/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.ts
@@ -5,7 +5,7 @@
 // clang-format off
 import {assert} from 'chrome://resources/js/assert.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
-import type {StorageAccessSiteException, AppProtocolEntry, ChooserType, HandlerEntry, OriginFileSystemGrants, ProtocolEntry, RawChooserException, RawSiteException, RecentSitePermissions, SiteGroup, SiteSettingsPrefsBrowserProxy, ZoomLevelEntry} from 'chrome://settings/lazy_load.js';
+import type {StorageAccessSiteException, AppProtocolEntry, ChooserType, HandlerEntry, OriginFileSystemGrants, SmartCardReaderGrants, ProtocolEntry, RawChooserException, RawSiteException, RecentSitePermissions, SiteGroup, SiteSettingsPrefsBrowserProxy, ZoomLevelEntry} from 'chrome://settings/lazy_load.js';
 import {ContentSetting, ContentSettingsTypes, SiteSettingSource} from 'chrome://settings/lazy_load.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
@@ -33,6 +33,7 @@
   private isPatternValidForType_: boolean = true;
   private recentSitePermissions_: RecentSitePermissions[] = [];
   private fileSystemGrantsList_: OriginFileSystemGrants[] = [];
+  private smartCardReadersGrants_: SmartCardReaderGrants[] = [];
   private storageAccessExceptionList_: StorageAccessSiteException[] = [];
 
   constructor() {
@@ -78,6 +79,9 @@
       'getFileSystemGrants',
       'revokeFileSystemGrant',
       'revokeFileSystemGrants',
+      'getSmartCardReaderGrants',
+      'revokeAllSmartCardReadersGrants',
+      'revokeSmartCardReaderGrant',
     ]);
 
 
@@ -681,4 +685,21 @@
   revokeFileSystemGrants(origin: string): void {
     this.methodCalled('revokeFileSystemGrants', origin);
   }
+
+  setSmartCardReaderGrants(grants: SmartCardReaderGrants[]): void {
+    this.smartCardReadersGrants_ = grants;
+  }
+
+  getSmartCardReaderGrants(): Promise<SmartCardReaderGrants[]> {
+    this.methodCalled('getSmartCardReaderGrants');
+    return Promise.resolve(this.smartCardReadersGrants_);
+  }
+
+  revokeAllSmartCardReadersGrants(): void {
+    this.methodCalled('revokeAllSmartCardReadersGrants');
+  }
+
+  revokeSmartCardReaderGrant(reader: string, origin: string): void {
+    this.methodCalled('revokeSmartCardReaderGrant', reader, origin);
+  }
 }
diff --git a/chrome/test/supervised_user/BUILD.gn b/chrome/test/supervised_user/BUILD.gn
index a13e01dd..3f77f69 100644
--- a/chrome/test/supervised_user/BUILD.gn
+++ b/chrome/test/supervised_user/BUILD.gn
@@ -39,5 +39,9 @@
     "child_account_test_utils.cc",
     "child_account_test_utils.h",
   ]
-  public_deps = [ "//base" ]
+  public_deps = [
+    "//base",
+    "//net:test_support",
+    "//services/network/public/cpp:cpp",
+    ]
 }
diff --git a/chrome/test/supervised_user/api_mock_setup_mixin.cc b/chrome/test/supervised_user/api_mock_setup_mixin.cc
index f4c0e21..4ae3964 100644
--- a/chrome/test/supervised_user/api_mock_setup_mixin.cc
+++ b/chrome/test/supervised_user/api_mock_setup_mixin.cc
@@ -4,17 +4,18 @@
 
 #include "chrome/test/supervised_user/api_mock_setup_mixin.h"
 
+#include <map>
 #include <string>
 #include <string_view>
 
 #include "base/command_line.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
-#include "base/strings/strcat.h"
 #include "base/test/bind.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/test/supervised_user/child_account_test_utils.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 #include "components/supervised_user/core/browser/proto/kidsmanagement_messages.pb.h"
@@ -22,7 +23,6 @@
 #include "components/supervised_user/core/common/supervised_user_constants.h"
 #include "components/supervised_user/test_support/kids_management_api_server_mock.h"
 #include "net/dns/mock_host_resolver.h"
-#include "services/network/public/cpp/network_switches.h"
 
 namespace supervised_user {
 
@@ -82,36 +82,19 @@
 
 void KidsManagementApiMockSetupMixin::SetUpCommandLine(
     base::CommandLine* command_line) {
-  CHECK(embedded_test_server_.Started());
-
-  std::string target = embedded_test_server_.host_port_pair().ToString();
-
-  // TODO(b/300129765): Remove manual deduplication.
-  // Workaround on problem where supervision_mixin has two submixins, where both
-  // of them need to alter host resolver rules. For some reason,
-  // host_resolver()->AddRule() is innefective, and
-  // base::CommandLine::AppendSwitch only respects the ultimate value.
-  std::string previous_switch_value =
-      command_line->GetSwitchValueASCII(network::switches::kHostResolverRules);
-  if (!previous_switch_value.empty()) {
-    base::StrAppend(&previous_switch_value, {","});
-  }
-
-  command_line->AppendSwitchASCII(
-      network::switches::kHostResolverRules,
-      base::JoinString({previous_switch_value, "MAP",
-                        kKidsManagementServiceEndpoint, target},
-                       " "));
-
-  LOG(INFO) << "Kids management api server is listening on " << target << ".";
-  LOG(INFO) << "\tAll requests to [" << kKidsManagementServiceEndpoint
-            << "] will be mapped to it.";
+  AddHostResolverRule(command_line, kKidsManagementServiceEndpoint,
+                      embedded_test_server_);
 }
 
 void KidsManagementApiMockSetupMixin::SetUpOnMainThread() {
   embedded_test_server_.StartAcceptingConnections();
-  WaitUntilReady(test_base_, prefs::kSupervisedUserCustodianName,
-                 kSimpsonFamily.at(kidsmanagement::HEAD_OF_HOUSEHOLD));
+  CHECK_EQ(kSimpsonFamily.count(kidsmanagement::HEAD_OF_HOUSEHOLD),
+           std::size_t(1))
+      << "Expected single head of household";
+
+  WaitUntilReady(
+      test_base_, prefs::kSupervisedUserCustodianName,
+      kSimpsonFamily.find(kidsmanagement::HEAD_OF_HOUSEHOLD)->second);
 }
 
 void KidsManagementApiMockSetupMixin::TearDownOnMainThread() {
diff --git a/chrome/test/supervised_user/child_account_test_utils.cc b/chrome/test/supervised_user/child_account_test_utils.cc
index c276726..ee5ad85 100644
--- a/chrome/test/supervised_user/child_account_test_utils.cc
+++ b/chrome/test/supervised_user/child_account_test_utils.cc
@@ -4,8 +4,14 @@
 
 #include "chrome/test/supervised_user/child_account_test_utils.h"
 
+#include <string>
+
 #include "base/base64.h"
+#include "base/command_line.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "services/network/public/cpp/network_switches.h"
 
 namespace supervised_user {
 
@@ -14,4 +20,19 @@
   return base::StringPrintf("dummy-header.%s.dummy-signature", encoded.c_str());
 }
 
+void AddHostResolverRule(base::CommandLine* command_line,
+                         std::string_view host,
+                         const net::test_server::EmbeddedTestServer& target) {
+  CHECK(target.Started());
+
+  std::string current_rules =
+      command_line->GetSwitchValueASCII(network::switches::kHostResolverRules);
+  std::string new_rule =
+      base::JoinString({"MAP", host, target.host_port_pair().ToString()}, " ");
+
+  command_line->AppendSwitchASCII(
+      network::switches::kHostResolverRules,
+      base::JoinString({current_rules, new_rule}, ","));
+}
+
 }  // namespace supervised_user
diff --git a/chrome/test/supervised_user/child_account_test_utils.h b/chrome/test/supervised_user/child_account_test_utils.h
index 574cafc..1e03da2 100644
--- a/chrome/test/supervised_user/child_account_test_utils.h
+++ b/chrome/test/supervised_user/child_account_test_utils.h
@@ -7,11 +7,32 @@
 
 #include <string>
 
+namespace base {
+class CommandLine;
+}  // namespace base
+
+namespace net::test_server {
+class EmbeddedTestServer;
+}  // namespace net::test_server
+
 namespace supervised_user {
 
 // Returns a base64-encoded placeholder token for child log-in.
 std::string GetChildAccountOAuthIdToken();
 
+// Append host resolver rule to command line. This should used in preference to
+// MockHostResolver::AddRule, as it ensures that the rules are active before the
+// test's SetUpOnMainThread() method is called.
+//
+// This can be called multiple times, and ensures that the rules are added
+// rather than overwritten. This does not attempt to deduplicate hosts which are
+// mapped multiple times.
+//
+// TODO(crbug.com/370393748): move this method somewhere more widely usable.
+void AddHostResolverRule(base::CommandLine* command_line,
+                         std::string_view host,
+                         const net::test_server::EmbeddedTestServer& target);
+
 }  // namespace supervised_user
 
 #endif  // CHROME_TEST_SUPERVISED_USER_CHILD_ACCOUNT_TEST_UTILS_H_
diff --git a/chrome/test/supervised_user/embedded_test_server_setup_mixin.cc b/chrome/test/supervised_user/embedded_test_server_setup_mixin.cc
index 0764827..1ddb243 100644
--- a/chrome/test/supervised_user/embedded_test_server_setup_mixin.cc
+++ b/chrome/test/supervised_user/embedded_test_server_setup_mixin.cc
@@ -15,6 +15,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
+#include "chrome/test/supervised_user/child_account_test_utils.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -24,10 +25,6 @@
 
 namespace {
 
-std::string CreateResolverRule(std::string_view host, std::string_view target) {
-  return base::StrCat({"MAP ", host, " ", target});
-}
-
 std::vector<std::string> SplitHostList(std::string_view host_list) {
   return base::SplitString(host_list, ",", base::TRIM_WHITESPACE,
                            base::SPLIT_WANT_NONEMPTY);
@@ -56,21 +53,13 @@
 
 void EmbeddedTestServerSetupMixin::SetUpCommandLine(
     base::CommandLine* command_line) {
-  CHECK(embedded_test_server_->Started());
+  base::ranges::for_each(
+      resolver_rules_map_host_list_, [&](const std::string& host) {
+        AddHostResolverRule(command_line, host, *embedded_test_server_);
+      });
 
-  std::string target = embedded_test_server_->host_port_pair().ToString();
-  std::vector<std::string> resolver_rules(resolver_rules_map_host_list_.size());
-
-  base::ranges::transform(resolver_rules_map_host_list_, resolver_rules.begin(),
-                          [&](const std::string& host) -> std::string {
-                            return CreateResolverRule(host, target);
-                          });
-
-  command_line->AppendSwitchASCII(
-      network::switches::kHostResolverRules,
-      base::JoinString(base::span<std::string>(resolver_rules), ", "));
-
-  LOG(INFO) << "Embedded test server is listening on " << target << ".";
+  LOG(INFO) << "Embedded test server is listening on "
+            << embedded_test_server_->host_port_pair().ToString() << ".";
   LOG(INFO) << "Following hosts will be mapped to it: ";
   for (const std::string& host_pattern : resolver_rules_map_host_list_) {
     LOG(INFO) << "\t" << host_pattern;
diff --git a/chrome/test/supervised_user/google_auth_state_waiter_mixin.cc b/chrome/test/supervised_user/google_auth_state_waiter_mixin.cc
index 3384f39..1a9d894 100644
--- a/chrome/test/supervised_user/google_auth_state_waiter_mixin.cc
+++ b/chrome/test/supervised_user/google_auth_state_waiter_mixin.cc
@@ -24,46 +24,30 @@
 GoogleAuthStateWaiterMixin::~GoogleAuthStateWaiterMixin() = default;
 
 void GoogleAuthStateWaiterMixin::SetUpOnMainThread() {
-  // TODO(b/364009851): this currently doesn't work on Windows and some Mac
-  // builders, because the network is not fully initialized by the point
-  // `SetUpOnMainThread()` is called.
-  // As a workaround, on these platforms there is a call to Wait in the test
-  // body.
-#if BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
   ChildAccountService* child_account_service =
       ChildAccountServiceFactory::GetForProfile(
           test_base_->browser()->profile());
-  WaitForGoogleAuthState(child_account_service, expected_auth_state_);
-#else
-  return;
-#endif
-}
-
-// static
-void GoogleAuthStateWaiterMixin::WaitForGoogleAuthState(
-    ChildAccountService* child_account_service,
-    ChildAccountService::AuthState expected_auth_state) {
-  CHECK(child_account_service);
 
   // Handle the case where the auth state was already as expected.
-  if (child_account_service->GetGoogleAuthState() == expected_auth_state) {
+  if (child_account_service->GetGoogleAuthState() == expected_auth_state_) {
     return;
   }
 
   // Observe auth state changes.
   base::RunLoop run_loop;
-  base::OnceClosure quit_closure = run_loop.QuitClosure();
   base::CallbackListSubscription subscription =
       child_account_service->ObserveGoogleAuthState(
           base::BindLambdaForTesting([&]() {
             if (child_account_service->GetGoogleAuthState() ==
-                expected_auth_state) {
-              std::move(quit_closure).Run();
+                expected_auth_state_) {
+              run_loop.Quit();
             }
           }));
 
   // Wait for the auth state to change.
   run_loop.Run();
+#endif
 }
 
 }  // namespace supervised_user
diff --git a/chrome/test/supervised_user/google_auth_state_waiter_mixin.h b/chrome/test/supervised_user/google_auth_state_waiter_mixin.h
index e40d999..72a28fc6 100644
--- a/chrome/test/supervised_user/google_auth_state_waiter_mixin.h
+++ b/chrome/test/supervised_user/google_auth_state_waiter_mixin.h
@@ -31,10 +31,6 @@
   // InProcessBrowserTestMixin:
   void SetUpOnMainThread() override;
 
-  static void WaitForGoogleAuthState(
-    ChildAccountService* child_account_service,
-    ChildAccountService::AuthState expected_auth_state);
-
  private:
   raw_ptr<InProcessBrowserTest> test_base_;
   ChildAccountService::AuthState expected_auth_state_;
diff --git a/chrome/test/supervised_user/supervision_mixin.cc b/chrome/test/supervised_user/supervision_mixin.cc
index 1f3a96a2..7ffe276b 100644
--- a/chrome/test/supervised_user/supervision_mixin.cc
+++ b/chrome/test/supervised_user/supervision_mixin.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
+#include "chrome/test/supervised_user/child_account_test_utils.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/base/signin_switches.h"
@@ -33,7 +34,6 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/test/browser_test_utils.h"
 #include "google_apis/gaia/fake_gaia.h"
-#include "net/dns/mock_host_resolver.h"
 
 namespace supervised_user {
 
@@ -85,6 +85,11 @@
 
 SupervisionMixin::~SupervisionMixin() = default;
 
+void SupervisionMixin::SetUpCommandLine(base::CommandLine* command_line) {
+  AddHostResolverRule(command_line, "accounts.google.com",
+                      *fake_gaia_mixin_.gaia_server());
+}
+
 void SupervisionMixin::SetUpInProcessBrowserTestFixture() {
   subscription_ =
       BrowserContextDependencyManager::GetInstance()
@@ -95,7 +100,6 @@
 void SupervisionMixin::SetUpOnMainThread() {
   SetUpIdentityTestEnvironment();
   ConfigureIdentityTestEnvironment();
-  SetUpTestServer();
 }
 
 // static
@@ -110,13 +114,6 @@
   }
 }
 
-void SupervisionMixin::SetUpTestServer() {
-  // By default, browser tests block anything that doesn't go to localhost, so
-  // account.google.com requests would never reach fake GAIA server without
-  // this.
-  test_base_->host_resolver()->AddRule("accounts.google.com", "127.0.0.1");
-}
-
 void SupervisionMixin::SetUpIdentityTestEnvironment() {
   adaptor_ =
       std::make_unique<IdentityTestEnvironmentProfileAdaptor>(GetProfile());
diff --git a/chrome/test/supervised_user/supervision_mixin.h b/chrome/test/supervised_user/supervision_mixin.h
index ba79dba..d80e4748 100644
--- a/chrome/test/supervised_user/supervision_mixin.h
+++ b/chrome/test/supervised_user/supervision_mixin.h
@@ -69,6 +69,7 @@
   ~SupervisionMixin() override;
 
   // InProcessBrowserTestMixin:
+  void SetUpCommandLine(base::CommandLine* command_line) override;
   void SetUpInProcessBrowserTestFixture() override;
   void SetUpOnMainThread() override;
 
diff --git a/clank b/clank
index fae8cf6..16d5088 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit fae8cf6f28d863b3e263c7fd30baed82ea532082
+Subproject commit 16d508806ccde9d81dbeae737909d2e181df48e1
diff --git a/components/autofill/content/renderer/BUILD.gn b/components/autofill/content/renderer/BUILD.gn
index 8ec796a..795a5d6 100644
--- a/components/autofill/content/renderer/BUILD.gn
+++ b/components/autofill/content/renderer/BUILD.gn
@@ -119,6 +119,7 @@
     "a11y_utils_browsertest.cc",
     "autofill_agent_browsertest.cc",
     "autofill_agent_form_interaction_browsertest.cc",
+    "dom_label_browsertest.cc",
     "form_autofill_issues_browsertest.cc",
     "form_autofill_util_browsertest.cc",
     "form_cache_browsertest.cc",
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc
index 8b2c5c3..5116be88 100644
--- a/components/autofill/content/renderer/autofill_agent.cc
+++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -446,77 +446,6 @@
   base::WeakPtrFactory<DeferringAutofillDriver> weak_ptr_factory_{this};
 };
 
-AutofillAgent::FocusStateNotifier::FocusStateNotifier(AutofillAgent* agent)
-    : agent_(CHECK_DEREF(agent)) {}
-
-AutofillAgent::FocusStateNotifier::~FocusStateNotifier() = default;
-
-void AutofillAgent::FocusStateNotifier::FocusedInputChanged(
-    const WebNode& node) {
-  CHECK(node);
-  mojom::FocusedFieldType new_focused_field_type =
-      mojom::FocusedFieldType::kUnknown;
-  FieldRendererId new_focused_field_id = FieldRendererId();
-  if (auto form_control_element = node.DynamicTo<WebFormControlElement>()) {
-    new_focused_field_type = GetFieldType(form_control_element);
-    new_focused_field_id = form_util::GetFieldRendererId(form_control_element);
-  }
-  NotifyIfChanged(new_focused_field_type, new_focused_field_id);
-}
-
-void AutofillAgent::FocusStateNotifier::ResetFocus() {
-  FieldRendererId new_focused_field_id = FieldRendererId();
-  mojom::FocusedFieldType new_focused_field_type =
-      mojom::FocusedFieldType::kUnknown;
-  NotifyIfChanged(new_focused_field_type, new_focused_field_id);
-}
-
-mojom::FocusedFieldType AutofillAgent::FocusStateNotifier::GetFieldType(
-    const WebFormControlElement& node) {
-  auto form_control_type = node.FormControlTypeForAutofill();
-  if (form_control_type == blink::mojom::FormControlType::kTextArea) {
-    return mojom::FocusedFieldType::kFillableTextArea;
-  }
-
-  WebInputElement input_element = node.DynamicTo<WebInputElement>();
-  if (!input_element || !input_element.IsTextField() ||
-      !form_util::IsElementEditable(input_element)) {
-    return mojom::FocusedFieldType::kUnfillableElement;
-  }
-
-  if (form_control_type == blink::mojom::FormControlType::kInputSearch) {
-    return mojom::FocusedFieldType::kFillableSearchField;
-  }
-  if (form_control_type == blink::mojom::FormControlType::kInputPassword) {
-    return mojom::FocusedFieldType::kFillablePasswordField;
-  }
-  if (agent_->password_autofill_agent_->IsUsernameInputField(input_element)) {
-    return mojom::FocusedFieldType::kFillableUsernameField;
-  }
-  if (form_util::IsWebauthnTaggedElement(node)) {
-    return mojom::FocusedFieldType::kFillableWebauthnTaggedField;
-  }
-  return mojom::FocusedFieldType::kFillableNonSearchField;
-}
-
-void AutofillAgent::FocusStateNotifier::NotifyIfChanged(
-    mojom::FocusedFieldType new_focused_field_type,
-    FieldRendererId new_focused_field_id) {
-  // Forward the request if the focused field is different from the previous
-  // one.
-  if (focused_field_id_ == new_focused_field_id &&
-      focused_field_type_ == new_focused_field_type) {
-    return;
-  }
-
-  // TODO(crbug.com/40260756): Move FocusedInputChanged to AutofillDriver.
-  agent_->GetPasswordManagerDriver().FocusedInputChanged(
-      new_focused_field_id, new_focused_field_type);
-
-  focused_field_type_ = new_focused_field_type;
-  focused_field_id_ = new_focused_field_id;
-}
-
 AutofillAgent::AutofillAgent(
     content::RenderFrame* render_frame,
     Config config,
@@ -852,7 +781,8 @@
   if (auto* autofill_driver = unsafe_autofill_driver()) {
     autofill_driver->DidEndTextFieldEditing();
   }
-  focus_state_notifier_.ResetFocus();
+  password_autofill_agent_->FocusedElementChangedWithCustomSemantics(
+      WebElement(), /*pass_key=*/{});
   if (password_generation_agent_) {
     password_generation_agent_->DidEndTextFieldEditing(element);
   }
@@ -1622,7 +1552,14 @@
     return;
   }
   if (WebElement focused_element = document.FocusedElement()) {
-    SendFocusedInputChangedNotificationToBrowser(focused_element);
+    password_autofill_agent_->FocusedElementChangedWithCustomSemantics(
+        focused_element,
+        /*pass_key=*/{});
+    if (auto input_element = focused_element.DynamicTo<WebInputElement>()) {
+      field_data_manager_->UpdateFieldDataMapWithNullValue(
+          form_util::GetFieldRendererId(input_element),
+          FieldPropertiesFlags::kHadFocus);
+    }
   }
 
   if (!config_.uses_keyboard_accessory_for_suggestions &&
@@ -1783,16 +1720,6 @@
   }
 }
 
-void AutofillAgent::SendFocusedInputChangedNotificationToBrowser(
-    const WebElement& node) {
-  focus_state_notifier_.FocusedInputChanged(node);
-  if (auto input_element = node.DynamicTo<WebInputElement>()) {
-    field_data_manager_->UpdateFieldDataMapWithNullValue(
-        form_util::GetFieldRendererId(input_element),
-        FieldPropertiesFlags::kHadFocus);
-  }
-}
-
 void AutofillAgent::AjaxSucceeded() {
   form_tracker_->AjaxSucceeded();
 }
@@ -2151,9 +2078,4 @@
   return autofill_driver_.get();
 }
 
-mojom::PasswordManagerDriver& AutofillAgent::GetPasswordManagerDriver() {
-  DCHECK(password_autofill_agent_);
-  return password_autofill_agent_->GetPasswordManagerDriver();
-}
-
 }  // namespace autofill
diff --git a/components/autofill/content/renderer/autofill_agent.h b/components/autofill/content/renderer/autofill_agent.h
index 1f3f00a..2373c16d 100644
--- a/components/autofill/content/renderer/autofill_agent.h
+++ b/components/autofill/content/renderer/autofill_agent.h
@@ -156,7 +156,6 @@
   // unsafe_autofill_driver() is nullptr if unsafe_render_frame() is nullptr and
   // the `autofill_driver_` has not been bound yet.
   mojom::AutofillDriver* unsafe_autofill_driver();
-  mojom::PasswordManagerDriver& GetPasswordManagerDriver();
 
   CallTimerState GetCallTimerState(CallTimerState::CallSite call_site) const;
 
@@ -266,36 +265,6 @@
   class DeferringAutofillDriver;
   friend class AutofillAgentTestApi;
 
-  // This class ensures that the driver will only receive notifications only
-  // when a focused field or its type (FocusedFieldType) change.
-  class FocusStateNotifier {
-   public:
-    // Creates a new notifier that uses the agent which owns it to access the
-    // real driver implementation.
-    explicit FocusStateNotifier(AutofillAgent* agent);
-
-    FocusStateNotifier(const FocusStateNotifier&) = delete;
-    FocusStateNotifier& operator=(const FocusStateNotifier&) = delete;
-
-    ~FocusStateNotifier();
-
-    // Notifies the driver about focusing the node.
-    void FocusedInputChanged(const blink::WebNode& node);
-    // Notifies the password manager driver about removing the focus from the
-    // currently focused node (with no setting it to a new one).
-    void ResetFocus();
-
-    mojom::FocusedFieldType GetFieldType(
-        const blink::WebFormControlElement& node);
-    void NotifyIfChanged(mojom::FocusedFieldType new_focused_field_type,
-                         FieldRendererId new_focused_field_id);
-
-    FieldRendererId focused_field_id_;
-    mojom::FocusedFieldType focused_field_type_ =
-        mojom::FocusedFieldType::kUnknown;
-    const raw_ref<AutofillAgent> agent_;
-  };
-
   // The RenderFrame* is nullptr while the AutofillAgent is pending deletion,
   // between OnDestruct() and ~AutofillAgent().
   content::RenderFrame* unsafe_render_frame() const {
@@ -365,8 +334,6 @@
                                    blink::WebDOMEvent event);
 
   void HandleFocusChangeComplete(bool focused_node_was_last_clicked);
-  void SendFocusedInputChangedNotificationToBrowser(
-      const blink::WebElement& node);
 
   void OnTextFieldDidChange(const blink::WebFormControlElement& element);
   void DidChangeScrollOffsetImpl(FieldRendererId element_id);
@@ -548,10 +515,6 @@
   scoped_refptr<FieldDataManager> field_data_manager_ =
       base::MakeRefCounted<FieldDataManager>();
 
-  // This notifier is used to avoid sending redundant messages to the password
-  // manager driver mojo interface.
-  FocusStateNotifier focus_state_notifier_{this};
-
   // State for, and only for, HandleFocusChangeComplete().
   struct Caret {
    private:
diff --git a/components/autofill/content/renderer/dom_label_browsertest.cc b/components/autofill/content/renderer/dom_label_browsertest.cc
new file mode 100644
index 0000000..9fafd5d
--- /dev/null
+++ b/components/autofill/content/renderer/dom_label_browsertest.cc
@@ -0,0 +1,217 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "base/base_paths.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/path_service.h"
+#include "base/strings/string_split.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "components/autofill/content/renderer/form_autofill_util.h"
+#include "components/autofill/core/common/field_data_manager.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "content/public/test/render_view_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_form_element.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+
+#if BUILDFLAG(IS_MAC)
+#include "base/apple/foundation_util.h"
+#endif
+
+namespace autofill {
+
+namespace {
+
+// The `GetTestDataDir()` contains and DOMs in *.html files and the expected
+// labels that Autofill is supposed to find in *.json files of the same name.
+base::FilePath GetTestDataDir() {
+  base::FilePath dir;
+  base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &dir);
+  return dir.AppendASCII("components")
+      .AppendASCII("test")
+      .AppendASCII("data")
+      .AppendASCII("autofill")
+      .AppendASCII("label-doms");
+}
+
+struct TestCase {
+  // Path to an HTML file containing a DOM.
+  base::FilePath dom_path;
+  // Path to a JSON file containing a single list with the expected label of
+  // every field in the DOM (the test setup ensures that extraction order is
+  // consistent across runs).
+  base::FilePath expected_output_path;
+};
+
+// Returns all tests found in `GetTestDataDir()` in consistent order.
+std::vector<TestCase> GetTests() {
+  base::FileEnumerator file_iterator(GetTestDataDir(),
+                                     /*recursive=*/true,
+                                     base::FileEnumerator::FILES);
+  std::vector<TestCase> tests;
+  for (base::FilePath file = file_iterator.Next(); !file.empty();
+       file = file_iterator.Next()) {
+    if (!file.MatchesExtension(FILE_PATH_LITERAL(".html"))) {
+      continue;
+    }
+    tests.push_back({.dom_path = file,
+                     .expected_output_path =
+                         file.ReplaceExtension(FILE_PATH_LITERAL(".json"))});
+  }
+  std::ranges::sort(tests, [](const TestCase& a, const TestCase& b) {
+    return a.dom_path < b.dom_path;
+  });
+#if BUILDFLAG(IS_MAC)
+  base::apple::ClearAmIBundledCache();
+#endif
+  return tests;
+}
+
+std::string LabelSourceToString(FormFieldData::LabelSource source) {
+  switch (source) {
+    case FormFieldData::LabelSource::kUnknown:
+      return "unknown";
+    case FormFieldData::LabelSource::kLabelTag:
+      return "labelTag";
+    case FormFieldData::LabelSource::kPTag:
+      return "pTag";
+    case FormFieldData::LabelSource::kDivTable:
+      return "divTable";
+    case FormFieldData::LabelSource::kTdTag:
+      return "tdTag";
+    case FormFieldData::LabelSource::kDdTag:
+      return "ddTag";
+    case FormFieldData::LabelSource::kLiTag:
+      return "liTag";
+    case FormFieldData::LabelSource::kPlaceHolder:
+      return "placeholder";
+    case FormFieldData::LabelSource::kAriaLabel:
+      return "ariaLabel";
+    case FormFieldData::LabelSource::kCombined:
+      return "combined";
+    case FormFieldData::LabelSource::kValue:
+      return "value";
+    case FormFieldData::LabelSource::kForId:
+      return "forId";
+    case FormFieldData::LabelSource::kForName:
+      return "forName";
+    case FormFieldData::LabelSource::kForShadowHostId:
+      return "forShadowHostId";
+    case FormFieldData::LabelSource::kForShadowHostName:
+      return "forShadowHostName";
+    case FormFieldData::LabelSource::kOverlayingLabel:
+      return "overlayingLabel";
+  }
+}
+
+class DomLabelTest : public content::RenderViewTest,
+                     public testing::WithParamInterface<TestCase> {
+ public:
+  void SetUp() override {
+    content::RenderViewTest::SetUp();
+    // Fail all requests to external resources (e.g. images).
+    CreateFakeURLLoaderFactory();
+  }
+
+  // Returns all forms found on the page in consistent order.
+  std::vector<FormData> ExtractFormDatas() {
+    blink::WebDocument document = GetMainFrame()->GetDocument();
+    // `GetTopLevelForms()` returns forms in DOM order.
+    blink::WebVector<blink::WebFormElement> form_elements =
+        document.GetTopLevelForms();
+    // Add a null WebFormElement to extract unowned fields into a separate form.
+    form_elements.emplace_back();
+    std::vector<FormData> result;
+    for (const blink::WebFormElement& form_element : form_elements) {
+      // Forms might be too large for Autofill.
+      if (std::optional<FormData> form = form_util::ExtractFormData(
+              document, form_element, *field_data_manager_,
+              CallTimerState{
+                  .call_site = CallTimerState::CallSite::kExtractForm,
+                  .last_autofill_agent_reset = {},
+                  .last_dom_content_loaded = {},
+              })) {
+        result.push_back(form.value());
+      }
+    }
+    return result;
+  }
+
+ private:
+  scoped_refptr<FieldDataManager> field_data_manager_ =
+      base::MakeRefCounted<FieldDataManager>();
+};
+
+TEST_P(DomLabelTest, DataDrivenLabels) {
+  const TestCase& test = GetParam();
+  std::string dom;
+  ASSERT_TRUE(base::ReadFileToString(test.dom_path, &dom));
+  LoadHTML(dom);
+
+  // Aggregate the labels + metadata of all form fields.
+  base::Value::List field_infos;
+  for (const FormData& form : ExtractFormDatas()) {
+    for (const FormFieldData& field : form.fields()) {
+      base::Value::Dict field_info;
+      field_info.Set("name", field.name());
+      field_info.Set("label", field.label());
+      field_info.Set("heuristic", LabelSourceToString(field.label_source()));
+      field_infos.Append(std::move(field_info));
+    }
+  }
+
+  // If no expected output exists, the `field_infos` become the expected output.
+  if (!base::PathExists(test.expected_output_path)) {
+    std::optional<std::string> output =
+        base::WriteJsonWithOptions(field_infos, base::OPTIONS_PRETTY_PRINT);
+    ASSERT_TRUE(output);
+    ASSERT_TRUE(base::WriteFile(test.expected_output_path, *output));
+    return;
+  }
+
+  // Check if the expected output matches `field_infos`.
+  std::string expected_output_content;
+  ASSERT_TRUE(base::ReadFileToString(test.expected_output_path,
+                                     &expected_output_content));
+  std::optional<base::Value> expected_output_json =
+      base::JSONReader::Read(expected_output_content);
+  ASSERT_TRUE(expected_output_json && expected_output_json->is_list());
+  const base::Value::List& expected_field_infos =
+      expected_output_json->GetList();
+  ASSERT_EQ(field_infos.size(), expected_field_infos.size());
+  for (size_t i = 0; i < field_infos.size(); i++) {
+    EXPECT_EQ(field_infos[i], expected_field_infos[i]);
+  }
+}
+
+std::string GenerateTestName(const testing::TestParamInfo<TestCase>& info) {
+  std::string name =
+      info.param.dom_path.BaseName().RemoveExtension().MaybeAsASCII();
+  std::ranges::replace_if(name, [](char c) { return !std::isalnum(c); }, '_');
+  return name;
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         DomLabelTest,
+                         testing::ValuesIn(GetTests()),
+                         GenerateTestName);
+
+}  // namespace
+
+}  // namespace autofill
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index 2c22eed..fc591c1 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "base/check.h"
+#include "base/check_deref.h"
 #include "base/containers/contains.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
@@ -686,20 +687,75 @@
   base::WeakPtrFactory<DeferringPasswordManagerDriver> weak_ptr_factory_{this};
 };
 
-////////////////////////////////////////////////////////////////////////////////
-// PasswordAutofillAgent, public:
+PasswordAutofillAgent::FocusStateNotifier::FocusStateNotifier(
+    PasswordAutofillAgent* agent)
+    : agent_(CHECK_DEREF(agent)) {}
+
+PasswordAutofillAgent::FocusStateNotifier::~FocusStateNotifier() = default;
+
+void PasswordAutofillAgent::FocusStateNotifier::FocusedElementChanged(
+    const WebElement& element) {
+  mojom::FocusedFieldType new_focused_field_type =
+      mojom::FocusedFieldType::kUnknown;
+  FieldRendererId new_focused_field_id = FieldRendererId();
+  if (auto form_control_element = element.DynamicTo<WebFormControlElement>()) {
+    new_focused_field_type = GetFieldType(form_control_element);
+    new_focused_field_id = form_util::GetFieldRendererId(form_control_element);
+  }
+  NotifyIfChanged(new_focused_field_type, new_focused_field_id);
+}
+
+mojom::FocusedFieldType PasswordAutofillAgent::FocusStateNotifier::GetFieldType(
+    const WebFormControlElement& node) {
+  auto form_control_type = node.FormControlTypeForAutofill();
+  if (form_control_type == blink::mojom::FormControlType::kTextArea) {
+    return mojom::FocusedFieldType::kFillableTextArea;
+  }
+
+  WebInputElement input_element = node.DynamicTo<WebInputElement>();
+  if (!input_element || !input_element.IsTextField() ||
+      !form_util::IsElementEditable(input_element)) {
+    return mojom::FocusedFieldType::kUnfillableElement;
+  }
+
+  if (form_control_type == blink::mojom::FormControlType::kInputSearch) {
+    return mojom::FocusedFieldType::kFillableSearchField;
+  }
+  if (form_control_type == blink::mojom::FormControlType::kInputPassword) {
+    return mojom::FocusedFieldType::kFillablePasswordField;
+  }
+  if (agent_->IsUsernameInputField(input_element)) {
+    return mojom::FocusedFieldType::kFillableUsernameField;
+  }
+  if (form_util::IsWebauthnTaggedElement(node)) {
+    return mojom::FocusedFieldType::kFillableWebauthnTaggedField;
+  }
+  return mojom::FocusedFieldType::kFillableNonSearchField;
+}
+
+void PasswordAutofillAgent::FocusStateNotifier::NotifyIfChanged(
+    mojom::FocusedFieldType new_focused_field_type,
+    FieldRendererId new_focused_field_id) {
+  // Forward the request if the focused field is different from the previous
+  // one.
+  if (focused_field_id_ == new_focused_field_id &&
+      focused_field_type_ == new_focused_field_type) {
+    return;
+  }
+
+  agent_->GetPasswordManagerDriver().FocusedInputChanged(
+      new_focused_field_id, new_focused_field_type);
+
+  focused_field_type_ = new_focused_field_type;
+  focused_field_id_ = new_focused_field_id;
+}
 
 PasswordAutofillAgent::PasswordAutofillAgent(
     content::RenderFrame* render_frame,
     blink::AssociatedInterfaceRegistry* registry,
     EnableHeavyFormDataScraping enable_heavy_form_data_scraping)
     : content::RenderFrameObserver(render_frame),
-      enable_heavy_form_data_scraping_(enable_heavy_form_data_scraping),
-      last_supplied_password_info_iter_(web_input_to_password_info_.end()),
-      logging_state_active_(false),
-      sent_request_to_store_(false),
-      checked_safe_browsing_reputation_(false),
-      password_generation_agent_(nullptr) {
+      enable_heavy_form_data_scraping_(enable_heavy_form_data_scraping) {
   registry->AddInterface<mojom::PasswordAutofillAgent>(base::BindRepeating(
       &PasswordAutofillAgent::BindPendingReceiver, base::Unretained(this)));
 }
@@ -1101,8 +1157,7 @@
 
     auto iter = web_input_to_password_info_.find(FieldRef(element));
     if (iter == web_input_to_password_info_.end()) {
-      PasswordToLoginMap::const_iterator password_iter =
-          password_to_username_.find(FieldRef(element));
+      auto password_iter = password_to_username_.find(FieldRef(element));
       if (password_iter == password_to_username_.end()) {
         if (!use_fallback_data || web_input_to_password_info_.empty()) {
           return false;
@@ -1708,9 +1763,6 @@
     NotifyPasswordManagerAboutClearedForm(form);
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// PasswordAutofillAgent, private:
-
 bool PasswordAutofillAgent::ShowSuggestionsForDomain(
     const WebInputElement& element,
     AutofillSuggestionTriggerSource trigger_source) {
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h
index 4ad8d87a..191e23a 100644
--- a/components/autofill/content/renderer/password_autofill_agent.h
+++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -276,9 +276,55 @@
 
   AutofillAgent& autofill_agent() { return *autofill_agent_; }
 
+  // Notifies the driver about focusing the node.
+  //
+  // If `element` is null, notifies the password manager driver about removing
+  // the focus from the currently focused node (with no setting it to a new
+  // one).
+  //
+  // TODO: crbug.com/370301890 - Fire this in
+  // RenderFrameObserver::FocusedElementChanged() and remove the plumbing from
+  // AutofillAgent?
+  void FocusedElementChangedWithCustomSemantics(
+      const blink::WebElement& element,
+      base::PassKey<AutofillAgent> pass_key) {
+    focus_state_notifier_.FocusedElementChanged(element);
+  }
+
  private:
   class DeferringPasswordManagerDriver;
 
+  // This class ensures that the driver will only receive notifications only
+  // when a focused field or its type (FocusedFieldType) change.
+  class FocusStateNotifier {
+   public:
+    // Creates a new notifier that uses the agent which owns it to access the
+    // real driver implementation.
+    explicit FocusStateNotifier(PasswordAutofillAgent* agent);
+
+    FocusStateNotifier(const FocusStateNotifier&) = delete;
+    FocusStateNotifier& operator=(const FocusStateNotifier&) = delete;
+
+    ~FocusStateNotifier();
+
+    // Notifies the driver about focusing the node.
+    void FocusedElementChanged(const blink::WebElement& element);
+
+    // Notifies the password manager driver about removing the focus from the
+    // currently focused node (with no setting it to a new one).
+    void ResetFocus();
+
+    mojom::FocusedFieldType GetFieldType(
+        const blink::WebFormControlElement& node);
+    void NotifyIfChanged(mojom::FocusedFieldType new_focused_field_type,
+                         FieldRendererId new_focused_field_id);
+
+    FieldRendererId focused_field_id_;
+    mojom::FocusedFieldType focused_field_type_ =
+        mojom::FocusedFieldType::kUnknown;
+    const raw_ref<PasswordAutofillAgent> agent_;
+  };
+
   // Enumeration representing possible keyboard replacing surface states. A
   // keyboard replacing surface can be either Touch To Fill UI or Android
   // Credential Manager UI. This is used to make sure that keyboard replacing
@@ -297,8 +343,6 @@
     // The user accepted a suggestion from a dropdown on a password field.
     bool password_field_suggestion_was_accepted = false;
   };
-  using WebInputToPasswordInfoMap = std::map<FieldRef, PasswordInfo>;
-  using PasswordToLoginMap = std::map<FieldRef, FieldRef>;
 
   // Stores information about form field structure.
   struct FormFieldInfo {
@@ -547,11 +591,18 @@
 
   // A map from WebInput elements to `PasswordInfo` for all elements that
   // password manager has fill information for.
-  WebInputToPasswordInfoMap web_input_to_password_info_;
+  //
+  // After any mutation, `last_supplied_password_info_iter_` must be updated.
+  std::map<FieldRef, PasswordInfo> web_input_to_password_info_;
+
   // A (sort-of) reverse map to `web_input_to_password_info_`.
-  PasswordToLoginMap password_to_username_;
+  std::map<FieldRef, FieldRef> password_to_username_;
+
   // The chronologically last insertion into `web_input_to_password_info_`.
-  WebInputToPasswordInfoMap::iterator last_supplied_password_info_iter_;
+  // This iterator always points to `web_input_to_password_info_`.
+  std::map<FieldRef, PasswordInfo>::iterator last_supplied_password_info_iter_ =
+      web_input_to_password_info_.end();
+
   // Set of fields that are reliably identified as non-credential fields.
   base::flat_set<FieldRendererId> suggestion_banned_fields_;
 
@@ -560,20 +611,19 @@
   PasswordValueGatekeeper gatekeeper_;
 
   // True indicates that user debug information should be logged.
-  bool logging_state_active_;
+  bool logging_state_active_ = false;
 
   std::vector<PreviewInfo> previewed_elements_;
 
   // True indicates that a request for credentials has been sent to the store.
-  bool sent_request_to_store_;
+  bool sent_request_to_store_ = false;
 
   // True indicates that a safe browsing reputation check has been triggered.
-  bool checked_safe_browsing_reputation_;
+  bool checked_safe_browsing_reputation_ = false;
 
   raw_ptr<AutofillAgent> autofill_agent_ = nullptr;
 
-  raw_ptr<PasswordGenerationAgent>
-      password_generation_agent_;  // Weak reference.
+  raw_ptr<PasswordGenerationAgent> password_generation_agent_ = nullptr;
 
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
   PagePasswordsAnalyser page_passwords_analyser_;
@@ -621,6 +671,10 @@
   // during their lifetime.
   std::map<FormRendererId, size_t> times_received_fill_data_;
 
+  // This notifier is used to avoid sending redundant messages to the password
+  // manager driver mojo interface.
+  FocusStateNotifier focus_state_notifier_{this};
+
 #if BUILDFLAG(IS_ANDROID)
   // Current state of the keyboard replacing surface. This is reset during
   // CleanupOnDocumentShutdown.
diff --git a/components/autofill/core/browser/autofill_plus_address_delegate.h b/components/autofill/core/browser/autofill_plus_address_delegate.h
index bee1b51..410b370 100644
--- a/components/autofill/core/browser/autofill_plus_address_delegate.h
+++ b/components/autofill/core/browser/autofill_plus_address_delegate.h
@@ -35,9 +35,6 @@
 // `AutofillClient`.
 class AutofillPlusAddressDelegate {
  public:
-  // Callback to return the list of plus address suggestions.
-  using GetSuggestionsCallback =
-      base::OnceCallback<void(std::vector<Suggestion>)>;
   // Describes interactions with Autofill suggestions for plus addresses.
   // The values are persisted to metrics, do not change them.
   enum class SuggestionEvent {
@@ -80,18 +77,19 @@
       const url::Origin& origin,
       base::OnceCallback<void(std::vector<std::string>)> callback) = 0;
 
-  // Returns the suggestions to show for the given origin and
-  // `focused_field_value`. If `trigger_source` indicates that this is a manual
-  // fallback (e.g. the suggestions were triggered from the context menu on
-  // Desktop), then `focused_field_value` is ignored. Otherwise, only
-  // suggestions whose prefix matches `focused_field_value` are shown.
-  virtual void GetSuggestions(
-      const url::Origin& last_committed_primary_main_frame_origin,
+  // Returns the suggestions to show for the given list of
+  // `plus_addresses`, `origin` and the `focused_field`. If
+  // `trigger_source` indicates that this is a manual fallback (e.g. the
+  // suggestions were triggered from the context menu on Desktop), then
+  // `focused_field` is ignored. Otherwise, only suggestions whose prefix
+  // matches `focused_field` are shown.
+  virtual std::vector<Suggestion> GetSuggestionsFromPlusAddresses(
+      const std::vector<std::string>& plus_addresses,
+      const url::Origin& origin,
       bool is_off_the_record,
       const PasswordFormClassification& focused_form_classification,
       const FormFieldData& focused_field,
-      AutofillSuggestionTriggerSource trigger_source,
-      GetSuggestionsCallback callback) = 0;
+      AutofillSuggestionTriggerSource trigger_source) = 0;
 
   // Returns the "Manage plus addresses..." suggestion which redirects the user
   // to the plus address management page.
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index 5d190c1..8416b38 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -1448,77 +1448,84 @@
           client().GetAutofillPredictionImprovementsDelegate();
       delegate && form_structure && delegate->IsFormEligible(*form_structure)) {
     delegate->HasDataStored(base::BindOnce(
-        &BrowserAutofillManager::
-            GenerateSuggestionsAndMaybeShowAutofillOrPredictionImprovementsUI,
+        &BrowserAutofillManager::GenerateSuggestionsAndMaybeShowUIPhase1,
         weak_ptr_factory_.GetWeakPtr(), form, field, trigger_source,
         base::OwnedRef(context), std::move(callback)));
     return;
   }
 
   // IMPORTANT NOTE: DON'T ADD CODE HERE, but in
-  // `GenerateSuggestionsAndMaybeShowAutofillOrPredictionImprovementsUI()`
-  // instead.
+  // `GenerateSuggestionsAndMaybeShowUIPhase1()` instead.
 
   // If user annotations wasn't checked for readiness above, synchronously move
   // on with generating suggestions and maybe showing the UI.
-  GenerateSuggestionsAndMaybeShowAutofillOrPredictionImprovementsUI(
+  GenerateSuggestionsAndMaybeShowUIPhase1(
       form, field, trigger_source, context, std::move(callback),
       AutofillPredictionImprovementsDelegate::HasData(false));
 }
 
-void BrowserAutofillManager::
-    GenerateSuggestionsAndMaybeShowAutofillOrPredictionImprovementsUI(
-        const FormData& form,
-        const FormFieldData& field,
-        AutofillSuggestionTriggerSource trigger_source,
-        SuggestionsContext& context,
-        OnGenerateSuggestionsCallback callback,
-        AutofillPredictionImprovementsDelegate::HasData has_data) {
+void BrowserAutofillManager::GenerateSuggestionsAndMaybeShowUIPhase1(
+    const FormData& form,
+    const FormFieldData& field,
+    AutofillSuggestionTriggerSource trigger_source,
+    SuggestionsContext& context,
+    OnGenerateSuggestionsCallback callback,
+    AutofillPredictionImprovementsDelegate::HasData
+        has_prediction_improvements_data) {
   FormStructure* form_structure = nullptr;
   AutofillField* autofill_field = nullptr;
-  // We cannot early-return here because GetCachedFormAndField() yields
-  // nullptr even if there it finds a FormStructure but its `autofill_count()`
-  // is 0. In such cases, we still need to offer Autocomplete (if prediction
-  // improvements doesn't take precedence). Therefore, the code below, including
-  // called functions, must handle `form_structure == nullptr` and
-  // `autofill_field == nullptr`.
+  // Note that this function cannot exit early in case GetCachedFormAndField()
+  // yields nullptrs for form_structure and autofill_field. This happens in case
+  // autofill_count() returns 0 (i.e. the number of autofillable fields is 0).
+  // Even if autofill cannot fill the form, Autocomplete gets a chance to fill
+  // the form. Therefore:
+  // * the following code needs to be executed (autocomplete is handled further
+  //   down in the code path)
+  // * the following code needs to gracefully deal with the situation that
+  //   form_structure and autofill_field are null.
   std::ignore =
       GetCachedFormAndField(form, field, &form_structure, &autofill_field);
-  if (AutofillPredictionImprovementsDelegate* delegate =
-          client().GetAutofillPredictionImprovementsDelegate();
-      delegate && has_data) {
-    autofill_metrics::SuggestionRankingContext ranking_context;
-    // `GetAvailableAddressAndCreditCardSuggestions()` handles cases where
-    // `form_structure` and / or `autofill_field` are nullptr's.
-    std::vector<Suggestion> suggestions =
-        GetAvailableAddressAndCreditCardSuggestions(
-            form, form_structure, field, autofill_field, trigger_source,
-            context, ranking_context);
-    if (delegate->MaybeUpdateSuggestions(
-            suggestions, field,
-            /*should_add_trigger_suggestion=*/trigger_source ==
-                    AutofillSuggestionTriggerSource::kPredictionImprovements ||
-                trigger_source == AutofillSuggestionTriggerSource::
-                                      kFormControlElementClicked)) {
-      std::move(callback).Run(/*show_suggestions=*/true, suggestions,
-                              /*ranking_context=*/std::nullopt);
-      return;
-    }
+
+  context.field_is_relevant_for_plus_addresses =
+      IsPlusAddressesManuallyTriggered(trigger_source) ||
+      (!context.should_show_mixed_content_warning &&
+       context.is_autofill_available &&
+       !context.do_not_generate_autofill_suggestions &&
+       context.filling_product == FillingProduct::kAddress && autofill_field &&
+       autofill_field->Type().group() == FieldTypeGroup::kEmail &&
+       client().GetPlusAddressDelegate() &&
+       client().GetPlusAddressDelegate()->IsPlusAddressFillingEnabled(
+           client().GetLastCommittedPrimaryMainFrameOrigin()));
+
+  auto generate_suggestions_and_maybe_show_ui_phase2 = base::BindOnce(
+      &BrowserAutofillManager::GenerateSuggestionsAndMaybeShowUIPhase2,
+      weak_ptr_factory_.GetWeakPtr(), form, form_structure, field,
+      autofill_field, trigger_source, has_prediction_improvements_data,
+      base::OwnedRef(context), std::move(callback));
+
+  if (context.field_is_relevant_for_plus_addresses) {
+    client().GetPlusAddressDelegate()->GetAffiliatedPlusAddresses(
+        client().GetLastCommittedPrimaryMainFrameOrigin(),
+        std::move(generate_suggestions_and_maybe_show_ui_phase2));
+
+    return;
   }
 
-  GenerateSuggestionsAndMaybeShowUI(form, form_structure, field, autofill_field,
-                                    trigger_source, context,
-                                    std::move(callback));
+  std::move(generate_suggestions_and_maybe_show_ui_phase2)
+      .Run(/*plus_addresses=*/{});
 }
 
-void BrowserAutofillManager::GenerateSuggestionsAndMaybeShowUI(
+void BrowserAutofillManager::GenerateSuggestionsAndMaybeShowUIPhase2(
     const FormData& form,
     const FormStructure* form_structure,
     const FormFieldData& field,
     AutofillField* autofill_field,
     AutofillSuggestionTriggerSource trigger_source,
+    AutofillPredictionImprovementsDelegate::HasData
+        has_prediction_improvements_data,
     SuggestionsContext& context,
-    OnGenerateSuggestionsCallback callback) {
+    OnGenerateSuggestionsCallback callback,
+    std::vector<std::string> plus_addresses) {
   autofill_metrics::SuggestionRankingContext ranking_context;
   std::vector<Suggestion> suggestions =
       GetAvailableAddressAndCreditCardSuggestions(
@@ -1536,6 +1543,22 @@
     return;
   }
 
+  if (AutofillPredictionImprovementsDelegate* delegate =
+          client().GetAutofillPredictionImprovementsDelegate();
+      delegate && has_prediction_improvements_data) {
+    bool suggestions_were_updated = delegate->MaybeUpdateSuggestions(
+        suggestions, field,
+        /*should_add_trigger_suggestion=*/trigger_source ==
+                AutofillSuggestionTriggerSource::kPredictionImprovements ||
+            trigger_source ==
+                AutofillSuggestionTriggerSource::kFormControlElementClicked);
+    if (suggestions_were_updated) {
+      std::move(callback).Run(/*show_suggestions=*/true, suggestions,
+                              /*ranking_context=*/std::nullopt);
+      return;
+    }
+  }
+
   const bool form_element_was_clicked =
       trigger_source ==
       AutofillSuggestionTriggerSource::kFormControlElementClicked;
@@ -1569,19 +1592,11 @@
     return;
   }
 
-  const bool field_is_relevant_for_plus_addresses =
-      (!context.should_show_mixed_content_warning &&
-       context.is_autofill_available &&
-       !context.do_not_generate_autofill_suggestions &&
-       context.filling_product == FillingProduct::kAddress && autofill_field &&
-       autofill_field->Type().group() == FieldTypeGroup::kEmail &&
-       client().GetPlusAddressDelegate());
-
   // Only offer plus address suggestions together with address suggestions if
-  // these exist. Otherwise, plus address suggestions will be queried and shown
-  // alongside single field form fill suggestions.
+  // these exist. Otherwise, plus address suggestions will be generated and
+  // shown alongside single field form fill suggestions.
   const bool should_offer_plus_addresses_with_profiles =
-      field_is_relevant_for_plus_addresses && !suggestions.empty();
+      context.field_is_relevant_for_plus_addresses && !suggestions.empty();
 
   // Try to show plus address suggestions. If the user specifically requested
   // plus addresses, disregard any other requirements (like having profile
@@ -1597,14 +1612,16 @@
             ? AutofillPlusAddressDelegate::SuggestionContext::kManualFallback
             : AutofillPlusAddressDelegate::SuggestionContext::
                   kAutofillProfileOnEmailField;
-    client().GetPlusAddressDelegate()->GetSuggestions(
-        client().GetLastCommittedPrimaryMainFrameOrigin(),
-        client().IsOffTheRecord(), password_form_classification, field,
-        trigger_source,
-        base::BindOnce(&BrowserAutofillManager::OnGetPlusAddressSuggestions,
-                       weak_ptr_factory_.GetWeakPtr(), suggestions_context,
-                       password_form_classification.type, form, field,
-                       std::move(suggestions), std::move(callback)));
+    std::vector<Suggestion> plus_address_suggestions =
+        client().GetPlusAddressDelegate()->GetSuggestionsFromPlusAddresses(
+            plus_addresses, client().GetLastCommittedPrimaryMainFrameOrigin(),
+            client().IsOffTheRecord(), password_form_classification, field,
+            trigger_source);
+
+    MixPlusAddressAndAddressSuggestions(
+        std::move(plus_address_suggestions), std::move(suggestions),
+        suggestions_context, password_form_classification.type, form, field,
+        std::move(callback));
 
     return;
   }
@@ -1655,7 +1672,7 @@
 
   const size_t barrier_calls =
       static_cast<size_t>(should_offer_single_field_form_fill) +
-      static_cast<size_t>(field_is_relevant_for_plus_addresses);
+      static_cast<size_t>(context.field_is_relevant_for_plus_addresses);
   if (barrier_calls == 0) {
     std::move(callback).Run(/*show_suggestions=*/true, std::move(suggestions),
                             std::nullopt);
@@ -1667,6 +1684,8 @@
                                       field.global_id());
   // The barrier callback bundles requests to generate suggestions for plus
   // addresses and single field form fill suggestions.
+  // TODO(crbug.com/324557053): Remove the BarrierCallback as one of the
+  // branches is not async anymore.
   auto barrier_callback = base::BarrierCallback<std::vector<Suggestion>>(
       barrier_calls,
       base::BindOnce(
@@ -1676,11 +1695,13 @@
           AutofillPlusAddressDelegate::SuggestionContext::kAutocomplete,
           password_form_classification.type, form, field, std::move(callback)));
 
-  if (field_is_relevant_for_plus_addresses) {
-    client().GetPlusAddressDelegate()->GetSuggestions(
-        client().GetLastCommittedPrimaryMainFrameOrigin(),
-        client().IsOffTheRecord(), password_form_classification, field,
-        trigger_source, barrier_callback);
+  if (context.field_is_relevant_for_plus_addresses) {
+    std::vector<Suggestion> plus_address_suggestions =
+        client().GetPlusAddressDelegate()->GetSuggestionsFromPlusAddresses(
+            plus_addresses, client().GetLastCommittedPrimaryMainFrameOrigin(),
+            client().IsOffTheRecord(), password_form_classification, field,
+            trigger_source);
+    barrier_callback.Run(std::move(plus_address_suggestions));
   }
 
   if (should_offer_single_field_form_fill) {
@@ -1822,15 +1843,15 @@
   }
 }
 
-void BrowserAutofillManager::OnGetPlusAddressSuggestions(
+void BrowserAutofillManager::MixPlusAddressAndAddressSuggestions(
+    std::vector<Suggestion> plus_address_suggestions,
+    std::vector<Suggestion> address_suggestions,
     AutofillPlusAddressDelegate::SuggestionContext suggestions_context,
     PasswordFormClassification::Type password_form_type,
     const FormData& form,
     const FormFieldData& field,
-    std::vector<Suggestion> address_suggestions,
-    OnGenerateSuggestionsCallback callback,
-    std::vector<Suggestion> suggestions) {
-  if (suggestions.empty()) {
+    OnGenerateSuggestionsCallback callback) {
+  if (plus_address_suggestions.empty()) {
     std::move(callback).Run(/*show_suggestions=*/true,
                             std::move(address_suggestions), std::nullopt);
     return;
@@ -1838,18 +1859,20 @@
 
   client().GetPlusAddressDelegate()->OnPlusAddressSuggestionShown(
       *this, form.global_id(), field.global_id(), suggestions_context,
-      password_form_type, suggestions[0].type);
+      password_form_type, plus_address_suggestions[0].type);
   if (address_suggestions.empty()) {
-      suggestions.emplace_back(SuggestionType::kSeparator);
-      suggestions.push_back(
-          client().GetPlusAddressDelegate()->GetManagePlusAddressSuggestion());
+    plus_address_suggestions.emplace_back(SuggestionType::kSeparator);
+    plus_address_suggestions.push_back(
+        client().GetPlusAddressDelegate()->GetManagePlusAddressSuggestion());
   }
-  suggestions.insert(suggestions.cend(),
-                     std::make_move_iterator(address_suggestions.begin()),
-                     std::make_move_iterator(address_suggestions.end()));
+  // Mix both types of suggestions.
+  plus_address_suggestions.insert(
+      plus_address_suggestions.cend(),
+      std::make_move_iterator(address_suggestions.begin()),
+      std::make_move_iterator(address_suggestions.end()));
 
-  std::move(callback).Run(/*show_suggestions=*/true, std::move(suggestions),
-                          std::nullopt);
+  std::move(callback).Run(/*show_suggestions=*/true,
+                          std::move(plus_address_suggestions), std::nullopt);
 }
 
 void BrowserAutofillManager::AuthenticateThenFillCreditCardForm(
diff --git a/components/autofill/core/browser/browser_autofill_manager.h b/components/autofill/core/browser/browser_autofill_manager.h
index 4dde0916..aeafce0 100644
--- a/components/autofill/core/browser/browser_autofill_manager.h
+++ b/components/autofill/core/browser/browser_autofill_manager.h
@@ -99,7 +99,7 @@
 // forms. One per frame; owned by the AutofillDriver.
 class BrowserAutofillManager : public AutofillManager {
  public:
-  // Triggered when `GenerateSuggestionsAndMaybeShowUI` is complete.
+  // Triggered when `GenerateSuggestionsAndMaybeShowUIPhase2` is complete.
   // `show_suggestions` indicates whether or not the list of `suggestions`
   // should be displayed (via the `external_delegate_`). `ranking_context`
   // contains information regarding the ranking of suggestions and is used for
@@ -618,43 +618,41 @@
       autofill_metrics::SuggestionRankingContext& ranking_context);
 
   // Generates and prioritizes different kinds of suggestions and
-  // suggestion surfaces accordingly (e.g. Fast Checkout,
-  // SingleFieldFormFiller(s), address and credit card popups). Suggestion flows
-  // that handle their own UI flow (e.g. FastCheckout, TTF,
-  // SingleFieldFormFiller) are triggered from within this function. Other flows
-  // that rely on the `external_delegate_` to show their suggestions, pass the
-  // suggestions list to the delegate on `OnGenerateSuggestionsComplete` and
-  // request them to be shown (via `show_suggestions`). Note that the `callback`
-  // is always called regardless of the suggestion surface. The only case when
-  // it's not called is when suggestions are suppressed (See
-  // `ShouldSuppressSuggestions`).
-  void GenerateSuggestionsAndMaybeShowUI(
-      const FormData& form,
-      const FormStructure* form_structure,
-      const FormFieldData& field,
-      AutofillField* autofill_field,
-      AutofillSuggestionTriggerSource trigger_source,
-      SuggestionsContext& context,
-      OnGenerateSuggestionsCallback callback);
-
-  // This method
-  // 1) is an event handler called when the
-  // `AutofillPredictionImprovementsDelegate` is checking user annotations for
-  // readiness.
-  // 2) continues Autofill's regular flow by calling
-  // `GenerateSuggestionsAndMaybeShowUI()` if user annotations isn't ready or
-  // `AutofillPredictionImprovementsDelegate` doesn't exist.
+  // suggestion surfaces accordingly (e.g. Fast Checkout, Prediction
+  // improvements, SingleFieldFormFiller(s), address and credit card popups).
+  // Suggestion flows that handle their own UI flow (e.g. FastCheckout, TTF,
+  // SingleFieldFormFiller) are triggered from within these functions.
   //
-  // To be clear, only one branch for showing suggestions in the UI will be
-  // followed eventually: either the one for Autofill or the one for prediction
-  // improvements (the latter might also show Autofill suggestions).
-  void GenerateSuggestionsAndMaybeShowAutofillOrPredictionImprovementsUI(
+  // This process is split into phrases 1 and 2 to support asynchronous
+  // operations in the middle.
+  //
+  // Phase 2 requires the list of `plus_addresses` as these can influence how
+  // address profile suggestions are shown. Other flows that rely on the
+  // `external_delegate_` to show their suggestions, pass the suggestions list
+  // to the delegate via `OnGenerateSuggestionsComplete` and request them to be
+  // shown (via `show_suggestions`). Note that the `callback` is almost always
+  // called, regardless of the suggestion surface. The only case when it's not
+  // called is when suggestions are suppressed (See
+  // `ShouldSuppressSuggestions`).
+  void GenerateSuggestionsAndMaybeShowUIPhase1(
       const FormData& form,
       const FormFieldData& field,
       AutofillSuggestionTriggerSource trigger_source,
       SuggestionsContext& context,
       OnGenerateSuggestionsCallback callback,
-      AutofillPredictionImprovementsDelegate::HasData has_data);
+      AutofillPredictionImprovementsDelegate::HasData
+          has_prediction_improvements_data);
+  void GenerateSuggestionsAndMaybeShowUIPhase2(
+      const FormData& form,
+      const FormStructure* form_structure,
+      const FormFieldData& field,
+      AutofillField* autofill_field,
+      AutofillSuggestionTriggerSource trigger_source,
+      AutofillPredictionImprovementsDelegate::HasData
+          has_prediction_improvements_data,
+      SuggestionsContext& context,
+      OnGenerateSuggestionsCallback callback,
+      std::vector<std::string> plus_addresses);
 
   // Receives the lists of plus address and single field form fill suggestions
   // and combines them. It gives priority to the plus address suggestions,
@@ -677,11 +675,11 @@
       SuppressReason suppress_reason);
 
   // The function receives a the list of `suggestions` from
-  // `GenerateSuggestionsAndMaybeShowUI` and displays them if `show_suggestions`
-  // is true (via the `external_delegate_`). It also logs whether there is a
-  // suggestion for the user and whether the suggestion is shown.
-  // `ranking_context` contains information regarding the ranking of suggestions
-  // and is used for metrics logging.
+  // `GenerateSuggestionsAndMaybeShowUIPhase2` and displays them if
+  // `show_suggestions` is true (via the `external_delegate_`). It also logs
+  // whether there is a suggestion for the user and whether the suggestion is
+  // shown. `ranking_context` contains information regarding the ranking of
+  // suggestions and is used for metrics logging.
   void OnGenerateSuggestionsComplete(
       const FormData& form,
       const FormFieldData& field,
@@ -692,14 +690,17 @@
       std::optional<autofill_metrics::SuggestionRankingContext>
           ranking_context);
 
-  void OnGetPlusAddressSuggestions(
+  // Combines plus address and address profile suggestions into a single list,
+  // prioritizing plus address suggestions first. Runs `callback` with the
+  // resulting list of suggestions.
+  void MixPlusAddressAndAddressSuggestions(
+      std::vector<Suggestion> plus_address_suggestions,
+      std::vector<Suggestion> address_suggestions,
       AutofillPlusAddressDelegate::SuggestionContext suggestions_context,
       PasswordFormClassification::Type password_form_type,
       const FormData& form,
       const FormFieldData& field,
-      std::vector<Suggestion> address_suggestions,
-      OnGenerateSuggestionsCallback callback,
-      std::vector<Suggestion> suggestions);
+      OnGenerateSuggestionsCallback callback);
 
   // For each submitted field in the |form_structure|, it determines whether
   // |ADDRESS_HOME_STATE| is a possible matching type.
diff --git a/components/autofill/core/browser/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
index 1d55fd4..0751ed8 100644
--- a/components/autofill/core/browser/browser_autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
@@ -169,6 +169,7 @@
 const std::string kArbitraryNickname = "Grocery Card";
 const std::u16string kArbitraryNickname16 = u"Grocery Card";
 constexpr Suggestion::Icon kAddressEntryIcon = Suggestion::Icon::kAccount;
+constexpr char kPlusAddress[] = "plus+remote@plus.plus";
 
 // Action `SaveArgElementsTo<k>(pointer)` saves the value pointed to by the
 // `k`th (0-based) argument of the mock function by moving it to `*pointer`.
@@ -8092,6 +8093,8 @@
         std::make_unique<NiceMock<MockAutofillPlusAddressDelegate>>();
     ON_CALL(*plus_address_delegate, GetManagePlusAddressSuggestion)
         .WillByDefault(Return(Suggestion(SuggestionType::kManagePlusAddress)));
+    ON_CALL(*plus_address_delegate, IsPlusAddressFillingEnabled)
+        .WillByDefault(Return(true));
     autofill_client_.set_plus_address_delegate(
         std::move(plus_address_delegate));
   }
@@ -8104,7 +8107,7 @@
 
 // Ensure that plus address options aren't queried for non-email fields.
 TEST_F(BrowserAutofillManagerPlusAddressTest, NoPlusAddressesWithNameFields) {
-  EXPECT_CALL(plus_address_delegate(), GetSuggestions).Times(0);
+  EXPECT_CALL(plus_address_delegate(), GetAffiliatedPlusAddresses).Times(0);
 
   // Set up our form data.
   FormData form = test::GetFormData(
@@ -8136,8 +8139,12 @@
   using enum PasswordFormClassification::Type;
 
   // Plus address suggestions request.
-  EXPECT_CALL(plus_address_delegate(), GetSuggestions)
-      .WillOnce(RunOnceCallback<5>(std::vector<Suggestion>{
+  const std::vector<std::string> plus_addresses = {kPlusAddress};
+  EXPECT_CALL(plus_address_delegate(), GetAffiliatedPlusAddresses)
+      .WillOnce(RunOnceCallback<1>(plus_addresses));
+  EXPECT_CALL(plus_address_delegate(),
+              GetSuggestionsFromPlusAddresses(plus_addresses, _, _, _, _, _))
+      .WillOnce(Return(std::vector<Suggestion>{
           Suggestion(SuggestionType::kFillExistingPlusAddress)}));
   // No single field form fill suggestions requests.
   EXPECT_CALL(single_field_form_fill_router(), OnGetSingleFieldSuggestions)
@@ -8181,8 +8188,10 @@
   personal_data().test_address_data_manager().ClearProfiles();
 
   // Plus address suggestions request.
-  EXPECT_CALL(plus_address_delegate(), GetSuggestions)
-      .WillOnce(RunOnceCallback<5>(std::vector<Suggestion>{
+  EXPECT_CALL(plus_address_delegate(), GetAffiliatedPlusAddresses)
+      .WillOnce(RunOnceCallback<1>(std::vector<std::string>{}));
+  EXPECT_CALL(plus_address_delegate(), GetSuggestionsFromPlusAddresses)
+      .WillOnce(Return(std::vector<Suggestion>{
           Suggestion(SuggestionType::kCreateNewPlusAddress)}));
   // Single field form fill suggestions request - No results.
   EXPECT_CALL(single_field_form_fill_router(), OnGetSingleFieldSuggestions)
@@ -8221,7 +8230,9 @@
   personal_data().test_address_data_manager().ClearProfiles();
 
   // No plus address suggestions request.
-  EXPECT_CALL(plus_address_delegate(), GetSuggestions).Times(0);
+  EXPECT_CALL(plus_address_delegate(), GetAffiliatedPlusAddresses).Times(0);
+  EXPECT_CALL(plus_address_delegate(), GetSuggestionsFromPlusAddresses)
+      .Times(0);
   // Single field form fill suggestions request.
   EXPECT_CALL(single_field_form_fill_router(), OnGetSingleFieldSuggestions)
       .WillRepeatedly([&](const FormStructure*, const FormFieldData& field,
@@ -8264,7 +8275,9 @@
   personal_data().test_address_data_manager().ClearProfiles();
 
   // No plus address suggestions request.
-  EXPECT_CALL(plus_address_delegate(), GetSuggestions).Times(0);
+  EXPECT_CALL(plus_address_delegate(), GetAffiliatedPlusAddresses).Times(0);
+  EXPECT_CALL(plus_address_delegate(), GetSuggestionsFromPlusAddresses)
+      .Times(0);
   // Single field form fill suggestions request.
   EXPECT_CALL(single_field_form_fill_router(), OnGetSingleFieldSuggestions)
       .WillRepeatedly([&](const FormStructure*, const FormFieldData& field,
@@ -8310,8 +8323,12 @@
   personal_data().test_address_data_manager().ClearProfiles();
 
   // Plus address suggestions request.
-  EXPECT_CALL(plus_address_delegate(), GetSuggestions)
-      .WillOnce(RunOnceCallback<5>(std::vector<Suggestion>{
+  const std::vector<std::string> plus_addresses = {kPlusAddress};
+  EXPECT_CALL(plus_address_delegate(), GetAffiliatedPlusAddresses)
+      .WillOnce(RunOnceCallback<1>(plus_addresses));
+  EXPECT_CALL(plus_address_delegate(),
+              GetSuggestionsFromPlusAddresses(plus_addresses, _, _, _, _, _))
+      .WillOnce(Return(std::vector<Suggestion>{
           Suggestion(SuggestionType::kFillExistingPlusAddress)}));
   // Single field form fill suggestions request.
   EXPECT_CALL(single_field_form_fill_router(), OnGetSingleFieldSuggestions)
@@ -8360,8 +8377,10 @@
   using enum AutofillPlusAddressDelegate::SuggestionContext;
   using enum PasswordFormClassification::Type;
   personal_data().test_address_data_manager().ClearProfiles();
-  EXPECT_CALL(plus_address_delegate(), GetSuggestions)
-      .WillOnce(RunOnceCallback<5>(std::vector<Suggestion>{}));
+  EXPECT_CALL(plus_address_delegate(), GetAffiliatedPlusAddresses)
+      .WillOnce(RunOnceCallback<1>(std::vector<std::string>{}));
+  EXPECT_CALL(plus_address_delegate(), GetSuggestionsFromPlusAddresses)
+      .WillOnce(Return(std::vector<Suggestion>{}));
   EXPECT_CALL(plus_address_delegate(), GetManagePlusAddressSuggestion).Times(0);
   EXPECT_CALL(plus_address_delegate(), OnPlusAddressSuggestionShown).Times(0);
   // Single field form fill suggestions request - No results.
@@ -8388,12 +8407,14 @@
 TEST_F(BrowserAutofillManagerPlusAddressTest, ManualFallbackPlusAddress) {
   using enum AutofillPlusAddressDelegate::SuggestionContext;
   using enum PasswordFormClassification::Type;
+  EXPECT_CALL(plus_address_delegate(), GetAffiliatedPlusAddresses)
+      .WillOnce(RunOnceCallback<1>(std::vector<std::string>{}));
   EXPECT_CALL(
       plus_address_delegate(),
-      GetSuggestions(
-          _, _, _, _,
-          AutofillSuggestionTriggerSource::kManualFallbackPlusAddresses, _))
-      .WillOnce(RunOnceCallback<5>(std::vector<Suggestion>{
+      GetSuggestionsFromPlusAddresses(
+          _, _, _, _, _,
+          AutofillSuggestionTriggerSource::kManualFallbackPlusAddresses))
+      .WillOnce(Return(std::vector<Suggestion>{
           Suggestion(SuggestionType::kCreateNewPlusAddress)}));
   EXPECT_CALL(plus_address_delegate(),
               OnPlusAddressSuggestionShown(
diff --git a/components/autofill/core/browser/data_model/autofill_i18n_formatting_expressions.h b/components/autofill/core/browser/data_model/autofill_i18n_formatting_expressions.h
index 7d8279a..e0532a7 100644
--- a/components/autofill/core/browser/data_model/autofill_i18n_formatting_expressions.h
+++ b/components/autofill/core/browser/data_model/autofill_i18n_formatting_expressions.h
@@ -37,7 +37,7 @@
       {{"DE", ADDRESS_HOME_ADDRESS}, u"${ADDRESS_HOME_STREET_ADDRESS;;}\n${ADDRESS_HOME_ZIP;;} ${ADDRESS_HOME_CITY;;}"},
       {{"DE", ADDRESS_HOME_STREET_ADDRESS}, u"${ADDRESS_HOME_STREET_LOCATION;;}\n${ADDRESS_HOME_OVERFLOW;;}"},
       {{"DE", ADDRESS_HOME_STREET_LOCATION}, u"${ADDRESS_HOME_STREET_NAME;;} ${ADDRESS_HOME_HOUSE_NUMBER;;}"},
-      {{"FR", ADDRESS_HOME_ADDRESS}, u"${ADDRESS_HOME_STREET_ADDRESS;;}\n${ADDRESS_HOME_ZIP;;} ${ADDRESS_HOME_CITY;;}"},
+      {{"FR", ADDRESS_HOME_ADDRESS}, u"${ADDRESS_HOME_STREET_ADDRESS;;}\n${ADDRESS_HOME_DEPENDENT_LOCALITY;;}\n${ADDRESS_HOME_ZIP;;} ${ADDRESS_HOME_CITY;;}"},
       {{"FR", ADDRESS_HOME_STREET_ADDRESS}, u"${ADDRESS_HOME_OVERFLOW;;}\n${ADDRESS_HOME_STREET_LOCATION;;}"},
       {{"FR", ADDRESS_HOME_STREET_LOCATION}, u"${ADDRESS_HOME_HOUSE_NUMBER;;} ${ADDRESS_HOME_STREET_NAME;;}"},
       {{"IN", ADDRESS_HOME_ADDRESS}, u"${ADDRESS_HOME_STREET_LOCATION;;}\n${ADDRESS_HOME_DEPENDENT_LOCALITY;;}\n${ADDRESS_HOME_LANDMARK;;}\n${ADDRESS_HOME_CITY;;}\n${ADDRESS_HOME_STATE;;}\n${ADDRESS_HOME_ZIP;;}"},
diff --git a/components/autofill/core/browser/data_model/autofill_i18n_hierarchies.h b/components/autofill/core/browser/data_model/autofill_i18n_hierarchies.h
index 287599c..0996528 100644
--- a/components/autofill/core/browser/data_model/autofill_i18n_hierarchies.h
+++ b/components/autofill/core/browser/data_model/autofill_i18n_hierarchies.h
@@ -39,7 +39,7 @@
 // Field types for country FR.
 inline constexpr FieldType kFieldTypeChildren_FR_ADDRESS_HOME_STREET_LOCATION[] = {ADDRESS_HOME_STREET_NAME, ADDRESS_HOME_HOUSE_NUMBER};
 inline constexpr FieldType kFieldTypeChildren_FR_ADDRESS_HOME_STREET_ADDRESS[] = {ADDRESS_HOME_STREET_LOCATION, ADDRESS_HOME_OVERFLOW};
-inline constexpr FieldType kFieldTypeChildren_FR_ADDRESS_HOME_ADDRESS[] = {ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, ADDRESS_HOME_COUNTRY};
+inline constexpr FieldType kFieldTypeChildren_FR_ADDRESS_HOME_ADDRESS[] = {ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_CITY, ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, ADDRESS_HOME_COUNTRY};
 // Field types for country IN.
 inline constexpr FieldType kFieldTypeChildren_IN_ADDRESS_HOME_STREET_LOCATION_AND_LOCALITY[] = {ADDRESS_HOME_STREET_LOCATION, ADDRESS_HOME_DEPENDENT_LOCALITY};
 inline constexpr FieldType kFieldTypeChildren_IN_ADDRESS_HOME_DEPENDENT_LOCALITY_AND_LANDMARK[] = {ADDRESS_HOME_LANDMARK, ADDRESS_HOME_DEPENDENT_LOCALITY};
diff --git a/components/autofill/core/browser/mock_autofill_plus_address_delegate.h b/components/autofill/core/browser/mock_autofill_plus_address_delegate.h
index 27ea2744..ab87b623 100644
--- a/components/autofill/core/browser/mock_autofill_plus_address_delegate.h
+++ b/components/autofill/core/browser/mock_autofill_plus_address_delegate.h
@@ -28,14 +28,14 @@
               (const url::Origin& origin,
                base::OnceCallback<void(std::vector<std::string>)> callback),
               (override));
-  MOCK_METHOD(void,
-              GetSuggestions,
-              (const url::Origin&,
+  MOCK_METHOD(std::vector<Suggestion>,
+              GetSuggestionsFromPlusAddresses,
+              (const std::vector<std::string>& plus_addresses,
+               const url::Origin&,
                bool,
                const PasswordFormClassification&,
                const FormFieldData&,
-               AutofillSuggestionTriggerSource,
-               GetSuggestionsCallback),
+               AutofillSuggestionTriggerSource),
               (override));
   MOCK_METHOD(autofill::Suggestion,
               GetManagePlusAddressSuggestion,
diff --git a/components/autofill/core/browser/suggestions_context.h b/components/autofill/core/browser/suggestions_context.h
index 7679bd9..74e93a12 100644
--- a/components/autofill/core/browser/suggestions_context.h
+++ b/components/autofill/core/browser/suggestions_context.h
@@ -47,6 +47,10 @@
   // avoided. This can happen in multiple scenarios (e.g. During manual
   // fallbacks for plus addresses or if the form is a mixed content form).
   bool do_not_generate_autofill_suggestions = false;
+  // Indicates whether the trigger field is related to plus addresses. Used to
+  // signal that fetching the list of plus addresses is required to generate
+  // the overall list of suggestions.
+  bool field_is_relevant_for_plus_addresses = false;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/ios/browser/autofill_agent.mm b/components/autofill/ios/browser/autofill_agent.mm
index 5ce6cf44..8a5ca81 100644
--- a/components/autofill/ios/browser/autofill_agent.mm
+++ b/components/autofill/ios/browser/autofill_agent.mm
@@ -15,6 +15,7 @@
 
 #import "base/apple/foundation_util.h"
 #import "base/containers/map_util.h"
+#import "base/debug/crash_logging.h"
 #import "base/feature_list.h"
 #import "base/format_macros.h"
 #import "base/functional/bind.h"
@@ -317,6 +318,11 @@
           completionHandler:(SuggestionHandledCompletion)completion {
   [[UIDevice currentDevice] playInputClick];
   DCHECK(completion);
+  // TODO(crbug.com/366247033): This double-checks the assumption that this
+  // crash is caused by an unexpected suggestion type, and not a nil suggestion.
+  // It can be removed once a root cause for the issue is known.
+  CHECK(suggestion, base::NotFatalUntil::M133);
+
   _suggestionHandledCompletion = [completion copy];
 
   if (suggestion.acceptanceA11yAnnouncement != nil) {
@@ -426,8 +432,11 @@
       autofillManager->OnUserAcceptedCardsFromAccountOption();
     }
   } else {
-    NOTREACHED_IN_MIGRATION()
-        << "unknown identifier " << base::to_underlying(suggestion.type);
+    // TODO(crbug.com/366247033): Remove this crash key once the underlying
+    // crash has been fixed.
+    SCOPED_CRASH_KEY_NUMBER("Bug366247033", "suggestion_type",
+                            static_cast<int>(suggestion.type));
+    NOTREACHED(base::NotFatalUntil::M133);
   }
 }
 
diff --git a/components/autofill_prediction_improvements/core/browser/BUILD.gn b/components/autofill_prediction_improvements/core/browser/BUILD.gn
index adc9ed6..1b880387 100644
--- a/components/autofill_prediction_improvements/core/browser/BUILD.gn
+++ b/components/autofill_prediction_improvements/core/browser/BUILD.gn
@@ -12,6 +12,7 @@
     "autofill_prediction_improvements_client.h",
     "autofill_prediction_improvements_features.cc",
     "autofill_prediction_improvements_features.h",
+    "autofill_prediction_improvements_filling_engine.cc",
     "autofill_prediction_improvements_filling_engine.h",
     "autofill_prediction_improvements_filling_engine_impl.cc",
     "autofill_prediction_improvements_filling_engine_impl.h",
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine.cc b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine.cc
new file mode 100644
index 0000000..71602d6
--- /dev/null
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine.cc
@@ -0,0 +1,46 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine.h"
+
+#include <string>
+
+#include "base/strings/utf_string_conversions.h"
+
+namespace autofill_prediction_improvements {
+
+AutofillPredictionImprovementsFillingEngine::Prediction::Prediction(
+    std::u16string value,
+    std::u16string label)
+    : Prediction(std::move(value), std::move(label), std::nullopt) {}
+
+AutofillPredictionImprovementsFillingEngine::Prediction::Prediction(
+    std::u16string value,
+    std::u16string label,
+    std::optional<std::u16string> select_option_text)
+    : value(std::move(value)),
+      label(std::move(label)),
+      select_option_text(select_option_text) {}
+
+AutofillPredictionImprovementsFillingEngine::Prediction::Prediction(
+    const Prediction& other) = default;
+AutofillPredictionImprovementsFillingEngine::Prediction::~Prediction() =
+    default;
+
+// For tests to readably print an instance of this struct.
+void PrintTo(
+    const AutofillPredictionImprovementsFillingEngine::Prediction& prediction,
+    std::ostream* os) {
+  *os << "Prediction { " << ".value = \"" << base::UTF16ToUTF8(prediction.value)
+      << "\", " << ".label = \"" << base::UTF16ToUTF8(prediction.label)
+      << "\", " << ".select_option_text = "
+      << (prediction.select_option_text
+              ? base::StrCat({"\"",
+                              base::UTF16ToUTF8(*prediction.select_option_text),
+                              "\""})
+              : "std::nullopt")
+      << " " << "}";
+}
+
+}  // namespace autofill_prediction_improvements
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine.h b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine.h
index e8bb9888..566b89e 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine.h
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine.h
@@ -5,8 +5,10 @@
 #ifndef COMPONENTS_AUTOFILL_PREDICTION_IMPROVEMENTS_CORE_BROWSER_AUTOFILL_PREDICTION_IMPROVEMENTS_FILLING_ENGINE_H_
 #define COMPONENTS_AUTOFILL_PREDICTION_IMPROVEMENTS_CORE_BROWSER_AUTOFILL_PREDICTION_IMPROVEMENTS_FILLING_ENGINE_H_
 
+#include "base/containers/flat_map.h"
 #include "base/functional/callback_forward.h"
 #include "base/types/expected.h"
+#include "components/autofill/core/common/unique_ids.h"
 
 namespace autofill {
 class FormData;
@@ -21,8 +23,34 @@
 // The filling engine that provides autofill predictions improvements.
 class AutofillPredictionImprovementsFillingEngine {
  public:
+  struct Prediction {
+    Prediction(std::u16string value, std::u16string label);
+    Prediction(std::u16string value,
+               std::u16string label,
+               std::optional<std::u16string> select_option_text);
+    Prediction(const Prediction& other);
+    ~Prediction();
+
+    // The value to be filled into a field. Also shown as the main text in the
+    // suggestion unless `select_option_text` is set.
+    std::u16string value;
+    // The label to be shown in the suggestion.
+    std::u16string label;
+    // Shown as main text in the suggestion if set.
+    std::optional<std::u16string> select_option_text = std::nullopt;
+
+   private:
+    // For tests to readably print an instance of this struct.
+    friend void PrintTo(
+        const AutofillPredictionImprovementsFillingEngine::Prediction&
+            prediction,
+        std::ostream* os);
+  };
+  using PredictionsByGlobalId =
+      base::flat_map<autofill::FieldGlobalId, Prediction>;
+  using PredictionsOrError = base::expected<PredictionsByGlobalId, bool>;
   using PredictionsReceivedCallback =
-      base::OnceCallback<void(base::expected<autofill::FormData, bool>,
+      base::OnceCallback<void(PredictionsOrError,
                               std::optional<std::string> feedback_id)>;
 
   virtual ~AutofillPredictionImprovementsFillingEngine() = default;
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl.cc b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl.cc
index 26ae84d..04d438d 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl.cc
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl.cc
@@ -14,6 +14,7 @@
 #include "components/autofill/core/browser/form_processing/optimization_guide_proto_util.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/unique_ids.h"
 #include "components/optimization_guide/core/optimization_guide_proto_util.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
 #include "components/optimization_guide/proto/features/common_quality_data.pb.h"
@@ -101,17 +102,18 @@
     return;
   }
 
-  FillFormDataWithResponse(form_data, maybe_response->form_data());
-  std::move(callback).Run(std::move(form_data),
-                          log_entry ? log_entry->model_execution_id() : "");
+  std::move(callback).Run(
+      ExtractPredictions(form_data, maybe_response->form_data()),
+      log_entry ? log_entry->model_execution_id() : "");
 }
 
 // static
-void AutofillPredictionImprovementsFillingEngineImpl::FillFormDataWithResponse(
-    autofill::FormData& form_data,
+AutofillPredictionImprovementsFillingEngine::PredictionsByGlobalId
+AutofillPredictionImprovementsFillingEngineImpl::ExtractPredictions(
+    const autofill::FormData& form_data,
     const optimization_guide::proto::FilledFormData& form_data_proto) {
-  std::vector<autofill::FormFieldData>& fields =
-      form_data.mutable_fields(/*pass_key=*/{});
+  std::vector<std::pair<autofill::FieldGlobalId, Prediction>> predictions;
+  const std::vector<autofill::FormFieldData>& fields = form_data.fields();
   for (const optimization_guide::proto::FilledFormFieldData&
            filled_form_field_proto : form_data_proto.filled_form_field_data()) {
     if (filled_form_field_proto.field_data().field_value().empty()) {
@@ -126,10 +128,40 @@
                 filled_form_field_proto.field_data().field_label()),
             &autofill::FormFieldData::label);
         it != fields.end()) {
-      it->set_value(base::UTF8ToUTF16(
-          filled_form_field_proto.field_data().field_value()));
+      const autofill::FormFieldData& field = *it;
+      const std::u16string predicted_value =
+          base::UTF8ToUTF16(filled_form_field_proto.field_data().field_value());
+      std::optional<std::u16string> select_option_text = std::nullopt;
+
+      if (field.IsSelectOrSelectListElement()) {
+        // Reject the prediction if it equals the currently selected option.
+        if (field.selected_option().has_value() &&
+            field.selected_option()->value == predicted_value) {
+          continue;
+        }
+
+        // Ensure that the predicted value actually is one of the select
+        // options.
+        auto predicted_select_option_it = base::ranges::find(
+            field.options(), predicted_value, &autofill::SelectOption::value);
+        if (predicted_select_option_it == field.options().end()) {
+          continue;
+        }
+
+        // Sets `Prediction::select_option_text` below which will then be shown
+        // in the suggestion as the main text.
+        select_option_text = predicted_select_option_it->text;
+      }
+
+      predictions.emplace_back(
+          field.global_id(),
+          Prediction{
+              std::move(predicted_value),
+              field.label().empty() ? field.placeholder() : field.label(),
+              select_option_text});
     }
   }
+  return predictions;
 }
 
 }  // namespace autofill_prediction_improvements
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl.h b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl.h
index 8d6e8f3..5aa59ebf 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl.h
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl.h
@@ -59,8 +59,8 @@
           execution_result,
       std::unique_ptr<optimization_guide::ModelQualityLogEntry> log_entry);
 
-  static void FillFormDataWithResponse(
-      autofill::FormData& form_data,
+  static PredictionsByGlobalId ExtractPredictions(
+      const autofill::FormData& form_data,
       const optimization_guide::proto::FilledFormData& form_data_proto);
 
   raw_ptr<optimization_guide::OptimizationGuideModelExecutor> model_executor_ =
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl_unittest.cc b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl_unittest.cc
index 1e2002b3..4fcb439 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl_unittest.cc
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl_unittest.cc
@@ -9,6 +9,8 @@
 #include "base/test/gmock_callback_support.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
+#include "components/autofill/core/browser/autofill_form_test_utils.h"
+#include "components/autofill/core/common/autofill_test_utils.h"
 #include "components/autofill/core/common/form_data.h"
 #include "components/optimization_guide/core/mock_optimization_guide_model_executor.h"
 #include "components/optimization_guide/proto/features/common_quality_data.pb.h"
@@ -20,8 +22,26 @@
 namespace autofill_prediction_improvements {
 namespace {
 
+using Prediction = AutofillPredictionImprovementsFillingEngine::Prediction;
+using PredictionsOrError =
+    AutofillPredictionImprovementsFillingEngine::PredictionsOrError;
 using ::testing::_;
+using ::testing::AllOf;
 using ::testing::An;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::Pair;
+
+MATCHER_P(HasPrediction, expected_prediction, "") {
+  EXPECT_THAT(arg, AllOf(Field("Prediction::value", &Prediction::value,
+                               expected_prediction.value),
+                         Field("Prediction::label", &Prediction::label,
+                               expected_prediction.label),
+                         Field("Prediction::select_option_text",
+                               &Prediction::select_option_text,
+                               expected_prediction.select_option_text)));
+  return true;
+}
 
 class AutofillPredictionImprovementsFillingEngineImplTest
     : public testing::Test {
@@ -47,6 +67,7 @@
 
  private:
   base::test::TaskEnvironment task_environment_;
+  autofill::test::AutofillUnitTestEnvironment autofill_test_env_;
   testing::NiceMock<optimization_guide::MockOptimizationGuideModelExecutor>
       model_executor_;
   std::unique_ptr<user_annotations::TestUserAnnotationsService>
@@ -81,6 +102,27 @@
           ->mutable_field_data();
   not_in_original_form_field->set_field_label("notinform");
   not_in_original_form_field->set_field_value("doesntmatter");
+  optimization_guide::proto::FormFieldData* filled_select_field =
+      response.mutable_form_data()
+          ->add_filled_form_field_data()
+          ->mutable_field_data();
+  filled_select_field->set_field_label("State");
+  filled_select_field->set_field_value("33");
+  optimization_guide::proto::FormFieldData* bad_response_select_field =
+      response.mutable_form_data()
+          ->add_filled_form_field_data()
+          ->mutable_field_data();
+  bad_response_select_field->set_field_label(
+      "Country Code - response not in select options, not filled");
+  bad_response_select_field->set_field_value("-2");
+  optimization_guide::proto::FormFieldData*
+      response_equals_selected_value_select_field =
+          response.mutable_form_data()
+              ->add_filled_form_field_data()
+              ->mutable_field_data();
+  response_equals_selected_value_select_field->set_field_label(
+      "Country - response equals selected value, not filled");
+  response_equals_selected_value_select_field->set_field_value("2");
   optimization_guide::proto::Any any;
   any.set_type_url(response.GetTypeName());
   response.SerializeToString(any.mutable_value());
@@ -92,35 +134,44 @@
                  OptimizationGuideModelExecutionResultCallback>()))
       .WillOnce(base::test::RunOnceCallback<2>(any, /*log_entry=*/nullptr));
 
-  autofill::FormFieldData form_field_data;
-  form_field_data.set_label(u"label");
-  autofill::FormFieldData form_field_data2;
-  form_field_data2.set_label(u"notinresponseandnotfilled");
-  autofill::FormFieldData form_field_data3;
-  form_field_data3.set_label(u"empty");
-  autofill::FormData form_data;
-  form_data.set_fields({form_field_data, form_field_data2, form_field_data3});
-  optimization_guide::proto::AXTreeUpdate ax_tree;
-  base::test::TestFuture<base::expected<autofill::FormData, bool>,
-                         std::optional<std::string>>
-      test_future;
-  engine()->GetPredictions(form_data, ax_tree, test_future.GetCallback());
+  autofill::test::FormDescription form_description = {
+      .fields = {
+          {.label = u"label"},
+          {.label = u"not in response, not filled"},
+          {.label = u"empty, not filled"},
+          {.label = u"State",
+           .value = u"-1",
+           .form_control_type = autofill::FormControlType::kSelectOne,
+           .select_options = {{.value = u"-1", .text = u"Select state"},
+                              {.value = u"33", .text = u"North Carolina"}}},
+          {.label =
+               u"Country Code - response not in select options, not filled",
+           .value = u"-1",
+           .form_control_type = autofill::FormControlType::kSelectOne,
+           .select_options = {{.value = u"-1", .text = u"Select country code"},
+                              {.value = u"+49", .text = u"Germany"}}},
+          {.label = u"Country - response equals selected value, not filled",
+           .value = u"2",
+           .form_control_type = autofill::FormControlType::kSelectOne,
+           .select_options = {{.value = u"1", .text = u"France"},
+                              {.value = u"2", .text = u"Spain"}}}}};
+  autofill::FormData form = autofill::test::GetFormData(form_description);
 
-  base::expected<autofill::FormData, bool> form_data_or_err =
+  optimization_guide::proto::AXTreeUpdate ax_tree;
+  base::test::TestFuture<PredictionsOrError, std::optional<std::string>>
+      test_future;
+  engine()->GetPredictions(form, ax_tree, test_future.GetCallback());
+
+  const PredictionsOrError predictions_or_error =
       std::get<0>(test_future.Take());
-  EXPECT_TRUE(form_data_or_err.has_value());
-  EXPECT_EQ(3u, form_data_or_err->fields().size());
-  autofill::FormFieldData filled_field_response = form_data_or_err->fields()[0];
-  EXPECT_EQ(u"label", filled_field_response.label());
-  EXPECT_EQ(u"value", filled_field_response.value());
-  autofill::FormFieldData filled_field_response2 =
-      form_data_or_err->fields()[1];
-  EXPECT_EQ(u"notinresponseandnotfilled", filled_field_response2.label());
-  EXPECT_TRUE(filled_field_response2.value().empty());
-  autofill::FormFieldData filled_field_response3 =
-      form_data_or_err->fields()[2];
-  EXPECT_EQ(u"empty", filled_field_response3.label());
-  EXPECT_TRUE(filled_field_response3.value().empty());
+  ASSERT_TRUE(predictions_or_error.has_value());
+  EXPECT_THAT(
+      predictions_or_error.value(),
+      ElementsAre(
+          Pair(form.fields()[0].global_id(),
+               HasPrediction(Prediction(u"value", u"label"))),
+          Pair(form.fields()[3].global_id(),
+               HasPrediction(Prediction(u"33", u"State", u"North Carolina")))));
 }
 
 TEST_F(AutofillPredictionImprovementsFillingEngineImplTest,
@@ -136,14 +187,13 @@
   autofill::FormData form_data;
   form_data.set_fields({form_field_data});
   optimization_guide::proto::AXTreeUpdate ax_tree;
-  base::test::TestFuture<base::expected<autofill::FormData, bool>,
-                         std::optional<std::string>>
+  base::test::TestFuture<PredictionsOrError, std::optional<std::string>>
       test_future;
   engine()->GetPredictions(form_data, ax_tree, test_future.GetCallback());
 
-  base::expected<autofill::FormData, bool> form_data_or_err =
+  const PredictionsOrError predictions_or_error =
       std::get<0>(test_future.Take());
-  EXPECT_FALSE(form_data_or_err.has_value());
+  EXPECT_FALSE(predictions_or_error.has_value());
 }
 
 TEST_F(AutofillPredictionImprovementsFillingEngineImplTest,
@@ -174,14 +224,13 @@
   autofill::FormData form_data;
   form_data.set_fields({form_field_data});
   optimization_guide::proto::AXTreeUpdate ax_tree;
-  base::test::TestFuture<base::expected<autofill::FormData, bool>,
-                         std::optional<std::string>>
+  base::test::TestFuture<PredictionsOrError, std::optional<std::string>>
       test_future;
   engine()->GetPredictions(form_data, ax_tree, test_future.GetCallback());
 
-  base::expected<autofill::FormData, bool> form_data_or_err =
+  const PredictionsOrError predictions_or_error =
       std::get<0>(test_future.Take());
-  EXPECT_FALSE(form_data_or_err.has_value());
+  EXPECT_FALSE(predictions_or_error.has_value());
 }
 
 TEST_F(AutofillPredictionImprovementsFillingEngineImplTest,
@@ -207,14 +256,13 @@
   autofill::FormData form_data;
   form_data.set_fields({form_field_data});
   optimization_guide::proto::AXTreeUpdate ax_tree;
-  base::test::TestFuture<base::expected<autofill::FormData, bool>,
-                         std::optional<std::string>>
+  base::test::TestFuture<PredictionsOrError, std::optional<std::string>>
       test_future;
   engine()->GetPredictions(form_data, ax_tree, test_future.GetCallback());
 
-  base::expected<autofill::FormData, bool> form_data_or_err =
+  const PredictionsOrError predictions_or_error =
       std::get<0>(test_future.Take());
-  EXPECT_FALSE(form_data_or_err.has_value());
+  EXPECT_FALSE(predictions_or_error.has_value());
 }
 
 }  // namespace
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.cc b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.cc
index e9147518..fb9631d7 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.cc
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.cc
@@ -62,16 +62,15 @@
 // Returns a field-by-field filling suggestion for `filled_field`, meant to be
 // added to another suggestion's `autofill::Suggestion::children`.
 autofill::Suggestion CreateChildSuggestionForFilling(
-    const autofill::FormFieldData& filled_field) {
+    const AutofillPredictionImprovementsFillingEngine::Prediction& prediction) {
+  const std::u16string& value_to_fill = prediction.select_option_text
+                                            ? *prediction.select_option_text
+                                            : prediction.value;
   autofill::Suggestion child_suggestion(
-      filled_field.value(),
-      autofill::SuggestionType::kFillPredictionImprovements);
-  child_suggestion.payload =
-      autofill::Suggestion::ValueToFill(filled_field.value());
+      value_to_fill, autofill::SuggestionType::kFillPredictionImprovements);
+  child_suggestion.payload = autofill::Suggestion::ValueToFill(value_to_fill);
   child_suggestion.labels.emplace_back();
-  child_suggestion.labels.back().emplace_back(filled_field.label().empty()
-                                                  ? filled_field.placeholder()
-                                                  : filled_field.label());
+  child_suggestion.labels.back().emplace_back(prediction.label);
   return child_suggestion;
 }
 
@@ -199,15 +198,14 @@
   if (!cache_) {
     return {};
   }
-  const autofill::FormFieldData* filled_field =
-      (*cache_).FindFieldByGlobalId(field.global_id());
-  if (!filled_field) {
+  if (!(*cache_).contains(field.global_id())) {
     return {};
   }
-  const std::u16string predicted_value = filled_field->value();
+  const AutofillPredictionImprovementsFillingEngine::Prediction& prediction =
+      (*cache_).at(field.global_id());
 
   autofill::Suggestion suggestion(
-      predicted_value, autofill::SuggestionType::kFillPredictionImprovements);
+      prediction.value, autofill::SuggestionType::kFillPredictionImprovements);
   auto payload = autofill::Suggestion::PredictionImprovementsPayload(
       GetValuesToFill(), GetFieldTypesToFill(), kIgnoreableSkipReasons);
   suggestion.payload = payload;
@@ -226,18 +224,17 @@
     suggestion.children.emplace_back(autofill::SuggestionType::kSeparator);
   }
   // Add the child suggestion for the triggering field on top.
-  suggestion.children.emplace_back(
-      CreateChildSuggestionForFilling(*filled_field));
+  suggestion.children.emplace_back(CreateChildSuggestionForFilling(prediction));
   // Then add child suggestions for all remaining, non-empty fields.
-  for (const auto& cached_field : (*cache_).fields()) {
+  for (const auto& [child_field_global_id, child_prediction] : (*cache_)) {
     // Only add a child suggestion if the field is not the triggering field and
     // the value to fill is not empty.
-    if (cached_field.global_id() == filled_field->global_id() ||
-        cached_field.value().empty()) {
+    if (child_field_global_id == field.global_id() ||
+        child_prediction.value.empty()) {
       continue;
     }
     suggestion.children.emplace_back(
-        CreateChildSuggestionForFilling(cached_field));
+        CreateChildSuggestionForFilling(child_prediction));
   }
   if (!suggestion.children.empty()) {
     suggestion.labels.emplace_back();
@@ -284,7 +281,7 @@
   if (!cache_) {
     return false;
   }
-  return (*cache_).FindFieldByGlobalId(field.global_id());
+  return (*cache_).contains(field.global_id());
 }
 
 bool AutofillPredictionImprovementsManager::IsFormEligible(
@@ -350,10 +347,11 @@
 void AutofillPredictionImprovementsManager::OnReceivedPredictions(
     const autofill::FormData& form,
     const autofill::FormFieldData& trigger_field,
-    base::expected<autofill::FormData, bool> prediction_improvements,
+    AutofillPredictionImprovementsFillingEngine::PredictionsOrError
+        predictions_or_error,
     std::optional<std::string> feedback_id) {
-  if (prediction_improvements.has_value()) {
-    cache_ = prediction_improvements.value();
+  if (predictions_or_error.has_value()) {
+    cache_ = predictions_or_error.value();
     feedback_id_ = feedback_id;
   }
 
@@ -362,7 +360,7 @@
   // don't see a flickering UI.
   loading_suggestion_timer_.Start(
       FROM_HERE, kMinTimeToShowLoading,
-      prediction_improvements.has_value()
+      predictions_or_error.has_value()
           ? base::BindRepeating(
                 &AutofillPredictionImprovementsManager::UpdateSuggestions,
                 weak_ptr_factory_.GetWeakPtr(),
@@ -415,10 +413,10 @@
     return {};
   }
   std::vector<std::pair<autofill::FieldGlobalId, std::u16string>>
-      values_to_fill((*cache_).fields().size());
-  for (size_t i = 0; i < (*cache_).fields().size(); i++) {
-    const autofill::FormFieldData& field = (*cache_).fields()[i];
-    values_to_fill[i] = {field.global_id(), field.value()};
+      values_to_fill((*cache_).size());
+  size_t i = 0;
+  for (const auto& [field_global_id, prediction] : (*cache_)) {
+    values_to_fill[i++] = {field_global_id, prediction.value};
   }
   return values_to_fill;
 }
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.h b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.h
index 4acc8a7..43f9c2c 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.h
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.h
@@ -93,7 +93,8 @@
   void OnReceivedPredictions(
       const autofill::FormData& form,
       const autofill::FormFieldData& trigger_field,
-      base::expected<autofill::FormData, bool> prediction_improvements,
+      AutofillPredictionImprovementsFillingEngine::PredictionsOrError
+          predictions_or_error,
       std::optional<std::string> feedback_id);
 
   // Resets the state of this class.
@@ -130,8 +131,9 @@
 
   // Most recently retrieved form with field values set to prediction
   // improvements.
-  // TODO(crbug.com/361414075): Set `cache_` and manage its lifecycle.
-  std::optional<autofill::FormData> cache_ = std::nullopt;
+  std::optional<
+      AutofillPredictionImprovementsFillingEngine::PredictionsByGlobalId>
+      cache_ = std::nullopt;
 
   // Address suggestions that will be shown as defined in
   // `CreateFillingSuggestions()` after prediction improvements was triggered.
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_test_api.h b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_test_api.h
index a5d2c7a..d06c630 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_test_api.h
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_test_api.h
@@ -16,7 +16,10 @@
       AutofillPredictionImprovementsManager* manager)
       : manager_(CHECK_DEREF(manager)) {}
 
-  void SetCache(std::optional<autofill::FormData> cache) {
+  void SetCache(
+      std::optional<
+          AutofillPredictionImprovementsFillingEngine::PredictionsByGlobalId>
+          cache) {
     manager_->cache_ = cache;
   }
 
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_unittest.cc b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_unittest.cc
index 8bb30b1..6471a9f 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_unittest.cc
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_unittest.cc
@@ -30,7 +30,10 @@
 
 using ::autofill::Suggestion;
 using ::autofill::SuggestionType;
+using PredictionsByGlobalId =
+    AutofillPredictionImprovementsFillingEngine::PredictionsByGlobalId;
 using ::testing::_;
+using ::testing::AllOf;
 using ::testing::ElementsAre;
 using ::testing::Eq;
 using ::testing::Field;
@@ -40,9 +43,42 @@
 using ::testing::Return;
 using ::testing::ReturnRef;
 using ::testing::SaveArg;
+using ::testing::VariantWith;
 
 MATCHER_P(HasType, expected_type, "") {
-  EXPECT_THAT(arg, Field(&Suggestion::type, Eq(expected_type)));
+  EXPECT_THAT(arg,
+              Field("Suggestion::type", &Suggestion::type, Eq(expected_type)));
+  return true;
+}
+
+MATCHER(HasPredictionImprovementsPayload, "") {
+  EXPECT_THAT(
+      arg,
+      Field("Suggestion::payload", &Suggestion::payload,
+            ::testing::VariantWith<Suggestion::PredictionImprovementsPayload>(
+                _)));
+  return true;
+}
+
+MATCHER_P(HasValueToFill, expected_value_to_fill, "") {
+  EXPECT_THAT(arg, Field("Suggestion::payload", &Suggestion::payload,
+                         VariantWith<Suggestion::ValueToFill>(
+                             Suggestion::ValueToFill(expected_value_to_fill))));
+  return true;
+}
+
+MATCHER_P(HasMainText, expected_main_text, "") {
+  EXPECT_THAT(arg, Field("Suggestion::main_text", &Suggestion::main_text,
+                         Field("Suggestion::Text::value",
+                               &Suggestion::Text::value, expected_main_text)));
+  return true;
+}
+
+MATCHER_P(HasLabel, expected_label, "") {
+  EXPECT_THAT(arg, Field("Suggestion::labels", &Suggestion::labels,
+                         ElementsAre(ElementsAre(Field(
+                             "Suggestion::Text::value",
+                             &Suggestion::Text::value, expected_label)))));
   return true;
 }
 
@@ -239,8 +275,13 @@
 
   manager_->OnClickedTriggerSuggestion(form, form.fields().front(),
                                        update_suggestions_callback.Get());
+  const autofill::FormFieldData& filled_field = filled_form.fields().front();
   std::move(axtree_received_callback).Run({});
-  std::move(predictions_received_callback).Run(filled_form, "");
+  std::move(predictions_received_callback)
+      .Run(
+          PredictionsByGlobalId{{filled_field.global_id(),
+                                 {filled_field.value(), filled_field.label()}}},
+          "");
   base::test::RunUntil([this]() {
     return !test_api(*manager_).loading_suggestion_timer().IsRunning();
   });
@@ -257,7 +298,6 @@
   const Suggestion::PredictionImprovementsPayload filling_payload =
       filling_suggestion[0]
           .GetPayload<Suggestion::PredictionImprovementsPayload>();
-  const autofill::FormFieldData& filled_field = filled_form.fields().front();
   EXPECT_THAT(
       filling_payload.values_to_fill,
       ElementsAre(Pair(filled_field.global_id(), filled_field.value())));
@@ -329,7 +369,8 @@
                   .heuristic_type = autofill::NAME_FIRST}}};
   autofill::FormData form = autofill::test::GetFormData(form_description);
   test_api(*manager_).SetAddressSuggestions(suggestions_to_show);
-  test_api(*manager_).SetCache(form);
+  test_api(*manager_).SetCache(PredictionsByGlobalId{
+      {form.fields().front().global_id(), {u"value", u"label"}}});
   EXPECT_TRUE(manager_->MaybeUpdateSuggestions(
       suggestions_to_show, form.fields().front(),
       /*should_add_trigger_suggestion=*/true));
@@ -352,7 +393,8 @@
       .fields = {{.role = autofill::NAME_FIRST,
                   .heuristic_type = autofill::NAME_FIRST}}};
   autofill::FormData form = autofill::test::GetFormData(form_description);
-  test_api(*manager_).SetCache(form);
+  test_api(*manager_).SetCache(PredictionsByGlobalId{
+      {form.fields().front().global_id(), {u"value", u"label"}}});
   EXPECT_TRUE(manager_->MaybeUpdateSuggestions(
       address_suggestions, form.fields().front(),
       /*should_add_trigger_suggestion=*/true));
@@ -364,6 +406,65 @@
                   HasType(SuggestionType::kPredictionImprovementsFeedback)));
 }
 
+// Tests that the filling suggestion incl. its children is created as expected.
+TEST_F(AutofillPredictionImprovementsManagerTest,
+       FillingSuggestionIsCreatedAsExpected) {
+  const std::u16string trigger_field_value = u"Jane";
+  const std::u16string trigger_field_label = u"First name";
+  const std::u16string select_field_value = u"33";
+  const std::u16string select_field_label = u"State";
+  const std::u16string select_field_option_text = u"North Carolina";
+  autofill::test::FormDescription form_description = {
+      .fields = {{.role = autofill::NAME_FIRST,
+                  .heuristic_type = autofill::NAME_FIRST},
+                 {.role = autofill::ADDRESS_HOME_STATE,
+                  .heuristic_type = autofill::ADDRESS_HOME_STATE,
+                  .form_control_type = autofill::FormControlType::kSelectOne}}};
+  autofill::FormData form = autofill::test::GetFormData(form_description);
+  test_api(*manager_).SetCache(PredictionsByGlobalId{
+      {form.fields()[0].global_id(),
+       {trigger_field_value, trigger_field_label}},
+      {form.fields()[1].global_id(),
+       {select_field_value, select_field_label, select_field_option_text}}});
+  std::vector<Suggestion> suggestions_to_show;
+
+  EXPECT_TRUE(
+      manager_->MaybeUpdateSuggestions(suggestions_to_show, form.fields()[0],
+                                       /*should_add_trigger_suggestion=*/true));
+
+  EXPECT_THAT(
+      suggestions_to_show,
+      ElementsAre(
+          AllOf(
+              HasType(SuggestionType::kFillPredictionImprovements),
+              HasPredictionImprovementsPayload(),
+              Field("Suggestion::children", &Suggestion::children,
+                    ElementsAre(
+                        AllOf(HasType(
+                                  SuggestionType::kFillPredictionImprovements),
+                              HasPredictionImprovementsPayload()),
+                        HasType(SuggestionType::kSeparator),
+                        AllOf(HasType(
+                                  SuggestionType::kFillPredictionImprovements),
+                              HasValueToFill(trigger_field_value),
+                              HasMainText(trigger_field_value),
+                              HasLabel(trigger_field_label)),
+                        AllOf(HasType(
+                                  SuggestionType::kFillPredictionImprovements),
+                              // For <select> elements expect both value to fill
+                              // and main text to be set to the option text, not
+                              // the value.
+                              HasValueToFill(select_field_option_text),
+                              HasMainText(select_field_option_text),
+                              HasLabel(select_field_label)),
+                        HasType(SuggestionType::kSeparator),
+                        HasType(SuggestionType::
+                                    kEditPredictionImprovementsInformation)))),
+          HasType(SuggestionType::kSeparator),
+          HasType(SuggestionType::kPredictionImprovementsDetails),
+          HasType(SuggestionType::kPredictionImprovementsFeedback)));
+}
+
 class AutofillPredictionImprovementsManagerUserFeedbackTest
     : public AutofillPredictionImprovementsManagerTest,
       public testing::WithParamInterface<
diff --git a/components/browsing_data/core/browsing_data_utils.cc b/components/browsing_data/core/browsing_data_utils.cc
index ec992ef4..c93d6cfa 100644
--- a/components/browsing_data/core/browsing_data_utils.cc
+++ b/components/browsing_data/core/browsing_data_utils.cc
@@ -123,51 +123,30 @@
     case TimePeriod::LAST_15_MINUTES:
       base::RecordAction(base::UserMetricsAction(
           "ClearBrowsingData_TimePeriodChanged_Last15Minutes"));
-      base::UmaHistogramEnumeration(
-          browsing_data::kDeleteBrowsingDataDialogHistogram,
-          DeleteBrowsingDataDialogAction::kLast15MinutesSelected);
       break;
     case TimePeriod::LAST_HOUR:
       base::RecordAction(base::UserMetricsAction(
           "ClearBrowsingData_TimePeriodChanged_LastHour"));
-      base::UmaHistogramEnumeration(
-          browsing_data::kDeleteBrowsingDataDialogHistogram,
-          DeleteBrowsingDataDialogAction::kLastHourSelected);
       break;
     case TimePeriod::LAST_DAY:
       base::RecordAction(base::UserMetricsAction(
           "ClearBrowsingData_TimePeriodChanged_LastDay"));
-      base::UmaHistogramEnumeration(
-          browsing_data::kDeleteBrowsingDataDialogHistogram,
-          DeleteBrowsingDataDialogAction::kLastDaySelected);
       break;
     case TimePeriod::LAST_WEEK:
       base::RecordAction(base::UserMetricsAction(
           "ClearBrowsingData_TimePeriodChanged_LastWeek"));
-      base::UmaHistogramEnumeration(
-          browsing_data::kDeleteBrowsingDataDialogHistogram,
-          DeleteBrowsingDataDialogAction::kLastWeekSelected);
       break;
     case TimePeriod::FOUR_WEEKS:
       base::RecordAction(base::UserMetricsAction(
           "ClearBrowsingData_TimePeriodChanged_LastMonth"));
-      base::UmaHistogramEnumeration(
-          browsing_data::kDeleteBrowsingDataDialogHistogram,
-          DeleteBrowsingDataDialogAction::kLastFourWeeksSelected);
       break;
     case TimePeriod::ALL_TIME:
       base::RecordAction(base::UserMetricsAction(
           "ClearBrowsingData_TimePeriodChanged_Everything"));
-      base::UmaHistogramEnumeration(
-          browsing_data::kDeleteBrowsingDataDialogHistogram,
-          DeleteBrowsingDataDialogAction::kAllTimeSelected);
       break;
     case TimePeriod::OLDER_THAN_30_DAYS:
       base::RecordAction(base::UserMetricsAction(
           "ClearBrowsingData_TimePeriodChanged_OlderThan30Days"));
-      base::UmaHistogramEnumeration(
-          browsing_data::kDeleteBrowsingDataDialogHistogram,
-          DeleteBrowsingDataDialogAction::kOlderThan30DaysSelected);
       break;
   }
 }
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index 5a34600..4782e41 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@
 {
-  "version": "46.5",
-  "log_list_timestamp": "2024-09-29T12:56:22Z",
+  "version": "46.6",
+  "log_list_timestamp": "2024-09-30T12:55:37Z",
   "operators": [
     {
       "name": "Google",
diff --git a/components/data_sharing/public/BUILD.gn b/components/data_sharing/public/BUILD.gn
index c4eb1e67..a633a5f 100644
--- a/components/data_sharing/public/BUILD.gn
+++ b/components/data_sharing/public/BUILD.gn
@@ -55,6 +55,7 @@
       "android/java/src/org/chromium/components/data_sharing/configs/DataSharingCreateUiConfig.java",
       "android/java/src/org/chromium/components/data_sharing/configs/DataSharingJoinUiConfig.java",
       "android/java/src/org/chromium/components/data_sharing/configs/DataSharingManageUiConfig.java",
+      "android/java/src/org/chromium/components/data_sharing/configs/DataSharingUiConfig.java",
       "android/java/src/org/chromium/components/data_sharing/configs/GroupMemberConfig.java",
       "android/java/src/org/chromium/components/data_sharing/configs/MemberPickerConfig.java",
     ]
diff --git a/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/AvatarConfig.java b/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/AvatarConfig.java
index 46df1a28..c2eabc2 100644
--- a/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/AvatarConfig.java
+++ b/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/AvatarConfig.java
@@ -3,59 +3,92 @@
 // found in the LICENSE file.
 
 package org.chromium.components.data_sharing.configs;
+
+import android.content.Context;
+import android.view.ViewGroup;
+
 import androidx.annotation.ColorInt;
 
+import org.chromium.base.Callback;
+import org.chromium.components.data_sharing.GroupMember;
+
+import java.util.List;
+
 /** Config class for Avatars UI. */
 public class AvatarConfig {
 
-    // Properties for avatar customization
+    // Properties for avatar customization.
+    // The size of the avatar icon in pixels.
     private int mAvatarSizeInPixels;
+    // The background color of the avatar icon.
     private @ColorInt int mAvatarBackgroundColor;
+    // The color of the border around the avatar icon.
     private @ColorInt int mBorderColor;
+    // The width of the border around the avatar icon in pixels.
     private int mBorderWidthInPixels;
 
+    // Properties for showing avatars.
+    private Context mContext;
+    private List<GroupMemberAvatarView> mGroupMemberAvatarViews;
+
+    public static class GroupMemberAvatarView {
+
+        private ViewGroup mView;
+        private GroupMember mGroupMember;
+
+        public GroupMemberAvatarView(ViewGroup view, GroupMember member) {
+            this.mView = view;
+            this.mGroupMember = member;
+        }
+
+        public ViewGroup getView() {
+            return mView;
+        }
+
+        public GroupMember getGroupMember() {
+            return mGroupMember;
+        }
+    }
+
+    // TODO (ritikagup) : If this is not called, remove it.
+    private Callback<Boolean> mSuccessCallback;
+
     private AvatarConfig(Builder builder) {
         this.mAvatarSizeInPixels = builder.mAvatarSizeInPixels;
         this.mAvatarBackgroundColor = builder.mAvatarBackgroundColor;
         this.mBorderColor = builder.mBorderColor;
         this.mBorderWidthInPixels = builder.mBorderWidthInPixels;
+        this.mContext = builder.mContext;
+        this.mGroupMemberAvatarViews = builder.mGroupMemberAvatarViews;
+        this.mSuccessCallback = builder.mSuccessCallback;
     }
 
-    /** Returns avatar icon size in pixels. */
     public int getAvatarSizeInPixels() {
         return mAvatarSizeInPixels;
     }
 
-    /** Returns background color for the avatar icon. */
     public @ColorInt int getAvatarBackgroundColor() {
         return mAvatarBackgroundColor;
     }
 
-    /** Returns border colour value for the avatar icon. */
     public @ColorInt int getBorderColor() {
         return mBorderColor;
     }
 
-    /** Returns border width in pixels for avatar icon. */
     public int getBorderWidthInPixels() {
         return mBorderWidthInPixels;
     }
 
-    // Setters for the properties (since they are non-final)
-    public void setAvatarSizeInPixels(int avatarSizeInPixels) {
-        this.mAvatarSizeInPixels = avatarSizeInPixels;
+    public Context getContext() {
+        return mContext;
     }
 
-    public void setAvatarBackgroundColor(@ColorInt int avatarBackgroundColor) {
-        this.mAvatarBackgroundColor = avatarBackgroundColor;
+    public List<GroupMemberAvatarView> getGroupMemberAvatarViews() {
+        return mGroupMemberAvatarViews;
     }
 
-    public void setBorderColor(@ColorInt int borderColor) {
-        this.mBorderColor = borderColor;
-    }
-
-    public void setBorderWidthInPixels(int borderWidthInPixels) {
-        this.mBorderWidthInPixels = borderWidthInPixels;
+    public Callback<Boolean> getSuccessCallback() {
+        return mSuccessCallback;
     }
 
     /** Builder for {@link AvatarConfig}. */
@@ -64,27 +97,52 @@
         private @ColorInt int mAvatarBackgroundColor;
         private @ColorInt int mBorderColor;
         private int mBorderWidthInPixels;
+        private Context mContext;
+        private List<GroupMemberAvatarView> mGroupMemberAvatarViews;
+        private Callback<Boolean> mSuccessCallback;
 
+        /** Sets avatar icon size in pixels. */
         public Builder setAvatarSizeInPixels(int avatarSizeInPixels) {
             this.mAvatarSizeInPixels = avatarSizeInPixels;
             return this;
         }
 
+        /** Sets background color for the avatar icon. */
         public Builder setAvatarBackgroundColor(@ColorInt int avatarBackgroundColor) {
             this.mAvatarBackgroundColor = avatarBackgroundColor;
             return this;
         }
 
+        /** Sets border colour value for the avatar icon. */
         public Builder setBorderColor(@ColorInt int borderColor) {
             this.mBorderColor = borderColor;
             return this;
         }
 
+        /** Sets border width in pixels for avatar icon. */
         public Builder setBorderWidthInPixels(int borderWidthInPixels) {
             this.mBorderWidthInPixels = borderWidthInPixels;
             return this;
         }
 
+        /** Sets current android context. */
+        public Builder setContext(Context context) {
+            this.mContext = context;
+            return this;
+        }
+
+        /** Sets group members and avatar views for them. */
+        public Builder setGroupMemberAvatarViews(
+                List<GroupMemberAvatarView> groupMemberAvatarViews) {
+            this.mGroupMemberAvatarViews = groupMemberAvatarViews;
+            return this;
+        }
+
+        public Builder setSuccessCallback(Callback<Boolean> successCallback) {
+            this.mSuccessCallback = successCallback;
+            return this;
+        }
+
         public AvatarConfig build() {
             return new AvatarConfig(this);
         }
diff --git a/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingCreateUiConfig.java b/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingCreateUiConfig.java
index 9a19939..9be90b3 100644
--- a/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingCreateUiConfig.java
+++ b/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingCreateUiConfig.java
@@ -4,9 +4,85 @@
 
 package org.chromium.components.data_sharing.configs;
 
-/** Config class for Data Sharing Create UI. */
+import android.graphics.Bitmap;
+
+import org.chromium.base.Callback;
+import org.chromium.components.data_sharing.GroupToken;
+import org.chromium.components.sync.protocol.GroupData;
+
+/** Config class for the Data Sharing Create UI. */
 public class DataSharingCreateUiConfig {
 
-    /** Constructor */
-    public DataSharingCreateUiConfig() {}
+    // --- Create Usage Config ---
+    private Bitmap mPreviewImage;
+    private CreateCallback mCreateCallback;
+    private DataSharingUiConfig mCommonConfig;
+
+    /** Callback interface for data sharing create UI events. */
+    public interface CreateCallback {
+        default void onGroupCreated(GroupData groupData) {}
+
+        default void onCancelClicked() {}
+
+        default void getDataSharingUrl(GroupToken tokenSecret, Callback<String> url) {}
+    }
+
+    private DataSharingCreateUiConfig(Builder builder) {
+        this.mPreviewImage = builder.mPreviewImage;
+        this.mCreateCallback = builder.mCreateCallback;
+        this.mCommonConfig = builder.mCommonConfig;
+    }
+
+    public Bitmap getPreviewImage() {
+        return mPreviewImage;
+    }
+
+    public CreateCallback getCreateCallback() {
+        return mCreateCallback;
+    }
+
+    public DataSharingUiConfig getCommonConfig() {
+        return mCommonConfig;
+    }
+
+    // Builder class
+    public static class Builder {
+        private Bitmap mPreviewImage;
+        private CreateCallback mCreateCallback;
+        private DataSharingUiConfig mCommonConfig;
+
+        /**
+         * Sets the preview image for the tab group.
+         *
+         * @param previewImage The preview image to display.
+         */
+        public Builder setPreviewImage(Bitmap previewImage) {
+            this.mPreviewImage = previewImage;
+            return this;
+        }
+
+        /**
+         * Sets the callback for create UI events.
+         *
+         * @param createCallback The callback to use for create events.
+         */
+        public Builder setCreateCallback(CreateCallback createCallback) {
+            this.mCreateCallback = createCallback;
+            return this;
+        }
+
+        /**
+         * Sets the data sharing common UI config.
+         *
+         * @param commonConfig The common UI configuration.
+         */
+        public Builder setCommonConfig(DataSharingUiConfig commonConfig) {
+            this.mCommonConfig = commonConfig;
+            return this;
+        }
+
+        public DataSharingCreateUiConfig build() {
+            return new DataSharingCreateUiConfig(this);
+        }
+    }
 }
diff --git a/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingJoinUiConfig.java b/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingJoinUiConfig.java
index a8dd99f..d8366ce 100644
--- a/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingJoinUiConfig.java
+++ b/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingJoinUiConfig.java
@@ -4,9 +4,121 @@
 
 package org.chromium.components.data_sharing.configs;
 
-/** Config class for Data Sharing Join UI. */
+import android.graphics.Bitmap;
+
+import org.chromium.components.data_sharing.GroupToken;
+import org.chromium.components.data_sharing.SharedDataPreview;
+import org.chromium.components.sync.protocol.GroupData;
+
+/** Config class for the Data Sharing Join UI. */
 public class DataSharingJoinUiConfig {
 
-    /** Constructor */
-    public DataSharingJoinUiConfig() {}
+    // --- Group related Info ---
+    private GroupToken mGroupToken;
+
+    // --- Tab Group Details ---
+    private Bitmap mPreviewImage;
+    private SharedDataPreview mSharedDataPreview;
+
+    // --- Join Usage Config ---
+    private JoinCallback mJoinCallback;
+    private DataSharingUiConfig mCommonConfig;
+
+    /** Callback interface for data sharing join UI events. */
+    public interface JoinCallback {
+        default void onGroupJoined(GroupData groupData) {}
+
+        default void onPreviewTabGroupDetailsClicked(String groupId) {}
+    }
+
+    private DataSharingJoinUiConfig(Builder builder) {
+        this.mGroupToken = builder.mGroupToken;
+        this.mPreviewImage = builder.mPreviewImage;
+        this.mSharedDataPreview = builder.mSharedDataPreview;
+        this.mJoinCallback = builder.mJoinCallback;
+        this.mCommonConfig = builder.mCommonConfig;
+    }
+
+    public GroupToken getGroupToken() {
+        return mGroupToken;
+    }
+
+    public Bitmap getPreviewImage() {
+        return mPreviewImage;
+    }
+
+    public SharedDataPreview getSharedDataPreview() {
+        return mSharedDataPreview;
+    }
+
+    public JoinCallback getJoinCallback() {
+        return mJoinCallback;
+    }
+
+    public DataSharingUiConfig getCommonConfig() {
+        return mCommonConfig;
+    }
+
+    // Builder class
+    public static class Builder {
+        private GroupToken mGroupToken;
+        private Bitmap mPreviewImage;
+        private SharedDataPreview mSharedDataPreview;
+        private JoinCallback mJoinCallback;
+        private DataSharingUiConfig mCommonConfig;
+
+        /**
+         * Sets the group token for the data sharing group.
+         *
+         * @param groupToken The token for the data sharing group includes groupId and token.
+         */
+        public Builder setGroupToken(GroupToken groupToken) {
+            this.mGroupToken = groupToken;
+            return this;
+        }
+
+        /**
+         * Sets the preview image for the tab group.
+         *
+         * @param previewImage The preview image of tab group.
+         */
+        public Builder setPreviewImage(Bitmap previewImage) {
+            this.mPreviewImage = previewImage;
+            return this;
+        }
+
+        /**
+         * Sets the shared data preview for tab group.
+         *
+         * @param sharedDataPreview Details about the tab group.
+         */
+        public Builder setSharedDataPreview(SharedDataPreview sharedDataPreview) {
+            this.mSharedDataPreview = sharedDataPreview;
+            return this;
+        }
+
+        /**
+         * Sets the callback for join UI events.
+         *
+         * @param joinCallback The callback to use for join UI events.
+         */
+        public Builder setJoinCallback(JoinCallback joinCallback) {
+            this.mJoinCallback = joinCallback;
+            return this;
+        }
+
+        /**
+         * Sets the data sharing common UI config.
+         *
+         * @param commonConfig The common UI config.
+         */
+        public Builder setCommonConfig(DataSharingUiConfig commonConfig) {
+            this.mCommonConfig = commonConfig;
+            return this;
+        }
+
+        public DataSharingJoinUiConfig build() {
+            return new DataSharingJoinUiConfig(this);
+        }
+    }
 }
diff --git a/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingManageUiConfig.java b/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingManageUiConfig.java
index 21210595..0112621 100644
--- a/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingManageUiConfig.java
+++ b/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingManageUiConfig.java
@@ -4,9 +4,99 @@
 
 package org.chromium.components.data_sharing.configs;
 
-/** Config class for Data Sharing Manage UI. */
+import org.chromium.base.Callback;
+import org.chromium.components.data_sharing.GroupToken;
+import org.chromium.components.sync.protocol.GroupData;
+import org.chromium.components.sync.protocol.GroupMember;
+
+/** Config class for the Data Sharing Manage UI. */
 public class DataSharingManageUiConfig {
 
-    /** Constructor */
-    public DataSharingManageUiConfig() {}
+    // --- Group related Info ---
+    private GroupToken mGroupToken;
+
+    // --- Manage Usage Config ---
+    private ManageCallback mManageCallback;
+    private DataSharingUiConfig mCommonConfig;
+
+    /** Callback interface for data sharing Manage UI events. */
+    public interface ManageCallback {
+        default void onLinkSharingToggled(boolean isLinkSharingEnabled, GroupData groupData) {}
+
+        default void onStopSharingInitiated(GroupData groupData, Callback<Boolean> readyToStop) {}
+
+        default void onStopSharingSuccess(GroupData groupData) {}
+
+        default void onMemberRemoved(GroupMember member) {}
+
+        default void onMemberBlocked(GroupMember member) {}
+
+        default void onMemberRemovedAndStopSharingInitiated(
+                GroupMember member, GroupData groupData, Callback<Boolean> readyToStop) {}
+
+        default void onMemberBlockedAndLeaveGroup(GroupMember member, GroupData groupData) {}
+
+        default void onLeaveGroup() {}
+
+        default void getDataSharingUrl(GroupToken groupToken, Callback<String> url) {}
+    }
+
+    private DataSharingManageUiConfig(Builder builder) {
+        this.mGroupToken = builder.mGroupToken;
+        this.mManageCallback = builder.mManageCallback;
+        this.mCommonConfig = builder.mCommonConfig;
+    }
+
+    public GroupToken getGroupToken() {
+        return mGroupToken;
+    }
+
+    public ManageCallback getManageCallback() {
+        return mManageCallback;
+    }
+
+    public DataSharingUiConfig getCommonConfig() {
+        return mCommonConfig;
+    }
+
+    // Builder class
+    public static class Builder {
+        private GroupToken mGroupToken;
+        private ManageCallback mManageCallback;
+        private DataSharingUiConfig mCommonConfig;
+
+        /**
+         * Sets the group token for the data sharing group.
+         *
+         * @param groupToken The token for the data sharing group includes groupId and token.
+         */
+        public Builder setGroupToken(GroupToken groupToken) {
+            this.mGroupToken = groupToken;
+            return this;
+        }
+
+        /**
+         * Sets the callback for manage UI events.
+         *
+         * @param manageCallback The callback to use for managing UI events.
+         */
+        public Builder setManageCallback(ManageCallback manageCallback) {
+            this.mManageCallback = manageCallback;
+            return this;
+        }
+
+        /**
+         * Sets the data sharing common UI config.
+         *
+         * @param commonConfig The common UI config.
+         */
+        public Builder setCommonConfig(DataSharingUiConfig commonConfig) {
+            this.mCommonConfig = commonConfig;
+            return this;
+        }
+
+        public DataSharingManageUiConfig build() {
+            return new DataSharingManageUiConfig(this);
+        }
+    }
 }
diff --git a/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingUiConfig.java b/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingUiConfig.java
new file mode 100644
index 0000000..b25bb3e
--- /dev/null
+++ b/components/data_sharing/public/android/java/src/org/chromium/components/data_sharing/configs/DataSharingUiConfig.java
@@ -0,0 +1,120 @@
+// 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.
+
+package org.chromium.components.data_sharing.configs;
+
+import android.content.Context;
+
+import org.chromium.url.GURL;
+
+/** Config class for the Data Sharing UI. */
+public class DataSharingUiConfig {
+
+    // --- Form Factor Config ---
+    private boolean mIsTablet;
+
+    // --- Tab Group Details ---
+    private String mTabGroupName;
+
+    // --- Usage Config ---
+    private Context mContext;
+    private GURL mLearnMoreHyperLink;
+    private DataSharingCallback mDataSharingCallback;
+
+    /** Callback interface for common data sharing UI events. */
+    public interface DataSharingCallback {
+        default void onLearnMoreAboutSharedTabGroupsClicked(GURL url) {}
+    }
+
+    private DataSharingUiConfig(Builder builder) {
+        this.mIsTablet = builder.mIsTablet;
+        this.mContext = builder.mContext;
+        this.mTabGroupName = builder.mTabGroupName;
+        this.mLearnMoreHyperLink = builder.mLearnMoreHyperLink;
+        this.mDataSharingCallback = builder.mDataSharingCallback;
+    }
+
+    public boolean getIsTablet() {
+        return mIsTablet;
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    public String getTabGroupName() {
+        return mTabGroupName;
+    }
+
+    public GURL getLearnMoreHyperLink() {
+        return mLearnMoreHyperLink;
+    }
+
+    public DataSharingCallback getDataSharingCallback() {
+        return mDataSharingCallback;
+    }
+
+    // Builder class
+    public static class Builder {
+        private boolean mIsTablet;
+        private Context mContext;
+        private String mTabGroupName;
+        private GURL mLearnMoreHyperLink;
+        private DataSharingCallback mDataSharingCallback;
+
+        /**
+         * Sets whether the device is a tablet.
+         *
+         * @param isTablet True if the device is a tablet.
+         */
+        public Builder setIsTablet(boolean isTablet) {
+            this.mIsTablet = isTablet;
+            return this;
+        }
+
+        /**
+         * Sets the current context.
+         *
+         * @param context The current android context.
+         */
+        public Builder setContext(Context context) {
+            this.mContext = context;
+            return this;
+        }
+
+        /**
+         * Sets the name of the tab group.
+         *
+         * @param tabGroupName The name of the tab group.
+         */
+        public Builder setTabGroupName(String tabGroupName) {
+            this.mTabGroupName = tabGroupName;
+            return this;
+        }
+
+        /**
+         * Sets the hyperlink for "learn more about shared tab groups".
+         *
+         * @param learnMoreHyperLink The hyperlink to learn more section.
+         */
+        public Builder setLearnMoreHyperLink(GURL learnMoreHyperLink) {
+            this.mLearnMoreHyperLink = learnMoreHyperLink;
+            return this;
+        }
+
+        /**
+         * Sets the callback for data sharing UI events.
+         *
+         * @param dataSharingCallback The callback for data sharing UI events.
+         */
+        public Builder setDataSharingCallback(DataSharingCallback dataSharingCallback) {
+            this.mDataSharingCallback = dataSharingCallback;
+            return this;
+        }
+
+        public DataSharingUiConfig build() {
+            return new DataSharingUiConfig(this);
+        }
+    }
+}
diff --git a/components/gwp_asan/common/BUILD.gn b/components/gwp_asan/common/BUILD.gn
index 7b39043..c77388f6 100644
--- a/components/gwp_asan/common/BUILD.gn
+++ b/components/gwp_asan/common/BUILD.gn
@@ -51,5 +51,8 @@
 
 fuzzer_test("pack_stack_trace_differential_fuzzer") {
   sources = [ "pack_stack_trace_differential_fuzzer.cc" ]
-  deps = [ "//components/gwp_asan/common" ]
+  deps = [
+    "//base",
+    "//components/gwp_asan/common",
+  ]
 }
diff --git a/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc b/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc
index 53669de7..fbe7eacc 100644
--- a/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc
+++ b/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc
@@ -7,12 +7,14 @@
 #pragma allow_unsafe_buffers
 #endif
 
-#include "components/gwp_asan/common/pack_stack_trace.h"
-
 #include <string.h>
+
 #include <algorithm>
 #include <memory>
 
+#include "base/containers/heap_array.h"
+#include "components/gwp_asan/common/pack_stack_trace.h"
+
 // Tests that whatever we give to Pack() is the same as what comes out of
 // Unpack().
 
@@ -30,23 +32,26 @@
   // We don't need a buffer large than Size*10 as the longest variable length
   // encoding of a 64-bit integer is 10 bytes long.)
   size_t packed_array_size = std::min(Size * 10, packed_max_size);
-  std::unique_ptr<uint8_t[]> packed(new uint8_t[packed_array_size]);
+  base::HeapArray<uint8_t> packed =
+      base::HeapArray<uint8_t>::Uninit(packed_array_size);
   size_t packed_out =
       gwp_asan::internal::Pack(reinterpret_cast<const uintptr_t*>(Data),
-                               entries, packed.get(), packed_max_size);
+                               entries, packed.data(), packed_max_size);
   if (packed_out > packed_array_size)
     __builtin_trap();
 
   size_t unpacked_array_size = std::min(unpacked_max_size, entries);
-  std::unique_ptr<uintptr_t[]> unpacked(new uintptr_t[unpacked_array_size]);
+  base::HeapArray<uintptr_t> unpacked =
+      base::HeapArray<uintptr_t>::Uninit(unpacked_array_size);
   size_t unpacked_out = gwp_asan::internal::Unpack(
-      packed.get(), packed_out, unpacked.get(), unpacked_max_size);
+      packed.data(), packed_out, unpacked.data(), unpacked_max_size);
   // We can only be sure there was enough room to pack the entire input when
   // packed_max_size was larger than the space allocated for packed data.
   if (packed_max_size > packed_array_size &&
       unpacked_out != unpacked_array_size)
     __builtin_trap();
-  if (memcmp(Data, unpacked.get(), unpacked_out * sizeof(uintptr_t)))
+  if (memcmp(Data, unpacked.data(), unpacked_out * sizeof(uintptr_t))) {
     __builtin_trap();
+  }
   return 0;
 }
diff --git a/components/language/ios/browser/ios_language_detection_tab_helper.mm b/components/language/ios/browser/ios_language_detection_tab_helper.mm
index 248d1e3a0..e6889b8f 100644
--- a/components/language/ios/browser/ios_language_detection_tab_helper.mm
+++ b/components/language/ios/browser/ios_language_detection_tab_helper.mm
@@ -44,52 +44,9 @@
   kTFLiteModelUsed = 0,
   kTFLiteModelUnavailable = 1,
   kTFLiteModelDisabled = 2,
-  kTFLiteModelIgnored = 3,
-  kMaxValue = kTFLiteModelIgnored
+  kTFLiteModelIgnored_DEPRECATED = 3,
+  kMaxValue = kTFLiteModelIgnored_DEPRECATED
 };
-
-enum class LanguageDetectionComparison {
-  kTFLiteModelOnly = 0,
-  kCLD3ModelOnly = 1,
-  kBothModelFailed = 2,
-  kBothModelAgree = 3,
-  kBothModelDisagree = 4,
-  kMaxValue = kBothModelDisagree
-};
-
-void ComparePageLanguageDetection(const std::string& tflite_language,
-                                  const std::string& cld3_language) {
-  bool tflite_failed = tflite_language.empty() ||
-                       tflite_language == translate::kUnknownLanguageCode;
-  bool cld3_failed =
-      cld3_language.empty() || cld3_language == translate::kUnknownLanguageCode;
-
-  if (tflite_failed) {
-    if (cld3_failed) {
-      base::UmaHistogramEnumeration(
-          "IOS.Translate.PageLoad.LanguageDetectionComparison",
-          LanguageDetectionComparison::kBothModelFailed);
-    } else {
-      base::UmaHistogramEnumeration(
-          "IOS.Translate.PageLoad.LanguageDetectionComparison",
-          LanguageDetectionComparison::kCLD3ModelOnly);
-    }
-  } else {
-    if (cld3_failed) {
-      base::UmaHistogramEnumeration(
-          "IOS.Translate.PageLoad.LanguageDetectionComparison",
-          LanguageDetectionComparison::kTFLiteModelOnly);
-    } else if (cld3_language == tflite_language) {
-      base::UmaHistogramEnumeration(
-          "IOS.Translate.PageLoad.LanguageDetectionComparison",
-          LanguageDetectionComparison::kBothModelAgree);
-    } else {
-      base::UmaHistogramEnumeration(
-          "IOS.Translate.PageLoad.LanguageDetectionComparison",
-          LanguageDetectionComparison::kBothModelDisagree);
-    }
-  }
-}
 }  // namespace
 
 IOSLanguageDetectionTabHelper::IOSLanguageDetectionTabHelper(
@@ -233,23 +190,11 @@
         kTranslateLanguageDetectionTFLiteModelEvaluationDuration,
         timer.Elapsed());
 
-    if (!translate::IsTFLiteLanguageDetectionIgnoreEnabled()) {
-      *detection_model_version = language_detection_model_->GetModelVersion();
-      base::UmaHistogramEnumeration(
-          "IOS.Translate.PageLoad.LanguageDetectionMethod",
-          LanguageDetectionMethod::kTFLiteModelUsed);
-      return tflite_language;
-    }
-
+    *detection_model_version = language_detection_model_->GetModelVersion();
     base::UmaHistogramEnumeration(
         "IOS.Translate.PageLoad.LanguageDetectionMethod",
-        LanguageDetectionMethod::kTFLiteModelIgnored);
-    std::string cld3_language = ::translate::DeterminePageLanguage(
-        code, html_lang, contents, model_detected_language, is_model_reliable,
-        model_reliability_score);
-
-    ComparePageLanguageDetection(tflite_language, cld3_language);
-    return cld3_language;
+        LanguageDetectionMethod::kTFLiteModelUsed);
+    return tflite_language;
   }
 
   if (translate::IsTFLiteLanguageDetectionEnabled()) {
diff --git a/components/permissions/features.cc b/components/permissions/features.cc
index 231863e..6c6f3a50 100644
--- a/components/permissions/features.cc
+++ b/components/permissions/features.cc
@@ -66,10 +66,6 @@
              "PermissionOnDeviceGeolocationPredictions",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kPermissionDedicatedCpssSetting,
-             "PermissionDedicatedCpssSettings",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kPermissionPredictionsV2,
              "PermissionPredictionsV2",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/components/permissions/features.h b/components/permissions/features.h
index 9e0bd08..1ef5d5c 100644
--- a/components/permissions/features.h
+++ b/components/permissions/features.h
@@ -44,9 +44,6 @@
 BASE_DECLARE_FEATURE(kPermissionOnDeviceGeolocationPredictions);
 
 COMPONENT_EXPORT(PERMISSIONS_COMMON)
-BASE_DECLARE_FEATURE(kPermissionDedicatedCpssSetting);
-
-COMPONENT_EXPORT(PERMISSIONS_COMMON)
 BASE_DECLARE_FEATURE(kPermissionPredictionsV2);
 
 COMPONENT_EXPORT(PERMISSIONS_COMMON)
diff --git a/components/plus_addresses/fake_plus_address_service.cc b/components/plus_addresses/fake_plus_address_service.cc
index 0805e26..c84c3ea1 100644
--- a/components/plus_addresses/fake_plus_address_service.cc
+++ b/components/plus_addresses/fake_plus_address_service.cc
@@ -41,13 +41,14 @@
 
 FakePlusAddressService::~FakePlusAddressService() = default;
 
-void FakePlusAddressService::GetSuggestions(
+std::vector<autofill::Suggestion>
+FakePlusAddressService::GetSuggestionsFromPlusAddresses(
+    const std::vector<std::string>& plus_addresses,
     const url::Origin& last_committed_primary_main_frame_origin,
     bool is_off_the_record,
     const autofill::PasswordFormClassification& focused_form_classification,
     const autofill::FormFieldData& focused_field,
-    autofill::AutofillSuggestionTriggerSource trigger_source,
-    GetSuggestionsCallback callback) {
+    autofill::AutofillSuggestionTriggerSource trigger_source) {
   if (IsPlusAddressCreationEnabled(last_committed_primary_main_frame_origin,
                                    is_off_the_record)) {
     Suggestion suggestion(
@@ -58,8 +59,7 @@
     suggestion.icon = Suggestion::Icon::kPlusAddress;
     suggestion.feature_for_iph =
         &feature_engagement::kIPHPlusAddressCreateSuggestionFeature;
-    std::move(callback).Run({suggestion});
-    return;
+    return {suggestion};
   }
 
   if (IsPlusAddressFillingEnabled(last_committed_primary_main_frame_origin)) {
@@ -71,8 +71,9 @@
           IDS_PLUS_ADDRESS_FILL_SUGGESTION_SECONDARY_TEXT))}};
     }
     suggestion.icon = Suggestion::Icon::kPlusAddress;
-    std::move(callback).Run({suggestion});
+    return {suggestion};
   }
+  return {};
 }
 
 bool FakePlusAddressService::IsPlusAddressFillingEnabled(
@@ -103,6 +104,17 @@
   }
 }
 
+void FakePlusAddressService::GetAffiliatedPlusAddresses(
+    const url::Origin& origin,
+    base::OnceCallback<void(std::vector<std::string>)> callback) {
+  if (should_return_no_affiliated_plus_profiles_) {
+    std::move(callback).Run({});
+  } else {
+    std::move(callback).Run(
+        std::vector<std::string>{plus_addresses::test::kFakePlusAddress});
+  }
+}
+
 void FakePlusAddressService::ReservePlusAddress(
     const url::Origin& origin,
     PlusAddressRequestCallback on_completed) {
diff --git a/components/plus_addresses/fake_plus_address_service.h b/components/plus_addresses/fake_plus_address_service.h
index c957d6ac..07ba44b 100644
--- a/components/plus_addresses/fake_plus_address_service.h
+++ b/components/plus_addresses/fake_plus_address_service.h
@@ -37,19 +37,22 @@
   static constexpr char kFacet[] = "https://facet.bar";
 
   // PlusAddressService:
-  void GetSuggestions(
+  std::vector<autofill::Suggestion> GetSuggestionsFromPlusAddresses(
+      const std::vector<std::string>& plus_addresses,
       const url::Origin& last_committed_primary_main_frame_origin,
       bool is_off_the_record,
       const autofill::PasswordFormClassification& focused_form_classification,
       const autofill::FormFieldData& focused_field,
-      autofill::AutofillSuggestionTriggerSource trigger_source,
-      GetSuggestionsCallback callback) override;
+      autofill::AutofillSuggestionTriggerSource trigger_source) override;
   bool IsPlusAddressFillingEnabled(const url::Origin& origin) const override;
   bool IsPlusAddressCreationEnabled(const url::Origin& origin,
                                     bool is_off_the_record) const override;
   bool IsPlusAddress(const std::string& potential_plus_address) const override;
   void GetAffiliatedPlusProfiles(const url::Origin& origin,
                                  GetPlusProfilesCallback callback) override;
+  void GetAffiliatedPlusAddresses(
+      const url::Origin& origin,
+      base::OnceCallback<void(std::vector<std::string>)> callback) override;
   void ReservePlusAddress(const url::Origin& origin,
                           PlusAddressRequestCallback on_completed) override;
   void ConfirmPlusAddress(const url::Origin& origin,
diff --git a/components/plus_addresses/plus_address_service.cc b/components/plus_addresses/plus_address_service.cc
index 7d412b49..438a058 100644
--- a/components/plus_addresses/plus_address_service.cc
+++ b/components/plus_addresses/plus_address_service.cc
@@ -307,48 +307,26 @@
           std::move(callback)));
 }
 
-void PlusAddressService::GetSuggestions(
-    const url::Origin& last_committed_primary_main_frame_origin,
+std::vector<Suggestion> PlusAddressService::GetSuggestionsFromPlusAddresses(
+    const std::vector<std::string>& plus_addresses,
+    const url::Origin& origin,
     bool is_off_the_record,
     const PasswordFormClassification& focused_form_classification,
     const FormFieldData& focused_field,
-    AutofillSuggestionTriggerSource trigger_source,
-    GetSuggestionsCallback callback) {
-  if (!IsPlusAddressFillingEnabled(last_committed_primary_main_frame_origin)) {
-    std::move(callback).Run({});
-    return;
+    AutofillSuggestionTriggerSource trigger_source) {
+  if (!IsPlusAddressFillingEnabled(origin)) {
+    return {};
   }
 
-  plus_address_match_helper_.GetAffiliatedPlusProfiles(
-      OriginToFacet(last_committed_primary_main_frame_origin),
-      base::BindOnce(&PlusAddressService::OnGetAffiliatedPlusProfiles,
-                     weak_factory_.GetWeakPtr(),
-                     last_committed_primary_main_frame_origin,
-                     focused_form_classification, focused_field, trigger_source,
-                     is_off_the_record, std::move(callback)));
-}
-
-Suggestion PlusAddressService::GetManagePlusAddressSuggestion() const {
-  return PlusAddressSuggestionGenerator::GetManagePlusAddressSuggestion();
-}
-
-void PlusAddressService::OnGetAffiliatedPlusProfiles(
-    url::Origin origin,
-    const PasswordFormClassification& focused_form_classification,
-    const FormFieldData& focused_field,
-    AutofillSuggestionTriggerSource trigger_source,
-    bool is_off_the_record,
-    GetSuggestionsCallback callback,
-    std::vector<PlusProfile> affiliated_profiles) {
   const bool is_creation_enabled =
       IsPlusAddressCreationEnabled(origin, is_off_the_record);
   std::vector<Suggestion> suggestions =
       PlusAddressSuggestionGenerator(
           &setting_service_.get(), plus_address_allocator_.get(),
           std::move(origin), GetPrimaryEmail().value_or(""))
-          .GetSuggestions(is_creation_enabled, focused_form_classification,
-                          focused_field, trigger_source,
-                          std::move(affiliated_profiles));
+          .GetSuggestions(plus_addresses, is_creation_enabled,
+                          focused_form_classification, focused_field,
+                          trigger_source);
   const autofill::DenseSet<SuggestionType> suggestion_types(suggestions,
                                                             &Suggestion::type);
 
@@ -361,7 +339,11 @@
     RecordAutofillSuggestionEvent(AutofillPlusAddressDelegate::SuggestionEvent::
                                       kCreateNewPlusAddressSuggested);
   }
-  std::move(callback).Run({std::move(suggestions)});
+  return suggestions;
+}
+
+Suggestion PlusAddressService::GetManagePlusAddressSuggestion() const {
+  return PlusAddressSuggestionGenerator::GetManagePlusAddressSuggestion();
 }
 
 void PlusAddressService::ReservePlusAddress(
diff --git a/components/plus_addresses/plus_address_service.h b/components/plus_addresses/plus_address_service.h
index f476571f..3c34a6d2 100644
--- a/components/plus_addresses/plus_address_service.h
+++ b/components/plus_addresses/plus_address_service.h
@@ -104,13 +104,13 @@
   void GetAffiliatedPlusAddresses(
       const url::Origin& origin,
       base::OnceCallback<void(std::vector<std::string>)> callback) override;
-  void GetSuggestions(
-      const url::Origin& last_committed_primary_main_frame_origin,
+  std::vector<autofill::Suggestion> GetSuggestionsFromPlusAddresses(
+      const std::vector<std::string>& plus_addresses,
+      const url::Origin& origin,
       bool is_off_the_record,
       const autofill::PasswordFormClassification& focused_form_classification,
       const autofill::FormFieldData& focused_field,
-      autofill::AutofillSuggestionTriggerSource trigger_source,
-      GetSuggestionsCallback callback) override;
+      autofill::AutofillSuggestionTriggerSource trigger_source) override;
   autofill::Suggestion GetManagePlusAddressSuggestion() const override;
   void RecordAutofillSuggestionEvent(SuggestionEvent suggestion_event) override;
   void OnPlusAddressSuggestionShown(
@@ -244,19 +244,6 @@
   // on `excluded_sites_` set, and scheme is http or https.
   bool IsSupportedOrigin(const url::Origin& origin) const;
 
-  // Called when PlusAddressService::OnGetAffiliatedPlusProfiles is resolved.
-  // Builds a list of suggestions from the list of `affiliated_profiles` and
-  // returns it via the `callback`.
-  // TODO(crbug.com/340494671): Move to the unnamed namespace.
-  void OnGetAffiliatedPlusProfiles(
-      url::Origin origin,
-      const autofill::PasswordFormClassification& focused_form_classification,
-      const autofill::FormFieldData& focused_field,
-      autofill::AutofillSuggestionTriggerSource trigger_source,
-      bool is_off_the_record,
-      GetSuggestionsCallback callback,
-      std::vector<PlusProfile> affiliated_profiles);
-
   // Reacts to the server response for confirming a plus address from an inline
   // suggestion.
   // - In all cases, it hides the showing suggestions.
diff --git a/components/plus_addresses/plus_address_service_unittest.cc b/components/plus_addresses/plus_address_service_unittest.cc
index 7d197f1..3c6b2536 100644
--- a/components/plus_addresses/plus_address_service_unittest.cc
+++ b/components/plus_addresses/plus_address_service_unittest.cc
@@ -181,13 +181,12 @@
     InitService();
   }
 
-  testing::AssertionResult ExpectServiceToReturnSuggestions(
+  std::vector<Suggestion> FetchPlusAddressSuggestions(
       const url::Origin& origin,
       bool is_off_the_record,
       const PasswordFormClassification& focused_form_classification,
       const FormFieldData& focused_field,
-      autofill::AutofillSuggestionTriggerSource trigger_source,
-      const auto& matcher) {
+      autofill::AutofillSuggestionTriggerSource trigger_source) {
     // Empty psl extension by default.
     ON_CALL(affiliation_service(), GetPSLExtensions)
         .WillByDefault(RunOnceCallback<0>(std::vector<std::string>()));
@@ -199,19 +198,21 @@
         .WillByDefault(RunOnceCallback<1>(
             std::vector<affiliations::GroupedFacets>{group}));
 
-    base::MockCallback<PlusAddressService::GetSuggestionsCallback> callback;
-    int calls = 0;
+    base::MockCallback<base::OnceCallback<void(std::vector<std::string>)>>
+        callback;
+    std::vector<std::string> affiliated_plus_addresses;
+    base::RunLoop run_loop;
     ON_CALL(callback, Run)
-        .WillByDefault([&](std::vector<autofill::Suggestion> suggestions) {
-          EXPECT_THAT(suggestions, matcher);
-          ++calls;
+        .WillByDefault([&](std::vector<std::string> plus_addresses) {
+          affiliated_plus_addresses = std::move(plus_addresses);
+          run_loop.Quit();
         });
-    service().GetSuggestions(origin, is_off_the_record,
-                             focused_form_classification, focused_field,
-                             trigger_source, callback.Get());
-    return calls == 1
-               ? testing::AssertionSuccess()
-               : (testing::AssertionFailure() << "Error fetching suggestions.");
+    service().GetAffiliatedPlusAddresses(origin, callback.Get());
+    run_loop.Quit();
+
+    return service().GetSuggestionsFromPlusAddresses(
+        affiliated_plus_addresses, origin, is_off_the_record,
+        focused_form_classification, focused_field, trigger_source);
   }
 
  protected:
@@ -1531,11 +1532,11 @@
 
   // We offer filling if the field is empty.
   FormFieldData focused_field;
-  EXPECT_TRUE(ExpectServiceToReturnSuggestions(
-      origin, /*is_off_the_record=*/false, PasswordFormClassification(),
-      focused_field,
-      AutofillSuggestionTriggerSource::kFormControlElementClicked,
-      IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
+  EXPECT_THAT(FetchPlusAddressSuggestions(
+                  origin, /*is_off_the_record=*/false,
+                  PasswordFormClassification(), focused_field,
+                  AutofillSuggestionTriggerSource::kFormControlElementClicked),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
   histogram_tester.ExpectUniqueSample(
       kPlusAddressSuggestionMetric,
       SuggestionEvent::kExistingPlusAddressSuggested, 1);
@@ -1543,11 +1544,11 @@
   // If the user types a letter and it matches the plus address (after
   // normalization), the plus address continues to be offered.
   focused_field.set_value(u"P");
-  EXPECT_TRUE(ExpectServiceToReturnSuggestions(
-      origin, /*is_off_the_record=*/false, PasswordFormClassification(),
-      focused_field,
-      AutofillSuggestionTriggerSource::kFormControlElementClicked,
-      IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
+  EXPECT_THAT(FetchPlusAddressSuggestions(
+                  origin, /*is_off_the_record=*/false,
+                  PasswordFormClassification(), focused_field,
+                  AutofillSuggestionTriggerSource::kFormControlElementClicked),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
   histogram_tester.ExpectUniqueSample(
       kPlusAddressSuggestionMetric,
       SuggestionEvent::kExistingPlusAddressSuggested, 2);
@@ -1555,10 +1556,11 @@
   // If the value does not match the prefix of the plus address, nothing is
   // shown.
   focused_field.set_value(u"pp");
-  EXPECT_TRUE(ExpectServiceToReturnSuggestions(
-      origin, /*is_off_the_record=*/false, PasswordFormClassification(),
-      focused_field,
-      AutofillSuggestionTriggerSource::kFormControlElementClicked, IsEmpty()));
+  EXPECT_THAT(FetchPlusAddressSuggestions(
+                  origin, /*is_off_the_record=*/false,
+                  PasswordFormClassification(), focused_field,
+                  AutofillSuggestionTriggerSource::kFormControlElementClicked),
+              IsEmpty());
   histogram_tester.ExpectUniqueSample(
       kPlusAddressSuggestionMetric,
       SuggestionEvent::kExistingPlusAddressSuggested, 2);
@@ -1575,11 +1577,12 @@
 
   // We offer filling if the field is empty.
   FormFieldData focused_field;
-  EXPECT_TRUE(ExpectServiceToReturnSuggestions(
-      origin, /*is_off_the_record=*/false, PasswordFormClassification(),
-      focused_field,
-      AutofillSuggestionTriggerSource::kManualFallbackPlusAddresses,
-      IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
+  EXPECT_THAT(
+      FetchPlusAddressSuggestions(
+          origin, /*is_off_the_record=*/false, PasswordFormClassification(),
+          focused_field,
+          AutofillSuggestionTriggerSource::kManualFallbackPlusAddresses),
+      IsSingleFillPlusAddressSuggestion(*profile.plus_address));
   histogram_tester.ExpectUniqueSample(
       kPlusAddressSuggestionMetric,
       SuggestionEvent::kExistingPlusAddressSuggested, 1);
@@ -1587,11 +1590,12 @@
   // We also offer filling if the field is not empty and the prefix does not
   // match the address.
   focused_field.set_value(u"pp");
-  EXPECT_TRUE(ExpectServiceToReturnSuggestions(
-      origin, /*is_off_the_record=*/false, PasswordFormClassification(),
-      focused_field,
-      AutofillSuggestionTriggerSource::kManualFallbackPlusAddresses,
-      IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
+  EXPECT_THAT(
+      FetchPlusAddressSuggestions(
+          origin, /*is_off_the_record=*/false, PasswordFormClassification(),
+          focused_field,
+          AutofillSuggestionTriggerSource::kManualFallbackPlusAddresses),
+      IsSingleFillPlusAddressSuggestion(*profile.plus_address));
   histogram_tester.ExpectUniqueSample(
       kPlusAddressSuggestionMetric,
       SuggestionEvent::kExistingPlusAddressSuggested, 2);
@@ -1605,21 +1609,22 @@
 
   // We offer creation if the field is empty.
   FormFieldData focused_field;
-  EXPECT_TRUE(ExpectServiceToReturnSuggestions(
-      origin, /*is_off_the_record=*/false, PasswordFormClassification(),
-      focused_field,
-      AutofillSuggestionTriggerSource::kFormControlElementClicked,
-      IsSingleCreatePlusAddressSuggestion()));
+  EXPECT_THAT(FetchPlusAddressSuggestions(
+                  origin, /*is_off_the_record=*/false,
+                  PasswordFormClassification(), focused_field,
+                  AutofillSuggestionTriggerSource::kFormControlElementClicked),
+              IsSingleCreatePlusAddressSuggestion());
   histogram_tester.ExpectUniqueSample(
       kPlusAddressSuggestionMetric,
       SuggestionEvent::kCreateNewPlusAddressSuggested, 1);
 
   // If the field value is not empty, nothing is shown.
   focused_field.set_value(u"some text");
-  EXPECT_TRUE(ExpectServiceToReturnSuggestions(
-      origin, /*is_off_the_record=*/false, PasswordFormClassification(),
-      focused_field,
-      AutofillSuggestionTriggerSource::kFormControlElementClicked, IsEmpty()));
+  EXPECT_THAT(FetchPlusAddressSuggestions(
+                  origin, /*is_off_the_record=*/false,
+                  PasswordFormClassification(), focused_field,
+                  AutofillSuggestionTriggerSource::kFormControlElementClicked),
+              IsEmpty());
   histogram_tester.ExpectUniqueSample(
       kPlusAddressSuggestionMetric,
       SuggestionEvent::kCreateNewPlusAddressSuggested, 1);
@@ -1634,21 +1639,23 @@
   const auto origin = url::Origin::Create(GURL("https://foo.com"));
 
   FormFieldData focused_field;
-  EXPECT_TRUE(ExpectServiceToReturnSuggestions(
-      origin, /*is_off_the_record=*/false, PasswordFormClassification(),
-      focused_field,
-      AutofillSuggestionTriggerSource::kManualFallbackPlusAddresses,
-      IsSingleCreatePlusAddressSuggestion()));
+  EXPECT_THAT(
+      FetchPlusAddressSuggestions(
+          origin, /*is_off_the_record=*/false, PasswordFormClassification(),
+          focused_field,
+          AutofillSuggestionTriggerSource::kManualFallbackPlusAddresses),
+      IsSingleCreatePlusAddressSuggestion());
   histogram_tester.ExpectUniqueSample(
       kPlusAddressSuggestionMetric,
       SuggestionEvent::kCreateNewPlusAddressSuggested, 1);
 
   focused_field.set_value(u"some text");
-  EXPECT_TRUE(ExpectServiceToReturnSuggestions(
-      origin, /*is_off_the_record=*/false, PasswordFormClassification(),
-      focused_field,
-      AutofillSuggestionTriggerSource::kManualFallbackPlusAddresses,
-      IsSingleCreatePlusAddressSuggestion()));
+  EXPECT_THAT(
+      FetchPlusAddressSuggestions(
+          origin, /*is_off_the_record=*/false, PasswordFormClassification(),
+          focused_field,
+          AutofillSuggestionTriggerSource::kManualFallbackPlusAddresses),
+      IsSingleCreatePlusAddressSuggestion());
   histogram_tester.ExpectUniqueSample(
       kPlusAddressSuggestionMetric,
       SuggestionEvent::kCreateNewPlusAddressSuggested, 2);
@@ -1659,11 +1666,12 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndDisableFeature(features::kPlusAddressesEnabled);
 
-  EXPECT_TRUE(ExpectServiceToReturnSuggestions(
-      url::Origin::Create(GURL("https://foo.com")),
-      /*is_off_the_record=*/false, PasswordFormClassification(),
-      FormFieldData(),
-      AutofillSuggestionTriggerSource::kFormControlElementClicked, IsEmpty()));
+  EXPECT_THAT(FetchPlusAddressSuggestions(
+                  url::Origin::Create(GURL("https://foo.com")),
+                  /*is_off_the_record=*/false, PasswordFormClassification(),
+                  FormFieldData(),
+                  AutofillSuggestionTriggerSource::kFormControlElementClicked),
+              IsEmpty());
 }
 
 // Tests that the only password form on which create suggestions are offered on
@@ -1677,41 +1685,37 @@
   const PlusProfile profile = test::CreatePlusProfile();
   const url::Origin origin = OriginFromFacet(profile.facet);
   auto get_suggestions_for_form_type =
-      [&](PasswordFormClassification::Type type, const auto& matcher) {
+      [&](PasswordFormClassification::Type type) {
         FormFieldData focused_field;
         focused_field.set_host_frame(autofill::test::MakeLocalFrameToken());
         focused_field.set_renderer_id(autofill::test::MakeFieldRendererId());
         auto form_classification = PasswordFormClassification{
             .type = type, .username_field = focused_field.global_id()};
-        return ExpectServiceToReturnSuggestions(
+        return FetchPlusAddressSuggestions(
             origin,
             /*is_off_the_record=*/false, form_classification, focused_field,
-            AutofillSuggestionTriggerSource::kFormControlElementClicked,
-            matcher);
+            AutofillSuggestionTriggerSource::kFormControlElementClicked);
       };
 
   using enum PasswordFormClassification::Type;
-  EXPECT_TRUE(get_suggestions_for_form_type(kLoginForm, IsEmpty()));
-  EXPECT_TRUE(get_suggestions_for_form_type(kChangePasswordForm, IsEmpty()));
-  EXPECT_TRUE(get_suggestions_for_form_type(kResetPasswordForm, IsEmpty()));
-  EXPECT_TRUE(get_suggestions_for_form_type(kSingleUsernameForm, IsEmpty()));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kSignupForm, IsSingleCreatePlusAddressSuggestion()));
+  EXPECT_THAT(get_suggestions_for_form_type(kLoginForm), IsEmpty());
+  EXPECT_THAT(get_suggestions_for_form_type(kChangePasswordForm), IsEmpty());
+  EXPECT_THAT(get_suggestions_for_form_type(kResetPasswordForm), IsEmpty());
+  EXPECT_THAT(get_suggestions_for_form_type(kSingleUsernameForm), IsEmpty());
+  EXPECT_THAT(get_suggestions_for_form_type(kSignupForm),
+              IsSingleCreatePlusAddressSuggestion());
 
   service().SavePlusProfile(profile);
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kLoginForm, IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kChangePasswordForm,
-      IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kResetPasswordForm,
-      IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kSingleUsernameForm,
-      IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kSignupForm, IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
+  EXPECT_THAT(get_suggestions_for_form_type(kLoginForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
+  EXPECT_THAT(get_suggestions_for_form_type(kChangePasswordForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
+  EXPECT_THAT(get_suggestions_for_form_type(kResetPasswordForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
+  EXPECT_THAT(get_suggestions_for_form_type(kSingleUsernameForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
+  EXPECT_THAT(get_suggestions_for_form_type(kSignupForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
 }
 
 // Tests that creation is offered on all password forms if the focused field is
@@ -1725,7 +1729,7 @@
   const PlusProfile profile = test::CreatePlusProfile();
   const url::Origin origin = OriginFromFacet(profile.facet);
   auto get_suggestions_for_form_type =
-      [&](PasswordFormClassification::Type type, const auto& matcher) {
+      [&](PasswordFormClassification::Type type) {
         FormFieldData focused_field;
         focused_field.set_host_frame(autofill::test::MakeLocalFrameToken());
         focused_field.set_renderer_id(autofill::test::MakeFieldRendererId());
@@ -1733,24 +1737,23 @@
             .type = type, .username_field = focused_field.global_id()};
         focused_field.set_renderer_id(
             autofill::FieldRendererId(focused_field.renderer_id().value() + 1));
-        return ExpectServiceToReturnSuggestions(
+        return FetchPlusAddressSuggestions(
             origin,
             /*is_off_the_record=*/false, form_classification, focused_field,
-            AutofillSuggestionTriggerSource::kFormControlElementClicked,
-            matcher);
+            AutofillSuggestionTriggerSource::kFormControlElementClicked);
       };
 
   using enum PasswordFormClassification::Type;
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kLoginForm, IsSingleCreatePlusAddressSuggestion()));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kChangePasswordForm, IsSingleCreatePlusAddressSuggestion()));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kResetPasswordForm, IsSingleCreatePlusAddressSuggestion()));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kSingleUsernameForm, IsSingleCreatePlusAddressSuggestion()));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kSignupForm, IsSingleCreatePlusAddressSuggestion()));
+  EXPECT_THAT(get_suggestions_for_form_type(kLoginForm),
+              IsSingleCreatePlusAddressSuggestion());
+  EXPECT_THAT(get_suggestions_for_form_type(kChangePasswordForm),
+              IsSingleCreatePlusAddressSuggestion());
+  EXPECT_THAT(get_suggestions_for_form_type(kResetPasswordForm),
+              IsSingleCreatePlusAddressSuggestion());
+  EXPECT_THAT(get_suggestions_for_form_type(kSingleUsernameForm),
+              IsSingleCreatePlusAddressSuggestion());
+  EXPECT_THAT(get_suggestions_for_form_type(kSignupForm),
+              IsSingleCreatePlusAddressSuggestion());
 }
 
 // Tests that plus address creation is offered on signup forms and single
@@ -1764,41 +1767,37 @@
   const PlusProfile profile = test::CreatePlusProfile();
   const url::Origin origin = OriginFromFacet(profile.facet);
   auto get_suggestions_for_form_type =
-      [&](PasswordFormClassification::Type type, const auto& matcher) {
+      [&](PasswordFormClassification::Type type) {
         FormFieldData focused_field;
         focused_field.set_host_frame(autofill::test::MakeLocalFrameToken());
         focused_field.set_renderer_id(autofill::test::MakeFieldRendererId());
         auto form_classification = PasswordFormClassification{
             .type = type, .username_field = focused_field.global_id()};
-        return ExpectServiceToReturnSuggestions(
+        return FetchPlusAddressSuggestions(
             origin,
             /*is_off_the_record=*/false, form_classification, focused_field,
-            AutofillSuggestionTriggerSource::kFormControlElementClicked,
-            matcher);
+            AutofillSuggestionTriggerSource::kFormControlElementClicked);
       };
   using enum PasswordFormClassification::Type;
-  EXPECT_TRUE(get_suggestions_for_form_type(kLoginForm, IsEmpty()));
-  EXPECT_TRUE(get_suggestions_for_form_type(kChangePasswordForm, IsEmpty()));
-  EXPECT_TRUE(get_suggestions_for_form_type(kResetPasswordForm, IsEmpty()));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kSingleUsernameForm, IsSingleCreatePlusAddressSuggestion()));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kSignupForm, IsSingleCreatePlusAddressSuggestion()));
+  EXPECT_THAT(get_suggestions_for_form_type(kLoginForm), IsEmpty());
+  EXPECT_THAT(get_suggestions_for_form_type(kChangePasswordForm), IsEmpty());
+  EXPECT_THAT(get_suggestions_for_form_type(kResetPasswordForm), IsEmpty());
+  EXPECT_THAT(get_suggestions_for_form_type(kSingleUsernameForm),
+              IsSingleCreatePlusAddressSuggestion());
+  EXPECT_THAT(get_suggestions_for_form_type(kSignupForm),
+              IsSingleCreatePlusAddressSuggestion());
 
   service().SavePlusProfile(profile);
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kLoginForm, IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kChangePasswordForm,
-      IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kResetPasswordForm,
-      IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kSingleUsernameForm,
-      IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kSignupForm, IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
+  EXPECT_THAT(get_suggestions_for_form_type(kLoginForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
+  EXPECT_THAT(get_suggestions_for_form_type(kChangePasswordForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
+  EXPECT_THAT(get_suggestions_for_form_type(kResetPasswordForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
+  EXPECT_THAT(get_suggestions_for_form_type(kSingleUsernameForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
+  EXPECT_THAT(get_suggestions_for_form_type(kSignupForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
 }
 
 // Tests that create suggestions are offered regardless of form type if the
@@ -1808,45 +1807,41 @@
   const PlusProfile profile = test::CreatePlusProfile();
   const url::Origin origin = OriginFromFacet(profile.facet);
   auto get_suggestions_for_form_type =
-      [&](PasswordFormClassification::Type type, const auto& matcher) {
+      [&](PasswordFormClassification::Type type) {
         FormFieldData focused_field;
         focused_field.set_host_frame(autofill::test::MakeLocalFrameToken());
         focused_field.set_renderer_id(autofill::test::MakeFieldRendererId());
         auto form_classification = PasswordFormClassification{
             .type = type, .username_field = focused_field.global_id()};
-        return ExpectServiceToReturnSuggestions(
+        return FetchPlusAddressSuggestions(
             origin,
             /*is_off_the_record=*/false, form_classification, focused_field,
-            AutofillSuggestionTriggerSource::kManualFallbackPlusAddresses,
-            matcher);
+            AutofillSuggestionTriggerSource::kManualFallbackPlusAddresses);
       };
 
   using enum PasswordFormClassification::Type;
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kLoginForm, IsSingleCreatePlusAddressSuggestion()));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kChangePasswordForm, IsSingleCreatePlusAddressSuggestion()));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kResetPasswordForm, IsSingleCreatePlusAddressSuggestion()));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kSingleUsernameForm, IsSingleCreatePlusAddressSuggestion()));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kSignupForm, IsSingleCreatePlusAddressSuggestion()));
+  EXPECT_THAT(get_suggestions_for_form_type(kLoginForm),
+              IsSingleCreatePlusAddressSuggestion());
+  EXPECT_THAT(get_suggestions_for_form_type(kChangePasswordForm),
+              IsSingleCreatePlusAddressSuggestion());
+  EXPECT_THAT(get_suggestions_for_form_type(kResetPasswordForm),
+              IsSingleCreatePlusAddressSuggestion());
+  EXPECT_THAT(get_suggestions_for_form_type(kSingleUsernameForm),
+              IsSingleCreatePlusAddressSuggestion());
+  EXPECT_THAT(get_suggestions_for_form_type(kSignupForm),
+              IsSingleCreatePlusAddressSuggestion());
 
   service().SavePlusProfile(profile);
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kLoginForm, IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kChangePasswordForm,
-      IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kResetPasswordForm,
-      IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kSingleUsernameForm,
-      IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
-  EXPECT_TRUE(get_suggestions_for_form_type(
-      kSignupForm, IsSingleFillPlusAddressSuggestion(*profile.plus_address)));
+  EXPECT_THAT(get_suggestions_for_form_type(kLoginForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
+  EXPECT_THAT(get_suggestions_for_form_type(kChangePasswordForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
+  EXPECT_THAT(get_suggestions_for_form_type(kResetPasswordForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
+  EXPECT_THAT(get_suggestions_for_form_type(kSingleUsernameForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
+  EXPECT_THAT(get_suggestions_for_form_type(kSignupForm),
+              IsSingleFillPlusAddressSuggestion(*profile.plus_address));
 }
 
 // Tests the content of the "Manage plus addresses..." suggestion.
@@ -2106,13 +2101,13 @@
           RunOnceCallback<1>(std::vector<affiliations::GroupedFacets>{group}));
 
   const url::Origin origin = url::Origin::Create(GURL("https://example.com"));
-  EXPECT_TRUE(ExpectServiceToReturnSuggestions(
-      origin,
-      /*is_off_the_record=*/false, PasswordFormClassification(),
-      FormFieldData(),
-      AutofillSuggestionTriggerSource::kFormControlElementClicked,
-      // There are no PLS, group or exact matches.
-      IsSingleCreatePlusAddressSuggestion()));
+  EXPECT_THAT(FetchPlusAddressSuggestions(
+                  origin,
+                  /*is_off_the_record=*/false, PasswordFormClassification(),
+                  FormFieldData(),
+                  AutofillSuggestionTriggerSource::kFormControlElementClicked),
+              // There are no PLS, group or exact matches.
+              IsSingleCreatePlusAddressSuggestion());
 }
 
 // Verifies that affiliated plus profiles are returned.
diff --git a/components/plus_addresses/plus_address_suggestion_generator.cc b/components/plus_addresses/plus_address_suggestion_generator.cc
index 9e1d9023..c332e60c 100644
--- a/components/plus_addresses/plus_address_suggestion_generator.cc
+++ b/components/plus_addresses/plus_address_suggestion_generator.cc
@@ -122,16 +122,16 @@
 
 std::vector<autofill::Suggestion>
 PlusAddressSuggestionGenerator::GetSuggestions(
+    const std::vector<std::string>& affiliated_plus_addresses,
     bool is_creation_enabled,
     const autofill::PasswordFormClassification& focused_form_classification,
     const autofill::FormFieldData& focused_field,
-    autofill::AutofillSuggestionTriggerSource trigger_source,
-    std::vector<PlusProfile> affiliated_profiles) {
+    autofill::AutofillSuggestionTriggerSource trigger_source) {
   using enum autofill::AutofillSuggestionTriggerSource;
   const std::u16string normalized_field_value =
       autofill::RemoveDiacriticsAndConvertToLowerCase(focused_field.value());
 
-  if (affiliated_profiles.empty()) {
+  if (affiliated_plus_addresses.empty()) {
     // Do not offer creation if disabled.
     if (!is_creation_enabled) {
       return {};
@@ -150,9 +150,10 @@
   }
 
   std::vector<Suggestion> suggestions;
-  suggestions.reserve(affiliated_profiles.size());
-  for (const PlusProfile& profile : affiliated_profiles) {
-    std::u16string plus_address = base::UTF8ToUTF16(*profile.plus_address);
+  suggestions.reserve(affiliated_plus_addresses.size());
+  for (const std::string& affiliated_plus_addresse :
+       affiliated_plus_addresses) {
+    std::u16string plus_address = base::UTF8ToUTF16(affiliated_plus_addresse);
     // Only suggest filling a plus address whose prefix matches the field's
     // value.
     if (trigger_source == kManualFallbackPlusAddresses ||
diff --git a/components/plus_addresses/plus_address_suggestion_generator.h b/components/plus_addresses/plus_address_suggestion_generator.h
index 1675cbd..f4491bd 100644
--- a/components/plus_addresses/plus_address_suggestion_generator.h
+++ b/components/plus_addresses/plus_address_suggestion_generator.h
@@ -42,11 +42,11 @@
   // are assumed to be the plus profiles affiliated with the primary main frame
   // origin.
   [[nodiscard]] std::vector<autofill::Suggestion> GetSuggestions(
+      const std::vector<std::string>& affiliated_plus_addresses,
       bool is_creation_enabled,
       const autofill::PasswordFormClassification& focused_form_classification,
       const autofill::FormFieldData& focused_field,
-      autofill::AutofillSuggestionTriggerSource trigger_source,
-      std::vector<PlusProfile> affiliated_profiles);
+      autofill::AutofillSuggestionTriggerSource trigger_source);
 
   // Updates `suggestion` with a refreshed plus address by setting a new
   // payload.
diff --git a/components/plus_addresses/plus_address_suggestion_generator_unittest.cc b/components/plus_addresses/plus_address_suggestion_generator_unittest.cc
index 7c518fd..ed3eb52 100644
--- a/components/plus_addresses/plus_address_suggestion_generator_unittest.cc
+++ b/components/plus_addresses/plus_address_suggestion_generator_unittest.cc
@@ -123,10 +123,10 @@
       &setting_service(), &allocator(),
       url::Origin::Create(GURL("https://foo.bar")), kPrimaryEmail);
   EXPECT_THAT(generator.GetSuggestions(
+                  /*affiliated_plus_addresses=*/{},
                   /*is_creation_enabled=*/true, PasswordFormClassification(),
                   FormFieldData(),
-                  AutofillSuggestionTriggerSource::kFormControlElementClicked,
-                  /*affiliated_profiles=*/{}),
+                  AutofillSuggestionTriggerSource::kFormControlElementClicked),
               ElementsAre(IsCreateInlineSuggestion(
                   /*suggested_plus_address=*/std::nullopt)));
 }
@@ -143,10 +143,10 @@
       &setting_service(), &allocator(),
       url::Origin::Create(GURL("https://foo.bar")), kPrimaryEmail);
   EXPECT_THAT(generator.GetSuggestions(
+                  /*affiliated_plus_addresses=*/{},
                   /*is_creation_enabled=*/true, PasswordFormClassification(),
                   FormFieldData(),
-                  AutofillSuggestionTriggerSource::kFormControlElementClicked,
-                  /*affiliated_profiles=*/{}),
+                  AutofillSuggestionTriggerSource::kFormControlElementClicked),
               ElementsAre(IsCreateInlineSuggestion(
                   /*suggested_plus_address=*/base::UTF8ToUTF16(
                       *test::CreatePlusProfile().plus_address))));
@@ -218,10 +218,10 @@
       url::Origin::Create(GURL("https://foo.bar")), kPrimaryEmail);
   EXPECT_THAT(
       generator.GetSuggestions(
+          /*affiliated_plus_addresses=*/{},
           /*is_creation_enabled=*/true, PasswordFormClassification(),
           FormFieldData(),
-          AutofillSuggestionTriggerSource::kFormControlElementClicked,
-          /*affiliated_profiles=*/{}),
+          AutofillSuggestionTriggerSource::kFormControlElementClicked),
       ElementsAre(AllOf(EqualsSuggestion(SuggestionType::kCreateNewPlusAddress),
                         Field(&Suggestion::labels, IsEmpty()))));
 }
@@ -243,10 +243,10 @@
       url::Origin::Create(GURL("https://foo.bar")), kPrimaryEmail);
 
   std::vector<Suggestion> suggestions = generator.GetSuggestions(
+      /*affiliated_plus_addresses=*/{},
       /*is_creation_enabled=*/true, PasswordFormClassification(),
       FormFieldData(),
-      AutofillSuggestionTriggerSource::kFormControlElementClicked,
-      /*affiliated_profiles=*/{});
+      AutofillSuggestionTriggerSource::kFormControlElementClicked);
   ASSERT_EQ(suggestions.size(), 1u);
 
   if constexpr (BUILDFLAG(IS_ANDROID)) {
diff --git a/components/plus_addresses_strings.grdp b/components/plus_addresses_strings.grdp
index cbd652c..4324b9c 100644
--- a/components/plus_addresses_strings.grdp
+++ b/components/plus_addresses_strings.grdp
@@ -159,6 +159,14 @@
 	Plus address for <ph name="DOMAIN">$1<ex>foo.com</ex></ph>
     </message>
 
+    <!-- iOS Error State strings -->
+    <message name="IDS_PLUS_ADDRESS_GENERIC_ERROR_ALERT_TITLE_IOS" desc="The title of the generic error alert." translateable="false">
+        Something Went Wrong
+    </message>
+    <message name="IDS_PLUS_ADDRESS_GENERIC_ERROR_ALERT_MESSAGE_IOS" desc="The message displayed in the generic error alert." translateable="false">
+	Try reloading the page or some lorem ipsum generic text
+    </message>
+
     <!-- Desktop strings -->
     <message name="IDS_PLUS_ADDRESS_MODAL_TITLE" desc="Title for the experimental enterprise plus addresses feature modal" translateable="false">
       Lorem Ipsum
diff --git a/components/policy/core/common/cloud/enterprise_metrics.h b/components/policy/core/common/cloud/enterprise_metrics.h
index 5c6597c..3c4a6ae 100644
--- a/components/policy/core/common/cloud/enterprise_metrics.h
+++ b/components/policy/core/common/cloud/enterprise_metrics.h
@@ -416,10 +416,6 @@
     "Enterprise.StateDetermination.DeviceIdentifierStatus";
 inline constexpr char kUMAStateDeterminationEnabled[] =
     "Enterprise.StateDetermination.Enabled";
-inline constexpr char kUMAStateDeterminationKillSwitchFetchNetworkErrorCode[] =
-    "Enterprise.StateDetermination.KillSwitchFetch.NetworkErrorCode";
-inline constexpr char kUMAStateDeterminationKillSwitchFetchNumTries[] =
-    "Enterprise.StateDetermination.KillSwitchFetch.NumTries";
 inline constexpr char kUMAStateDeterminationOnFlex[] =
     "Enterprise.StateDetermination.OnFlex";
 inline constexpr char kUMAStateDeterminationOwnershipStatus[] =
diff --git a/components/resources/autofill_scaled_resources.grdp b/components/resources/autofill_scaled_resources.grdp
index f42fd43b..ec5a674 100644
--- a/components/resources/autofill_scaled_resources.grdp
+++ b/components/resources/autofill_scaled_resources.grdp
@@ -30,7 +30,8 @@
   <structure type="chrome_scaled_image" name="IDR_AUTOFILL_METADATA_CC_TROY" file="autofill/metadata/troy.png" />
   <if expr="_google_chrome">
     <then>
-      <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PREDICTION_IMPROVEMENTS_LOGO" file="google_chrome/autofill_prediction_improvements/autofill_prediction_improvements_logo.png" />
+      <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PREDICTION_IMPROVEMENTS_LOGO"
+        file="autofill/cc-generic.png" />
       <structure type="chrome_scaled_image" name="IDR_AUTOFILL_GOOGLE_PAY" file="google_chrome/autofill/googlepay.png" />
       <structure type="chrome_scaled_image" name="IDR_AUTOFILL_GOOGLE_PAY_DARK" file="google_chrome/autofill/googlepay_dark.png" />
       <structure type="chrome_scaled_image" name="IDR_AUTOFILL_GOOGLE_PAY_SMALL" file="google_chrome/autofill/googlepay_28.png" />
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 170bc82..8dc2651 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
@@ -14,6 +14,7 @@
 #include "base/check_is_test.h"
 #include "base/command_line.h"
 #include "base/debug/dump_without_crashing.h"
+#include "base/feature_list.h"
 #include "base/json/json_reader.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
@@ -168,21 +169,36 @@
 
 }  // namespace
 
-SearchEngineChoiceService::SearchEngineChoiceService(PrefService& profile_prefs,
-                                                     PrefService* local_state,
-                                                     int variations_country_id)
+SearchEngineChoiceService::SearchEngineChoiceService(
+    PrefService& profile_prefs,
+    PrefService* local_state,
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+    bool is_profile_eligbile_for_dse_guest_propagation,
+#endif
+    int variations_country_id)
     : profile_prefs_(profile_prefs),
+      local_state_(local_state),
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+      is_profile_eligible_for_dse_guest_propagation_(
+          is_profile_eligbile_for_dse_guest_propagation),
+#endif
       variations_country_id_(variations_country_id) {
-  ProcessPendingChoiceScreenDisplayState(local_state);
+  ProcessPendingChoiceScreenDisplayState();
   PreprocessPrefsForReprompt();
 }
 
 SearchEngineChoiceService::SearchEngineChoiceService(
     PrefService& profile_prefs,
     PrefService* local_state,
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+    bool is_profile_eligible_for_dse_guest_propagation,
+#endif
     variations::VariationsService* variations_service)
     : SearchEngineChoiceService(profile_prefs,
                                 local_state,
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+                                is_profile_eligible_for_dse_guest_propagation,
+#endif
 #if BUILDFLAG(IS_FUCHSIA)
                                 // We can't add a dependency from Fuchsia to
                                 // `//components/variations/service`.
@@ -538,19 +554,18 @@
   }
 }
 
-void SearchEngineChoiceService::ProcessPendingChoiceScreenDisplayState(
-    PrefService* local_state) {
+void SearchEngineChoiceService::ProcessPendingChoiceScreenDisplayState() {
   if (!profile_prefs_->HasPrefPath(
           prefs::kDefaultSearchProviderPendingChoiceScreenDisplayState)) {
     return;
   }
 
-  if (!local_state) {
+  if (!local_state_) {
     // `g_browser_process->local_state()` is null in unit tests unless properly
     // set up.
     CHECK_IS_TEST();
   } else if (!SearchEngineChoiceMetricsServiceAccessor::
-                 IsMetricsReportingEnabled(local_state)) {
+                 IsMetricsReportingEnabled(local_state_)) {
     // The display state should not be cached when UMA is disabled.
 
     profile_prefs_->ClearPref(
@@ -648,6 +663,35 @@
   country_id_cache_.reset();
 }
 
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+bool SearchEngineChoiceService::IsProfileEligibleForDseGuestPropagation()
+    const {
+  return base::FeatureList::IsEnabled(
+             switches::kSearchEngineChoiceGuestExperience) &&
+         is_profile_eligible_for_dse_guest_propagation_;
+}
+
+bool SearchEngineChoiceService::ShouldPropagateDseBetweenGuestSessions() const {
+  return base::FeatureList::IsEnabled(
+             switches::kSearchEngineChoiceGuestExperience) &&
+         local_state_->HasPrefPath(
+             prefs::kDefaultSearchProviderGuestModePrepopulatedId);
+}
+
+void SearchEngineChoiceService::PropagateSearchEngineBetweenGuestSessions(
+    int prepopulated_id) {
+  CHECK(prepopulated_id > 0 &&
+        prepopulated_id <=
+            TemplateURLPrepopulateData::kMaxPrepopulatedEngineID);
+  if (!base::FeatureList::IsEnabled(
+          switches::kSearchEngineChoiceGuestExperience)) {
+    return;
+  }
+  local_state_->SetInt64(prefs::kDefaultSearchProviderGuestModePrepopulatedId,
+                         prepopulated_id);
+}
+#endif
+
 #if BUILDFLAG(IS_ANDROID)
 void SearchEngineChoiceService::ProcessGetCountryResponseFromPlayApi(
     int country_id) {
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 f00c8b8..d562561 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
@@ -36,9 +36,15 @@
   SearchEngineChoiceService(
       PrefService& profile_prefs,
       PrefService* local_state,
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+      bool is_profile_eligible_for_dse_guest_propagation,
+#endif
       int variations_country_id = country_codes::kCountryIDUnknown);
   SearchEngineChoiceService(PrefService& profile_prefs,
                             PrefService* local_state,
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+                            bool is_profile_eligible_for_dse_guest_propagation,
+#endif
                             variations::VariationsService* variations_service);
   ~SearchEngineChoiceService() override;
 
@@ -94,6 +100,20 @@
   // in tests.
   void ClearCountryIdCacheForTesting();
 
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+  // Returns whether the profile is eligible for the default search engine to be
+  // used across all guest sessions.
+  bool IsProfileEligibleForDseGuestPropagation() const;
+
+  // Returns whether there is a default search engine configured to be
+  // propagated to new guest sessions.
+  bool ShouldPropagateDseBetweenGuestSessions() const;
+
+  // Save the `prepopulated_id` of the chosen search engine to be used for all
+  // guest sessions.
+  void PropagateSearchEngineBetweenGuestSessions(int prepopulated_id);
+#endif
+
   // Register Local state preferences in `registry`.
   static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
 
@@ -103,7 +123,7 @@
   // the choice are cleared, which triggers a reprompt on the next page load.
   void PreprocessPrefsForReprompt();
 
-  void ProcessPendingChoiceScreenDisplayState(PrefService* local_state);
+  void ProcessPendingChoiceScreenDisplayState();
 
   int GetCountryIdInternal();
 
@@ -112,6 +132,10 @@
 #endif
 
   const raw_ref<PrefService> profile_prefs_;
+  const raw_ptr<PrefService> local_state_;
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+  bool is_profile_eligible_for_dse_guest_propagation_ = false;
+#endif
   const int variations_country_id_;
 
   // Used to ensure that the value returned from `GetCountryId` never changes
diff --git a/components/search_engines/search_engine_choice/search_engine_choice_service_unittest.cc b/components/search_engines/search_engine_choice/search_engine_choice_service_unittest.cc
index e9c68f7..f7f43ee 100644
--- a/components/search_engines/search_engine_choice/search_engine_choice_service_unittest.cc
+++ b/components/search_engines/search_engine_choice/search_engine_choice_service_unittest.cc
@@ -93,6 +93,10 @@
     TemplateURLPrepopulateData::RegisterProfilePrefs(pref_service_.registry());
     local_state_.registry()->RegisterBooleanPref(
         metrics::prefs::kMetricsReportingEnabled, true);
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+    local_state_.registry()->RegisterInt64Pref(
+        prefs::kDefaultSearchProviderGuestModePrepopulatedId, 0);
+#endif
 
     // Override the country checks to simulate being in Belgium.
     base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
@@ -116,7 +120,11 @@
       CHECK(!search_engine_choice_service_);
     }
     search_engine_choice_service_ = std::make_unique<SearchEngineChoiceService>(
-        pref_service_, &local_state_, variation_country_id);
+        pref_service_, &local_state_,
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+        /*is_profile_eligible_for_dse_guest_propagation=*/false,
+#endif
+        variation_country_id);
   }
 
   policy::MockPolicyService& policy_service() { return policy_service_; }
@@ -139,6 +147,8 @@
 
     return CHECK_DEREF(search_engine_choice_service_.get());
   }
+  TestingPrefServiceSimple& local_state() { return local_state_; }
+
   base::HistogramTester histogram_tester_;
 
  private:
@@ -286,6 +296,27 @@
 #endif
 }
 
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+TEST_F(SearchEngineChoiceServiceTest, GuestSessionDsePropagation) {
+  base::test::ScopedFeatureList scoped_feature_list{
+      switches::kSearchEngineChoiceGuestExperience};
+
+  EXPECT_FALSE(local_state().HasPrefPath(
+      prefs::kDefaultSearchProviderGuestModePrepopulatedId));
+  EXPECT_FALSE(
+      search_engine_choice_service().ShouldPropagateDseBetweenGuestSessions());
+
+  constexpr int prepopulated_id = 1;
+  search_engine_choice_service().PropagateSearchEngineBetweenGuestSessions(
+      prepopulated_id);
+  EXPECT_EQ(local_state().GetInt64(
+                prefs::kDefaultSearchProviderGuestModePrepopulatedId),
+            prepopulated_id);
+  EXPECT_TRUE(
+      search_engine_choice_service().ShouldPropagateDseBetweenGuestSessions());
+}
+#endif
+
 // Test that the choice screen doesn't get displayed if the
 // 'DefaultSearchProviderEnabled' policy is set to true and the
 // DefaultSearchProviderSearchURL' is set.
@@ -1403,7 +1434,12 @@
         metrics::prefs::kMetricsReportingEnabled, true);
 
     search_engine_choice_service_ = std::make_unique<SearchEngineChoiceService>(
-        pref_service_, &local_state_);
+        pref_service_, &local_state_
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+        ,
+        /*is_profile_eligible_for_dse_guest_propagation=*/false
+#endif
+    );
   }
 
   ~SearchEngineChoiceUtilsResourceIdsTest() override = default;
@@ -1469,7 +1505,11 @@
   ASSERT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kSearchEngineChoiceCountry));
   SearchEngineChoiceService search_engine_choice_service(
-      pref_service(), &local_state(), country_codes::kCountryIDUnknown);
+      pref_service(), &local_state(),
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+      /*is_profile_eligible_for_dse_guest_propagation=*/false,
+#endif
+      country_codes::kCountryIDUnknown);
 
   EXPECT_EQ(search_engine_choice_service.GetCountryId(),
             country_codes::GetCurrentCountryID());
@@ -1487,7 +1527,11 @@
   }
 
   SearchEngineChoiceService search_engine_choice_service(
-      pref_service(), &local_state(), variation_country_id);
+      pref_service(), &local_state(),
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+      /*is_profile_eligible_for_dse_guest_propagation=*/false,
+#endif
+      variation_country_id);
 
   EXPECT_EQ(variation_country_id, search_engine_choice_service.GetCountryId());
 }
diff --git a/components/search_engines/search_engine_choice/search_engine_choice_utils.h b/components/search_engines/search_engine_choice/search_engine_choice_utils.h
index d63cdd1..e384fcc 100644
--- a/components/search_engines/search_engine_choice/search_engine_choice_utils.h
+++ b/components/search_engines/search_engine_choice/search_engine_choice_utils.h
@@ -92,8 +92,11 @@
   // The browser attempting to show the choice screen in a dialog is already
   // showing a choice screen.
   kAlreadyBeingShown = 16,
+  // The user made the choice in the guest session and opted to save it across
+  // guest sessions.
+  kUsingPersistedGuestSessionChoice = 17,
 
-  kMaxValue = kAlreadyBeingShown,
+  kMaxValue = kUsingPersistedGuestSessionChoice,
 };
 // LINT.ThenChange(/tools/metrics/histograms/enums.xml:SearchEngineChoiceScreenConditions)
 
diff --git a/components/search_engines/search_engines_test_environment.cc b/components/search_engines/search_engines_test_environment.cc
index fab14bc..b47a5632 100644
--- a/components/search_engines/search_engines_test_environment.cc
+++ b/components/search_engines/search_engines_test_environment.cc
@@ -37,8 +37,13 @@
                               country_codes::CountryCharsToCountryID('U', 'S'));
   }
 
-  search_engine_choice_service_ =
-      std::make_unique<SearchEngineChoiceService>(*pref_service_, local_state_);
+  search_engine_choice_service_ = std::make_unique<SearchEngineChoiceService>(
+      *pref_service_, local_state_
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+      ,
+      /*is_profile_eligible_for_dse_guest_propagation=*/false
+#endif
+  );
 
   template_url_service_ = std::make_unique<TemplateURLService>(
       *pref_service_, *search_engine_choice_service_,
diff --git a/components/search_engines/template_url_service_test_util.cc b/components/search_engines/template_url_service_test_util.cc
index cb1c733..8e5fd91a 100644
--- a/components/search_engines/template_url_service_test_util.cc
+++ b/components/search_engines/template_url_service_test_util.cc
@@ -79,7 +79,11 @@
 
   search_engine_choice_service_ =
       std::make_unique<search_engines::SearchEngineChoiceService>(
-          pref_service_, &local_state_, country_codes::kCountryIDUnknown);
+          pref_service_, &local_state_,
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+          /*is_profile_eligible_for_dse_guest_propagation=*/false,
+#endif
+          country_codes::kCountryIDUnknown);
 
   template_url_service_ = CreateService();
 }
diff --git a/components/signin/public/base/signin_metrics.cc b/components/signin/public/base/signin_metrics.cc
index 709ae87..5e680e32 100644
--- a/components/signin/public/base/signin_metrics.cc
+++ b/components/signin/public/base/signin_metrics.cc
@@ -295,25 +295,13 @@
 }
 #endif  // BUILDFLAG(IS_IOS)
 
-void RecordTabAndGroupCountsOnSignin(signin_metrics::AccessPoint access_point,
-                                     signin::ConsentLevel consent_level,
-                                     size_t tabs_count,
-                                     size_t groups_count,
-                                     size_t grouped_tabs_count) {
+void RecordOpenTabCountOnSignin(signin_metrics::AccessPoint access_point,
+                                signin::ConsentLevel consent_level,
+                                size_t tabs_count) {
   std::string_view consent_level_token =
       consent_level == signin::ConsentLevel::kSignin ? ".OnSignin" : ".OnSync";
-
-  std::string tabs_histogram_name =
-      base::StrCat({"Signin.OpenTabsCount", consent_level_token});
-  base::UmaHistogramCounts1000(tabs_histogram_name, tabs_count);
-
-  std::string groups_histogram_name =
-      base::StrCat({"Signin.TabGroupsCount", consent_level_token});
-  base::UmaHistogramCounts100(groups_histogram_name, groups_count);
-
-  std::string grouped_tabs_histogram_name =
-      base::StrCat({"Signin.TabGroupsTabsCount", consent_level_token});
-  base::UmaHistogramCounts1000(grouped_tabs_histogram_name, grouped_tabs_count);
+  base::UmaHistogramCounts1000(
+      base::StrCat({"Signin.OpenTabsCount", consent_level_token}), tabs_count);
 }
 
 // --------------------------------------------------------------
diff --git a/components/signin/public/base/signin_metrics.h b/components/signin/public/base/signin_metrics.h
index ca22aab..d40a1ed0 100644
--- a/components/signin/public/base/signin_metrics.h
+++ b/components/signin/public/base/signin_metrics.h
@@ -665,13 +665,11 @@
 void RecordSignoutForceClearDataChoice(bool force_clear_data);
 #endif  // BUILDFLAG(IS_IOS)
 
-// Records the total number of open tabs, the number of tab groups, and the
-// number of tabs contained in any group, at the moment of signin.
-void RecordTabAndGroupCountsOnSignin(signin_metrics::AccessPoint access_point,
-                                     signin::ConsentLevel consent_level,
-                                     size_t tabs_count,
-                                     size_t groups_count,
-                                     size_t grouped_tabs_count);
+// Records the total number of open tabs at the moment of signin or enabling
+// sync.
+void RecordOpenTabCountOnSignin(signin_metrics::AccessPoint access_point,
+                                signin::ConsentLevel consent_level,
+                                size_t tabs_count);
 
 // -----------------------------------------------------------------------------
 // User actions
diff --git a/components/supervised_user/test_support/kids_management_api_server_mock.cc b/components/supervised_user/test_support/kids_management_api_server_mock.cc
index 5ea5966..d0526b7 100644
--- a/components/supervised_user/test_support/kids_management_api_server_mock.cc
+++ b/components/supervised_user/test_support/kids_management_api_server_mock.cc
@@ -19,7 +19,7 @@
 
 namespace supervised_user {
 
-const std::map<kidsmanagement::FamilyRole, std::string> kSimpsonFamily = {
+const std::multimap<kidsmanagement::FamilyRole, std::string> kSimpsonFamily = {
     {kidsmanagement::HEAD_OF_HOUSEHOLD, "marge@gmail.com"},
     {kidsmanagement::PARENT, "homer@gmail.com"},
     {kidsmanagement::MEMBER, "abraham@gmail.com"},
diff --git a/components/supervised_user/test_support/kids_management_api_server_mock.h b/components/supervised_user/test_support/kids_management_api_server_mock.h
index bf02a97..6ff68b73 100644
--- a/components/supervised_user/test_support/kids_management_api_server_mock.h
+++ b/components/supervised_user/test_support/kids_management_api_server_mock.h
@@ -23,7 +23,8 @@
 
 namespace supervised_user {
 
-extern const std::map<kidsmanagement::FamilyRole, std::string> kSimpsonFamily;
+extern const std::multimap<kidsmanagement::FamilyRole, std::string>
+    kSimpsonFamily;
 
 // Configures the scoped feature list so that the related feature is initialized
 // with right parameters to divert kids management api traffic to an http
diff --git a/components/sync/service/data_type_manager.h b/components/sync/service/data_type_manager.h
index 3e34c4e..74f43a7c 100644
--- a/components/sync/service/data_type_manager.h
+++ b/components/sync/service/data_type_manager.h
@@ -21,6 +21,7 @@
 
 struct ConfigureContext;
 class DataTypeConfigurer;
+struct LocalDataDescription;
 
 // This interface is for managing the start up and shut down life cycle
 // of many different syncable data types.
@@ -132,6 +133,25 @@
       DataTypeSet requested_types,
       base::OnceCallback<void(DataTypeSet)> callback) const = 0;
 
+  // Queries the count and description/preview of existing local data for
+  // `types` data types. This is usually an asynchronous operation that returns
+  // the result via `callback` once available, which includes the description
+  // for each datatype in `types` that is active and supports batch uploading.
+  // This function may invoke `callback` immediately in some cases, e.g. if
+  // `types` is empty or none of the types is active.
+  virtual void GetLocalDataDescriptions(
+      DataTypeSet types,
+      base::OnceCallback<void(std::map<DataType, LocalDataDescription>)>
+          callback) = 0;
+
+  // Requests sync service to move all local data to account for `types` data
+  // types. This is an asynchronous method which moves the local data for all
+  // `types` to the account store locally. Upload to the server will happen as
+  // part of the regular commit process, and is NOT part of this method.
+  // Note: Only data types that are enabled and support this functionality are
+  // triggered for upload.
+  virtual void TriggerLocalDataMigration(DataTypeSet types) = 0;
+
   // The current state of the data type manager.
   virtual State state() const = 0;
 
diff --git a/components/sync/service/data_type_manager_impl.cc b/components/sync/service/data_type_manager_impl.cc
index 040e95c..87b9e6de 100644
--- a/components/sync/service/data_type_manager_impl.cc
+++ b/components/sync/service/data_type_manager_impl.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "base/barrier_callback.h"
 #include "base/containers/contains.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
@@ -28,6 +29,7 @@
 #include "components/sync/service/data_type_status_table.h"
 #include "components/sync/service/get_all_nodes_request_barrier.h"
 #include "components/sync/service/get_types_with_unsynced_data_request_barrier.h"
+#include "components/sync/service/local_data_description.h"
 
 namespace syncer {
 
@@ -115,6 +117,17 @@
   return result;
 }
 
+std::map<DataType, LocalDataDescription> JoinAllTypesAndLocalDataDescriptions(
+    const std::vector<std::pair<DataType, LocalDataDescription>>& pairs) {
+  return std::map<DataType, LocalDataDescription>(pairs.begin(), pairs.end());
+}
+
+std::pair<DataType, LocalDataDescription> JoinTypeAndLocalDataDescription(
+    DataType type,
+    LocalDataDescription description) {
+  return {type, description};
+}
+
 }  // namespace
 
 DataTypeManagerImpl::DataTypeManagerImpl(
@@ -990,8 +1003,24 @@
   }
 }
 
-DataTypeManager::State DataTypeManagerImpl::state() const {
-  return state_;
+void DataTypeManagerImpl::GetLocalDataDescriptions(
+    DataTypeSet types,
+    base::OnceCallback<void(std::map<DataType, LocalDataDescription>)>
+        callback) {
+  types.RetainAll(GetDataTypesWithLocalDataBatchUploader());
+  // Only retain types that are not only preferred but also active, that is,
+  // those which are configured and have not encountered any error.
+  types.RetainAll(GetActiveDataTypes());
+
+  auto barrier_callback =
+      base::BarrierCallback<std::pair<DataType, LocalDataDescription>>(
+          types.size(), base::BindOnce(&JoinAllTypesAndLocalDataDescriptions)
+                            .Then(std::move(callback)));
+  for (DataType type : types) {
+    controllers_.at(type)->GetLocalDataBatchUploader()->GetLocalDataDescription(
+        base::BindOnce(&JoinTypeAndLocalDataDescription, type)
+            .Then(barrier_callback));
+  }
 }
 
 const DataTypeController::TypeMap& DataTypeManagerImpl::GetControllerMap()
@@ -999,10 +1028,38 @@
   return controllers_;
 }
 
+void DataTypeManagerImpl::TriggerLocalDataMigration(DataTypeSet types) {
+  types.RetainAll(GetDataTypesWithLocalDataBatchUploader());
+  // Only retain types that are not only preferred but also active, that is,
+  // those which are configured and have not encountered any error.
+  types.RetainAll(GetActiveDataTypes());
+
+  for (DataType type : types) {
+    controllers_.at(type)
+        ->GetLocalDataBatchUploader()
+        ->TriggerLocalDataMigration();
+  }
+}
+
+DataTypeManager::State DataTypeManagerImpl::state() const {
+  return state_;
+}
+
 DataTypeSet DataTypeManagerImpl::GetEnabledTypes() const {
   return Difference(preferred_types_, data_type_status_table_.GetFailedTypes());
 }
 
+DataTypeSet DataTypeManagerImpl::GetDataTypesWithLocalDataBatchUploader()
+    const {
+  DataTypeSet types;
+  for (const auto& [type, controller] : controllers_) {
+    if (controller->GetLocalDataBatchUploader()) {
+      types.Put(type);
+    }
+  }
+  return types;
+}
+
 void DataTypeManagerImpl::RecordMemoryUsageAndCountsHistograms() {
   CHECK(configurer_);
   for (DataType type : GetActiveDataTypes()) {
diff --git a/components/sync/service/data_type_manager_impl.h b/components/sync/service/data_type_manager_impl.h
index 817f50d..63b9924 100644
--- a/components/sync/service/data_type_manager_impl.h
+++ b/components/sync/service/data_type_manager_impl.h
@@ -57,6 +57,11 @@
   void GetTypesWithUnsyncedData(
       DataTypeSet requested_types,
       base::OnceCallback<void(DataTypeSet)> callback) const override;
+  void GetLocalDataDescriptions(
+      DataTypeSet types,
+      base::OnceCallback<void(std::map<DataType, LocalDataDescription>)>
+          callback) override;
+  void TriggerLocalDataMigration(DataTypeSet types) override;
   State state() const override;
   TypeStatusMapForDebugging GetTypeStatusMapForDebugging(
       DataTypeSet throttled_types,
@@ -138,6 +143,9 @@
 
   DataTypeSet GetEnabledTypes() const;
 
+  // Returns the types that have a non-null DataTypeLocalDataBatchUploader.
+  DataTypeSet GetDataTypesWithLocalDataBatchUploader() const;
+
   // Records per type histograms for estimated memory usage and number of
   // entities.
   void RecordMemoryUsageAndCountsHistograms();
diff --git a/components/sync/service/data_type_manager_impl_unittest.cc b/components/sync/service/data_type_manager_impl_unittest.cc
index 78ec8a53..898e024 100644
--- a/components/sync/service/data_type_manager_impl_unittest.cc
+++ b/components/sync/service/data_type_manager_impl_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/functional/callback.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
 #include "components/sync/base/data_type.h"
 #include "components/sync/base/features.h"
@@ -20,7 +21,9 @@
 #include "components/sync/service/data_type_encryption_handler.h"
 #include "components/sync/service/data_type_manager_observer.h"
 #include "components/sync/service/data_type_status_table.h"
+#include "components/sync/service/local_data_description.h"
 #include "components/sync/test/fake_data_type_controller.h"
+#include "components/sync/test/mock_data_type_local_data_batch_uploader.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -149,6 +152,9 @@
   DataTypeSet connected_types() const { return connected_types_; }
   int configure_call_count() const { return configure_call_count_; }
   const ConfigureParams& last_params() const { return last_params_; }
+  bool has_ongoing_configuration() const {
+    return !last_params_.ready_task.is_null();
+  }
 
  private:
   DataTypeSet connected_types_;
@@ -188,10 +194,12 @@
   ~DataTypeManagerImplTest() override = default;
 
   void InitDataTypeManager(DataTypeSet types_without_transport_mode_support,
-                           DataTypeSet types_with_transport_mode_support = {}) {
+                           DataTypeSet types_with_transport_mode_support = {},
+                           DataTypeSet types_with_batch_uploader = {}) {
     CHECK(Intersection(types_without_transport_mode_support,
                        types_with_transport_mode_support)
               .empty());
+    CHECK(types_with_transport_mode_support.HasAll(types_with_batch_uploader));
 
     DataTypeController::TypeVector controllers;
     for (DataType type : types_without_transport_mode_support) {
@@ -199,8 +207,12 @@
           type, /*enable_transport_mode=*/false));
     }
     for (DataType type : types_with_transport_mode_support) {
+      auto batch_uploader =
+          types_with_batch_uploader.Has(type)
+              ? std::make_unique<MockDataTypeLocalDataBatchUploader>()
+              : nullptr;
       controllers.push_back(std::make_unique<FakeDataTypeController>(
-          type, /*enable_transport_mode=*/true));
+          type, /*enable_transport_mode=*/true, std::move(batch_uploader)));
     }
     InitDataTypeManagerWithControllers(std::move(controllers));
   }
@@ -237,6 +249,14 @@
     return FinishDownloadWithFailedTypes(DataTypeSet());
   }
 
+  // Completes ongoing download requests until idle, i.e. until there is no
+  // download ongoing.
+  void FinishAllDownloadsUntilIdle() {
+    while (configurer_.has_ongoing_configuration()) {
+      FinishDownload();
+    }
+  }
+
   // Gets the fake controller for the given type, which should have
   // been previously added via InitDataTypeManager().
   FakeDataTypeController* GetController(DataType data_type) const {
@@ -248,6 +268,16 @@
     return static_cast<FakeDataTypeController*>(it->second.get());
   }
 
+  // Gets the batch uploader for the given type, which should have
+  // been previously added via InitDataTypeManager(). Returns null if the
+  // datatype was initialized without a batch uploader.
+  MockDataTypeLocalDataBatchUploader* GetBatchUploader(
+      DataType data_type) const {
+    CHECK(dtm_);
+    return static_cast<MockDataTypeLocalDataBatchUploader*>(
+        GetController(data_type)->GetLocalDataBatchUploader());
+  }
+
   void FailEncryptionFor(DataTypeSet encrypted_types) {
     encryption_handler_.set_crypto_error(true);
     encryption_handler_.set_encrypted_types(encrypted_types);
@@ -1872,6 +1902,172 @@
   EXPECT_EQ(1, GetController(PREFERENCES)->model()->clear_metadata_count());
 }
 
+TEST_F(DataTypeManagerImplTest, ShouldGetLocalDataDescriptionsForOneType) {
+  InitDataTypeManager(
+      /*types_without_transport_mode_support=*/{},
+      /*types_with_transport_mode_support=*/{BOOKMARKS, READING_LIST},
+      /*types_with_batch_uploader=*/{BOOKMARKS, READING_LIST});
+  Configure({BOOKMARKS, READING_LIST});
+  FinishAllDownloadsUntilIdle();
+  ASSERT_EQ(dtm_->GetActiveDataTypes(),
+            AddControlTypesTo({BOOKMARKS, READING_LIST}));
+
+  base::OnceCallback<void(LocalDataDescription)> bookmarks_upload_callback;
+
+  // Only the controller for bookmarks should be exercised.
+  EXPECT_CALL(*GetBatchUploader(READING_LIST), GetLocalDataDescription)
+      .Times(0);
+  EXPECT_CALL(*GetBatchUploader(BOOKMARKS), GetLocalDataDescription)
+      .WillOnce([&](base::OnceCallback<void(LocalDataDescription)> callback) {
+        bookmarks_upload_callback = std::move(callback);
+      });
+
+  base::MockCallback<
+      base::OnceCallback<void(std::map<DataType, LocalDataDescription>)>>
+      mock_completion_callback;
+
+  dtm_->GetLocalDataDescriptions({BOOKMARKS}, mock_completion_callback.Get());
+
+  ASSERT_TRUE(bookmarks_upload_callback);
+
+  // When bookmarks complete, the caller should also be notified about
+  // completion.
+  LocalDataDescription bookmarks_description;
+  bookmarks_description.item_count = 42;
+  EXPECT_CALL(mock_completion_callback,
+              Run(ElementsAre(Pair(BOOKMARKS, bookmarks_description))));
+  std::move(bookmarks_upload_callback).Run(bookmarks_description);
+}
+
+TEST_F(DataTypeManagerImplTest,
+       ShouldGetLocalDataDescriptionsForMultipleTypes) {
+  InitDataTypeManager(
+      /*types_without_transport_mode_support=*/{},
+      /*types_with_transport_mode_support=*/{BOOKMARKS, READING_LIST},
+      /*types_with_batch_uploader=*/{BOOKMARKS, READING_LIST});
+  Configure({BOOKMARKS, READING_LIST});
+  FinishAllDownloadsUntilIdle();
+  ASSERT_EQ(dtm_->GetActiveDataTypes(),
+            AddControlTypesTo({BOOKMARKS, READING_LIST}));
+
+  base::OnceCallback<void(LocalDataDescription)> bookmarks_upload_callback;
+  base::OnceCallback<void(LocalDataDescription)> reading_list_upload_callback;
+
+  // Both controllers should be exercised.
+  EXPECT_CALL(*GetBatchUploader(BOOKMARKS), GetLocalDataDescription)
+      .WillOnce([&](base::OnceCallback<void(LocalDataDescription)> callback) {
+        bookmarks_upload_callback = std::move(callback);
+      });
+  EXPECT_CALL(*GetBatchUploader(READING_LIST), GetLocalDataDescription)
+      .WillOnce([&](base::OnceCallback<void(LocalDataDescription)> callback) {
+        reading_list_upload_callback = std::move(callback);
+      });
+
+  base::MockCallback<
+      base::OnceCallback<void(std::map<DataType, LocalDataDescription>)>>
+      mock_completion_callback;
+  EXPECT_CALL(mock_completion_callback, Run).Times(0);
+
+  dtm_->GetLocalDataDescriptions({BOOKMARKS, READING_LIST},
+                                 mock_completion_callback.Get());
+
+  ASSERT_TRUE(bookmarks_upload_callback);
+  ASSERT_TRUE(reading_list_upload_callback);
+
+  // When bookmarks complete, nothing happens because reading list is still
+  // ongoing.
+  LocalDataDescription bookmarks_description;
+  bookmarks_description.item_count = 42;
+  std::move(bookmarks_upload_callback).Run(bookmarks_description);
+
+  // When both types complete, the caller should also be notified about
+  // completion.
+  LocalDataDescription reading_list_description;
+  reading_list_description.item_count = 31;
+  EXPECT_CALL(
+      mock_completion_callback,
+      Run(UnorderedElementsAre(Pair(BOOKMARKS, bookmarks_description),
+                               Pair(READING_LIST, reading_list_description))));
+  std::move(reading_list_upload_callback).Run(reading_list_description);
+}
+
+TEST_F(DataTypeManagerImplTest,
+       ShouldOnlyGetLocalDataDescriptionsFromActiveTypes) {
+  InitDataTypeManager(
+      /*types_without_transport_mode_support=*/{},
+      /*types_with_transport_mode_support=*/{BOOKMARKS, READING_LIST},
+      /*types_with_batch_uploader=*/{BOOKMARKS, READING_LIST});
+  Configure({BOOKMARKS});
+  FinishAllDownloadsUntilIdle();
+  ASSERT_EQ(dtm_->GetActiveDataTypes(), AddControlTypesTo({BOOKMARKS}));
+
+  base::OnceCallback<void(LocalDataDescription)> bookmarks_upload_callback;
+
+  // Only the controller for bookmarks should be exercised, because reading list
+  // is not active.
+  EXPECT_CALL(*GetBatchUploader(READING_LIST), GetLocalDataDescription)
+      .Times(0);
+  EXPECT_CALL(*GetBatchUploader(BOOKMARKS), GetLocalDataDescription)
+      .WillOnce([&](base::OnceCallback<void(LocalDataDescription)> callback) {
+        bookmarks_upload_callback = std::move(callback);
+      });
+
+  base::MockCallback<
+      base::OnceCallback<void(std::map<DataType, LocalDataDescription>)>>
+      mock_completion_callback;
+  EXPECT_CALL(mock_completion_callback, Run).Times(0);
+
+  dtm_->GetLocalDataDescriptions({BOOKMARKS, READING_LIST},
+                                 mock_completion_callback.Get());
+
+  // When bookmarks complete, the caller should also be notified about
+  // completion.
+  EXPECT_CALL(mock_completion_callback, Run(ElementsAre(Pair(BOOKMARKS, _))));
+  std::move(bookmarks_upload_callback).Run(LocalDataDescription());
+}
+
+TEST_F(DataTypeManagerImplTest,
+       ShouldReturnEmptyGetLocalDataDescriptionsIfNoActiveTypes) {
+  InitDataTypeManager(
+      /*types_without_transport_mode_support=*/{},
+      /*types_with_transport_mode_support=*/{BOOKMARKS, READING_LIST},
+      /*types_with_batch_uploader=*/{BOOKMARKS, READING_LIST});
+  Configure({});
+  FinishAllDownloadsUntilIdle();
+  ASSERT_EQ(dtm_->GetActiveDataTypes(), ControlTypes());
+
+  // The types aren't active so the batch uploaders should not be exercised.
+  EXPECT_CALL(*GetBatchUploader(READING_LIST), GetLocalDataDescription)
+      .Times(0);
+  EXPECT_CALL(*GetBatchUploader(BOOKMARKS), GetLocalDataDescription).Times(0);
+
+  base::MockCallback<
+      base::OnceCallback<void(std::map<DataType, LocalDataDescription>)>>
+      mock_completion_callback;
+  EXPECT_CALL(mock_completion_callback, Run);
+  dtm_->GetLocalDataDescriptions({BOOKMARKS, READING_LIST},
+                                 mock_completion_callback.Get());
+}
+
+TEST_F(DataTypeManagerImplTest,
+       ShouldOnlyMigrateActiveTypesUponTriggerLocalDataMigration) {
+  InitDataTypeManager(
+      /*types_without_transport_mode_support=*/{},
+      /*types_with_transport_mode_support=*/{BOOKMARKS, READING_LIST},
+      /*types_with_batch_uploader=*/{BOOKMARKS, READING_LIST});
+  Configure({BOOKMARKS});
+  FinishAllDownloadsUntilIdle();
+  ASSERT_EQ(dtm_->GetActiveDataTypes(), AddControlTypesTo({BOOKMARKS}));
+
+  // Only the controller for bookmarks should be exercised, because reading list
+  // is not active.
+  EXPECT_CALL(*GetBatchUploader(READING_LIST), TriggerLocalDataMigration)
+      .Times(0);
+  EXPECT_CALL(*GetBatchUploader(BOOKMARKS), TriggerLocalDataMigration);
+
+  dtm_->TriggerLocalDataMigration({BOOKMARKS, READING_LIST});
+}
+
 }  // namespace
 
 }  // namespace syncer
diff --git a/components/sync/service/sync_prefs.cc b/components/sync/service/sync_prefs.cc
index d775bc9..aad95901 100644
--- a/components/sync/service/sync_prefs.cc
+++ b/components/sync/service/sync_prefs.cc
@@ -469,13 +469,13 @@
       pref_service_, prefs::internal::kSyncEncryptionBootstrapTokenPerAccount,
       available_gaia_ids);
 
-  // TODO(crbug.com/337034860): This is not the right place for clearing
+  // TODO(crbug.com/368409110): This is not the right place for clearing
   // transport-data-related prefs - ideally there'd be an observer API for
   // "accounts on this device".
   SyncTransportDataPrefs::KeepAccountSettingsPrefsOnlyForUsers(
       pref_service_, available_gaia_ids);
 
-  // TODO(crbug.com/363927991): This is *absolutely* not the right place for
+  // TODO(crbug.com/368409110): This is *absolutely* not the right place for
   // clearing not-sync-related prefs. Move this elsewhere once signin code
   // provides an observer API for "accounts on this device".
   tab_groups::prefs::KeepAccountSettingsPrefsOnlyForUsers(pref_service_,
diff --git a/components/sync/service/sync_prefs_unittest.cc b/components/sync/service/sync_prefs_unittest.cc
index e5736143..7cc730e 100644
--- a/components/sync/service/sync_prefs_unittest.cc
+++ b/components/sync/service/sync_prefs_unittest.cc
@@ -47,14 +47,13 @@
  protected:
   SyncPrefsTest() {
     SyncPrefs::RegisterProfilePrefs(pref_service_.registry());
-    // TODO(crbug.com/337034860): This is required due to a workaround in
-    // KeepAccountSettingsPrefsOnlyForUsers(); see TODO there.
+    // TODO(crbug.com/368409110): These prefs are required due to a workaround
+    // in KeepAccountSettingsPrefsOnlyForUsers(); see TODOs there.
     SyncTransportDataPrefs::RegisterProfilePrefs(pref_service_.registry());
-    // TODO(crbug.com/363927991): Similarly, necessary for a workaround in
-    // KeepAccountSettingsPrefsOnlyForUsers(); see TODO there.
     pref_service_.registry()->RegisterDictionaryPref(
         tab_groups::prefs::kLocallyClosedRemoteTabGroupIds,
         base::Value::Dict());
+
     // Pref is registered in signin internal `PrimaryAccountManager`.
     pref_service_.registry()->RegisterBooleanPref(
         ::prefs::kExplicitBrowserSignin, false);
diff --git a/components/sync/service/sync_service_impl.cc b/components/sync/service/sync_service_impl.cc
index 1fdece8..b39bf71 100644
--- a/components/sync/service/sync_service_impl.cc
+++ b/components/sync/service/sync_service_impl.cc
@@ -212,21 +212,6 @@
 #endif  // !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
 }
 
-std::map<DataType, LocalDataDescription> JoinAllTypesAndLocalDataDescriptions(
-    const std::vector<std::pair<DataType, LocalDataDescription>>& pairs) {
-  std::map<DataType, LocalDataDescription> map;
-  for (const std::pair<DataType, LocalDataDescription>& pair : pairs) {
-    map.emplace(pair);
-  }
-  return map;
-}
-
-std::pair<DataType, LocalDataDescription> JoinTypeAndLocalDataDescription(
-    DataType type,
-    LocalDataDescription description) {
-  return {type, description};
-}
-
 }  // namespace
 
 SyncServiceImpl::InitParams::InitParams() = default;
@@ -2257,29 +2242,17 @@
     return;
   }
 
-  // Only retain types that are not only preferred but also active, that is,
-  // those which are configured and have not encountered any error.
-  types.RetainAll(GetActiveDataTypes());
-
   if (!base::FeatureList::IsEnabled(
           syncer::kSyncEnableModelTypeLocalDataBatchUploaders)) {
+    // Only retain types that are not only preferred but also active, that is,
+    // those which are configured and have not encountered any error.
+    types.RetainAll(GetActiveDataTypes());
+
     sync_client_->GetLocalDataDescriptions(types, std::move(callback));
     return;
   }
 
-  types.RetainAll(GetDataTypesWithLocalDataBatchUploader());
-  auto barrier_callback =
-      base::BarrierCallback<std::pair<DataType, LocalDataDescription>>(
-          types.size(), base::BindOnce(&JoinAllTypesAndLocalDataDescriptions)
-                            .Then(std::move(callback)));
-  for (DataType type : types) {
-    data_type_manager_->GetControllerMap()
-        .at(type)
-        ->GetLocalDataBatchUploader()
-        ->GetLocalDataDescription(
-            base::BindOnce(&JoinTypeAndLocalDataDescription, type)
-                .Then(barrier_callback));
-  }
+  data_type_manager_->GetLocalDataDescriptions(types, std::move(callback));
 }
 
 void SyncServiceImpl::TriggerLocalDataMigration(DataTypeSet types) {
@@ -2297,34 +2270,17 @@
     return;
   }
 
-  // Only retain types that are not only preferred but also active, that is,
-  // those which are configured and have not encountered any error.
-  types.RetainAll(GetActiveDataTypes());
-
   if (!base::FeatureList::IsEnabled(
           syncer::kSyncEnableModelTypeLocalDataBatchUploaders)) {
+    // Only retain types that are not only preferred but also active, that is,
+    // those which are configured and have not encountered any error.
+    types.RetainAll(GetActiveDataTypes());
+
     sync_client_->TriggerLocalDataMigration(types);
     return;
   }
 
-  types.RetainAll(GetDataTypesWithLocalDataBatchUploader());
-  for (DataType type : types) {
-    data_type_manager_->GetControllerMap()
-        .at(type)
-        ->GetLocalDataBatchUploader()
-        ->TriggerLocalDataMigration();
-  }
-}
-
-DataTypeSet SyncServiceImpl::GetDataTypesWithLocalDataBatchUploader() const {
-  DataTypeSet types;
-  for (const auto& [type, controller] :
-       data_type_manager_->GetControllerMap()) {
-    if (controller->GetLocalDataBatchUploader()) {
-      types.Put(type);
-    }
-  }
-  return types;
+  return data_type_manager_->TriggerLocalDataMigration(types);
 }
 
 }  // namespace syncer
diff --git a/components/sync/service/sync_service_impl.h b/components/sync/service/sync_service_impl.h
index 9db2d451..cd2f17a 100644
--- a/components/sync/service/sync_service_impl.h
+++ b/components/sync/service/sync_service_impl.h
@@ -366,9 +366,6 @@
   // passphrase type.
   void RegisterTrustedVaultSyntheticFieldTrialsIfNecessary();
 
-  // Returns the types that have a non-null DataTypeLocalDataBatchUploader.
-  DataTypeSet GetDataTypesWithLocalDataBatchUploader() const;
-
   // The actual implementation of GetLocalDataDescriptions(), where some code
   // paths can be synchronous. GetLocalDataDescriptions() posts a task before
   // invoking this, to ensure that the public call is always async.
diff --git a/components/sync/service/sync_service_impl_unittest.cc b/components/sync/service/sync_service_impl_unittest.cc
index ae1fd8c3..6a0b5697 100644
--- a/components/sync/service/sync_service_impl_unittest.cc
+++ b/components/sync/service/sync_service_impl_unittest.cc
@@ -2145,70 +2145,6 @@
 }
 
 TEST_F(SyncServiceImplTest,
-       ShouldOnlyForwardEnabledTypesUponGetLocalDataDescriptions) {
-  SignInWithoutSyncConsent();
-
-  // Both DEVICE_INFO and AUTOFILL will be passed to GetLocalDataDescription(),
-  // but only DEVICE_INFO is enabled in transport mode. So only the DEVICE_INFO
-  // uploader should be queried.
-  auto device_info_uploader =
-      std::make_unique<MockDataTypeLocalDataBatchUploader>();
-  auto autofill_uploader =
-      std::make_unique<MockDataTypeLocalDataBatchUploader>();
-  EXPECT_CALL(*device_info_uploader, GetLocalDataDescription)
-      .WillOnce(base::test::RunOnceCallback<0>(syncer::LocalDataDescription()));
-  EXPECT_CALL(*autofill_uploader, GetLocalDataDescription).Times(0);
-
-  std::vector<FakeControllerInitParams> params;
-  params.emplace_back(DEVICE_INFO, /*enable_transport_mode=*/true,
-                      std::move(device_info_uploader));
-  params.emplace_back(AUTOFILL, /*enable_transport_mode=*/false,
-                      std::move(autofill_uploader));
-  InitializeService(std::move(params));
-  base::RunLoop().RunUntilIdle();
-
-  ASSERT_EQ(service()->GetActiveDataTypes(),
-            DataTypeSet({NIGORI, DEVICE_INFO}));
-
-  base::test::TestFuture<std::map<DataType, LocalDataDescription>> descriptions;
-  service()->GetLocalDataDescriptions({DEVICE_INFO, AUTOFILL},
-                                      descriptions.GetCallback());
-  EXPECT_TRUE(descriptions.Wait());
-}
-
-TEST_F(SyncServiceImplTest,
-       ShouldNotForwardTypesWithErrorUponGetLocalDataDescriptions) {
-  SignInWithoutSyncConsent();
-
-  // Both DEVICE_INFO and AUTOFILL_WALLET_DATA will be passed to
-  // GetLocalDataDescription(), but AUTOFILL_WALLET_DATA will be in an error
-  // state. So only the DEVICE_INFO uploader should be queried.
-  auto device_info_uploader =
-      std::make_unique<MockDataTypeLocalDataBatchUploader>();
-  auto wallet_uploader = std::make_unique<MockDataTypeLocalDataBatchUploader>();
-  EXPECT_CALL(*device_info_uploader, GetLocalDataDescription)
-      .WillOnce(base::test::RunOnceCallback<0>(syncer::LocalDataDescription()));
-  EXPECT_CALL(*wallet_uploader, GetLocalDataDescription).Times(0);
-
-  std::vector<FakeControllerInitParams> params;
-  params.emplace_back(DEVICE_INFO, /*enable_transport_mode=*/true,
-                      std::move(device_info_uploader));
-  params.emplace_back(AUTOFILL_WALLET_DATA, /*enable_transport_mode=*/true,
-                      std::move(wallet_uploader));
-  InitializeService(std::move(params));
-  base::RunLoop().RunUntilIdle();
-
-  // Simulate a data type error.
-  service()->ReportDataTypeErrorForTest(AUTOFILL_WALLET_DATA);
-  ASSERT_FALSE(service()->GetActiveDataTypes().Has(AUTOFILL_WALLET_DATA));
-
-  base::test::TestFuture<std::map<DataType, LocalDataDescription>> descriptions;
-  service()->GetLocalDataDescriptions({DEVICE_INFO, AUTOFILL_WALLET_DATA},
-                                      descriptions.GetCallback());
-  EXPECT_TRUE(descriptions.Wait());
-}
-
-TEST_F(SyncServiceImplTest,
        ShouldNotForwardUponGetLocalDataDescriptionsIfSyncDisabled) {
   prefs()->SetManagedPref(prefs::internal::kSyncManaged, base::Value(true));
   SignInWithoutSyncConsent();
@@ -2239,28 +2175,6 @@
 }
 
 TEST_F(SyncServiceImplTest,
-       ShouldReturnEmptyUponGetLocalDataDescriptionsForSignedOutUsers) {
-  // DEVICE_INFO will be passed to GetLocalDataDescription(), but the user is
-  // signed out. So the uploader should not be queried.
-  auto device_info_uploader =
-      std::make_unique<MockDataTypeLocalDataBatchUploader>();
-  EXPECT_CALL(*device_info_uploader, GetLocalDataDescription).Times(0);
-
-  std::vector<FakeControllerInitParams> params;
-  params.emplace_back(DEVICE_INFO, /*enable_transport_mode=*/true,
-                      std::move(device_info_uploader));
-  InitializeService(std::move(params));
-  base::RunLoop().RunUntilIdle();
-
-  ASSERT_TRUE(service()->GetPreferredDataTypes().Has(DEVICE_INFO));
-
-  base::test::TestFuture<std::map<DataType, LocalDataDescription>> descriptions;
-  service()->GetLocalDataDescriptions({DEVICE_INFO},
-                                      descriptions.GetCallback());
-  EXPECT_THAT(descriptions.Get(), IsEmpty());
-}
-
-TEST_F(SyncServiceImplTest,
        ShouldReturnEmptyUponGetLocalDataDescriptionsForSyncingUsers) {
   PopulatePrefsForInitialSyncFeatureSetupComplete();
   SignInWithSyncConsent();
@@ -2286,96 +2200,6 @@
   EXPECT_THAT(descriptions.Get(), IsEmpty());
 }
 
-TEST_F(SyncServiceImplTest, ShouldJoinLocalDataDescriptionsForDifferentTypes) {
-  SignInWithoutSyncConsent();
-
-  auto device_info_uploader =
-      std::make_unique<MockDataTypeLocalDataBatchUploader>();
-  auto wallet_uploader = std::make_unique<MockDataTypeLocalDataBatchUploader>();
-  syncer::LocalDataDescription device_info_description;
-  device_info_description.item_count = 42;
-  syncer::LocalDataDescription wallet_description;
-  wallet_description.item_count = 43;
-  EXPECT_CALL(*device_info_uploader, GetLocalDataDescription)
-      .WillOnce(base::test::RunOnceCallback<0>(device_info_description));
-  EXPECT_CALL(*wallet_uploader, GetLocalDataDescription)
-      .WillOnce(base::test::RunOnceCallback<0>(wallet_description));
-
-  std::vector<FakeControllerInitParams> params;
-  params.emplace_back(DEVICE_INFO, /*enable_transport_mode=*/true,
-                      std::move(device_info_uploader));
-  params.emplace_back(AUTOFILL_WALLET_DATA, /*enable_transport_mode=*/true,
-                      std::move(wallet_uploader));
-  InitializeService(std::move(params));
-  base::RunLoop().RunUntilIdle();
-
-  base::test::TestFuture<std::map<DataType, LocalDataDescription>> descriptions;
-  service()->GetLocalDataDescriptions({DEVICE_INFO, AUTOFILL_WALLET_DATA},
-                                      descriptions.GetCallback());
-  EXPECT_THAT(
-      descriptions.Get(),
-      UnorderedElementsAre(Pair(DEVICE_INFO, device_info_description),
-                           Pair(AUTOFILL_WALLET_DATA, wallet_description)));
-}
-
-TEST_F(SyncServiceImplTest,
-       ShouldOnlyForwardEnabledTypesUponTriggerLocalDataMigration) {
-  SignInWithoutSyncConsent();
-
-  // Both DEVICE_INFO and AUTOFILL will be passed to TriggerLocalDataMigration()
-  // but only DEVICE_INFO is enabled in transport mode. So only DEVICE_INFO
-  // should be uploaded.
-  auto device_info_uploader =
-      std::make_unique<MockDataTypeLocalDataBatchUploader>();
-  auto autofill_uploader =
-      std::make_unique<MockDataTypeLocalDataBatchUploader>();
-  EXPECT_CALL(*device_info_uploader, TriggerLocalDataMigration);
-  EXPECT_CALL(*autofill_uploader, TriggerLocalDataMigration).Times(0);
-
-  std::vector<FakeControllerInitParams> params;
-  params.emplace_back(DEVICE_INFO, /*enable_transport_mode=*/true,
-                      std::move(device_info_uploader));
-  params.emplace_back(AUTOFILL, /*enable_transport_mode=*/false,
-                      std::move(autofill_uploader));
-  InitializeService(std::move(params));
-  base::RunLoop().RunUntilIdle();
-
-  // Only DEVICE_INFO is enabled since AUTOFILL is not supported in
-  // transport-only mode.
-  ASSERT_EQ(service()->GetActiveDataTypes(),
-            DataTypeSet({NIGORI, DEVICE_INFO}));
-
-  service()->TriggerLocalDataMigration({DEVICE_INFO, AUTOFILL});
-}
-
-TEST_F(SyncServiceImplTest,
-       ShouldNotForwardTypesWithErrorUponTriggerLocalDataMigration) {
-  SignInWithoutSyncConsent();
-
-  // Both DEVICE_INFO and AUTOFILL_WALLET_DATA will be passed to
-  // TriggerLocalDataMigration(), but AUTOFILL_WALLET_DATA will be in an error
-  // state. So only DEVICE_INFO should be uploaded.
-  auto device_info_uploader =
-      std::make_unique<MockDataTypeLocalDataBatchUploader>();
-  auto wallet_uploader = std::make_unique<MockDataTypeLocalDataBatchUploader>();
-  EXPECT_CALL(*device_info_uploader, TriggerLocalDataMigration);
-  EXPECT_CALL(*wallet_uploader, TriggerLocalDataMigration).Times(0);
-
-  std::vector<FakeControllerInitParams> params;
-  params.emplace_back(DEVICE_INFO, /*enable_transport_mode=*/true,
-                      std::move(device_info_uploader));
-  params.emplace_back(AUTOFILL_WALLET_DATA, /*enable_transport_mode=*/true,
-                      std::move(wallet_uploader));
-  InitializeService(std::move(params));
-  base::RunLoop().RunUntilIdle();
-
-  // Simulate a data type error.
-  service()->ReportDataTypeErrorForTest(AUTOFILL_WALLET_DATA);
-  ASSERT_FALSE(service()->GetActiveDataTypes().Has(AUTOFILL_WALLET_DATA));
-
-  service()->TriggerLocalDataMigration({DEVICE_INFO, AUTOFILL_WALLET_DATA});
-}
-
 TEST_F(SyncServiceImplTest,
        ShouldNotForwardUponTriggerLocalDataMigrationIfSyncDisabled) {
   prefs()->SetManagedPref(prefs::internal::kSyncManaged, base::Value(true));
@@ -2404,25 +2228,6 @@
 }
 
 TEST_F(SyncServiceImplTest,
-       ShouldDoNothingUponTriggerLocalDataMigrationForNotSignedInUsers) {
-  // DEVICE_INFO will be passed to TriggerLocalDataMigration(), but the user is
-  // signed out. So data should not be uploaded.
-  auto device_info_uploader =
-      std::make_unique<MockDataTypeLocalDataBatchUploader>();
-  EXPECT_CALL(*device_info_uploader, TriggerLocalDataMigration).Times(0);
-
-  std::vector<FakeControllerInitParams> params;
-  params.emplace_back(DEVICE_INFO, /*enable_transport_mode=*/true,
-                      std::move(device_info_uploader));
-  InitializeService(std::move(params));
-  base::RunLoop().RunUntilIdle();
-
-  ASSERT_TRUE(service()->GetPreferredDataTypes().Has(DEVICE_INFO));
-
-  service()->TriggerLocalDataMigration({DEVICE_INFO});
-}
-
-TEST_F(SyncServiceImplTest,
        ShouldDoNothingUponTriggerLocalDataMigrationForSyncingUsers) {
   PopulatePrefsForInitialSyncFeatureSetupComplete();
   SignInWithSyncConsent();
diff --git a/components/sync/service/sync_user_settings_impl_unittest.cc b/components/sync/service/sync_user_settings_impl_unittest.cc
index 2bcbbd0..891e29d 100644
--- a/components/sync/service/sync_user_settings_impl_unittest.cc
+++ b/components/sync/service/sync_user_settings_impl_unittest.cc
@@ -71,7 +71,7 @@
   SyncUserSettingsImplTest() {
     SyncPrefs::RegisterProfilePrefs(pref_service_.registry());
     SyncTransportDataPrefs::RegisterProfilePrefs(pref_service_.registry());
-    // TODO(crbug.com/363927991): Necessary for a workaround in
+    // TODO(crbug.com/368409110): Necessary for a workaround in
     // SyncPrefs::KeepAccountSettingsPrefsOnlyForUsers(); see TODO there.
     pref_service_.registry()->RegisterDictionaryPref(
         tab_groups::prefs::kLocallyClosedRemoteTabGroupIds,
diff --git a/components/sync/test/data_type_manager_mock.h b/components/sync/test/data_type_manager_mock.h
index 17b4239b..8fa49b0 100644
--- a/components/sync/test/data_type_manager_mock.h
+++ b/components/sync/test/data_type_manager_mock.h
@@ -8,6 +8,7 @@
 #include "base/functional/callback_forward.h"
 #include "components/sync/model/sync_error.h"
 #include "components/sync/service/data_type_manager.h"
+#include "components/sync/service/local_data_description.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace syncer {
@@ -50,6 +51,13 @@
               GetTypesWithUnsyncedData,
               (DataTypeSet, base::OnceCallback<void(DataTypeSet)>),
               (const override));
+  MOCK_METHOD(
+      void,
+      GetLocalDataDescriptions,
+      (DataTypeSet,
+       base::OnceCallback<void(std::map<DataType, LocalDataDescription>)>),
+      (override));
+  MOCK_METHOD(void, TriggerLocalDataMigration, (DataTypeSet), (override));
   MOCK_METHOD(State, state, (), (const override));
   MOCK_METHOD(TypeStatusMapForDebugging,
               GetTypeStatusMapForDebugging,
diff --git a/components/test/data/autofill/label-doms/basic-test.html b/components/test/data/autofill/label-doms/basic-test.html
new file mode 100644
index 0000000..697c6ee
--- /dev/null
+++ b/components/test/data/autofill/label-doms/basic-test.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      .overlaying {
+        position: absolute;
+        left: 10px;
+      }
+    </style>
+  </head>
+  <body>
+    <div>
+      <label>Nested input<input id=input1></label>
+    </div>
+    <div>
+      <label for=input2>Label for</label><input id=input2>
+    </div>
+    <div>
+      Preceding text node <input id=input3>
+    </div>
+    <div>
+      <input placeholder=placeholder id=input4>
+    </div>
+    <div>
+      <input id=input5>
+      <span class=overlaying>poor man's placeholder</span>
+    </div>
+    <div>
+      <input id=input6 aria-label=aria-label>
+    </div>
+    <div>
+      <div>
+        sibling/ancestor
+      </div>
+      <div>
+        <input id=input7>
+      </div>
+    </div>
+  </body>
+</html>
diff --git a/components/test/data/autofill/label-doms/basic-test.json b/components/test/data/autofill/label-doms/basic-test.json
new file mode 100644
index 0000000..5610de2
--- /dev/null
+++ b/components/test/data/autofill/label-doms/basic-test.json
@@ -0,0 +1,29 @@
+[ {
+   "heuristic": "forId",
+   "label": "Nested input",
+   "name": "input1"
+}, {
+   "heuristic": "forId",
+   "label": "Label for",
+   "name": "input2"
+}, {
+   "heuristic": "combined",
+   "label": "Preceding text node",
+   "name": "input3"
+}, {
+   "heuristic": "placeholder",
+   "label": "placeholder",
+   "name": "input4"
+}, {
+   "heuristic": "overlayingLabel",
+   "label": "poor man's placeholder",
+   "name": "input5"
+}, {
+   "heuristic": "ariaLabel",
+   "label": "aria-label",
+   "name": "input6"
+}, {
+   "heuristic": "divTable",
+   "label": "sibling/ancestor",
+   "name": "input7"
+} ]
diff --git a/components/translate/core/common/translate_util.cc b/components/translate/core/common/translate_util.cc
index 4dd0204..06a201a 100644
--- a/components/translate/core/common/translate_util.cc
+++ b/components/translate/core/common/translate_util.cc
@@ -28,10 +28,6 @@
 #endif
 );
 
-BASE_FEATURE(kTFLiteLanguageDetectionIgnoreEnabled,
-             "TFLiteLanguageDetectionIgnoreEnabled",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 GURL GetTranslateSecurityOrigin() {
   std::string security_origin(kSecurityOrigin);
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
@@ -46,10 +42,6 @@
   return base::FeatureList::IsEnabled(kTFLiteLanguageDetectionEnabled);
 }
 
-bool IsTFLiteLanguageDetectionIgnoreEnabled() {
-  return base::FeatureList::IsEnabled(kTFLiteLanguageDetectionIgnoreEnabled);
-}
-
 float GetTFLiteLanguageDetectionThreshold() {
   return base::GetFieldTrialParamByFeatureAsDouble(
       kTFLiteLanguageDetectionEnabled, "reliability_threshold", .7);
diff --git a/components/translate/core/common/translate_util.h b/components/translate/core/common/translate_util.h
index 9cd0318..d502946 100644
--- a/components/translate/core/common/translate_util.h
+++ b/components/translate/core/common/translate_util.h
@@ -14,10 +14,6 @@
 // Controls whether the TFLite-based language detection is enabled.
 BASE_DECLARE_FEATURE(kTFLiteLanguageDetectionEnabled);
 
-// Controls whether the TFLite-based language detection is computed, but ignored
-// and the CLD3 version is used instead.
-BASE_DECLARE_FEATURE(kTFLiteLanguageDetectionIgnoreEnabled);
-
 // Isolated world sets following security-origin by default.
 extern const char kSecurityOrigin[];
 
@@ -28,10 +24,6 @@
 // Return whether TFLite-based language detection is enabled.
 bool IsTFLiteLanguageDetectionEnabled();
 
-// Return whether TFLite-based language detection is enabled, but the result is
-// ignored.
-bool IsTFLiteLanguageDetectionIgnoreEnabled();
-
 // Return the threshold used to determine if TFLite language detection model's
 // prediction is reliable.
 float GetTFLiteLanguageDetectionThreshold();
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index bc1dfe5..9e0c3bd 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -342,13 +342,6 @@
              "UseDisplaySDRMaxLuminanceNits",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Invalidate the `viz::LocalSurfaceId` on the browser side when the page is
-// navigated away. This flag serves as the kill-switch for the uncaught edge
-// cases in production.
-BASE_FEATURE(kInvalidateLocalSurfaceIdPreCommit,
-             "InvalidateLocalSurfaceIdPreCommit",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // On mac, when the RenderWidgetHostViewMac is hidden, also hide the
 // DelegatedFrameHost. Among other things, it unlocks the compositor frames,
 // which can saves hundreds of MiB of memory with bfcache entries.
diff --git a/components/viz/common/features.h b/components/viz/common/features.h
index 0e57931a..2cb0335 100644
--- a/components/viz/common/features.h
+++ b/components/viz/common/features.h
@@ -104,7 +104,6 @@
 VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kEnableADPFGpuCompositorThread);
 VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kEnableADPFAsyncThreadsVerification);
 VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kUseDisplaySDRMaxLuminanceNits);
-VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kInvalidateLocalSurfaceIdPreCommit);
 VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kHideDelegatedFrameHostMac);
 VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kEvictionUnlocksResources);
 VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kSingleVideoFrameRateThrottling);
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index e2096b8..c69bd7c9 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1713,8 +1713,6 @@
     "renderer_host/indexed_db_client_state_checker_factory.h",
     "renderer_host/input/fling_scheduler.cc",
     "renderer_host/input/fling_scheduler.h",
-    "renderer_host/input/input_device_change_observer.cc",
-    "renderer_host/input/input_device_change_observer.h",
     "renderer_host/input/input_injector_impl.cc",
     "renderer_host/input/input_injector_impl.h",
     "renderer_host/input/motion_event_web.cc",
@@ -2226,6 +2224,8 @@
     "web_contents/file_chooser_impl.h",
     "web_contents/java_script_dialog_commit_deferring_condition.cc",
     "web_contents/java_script_dialog_commit_deferring_condition.h",
+    "web_contents/slow_web_preference_cache.cc",
+    "web_contents/slow_web_preference_cache.h",
     "web_contents/web_contents_impl.cc",
     "web_contents/web_contents_impl.h",
     "web_contents/web_contents_view.h",
@@ -2405,10 +2405,12 @@
       "compute_pressure/pressure_client_impl.h",
       "compute_pressure/pressure_service_base.cc",
       "compute_pressure/pressure_service_base.h",
+      "compute_pressure/pressure_service_for_dedicated_worker.cc",
+      "compute_pressure/pressure_service_for_dedicated_worker.h",
       "compute_pressure/pressure_service_for_frame.cc",
       "compute_pressure/pressure_service_for_frame.h",
-      "compute_pressure/pressure_service_for_worker.cc",
-      "compute_pressure/pressure_service_for_worker.h",
+      "compute_pressure/pressure_service_for_shared_worker.cc",
+      "compute_pressure/pressure_service_for_shared_worker.h",
       "compute_pressure/web_contents_pressure_manager_proxy.cc",
       "compute_pressure/web_contents_pressure_manager_proxy.h",
     ]
diff --git a/content/browser/compute_pressure/pressure_service_for_dedicated_worker.cc b/content/browser/compute_pressure/pressure_service_for_dedicated_worker.cc
new file mode 100644
index 0000000..42e7ad1
--- /dev/null
+++ b/content/browser/compute_pressure/pressure_service_for_dedicated_worker.cc
@@ -0,0 +1,48 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/compute_pressure/pressure_service_for_dedicated_worker.h"
+
+#include "content/browser/compute_pressure/web_contents_pressure_manager_proxy.h"
+#include "content/browser/renderer_host/render_frame_host_impl.h"
+#include "content/browser/worker_host/dedicated_worker_host.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+PressureServiceForDedicatedWorker::PressureServiceForDedicatedWorker(
+    DedicatedWorkerHost* host)
+    : worker_host_(host) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+PressureServiceForDedicatedWorker::~PressureServiceForDedicatedWorker() =
+    default;
+
+bool PressureServiceForDedicatedWorker::ShouldDeliverUpdate() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // https://www.w3.org/TR/compute-pressure/#dfn-owning-document-set
+  // https://www.w3.org/TR/compute-pressure/#dfn-may-receive-data
+  auto* rfh =
+      RenderFrameHostImpl::FromID(worker_host_->GetAncestorRenderFrameHostId());
+  return HasImplicitFocus(rfh);
+}
+
+std::optional<base::UnguessableToken>
+PressureServiceForDedicatedWorker::GetTokenFor(
+    device::mojom::PressureSource source) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const auto* web_contents =
+      WebContents::FromRenderFrameHost(RenderFrameHostImpl::FromID(
+          worker_host_->GetAncestorRenderFrameHostId()));
+  if (const auto* pressure_manager_proxy =
+          WebContentsPressureManagerProxy::FromWebContents(web_contents)) {
+    return pressure_manager_proxy->GetTokenFor(source);
+  }
+  return std::nullopt;
+}
+
+}  // namespace content
diff --git a/content/browser/compute_pressure/pressure_service_for_dedicated_worker.h b/content/browser/compute_pressure/pressure_service_for_dedicated_worker.h
new file mode 100644
index 0000000..b40805f
--- /dev/null
+++ b/content/browser/compute_pressure/pressure_service_for_dedicated_worker.h
@@ -0,0 +1,44 @@
+// 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 CONTENT_BROWSER_COMPUTE_PRESSURE_PRESSURE_SERVICE_FOR_DEDICATED_WORKER_H_
+#define CONTENT_BROWSER_COMPUTE_PRESSURE_PRESSURE_SERVICE_FOR_DEDICATED_WORKER_H_
+
+#include <type_traits>
+
+#include "base/memory/raw_ptr.h"
+#include "base/thread_annotations.h"
+#include "content/browser/compute_pressure/pressure_service_base.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class DedicatedWorkerHost;
+
+class CONTENT_EXPORT PressureServiceForDedicatedWorker
+    : public PressureServiceBase {
+ public:
+  explicit PressureServiceForDedicatedWorker(DedicatedWorkerHost* host);
+
+  ~PressureServiceForDedicatedWorker() override;
+
+  PressureServiceForDedicatedWorker(const PressureServiceForDedicatedWorker&) =
+      delete;
+  PressureServiceForDedicatedWorker& operator=(
+      const PressureServiceForDedicatedWorker&) = delete;
+
+  // PressureServiceBase overrides.
+  bool ShouldDeliverUpdate() const override;
+  std::optional<base::UnguessableToken> GetTokenFor(
+      device::mojom::PressureSource) const override;
+
+ private:
+  // DedicatedWorkerHost owns an instance of this class.
+  raw_ptr<DedicatedWorkerHost> GUARDED_BY_CONTEXT(sequence_checker_)
+      worker_host_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_COMPUTE_PRESSURE_PRESSURE_SERVICE_FOR_DEDICATED_WORKER_H_
diff --git a/content/browser/compute_pressure/pressure_service_for_shared_worker.cc b/content/browser/compute_pressure/pressure_service_for_shared_worker.cc
new file mode 100644
index 0000000..a19f3a9f
--- /dev/null
+++ b/content/browser/compute_pressure/pressure_service_for_shared_worker.cc
@@ -0,0 +1,48 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/compute_pressure/pressure_service_for_shared_worker.h"
+
+#include "base/ranges/algorithm.h"
+#include "content/browser/compute_pressure/web_contents_pressure_manager_proxy.h"
+#include "content/browser/renderer_host/render_frame_host_impl.h"
+#include "content/browser/worker_host/shared_worker_host.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+PressureServiceForSharedWorker::PressureServiceForSharedWorker(
+    SharedWorkerHost* host)
+    : worker_host_(host) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+PressureServiceForSharedWorker::~PressureServiceForSharedWorker() = default;
+
+bool PressureServiceForSharedWorker::ShouldDeliverUpdate() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // https://www.w3.org/TR/compute-pressure/#dfn-owning-document-set
+  // https://www.w3.org/TR/compute-pressure/#dfn-may-receive-data
+  if (base::ranges::any_of(
+          worker_host_->GetRenderFrameIDsForWorker(), [](const auto& id) {
+            return HasImplicitFocus(RenderFrameHostImpl::FromID(id));
+          })) {
+    return true;
+  }
+  return false;
+}
+
+std::optional<base::UnguessableToken>
+PressureServiceForSharedWorker::GetTokenFor(
+    device::mojom::PressureSource source) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Shared workers always return std::nullopt, as there is no one
+  // corresponding WebContents instance to retrieve a
+  // WebContentsPressureManagerProxy instance from.
+  return std::nullopt;
+}
+
+}  // namespace content
diff --git a/content/browser/compute_pressure/pressure_service_for_shared_worker.h b/content/browser/compute_pressure/pressure_service_for_shared_worker.h
new file mode 100644
index 0000000..543c357
--- /dev/null
+++ b/content/browser/compute_pressure/pressure_service_for_shared_worker.h
@@ -0,0 +1,43 @@
+// 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 CONTENT_BROWSER_COMPUTE_PRESSURE_PRESSURE_SERVICE_FOR_SHARED_WORKER_H_
+#define CONTENT_BROWSER_COMPUTE_PRESSURE_PRESSURE_SERVICE_FOR_SHARED_WORKER_H_
+
+#include <type_traits>
+
+#include "base/memory/raw_ptr.h"
+#include "base/thread_annotations.h"
+#include "content/browser/compute_pressure/pressure_service_base.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class SharedWorkerHost;
+
+class CONTENT_EXPORT PressureServiceForSharedWorker
+    : public PressureServiceBase {
+ public:
+  explicit PressureServiceForSharedWorker(SharedWorkerHost* host);
+
+  ~PressureServiceForSharedWorker() override;
+
+  PressureServiceForSharedWorker(const PressureServiceForSharedWorker&) =
+      delete;
+  PressureServiceForSharedWorker& operator=(
+      const PressureServiceForSharedWorker&) = delete;
+
+  // PressureServiceBase overrides.
+  bool ShouldDeliverUpdate() const override;
+  std::optional<base::UnguessableToken> GetTokenFor(
+      device::mojom::PressureSource) const override;
+
+ private:
+  // SharedWorkerHost owns an instance of this class.
+  raw_ptr<SharedWorkerHost> GUARDED_BY_CONTEXT(sequence_checker_) worker_host_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_COMPUTE_PRESSURE_PRESSURE_SERVICE_FOR_SHARED_WORKER_H_
diff --git a/content/browser/compute_pressure/pressure_service_for_worker.cc b/content/browser/compute_pressure/pressure_service_for_worker.cc
deleted file mode 100644
index 1f79798f..0000000
--- a/content/browser/compute_pressure/pressure_service_for_worker.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/compute_pressure/pressure_service_for_worker.h"
-
-#include "content/browser/compute_pressure/web_contents_pressure_manager_proxy.h"
-#include "content/browser/renderer_host/render_frame_host_impl.h"
-#include "content/browser/worker_host/dedicated_worker_host.h"
-#include "content/browser/worker_host/shared_worker_host.h"
-
-namespace content {
-
-template <typename WorkerHost>
-bool PressureServiceForWorker<WorkerHost>::ShouldDeliverUpdate() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // https://www.w3.org/TR/compute-pressure/#dfn-owning-document-set
-  // https://www.w3.org/TR/compute-pressure/#dfn-may-receive-data
-  if constexpr (std::is_same_v<WorkerHost, DedicatedWorkerHost>) {
-    auto* rfh = RenderFrameHostImpl::FromID(
-        worker_host_->GetAncestorRenderFrameHostId());
-    return HasImplicitFocus(rfh);
-  } else if constexpr (std::is_same_v<WorkerHost, SharedWorkerHost>) {
-    if (base::ranges::any_of(
-            worker_host_->GetRenderFrameIDsForWorker(), [](const auto& id) {
-              return HasImplicitFocus(RenderFrameHostImpl::FromID(id));
-            })) {
-      return true;
-    }
-  }
-  return false;
-}
-
-template <typename WorkerHost>
-std::optional<base::UnguessableToken>
-PressureServiceForWorker<WorkerHost>::GetTokenFor(
-    device::mojom::PressureSource source) const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if constexpr (std::is_same_v<WorkerHost, DedicatedWorkerHost>) {
-    const auto* web_contents =
-        WebContents::FromRenderFrameHost(RenderFrameHostImpl::FromID(
-            worker_host_->GetAncestorRenderFrameHostId()));
-    if (const auto* pressure_manager_proxy =
-            WebContentsPressureManagerProxy::FromWebContents(web_contents)) {
-      return pressure_manager_proxy->GetTokenFor(source);
-    }
-  }
-
-  // Shared workers always return std::nullopt, as there is no one
-  // corresponding WebContents instance to retrieve a
-  // WebContentsPressureManagerProxy instance from.
-  return std::nullopt;
-}
-
-template class EXPORT_TEMPLATE_DEFINE(CONTENT_EXPORT)
-    PressureServiceForWorker<DedicatedWorkerHost>;
-template class EXPORT_TEMPLATE_DEFINE(CONTENT_EXPORT)
-    PressureServiceForWorker<SharedWorkerHost>;
-
-}  // namespace content
diff --git a/content/browser/compute_pressure/pressure_service_for_worker.h b/content/browser/compute_pressure/pressure_service_for_worker.h
deleted file mode 100644
index e70f1e71..0000000
--- a/content/browser/compute_pressure/pressure_service_for_worker.h
+++ /dev/null
@@ -1,57 +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 CONTENT_BROWSER_COMPUTE_PRESSURE_PRESSURE_SERVICE_FOR_WORKER_H_
-#define CONTENT_BROWSER_COMPUTE_PRESSURE_PRESSURE_SERVICE_FOR_WORKER_H_
-
-#include <type_traits>
-
-#include "base/export_template.h"
-#include "base/functional/callback.h"
-#include "base/memory/raw_ptr.h"
-#include "base/ranges/algorithm.h"
-#include "base/sequence_checker.h"
-#include "base/thread_annotations.h"
-#include "content/browser/compute_pressure/pressure_service_base.h"
-#include "content/common/content_export.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace content {
-
-class DedicatedWorkerHost;
-class SharedWorkerHost;
-
-template <typename WorkerHost>
-class EXPORT_TEMPLATE_DECLARE(CONTENT_EXPORT) PressureServiceForWorker
-    : public PressureServiceBase {
- public:
-  explicit PressureServiceForWorker(WorkerHost* host) : worker_host_(host) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  }
-
-  ~PressureServiceForWorker() override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  }
-
-  PressureServiceForWorker(const PressureServiceForWorker&) = delete;
-  PressureServiceForWorker& operator=(const PressureServiceForWorker&) = delete;
-
-  // PressureServiceBase override.
-  bool ShouldDeliverUpdate() const override;
-  std::optional<base::UnguessableToken> GetTokenFor(
-      device::mojom::PressureSource) const override;
-
- private:
-  // DedicatedWorkerHost/SharedWorkerHost owns an instance of this class.
-  raw_ptr<WorkerHost> GUARDED_BY_CONTEXT(sequence_checker_) worker_host_;
-};
-
-extern template class EXPORT_TEMPLATE_DECLARE(CONTENT_EXPORT)
-    PressureServiceForWorker<DedicatedWorkerHost>;
-extern template class EXPORT_TEMPLATE_DECLARE(CONTENT_EXPORT)
-    PressureServiceForWorker<SharedWorkerHost>;
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_COMPUTE_PRESSURE_PRESSURE_SERVICE_FOR_WORKER_H_
diff --git a/content/browser/preloading/prerender/prerender_host_registry.h b/content/browser/preloading/prerender/prerender_host_registry.h
index f929f06..13e2d13 100644
--- a/content/browser/preloading/prerender/prerender_host_registry.h
+++ b/content/browser/preloading/prerender/prerender_host_registry.h
@@ -21,6 +21,7 @@
 #include "base/types/pass_key.h"
 #include "content/browser/preloading/preloading_confidence.h"
 #include "content/browser/preloading/prerender/prerender_final_status.h"
+#include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/content_export.h"
 #include "content/common/frame.mojom-forward.h"
 #include "content/public/browser/preloading.h"
diff --git a/content/browser/renderer_host/input/input_device_change_observer.cc b/content/browser/renderer_host/input/input_device_change_observer.cc
deleted file mode 100644
index 03bcddb..0000000
--- a/content/browser/renderer_host/input/input_device_change_observer.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/renderer_host/input/input_device_change_observer.h"
-#include "base/trace_event/trace_event.h"
-#include "build/build_config.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-
-#if BUILDFLAG(IS_WIN)
-#include "ui/events/devices/input_device_observer_win.h"
-#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-#include "ui/events/devices/device_data_manager.h"
-#elif BUILDFLAG(IS_ANDROID)
-#include "ui/events/devices/input_device_observer_android.h"
-#endif
-
-namespace content {
-
-InputDeviceChangeObserver::InputDeviceChangeObserver(RenderViewHostImpl* rvhi) {
-  render_view_host_impl_ = rvhi;
-#if BUILDFLAG(IS_WIN)
-  ui::InputDeviceObserverWin::GetInstance()->AddObserver(this);
-#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-  ui::DeviceDataManager::GetInstance()->AddObserver(this);
-#elif BUILDFLAG(IS_ANDROID)
-  ui::InputDeviceObserverAndroid::GetInstance()->AddObserver(this);
-#endif
-}
-
-InputDeviceChangeObserver::~InputDeviceChangeObserver() {
-#if BUILDFLAG(IS_WIN)
-  ui::InputDeviceObserverWin::GetInstance()->RemoveObserver(this);
-#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-  ui::DeviceDataManager::GetInstance()->RemoveObserver(this);
-#elif BUILDFLAG(IS_ANDROID)
-  ui::InputDeviceObserverAndroid::GetInstance()->RemoveObserver(this);
-#endif
-  render_view_host_impl_ = nullptr;
-}
-
-void InputDeviceChangeObserver::OnInputDeviceConfigurationChanged(uint8_t) {
-  TRACE_EVENT0("input",
-               "InputDeviceChangeObserver::OnInputDeviceConfigurationChanged");
-  render_view_host_impl_->OnHardwareConfigurationChanged();
-}
-
-}  // namespace content
diff --git a/content/browser/renderer_host/input/input_device_change_observer.h b/content/browser/renderer_host/input/input_device_change_observer.h
deleted file mode 100644
index f1856329..0000000
--- a/content/browser/renderer_host/input/input_device_change_observer.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_DEVICE_CHANGE_OBSERVER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_DEVICE_CHANGE_OBSERVER_H_
-
-#include "base/memory/raw_ptr.h"
-#include "ui/events/devices/input_device_event_observer.h"
-
-namespace content {
-class RenderViewHostImpl;
-
-// This class monitors input changes on all platforms.
-//
-// It is responsible to instantiate the various platforms observers
-// and it gets notified whenever the input capabilities change. Whenever
-// a change is detected the WebKit preferences are getting updated so the
-// interactions media-queries can be updated.
-class InputDeviceChangeObserver : public ui::InputDeviceEventObserver {
- public:
-  InputDeviceChangeObserver(RenderViewHostImpl* rvh);
-
-  InputDeviceChangeObserver(const InputDeviceChangeObserver&) = delete;
-  InputDeviceChangeObserver& operator=(const InputDeviceChangeObserver&) =
-      delete;
-
-  ~InputDeviceChangeObserver() override;
-
-  // InputDeviceEventObserver public overrides.
-  void OnInputDeviceConfigurationChanged(uint8_t input_device_types) override;
-
- private:
-  raw_ptr<RenderViewHostImpl> render_view_host_impl_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_DEVICE_CHANGE_OBSERVER_H_
diff --git a/content/browser/renderer_host/input/interaction_mq_dynamic_browsertest.cc b/content/browser/renderer_host/input/interaction_mq_dynamic_browsertest.cc
index 891b7dca..1633f49 100644
--- a/content/browser/renderer_host/input/interaction_mq_dynamic_browsertest.cc
+++ b/content/browser/renderer_host/input/interaction_mq_dynamic_browsertest.cc
@@ -4,7 +4,7 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/web_contents/slow_web_preference_cache.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -31,12 +31,9 @@
     (BUILDFLAG(IS_ANDROID) && !defined(ADDRESS_SANITIZER))
 IN_PROC_BROWSER_TEST_F(InteractionMediaQueriesDynamicTest,
                        PointerMediaQueriesDynamic) {
-  RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>(
-      shell()->web_contents()->GetPrimaryMainFrame()->GetRenderViewHost());
-
   ui::SetAvailablePointerAndHoverTypesForTesting(ui::POINTER_TYPE_NONE,
                                                  ui::HOVER_TYPE_NONE);
-  rvhi->OnHardwareConfigurationChanged();
+  SlowWebPreferenceCache::GetInstance()->OnInputDeviceConfigurationChanged(0);
 
   GURL test_url = GetTestUrl("", "interaction-mq-dynamic.html");
   const std::u16string kSuccessTitle(u"SUCCESS");
@@ -45,7 +42,7 @@
 
   ui::SetAvailablePointerAndHoverTypesForTesting(ui::POINTER_TYPE_COARSE,
                                                  ui::HOVER_TYPE_HOVER);
-  rvhi->OnHardwareConfigurationChanged();
+  SlowWebPreferenceCache::GetInstance()->OnInputDeviceConfigurationChanged(0);
   EXPECT_EQ(kSuccessTitle, title_watcher.WaitAndGetTitle());
 }
 #endif
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 40b4ae58..ddc36fb4 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -2444,15 +2444,11 @@
   DCHECK(IsBackForwardCacheEnabled());
   DCHECK(IsInPrimaryMainFrame());
 
-  if (base::FeatureList::IsEnabled(
-          features::kInvalidateLocalSurfaceIdPreCommit)) {
-    // Notifies the View that the page is stored in the `BackForwardCache`.
-    //
-    // We shouldn't BFCache a renderer without a View.
-    CHECK(GetView());
-    static_cast<RenderWidgetHostViewBase*>(GetView())
-        ->DidEnterBackForwardCache();
-  }
+  // Notifies the View that the page is stored in the `BackForwardCache`.
+  //
+  // We shouldn't BFCache a renderer without a View.
+  CHECK(GetView());
+  static_cast<RenderWidgetHostViewBase*>(GetView())->DidEnterBackForwardCache();
 
   // Cancel loading memory tracker if it hasn't already recorded loading
   // memory stats, as we would now be including stats from the navigation
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index dc056cf..d4333c8 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -8308,8 +8308,6 @@
     enabled_features.push_back(
         {blink::features::kViewTransitionOnNavigation, {{}}});
     enabled_features.push_back({blink::features::kPageSwapEvent, {{}}});
-    enabled_features.push_back(
-        {features::kInvalidateLocalSurfaceIdPreCommit, {{}}});
     scoped_feature_list_.InitWithFeaturesAndParameters(
         enabled_features,
         GetDefaultDisabledBackForwardCacheFeaturesForTesting());
diff --git a/content/browser/renderer_host/render_view_host_delegate.h b/content/browser/renderer_host/render_view_host_delegate.h
index fd7d7ea..d989a50 100644
--- a/content/browser/renderer_host/render_view_host_delegate.h
+++ b/content/browser/renderer_host/render_view_host_delegate.h
@@ -105,14 +105,6 @@
   // associated with this RenderViewHost.
   virtual blink::ColorProviderColorMaps GetColorProviderColorMaps() const = 0;
 
-  // Triggers a total recomputation of WebPreferences by resetting the current
-  // cached WebPreferences to null and triggering the recomputation path for
-  // both the "slow" attributes (hardware configurations/things that require
-  // slow platform/device polling) which normally won't get recomputed after
-  // the first time we set it and "fast" attributes (which always gets
-  // recomputed).
-  virtual void RecomputeWebPreferencesSlow() {}
-
   // Returns true if the render view is rendering a guest.
   virtual bool IsGuest();
 
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index f130879c..52ce9d2 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -103,7 +103,6 @@
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/native_widget_types.h"
-#include "ui/gl/gpu_switching_manager.h"
 #include "ui/native_theme/native_theme_features.h"
 #include "url/url_constants.h"
 
@@ -348,7 +347,6 @@
   GetAgentSchedulingGroup().AddRoute(routing_id_, this);
 
   GetProcess()->AddObserver(this);
-  ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
 
   // New views may be created during RenderProcessHost::ProcessDied(), within a
   // brief window where the internal ChannelProxy is null. This ensures that the
@@ -361,9 +359,6 @@
   if (!is_active())
     GetWidget()->UpdatePriority();
 
-  input_device_change_observer_ =
-      std::make_unique<InputDeviceChangeObserver>(this);
-
   bool initially_hidden = frame_tree_->delegate()->IsHidden();
   page_lifecycle_state_manager_ = std::make_unique<PageLifecycleStateManager>(
       this, initially_hidden ? blink::mojom::PageVisibilityState::kHidden
@@ -383,8 +378,6 @@
   // Destroy the RenderWidgetHost.
   GetWidget()->ShutdownAndDestroyWidget(false);
 
-  ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this);
-
   // Detach the routing ID as the object is going away.
   GetAgentSchedulingGroup().RemoveRoute(GetRoutingID());
   g_routing_id_view_map.Get().erase(
@@ -918,10 +911,6 @@
   }
 }
 
-void RenderViewHostImpl::OnHardwareConfigurationChanged() {
-  delegate_->RecomputeWebPreferencesSlow();
-}
-
 void RenderViewHostImpl::EnablePreferredSizeMode() {
   if (is_active()) {
     GetMainRenderFrameHost()
@@ -935,10 +924,6 @@
       &RenderViewHostImpl::RenderViewReady, weak_factory_.GetWeakPtr()));
 }
 
-void RenderViewHostImpl::OnGpuSwitched(gl::GpuPreference active_gpu_heuristic) {
-  OnHardwareConfigurationChanged();
-}
-
 void RenderViewHostImpl::RenderViewReady() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   delegate_->RenderViewReady(this);
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h
index fe60e69..961e557 100644
--- a/content/browser/renderer_host/render_view_host_impl.h
+++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -24,7 +24,6 @@
 #include "build/build_config.h"
 #include "content/browser/renderer_host/browsing_context_state.h"
 #include "content/browser/renderer_host/frame_tree.h"
-#include "content/browser/renderer_host/input/input_device_change_observer.h"
 #include "content/browser/renderer_host/page_lifecycle_state_manager.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_owner_delegate.h"
@@ -44,8 +43,6 @@
 #include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/window_open_disposition.h"
-#include "ui/gl/gpu_preference.h"
-#include "ui/gl/gpu_switching_observer.h"
 
 namespace blink {
 namespace web_pref {
@@ -97,7 +94,6 @@
     : public RenderViewHost,
       public RenderWidgetHostOwnerDelegate,
       public RenderProcessHostObserver,
-      public ui::GpuSwitchingObserver,
       public IPC::Listener,
       public base::RefCounted<RenderViewHostImpl> {
  public:
@@ -147,9 +143,6 @@
   void RenderProcessExited(RenderProcessHost* host,
                            const ChildProcessTerminationInfo& info) override;
 
-  // GpuSwitchingObserver implementation.
-  void OnGpuSwitched(gl::GpuPreference active_gpu_heuristic) override;
-
   // Set up the `blink::WebView` child process. Virtual because it is overridden
   // by TestRenderViewHost.
   // `opener_route_id` parameter indicates which `blink::WebView` created this
@@ -237,12 +230,6 @@
   // re-entrantly.
   void PostRenderViewReady();
 
-  // Passes current web preferences to the renderer after recomputing all of
-  // them, including the slow-to-compute hardware preferences.
-  // (WebContents::OnWebPreferencesChanged is a faster alternate that avoids
-  // slow recomputations.)
-  void OnHardwareConfigurationChanged();
-
   // Sets the routing id for the main frame. When set to MSG_ROUTING_NONE, the
   // view is not considered active.
   void SetMainFrameRoutingId(int routing_id);
@@ -418,9 +405,6 @@
 
   std::optional<mojom::ViewWidgetType> view_widget_type_;
 
-  // This monitors input changes so they can be reflected to the interaction MQ.
-  std::unique_ptr<InputDeviceChangeObserver> input_device_change_observer_;
-
   // This controls the lifecycle change and notify the renderer.
   std::unique_ptr<PageLifecycleStateManager> page_lifecycle_state_manager_;
 
diff --git a/content/browser/renderer_host/render_widget_host_view_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_browsertest.cc
index 9b90c88..ba00133 100644
--- a/content/browser/renderer_host/render_widget_host_view_browsertest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_browsertest.cc
@@ -604,10 +604,6 @@
 
 IN_PROC_BROWSER_TEST_F(BFCachedRenderWidgetHostViewBrowserTest,
                        BFCacheRestoredPageHasNewLocalSurfaceId) {
-  if (!base::FeatureList::IsEnabled(
-          features::kInvalidateLocalSurfaceIdPreCommit)) {
-    return;
-  }
   ASSERT_TRUE(embedded_test_server()->Start());
   ASSERT_TRUE(
       NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
@@ -673,10 +669,6 @@
 IN_PROC_BROWSER_TEST_F(
     BFCachedRenderWidgetHostViewBrowserTest,
     BFCachedPageResizedWhileHiddenShouldNotHavePreservedFallback) {
-  if (!base::FeatureList::IsEnabled(
-          features::kInvalidateLocalSurfaceIdPreCommit)) {
-    return;
-  }
   ASSERT_TRUE(embedded_test_server()->Start());
   ASSERT_TRUE(
       NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
@@ -738,10 +730,6 @@
 // Same as above, except that the resize operation is a no-op.
 IN_PROC_BROWSER_TEST_F(BFCachedRenderWidgetHostViewBrowserTest,
                        BFCachedPageNoopResizedWhileHiddenHasPreservedFallback) {
-  if (!base::FeatureList::IsEnabled(
-          features::kInvalidateLocalSurfaceIdPreCommit)) {
-    return;
-  }
   ASSERT_TRUE(embedded_test_server()->Start());
   ASSERT_TRUE(
       NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
@@ -809,10 +797,6 @@
 
 IN_PROC_BROWSER_TEST_F(BFCachedRenderWidgetHostViewBrowserTest,
                        BFCachedViewShouldNotBeEvicted) {
-  if (!base::FeatureList::IsEnabled(
-          features::kInvalidateLocalSurfaceIdPreCommit)) {
-    return;
-  }
   ASSERT_TRUE(embedded_test_server()->Start());
   ASSERT_TRUE(
       NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
@@ -1950,11 +1934,6 @@
     command_line->AppendSwitch(switches::kSitePerProcess);
 
     std::vector<base::test::FeatureRefAndParams> enabled_features;
-    base::test::FeatureRefAndParams must_enable(
-        /*feature=*/features::kInvalidateLocalSurfaceIdPreCommit,
-        /*params=*/std::map<std::string, std::string>());
-    enabled_features.push_back(std::move(must_enable));
-
     bool bfcache_enabled = GetParam();
     if (bfcache_enabled) {
       scoped_feature_list_.InitWithFeaturesAndParameters(
diff --git a/content/browser/renderer_host/render_widget_host_view_ios.mm b/content/browser/renderer_host/render_widget_host_view_ios.mm
index 54afc75..9e40579d 100644
--- a/content/browser/renderer_host/render_widget_host_view_ios.mm
+++ b/content/browser/renderer_host/render_widget_host_view_ios.mm
@@ -411,11 +411,8 @@
 }
 
 void RenderWidgetHostViewIOS::OnOldViewDidNavigatePreCommit() {
-  if (base::FeatureList::IsEnabled(
-          features::kInvalidateLocalSurfaceIdPreCommit)) {
-    CHECK(browser_compositor_) << "Shouldn't be called during destruction!";
-    browser_compositor_->DidNavigateMainFramePreCommit();
-  }
+  CHECK(browser_compositor_) << "Shouldn't be called during destruction!";
+  browser_compositor_->DidNavigateMainFramePreCommit();
 }
 
 void RenderWidgetHostViewIOS::OnNewViewDidNavigatePostCommit() {
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index 4a6a4da..c6512e8 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -622,11 +622,8 @@
 }
 
 void RenderWidgetHostViewMac::OnOldViewDidNavigatePreCommit() {
-  if (base::FeatureList::IsEnabled(
-          features::kInvalidateLocalSurfaceIdPreCommit)) {
-    CHECK(browser_compositor_) << "Shouldn't be called during destruction!";
-    browser_compositor_->DidNavigateMainFramePreCommit();
-  }
+  CHECK(browser_compositor_) << "Shouldn't be called during destruction!";
+  browser_compositor_->DidNavigateMainFramePreCommit();
 }
 
 void RenderWidgetHostViewMac::OnNewViewDidNavigatePostCommit() {
diff --git a/content/browser/web_contents/slow_web_preference_cache.cc b/content/browser/web_contents/slow_web_preference_cache.cc
new file mode 100644
index 0000000..30dfa0b
--- /dev/null
+++ b/content/browser/web_contents/slow_web_preference_cache.cc
@@ -0,0 +1,238 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/web_contents/slow_web_preference_cache.h"
+
+#include "base/command_line.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/no_destructor.h"
+#include "base/system/sys_info.h"
+#include "base/trace_event/optional_trace_event.h"
+#include "content/public/common/content_switches.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
+#include "ui/base/pointer/pointer_device.h"
+#include "ui/gl/gpu_switching_manager.h"
+
+#if BUILDFLAG(IS_WIN)
+#include "ui/events/devices/input_device_observer_win.h"
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#include "ui/events/devices/device_data_manager.h"
+#elif BUILDFLAG(IS_ANDROID)
+#include "ui/base/device_form_factor.h"
+#include "ui/events/devices/input_device_observer_android.h"
+#endif
+
+namespace content {
+
+namespace {
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class AllPointerTypes {
+  kNone = 0,
+  kCoarse = 1,
+  kFine = 2,
+  kBoth = 3,
+  kMaxValue = kBoth
+};
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class PrimaryPointerType {
+  kNone = 0,
+  kCoarse = 1,
+  kFine = 2,
+  kMaxValue = kFine
+};
+}  // namespace
+
+SlowWebPreferenceCacheObserver::~SlowWebPreferenceCacheObserver() = default;
+
+SlowWebPreferenceCache::SlowWebPreferenceCache() {
+  gpu_switch_observation_.Observe(ui::GpuSwitchingManager::GetInstance());
+
+#if BUILDFLAG(IS_WIN)
+  ui::InputDeviceObserverWin::GetInstance()->AddObserver(this);
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+  ui::DeviceDataManager::GetInstance()->AddObserver(this);
+#elif BUILDFLAG(IS_ANDROID)
+  ui::InputDeviceObserverAndroid::GetInstance()->AddObserver(this);
+#endif
+}
+
+SlowWebPreferenceCache::~SlowWebPreferenceCache() {
+#if BUILDFLAG(IS_WIN)
+  ui::InputDeviceObserverWin::GetInstance()->RemoveObserver(this);
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+  ui::DeviceDataManager::GetInstance()->RemoveObserver(this);
+#elif BUILDFLAG(IS_ANDROID)
+  ui::InputDeviceObserverAndroid::GetInstance()->RemoveObserver(this);
+#endif
+}
+
+// static
+SlowWebPreferenceCache* SlowWebPreferenceCache::GetInstance() {
+  static base::NoDestructor<SlowWebPreferenceCache> instance;
+  return instance.get();
+}
+
+void SlowWebPreferenceCache::AddObserver(
+    SlowWebPreferenceCacheObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void SlowWebPreferenceCache::RemoveObserver(
+    SlowWebPreferenceCacheObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void SlowWebPreferenceCache::Load(blink::web_pref::WebPreferences* prefs) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!is_initialized_) {
+    Update();
+  }
+
+#define SET_FROM_CACHE(prefs, field) prefs->field = field##_
+  SET_FROM_CACHE(prefs, touch_event_feature_detection_enabled);
+  SET_FROM_CACHE(prefs, available_pointer_types);
+  SET_FROM_CACHE(prefs, available_hover_types);
+  SET_FROM_CACHE(prefs, primary_pointer_type);
+  SET_FROM_CACHE(prefs, primary_hover_type);
+  SET_FROM_CACHE(prefs, pointer_events_max_touch_points);
+  SET_FROM_CACHE(prefs, number_of_cpu_cores);
+
+#if BUILDFLAG(IS_ANDROID)
+  SET_FROM_CACHE(prefs, video_fullscreen_orientation_lock_enabled);
+  SET_FROM_CACHE(prefs, video_rotate_to_fullscreen_enabled);
+#endif
+
+#undef SET_FROM_CACHE
+}
+
+void SlowWebPreferenceCache::OnInputDeviceConfigurationChanged(uint8_t) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (Update()) {
+    observers_.Notify(
+        &SlowWebPreferenceCacheObserver::OnSlowWebPreferenceChanged);
+  }
+}
+
+void SlowWebPreferenceCache::OnGpuSwitched(gl::GpuPreference) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (Update()) {
+    observers_.Notify(
+        &SlowWebPreferenceCacheObserver::OnSlowWebPreferenceChanged);
+  }
+}
+
+bool SlowWebPreferenceCache::Update() {
+  OPTIONAL_TRACE_EVENT0("content", "SlowWebPreferenceCache::Update");
+  bool prev_touch_event_feature_detection_enabled =
+      touch_event_feature_detection_enabled_;
+  int prev_available_pointer_types = available_pointer_types_;
+  int prev_available_hover_types = available_hover_types_;
+  blink::mojom::PointerType prev_primary_pointer_type = primary_pointer_type_;
+  blink::mojom::HoverType prev_primary_hover_type = primary_hover_type_;
+  int prev_pointer_events_max_touch_points = pointer_events_max_touch_points_;
+  int prev_number_of_cpu_cores = number_of_cpu_cores_;
+#if BUILDFLAG(IS_ANDROID)
+  bool prev_video_fullscreen_orientation_lock_enabled =
+      video_fullscreen_orientation_lock_enabled_;
+  bool prev_video_rotate_to_fullscreen_enabled =
+      video_rotate_to_fullscreen_enabled_;
+#endif  // BUILDFLAG(IS_ANDROID)
+
+  is_initialized_ = true;
+
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+
+  // On Android, Touch event feature detection is enabled by default,
+  // Otherwise default is disabled.
+  std::string touch_enabled_default_switch =
+      switches::kTouchEventFeatureDetectionDisabled;
+#if BUILDFLAG(IS_ANDROID)
+  touch_enabled_default_switch = switches::kTouchEventFeatureDetectionEnabled;
+#endif  // BUILDFLAG(IS_ANDROID)
+  const std::string touch_enabled_switch =
+      command_line.HasSwitch(switches::kTouchEventFeatureDetection)
+          ? command_line.GetSwitchValueASCII(
+                switches::kTouchEventFeatureDetection)
+          : touch_enabled_default_switch;
+
+  touch_event_feature_detection_enabled_ =
+      (touch_enabled_switch == switches::kTouchEventFeatureDetectionAuto)
+          ? (ui::GetTouchScreensAvailability() ==
+             ui::TouchScreensAvailability::ENABLED)
+          : (touch_enabled_switch.empty() ||
+             touch_enabled_switch ==
+                 switches::kTouchEventFeatureDetectionEnabled);
+
+  std::tie(available_pointer_types_, available_hover_types_) =
+      GetAvailablePointerAndHoverTypes();
+  primary_pointer_type_ = static_cast<blink::mojom::PointerType>(
+      ui::GetPrimaryPointerType(available_pointer_types_));
+  primary_hover_type_ = static_cast<blink::mojom::HoverType>(
+      ui::GetPrimaryHoverType(available_hover_types_));
+
+  pointer_events_max_touch_points_ = ui::MaxTouchPoints();
+
+  number_of_cpu_cores_ = base::SysInfo::NumberOfProcessors();
+
+  AllPointerTypes all_pointer_types = AllPointerTypes::kNone;
+  if (available_pointer_types_ & ui::POINTER_TYPE_COARSE &&
+      available_pointer_types_ & ui::POINTER_TYPE_FINE) {
+    all_pointer_types = AllPointerTypes::kBoth;
+  } else if (available_pointer_types_ & ui::POINTER_TYPE_COARSE) {
+    all_pointer_types = AllPointerTypes::kCoarse;
+  } else if (available_pointer_types_ & ui::POINTER_TYPE_FINE) {
+    all_pointer_types = AllPointerTypes::kFine;
+  }
+  PrimaryPointerType primary_pointer = PrimaryPointerType::kNone;
+  if (primary_pointer_type_ == blink::mojom::PointerType::kPointerCoarseType) {
+    primary_pointer = PrimaryPointerType::kCoarse;
+  } else if (primary_pointer_type_ ==
+             blink::mojom::PointerType::kPointerFineType) {
+    primary_pointer = PrimaryPointerType::kFine;
+  }
+  base::UmaHistogramEnumeration("Input.PointerTypesAll", all_pointer_types);
+  base::UmaHistogramEnumeration("Input.PointerTypePrimary", primary_pointer);
+
+#if BUILDFLAG(IS_ANDROID)
+  const bool device_is_phone =
+      ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_PHONE;
+  video_fullscreen_orientation_lock_enabled_ = device_is_phone;
+  video_rotate_to_fullscreen_enabled_ = device_is_phone;
+  if (video_fullscreen_orientation_lock_enabled_ !=
+          prev_video_fullscreen_orientation_lock_enabled ||
+      video_rotate_to_fullscreen_enabled_ !=
+          prev_video_rotate_to_fullscreen_enabled) {
+    return true;
+  }
+#endif
+
+  return touch_event_feature_detection_enabled_ !=
+             prev_touch_event_feature_detection_enabled ||
+         available_pointer_types_ != prev_available_pointer_types ||
+         available_hover_types_ != prev_available_hover_types ||
+         primary_pointer_type_ != prev_primary_pointer_type ||
+         primary_hover_type_ != prev_primary_hover_type ||
+         pointer_events_max_touch_points_ !=
+             prev_pointer_events_max_touch_points ||
+         number_of_cpu_cores_ != prev_number_of_cpu_cores;
+}
+
+// static
+std::pair<int, int> SlowWebPreferenceCache::GetAvailablePointerAndHoverTypes() {
+  // On Windows we have to temporarily allow blocking calls since
+  // ui::GetAvailablePointerAndHoverTypes needs to call some in order to
+  // figure out tablet device details in base::win::IsDeviceUsedAsATablet,
+  // see https://crbug.com/1262162.
+#if BUILDFLAG(IS_WIN)
+  base::ScopedAllowBlocking scoped_allow_blocking;
+#endif
+  return ui::GetAvailablePointerAndHoverTypes();
+}
+
+}  // namespace content
diff --git a/content/browser/web_contents/slow_web_preference_cache.h b/content/browser/web_contents/slow_web_preference_cache.h
new file mode 100644
index 0000000..206fe48
--- /dev/null
+++ b/content/browser/web_contents/slow_web_preference_cache.h
@@ -0,0 +1,90 @@
+// 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 CONTENT_BROWSER_WEB_CONTENTS_SLOW_WEB_PREFERENCE_CACHE_H_
+#define CONTENT_BROWSER_WEB_CONTENTS_SLOW_WEB_PREFERENCE_CACHE_H_
+
+#include "base/no_destructor.h"
+#include "base/observer_list.h"
+#include "base/scoped_observation.h"
+#include "base/sequence_checker.h"
+#include "content/common/content_export.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
+#include "ui/events/devices/input_device_event_observer.h"
+#include "ui/gl/gpu_switching_manager.h"
+#include "ui/gl/gpu_switching_observer.h"
+
+namespace content {
+
+class CONTENT_EXPORT SlowWebPreferenceCacheObserver
+    : public base::CheckedObserver {
+ public:
+  virtual void OnSlowWebPreferenceChanged() {}
+  ~SlowWebPreferenceCacheObserver() override;
+};
+
+// This is the cache of "slow" attributes (hardware configurations/things that
+// require slow platform/device polling) of WebPreferences which normally won't
+// get recomputed after the first time we set it , as opposed to "fast"
+// attributes (which always gets recomputed). This cache is shared globally
+// among multiple WebContents.
+//
+// Each WebContents monitors the change on slow attributes via
+// SlowWebPreferenceCacheObserver.
+//
+// This is not thread safe. This is expected to be accessed on the main thread.
+class CONTENT_EXPORT SlowWebPreferenceCache
+    : public ui::InputDeviceEventObserver,
+      public ui::GpuSwitchingObserver {
+ public:
+  SlowWebPreferenceCache(const SlowWebPreferenceCache&) = delete;
+  SlowWebPreferenceCache& operator=(const SlowWebPreferenceCache&) = delete;
+
+  static SlowWebPreferenceCache* GetInstance();
+  void AddObserver(SlowWebPreferenceCacheObserver* observer);
+  void RemoveObserver(SlowWebPreferenceCacheObserver* observer);
+  void Load(blink::web_pref::WebPreferences* prefs);
+
+  // InputDeviceEventObserver implementation
+  void OnInputDeviceConfigurationChanged(uint8_t) override;
+  // GpuSwitchingObserver implementation
+  void OnGpuSwitched(gl::GpuPreference) override;
+
+ private:
+  friend base::NoDestructor<SlowWebPreferenceCache>;
+  SlowWebPreferenceCache();
+  ~SlowWebPreferenceCache() override;
+  // Updates the all slow attributes and returns whether any attribute is
+  // changed or not.
+  bool Update();
+  // Wrapper for ui::GetAvailablePointerAndHoverTypes which temporarily allows
+  // blocking calls required on Windows when running on touch enabled devices.
+  static std::pair<int, int> GetAvailablePointerAndHoverTypes();
+
+  bool is_initialized_ = false;
+  base::ObserverList<SlowWebPreferenceCacheObserver> observers_;
+  base::ScopedObservation<ui::GpuSwitchingManager, ui::GpuSwitchingObserver>
+      gpu_switch_observation_{this};
+
+  bool touch_event_feature_detection_enabled_ = false;
+  int available_pointer_types_ = 0;
+  int available_hover_types_ = 0;
+  blink::mojom::PointerType primary_pointer_type_ =
+      blink::mojom::PointerType::kPointerNone;
+  blink::mojom::HoverType primary_hover_type_ =
+      blink::mojom::HoverType::kHoverNone;
+  int pointer_events_max_touch_points_ = 0;
+  int number_of_cpu_cores_ = 1;
+
+#if BUILDFLAG(IS_ANDROID)
+  bool video_fullscreen_orientation_lock_enabled_ = false;
+  bool video_rotate_to_fullscreen_enabled_ = false;
+#endif  // BUILDFLAG(IS_ANDROID)
+
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_WEB_CONTENTS_SLOW_WEB_PREFERENCE_CACHE_H_
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index a8bb15f5..17cbdf41 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -43,7 +43,6 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/to_string.h"
-#include "base/system/sys_info.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "base/timer/elapsed_timer.h"
@@ -120,6 +119,7 @@
 #include "content/browser/site_instance_impl.h"
 #include "content/browser/wake_lock/wake_lock_context_host.h"
 #include "content/browser/web_contents/java_script_dialog_commit_deferring_condition.h"
+#include "content/browser/web_contents/slow_web_preference_cache.h"
 #include "content/browser/web_contents/web_contents_view.h"
 #include "content/browser/web_contents/web_contents_view_child_frame.h"
 #include "content/browser/webui/web_ui_controller_factory_registry.h"
@@ -254,25 +254,6 @@
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
-enum class AllPointerTypes {
-  kNone = 0,
-  kCoarse = 1,
-  kFine = 2,
-  kBoth = 3,
-  kMaxValue = kBoth
-};
-
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class PrimaryPointerType {
-  kNone = 0,
-  kCoarse = 1,
-  kFine = 2,
-  kMaxValue = kFine
-};
-
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
 //
 // LINT.IfChange(CrashRepHandlingOutcome)
 enum class CrashRepHandlingOutcome {
@@ -1306,6 +1287,8 @@
 
   ui::NativeTheme* native_theme = ui::NativeTheme::GetInstanceForWeb();
   native_theme_observation_.Observe(native_theme);
+  slow_web_preference_cache_observation_.Observe(
+      SlowWebPreferenceCache::GetInstance());
   using_dark_colors_ = native_theme->ShouldUseDarkColors();
   in_forced_colors_ = native_theme->InForcedColorsMode();
   preferred_color_scheme_ = native_theme->GetPreferredColorScheme();
@@ -3240,11 +3223,13 @@
 
   blink::web_pref::WebPreferences prefs;
 
+  // Sets the hardware-related fields in |prefs| that are slow to compute. The
+  // fields are set from cache if available, otherwise recomputed.
+  SlowWebPreferenceCache::GetInstance()->Load(&prefs);
+
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
 
-  SetSlowWebPreferences(command_line, &prefs);
-
   prefs.web_security_enabled =
       !command_line.HasSwitch(switches::kDisableWebSecurity);
 
@@ -3418,93 +3403,6 @@
   return prefs;
 }
 
-void WebContentsImpl::SetSlowWebPreferences(
-    const base::CommandLine& command_line,
-    blink::web_pref::WebPreferences* prefs) {
-  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SetSlowWebPreferences");
-
-  if (web_preferences_.get()) {
-#define SET_FROM_CACHE(prefs, field) prefs->field = web_preferences_->field
-
-    SET_FROM_CACHE(prefs, touch_event_feature_detection_enabled);
-    SET_FROM_CACHE(prefs, available_pointer_types);
-    SET_FROM_CACHE(prefs, available_hover_types);
-    SET_FROM_CACHE(prefs, primary_pointer_type);
-    SET_FROM_CACHE(prefs, primary_hover_type);
-    SET_FROM_CACHE(prefs, pointer_events_max_touch_points);
-    SET_FROM_CACHE(prefs, number_of_cpu_cores);
-
-#if BUILDFLAG(IS_ANDROID)
-    SET_FROM_CACHE(prefs, video_fullscreen_orientation_lock_enabled);
-    SET_FROM_CACHE(prefs, video_rotate_to_fullscreen_enabled);
-#endif
-
-#undef SET_FROM_CACHE
-  } else {
-    // Every prefs->field modified below should have a SET_FROM_CACHE entry
-    // above.
-
-    // On Android, Touch event feature detection is enabled by default,
-    // Otherwise default is disabled.
-    std::string touch_enabled_default_switch =
-        switches::kTouchEventFeatureDetectionDisabled;
-#if BUILDFLAG(IS_ANDROID)
-    touch_enabled_default_switch = switches::kTouchEventFeatureDetectionEnabled;
-#endif  // BUILDFLAG(IS_ANDROID)
-    const std::string touch_enabled_switch =
-        command_line.HasSwitch(switches::kTouchEventFeatureDetection)
-            ? command_line.GetSwitchValueASCII(
-                  switches::kTouchEventFeatureDetection)
-            : touch_enabled_default_switch;
-
-    prefs->touch_event_feature_detection_enabled =
-        (touch_enabled_switch == switches::kTouchEventFeatureDetectionAuto)
-            ? (ui::GetTouchScreensAvailability() ==
-               ui::TouchScreensAvailability::ENABLED)
-            : (touch_enabled_switch.empty() ||
-               touch_enabled_switch ==
-                   switches::kTouchEventFeatureDetectionEnabled);
-
-    std::tie(prefs->available_pointer_types, prefs->available_hover_types) =
-        GetAvailablePointerAndHoverTypes();
-    prefs->primary_pointer_type = static_cast<blink::mojom::PointerType>(
-        ui::GetPrimaryPointerType(prefs->available_pointer_types));
-    prefs->primary_hover_type = static_cast<blink::mojom::HoverType>(
-        ui::GetPrimaryHoverType(prefs->available_hover_types));
-
-    prefs->pointer_events_max_touch_points = ui::MaxTouchPoints();
-
-    prefs->number_of_cpu_cores = base::SysInfo::NumberOfProcessors();
-
-    AllPointerTypes all_pointer_types = AllPointerTypes::kNone;
-    if (prefs->available_pointer_types & ui::POINTER_TYPE_COARSE &&
-        prefs->available_pointer_types & ui::POINTER_TYPE_FINE) {
-      all_pointer_types = AllPointerTypes::kBoth;
-    } else if (prefs->available_pointer_types & ui::POINTER_TYPE_COARSE) {
-      all_pointer_types = AllPointerTypes::kCoarse;
-    } else if (prefs->available_pointer_types & ui::POINTER_TYPE_FINE) {
-      all_pointer_types = AllPointerTypes::kFine;
-    }
-    PrimaryPointerType primary_pointer = PrimaryPointerType::kNone;
-    if (prefs->primary_pointer_type ==
-        blink::mojom::PointerType::kPointerCoarseType) {
-      primary_pointer = PrimaryPointerType::kCoarse;
-    } else if (prefs->primary_pointer_type ==
-               blink::mojom::PointerType::kPointerFineType) {
-      primary_pointer = PrimaryPointerType::kFine;
-    }
-    base::UmaHistogramEnumeration("Input.PointerTypesAll", all_pointer_types);
-    base::UmaHistogramEnumeration("Input.PointerTypePrimary", primary_pointer);
-
-#if BUILDFLAG(IS_ANDROID)
-    const bool device_is_phone =
-        ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_PHONE;
-    prefs->video_fullscreen_orientation_lock_enabled = device_is_phone;
-    prefs->video_rotate_to_fullscreen_enabled = device_is_phone;
-#endif
-  }
-}
-
 void WebContentsImpl::OnWebPreferencesChanged() {
   OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::OnWebPreferencesChanged");
 
@@ -7017,13 +6915,10 @@
     ExitFullscreen(false);
   }
 
-  if (base::FeatureList::IsEnabled(
-          features::kInvalidateLocalSurfaceIdPreCommit)) {
-    auto* rwhvb = static_cast<RenderWidgetHostViewBase*>(
-        frame_tree_node->current_frame_host()->GetView());
-    if (rwhvb) {
-      rwhvb->OnOldViewDidNavigatePreCommit();
-    }
+  auto* rwhvb = static_cast<RenderWidgetHostViewBase*>(
+      frame_tree_node->current_frame_host()->GetView());
+  if (rwhvb) {
+    rwhvb->OnOldViewDidNavigatePreCommit();
   }
 
   // Clean up keyboard lock state when navigating.
@@ -7053,26 +6948,11 @@
       // page.
       ClearTargetURL();
 
-      // If the feature flag is enabled, we only call the PostCommit on the new
-      // View, for a primary main frame navigation. The PreCommit counterpart is
-      // called in `WCImpl:DidNavigateMainFramePreCommit`.
-      if (rwhvb && base::FeatureList::IsEnabled(
-                       features::kInvalidateLocalSurfaceIdPreCommit)) {
+      // Run the post-commit tasks on the new View.
+      if (rwhvb) {
         rwhvb->OnNewViewDidNavigatePostCommit();
       }
     }
-
-    // If the feature flag is disabled, we restore to the "original" behavior (
-    // the behavior prior to https://crrev.com/c/4702023):
-    // When the new view is just made visible, we:
-    // - Reset the LocalSurfaceId on the new View (PreCommit), and
-    // - Cancel the ongoing gestures on the new View (PostCommit), and
-    // - We call the two APIs on all main frames, including non-primary ones.
-    if (rwhvb && !base::FeatureList::IsEnabled(
-                     features::kInvalidateLocalSurfaceIdPreCommit)) {
-      rwhvb->OnOldViewDidNavigatePreCommit();
-      rwhvb->OnNewViewDidNavigatePostCommit();
-    }
   }
 
   PageImpl& page = render_frame_host->GetPage();
@@ -7408,20 +7288,6 @@
       [](RenderViewHostImpl* rvh) { rvh->SendWebPreferencesToRenderer(); });
 }
 
-void WebContentsImpl::RecomputeWebPreferencesSlow() {
-  OPTIONAL_TRACE_EVENT0("content",
-                        "WebContentsImpl::RecomputeWebPreferencesSlow");
-  // OnWebPreferencesChanged is a no-op when this is true.
-  if (updating_web_preferences_) {
-    return;
-  }
-  // Resets |web_preferences_| so that we won't have any cached value for slow
-  // attributes (which won't get recomputed if we have pre-existing values for
-  // them).
-  web_preferences_.reset();
-  OnWebPreferencesChanged();
-}
-
 std::optional<SkColor> WebContentsImpl::GetBaseBackgroundColor() {
   return page_base_background_color_;
 }
@@ -10892,6 +10758,10 @@
   return *color_provider;
 }
 
+void WebContentsImpl::OnSlowWebPreferenceChanged() {
+  OnWebPreferencesChanged();
+}
+
 blink::mojom::FrameWidgetInputHandler*
 WebContentsImpl::GetFocusedFrameWidgetInputHandler() {
   auto* focused_render_widget_host =
@@ -11328,18 +11198,6 @@
       .virtual_keyboard_mode();
 }
 
-// static
-std::pair<int, int> WebContentsImpl::GetAvailablePointerAndHoverTypes() {
-  // On Windows we have to temporarily allow blocking calls since
-  // ui::GetAvailablePointerAndHoverTypes needs to call some in order to
-  // figure out tablet device details in base::win::IsDeviceUsedAsATablet,
-  // see https://crbug.com/1262162.
-#if BUILDFLAG(IS_WIN)
-  base::ScopedAllowBlocking scoped_allow_blocking;
-#endif
-  return ui::GetAvailablePointerAndHoverTypes();
-}
-
 void WebContentsImpl::SetOverscrollNavigationEnabled(bool enabled) {
   GetView()->SetOverscrollControllerEnabled(enabled);
 }
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 188a98a..5dc1ade 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -49,6 +49,7 @@
 #include "content/browser/renderer_host/render_widget_host_delegate.h"
 #include "content/browser/renderer_host/visible_time_request_trigger.h"
 #include "content/browser/web_contents/file_chooser_impl.h"
+#include "content/browser/web_contents/slow_web_preference_cache.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/fullscreen_types.h"
 #include "content/public/browser/global_routing_id.h"
@@ -201,6 +202,7 @@
       public NavigatorDelegate,
       public ui::NativeThemeObserver,
       public ui::ColorProviderSourceObserver,
+      public SlowWebPreferenceCacheObserver,
       public input::RenderWidgetHostInputEventRouter::Delegate {
  public:
   class FriendWrapper;
@@ -951,7 +953,6 @@
   bool IsJavaScriptDialogShowing() const override;
   bool ShouldIgnoreUnresponsiveRenderer() override;
   bool IsGuest() override;
-  void RecomputeWebPreferencesSlow() override;
   std::optional<SkColor> GetBaseBackgroundColor() override;
   void StartPrefetch(const GURL& prefetch_url,
                      bool use_prefetch_proxy,
@@ -1943,6 +1944,9 @@
   // always return a valid ColorProvider instance.
   const ui::ColorProvider& GetColorProvider() const override;
 
+  // implements SlowWebPreferenceCacheObserver
+  void OnSlowWebPreferenceChanged() override;
+
   // Sets the visibility to |new_visibility| and propagates this to the
   // renderer side, taking into account the current capture state. This
   // can be called with the current visibility to affect capturing
@@ -1974,11 +1978,6 @@
       ForEachRenderViewHostTypes view_mask,
       RenderViewHostIterationCallback on_render_view_host);
 
-  // Sets the hardware-related fields in |prefs| that are slow to compute.  The
-  // fields are set from cache if available, otherwise recomputed.
-  void SetSlowWebPreferences(const base::CommandLine& command_line,
-                             blink::web_pref::WebPreferences* prefs);
-
   // This is the actual implementation of the various overloads of
   // |ForEachRenderFrameHost|.
   void ForEachRenderFrameHostImpl(
@@ -2031,10 +2030,6 @@
   // this WebContents (i.e. for WebContents::GetTitle()).
   NavigationEntry* GetNavigationEntryForTitle();
 
-  // Wrapper for ui::GetAvailablePointerAndHoverTypes which temporarily allows
-  // blocking calls required on Windows when running on touch enabled devices.
-  static std::pair<int, int> GetAvailablePointerAndHoverTypes();
-
   // Apply shared logic for SetHasPictureInPictureVideo() and
   // SetHasPictureInPictureDocument().
   void SetHasPictureInPictureCommon(bool has_picture_in_picture);
@@ -2472,6 +2467,10 @@
   base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver>
       native_theme_observation_{this};
 
+  base::ScopedObservation<SlowWebPreferenceCache,
+                          SlowWebPreferenceCacheObserver>
+      slow_web_preference_cache_observation_{this};
+
   bool using_dark_colors_ = false;
   bool in_forced_colors_ = false;
   ui::NativeTheme::PreferredColorScheme preferred_color_scheme_ =
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index 6c105c76..7d4a238 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -861,7 +861,7 @@
 
   if (!pressure_service_) {
     pressure_service_ =
-        std::make_unique<PressureServiceForWorker<DedicatedWorkerHost>>(this);
+        std::make_unique<PressureServiceForDedicatedWorker>(this);
   }
 
   pressure_service_->BindReceiver(std::move(receiver));
diff --git a/content/browser/worker_host/dedicated_worker_host.h b/content/browser/worker_host/dedicated_worker_host.h
index ef5bcbc..7894039 100644
--- a/content/browser/worker_host/dedicated_worker_host.h
+++ b/content/browser/worker_host/dedicated_worker_host.h
@@ -56,7 +56,7 @@
 #endif
 
 #if BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
-#include "content/browser/compute_pressure/pressure_service_for_worker.h"
+#include "content/browser/compute_pressure/pressure_service_for_dedicated_worker.h"
 #include "third_party/blink/public/mojom/compute_pressure/web_pressure_manager.mojom.h"
 #endif  // BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
 
@@ -231,7 +231,7 @@
   }
 
 #if BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
-  PressureServiceForWorker<DedicatedWorkerHost>* pressure_service() {
+  PressureServiceForDedicatedWorker* pressure_service() {
     return pressure_service_.get();
   }
 #endif  // BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
@@ -392,8 +392,7 @@
   std::unique_ptr<ServiceWorkerMainResourceHandle> service_worker_handle_;
 
 #if BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
-  std::unique_ptr<PressureServiceForWorker<DedicatedWorkerHost>>
-      pressure_service_;
+  std::unique_ptr<PressureServiceForDedicatedWorker> pressure_service_;
 #endif  // BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
 
   // Script request URL used, only for DevTools and tracing. Only set after
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index dfdc4e3..b905061a 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -606,8 +606,7 @@
   }
 
   if (!pressure_service_) {
-    pressure_service_ =
-        std::make_unique<PressureServiceForWorker<SharedWorkerHost>>(this);
+    pressure_service_ = std::make_unique<PressureServiceForSharedWorker>(this);
   }
 
   pressure_service_->BindReceiver(std::move(receiver));
diff --git a/content/browser/worker_host/shared_worker_host.h b/content/browser/worker_host/shared_worker_host.h
index 9cccc87b..802d5c47 100644
--- a/content/browser/worker_host/shared_worker_host.h
+++ b/content/browser/worker_host/shared_worker_host.h
@@ -53,7 +53,7 @@
 #include "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom.h"
 
 #if BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
-#include "content/browser/compute_pressure/pressure_service_for_worker.h"
+#include "content/browser/compute_pressure/pressure_service_for_shared_worker.h"
 #include "third_party/blink/public/mojom/compute_pressure/web_pressure_manager.mojom.h"
 #endif  // BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
 
@@ -186,7 +186,7 @@
   }
 
 #if BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
-  PressureServiceForWorker<SharedWorkerHost>* pressure_service() {
+  PressureServiceForSharedWorker* pressure_service() {
     return pressure_service_.get();
   }
 #endif  // BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
@@ -327,7 +327,7 @@
   std::unique_ptr<SharedWorkerContentSettingsProxyImpl> content_settings_;
 
 #if BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
-  std::unique_ptr<PressureServiceForWorker<SharedWorkerHost>> pressure_service_;
+  std::unique_ptr<PressureServiceForSharedWorker> pressure_service_;
 #endif  // BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
 
   // This is kept alive during the lifetime of the shared worker, since it's
diff --git a/content/test/content_browser_test_utils_internal.h b/content/test/content_browser_test_utils_internal.h
index c832dd8..62249e3 100644
--- a/content/test/content_browser_test_utils_internal.h
+++ b/content/test/content_browser_test_utils_internal.h
@@ -23,6 +23,7 @@
 #include "content/browser/renderer_host/back_forward_cache_metrics.h"
 #include "content/browser/renderer_host/navigation_type.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/javascript_dialog_manager.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
diff --git a/content/test/data/accessibility/html/figcaption-expected-auralinux.txt b/content/test/data/accessibility/html/figcaption-expected-auralinux.txt
index ebdebfc1..edf991e 100644
--- a/content/test/data/accessibility/html/figcaption-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/figcaption-expected-auralinux.txt
@@ -1,7 +1,7 @@
 [document web]
-++[panel] details=[caption]
+++[panel]
 ++++[image] name='This is a green box.'
-++++[caption] details-for=[panel]
+++++[caption]
 ++++++[static] name='Fig.1 - A green Box'
 ++[panel]
 ++++[image] name='This is a blue box.'
diff --git a/content/test/data/accessibility/html/figcaption-expected-blink.txt b/content/test/data/accessibility/html/figcaption-expected-blink.txt
index 20dab35a..3c161699 100644
--- a/content/test/data/accessibility/html/figcaption-expected-blink.txt
+++ b/content/test/data/accessibility/html/figcaption-expected-blink.txt
@@ -1,7 +1,7 @@
 rootWebArea
 ++genericContainer ignored
 ++++genericContainer ignored
-++++++figure detailsIds=figcaption
+++++++figure
 ++++++++image name='This is a green box.'
 ++++++++figcaption
 ++++++++++staticText name='Fig.1 - A green Box'
diff --git a/content/test/data/accessibility/html/figcaption-nested-expected-blink.txt b/content/test/data/accessibility/html/figcaption-nested-expected-blink.txt
index e9f1a61e..46ab87a 100644
--- a/content/test/data/accessibility/html/figcaption-nested-expected-blink.txt
+++ b/content/test/data/accessibility/html/figcaption-nested-expected-blink.txt
@@ -1,17 +1,17 @@
 rootWebArea
 ++genericContainer ignored
 ++++genericContainer ignored
-++++++figure detailsIds=figcaption
+++++++figure
 ++++++++figcaption
 ++++++++++staticText name='External Caption'
 ++++++++++++inlineTextBox name='External Caption'
-++++++++figure detailsIds=figcaption
+++++++++figure
 ++++++++++image name='This is a green box.'
 ++++++++++figcaption
 ++++++++++++staticText name='Internal caption'
 ++++++++++++++inlineTextBox name='Internal caption'
-++++++figure detailsIds=figcaption
-++++++++figure detailsIds=figcaption
+++++++figure
+++++++++figure
 ++++++++++image name='This is a green box.'
 ++++++++++figcaption
 ++++++++++++staticText name='Internal caption'
diff --git a/extensions/browser/api/api_browser_context_keyed_service_factories.cc b/extensions/browser/api/api_browser_context_keyed_service_factories.cc
index 4c8e71d..f94fa218 100644
--- a/extensions/browser/api/api_browser_context_keyed_service_factories.cc
+++ b/extensions/browser/api/api_browser_context_keyed_service_factories.cc
@@ -42,6 +42,7 @@
 #include "extensions/browser/api/socket/socket.h"
 #include "extensions/browser/api/socket/tcp_socket.h"
 #include "extensions/browser/api/socket/udp_socket.h"
+#include "extensions/browser/api/socket/write_quota_checker.h"
 #include "extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h"
 #include "extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h"
 #include "extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h"
@@ -131,6 +132,7 @@
   WebcamPrivateAPI::GetFactoryInstance();
 #endif
   WebRequestProxyingWebSocket::EnsureAssociatedFactoryBuilt();
+  WriteQuotaChecker::GetFactoryInstance();
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 }
 
diff --git a/extensions/browser/api/socket/BUILD.gn b/extensions/browser/api/socket/BUILD.gn
index 418d94a0..c4be578 100644
--- a/extensions/browser/api/socket/BUILD.gn
+++ b/extensions/browser/api/socket/BUILD.gn
@@ -22,6 +22,8 @@
     "tls_socket.h",
     "udp_socket.cc",
     "udp_socket.h",
+    "write_quota_checker.cc",
+    "write_quota_checker.h",
   ]
 
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
diff --git a/extensions/browser/api/socket/socket.h b/extensions/browser/api/socket/socket.h
index 7205515..d6f41bfd 100644
--- a/extensions/browser/api/socket/socket.h
+++ b/extensions/browser/api/socket/socket.h
@@ -19,6 +19,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "extensions/browser/api/api_resource.h"
 #include "extensions/browser/api/api_resource_manager.h"
+#include "extensions/browser/api/socket/write_quota_checker.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/io_buffer.h"
@@ -180,6 +181,21 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 };
 
+template <>
+struct BrowserContextFactoryDependencies<ApiResourceManager<Socket>> {
+  static void DeclareFactoryDependencies(
+      BrowserContextKeyedAPIFactory<ApiResourceManager<Socket>>* factory) {
+    // Base deps from BrowserContextFactoryDependencies<ApiResourceManager<T>.
+    factory->DependsOn(
+        ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
+    factory->DependsOn(ExtensionRegistryFactory::GetInstance());
+    factory->DependsOn(ProcessManagerFactory::GetInstance());
+
+    // Extra deps for ApiResourceManager<Socket> service.
+    factory->DependsOn(WriteQuotaChecker::GetFactoryInstance());
+  }
+};
+
 }  //  namespace extensions
 
 #endif  // EXTENSIONS_BROWSER_API_SOCKET_SOCKET_H_
diff --git a/extensions/browser/api/socket/socket_api.cc b/extensions/browser/api/socket/socket_api.cc
index 99030a3..9ccaaf5 100644
--- a/extensions/browser/api/socket/socket_api.cc
+++ b/extensions/browser/api/socket/socket_api.cc
@@ -27,6 +27,7 @@
 #include "extensions/browser/api/socket/tcp_socket.h"
 #include "extensions/browser/api/socket/tls_socket.h"
 #include "extensions/browser/api/socket/udp_socket.h"
+#include "extensions/browser/api/socket/write_quota_checker.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/api/sockets/sockets_manifest_data.h"
 #include "extensions/common/extension.h"
@@ -91,6 +92,17 @@
 using content::BrowserThread;
 using content::SocketPermissionRequest;
 
+SocketApiFunction::ScopedWriteQuota::ScopedWriteQuota(SocketApiFunction* owner,
+                                                      size_t bytes_used)
+    : owner_(owner), bytes_used_(bytes_used) {
+  DCHECK(owner_);
+}
+
+SocketApiFunction::ScopedWriteQuota::~ScopedWriteQuota() {
+  WriteQuotaChecker::Get(owner_->browser_context())
+      ->ReturnBytes(owner_->extension_id(), bytes_used_);
+}
+
 SocketApiFunction::SocketApiFunction() = default;
 
 SocketApiFunction::~SocketApiFunction() = default;
@@ -202,6 +214,21 @@
   return SocketsManifestData::CheckRequest(extension(), param);
 }
 
+bool SocketApiFunction::TakeWriteQuota(size_t bytes_to_write) {
+  if (!WriteQuotaChecker::Get(browser_context())
+           ->TakeBytes(extension_id(), bytes_to_write)) {
+    return false;
+  }
+
+  DCHECK(!write_quota_used_.has_value());
+  write_quota_used_.emplace(this, bytes_to_write);
+  return true;
+}
+
+void SocketApiFunction::ReturnWriteQuota() {
+  write_quota_used_.reset();
+}
+
 SocketExtensionWithDnsLookupFunction::SocketExtensionWithDnsLookupFunction() =
     default;
 
@@ -592,6 +619,9 @@
 
   int socket_id = socket_id_value.GetInt();
   size_t io_buffer_size = data_value.GetBlob().size();
+  if (!TakeWriteQuota(io_buffer_size)) {
+    return RespondNow(Error(kExceedWriteQuotaError));
+  }
 
   auto io_buffer =
       base::MakeRefCounted<net::IOBufferWithSize>(data_value.GetBlob().size());
@@ -611,6 +641,8 @@
 }
 
 void SocketWriteFunction::OnCompleted(int bytes_written) {
+  ReturnWriteQuota();
+
   base::Value::Dict result;
   result.Set(kBytesWrittenKey, bytes_written);
   Respond(WithArguments(std::move(result)));
@@ -681,7 +713,6 @@
   hostname_ = hostname_value.GetString();
 
   io_buffer_size_ = data_value.GetBlob().size();
-
   io_buffer_ =
       base::MakeRefCounted<net::IOBufferWithSize>(data_value.GetBlob().size());
   base::ranges::copy(data_value.GetBlob(), io_buffer_->data());
@@ -719,11 +750,18 @@
     return;
   }
 
+  if (!TakeWriteQuota(io_buffer_size_)) {
+    Respond(Error(kExceedWriteQuotaError));
+    return;
+  }
+
   socket->SendTo(io_buffer_, io_buffer_size_, addresses_.front(),
                  base::BindOnce(&SocketSendToFunction::OnCompleted, this));
 }
 
 void SocketSendToFunction::OnCompleted(int bytes_written) {
+  ReturnWriteQuota();
+
   api::socket::WriteInfo info;
   info.bytes_written = bytes_written;
   Respond(ArgumentList(api::socket::SendTo::Results::Create(info)));
diff --git a/extensions/browser/api/socket/socket_api.h b/extensions/browser/api/socket/socket_api.h
index 0822f2a..7fbc0ce 100644
--- a/extensions/browser/api/socket/socket_api.h
+++ b/extensions/browser/api/socket/socket_api.h
@@ -9,6 +9,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <unordered_set>
 
@@ -62,7 +63,7 @@
 // "socket" namespace vs new version in "socket.xxx" namespaces).
 class SocketResourceManagerInterface {
  public:
-  virtual ~SocketResourceManagerInterface() {}
+  virtual ~SocketResourceManagerInterface() = default;
 
   virtual bool SetBrowserContext(content::BrowserContext* context) = 0;
   virtual int Add(Socket* socket) = 0;
@@ -123,6 +124,9 @@
 // Base class for socket API functions, with some helper functions.
 class SocketApiFunction : public ExtensionFunction {
  public:
+  inline static constexpr char kExceedWriteQuotaError[] =
+      "Exceeded write quota.";
+
   SocketApiFunction();
 
  protected:
@@ -148,6 +152,13 @@
   // for CrOS Terminal.
   bool CheckRequest(const content::SocketPermissionRequest& param) const;
 
+  // Adds `bytes_to_write` against the write quota. Returns false if it would
+  // exceed the write quota.
+  bool TakeWriteQuota(size_t bytes_to_write);
+
+  // Returns bytes taken in last `TakeWriteQuota` call to the write quota.
+  void ReturnWriteQuota();
+
   virtual std::unique_ptr<SocketResourceManagerInterface>
   CreateSocketResourceManager();
 
@@ -163,7 +174,18 @@
                         Socket* socket);
 
  private:
+  class ScopedWriteQuota {
+   public:
+    ScopedWriteQuota(SocketApiFunction* owner, size_t bytes_used);
+    ~ScopedWriteQuota();
+
+   private:
+    const raw_ptr<SocketApiFunction> owner_;
+    const size_t bytes_used_;
+  };
+
   std::unique_ptr<SocketResourceManagerInterface> manager_;
+  std::optional<ScopedWriteQuota> write_quota_used_;
 };
 
 class SocketExtensionWithDnsLookupFunction
diff --git a/extensions/browser/api/socket/socket_apitest.cc b/extensions/browser/api/socket/socket_apitest.cc
index 7830444..53a8c714 100644
--- a/extensions/browser/api/socket/socket_apitest.cc
+++ b/extensions/browser/api/socket/socket_apitest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "extensions/browser/api/socket/socket_api.h"
+#include "extensions/browser/api/socket/write_quota_checker.h"
 #include "extensions/browser/api_test_utils.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
@@ -12,7 +13,7 @@
 
 namespace extensions {
 
-class SocketApiTest : public AppShellTest {};
+using SocketApiTest = AppShellTest;
 
 IN_PROC_BROWSER_TEST_F(SocketApiTest, SocketUDPCreateGood) {
   scoped_refptr<extensions::SocketCreateFunction> socket_create_function(
@@ -66,4 +67,30 @@
   ASSERT_FALSE(result->GetList().empty());
 }
 
+IN_PROC_BROWSER_TEST_F(SocketApiTest, WriteQuotaChecker) {
+  WriteQuotaChecker* checker = WriteQuotaChecker::Get(browser_context());
+
+  constexpr size_t kBytesLimit = 100;
+  WriteQuotaChecker::ScopedBytesLimitForTest scoped_limit(checker, kBytesLimit);
+
+  const ExtensionId extension_id = "test_extension_id";
+  const ExtensionId another_extension_id = "another_test_extension_id";
+
+  // Fails if a single request is too large.
+  EXPECT_FALSE(checker->TakeBytes(extension_id, kBytesLimit + 1));
+
+  // Fails if combined multiple requests are larger than limit.
+  EXPECT_TRUE(checker->TakeBytes(extension_id, kBytesLimit));
+  EXPECT_FALSE(checker->TakeBytes(extension_id, 1));
+
+  // Different extension is not affected.
+  EXPECT_TRUE(checker->TakeBytes(another_extension_id, kBytesLimit));
+
+  // Simulate a request is done and return bytes to the pool.
+  checker->ReturnBytes(extension_id, kBytesLimit);
+
+  // Writes are allowed again.
+  EXPECT_TRUE(checker->TakeBytes(extension_id, 1));
+}
+
 }  //  namespace extensions
diff --git a/extensions/browser/api/socket/write_quota_checker.cc b/extensions/browser/api/socket/write_quota_checker.cc
new file mode 100644
index 0000000..f943746
--- /dev/null
+++ b/extensions/browser/api/socket/write_quota_checker.cc
@@ -0,0 +1,80 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/api/socket/write_quota_checker.h"
+
+#include "base/check.h"
+#include "base/check_op.h"
+#include "base/no_destructor.h"
+#include "extensions/browser/extension_registry.h"
+
+namespace extensions {
+
+namespace {
+
+// Maximum pending bytes to write.
+constexpr size_t kWriteLimit = 200 * 1024 * 1024;
+
+}  // namespace
+
+WriteQuotaChecker::ScopedBytesLimitForTest::ScopedBytesLimitForTest(
+    WriteQuotaChecker* checker,
+    size_t new_bytes_limit)
+    : scoped_bytes_limit_(&checker->bytes_limit_, new_bytes_limit) {}
+
+WriteQuotaChecker::ScopedBytesLimitForTest::~ScopedBytesLimitForTest() =
+    default;
+
+// static
+WriteQuotaChecker* WriteQuotaChecker::Get(content::BrowserContext* context) {
+  return BrowserContextKeyedAPIFactory<WriteQuotaChecker>::Get(context);
+}
+
+// static
+BrowserContextKeyedAPIFactory<WriteQuotaChecker>*
+WriteQuotaChecker::GetFactoryInstance() {
+  static base::NoDestructor<BrowserContextKeyedAPIFactory<WriteQuotaChecker>>
+      factory;
+  return factory.get();
+}
+
+WriteQuotaChecker::WriteQuotaChecker(content::BrowserContext* context)
+    : bytes_limit_(kWriteLimit) {
+  extension_registry_observation_.Observe(ExtensionRegistry::Get(context));
+}
+
+WriteQuotaChecker::~WriteQuotaChecker() = default;
+
+bool WriteQuotaChecker::TakeBytes(const ExtensionId& extension_id,
+                                  size_t bytes) {
+  auto& bytes_used = bytes_used_map_[extension_id];
+
+  size_t new_bytes_used = bytes_used + bytes;
+  if (new_bytes_used > bytes_limit_) {
+    return false;
+  }
+
+  bytes_used = new_bytes_used;
+  return true;
+}
+
+void WriteQuotaChecker::ReturnBytes(const ExtensionId& extension_id,
+                                    size_t bytes) {
+  auto it = bytes_used_map_.find(extension_id);
+  CHECK(it != bytes_used_map_.end());
+  CHECK_GE(it->second, bytes);
+
+  it->second -= bytes;
+  if (it->second == 0) {
+    bytes_used_map_.erase(it);
+  }
+}
+
+void WriteQuotaChecker::OnExtensionUnloaded(content::BrowserContext* context,
+                                            const Extension* extension,
+                                            UnloadedExtensionReason reason) {
+  bytes_used_map_.erase(extension->id());
+}
+
+}  // namespace extensions
diff --git a/extensions/browser/api/socket/write_quota_checker.h b/extensions/browser/api/socket/write_quota_checker.h
new file mode 100644
index 0000000..c579448
--- /dev/null
+++ b/extensions/browser/api/socket/write_quota_checker.h
@@ -0,0 +1,69 @@
+// 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 EXTENSIONS_BROWSER_API_SOCKET_WRITE_QUOTA_CHECKER_H_
+#define EXTENSIONS_BROWSER_API_SOCKET_WRITE_QUOTA_CHECKER_H_
+
+#include <stddef.h>
+
+#include <map>
+
+#include "base/auto_reset.h"
+#include "base/scoped_observation.h"
+#include "extensions/browser/browser_context_keyed_api_factory.h"
+#include "extensions/browser/extension_registry_observer.h"
+#include "extensions/common/extension_id.h"
+
+namespace extensions {
+
+// Tracks bytes quota per extension.
+class WriteQuotaChecker : public BrowserContextKeyedAPI,
+                          public extensions::ExtensionRegistryObserver {
+ public:
+  class ScopedBytesLimitForTest {
+   public:
+    ScopedBytesLimitForTest(WriteQuotaChecker* checker, size_t new_bytes_limit);
+    ~ScopedBytesLimitForTest();
+
+   private:
+    base::AutoReset<size_t> scoped_bytes_limit_;
+  };
+
+  static WriteQuotaChecker* Get(content::BrowserContext* context);
+
+  // BrowserContextKeyedAPI:
+  static BrowserContextKeyedAPIFactory<WriteQuotaChecker>* GetFactoryInstance();
+  static const char* service_name() { return "WriteQuotaChecker"; }
+
+  explicit WriteQuotaChecker(content::BrowserContext* context);
+  ~WriteQuotaChecker() override;
+
+  // Attempts to take more bytes to write. Returns false if the attempt would
+  // exceed the limit.
+  bool TakeBytes(const ExtensionId& extension_id, size_t bytes);
+
+  // Puts bytes back to the pool after done with writing.
+  void ReturnBytes(const ExtensionId& extension_id, size_t bytes);
+
+ private:
+  friend class BrowserContextKeyedAPIFactory<WriteQuotaChecker>;
+
+  // ExtensionRegistryObserver:
+  void OnExtensionUnloaded(content::BrowserContext* browser_context,
+                           const Extension* extension,
+                           UnloadedExtensionReason reason) override;
+
+  // Max pending write bytes.
+  size_t bytes_limit_ = 0;
+
+  // Tracked writing bytes per extension.
+  std::map<ExtensionId, size_t> bytes_used_map_;
+
+  base::ScopedObservation<ExtensionRegistry, ExtensionRegistryObserver>
+      extension_registry_observation_{this};
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_BROWSER_API_SOCKET_WRITE_QUOTA_CHECKER_H_
diff --git a/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc b/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc
index fe918e1..e7946f1 100644
--- a/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc
+++ b/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc
@@ -358,6 +358,9 @@
       sockets_tcp::Send::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(params);
   size_t io_buffer_size = params->data.size();
+  if (!TakeWriteQuota(io_buffer_size)) {
+    return RespondNow(Error(kExceedWriteQuotaError));
+  }
 
   auto io_buffer =
       base::MakeRefCounted<net::IOBufferWithSize>(params->data.size());
@@ -374,6 +377,8 @@
 }
 
 void SocketsTcpSendFunction::OnCompleted(int net_result) {
+  ReturnWriteQuota();
+
   if (net_result >= net::OK) {
     SetSendResult(net::OK, net_result);
   } else {
diff --git a/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc b/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc
index 2bbc837a..02543ac 100644
--- a/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc
+++ b/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc
@@ -14,6 +14,7 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/network_service_test_helper.h"
+#include "extensions/browser/api/socket/write_quota_checker.h"
 #include "extensions/browser/api/sockets_tcp/sockets_tcp_api.h"
 #include "extensions/browser/api_test_utils.h"
 #include "extensions/common/extension.h"
@@ -168,4 +169,38 @@
   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
 
+IN_PROC_BROWSER_TEST_F(SocketsTcpApiTest, SocketTcpSendWriteQuota) {
+  WriteQuotaChecker* write_quota_checker =
+      WriteQuotaChecker::Get(browser_context());
+  constexpr size_t kBytesLimit = 1;
+  WriteQuotaChecker::ScopedBytesLimitForTest scoped_quota(write_quota_checker,
+                                                          kBytesLimit);
+
+  net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTP);
+  test_server.AddDefaultHandlers();
+  EXPECT_TRUE(test_server.Start());
+
+  net::HostPortPair host_port_pair = test_server.host_port_pair();
+  int port = host_port_pair.port();
+  ASSERT_GT(port, 0);
+
+  // Test that connect() is properly resolving hostnames.
+  host_port_pair.set_host(kHostname);
+
+  ResultCatcher catcher;
+  catcher.RestrictToBrowserContext(browser_context());
+
+  ExtensionTestMessageListener listener("info_please",
+                                        ReplyBehavior::kWillReply);
+
+  scoped_refptr<const Extension> test_extension = LoadApp("sockets_tcp/api");
+  ASSERT_TRUE(test_extension);
+
+  EXPECT_TRUE(listener.WaitUntilSatisfied());
+  listener.Reply(base::StringPrintf("tcp_send_write_quota:%s:%d",
+                                    host_port_pair.host().c_str(), port));
+
+  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/api/sockets_udp/sockets_udp_api.cc b/extensions/browser/api/sockets_udp/sockets_udp_api.cc
index 6481d61..befb9d7 100644
--- a/extensions/browser/api/sockets_udp/sockets_udp_api.cc
+++ b/extensions/browser/api/sockets_udp/sockets_udp_api.cc
@@ -234,6 +234,9 @@
   params_ = sockets_udp::Send::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(params_);
   io_buffer_size_ = params_->data.size();
+  if (!TakeWriteQuota(io_buffer_size_)) {
+    return RespondNow(Error(kExceedWriteQuotaError));
+  }
 
   io_buffer_ =
       base::MakeRefCounted<net::IOBufferWithSize>(params_->data.size());
@@ -289,6 +292,8 @@
 }
 
 void SocketsUdpSendFunction::OnCompleted(int net_result) {
+  ReturnWriteQuota();
+
   if (net_result >= net::OK) {
     SetSendResult(net::OK, net_result);
   } else {
diff --git a/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc b/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc
index dceb020a..b73d761b 100644
--- a/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc
+++ b/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc
@@ -6,6 +6,7 @@
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "content/public/browser/storage_partition.h"
+#include "extensions/browser/api/socket/write_quota_checker.h"
 #include "extensions/browser/api/sockets_udp/sockets_udp_api.h"
 #include "extensions/browser/api/sockets_udp/test_udp_echo_server.h"
 #include "extensions/browser/api_test_utils.h"
@@ -58,7 +59,7 @@
       &host_port_pair));
 
   int port = host_port_pair.port();
-  ASSERT_TRUE(port > 0);
+  ASSERT_GT(port, 0);
 
   // Test that sendTo() is properly resolving hostnames.
   host_port_pair.set_host(kHostname);
@@ -90,4 +91,34 @@
   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
 
+IN_PROC_BROWSER_TEST_F(SocketsUdpApiTest, SocketsUdpSendWriteQuota) {
+  WriteQuotaChecker* write_quota_checker =
+      WriteQuotaChecker::Get(browser_context());
+  constexpr size_t kBytesLimit = 1;
+  WriteQuotaChecker::ScopedBytesLimitForTest scoped_quota(write_quota_checker,
+                                                          kBytesLimit);
+
+  TestUdpEchoServer udp_echo_server;
+  net::HostPortPair host_port_pair;
+  ASSERT_TRUE(udp_echo_server.Start(
+      browser_context()->GetDefaultStoragePartition()->GetNetworkContext(),
+      &host_port_pair));
+
+  int port = host_port_pair.port();
+  ASSERT_GT(port, 0);
+
+  ResultCatcher catcher;
+  catcher.RestrictToBrowserContext(browser_context());
+
+  ExtensionTestMessageListener listener("info_please",
+                                        ReplyBehavior::kWillReply);
+
+  ASSERT_TRUE(LoadApp("sockets_udp/api"));
+  EXPECT_TRUE(listener.WaitUntilSatisfied());
+  listener.Reply(base::StringPrintf("udp_send_write_quota:%s:%d",
+                                    host_port_pair.host().c_str(), port));
+
+  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
+
 }  // namespace extensions
diff --git a/extensions/test/data/sockets_tcp/api/background.js b/extensions/test/data/sockets_tcp/api/background.js
index cc59d3ad..7f4f751 100644
--- a/extensions/test/data/sockets_tcp/api/background.js
+++ b/extensions/test/data/sockets_tcp/api/background.js
@@ -422,6 +422,41 @@
   }
 };  // testSecure()
 
+var testSendWriteQuota = function() {
+  createSocket();
+
+  function createSocket() {
+    console.log("createSocket");
+    chrome.sockets.tcp.create({
+      "name": "test",
+      "persistent": true,
+      "bufferSize": 104
+    }, onCreateComplete);
+  }
+
+  function onCreateComplete(socketInfo) {
+    console.log("onCreateComplete");
+    tcp_socketId = socketInfo.socketId;
+    chrome.test.assertTrue(tcp_socketId > 0, "failed to create socket");
+
+    string2ArrayBuffer(httpPost, function(arrayBuffer) {
+      chrome.sockets.tcp.send(tcp_socketId, arrayBuffer, onSendComplete);
+    });
+  }
+
+  function onSendComplete(sendInfo) {
+    console.log("onSendComplete: ", sendInfo,
+                ', lastError=', chrome.runtime.lastError);
+    if (chrome.runtime.lastError &&
+        chrome.runtime.lastError.message == "Exceeded write quota.") {
+      chrome.test.succeed();
+      return;
+    }
+
+    chrome.test.fail('Write quota not enforced');
+  }
+};  // testSendWriteQuota
+
 function waitForBlockingOperation() {
   if (++waitCount < 10) {
     setTimeout(waitForBlockingOperation, 1000);
@@ -450,6 +485,12 @@
       console.log("Running tests for HTTPS, server " +
           https_address + ":" + https_port);
       tests = tests.concat([testSecure]);
+    } else if (test_type == 'tcp_send_write_quota') {
+      tcp_address = parts[1];
+      tcp_port = parseInt(parts[2]);
+      console.log("Running tests for TCP, server " +
+          tcp_address + ":" + tcp_port);
+      tests = tests.concat([testSendWriteQuota]);
     } else {
       chrome.test.fail("Invalid test type: " + test_type);
     }
diff --git a/extensions/test/data/sockets_udp/api/background.js b/extensions/test/data/sockets_udp/api/background.js
index c95861d..bbe9caa 100644
--- a/extensions/test/data/sockets_udp/api/background.js
+++ b/extensions/test/data/sockets_udp/api/background.js
@@ -376,6 +376,48 @@
   }
 };
 
+var testSendWriteQuota = function() {
+  socketId = 0;
+  var localSocketId;
+
+  console.log("testSendWriteQuota");
+  setTimeout(waitForBlockingOperation, 1000);
+  chrome.sockets.udp.create({}, onCreate);
+
+  function onCreate(socketInfo) {
+    console.log("socket created: " + socketInfo.socketId);
+    localSocketId = socketId = socketInfo.socketId;
+    chrome.test.assertTrue(localSocketId > 0, "failed to create socket");
+
+    chrome.sockets.udp.bind(localSocketId, "0.0.0.0", 0, onBind);
+  }
+
+  function onBind(result) {
+    console.log("socket bound to local host");
+    chrome.test.assertEq(0, result, "Bind failed with error: " + result);
+    if (result < 0)
+      return;
+
+    string2ArrayBuffer(request, function(arrayBuffer) {
+      chrome.sockets.udp.send(localSocketId, arrayBuffer, address, port,
+                              onSendComplete);
+    });
+  }
+
+  function onSendComplete(sendInfo) {
+    console.log("onSendComplete: ", sendInfo,
+                ', lastError=', chrome.runtime.lastError);
+    if (chrome.runtime.lastError &&
+        chrome.runtime.lastError.message == "Exceeded write quota.") {
+      chrome.test.succeed();
+      return;
+    }
+
+    chrome.test.fail('Write quota not enforced');
+  }
+};
+
+
 ///////////////////////////////////////////////////////////////////////////////
 // Test driver
 //
@@ -390,6 +432,9 @@
   if (test_type == 'multicast') {
     console.log("Running multicast tests");
     chrome.test.runTests([ testMulticast ]);
+  } else if (test_type == 'udp_send_write_quota') {
+    console.log("Running UDP send write quota tests");
+    chrome.test.runTests([ testSendWriteQuota ]);
   } else {
     console.log("Running udp tests");
     chrome.test.runTests([testSocketCreation, testSending, testSetPaused,
diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc
index b1237e4e..0894e32 100644
--- a/gin/v8_initializer.cc
+++ b/gin/v8_initializer.cc
@@ -22,6 +22,7 @@
 #include "base/debug/alias.h"
 #include "base/debug/crash_logging.h"
 #include "base/feature_list.h"
+#include "base/feature_visitor.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/memory_mapped_file.h"
@@ -218,10 +219,77 @@
   }
 }
 
+constexpr std::string_view kV8FlagFeaturePrefix = "V8Flag_";
+
+}  // namespace
+
+class V8FeatureVisitor : public base::FeatureVisitor {
+ public:
+  void Visit(const std::string& feature_name,
+             base::FeatureList::OverrideState override_state,
+             const std::map<std::string, std::string>& params,
+             const std::string& trial_name,
+             const std::string& group_name) override {
+    std::string_view feature_name_view(feature_name);
+
+    // VisitFeaturesAndParams is called with kV8FlagFeaturePrefix as a filter
+    // prefix, so we expect all feature names to start with "V8Flag_". Strip
+    // this prefix off to get the corresponding V8 flag name.
+    DCHECK(feature_name_view.starts_with(kV8FlagFeaturePrefix));
+    std::string_view flag_name =
+        feature_name_view.substr(kV8FlagFeaturePrefix.size());
+
+    switch (override_state) {
+      case base::FeatureList::OverrideState::OVERRIDE_USE_DEFAULT:
+        return;
+
+      case base::FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE:
+        SetV8FlagsFormatted("--no-%s", flag_name);
+        // Do not set parameters for disabled features.
+        break;
+
+      case base::FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE:
+        SetV8FlagsFormatted("--%s", flag_name);
+        for (const auto& [param_name, param_value] : params) {
+          SetV8FlagsFormatted("--%s=%s", param_name.c_str(),
+                              param_value.c_str());
+        }
+        break;
+    }
+  }
+};
+
+namespace {
+
 void SetFlags(IsolateHolder::ScriptMode mode,
               const std::string js_command_line_flags) {
-  // We assume that all feature flag defaults correspond to the default
-  // values of the corresponding V8 flags.
+  // Chromium features prefixed with "V8Flag_" are forwarded to V8 as V8 flags,
+  // with the "V8Flag_" prefix stripped off. For example, an enabled feature
+  // "V8Flag_foo_bar" will be passed to V8 as the flag `--foo_bar`. Similarly,
+  // if that feature is explicitly disabled, it will be passed to V8 as
+  // `--no-foo_bar`. No Chromium-side declaration of a V8Flag_foo_bar feature
+  // is necessary, the matching is done on strings.
+  //
+  // Parameters attached to features will also be passed through, with the same
+  // name as the parameter and the value passed by string, to be decoded by V8's
+  // flag parsing.
+  //
+  // Thus, running Chromium with:
+  //
+  //   --enable-features=V8Flag_foo,V8Flag_bar:bar_param/20
+  //   --disable-features=V8Flag_baz
+  //
+  // will be converted, on V8 initialization, to V8 flags:
+  //
+  //   --foo --bar --bar_param=20 --no-baz
+  V8FeatureVisitor feature_visitor;
+  base::FeatureList::VisitFeaturesAndParams(feature_visitor,
+                                            kV8FlagFeaturePrefix);
+
+  // Otherwise, feature flags explicitly defined in Chromium are translated
+  // to V8 flags as follows. We ignore feature flag default values, instead
+  // using the corresponding V8 flags default values if there is no explicit
+  // feature override.
   SetV8FlagsIfOverridden(features::kV8CompactCodeSpaceWithStack,
                          "--compact-code-space-with-stack",
                          "--no-compact-code-space-with-stack");
diff --git a/gpu/command_buffer/service/dawn_context_provider.cc b/gpu/command_buffer/service/dawn_context_provider.cc
index 3a53a3011..cafd18c 100644
--- a/gpu/command_buffer/service/dawn_context_provider.cc
+++ b/gpu/command_buffer/service/dawn_context_provider.cc
@@ -361,6 +361,24 @@
 
   std::optional<error::ContextLostReason> GetResetStatus() const;
 
+  // Provided to wgpu::Device as logging callback.
+  static void LogInfo(WGPULoggingType type,
+                      char const* message,
+                      void* userdata) {
+    switch (static_cast<wgpu::LoggingType>(type)) {
+      case wgpu::LoggingType::Warning:
+        LOG(WARNING) << message;
+        break;
+      case wgpu::LoggingType::Error:
+        LOG(ERROR) << message;
+        SetDawnErrorCrashKey(message);
+        base::debug::DumpWithoutCrashing();
+        break;
+      default:
+        break;
+    }
+  }
+
  private:
   friend class base::RefCountedThreadSafe<DawnSharedContext>;
 
@@ -389,24 +407,6 @@
     }
   }
 
-  // Provided to wgpu::Device as logging callback.
-  static void LogInfo(WGPULoggingType type,
-                      char const* message,
-                      void* userdata) {
-    switch (static_cast<wgpu::LoggingType>(type)) {
-      case wgpu::LoggingType::Warning:
-        LOG(WARNING) << message;
-        break;
-      case wgpu::LoggingType::Error:
-        LOG(ERROR) << message;
-        SetDawnErrorCrashKey(message);
-        base::debug::DumpWithoutCrashing();
-        break;
-      default:
-        break;
-    }
-  }
-
   ~DawnSharedContext() override;
 
   void OnError(wgpu::ErrorType error_type, const char* message);
@@ -792,6 +792,29 @@
       new DawnContextProvider(std::move(dawn_shared_context)));
 }
 
+#if BUILDFLAG(IS_WIN)
+std::unique_ptr<webgpu::DawnInstance>
+DawnContextProvider::CreateDawnInstanceForD3D12ShaderCache(
+    const GpuPreferences& gpu_preferences) {
+  std::unique_ptr<webgpu::DawnInstance> instance = webgpu::DawnInstance::Create(
+      nullptr, gpu_preferences, webgpu::SafetyLevel::kUnsafe,
+      &DawnSharedContext::LogInfo, nullptr);
+
+  // Request D3D12 adapters to initialize the access to D3D12 shader cache.
+  wgpu::RequestAdapterOptions adapter_options;
+  adapter_options.backendType = wgpu::BackendType::D3D12;
+  adapter_options.forceFallbackAdapter = false;
+  dawn::native::d3d::RequestAdapterOptionsLUID adapter_options_luid;
+  if (GetANGLED3D11DeviceLUID(&adapter_options_luid.adapterLUID)) {
+    adapter_options.nextInChain = &adapter_options_luid;
+  }
+  adapter_options.compatibilityMode = false;
+  instance->EnumerateAdapters(&adapter_options);
+
+  return instance;
+}
+#endif
+
 std::unique_ptr<DawnContextProvider>
 DawnContextProvider::CreateWithSharedDevice(
     const DawnContextProvider* existing) {
diff --git a/gpu/command_buffer/service/dawn_context_provider.h b/gpu/command_buffer/service/dawn_context_provider.h
index 64fbb1d..c91a909 100644
--- a/gpu/command_buffer/service/dawn_context_provider.h
+++ b/gpu/command_buffer/service/dawn_context_provider.h
@@ -33,6 +33,10 @@
 
 namespace gpu {
 
+namespace webgpu {
+class DawnInstance;
+}  // namespace webgpu
+
 class DawnSharedContext;
 
 class GPU_GLES2_EXPORT DawnContextProvider {
@@ -61,6 +65,13 @@
   static std::unique_ptr<DawnContextProvider> CreateWithSharedDevice(
       const DawnContextProvider* existing);
 
+#if BUILDFLAG(IS_WIN)
+  // Create a DawnInstance and request a D3D12 adapter to initialize the access
+  // to D3D12 shader cache.
+  static std::unique_ptr<webgpu::DawnInstance>
+  CreateDawnInstanceForD3D12ShaderCache(const GpuPreferences& gpu_preferences);
+#endif
+
   static wgpu::BackendType GetDefaultBackendType();
   static bool DefaultForceFallbackAdapter();
 
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index 7f910ff..5dcc235 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -82,6 +82,7 @@
 #endif
 
 #if BUILDFLAG(USE_DAWN) || BUILDFLAG(SKIA_USE_DAWN)
+#include "gpu/command_buffer/service/dawn_instance.h"
 #include "third_party/dawn/include/dawn/dawn_proc.h"          // nogncheck
 #include "third_party/dawn/include/dawn/native/DawnNative.h"  // nogncheck
 #endif
@@ -850,6 +851,20 @@
     watchdog_init.SetGpuWatchdogPtr(watchdog_thread_.get());
   }
 
+  InitializeDawnProcs();
+
+#if BUILDFLAG(IS_WIN)
+  // We need to create a D3D12Device to enable the access to D3D12 shader cache
+  // files on many GPU drivers before the launch of the GPU sandbox (otherwise
+  // such access will always be blocked by the GPU sandbox). To achieve this
+  // goal we create a Dawn instance and attempt to request an adapter for D3D12
+  // backend so that a D3D12Device will be saved in the Dawn instance.
+  // TODO(chromium:40700602): Fix WebGPU shader cache partitioning.
+  dawn_instance_for_d3d12_shader_cache_ =
+      gpu::DawnContextProvider::CreateDawnInstanceForD3D12ShaderCache(
+          gpu_preferences_);
+#endif
+
   UMA_HISTOGRAM_ENUMERATION("GPU.GLImplementation", gl::GetGLImplementation());
 
   if (!gpu_info_.sandboxed && !attempted_startsandbox) {
@@ -858,8 +873,6 @@
   }
   UMA_HISTOGRAM_BOOLEAN("GPU.Sandboxed", gpu_info_.sandboxed);
 
-  InitializeDawnProcs();
-
   if (gpu_preferences_.gr_context_type == GrContextType::kGraphiteDawn) {
     if (!InitializeDawn()) {
       if (gpu_feature_info_.status_values[GPU_FEATURE_TYPE_SKIA_GRAPHITE] ==
diff --git a/gpu/ipc/service/gpu_init.h b/gpu/ipc/service/gpu_init.h
index d2176bdb..5c1a1c0 100644
--- a/gpu/ipc/service/gpu_init.h
+++ b/gpu/ipc/service/gpu_init.h
@@ -34,6 +34,10 @@
 
 namespace gpu {
 
+namespace webgpu {
+class DawnInstance;
+}  // namespace webgpu
+
 class VulkanImplementation;
 
 class GPU_IPC_SERVICE_EXPORT GpuSandboxHelper {
@@ -111,6 +115,10 @@
   std::unique_ptr<DawnContextProvider> dawn_context_provider_;
 #endif
 
+#if BUILDFLAG(IS_WIN)
+  std::unique_ptr<webgpu::DawnInstance> dawn_instance_for_d3d12_shader_cache_;
+#endif
+
   GPUInfo gpu_info_;
   GpuFeatureInfo gpu_feature_info_;
   GpuPreferences gpu_preferences_;
diff --git a/ios/chrome/app/application_delegate/metric_kit_subscriber.mm b/ios/chrome/app/application_delegate/metric_kit_subscriber.mm
index 9b1a85b0..7b1e46b 100644
--- a/ios/chrome/app/application_delegate/metric_kit_subscriber.mm
+++ b/ios/chrome/app/application_delegate/metric_kit_subscriber.mm
@@ -162,17 +162,13 @@
 }
 
 + (void)createExtendedLaunchTask {
-#if defined(__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0
   [MXMetricManager extendLaunchMeasurementForTaskID:kMainLaunchTaskId
                                               error:nil];
-#endif
 }
 
 + (void)endExtendedLaunchTask {
-#if defined(__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0
   [MXMetricManager finishExtendedLaunchMeasurementForTaskID:kMainLaunchTaskId
                                                       error:nil];
-#endif
 }
 
 - (void)setEnabled:(BOOL)enable {
@@ -309,7 +305,6 @@
   [self logStartupDurationMXHistogram:histogrammedTimeToFirstDraw
                        toUMAHistogram:prefix + "TimeToFirstDraw"];
 
-#if defined(__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0
   MXHistogram* histogrammedOptimizedTimeToFirstDraw =
       payload.applicationLaunchMetrics.histogrammedOptimizedTimeToFirstDraw;
   [self logStartupDurationMXHistogram:histogrammedOptimizedTimeToFirstDraw
@@ -319,7 +314,6 @@
       payload.applicationLaunchMetrics.histogrammedExtendedLaunch;
   [self logStartupDurationMXHistogram:histogrammedExtendedLaunch
                        toUMAHistogram:prefix + "ExtendedLaunch"];
-#endif
 
   MXHistogram* histogrammedApplicationHangTime =
       payload.applicationResponsivenessMetrics.histogrammedApplicationHangTime;
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index afbde8c..2771183 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -331,8 +331,12 @@
   // cleared for all time, which is intended to happen in this case.
   BrowsingDataRemover* browsingDataRemover =
       BrowsingDataRemoverFactory::GetForBrowserState(browser_state_);
+  BrowsingDataRemover::RemovalParams params;
+  params.keep_active_tab =
+      BrowsingDataRemover::KeepActiveTabPolicy::kKeepActiveTab;
   browsingDataRemover->RemoveInRange(last_signin_timestamp, base::Time::Now(),
-                                     remove_mask, std::move(completion));
+                                     remove_mask, std::move(completion),
+                                     params);
 }
 
 }  // namespace
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 833fed2..d8698481 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -5813,7 +5813,7 @@
         Show tabs
       </message>
       <message name="IDS_IOS_TOOLBAR_SHOW_TAB_GROUP" desc="The accessibility label for the toolbar show tab group button [iOS only].">
-        Show tabs in group
+        Show all tabs in tab group
       </message>
       <message name="IDS_IOS_TOOLBAR_SWIPE_IPH" desc="The text for the full screen in-product help for side swipe on the tab strip to navigate between tabs. [iOS only]">
         You can swipe the toolbar to switch between tabs.
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TOOLBAR_SHOW_TAB_GROUP.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TOOLBAR_SHOW_TAB_GROUP.png.sha1
index 8fd4c70f..6f9221a2 100644
--- a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TOOLBAR_SHOW_TAB_GROUP.png.sha1
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TOOLBAR_SHOW_TAB_GROUP.png.sha1
@@ -1 +1 @@
-a313318b5674678923d3d188cdbd1268f6a678a3
\ No newline at end of file
+7c56159b3dd9aa4d3679e37bc35f370898c746f9
\ No newline at end of file
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/card_view_controller_egtest.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/card_view_controller_egtest.mm
index a7da027..2830e79 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/card_view_controller_egtest.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/card_view_controller_egtest.mm
@@ -875,24 +875,8 @@
 
   // As of Xcode 14 beta 2, tapping the keyboard does not dismiss the
   // accessory view popup.
-  bool systemDismissesView = true;
-#if defined(__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0
-  systemDismissesView = false;
-#endif  // defined(__IPHONE_16_0)
-
-  if (systemDismissesView) {
-    // Verify the credit card controller table view and the credit card icon is
-    // not visible.
-    [[EarlGrey
-        selectElementWithMatcher:manual_fill::CreditCardTableViewMatcher()]
-        assertWithMatcher:grey_notVisible()];
-    [[EarlGrey selectElementWithMatcher:manual_fill::KeyboardIconMatcher()]
-        assertWithMatcher:grey_notVisible()];
-  } else {
-    [[EarlGrey
-        selectElementWithMatcher:manual_fill::CreditCardTableViewMatcher()]
-        assertWithMatcher:grey_sufficientlyVisible()];
-  }
+  [[EarlGrey selectElementWithMatcher:manual_fill::CreditCardTableViewMatcher()]
+      assertWithMatcher:grey_sufficientlyVisible()];
 }
 
 // Tests that, after switching fields, the content size of the table view didn't
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 554ca1b..f99ebe0 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
+++ b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
@@ -3907,6 +3907,42 @@
   _quickDeleteCoordinator = nil;
 }
 
+- (void)stopQuickDeleteForAnimationWithCompletion:(ProceduralBlock)completion {
+  CHECK(IsIosQuickDeleteEnabled());
+
+  // TODO(crbug.com/335387869): Remove NotFatalUntil and the if below when we're
+  // sure this code path is infeasible. The BrowserViewController should always
+  // have at least the QuickDeleteViewController on top of it.
+  CHECK(self.viewController.presentedViewController, base::NotFatalUntil::M133);
+
+  // If BrowserViewController has not presented any view controller, then
+  // trigger `completion` immediately.
+  if (!self.viewController.presentedViewController) {
+    completion();
+    [self stopQuickDelete];
+    return;
+  }
+
+  // If BrowserViewController has presented a view controller, then dismiss
+  // every VC on top of it.
+  id<ApplicationCommands> applicationCommandsHandler =
+      HandlerForProtocol(self.dispatcher, ApplicationCommands);
+  __weak __typeof(self) weakSelf = self;
+  ProceduralBlock dismissalCompletion = ^{
+    if (completion) {
+      completion();
+    }
+
+    // Properly shutdown all coordinators started either by this coordinator or
+    // by the scene controller. This should include Quick Delete, History and
+    // the Privacy Settings.
+    [weakSelf clearPresentedStateWithCompletion:nil dismissOmnibox:YES];
+    [applicationCommandsHandler dismissModalDialogsWithCompletion:nil];
+  };
+  [self.viewController dismissViewControllerAnimated:YES
+                                          completion:dismissalCompletion];
+}
+
 #pragma mark - WhatsNewCommands
 
 - (void)showWhatsNew {
diff --git a/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.h b/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.h
index d70faf95..1c50df5 100644
--- a/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.h
+++ b/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.h
@@ -29,6 +29,13 @@
       PrefService* pref_service,
       UpdateUICallback update_ui_callback);
 
+  static std::unique_ptr<BrowsingDataCounterWrapper> CreateCounterWrapper(
+      std::string_view pref_name,
+      ChromeBrowserState* browser_state,
+      PrefService* pref_service,
+      base::Time begin_time,
+      UpdateUICallback update_ui_callback);
+
   BrowsingDataCounterWrapper(const BrowsingDataCounterWrapper&) = delete;
   BrowsingDataCounterWrapper& operator=(const BrowsingDataCounterWrapper&) =
       delete;
@@ -37,12 +44,20 @@
 
   void RestartCounter();
 
+  void SetBeginTime(base::Time beginTime);
+
  private:
   BrowsingDataCounterWrapper(
       std::unique_ptr<browsing_data::BrowsingDataCounter> counter,
       PrefService* pref_service,
       UpdateUICallback update_ui_callback);
 
+  BrowsingDataCounterWrapper(
+      std::unique_ptr<browsing_data::BrowsingDataCounter> counter,
+      PrefService* pref_service,
+      base::Time begin_time,
+      UpdateUICallback update_ui_callback);
+
   // Method to be passed as callback to the counter. This will be invoked when
   // the result is ready.
   void UpdateWithResult(
diff --git a/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.mm b/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.mm
index 8dea0455..39d2857d 100644
--- a/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.mm
+++ b/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.mm
@@ -99,12 +99,36 @@
                                      std::move(update_ui_callback)));
 }
 
+// static
+std::unique_ptr<BrowsingDataCounterWrapper>
+BrowsingDataCounterWrapper::CreateCounterWrapper(
+    std::string_view pref_name,
+    ChromeBrowserState* browser_state,
+    PrefService* pref_service,
+    base::Time begin_time,
+    UpdateUICallback update_ui_callback) {
+  std::unique_ptr<browsing_data::BrowsingDataCounter> counter =
+      CreateCounterForBrowserStateAndPref(browser_state, pref_name);
+  if (!counter) {
+    return nullptr;
+  }
+
+  return base::WrapUnique<BrowsingDataCounterWrapper>(
+      new BrowsingDataCounterWrapper(std::move(counter), pref_service,
+                                     begin_time,
+                                     std::move(update_ui_callback)));
+}
+
 BrowsingDataCounterWrapper::~BrowsingDataCounterWrapper() = default;
 
 void BrowsingDataCounterWrapper::RestartCounter() {
   counter_->Restart();
 }
 
+void BrowsingDataCounterWrapper::SetBeginTime(base::Time beginTime) {
+  counter_->SetBeginTime(beginTime);
+}
+
 BrowsingDataCounterWrapper::BrowsingDataCounterWrapper(
     std::unique_ptr<browsing_data::BrowsingDataCounter> counter,
     PrefService* pref_service,
@@ -119,6 +143,21 @@
                           base::Unretained(this)));
 }
 
+BrowsingDataCounterWrapper::BrowsingDataCounterWrapper(
+    std::unique_ptr<browsing_data::BrowsingDataCounter> counter,
+    PrefService* pref_service,
+    base::Time begin_time,
+    UpdateUICallback update_ui_callback)
+    : counter_(std::move(counter)),
+      update_ui_callback_(std::move(update_ui_callback)) {
+  DCHECK(counter_);
+  DCHECK(update_ui_callback_);
+  counter_->InitWithoutPeriodPref(
+      pref_service, browsing_data::ClearBrowsingDataTab::ADVANCED, begin_time,
+      base::BindRepeating(&BrowsingDataCounterWrapper::UpdateWithResult,
+                          base::Unretained(this)));
+}
+
 void BrowsingDataCounterWrapper::UpdateWithResult(
     std::unique_ptr<browsing_data::BrowsingDataCounter::Result> result) {
   DCHECK(result);
diff --git a/ios/chrome/browser/browsing_data/model/browsing_data_remover.h b/ios/chrome/browser/browsing_data/model/browsing_data_remover.h
index 189421b..1553446 100644
--- a/ios/chrome/browser/browsing_data/model/browsing_data_remover.h
+++ b/ios/chrome/browser/browsing_data/model/browsing_data_remover.h
@@ -34,6 +34,15 @@
     kForceReload,
   };
 
+  // If kAutomatic, BrowsingDataRemover decides if the active tab should stay
+  // open. Otherwise, forces the active tab to close or not when using
+  // BrowsingDataRemoverMask::CLOSE_TABS.
+  enum class KeepActiveTabPolicy {
+    kAutomatic,
+    kKeepActiveTab,
+    kCloseActiveTab,
+  };
+
   // Parameters for removing browsing data.
   struct RemovalParams {
     // Returns RemovalParams with default values.
@@ -49,6 +58,10 @@
     // deletion has completed, or if BrowsingDataRemover decides if reloading is
     // necessary.
     WebStatesReloadPolicy reload_web_states = WebStatesReloadPolicy::kAutomatic;
+
+    // Indicates if BrowsingDataRemoverMask::CLOSE_TABS is allowed to close
+    // the active tab or not.
+    KeepActiveTabPolicy keep_active_tab = KeepActiveTabPolicy::kAutomatic;
   };
 
   BrowsingDataRemover();
diff --git a/ios/chrome/browser/browsing_data/model/browsing_data_remover_impl.h b/ios/chrome/browser/browsing_data/model/browsing_data_remover_impl.h
index 794029d..a3e03c4 100644
--- a/ios/chrome/browser/browsing_data/model/browsing_data_remover_impl.h
+++ b/ios/chrome/browser/browsing_data/model/browsing_data_remover_impl.h
@@ -105,7 +105,8 @@
   // Removes the specified items related to browsing.
   void RemoveImpl(base::Time delete_begin,
                   base::Time delete_end,
-                  BrowsingDataRemoveMask mask);
+                  BrowsingDataRemoveMask mask,
+                  RemovalParams params);
 
   // Removes the browsing data stored in WKWebsiteDataStore if needed.
   void RemoveDataFromWKWebsiteDataStore(base::Time delete_begin,
@@ -116,13 +117,15 @@
   // close and closes them. Otherwise, fetches the relevant information from
   // persisted storage first.
   void MaybeFetchTabsInfoThenCloseTabs(base::Time delete_begin,
-                                       base::Time delete_end);
+                                       base::Time delete_end,
+                                       RemovalParams params);
 
   // Called when the information about tabs from a single browser has been
   // loaded from persisted storage. Closes tabs from that browser.
   void OnTabsInformationLoaded(base::WeakPtr<Browser> weak_browser,
                                base::Time delete_begin,
                                base::Time delete_end,
+                               RemovalParams params,
                                base::OnceClosure callback,
                                tabs_closure_util::WebStateIDToTime result);
 
diff --git a/ios/chrome/browser/browsing_data/model/browsing_data_remover_impl.mm b/ios/chrome/browser/browsing_data/model/browsing_data_remover_impl.mm
index 90efb5c..ea53cb19 100644
--- a/ios/chrome/browser/browsing_data/model/browsing_data_remover_impl.mm
+++ b/ios/chrome/browser/browsing_data/model/browsing_data_remover_impl.mm
@@ -180,12 +180,16 @@
 void CloseTabsHelper(base::WeakPtr<Browser> browser,
                      base::Time delete_begin,
                      base::Time delete_end,
-                     const tabs_closure_util::WebStateIDToTime& tabs_info) {
+                     const tabs_closure_util::WebStateIDToTime& tabs_info,
+                     BrowsingDataRemover::RemovalParams params) {
   if (!browser) {
     return;
   }
+  bool keep_active_tab =
+      params.keep_active_tab ==
+      BrowsingDataRemover::KeepActiveTabPolicy::kKeepActiveTab;
   tabs_closure_util::CloseTabs(browser->GetWebStateList(), delete_begin,
-                               delete_end, tabs_info);
+                               delete_end, tabs_info, keep_active_tab);
 }
 
 }  // namespace
@@ -369,7 +373,7 @@
 
   PrepareForRemoval(removal_task.mask, removal_task.params);
   RemoveImpl(removal_task.delete_begin, removal_task.delete_end,
-             removal_task.mask);
+             removal_task.mask, removal_task.params);
 }
 
 void BrowsingDataRemoverImpl::PrepareForRemoval(BrowsingDataRemoveMask mask,
@@ -442,7 +446,8 @@
 
 void BrowsingDataRemoverImpl::RemoveImpl(base::Time delete_begin,
                                          base::Time delete_end,
-                                         BrowsingDataRemoveMask mask) {
+                                         BrowsingDataRemoveMask mask,
+                                         RemovalParams params) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::ScopedClosureRunner synchronous_clear_operations(
       CreatePendingTaskCompletionClosure());
@@ -460,7 +465,7 @@
 
     // Remove all HTTPS-Only Mode allowlist decisions.
     HttpsUpgradeService* https_upgrade_service =
-        HttpsUpgradeServiceFactory::GetForBrowserState(browser_state_);
+        HttpsUpgradeServiceFactory::GetForProfile(browser_state_);
     https_upgrade_service->ClearAllowlist(delete_begin, delete_end);
   }
 
@@ -706,7 +711,7 @@
   // Close tabs.
   if (IsRemoveDataMaskSet(mask, BrowsingDataRemoveMask::CLOSE_TABS)) {
     base::RecordAction(base::UserMetricsAction("ClearBrowsingData_Tabs"));
-    MaybeFetchTabsInfoThenCloseTabs(delete_begin, delete_end);
+    MaybeFetchTabsInfoThenCloseTabs(delete_begin, delete_end, params);
   }
 
   // Always wipe accumulated network related data (TransportSecurityState and
@@ -780,7 +785,8 @@
 
 void BrowsingDataRemoverImpl::MaybeFetchTabsInfoThenCloseTabs(
     base::Time delete_begin,
-    base::Time delete_end) {
+    base::Time delete_end,
+    RemovalParams params) {
   BrowserList* browser_list =
       BrowserListFactory::GetForBrowserState(browser_state_);
   scoped_refptr<base::SequencedTaskRunner> current_task_runner =
@@ -793,7 +799,7 @@
       current_task_runner->PostTaskAndReply(
           FROM_HERE,
           base::BindOnce(&CloseTabsHelper, browser->AsWeakPtr(), delete_begin,
-                         delete_end, cached_tabs_info_),
+                         delete_end, cached_tabs_info_, params),
           CreatePendingTaskCompletionClosure());
     } else {
       service->LoadDataFromStorage(
@@ -802,7 +808,8 @@
               &tabs_closure_util::GetLastCommittedTimestampFromStorage),
           base::BindOnce(&BrowsingDataRemoverImpl::OnTabsInformationLoaded,
                          GetWeakPtr(), browser->AsWeakPtr(), delete_begin,
-                         delete_end, CreatePendingTaskCompletionClosure()));
+                         delete_end, params,
+                         CreatePendingTaskCompletionClosure()));
     }
   }
 }
@@ -811,6 +818,7 @@
     base::WeakPtr<Browser> weak_browser,
     base::Time delete_begin,
     base::Time delete_end,
+    RemovalParams params,
     base::OnceClosure callback,
     tabs_closure_util::WebStateIDToTime result) {
   Browser* browser = weak_browser.get();
@@ -818,8 +826,10 @@
     tabs_closure_util::WebStateIDToTime tabs_info =
         tabs_closure_util::GetTabsInfoForCache(result, delete_begin,
                                                delete_end);
+    bool keep_active_tab =
+        params.keep_active_tab == KeepActiveTabPolicy::kKeepActiveTab;
     tabs_closure_util::CloseTabs(browser->GetWebStateList(), delete_begin,
-                                 delete_end, tabs_info);
+                                 delete_end, tabs_info, keep_active_tab);
   }
   std::move(callback).Run();
 }
diff --git a/ios/chrome/browser/browsing_data/model/tabs_closure_util.h b/ios/chrome/browser/browsing_data/model/tabs_closure_util.h
index fcd0860..0e5f9f3 100644
--- a/ios/chrome/browser/browsing_data/model/tabs_closure_util.h
+++ b/ios/chrome/browser/browsing_data/model/tabs_closure_util.h
@@ -55,7 +55,8 @@
 void CloseTabs(WebStateList* web_state_list,
                base::Time begin_time,
                base::Time end_time,
-               const WebStateIDToTime& cached_tabs_to_close);
+               const WebStateIDToTime& cached_tabs_to_close,
+               bool keep_active_tab);
 
 }  // namespace tabs_closure_util
 
diff --git a/ios/chrome/browser/browsing_data/model/tabs_closure_util.mm b/ios/chrome/browser/browsing_data/model/tabs_closure_util.mm
index f9964b6..6dd78a96 100644
--- a/ios/chrome/browser/browsing_data/model/tabs_closure_util.mm
+++ b/ios/chrome/browser/browsing_data/model/tabs_closure_util.mm
@@ -138,7 +138,8 @@
 void CloseTabs(WebStateList* web_state_list,
                base::Time begin_time,
                base::Time end_time,
-               const WebStateIDToTime& cached_tabs_to_close) {
+               const WebStateIDToTime& cached_tabs_to_close,
+               bool keep_active_tab) {
   CHECK(web_state_list);
 
   std::set<web::WebStateID> web_state_ids_to_close = GetTabsToClose(
@@ -153,6 +154,10 @@
   for (int index = web_state_list->pinned_tabs_count();
        index < web_state_list->count(); ++index) {
     web::WebState* web_state = web_state_list->GetWebStateAt(index);
+    if (keep_active_tab && index == web_state_list->active_index()) {
+      continue;
+    }
+
     if (web_state_ids_to_close.contains(web_state->GetUniqueIdentifier())) {
       indices_to_close.push_back(index);
     }
diff --git a/ios/chrome/browser/browsing_data/model/tabs_closure_util_unittest.mm b/ios/chrome/browser/browsing_data/model/tabs_closure_util_unittest.mm
index 502b9b68..7829bdd 100644
--- a/ios/chrome/browser/browsing_data/model/tabs_closure_util_unittest.mm
+++ b/ios/chrome/browser/browsing_data/model/tabs_closure_util_unittest.mm
@@ -202,11 +202,31 @@
   WebStateIDToTime tabs =
       AppendUnrealizedWebstates(end_time - base::Minutes(1));
 
-  CloseTabs(web_state_list, begin_time, end_time, tabs);
+  CloseTabs(web_state_list, begin_time, end_time, tabs,
+            /*keep_active_tab=*/false);
 
   EXPECT_TRUE(web_state_list->empty());
 }
 
+// Tests that `CloseTabs` correctly closes the tabs within the time range, in
+// this case, all tabs associated with the browser which are unrealized.
+TEST_F(TabsClosureUtilTest, CloseTabs_KeepActiveTab) {
+  WebStateList* web_state_list = browser()->GetWebStateList();
+  base::Time end_time = base::Time::Now();
+  base::Time begin_time = end_time - base::Hours(1);
+
+  WebStateIDToTime tabs =
+      AppendUnrealizedWebstates(end_time - base::Minutes(1));
+  web::WebState* web_state = web_state_list->GetWebStateAt(0);
+  web_state_list->ActivateWebStateAt(0);
+
+  CloseTabs(web_state_list, begin_time, end_time, tabs,
+            /*keep_active_tab=*/true);
+
+  EXPECT_EQ(web_state_list->count(), 1);
+  EXPECT_EQ(web_state_list->GetWebStateAt(0), web_state);
+}
+
 // Tests that `GetTabsToClose` correctly return the tabs within the time range,
 // in this case, all tabs associated with the browser which are unrealized.
 TEST_F(TabsClosureUtilTest, GetTabsToClose_RemoveAllTabs) {
@@ -305,7 +325,8 @@
 
   // The unrelized webstates are passed direcly. The realized webstates will be
   // checked directly.
-  CloseTabs(web_state_list, begin_time, end_time, tabs);
+  CloseTabs(web_state_list, begin_time, end_time, tabs,
+            /*keep_active_tab=*/false);
 
   EXPECT_TRUE(web_state_list->empty());
 }
@@ -387,7 +408,8 @@
       AppendUnrealizedWebstates(end_time - base::Minutes(1));
 
   CloseTabs(web_state_list, begin_time, end_time,
-            {{tabs.begin()->first, tabs.begin()->second}});
+            {{tabs.begin()->first, tabs.begin()->second}},
+            /*keep_active_tab=*/false);
 
   EXPECT_EQ(web_state_list->count(), 1);
   EXPECT_EQ(web_state_list->GetWebStateAt(0), web_state1());
@@ -470,7 +492,8 @@
   AppendUnrealizedWebstates(end_time - base::Minutes(1));
 
   CloseTabs(web_state_list, begin_time, end_time,
-            {{web::WebStateID::NewUnique(), end_time - base::Minutes(1)}});
+            {{web::WebStateID::NewUnique(), end_time - base::Minutes(1)}},
+            /*keep_active_tab=*/false);
 
   EXPECT_EQ(web_state_list->count(), 2);
   EXPECT_EQ(web_state_list->GetWebStateAt(0), web_state0());
@@ -526,7 +549,8 @@
       AppendWebState(/*realized=*/false, end_time - base::Minutes(1)));
   webstate->SetLastActiveTime(end_time - base::Minutes(1));
 
-  CloseTabs(web_state_list, begin_time, end_time, {});
+  CloseTabs(web_state_list, begin_time, end_time, {},
+            /*keep_active_tab=*/false);
 
   EXPECT_TRUE(web_state_list->empty());
 }
@@ -562,7 +586,8 @@
       /*realized=*/false, last_navigation_time, /*pinned=*/true));
 
   CloseTabs(web_state_list, begin_time, end_time,
-            {{webstate->GetUniqueIdentifier(), last_navigation_time}});
+            {{webstate->GetUniqueIdentifier(), last_navigation_time}},
+            /*keep_active_tab=*/false);
 
   EXPECT_EQ(web_state_list->count(), 1);
 }
@@ -597,7 +622,8 @@
       AppendWebState(/*realized=*/true, last_navigation_time, /*pinned=*/true));
 
   CloseTabs(web_state_list, begin_time, end_time,
-            {{webstate->GetUniqueIdentifier(), last_navigation_time}});
+            {{webstate->GetUniqueIdentifier(), last_navigation_time}},
+            /*keep_active_tab=*/false);
 
   EXPECT_EQ(web_state_list->count(), 1);
 }
diff --git a/ios/chrome/browser/external_files/model/external_file_remover_factory.mm b/ios/chrome/browser/external_files/model/external_file_remover_factory.mm
index 98f17d0..039682f 100644
--- a/ios/chrome/browser/external_files/model/external_file_remover_factory.mm
+++ b/ios/chrome/browser/external_files/model/external_file_remover_factory.mm
@@ -44,9 +44,7 @@
 std::unique_ptr<KeyedService>
 ExternalFileRemoverFactory::BuildServiceInstanceFor(
     web::BrowserState* context) const {
-  ChromeBrowserState* browser_state =
-      ChromeBrowserState::FromBrowserState(context);
+  ProfileIOS* profile = ProfileIOS::FromBrowserState(context);
   return std::make_unique<ExternalFileRemoverImpl>(
-      browser_state,
-      IOSChromeTabRestoreServiceFactory::GetForBrowserState(browser_state));
+      profile, IOSChromeTabRestoreServiceFactory::GetForProfile(profile));
 }
diff --git a/ios/chrome/browser/external_files/model/external_file_remover_impl.h b/ios/chrome/browser/external_files/model/external_file_remover_impl.h
index edef60f32..c48c1fb7 100644
--- a/ios/chrome/browser/external_files/model/external_file_remover_impl.h
+++ b/ios/chrome/browser/external_files/model/external_file_remover_impl.h
@@ -27,7 +27,7 @@
   // Creates an ExternalFileRemoverImpl to remove external documents not
   // referenced by the specified BrowserViewController. Use Remove to initiate
   // the removal.
-  ExternalFileRemoverImpl(ChromeBrowserState* browser_state,
+  ExternalFileRemoverImpl(ProfileIOS* profile,
                           sessions::TabRestoreService* tab_restore_service);
 
   ExternalFileRemoverImpl(const ExternalFileRemoverImpl&) = delete;
@@ -68,9 +68,9 @@
   std::vector<DelayedFileRemoveRequest> delayed_file_remove_requests_;
   // Pointer to the tab restore service.
   raw_ptr<sessions::TabRestoreService> tab_restore_service_ = nullptr;
-  // ChromeBrowserState used to get the referenced files. Must outlive this
+  // ProfileIOS used to get the referenced files. Must outlive this
   // object.
-  raw_ptr<ChromeBrowserState> browser_state_ = nullptr;
+  raw_ptr<ProfileIOS> profile_ = nullptr;
   // Used to ensure that this class' methods are called on the correct sequence.
   SEQUENCE_CHECKER(sequence_checker_);
   // Used to ensure `Remove()` is not run when this object is destroyed.
diff --git a/ios/chrome/browser/external_files/model/external_file_remover_impl.mm b/ios/chrome/browser/external_files/model/external_file_remover_impl.mm
index ec064e6..693a730 100644
--- a/ios/chrome/browser/external_files/model/external_file_remover_impl.mm
+++ b/ios/chrome/browser/external_files/model/external_file_remover_impl.mm
@@ -73,8 +73,7 @@
   }
   // Do the same for the recently closed tabs.
   sessions::TabRestoreService* restore_service =
-      IOSChromeTabRestoreServiceFactory::GetForBrowserState(
-          browser->GetBrowserState());
+      IOSChromeTabRestoreServiceFactory::GetForProfile(browser->GetProfile());
   DCHECK(restore_service);
   for (const auto& entry : restore_service->entries()) {
     sessions::tab_restore::Tab* tab =
@@ -147,10 +146,10 @@
 }  // namespace
 
 ExternalFileRemoverImpl::ExternalFileRemoverImpl(
-    ChromeBrowserState* browser_state,
+    ProfileIOS* profile,
     sessions::TabRestoreService* tab_restore_service)
     : tab_restore_service_(tab_restore_service),
-      browser_state_(browser_state),
+      profile_(profile),
       weak_ptr_factory_(this) {
   DCHECK(tab_restore_service_);
   tab_restore_service_->AddObserver(this);
@@ -240,10 +239,9 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Add files from all Browsers.
   NSMutableSet* referenced_external_files = [NSMutableSet set];
-  BrowserList* browser_list =
-      BrowserListFactory::GetForBrowserState(browser_state_);
+  BrowserList* browser_list = BrowserListFactory::GetForProfile(profile_);
   const BrowserList::BrowserType browser_types =
-      browser_state_->IsOffTheRecord()
+      profile_->IsOffTheRecord()
           ? BrowserList::BrowserType::kIncognito
           : BrowserList::BrowserType::kRegularAndInactive;
   std::set<Browser*> browsers = browser_list->BrowsersOfType(browser_types);
@@ -255,7 +253,7 @@
   }
 
   bookmarks::BookmarkModel* bookmark_model =
-      ios::BookmarkModelFactory::GetForBrowserState(browser_state_);
+      ios::BookmarkModelFactory::GetForProfile(profile_);
   // Check if the bookmark model is loaded.
   if (!bookmark_model || !bookmark_model->loaded())
     return referenced_external_files;
diff --git a/ios/chrome/browser/favicon/model/favicon_browser_agent.mm b/ios/chrome/browser/favicon/model/favicon_browser_agent.mm
index 6816bc0..8a60fa9 100644
--- a/ios/chrome/browser/favicon/model/favicon_browser_agent.mm
+++ b/ios/chrome/browser/favicon/model/favicon_browser_agent.mm
@@ -21,9 +21,9 @@
       << "PagePlaceholderBrowserAgent created for a Browser with a non-empty "
          "WebStateList.";
 
-  ChromeBrowserState* browser_state = browser_->GetBrowserState();
+  ProfileIOS* profile = browser_->GetProfile();
   session_restoration_service_observation_.Observe(
-      SessionRestorationServiceFactory::GetForBrowserState(browser_state));
+      SessionRestorationServiceFactory::GetForProfile(profile));
 }
 
 FaviconBrowserAgent::~FaviconBrowserAgent() {
diff --git a/ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.mm b/ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.mm
index d8bb45c..330f9c1 100644
--- a/ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.mm
+++ b/ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.mm
@@ -15,10 +15,9 @@
 namespace {
 
 std::unique_ptr<KeyedService> BuildFaviconLoader(web::BrowserState* context) {
-  ChromeBrowserState* browser_state =
-      ChromeBrowserState::FromBrowserState(context);
+  ProfileIOS* profile = ProfileIOS::FromBrowserState(context);
   return std::make_unique<FaviconLoader>(
-      IOSChromeLargeIconServiceFactory::GetForBrowserState(browser_state));
+      IOSChromeLargeIconServiceFactory::GetForProfile(profile));
 }
 
 }  // namespace
diff --git a/ios/chrome/browser/favicon/model/ios_chrome_large_icon_cache_factory.h b/ios/chrome/browser/favicon/model/ios_chrome_large_icon_cache_factory.h
index efa77d7..5937b2d 100644
--- a/ios/chrome/browser/favicon/model/ios_chrome_large_icon_cache_factory.h
+++ b/ios/chrome/browser/favicon/model/ios_chrome_large_icon_cache_factory.h
@@ -15,11 +15,11 @@
 class LargeIconCache;
 
 // Singleton that owns all LargeIconCaches and associates them with
-// ChromeBrowserState.
+// ProfileIOS.
 class IOSChromeLargeIconCacheFactory : public BrowserStateKeyedServiceFactory {
  public:
   // TODO(crbug.com/358301380): remove this method.
-  static LargeIconCache* GetForBrowserState(ChromeBrowserState* browser_state);
+  static LargeIconCache* GetForBrowserState(ProfileIOS* profile);
 
   static LargeIconCache* GetForProfile(ProfileIOS* profile);
   static IOSChromeLargeIconCacheFactory* GetInstance();
diff --git a/ios/chrome/browser/favicon/model/ios_chrome_large_icon_service_factory.cc b/ios/chrome/browser/favicon/model/ios_chrome_large_icon_service_factory.cc
index efabbdff..b16cfed 100644
--- a/ios/chrome/browser/favicon/model/ios_chrome_large_icon_service_factory.cc
+++ b/ios/chrome/browser/favicon/model/ios_chrome_large_icon_service_factory.cc
@@ -25,14 +25,13 @@
 
 std::unique_ptr<KeyedService> BuildLargeIconService(
     web::BrowserState* context) {
-  ChromeBrowserState* browser_state =
-      ChromeBrowserState::FromBrowserState(context);
+  ProfileIOS* profile = ProfileIOS::FromBrowserState(context);
   return std::make_unique<favicon::LargeIconServiceImpl>(
-      ios::FaviconServiceFactory::GetForBrowserState(
-          browser_state, ServiceAccessType::EXPLICIT_ACCESS),
+      ios::FaviconServiceFactory::GetForProfile(
+          profile, ServiceAccessType::EXPLICIT_ACCESS),
       std::make_unique<image_fetcher::ImageFetcherImpl>(
           image_fetcher::CreateIOSImageDecoder(),
-          browser_state->GetSharedURLLoaderFactory()),
+          profile->GetSharedURLLoaderFactory()),
       kDipForServerRequests, kIconTypeForServerRequests,
       kGoogleServerClientParam);
 }
diff --git a/ios/chrome/browser/favicon/model/large_icon_cache.h b/ios/chrome/browser/favicon/model/large_icon_cache.h
index 8132680..9520a6df 100644
--- a/ios/chrome/browser/favicon/model/large_icon_cache.h
+++ b/ios/chrome/browser/favicon/model/large_icon_cache.h
@@ -21,7 +21,7 @@
 //
 // Example usage:
 //   LargeIconCache* large_icon_cache =
-//       IOSChromeLargeIconServiceFactory::GetForBrowserState(browser_state);
+//       IOSChromeLargeIconServiceFactory::GetForProfile(profile);
 //   std::unique_ptr<favicon_base::LargeIconResult> icon =
 //       large_icon_cache->GetCachedResult(...);
 //
diff --git a/ios/chrome/browser/feature_engagement/model/tracker_factory.h b/ios/chrome/browser/feature_engagement/model/tracker_factory.h
index a41c7f38..79533a5 100644
--- a/ios/chrome/browser/feature_engagement/model/tracker_factory.h
+++ b/ios/chrome/browser/feature_engagement/model/tracker_factory.h
@@ -19,7 +19,7 @@
 class TrackerFactory : public BrowserStateKeyedServiceFactory {
  public:
   // TODO(crbug.com/358301380): remove this method.
-  static Tracker* GetForBrowserState(ChromeBrowserState* browser_state);
+  static Tracker* GetForBrowserState(ProfileIOS* profile);
 
   static Tracker* GetForProfile(ProfileIOS* profile);
   static TrackerFactory* GetInstance();
diff --git a/ios/chrome/browser/feature_engagement/model/tracker_factory_util.mm b/ios/chrome/browser/feature_engagement/model/tracker_factory_util.mm
index a49c1b3..0e44fad 100644
--- a/ios/chrome/browser/feature_engagement/model/tracker_factory_util.mm
+++ b/ios/chrome/browser/feature_engagement/model/tracker_factory_util.mm
@@ -32,18 +32,17 @@
     return CreateDemoModeTracker(fetDemoModeOverride.value());
   }
 
-  ChromeBrowserState* browser_state =
-      ChromeBrowserState::FromBrowserState(context);
+  ProfileIOS* profile = ProfileIOS::FromBrowserState(context);
 
   scoped_refptr<base::SequencedTaskRunner> background_task_runner =
       base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
 
-  base::FilePath storage_dir = browser_state->GetStatePath().Append(
+  base::FilePath storage_dir = profile->GetStatePath().Append(
       kIOSFeatureEngagementTrackerStorageDirname);
 
   leveldb_proto::ProtoDatabaseProvider* db_provider =
-      browser_state->GetProtoDatabaseProvider();
+      profile->GetProtoDatabaseProvider();
 
   auto default_browser_event_exporter =
       std::make_unique<DefaultBrowserEventExporter>();
diff --git a/ios/chrome/browser/feature_engagement/model/tracker_util.h b/ios/chrome/browser/feature_engagement/model/tracker_util.h
index 2ab74b1..ad478685 100644
--- a/ios/chrome/browser/feature_engagement/model/tracker_util.h
+++ b/ios/chrome/browser/feature_engagement/model/tracker_util.h
@@ -12,7 +12,7 @@
 // Sends a new tab event to the feature_engagement::Tracker based on
 // `is_incognito`. If `is_incognito` is `true`, then the "Incognito Tab Opened"
 // is fired. If `is_incognito` is `false`, then the "New Tab Event" is fired.
-void NotifyNewTabEvent(ChromeBrowserState* browser_state, bool is_incognito);
+void NotifyNewTabEvent(ProfileIOS* profile, bool is_incognito);
 
 // Sends a new tab event to the feature_engagement::Tracker based on
 // `command.incognito` and `command.userInitiated`. If `command.userInitiated`
@@ -20,7 +20,7 @@
 // then one of the new tab events is fired. If `command.incognito` is `true`,
 // then the "Incognito Tab Opened" event is fired, and if `command.incognito` is
 // `false`, then the "New Tab Opened" event is fired.
-void NotifyNewTabEventForCommand(ChromeBrowserState* browser_state,
+void NotifyNewTabEventForCommand(ProfileIOS* profile,
                                  OpenNewTabCommand* command);
 }  // namespace feature_engagement
 
diff --git a/ios/chrome/browser/feature_engagement/model/tracker_util.mm b/ios/chrome/browser/feature_engagement/model/tracker_util.mm
index 012acbb..4611a367 100644
--- a/ios/chrome/browser/feature_engagement/model/tracker_util.mm
+++ b/ios/chrome/browser/feature_engagement/model/tracker_util.mm
@@ -11,18 +11,17 @@
 
 namespace feature_engagement {
 
-void NotifyNewTabEvent(ChromeBrowserState* browser_state, bool is_incognito) {
+void NotifyNewTabEvent(ProfileIOS* profile, bool is_incognito) {
   const char* const event =
       is_incognito ? feature_engagement::events::kIncognitoTabOpened
                    : feature_engagement::events::kNewTabOpened;
-  TrackerFactory::GetForBrowserState(browser_state)
-      ->NotifyEvent(std::string(event));
+  TrackerFactory::GetForProfile(profile)->NotifyEvent(std::string(event));
 }
 
-void NotifyNewTabEventForCommand(ChromeBrowserState* browser_state,
+void NotifyNewTabEventForCommand(ProfileIOS* profile,
                                  OpenNewTabCommand* command) {
   if (command.isUserInitiated) {
-    NotifyNewTabEvent(browser_state, command.inIncognito);
+    NotifyNewTabEvent(profile, command.inIncognito);
   }
 }
 
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 4ad8ad13..b46ed8b 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -1355,11 +1355,6 @@
                                     kFeedBackgroundRefreshVariations,
                                     "FeedBackgroundRefresh")},
 #endif  // BUILDFLAG(IOS_BACKGROUND_MODE_ENABLED)
-    {"enable-tflite-language-detection-ignore",
-     flag_descriptions::kTFLiteLanguageDetectionIgnoreName,
-     flag_descriptions::kTFLiteLanguageDetectionIgnoreDescription,
-     flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(translate::kTFLiteLanguageDetectionIgnoreEnabled)},
     {"tab-grid-new-transitions", flag_descriptions::kTabGridNewTransitionsName,
      flag_descriptions::kTabGridNewTransitionsDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kTabGridNewTransitions)},
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 2cc5475..0153cc3 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -1070,12 +1070,6 @@
 const char kTFLiteLanguageDetectionDescription[] =
     "Uses TFLite for language detection in place of CLD3";
 
-const char kTFLiteLanguageDetectionIgnoreName[] =
-    "Ignore TFLite-based Language Detection";
-const char kTFLiteLanguageDetectionIgnoreDescription[] =
-    "Computes the TFLite language detection but ignore the result and uses the "
-    "CLD3 detection instead.";
-
 const char kThemeColorInTopToolbarName[] = "Top toolbar use page's theme color";
 const char kThemeColorInTopToolbarDescription[] =
     "When enabled with bottom omnibox, the top toolbar background color is the "
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index fc855372..7d81e4c7 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -956,11 +956,6 @@
 extern const char kTFLiteLanguageDetectionName[];
 extern const char kTFLiteLanguageDetectionDescription[];
 
-// Title and description for the flag to compute both TFLite and CLD3 detection
-// and ignore TFLite one.
-extern const char kTFLiteLanguageDetectionIgnoreName[];
-extern const char kTFLiteLanguageDetectionIgnoreDescription[];
-
 // Title and description for the flag to use the page's theme color in the
 // top toolbar.
 extern const char kThemeColorInTopToolbarName[];
diff --git a/ios/chrome/browser/history/ui_bundled/history_table_view_controller.mm b/ios/chrome/browser/history/ui_bundled/history_table_view_controller.mm
index 94515663..2953c63 100644
--- a/ios/chrome/browser/history/ui_bundled/history_table_view_controller.mm
+++ b/ios/chrome/browser/history/ui_bundled/history_table_view_controller.mm
@@ -64,6 +64,7 @@
 #import "ios/web/public/navigation/navigation_manager.h"
 #import "ios/web/public/navigation/referrer.h"
 #import "ios/web/public/web_state.h"
+#import "ui/base/device_form_factor.h"
 #import "ui/base/l10n/l10n_util.h"
 #import "ui/base/l10n/l10n_util_mac.h"
 #import "ui/strings/grit/ui_strings.h"
@@ -530,7 +531,9 @@
     }
     id<QuickDeleteCommands> quickDeleteHandler = HandlerForProtocol(
         self.browser->GetCommandDispatcher(), QuickDeleteCommands);
-    [quickDeleteHandler showQuickDeleteAndCanPerformTabsClosureAnimation:NO];
+    [quickDeleteHandler
+        showQuickDeleteAndCanPerformTabsClosureAnimation:
+            ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET];
     return;
   }
 
diff --git a/ios/chrome/browser/history/ui_bundled/history_ui_egtest.mm b/ios/chrome/browser/history/ui_bundled/history_ui_egtest.mm
index 70ae164..9738c99f 100644
--- a/ios/chrome/browser/history/ui_bundled/history_ui_egtest.mm
+++ b/ios/chrome/browser/history/ui_bundled/history_ui_egtest.mm
@@ -11,6 +11,7 @@
 #import "base/strings/utf_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #import "components/browsing_data/core/browsing_data_utils.h"
+#import "components/browsing_data/core/pref_names.h"
 #import "components/sync/base/command_line_switches.h"
 #import "components/url_formatter/elide_url.h"
 #import "ios/chrome/browser/history/ui_bundled/history_ui_constants.h"
@@ -860,6 +861,12 @@
   // Reset all prefs, so they're at their default value.
   [ChromeEarlGrey resetBrowsingDataPrefs];
 
+  // Disable closing tabs as it's on by default in delete browsing data, so the
+  // tab closure animation is not run in iPads. This is needed so the history UI
+  // is not closed due to the animation.
+  [ChromeEarlGrey setBoolValue:false
+                   forUserPref:browsing_data::prefs::kCloseTabs];
+
   // Create an history entry that took place one day ago.
   const base::Time oneDayAgo = base::Time::Now() - base::Days(1);
   [ChromeEarlGrey addHistoryServiceTypedURL:olderURL visitTimestamp:oneDayAgo];
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator.mm b/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator.mm
index ecfca35..595f8c0 100644
--- a/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator.mm
+++ b/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator.mm
@@ -59,8 +59,8 @@
 
 - (void)start {
   _mediator = [[HomeCustomizationMediator alloc]
-      initWithPrefService:ChromeBrowserState::FromBrowserState(
-                              self.browser->GetBrowserState())
+      initWithPrefService:ProfileIOS::FromBrowserState(
+                              self.browser->GetProfile())
                               ->GetPrefs()];
   _mediator.navigationDelegate = self;
 
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator_unittest.mm b/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator_unittest.mm
index 3dd214a6..6dbfdc0 100644
--- a/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator_unittest.mm
+++ b/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator_unittest.mm
@@ -26,8 +26,8 @@
   void SetUp() override {
     scoped_feature_list_.InitWithFeatures({kHomeCustomization}, {});
 
-    browser_state_ = TestChromeBrowserState::Builder().Build();
-    browser_ = std::make_unique<TestBrowser>(browser_state_.get());
+    profile_ = TestProfileIOS::Builder().Build();
+    browser_ = std::make_unique<TestBrowser>(profile_.get());
     base_view_controller_ = [[UIViewController alloc] init];
 
     coordinator_ = [[HomeCustomizationCoordinator alloc]
@@ -39,7 +39,7 @@
   web::WebTaskEnvironment task_environment_;
   base::test::ScopedFeatureList scoped_feature_list_;
   HomeCustomizationCoordinator* coordinator_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
   std::unique_ptr<TestBrowser> browser_;
   UIViewController* base_view_controller_;
 };
diff --git a/ios/chrome/browser/https_upgrades/model/https_only_mode_upgrade_tab_helper_unittest.mm b/ios/chrome/browser/https_upgrades/model/https_only_mode_upgrade_tab_helper_unittest.mm
index ff061341e..ed616e4 100644
--- a/ios/chrome/browser/https_upgrades/model/https_only_mode_upgrade_tab_helper_unittest.mm
+++ b/ios/chrome/browser/https_upgrades/model/https_only_mode_upgrade_tab_helper_unittest.mm
@@ -56,15 +56,15 @@
     : public testing::TestWithParam<HttpsUpgradesTestType> {
  protected:
   HttpsOnlyModeUpgradeTabHelperTest() {
-    TestChromeBrowserState::Builder builder;
+    TestProfileIOS::Builder builder;
     builder.AddTestingFactory(PrerenderServiceFactory::GetInstance(),
                               base::BindRepeating(&BuildFakePrerenderService));
     builder.AddTestingFactory(
         HttpsUpgradeServiceFactory::GetInstance(),
         base::BindRepeating(&BuildFakeHttpsUpgradeService));
 
-    browser_state_ = std::move(builder).Build();
-    web_state_.SetBrowserState(browser_state_.get());
+    profile_ = std::move(builder).Build();
+    web_state_.SetBrowserState(profile_.get());
 
     switch (GetParam()) {
       case HttpsUpgradesTestType::kNone:
@@ -76,8 +76,7 @@
         break;
 
       case HttpsUpgradesTestType::kHttpsOnlyMode:
-        browser_state_->GetPrefs()->SetBoolean(prefs::kHttpsOnlyModeEnabled,
-                                               true);
+        profile_->GetPrefs()->SetBoolean(prefs::kHttpsOnlyModeEnabled, true);
         scoped_feature_list_.InitWithFeatures(
             /*enabled_features=*/{security_interstitials::features::
                                       kHttpsOnlyMode},
@@ -94,8 +93,7 @@
         break;
 
       case HttpsUpgradesTestType::kBoth:
-        browser_state_->GetPrefs()->SetBoolean(prefs::kHttpsOnlyModeEnabled,
-                                               true);
+        profile_->GetPrefs()->SetBoolean(prefs::kHttpsOnlyModeEnabled, true);
         scoped_feature_list_
             .InitWithFeatures(/*enabled_features=*/
                               {security_interstitials::features::kHttpsOnlyMode,
@@ -106,9 +104,9 @@
     }
 
     HttpsOnlyModeUpgradeTabHelper::CreateForWebState(
-        &web_state_, browser_state_->GetPrefs(),
-        PrerenderServiceFactory::GetForProfile(browser_state_.get()),
-        HttpsUpgradeServiceFactory::GetForProfile(browser_state_.get()));
+        &web_state_, profile_->GetPrefs(),
+        PrerenderServiceFactory::GetForProfile(profile_.get()),
+        HttpsUpgradeServiceFactory::GetForProfile(profile_.get()));
     HttpsOnlyModeContainer::CreateForWebState(&web_state_);
   }
 
@@ -150,7 +148,7 @@
   web::FakeWebState web_state_;
 
  private:
-  std::unique_ptr<ChromeBrowserState> browser_state_;
+  std::unique_ptr<ProfileIOS> profile_;
   base::test::TaskEnvironment task_environment_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
diff --git a/ios/chrome/browser/https_upgrades/model/https_upgrade_app_interface.mm b/ios/chrome/browser/https_upgrades/model/https_upgrade_app_interface.mm
index 4865d84..32b1444 100644
--- a/ios/chrome/browser/https_upgrades/model/https_upgrade_app_interface.mm
+++ b/ios/chrome/browser/https_upgrades/model/https_upgrade_app_interface.mm
@@ -18,33 +18,33 @@
 @implementation HttpsUpgradeAppInterface
 
 + (void)setHTTPSPortForTesting:(int)HTTPSPort useFakeHTTPS:(bool)useFakeHTTPS {
-  HttpsUpgradeServiceFactory::GetForBrowserState(
-      chrome_test_util::GetOriginalBrowserState())
+  HttpsUpgradeServiceFactory::GetForProfile(
+      chrome_test_util::GetOriginalProfile())
       ->SetHttpsPortForTesting(HTTPSPort, useFakeHTTPS);
 
-  HttpsUpgradeServiceFactory::GetForBrowserState(
-      chrome_test_util::GetCurrentIncognitoBrowserState())
+  HttpsUpgradeServiceFactory::GetForProfile(
+      chrome_test_util::GetCurrentIncognitoProfile())
       ->SetHttpsPortForTesting(HTTPSPort, useFakeHTTPS);
 }
 
 + (void)setFallbackHttpPortForTesting:(int)HTTPPort {
-  HttpsUpgradeServiceFactory::GetForBrowserState(
-      chrome_test_util::GetOriginalBrowserState())
+  HttpsUpgradeServiceFactory::GetForProfile(
+      chrome_test_util::GetOriginalProfile())
       ->SetFallbackHttpPortForTesting(HTTPPort);
 
-  HttpsUpgradeServiceFactory::GetForBrowserState(
-      chrome_test_util::GetCurrentIncognitoBrowserState())
+  HttpsUpgradeServiceFactory::GetForProfile(
+      chrome_test_util::GetCurrentIncognitoProfile())
       ->SetFallbackHttpPortForTesting(HTTPPort);
 }
 
 + (void)setFallbackDelayForTesting:(int)fallbackDelayInMilliseconds {
-  HttpsUpgradeServiceFactory::GetForBrowserState(
-      chrome_test_util::GetOriginalBrowserState())
+  HttpsUpgradeServiceFactory::GetForProfile(
+      chrome_test_util::GetOriginalProfile())
       ->SetFallbackDelayForTesting(
           base::Milliseconds(fallbackDelayInMilliseconds));
 
-  HttpsUpgradeServiceFactory::GetForBrowserState(
-      chrome_test_util::GetCurrentIncognitoBrowserState())
+  HttpsUpgradeServiceFactory::GetForProfile(
+      chrome_test_util::GetCurrentIncognitoProfile())
       ->SetFallbackDelayForTesting(
           base::Milliseconds(fallbackDelayInMilliseconds));
 }
@@ -66,8 +66,8 @@
 + (void)clearAllowlist {
   // Clear the persistent allowlist.
   HostContentSettingsMap::PatternSourcePredicate pattern_filter;
-  ios::HostContentSettingsMapFactory::GetForBrowserState(
-      chrome_test_util::GetOriginalBrowserState())
+  ios::HostContentSettingsMapFactory::GetForProfile(
+      chrome_test_util::GetOriginalProfile())
       ->ClearSettingsForOneType(ContentSettingsType::HTTP_ALLOWED);
   // Clear the temporary allowlist for incognito.
   web::WebState* web_state = chrome_test_util::GetCurrentWebState();
diff --git a/ios/chrome/browser/https_upgrades/model/https_upgrade_service_factory.h b/ios/chrome/browser/https_upgrades/model/https_upgrade_service_factory.h
index f3ece322..f2918d1 100644
--- a/ios/chrome/browser/https_upgrades/model/https_upgrade_service_factory.h
+++ b/ios/chrome/browser/https_upgrades/model/https_upgrade_service_factory.h
@@ -17,8 +17,6 @@
 class HttpsUpgradeServiceFactory : public BrowserStateKeyedServiceFactory {
  public:
   static HttpsUpgradeService* GetForProfile(ProfileIOS* profile);
-  // Deprecated: use GetForProfile(...).
-  static HttpsUpgradeService* GetForBrowserState(ProfileIOS* profile);
   static HttpsUpgradeServiceFactory* GetInstance();
 
   HttpsUpgradeServiceFactory(const HttpsUpgradeServiceFactory&) = delete;
diff --git a/ios/chrome/browser/https_upgrades/model/https_upgrade_service_factory.mm b/ios/chrome/browser/https_upgrades/model/https_upgrade_service_factory.mm
index 410085e..4bf81fd7 100644
--- a/ios/chrome/browser/https_upgrades/model/https_upgrade_service_factory.mm
+++ b/ios/chrome/browser/https_upgrades/model/https_upgrade_service_factory.mm
@@ -19,12 +19,6 @@
 }
 
 // static
-HttpsUpgradeService* HttpsUpgradeServiceFactory::GetForBrowserState(
-    ProfileIOS* profile) {
-  return GetForProfile(profile);
-}
-
-// static
 HttpsUpgradeServiceFactory* HttpsUpgradeServiceFactory::GetInstance() {
   static base::NoDestructor<HttpsUpgradeServiceFactory> instance;
   return instance.get();
@@ -43,7 +37,7 @@
 HttpsUpgradeServiceFactory::BuildServiceInstanceFor(
     web::BrowserState* context) const {
   return std::make_unique<HttpsUpgradeServiceImpl>(
-      ChromeBrowserState::FromBrowserState(context));
+      ProfileIOS::FromBrowserState(context));
 }
 
 web::BrowserState* HttpsUpgradeServiceFactory::GetBrowserStateToUse(
diff --git a/ios/chrome/browser/https_upgrades/model/https_upgrade_service_impl.h b/ios/chrome/browser/https_upgrades/model/https_upgrade_service_impl.h
index 17b859ad..2631c23 100644
--- a/ios/chrome/browser/https_upgrades/model/https_upgrade_service_impl.h
+++ b/ios/chrome/browser/https_upgrades/model/https_upgrade_service_impl.h
@@ -20,7 +20,7 @@
 // Decisions are scoped to the host.
 class HttpsUpgradeServiceImpl : public HttpsUpgradeService {
  public:
-  explicit HttpsUpgradeServiceImpl(ChromeBrowserState* context);
+  explicit HttpsUpgradeServiceImpl(ProfileIOS* context);
   ~HttpsUpgradeServiceImpl() override;
 
   // HttpsUpgradeService methods:
@@ -30,7 +30,7 @@
 
  private:
   std::unique_ptr<base::Clock> clock_;
-  raw_ptr<ChromeBrowserState> context_;
+  raw_ptr<ProfileIOS> context_;
   security_interstitials::HttpsOnlyModeAllowlist allowlist_;
 };
 
diff --git a/ios/chrome/browser/https_upgrades/model/https_upgrade_service_impl.mm b/ios/chrome/browser/https_upgrades/model/https_upgrade_service_impl.mm
index e12da87..049dc8fb 100644
--- a/ios/chrome/browser/https_upgrades/model/https_upgrade_service_impl.mm
+++ b/ios/chrome/browser/https_upgrades/model/https_upgrade_service_impl.mm
@@ -15,13 +15,12 @@
 const base::TimeDelta kDeltaDefaultExpiration = base::Days(15);
 }  // namespace
 
-HttpsUpgradeServiceImpl::HttpsUpgradeServiceImpl(ChromeBrowserState* context)
+HttpsUpgradeServiceImpl::HttpsUpgradeServiceImpl(ProfileIOS* context)
     : clock_(new base::DefaultClock()),
       context_(context),
-      allowlist_(
-          ios::HostContentSettingsMapFactory::GetForBrowserState(context),
-          clock_.get(),
-          kDeltaDefaultExpiration) {
+      allowlist_(ios::HostContentSettingsMapFactory::GetForProfile(context),
+                 clock_.get(),
+                 kDeltaDefaultExpiration) {
   DCHECK(context_);
 }
 
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 bee252c..5e5eab4 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
@@ -27,6 +27,7 @@
 #import "ios/web/public/navigation/web_state_policy_decider_bridge.h"
 #import "ios/web/public/ui/context_menu_params.h"
 #import "ios/web/public/ui/crw_web_view_proxy.h"
+#import "ios/web/public/ui/crw_web_view_scroll_view_proxy.h"
 #import "ios/web/public/web_state.h"
 #import "ios/web/public/web_state_delegate.h"
 #import "ios/web/public/web_state_delegate_bridge.h"
@@ -146,7 +147,10 @@
   _consumer = consumer;
   CHECK(_webState, kLensOverlayNotFatalUntil);
   _webState->SetWebUsageEnabled(true);
-  [self.consumer setWebView:_webState->GetView()];
+  // Mark hidden until the first page has finished loading, preventing a
+  // momentary display of the web view's white background.
+  [_consumer setWebViewHidden:YES];
+  [_consumer setWebView:_webState->GetView()];
 }
 
 - (void)setWebStateDelegate:
@@ -181,6 +185,9 @@
   GURL latestLoadedURL = _webState->GetLastCommittedURL();
   BOOL latestURLValid = latestLoadedURL.is_valid();
   if (darkModeChanged && latestURLValid) {
+    // The web view is hidden until the page fully loads to prevent a brief
+    // flash of mixed dark and light UI elements.
+    [_consumer setWebViewHidden:YES];
     [self loadResultsURL:latestLoadedURL];
   }
 }
@@ -243,6 +250,10 @@
 
 #pragma mark - CRWWebStateObserver
 
+- (void)webState:(web::WebState*)webState didLoadPageWithSuccess:(BOOL)success {
+  [_consumer setWebViewHidden:NO];
+}
+
 - (void)webState:(web::WebState*)webState
     didFinishNavigation:(web::NavigationContext*)navigationContext {
   _isInflightRequestLensInitiated = NO;
@@ -397,7 +408,10 @@
   _policyDeciderBridge =
       std::make_unique<web::WebStatePolicyDeciderBridge>(_webState.get(), self);
   AttachTabHelpers(_webState.get(), TabHelperFilter::kBottomSheet);
-  _webState->GetWebViewProxy().allowsBackForwardNavigationGestures = NO;
+  id<CRWWebViewProxy> webViewProxy = _webState->GetWebViewProxy();
+  webViewProxy.allowsBackForwardNavigationGestures = NO;
+  // Allow the scrollView to cover the safe area.
+  webViewProxy.scrollViewProxy.clipsToBounds = NO;
 
   if (self.consumer) {
     _webState->SetWebUsageEnabled(true);
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_result_page_consumer.h b/ios/chrome/browser/lens_overlay/ui/lens_result_page_consumer.h
index 795f733..bfbef4ee 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_result_page_consumer.h
+++ b/ios/chrome/browser/lens_overlay/ui/lens_result_page_consumer.h
@@ -13,6 +13,9 @@
 /// Sets `webView` in the consumer.
 - (void)setWebView:(UIView*)webView;
 
+/// Sets the visibility of the web view.
+- (void)setWebViewHidden:(BOOL)hidden;
+
 /// Sets the loading progress.
 /// This value is bound between 0 (meaning no progress) and 1 (meaning the page
 /// has fully loaded).
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_result_page_view_controller.mm b/ios/chrome/browser/lens_overlay/ui/lens_result_page_view_controller.mm
index 16633fd0..15253ac2 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_result_page_view_controller.mm
+++ b/ios/chrome/browser/lens_overlay/ui/lens_result_page_view_controller.mm
@@ -86,6 +86,8 @@
   UIButton* _omniboxTapTarget;
   /// Loading progress bar.
   LensOverlayProgressBar* _progressBar;
+  /// Whether the web view should be hidden.
+  BOOL _webViewHidden;
 }
 
 - (instancetype)init {
@@ -112,6 +114,7 @@
   CHECK(self.webViewContainer, kLensOverlayNotFatalUntil);
   // Webview container.
   self.webViewContainer.translatesAutoresizingMaskIntoConstraints = NO;
+  self.webViewContainer.clipsToBounds = YES;
   [self.view addSubview:self.webViewContainer];
 
   // Omnibox popup container.
@@ -258,6 +261,15 @@
 }
 #endif
 
+- (void)setWebViewHidden:(BOOL)hidden {
+  if (_webViewHidden == hidden) {
+    return;
+  }
+
+  _webViewHidden = hidden;
+  _webView.hidden = hidden;
+}
+
 - (void)setEditView:(UIView<TextFieldViewContaining>*)editView {
   CHECK(!_editView, kLensOverlayNotFatalUntil);
   CHECK(editView, kLensOverlayNotFatalUntil);
@@ -326,6 +338,7 @@
     [_webView removeFromSuperview];
   }
   _webView = webView;
+  _webView.hidden = _webViewHidden;
 
   _webView.translatesAutoresizingMaskIntoConstraints = NO;
   if (!_webView || !self.webViewContainer) {
diff --git a/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm b/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm
index e486e32..8281b016 100644
--- a/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm
+++ b/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm
@@ -413,10 +413,6 @@
 }
 
 - (void)focusOmnibox {
-#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_16_0
-  // Dismiss the edit menu.
-  [[UIMenuController sharedMenuController] hideMenu];
-#endif
 
   // When the NTP and fakebox are visible, make the fakebox animates into place
   // before focusing the omnibox.
diff --git a/ios/chrome/browser/mailto_handler/model/mailto_handler_service_factory.mm b/ios/chrome/browser/mailto_handler/model/mailto_handler_service_factory.mm
index 69e8172..bdb122b 100644
--- a/ios/chrome/browser/mailto_handler/model/mailto_handler_service_factory.mm
+++ b/ios/chrome/browser/mailto_handler/model/mailto_handler_service_factory.mm
@@ -47,10 +47,9 @@
   MailtoHandlerConfiguration* configuration =
       [[MailtoHandlerConfiguration alloc] init];
 
-  ChromeBrowserState* browser_state =
-      ChromeBrowserState::FromBrowserState(context);
+  ProfileIOS* profile = ProfileIOS::FromBrowserState(context);
   configuration.authService =
-      AuthenticationServiceFactory::GetForBrowserState(browser_state);
+      AuthenticationServiceFactory::GetForProfile(profile);
 
   ApplicationContext* application_context = GetApplicationContext();
   configuration.localState = application_context->GetLocalState();
diff --git a/ios/chrome/browser/mini_map/ui_bundled/mini_map_coordinator.mm b/ios/chrome/browser/mini_map/ui_bundled/mini_map_coordinator.mm
index bc61069..cb10dd49 100644
--- a/ios/chrome/browser/mini_map/ui_bundled/mini_map_coordinator.mm
+++ b/ios/chrome/browser/mini_map/ui_bundled/mini_map_coordinator.mm
@@ -79,7 +79,7 @@
 - (void)start {
   [super start];
 
-  PrefService* prefService = self.browser->GetBrowserState()->GetPrefs();
+  PrefService* prefService = self.browser->GetProfile()->GetPrefs();
   self.mediator = [[MiniMapMediator alloc] initWithPrefs:prefService
                                                 webState:self.webState.get()];
   self.mediator.delegate = self;
@@ -243,8 +243,7 @@
     [self.mediator userOpenedURLFromMiniMap];
     OpenNewTabCommand* command = [OpenNewTabCommand
         commandWithURLFromChrome:net::GURLWithNSURL(url)
-                     inIncognito:self.browser->GetBrowserState()
-                                     ->IsOffTheRecord()];
+                     inIncognito:self.browser->GetProfile()->IsOffTheRecord()];
     id<ApplicationCommands> applicationHandler = HandlerForProtocol(
         self.browser->GetCommandDispatcher(), ApplicationCommands);
     [applicationHandler openURLInNewTab:command];
diff --git a/ios/chrome/browser/mini_map/ui_bundled/mini_map_coordinator_unittest.mm b/ios/chrome/browser/mini_map/ui_bundled/mini_map_coordinator_unittest.mm
index 94d34cec..f4ea7840 100644
--- a/ios/chrome/browser/mini_map/ui_bundled/mini_map_coordinator_unittest.mm
+++ b/ios/chrome/browser/mini_map/ui_bundled/mini_map_coordinator_unittest.mm
@@ -68,10 +68,10 @@
 class MiniMapCoordinatorTest : public PlatformTest {
  protected:
   MiniMapCoordinatorTest() {
-    TestChromeBrowserState::Builder builder;
+    TestProfileIOS::Builder builder;
     builder.SetPrefService(CreatePrefService());
-    browser_state_ = std::move(builder).Build();
-    browser_ = std::make_unique<TestBrowser>(browser_state_.get());
+    profile_ = std::move(builder).Build();
+    browser_ = std::make_unique<TestBrowser>(profile_.get());
     mock_application_command_handler_ =
         OCMStrictProtocolMock(@protocol(ApplicationCommands));
     mock_application_settings_command_handler_ =
@@ -125,7 +125,7 @@
 
  protected:
   base::test::TaskEnvironment environment_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
   id<MiniMapMediatorDelegate> delegate_;
   std::unique_ptr<Browser> browser_;
   MiniMapCoordinator* coordinator_;
@@ -184,9 +184,8 @@
   }
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(web::features::kOneTapForMaps);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted,
-                                         false);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, false);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
   id mini_map_controller = OCMStrictProtocolMock(@protocol(MiniMapController));
   factory_.controller = mini_map_controller;
   SetupCoordinator(YES, MiniMapMode::kMap);
@@ -221,8 +220,8 @@
   }
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(web::features::kOneTapForMaps);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, true);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
   id mini_map_controller = OCMStrictProtocolMock(@protocol(MiniMapController));
   factory_.controller = mini_map_controller;
 
@@ -249,9 +248,8 @@
        web::features::kOneTapForMapsConsentModeIPHParam}};
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       web::features::kOneTapForMaps, feature_parameters);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted,
-                                         false);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, false);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
   id mini_map_controller = OCMStrictProtocolMock(@protocol(MiniMapController));
   factory_.controller = mini_map_controller;
 
@@ -270,7 +268,7 @@
   SetupCoordinator(YES, MiniMapMode::kMap);
   environment_.RunUntilIdle();
   EXPECT_TRUE(
-      browser_state_->GetPrefs()->GetBoolean(prefs::kDetectAddressesAccepted));
+      profile_->GetPrefs()->GetBoolean(prefs::kDetectAddressesAccepted));
   EXPECT_OCMOCK_VERIFY(mini_map_controller);
 }
 
@@ -285,8 +283,8 @@
        web::features::kOneTapForMapsConsentModeIPHParam}};
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       web::features::kOneTapForMaps, feature_parameters);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, true);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
   id mini_map_controller = OCMStrictProtocolMock(@protocol(MiniMapController));
   factory_.controller = mini_map_controller;
 
@@ -310,9 +308,8 @@
   base::HistogramTester histogram_tester;
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(web::features::kOneTapForMaps);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted,
-                                         false);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, false);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
   id mini_map_controller = OCMStrictProtocolMock(@protocol(MiniMapController));
   factory_.controller = mini_map_controller;
 
@@ -341,9 +338,8 @@
   base::HistogramTester histogram_tester;
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(web::features::kOneTapForMaps);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted,
-                                         false);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, false);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
   id mini_map_controller = OCMStrictProtocolMock(@protocol(MiniMapController));
   factory_.controller = mini_map_controller;
 
@@ -373,9 +369,8 @@
   base::HistogramTester histogram_tester;
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(web::features::kOneTapForMaps);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted,
-                                         false);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, false);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
   id mini_map_controller = OCMStrictProtocolMock(@protocol(MiniMapController));
   factory_.controller = mini_map_controller;
 
diff --git a/ios/chrome/browser/mini_map/ui_bundled/mini_map_mediator_unittest.mm b/ios/chrome/browser/mini_map/ui_bundled/mini_map_mediator_unittest.mm
index d754277..a0f9739 100644
--- a/ios/chrome/browser/mini_map/ui_bundled/mini_map_mediator_unittest.mm
+++ b/ios/chrome/browser/mini_map/ui_bundled/mini_map_mediator_unittest.mm
@@ -23,15 +23,14 @@
 class MiniMapMediatorTest : public PlatformTest {
  protected:
   MiniMapMediatorTest() {
-    TestChromeBrowserState::Builder builder;
+    TestProfileIOS::Builder builder;
     builder.SetPrefService(CreatePrefService());
-    browser_state_ = std::move(builder).Build();
+    profile_ = std::move(builder).Build();
 
     delegate_ = OCMStrictProtocolMock(@protocol(MiniMapMediatorDelegate));
 
-    mediator_ =
-        [[MiniMapMediator alloc] initWithPrefs:browser_state_->GetPrefs()
-                                      webState:nullptr];
+    mediator_ = [[MiniMapMediator alloc] initWithPrefs:profile_->GetPrefs()
+                                              webState:nullptr];
     mediator_.delegate = delegate_;
   }
 
@@ -50,7 +49,7 @@
 
  protected:
   base::test::TaskEnvironment environment_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
   id delegate_;
   MiniMapMediator* mediator_;
 };
@@ -63,9 +62,8 @@
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(web::features::kOneTapForMaps);
 
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted,
-                                         false);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, false);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
   OCMExpect([delegate_ showMapWithIPH:NO]);
   [mediator_ userInitiatedMiniMapConsentRequired:NO];
 }
@@ -77,18 +75,16 @@
   }
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(web::features::kOneTapForMaps);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted,
-                                         false);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, false);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
   OCMExpect([delegate_ showConsentInterstitial]);
   [mediator_ userInitiatedMiniMapConsentRequired:YES];
   OCMExpect([delegate_ showMapWithIPH:NO]);
   [mediator_ userConsented];
   environment_.RunUntilIdle();
   EXPECT_TRUE(
-      browser_state_->GetPrefs()->GetBoolean(prefs::kDetectAddressesAccepted));
-  EXPECT_TRUE(
-      browser_state_->GetPrefs()->GetBoolean(prefs::kDetectAddressesEnabled));
+      profile_->GetPrefs()->GetBoolean(prefs::kDetectAddressesAccepted));
+  EXPECT_TRUE(profile_->GetPrefs()->GetBoolean(prefs::kDetectAddressesEnabled));
 }
 
 // Tests that settings are updated correctly after user declines.
@@ -98,18 +94,17 @@
   }
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(web::features::kOneTapForMaps);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted,
-                                         false);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, false);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
   OCMExpect([delegate_ showConsentInterstitial]);
   [mediator_ userInitiatedMiniMapConsentRequired:YES];
   OCMExpect([delegate_ dismissConsentInterstitialWithCompletion:[OCMArg any]]);
   [mediator_ userDeclined];
   environment_.RunUntilIdle();
   EXPECT_FALSE(
-      browser_state_->GetPrefs()->GetBoolean(prefs::kDetectAddressesAccepted));
+      profile_->GetPrefs()->GetBoolean(prefs::kDetectAddressesAccepted));
   EXPECT_FALSE(
-      browser_state_->GetPrefs()->GetBoolean(prefs::kDetectAddressesEnabled));
+      profile_->GetPrefs()->GetBoolean(prefs::kDetectAddressesEnabled));
 }
 
 // Tests that consent is presented if it is forced.
@@ -124,17 +119,16 @@
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       web::features::kOneTapForMaps, feature_parameters);
 
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, true);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
   OCMExpect([delegate_ showConsentInterstitial]);
   [mediator_ userInitiatedMiniMapConsentRequired:YES];
   OCMExpect([delegate_ showMapWithIPH:NO]);
   [mediator_ userConsented];
   environment_.RunUntilIdle();
   EXPECT_TRUE(
-      browser_state_->GetPrefs()->GetBoolean(prefs::kDetectAddressesAccepted));
-  EXPECT_TRUE(
-      browser_state_->GetPrefs()->GetBoolean(prefs::kDetectAddressesEnabled));
+      profile_->GetPrefs()->GetBoolean(prefs::kDetectAddressesAccepted));
+  EXPECT_TRUE(profile_->GetPrefs()->GetBoolean(prefs::kDetectAddressesEnabled));
 }
 
 // Tests that consent screen is not triggered but IPH is displayed.
@@ -149,17 +143,15 @@
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       web::features::kOneTapForMaps, feature_parameters);
 
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted,
-                                         false);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, false);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
   OCMExpect([delegate_ showMapWithIPH:YES]);
   [mediator_ userInitiatedMiniMapConsentRequired:YES];
 
   environment_.RunUntilIdle();
   EXPECT_TRUE(
-      browser_state_->GetPrefs()->GetBoolean(prefs::kDetectAddressesAccepted));
-  EXPECT_TRUE(
-      browser_state_->GetPrefs()->GetBoolean(prefs::kDetectAddressesEnabled));
+      profile_->GetPrefs()->GetBoolean(prefs::kDetectAddressesAccepted));
+  EXPECT_TRUE(profile_->GetPrefs()->GetBoolean(prefs::kDetectAddressesEnabled));
 }
 
 // Tests that consent screen is not triggered if not needed.
@@ -174,9 +166,8 @@
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       web::features::kOneTapForMaps, feature_parameters);
 
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted,
-                                         false);
-  browser_state_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesAccepted, false);
+  profile_->GetPrefs()->SetBoolean(prefs::kDetectAddressesEnabled, true);
   OCMExpect([delegate_ showMapWithIPH:NO]);
   [mediator_ userInitiatedMiniMapConsentRequired:NO];
 }
diff --git a/ios/chrome/browser/plus_addresses/coordinator/BUILD.gn b/ios/chrome/browser/plus_addresses/coordinator/BUILD.gn
index 2c6fd408..443f11d 100644
--- a/ios/chrome/browser/plus_addresses/coordinator/BUILD.gn
+++ b/ios/chrome/browser/plus_addresses/coordinator/BUILD.gn
@@ -14,10 +14,12 @@
     "//components/plus_addresses",
     "//components/plus_addresses/metrics",
     "//components/plus_addresses/settings",
+    "//components/strings:components_strings_grit",
     "//ios/chrome/browser/autofill/model/bottom_sheet:bottom_sheet",
     "//ios/chrome/browser/plus_addresses/model",
     "//ios/chrome/browser/plus_addresses/ui:constants",
     "//ios/chrome/browser/plus_addresses/ui:ui",
+    "//ios/chrome/browser/shared/coordinator/alert",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/profile",
diff --git a/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_coordinator.mm b/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_coordinator.mm
index 38a852b..f127cb9 100644
--- a/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_coordinator.mm
+++ b/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_coordinator.mm
@@ -8,27 +8,38 @@
 #import "components/plus_addresses/plus_address_service.h"
 #import "components/plus_addresses/plus_address_types.h"
 #import "components/plus_addresses/settings/plus_address_setting_service.h"
+#import "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.h"
 #import "ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_mediator.h"
 #import "ios/chrome/browser/plus_addresses/model/plus_address_service_factory.h"
 #import "ios/chrome/browser/plus_addresses/model/plus_address_setting_service_factory.h"
 #import "ios/chrome/browser/plus_addresses/ui/plus_address_bottom_sheet_view_controller.h"
+#import "ios/chrome/browser/shared/coordinator/alert/alert_coordinator.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/shared/public/commands/browser_coordinator_commands.h"
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
 #import "ios/chrome/browser/url_loading/model/url_loading_browser_agent.h"
+#import "ui/base/l10n/l10n_util_mac.h"
 
 namespace {
 constexpr CGFloat kHalfSheetCornerRadius = 20;
 }  // namespace
 
+@interface PlusAddressBottomSheetCoordinator () <
+    PlusAddressBottomSheetMediatorDelegate>
+
+@end
+
 @implementation PlusAddressBottomSheetCoordinator {
   // The view controller responsible for display of the bottom sheet.
   PlusAddressBottomSheetViewController* _viewController;
   // A mediator that hides data operations from the view controller.
   PlusAddressBottomSheetMediator* _mediator;
+
+  // Alert coordinator used to show error alerts.
+  AlertCoordinator* _alertCoordinator;
 }
 
 #pragma mark - ChromeCoordinator
@@ -49,6 +60,7 @@
   _mediator = [[PlusAddressBottomSheetMediator alloc]
       initWithPlusAddressService:plusAddressService
        plusAddressSettingService:plusAddressSettingService
+                        delegate:self
                        activeUrl:activeWebState->GetLastCommittedURL()
                 autofillCallback:bottomSheetTabHelper
                                      ->GetPendingPlusAddressFillCallback()
@@ -87,4 +99,20 @@
   _mediator = nil;
 }
 
+#pragma mark - PlusAddressBottomSheetMediatorDelegate
+
+- (void)showErrorAlert {
+  _alertCoordinator = [[AlertCoordinator alloc]
+      initWithBaseViewController:_viewController
+                         browser:self.browser
+                           title:
+                               l10n_util::GetNSString(
+                                   IDS_PLUS_ADDRESS_GENERIC_ERROR_ALERT_TITLE_IOS)
+                         message:
+                             l10n_util::GetNSString(
+                                 IDS_PLUS_ADDRESS_GENERIC_ERROR_ALERT_MESSAGE_IOS)];
+
+  [_alertCoordinator start];
+}
+
 @end
diff --git a/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_mediator.h b/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_mediator.h
index ff50044..f8739af 100644
--- a/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_mediator.h
+++ b/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_mediator.h
@@ -19,6 +19,14 @@
 @protocol PlusAddressBottomSheetConsumer;
 class UrlLoadingBrowserAgent;
 
+// Delegate for this mediator.
+@protocol PlusAddressBottomSheetMediatorDelegate
+
+// Shows alert with the mesage.
+- (void)showErrorAlert;
+
+@end
+
 // Mediator for the plus_addresses bottom sheet. It is responsible for service
 // interactions underlying the UI.
 @interface PlusAddressBottomSheetMediator
@@ -31,6 +39,8 @@
     initWithPlusAddressService:(plus_addresses::PlusAddressService*)service
      plusAddressSettingService:
          (plus_addresses::PlusAddressSettingService*)plusAddressSettingService
+                      delegate:
+                          (id<PlusAddressBottomSheetMediatorDelegate>)delegate
                      activeUrl:(GURL)activeUrl
               autofillCallback:(plus_addresses::PlusAddressCallback)callback
                      urlLoader:(UrlLoadingBrowserAgent*)urlLoader
diff --git a/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_mediator.mm b/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_mediator.mm
index fb60b21..4dc00af 100644
--- a/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_mediator.mm
+++ b/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_mediator.mm
@@ -33,12 +33,17 @@
   NSString* _reservedPlusAddress;
   raw_ptr<UrlLoadingBrowserAgent> _urlLoader;
   BOOL _incognito;
+
+  // The delegate for this mediator.
+  __weak id<PlusAddressBottomSheetMediatorDelegate> _delegate;
 }
 
 - (instancetype)
     initWithPlusAddressService:(plus_addresses::PlusAddressService*)service
      plusAddressSettingService:
          (plus_addresses::PlusAddressSettingService*)plusAddressSettingService
+                      delegate:
+                          (id<PlusAddressBottomSheetMediatorDelegate>)delegate
                      activeUrl:(GURL)activeUrl
               autofillCallback:(plus_addresses::PlusAddressCallback)callback
                      urlLoader:(UrlLoadingBrowserAgent*)urlLoader
@@ -50,6 +55,7 @@
   if (self) {
     _plusAddressService = service;
     _plusAddressSettingService = plusAddressSettingService;
+    _delegate = delegate;
     _mainFrameOrigin = url::Origin::Create(activeUrl);
     _autofillCallback = std::move(callback);
     _urlLoader = urlLoader;
@@ -70,9 +76,9 @@
       [weakSelf didReservePlusAddress:base::SysUTF8ToNSString(
                                           *maybePlusProfile->plus_address)];
     } else {
-      [weakSelf.consumer notifyError:plus_addresses::metrics::
-                                         PlusAddressModalCompletionStatus::
-                                             kReservePlusAddressError];
+      [weakSelf notifyError:plus_addresses::metrics::
+                                PlusAddressModalCompletionStatus::
+                                    kReservePlusAddressError];
     }
   });
   _plusAddressService->ReservePlusAddress(_mainFrameOrigin,
@@ -89,9 +95,9 @@
           [weakSelf runAutofillCallback:base::SysUTF8ToNSString(
                                             *maybePlusProfile->plus_address)];
         } else {
-          [weakSelf.consumer notifyError:plus_addresses::metrics::
-                                             PlusAddressModalCompletionStatus::
-                                                 kConfirmPlusAddressError];
+          [weakSelf notifyError:plus_addresses::metrics::
+                                    PlusAddressModalCompletionStatus::
+                                        kConfirmPlusAddressError];
         }
       });
   _plusAddressService->ConfirmPlusAddress(
@@ -134,9 +140,9 @@
           [weakSelf didReservePlusAddress:base::SysUTF8ToNSString(
                                               *maybePlusProfile->plus_address)];
         } else {
-          [weakSelf.consumer notifyError:plus_addresses::metrics::
-                                             PlusAddressModalCompletionStatus::
-                                                 kReservePlusAddressError];
+          [weakSelf notifyError:plus_addresses::metrics::
+                                    PlusAddressModalCompletionStatus::
+                                        kReservePlusAddressError];
         }
       });
   _plusAddressService->RefreshPlusAddress(_mainFrameOrigin,
@@ -179,4 +185,16 @@
       return GURL(plus_addresses::features::kPlusAddressLearnMoreUrl.Get());
   }
 }
+
+// Informs both the `consumer` and `_delegate` to prepare to show the error
+// state.
+- (void)notifyError:
+    (plus_addresses::metrics::PlusAddressModalCompletionStatus)status {
+  [self.consumer notifyError:status];
+  if (base::FeatureList::IsEnabled(
+          plus_addresses::features::kPlusAddressIOSErrorStatesEnabled)) {
+    [_delegate showErrorAlert];
+  }
+}
+
 @end
diff --git a/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_mediator_unittest.mm b/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_mediator_unittest.mm
index 87180a0..8335965 100644
--- a/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_mediator_unittest.mm
+++ b/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_mediator_unittest.mm
@@ -55,6 +55,7 @@
     mediator_ = [[PlusAddressBottomSheetMediator alloc]
         initWithPlusAddressService:&service()
          plusAddressSettingService:&plus_address_setting_service_
+                          delegate:nil
                          activeUrl:GURL(FakePlusAddressService::kFacet)
                   autofillCallback:base::DoNothing()
                          urlLoader:url_loader_
diff --git a/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.h b/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.h
index 68554b0..c819970 100644
--- a/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.h
+++ b/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.h
@@ -29,6 +29,9 @@
 // Setter to show reserve error states in `FakePlusAddressService` in tests.
 + (void)setShouldFailToReserve:(BOOL)shouldFailToReserve;
 
+// Setter to show confirm error states in `FakePlusAddressService` in tests.
++ (void)setShouldFailToConfirm:(BOOL)shouldFailToConfirm;
+
 // Returns the primary email from `FakePlusAddressService`.
 + (NSString*)primaryEmail;
 
diff --git a/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.mm b/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.mm
index a34dcef..3e944543 100644
--- a/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.mm
+++ b/ios/chrome/browser/plus_addresses/ui/plus_address_app_interface.mm
@@ -51,6 +51,10 @@
   GetFakePlusAddressService()->set_should_fail_to_reserve(shouldFailToReserve);
 }
 
++ (void)setShouldFailToConfirm:(BOOL)shouldFailToConfirm {
+  GetFakePlusAddressService()->set_should_fail_to_confirm(shouldFailToConfirm);
+}
+
 + (NSString*)primaryEmail {
   return base::SysUTF8ToNSString(
       *(GetFakePlusAddressService()->GetPrimaryEmail()));
diff --git a/ios/chrome/browser/plus_addresses/ui/plus_address_bottom_sheet_view_controller.mm b/ios/chrome/browser/plus_addresses/ui/plus_address_bottom_sheet_view_controller.mm
index 178b860..aa7ffb4 100644
--- a/ios/chrome/browser/plus_addresses/ui/plus_address_bottom_sheet_view_controller.mm
+++ b/ios/chrome/browser/plus_addresses/ui/plus_address_bottom_sheet_view_controller.mm
@@ -278,23 +278,24 @@
 }
 
 - (void)notifyError:(PlusAddressModalCompletionStatus)status {
-  // With any error, whether during the reservation step or the confirmation
-  // step, disable submission of the modal.
   _bottomSheetErrorStatus = status;
-  self.primaryActionButton.enabled = NO;
-
-  _reservedPlusAddressTableView.hidden = YES;
-  [_reservedPlusAddressTableView reloadData];
-
-  _errorMessage.hidden = NO;
   if (base::FeatureList::IsEnabled(
           plus_addresses::features::kPlusAddressIOSErrorStatesEnabled)) {
     self.isLoading = NO;
   } else {
+    // With any error, whether during the reservation step or the confirmation
+    // step, disable submission of the modal.
+    self.primaryActionButton.enabled = NO;
+
+    _reservedPlusAddressTableView.hidden = YES;
+    [_reservedPlusAddressTableView reloadData];
+
+    _errorMessage.hidden = NO;
+
     [_activityIndicator stopAnimating];
+    // Resize to accommodate error message.
+    [self expandBottomSheet];
   }
-  // Resize to accommodate error message.
-  [self expandBottomSheet];
 }
 
 #pragma mark - UITextViewDelegate
diff --git a/ios/chrome/browser/plus_addresses/ui/plus_addresses_egtest.mm b/ios/chrome/browser/plus_addresses/ui/plus_addresses_egtest.mm
index 8dc9cfc7..fd659ba 100644
--- a/ios/chrome/browser/plus_addresses/ui/plus_addresses_egtest.mm
+++ b/ios/chrome/browser/plus_addresses/ui/plus_addresses_egtest.mm
@@ -89,6 +89,7 @@
   _fakeIdentity = [FakeSystemIdentity fakeIdentity1];
   [SigninEarlGrey signinWithFakeIdentity:_fakeIdentity];
 
+  [PlusAddressAppInterface setPlusAddressFillingEnabled:YES];
   [self loadPlusAddressEligiblePage];
 }
 
@@ -115,6 +116,11 @@
         feature_engagement::kIPHPlusAddressCreateSuggestionFeature.name;
   }
 
+  if ([self isRunningTest:@selector(testGenericErrorAlert)]) {
+    config.features_enabled_and_params.push_back(
+        {plus_addresses::features::kPlusAddressIOSErrorStatesEnabled, {}});
+  }
+
   return config;
 }
 
@@ -382,4 +388,27 @@
   [ChromeEarlGrey waitForUIElementToAppearWithMatcher:iph_chip];
 }
 
+// Tests that an error alert is shown if the plus address is failed to confirm.
+- (void)testGenericErrorAlert {
+  [PlusAddressAppInterface setShouldFailToConfirm:YES];
+  [self openCreatePlusAddressBottomSheet];
+
+  id<GREYMatcher> plusAddressLabelMatcher = GetMatcherForPlusAddressLabel(
+      base::SysUTF8ToNSString(plus_addresses::test::kFakePlusAddress));
+  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:plusAddressLabelMatcher];
+
+  id<GREYMatcher> confirmButton =
+      chrome_test_util::ButtonWithAccessibilityLabelId(
+          IDS_PLUS_ADDRESS_BOTTOMSHEET_OK_TEXT_IOS);
+
+  // Click the okay button, confirming the plus address.
+  [[EarlGrey selectElementWithMatcher:confirmButton] performAction:grey_tap()];
+
+  id<GREYMatcher> error_alert = grey_text(
+      l10n_util::GetNSString(IDS_PLUS_ADDRESS_GENERIC_ERROR_ALERT_MESSAGE_IOS));
+
+  // Ensure the error alert is shown.
+  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:error_alert];
+}
+
 @end
diff --git a/ios/chrome/browser/profile/model/profile_manager_ios_impl.h b/ios/chrome/browser/profile/model/profile_manager_ios_impl.h
index 0eea87de..836d511 100644
--- a/ios/chrome/browser/profile/model/profile_manager_ios_impl.h
+++ b/ios/chrome/browser/profile/model/profile_manager_ios_impl.h
@@ -10,6 +10,7 @@
 #include <string>
 #include <string_view>
 
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
@@ -22,6 +23,12 @@
 
 class PrefService;
 
+// Feature used to disable the culling of legacy profiles (i.e. old profile
+// dating back from many years ago when a first experimentation was done to
+// try to support multi-profiles before WKWebView added the required API in
+// iOS 17.0).
+BASE_DECLARE_FEATURE(kHideLegacyProfiles);
+
 // ProfileManagerIOS implementation.
 class ProfileManagerIOSImpl : public ProfileManagerIOS,
                               public ProfileIOS::Delegate {
@@ -87,6 +94,12 @@
   void DoFinalInit(ProfileIOS* profile);
   void DoFinalInitForServices(ProfileIOS* profile);
 
+  // Hides legacy profiles (i.e. all known profiles not listed in `profiles`).
+  void HideLegacyProfiles(const std::set<std::string>& profiles);
+
+  // Restores legacy profiles (if any).
+  void RestoreLegacyProfiles(const std::set<std::string>& profiles);
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   // The PrefService storing the local state.
diff --git a/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm b/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm
index 47abdbbb..e1fa341 100644
--- a/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm
+++ b/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm
@@ -9,6 +9,7 @@
 #import <utility>
 
 #import "base/check.h"
+#import "base/feature_list.h"
 #import "base/files/file_enumerator.h"
 #import "base/files/file_path.h"
 #import "base/functional/bind.h"
@@ -136,6 +137,10 @@
 
 }  // namespace
 
+BASE_FEATURE(kHideLegacyProfiles,
+             "HideLegacyProfiles",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 // Stores information about a single Profile.
 class ProfileManagerIOSImpl::ProfileInfo {
  public:
@@ -231,6 +236,13 @@
     profiles.insert(kIOSChromeInitialBrowserState);
   }
 
+  // Take care of the legacy profiles.
+  if (base::FeatureList::IsEnabled(kHideLegacyProfiles)) {
+    HideLegacyProfiles(profiles);
+  } else {
+    RestoreLegacyProfiles(profiles);
+  }
+
   for (const std::string& name : profiles) {
     ProfileIOS* profile = CreateProfile(name);
     DCHECK(profile != nullptr);
@@ -392,6 +404,11 @@
 
 bool ProfileManagerIOSImpl::CanCreateProfileWithName(std::string_view name) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Cannot create a profile with the same name as a legacy profile.
+  if (local_state_->GetDict(prefs::kLegacyProfileMap).Find(name)) {
+    return false;
+  }
+
   // TODO(crbug.com/335630301): check whether there is a Profile with that name
   // whose deletion is pending, and return false if this is the case (to avoid
   // recovering its state).
@@ -495,3 +512,56 @@
   SupervisedUserServiceFactory::GetForProfile(profile)->Init();
   ListFamilyMembersServiceFactory::GetForProfile(profile)->Init();
 }
+
+void ProfileManagerIOSImpl::HideLegacyProfiles(
+    const std::set<std::string>& profiles) {
+  CHECK(base::FeatureList::IsEnabled(kHideLegacyProfiles));
+  if (local_state_->GetBoolean(prefs::kLegacyProfileHidden)) {
+    return;
+  }
+
+  base::Value::Dict legacy_profiles;
+
+  const size_t count = profile_attributes_storage_.GetNumberOfProfiles();
+  for (size_t i = 0; i < count; ++i) {
+    const size_t index = count - i - 1;  // iterate backwards
+    ProfileAttributesIOS attr =
+        profile_attributes_storage_.GetAttributesForProfileAtIndex(index);
+
+    const std::string name = attr.GetProfileName();
+    if (!base::Contains(profiles, name)) {
+      legacy_profiles.Set(name, std::move(attr).GetStorage());
+      profile_attributes_storage_.RemoveProfile(name);
+    }
+  }
+
+  local_state_->SetBoolean(prefs::kLegacyProfileHidden, true);
+  local_state_->SetDict(prefs::kLegacyProfileMap, std::move(legacy_profiles));
+}
+
+void ProfileManagerIOSImpl::RestoreLegacyProfiles(
+    const std::set<std::string>& profiles) {
+  CHECK(!base::FeatureList::IsEnabled(kHideLegacyProfiles));
+  if (!local_state_->GetBoolean(prefs::kLegacyProfileHidden)) {
+    return;
+  }
+
+  const base::Value::Dict& legacy_profiles =
+      local_state_->GetDict(prefs::kLegacyProfileMap);
+
+  for (const auto [key, value] : legacy_profiles) {
+    DCHECK(!base::Contains(profiles, key));
+    DCHECK(value.is_dict());
+
+    profile_attributes_storage_.AddProfile(key);
+    profile_attributes_storage_.UpdateAttributesForProfileWithName(
+        key, base::BindOnce(
+                 [](const base::Value::Dict* dict, ProfileAttributesIOS attr) {
+                   return ProfileAttributesIOS(attr.GetProfileName(), dict);
+                 },
+                 &value.GetDict()));
+  }
+
+  local_state_->ClearPref(prefs::kLegacyProfileHidden);
+  local_state_->ClearPref(prefs::kLegacyProfileMap);
+}
diff --git a/ios/chrome/browser/profile/model/profile_manager_ios_impl_unittest.mm b/ios/chrome/browser/profile/model/profile_manager_ios_impl_unittest.mm
index 3b522237..7721053e6 100644
--- a/ios/chrome/browser/profile/model/profile_manager_ios_impl_unittest.mm
+++ b/ios/chrome/browser/profile/model/profile_manager_ios_impl_unittest.mm
@@ -6,6 +6,7 @@
 
 #import "base/containers/contains.h"
 #import "base/scoped_observation.h"
+#import "base/test/scoped_feature_list.h"
 #import "base/test/test_file_util.h"
 #import "base/threading/thread_restrictions.h"
 #import "components/variations/scoped_variations_ids_provider.h"
@@ -32,6 +33,7 @@
 const char kProfileName2[] = "Profile2";
 const char kTestProfile1[] = "TestProfile1";
 const char kTestProfile2[] = "TestProfile2";
+const char kLegacyProfile[] = "LegacyProfile";
 
 // A scoped ProfileManagerObserverIOS which records which events have been
 // received.
@@ -73,6 +75,12 @@
   bool on_profile_loaded_called_ = false;
 };
 
+// Returns a callback that fail the current test if invoked.
+template <typename... Args>
+base::OnceCallback<void(Args...)> FailCallback() {
+  return base::BindOnce([](Args...) { GTEST_FAIL(); });
+}
+
 // Returns a callback taking a single parameter and storing it in `output`.
 // The `output` must outlive the returned callback as it is captured by copy.
 template <typename T>
@@ -80,11 +88,47 @@
   return base::BindOnce([](T* output, T value) { *output = value; }, output);
 }
 
+// State in which a feature should be.
+enum class FeatureState {
+  kDefault,
+  kEnabled,
+  kDisabled,
+};
+
+// Wrapper around a ScopedFeatureList that initialize it while putting the
+// feature as either in its default state, as force-enabled or force-disabled.
+// This allow to ensure the ScopedFeatureList is fully initialized before the
+// threads are created (as the initialization is not thread-safe and some of
+// the code running on background threads check the FeatureList).
+template <FeatureState state>
+class ScopedFeatureListWithState {
+ public:
+  ScopedFeatureListWithState(const base::Feature& feature) {
+    switch (state) {
+      case FeatureState::kDefault:
+        scoped_feature_list_.Init();
+        break;
+
+      case FeatureState::kEnabled:
+        scoped_feature_list_.InitAndEnableFeature(feature);
+        break;
+
+      case FeatureState::kDisabled:
+        scoped_feature_list_.InitAndDisableFeature(feature);
+        break;
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
 }  // namespace
 
-class ProfileManagerIOSImplTest : public PlatformTest {
+template <FeatureState state>
+class ConfigurableProfileManagerIOSImplTest : public PlatformTest {
  public:
-  ProfileManagerIOSImplTest()
+  ConfigurableProfileManagerIOSImplTest()
       : profile_manager_(GetApplicationContext()->GetLocalState(),
                          base::CreateUniqueTempDirectoryScopedToTest()) {
     TestingApplicationContext* application_context =
@@ -123,7 +167,7 @@
     std::ignore = chrome_io_->system_url_request_context_getter();
   }
 
-  ~ProfileManagerIOSImplTest() override {
+  ~ConfigurableProfileManagerIOSImplTest() override {
     TestingApplicationContext* application_context =
         TestingApplicationContext::GetGlobal();
 
@@ -139,6 +183,10 @@
 
   ProfileManagerIOSImpl& profile_manager() { return profile_manager_; }
 
+  ProfileAttributesStorageIOS& profile_attributes_storage() {
+    return *profile_manager_.GetProfileAttributesStorage();
+  }
+
   // Returns the name of the loaded Profiles.
   std::set<std::string> GetLoadedProfileNames() {
     std::set<std::string> profile_names;
@@ -154,6 +202,7 @@
   }
 
  private:
+  ScopedFeatureListWithState<state> scoped_feature_list_{kHideLegacyProfiles};
   IOSChromeScopedTestingLocalState scoped_testing_local_state_;
   std::unique_ptr<IOSChromeIOThread> chrome_io_;
   web::WebTaskEnvironment web_task_environment_{
@@ -165,6 +214,10 @@
       variations::VariationsIdsProvider::Mode::kUseSignedInState};
 };
 
+// By default tests use the default state of the kHideLegacyProfiles feature.
+using ProfileManagerIOSImplTest =
+    ConfigurableProfileManagerIOSImplTest<FeatureState::kDefault>;
+
 // Tests that GetLoadedProfiles() returns an empty list before the Profiles are
 // loaded, and then a list containing at least one Profile, and the last used
 // Profile is loaded.
@@ -299,7 +352,7 @@
 TEST_F(ProfileManagerIOSImplTest, LoadProfileAsync) {
   // Pretends that a Profile named `kProfileName1` exists. Required as
   // LoadProfileAsync(...) won't create new Profiles.
-  profile_manager().GetProfileAttributesStorage()->AddProfile(kProfileName1);
+  profile_attributes_storage().AddProfile(kProfileName1);
 
   base::RunLoop run_loop;
   ProfileIOS* created_profile = nullptr;
@@ -338,7 +391,7 @@
 TEST_F(ProfileManagerIOSImplTest, LoadProfileAsync_Reload) {
   // Pretends that a Profile named `kProfileName1` exists. Required as
   // LoadProfileAsync(...) won't create new Profiles.
-  profile_manager().GetProfileAttributesStorage()->AddProfile(kProfileName1);
+  profile_attributes_storage().AddProfile(kProfileName1);
 
   // Load the Profile a first time.
   {
@@ -410,9 +463,7 @@
 TEST_F(ProfileManagerIOSImplTest, LoadProfileAsync_Missing) {
   // Ensures that no Profile named `kProfileName1` exists. This will cause
   // LoadProfileAsync(...) to fail since it does not create new Profiles.
-  ASSERT_FALSE(
-      profile_manager().GetProfileAttributesStorage()->HasProfileWithName(
-          kProfileName1));
+  ASSERT_FALSE(profile_attributes_storage().HasProfileWithName(kProfileName1));
 
   base::RunLoop run_loop;
   ProfileIOS* created_profile = nullptr;
@@ -443,9 +494,7 @@
 TEST_F(ProfileManagerIOSImplTest, CreateProfileAsync) {
   // Ensures that no Profile named `kProfileName1` exists. This will cause
   // CreateProfileAsync(...) to create a new Profile.
-  ASSERT_FALSE(
-      profile_manager().GetProfileAttributesStorage()->HasProfileWithName(
-          kProfileName1));
+  ASSERT_FALSE(profile_attributes_storage().HasProfileWithName(kProfileName1));
 
   base::RunLoop run_loop;
   ProfileIOS* created_profile = nullptr;
@@ -484,9 +533,7 @@
 TEST_F(ProfileManagerIOSImplTest, CreateProfileAsync_Reload) {
   // Ensures that no Profile named `kProfileName1` exists. This will cause
   // CreateProfileAsync(...) to create a new Profile.
-  ASSERT_FALSE(
-      profile_manager().GetProfileAttributesStorage()->HasProfileWithName(
-          kProfileName1));
+  ASSERT_FALSE(profile_attributes_storage().HasProfileWithName(kProfileName1));
 
   // Load the Profile a first time.
   {
@@ -559,7 +606,7 @@
 TEST_F(ProfileManagerIOSImplTest, LoadProfile) {
   // Pretends that a Profile named `kProfileName1` exists. Required as
   // LoadProfile(...) won't create new Profiles.
-  profile_manager().GetProfileAttributesStorage()->AddProfile(kProfileName1);
+  profile_attributes_storage().AddProfile(kProfileName1);
 
   // Load the Profile synchronously.
   ProfileIOS* profile = profile_manager().LoadProfile(kProfileName1);
@@ -576,9 +623,7 @@
 TEST_F(ProfileManagerIOSImplTest, LoadProfile_Missing) {
   // Ensures that no Profile named `kProfileName1` exists. This will cause
   // LoadProfile(...) to fail since it does not create new Profiles.
-  ASSERT_FALSE(
-      profile_manager().GetProfileAttributesStorage()->HasProfileWithName(
-          kProfileName1));
+  ASSERT_FALSE(profile_attributes_storage().HasProfileWithName(kProfileName1));
 
   // Load the Profile synchronously.
   ProfileIOS* profile = profile_manager().LoadProfile(kProfileName1);
@@ -592,9 +637,7 @@
 TEST_F(ProfileManagerIOSImplTest, CreateProfile) {
   // Ensures that no Profile named `kProfileName1` exists. This will cause
   // CreateProfileAsync(...) to create a new Profile.
-  ASSERT_FALSE(
-      profile_manager().GetProfileAttributesStorage()->HasProfileWithName(
-          kProfileName1));
+  ASSERT_FALSE(profile_attributes_storage().HasProfileWithName(kProfileName1));
 
   // Create the Profile synchronously.
   ProfileIOS* profile = profile_manager().CreateProfile(kProfileName1);
@@ -606,3 +649,170 @@
   // object.
   EXPECT_EQ(profile, profile_manager().CreateProfile(kProfileName1));
 }
+
+using ProfileManagerIOSImplTest_HideLegacyProfile =
+    ConfigurableProfileManagerIOSImplTest<FeatureState::kEnabled>;
+
+// Tests that legacy profiles are hidden when kHideLegacyProfiles is enabled.
+TEST_F(ProfileManagerIOSImplTest_HideLegacyProfile, Hide) {
+  PrefService* local_state = GetApplicationContext()->GetLocalState();
+  ASSERT_FALSE(local_state->GetBoolean(prefs::kLegacyProfileHidden));
+
+  // Create a legacy profile.
+  profile_attributes_storage().AddProfile(kLegacyProfile);
+  ASSERT_TRUE(profile_attributes_storage().HasProfileWithName(kLegacyProfile));
+  local_state->ClearPref(prefs::kLastActiveProfiles);
+
+  // Check that the profile is correctly marked as legacy when the profiles
+  // are loaded.
+  profile_manager().LoadProfiles();
+
+  // Exactly one Profile must be loaded, it must be the last used Profile with
+  // name `kIOSChromeInitialBrowserState`.
+  EXPECT_EQ(GetLoadedProfileNames(),
+            (std::set<std::string>{kIOSChromeInitialBrowserState}));
+
+  // The legacy profile should no longer be visible in the
+  // ProfileAttributesStorageIOS.
+  EXPECT_FALSE(profile_attributes_storage().HasProfileWithName(kLegacyProfile));
+
+  base::RunLoop run_loop;
+  ProfileIOS* loaded_profile = nullptr;
+
+  // Trying to create a profile named kLegacyProfile should fail and call the
+  // initialized_callback with nullptr.
+  const bool success = profile_manager().CreateProfileAsync(
+      kLegacyProfile,
+      CaptureParam(&loaded_profile).Then(run_loop.QuitClosure()),
+      FailCallback<ProfileIOS*>());
+  EXPECT_FALSE(success);
+
+  run_loop.Run();
+  EXPECT_EQ(loaded_profile, nullptr);
+}
+
+// Tests that legacy profiles are hidden when kHideLegacyProfiles is enabled,
+// but that this only happens once.
+TEST_F(ProfileManagerIOSImplTest_HideLegacyProfile, Hide_AlreadyDone) {
+  // Create profile that is not referenced (i.e. not loaded) thus could be
+  // considered legacy, but pretend that the categorisation of legacy profiles
+  // has already been run.
+  profile_attributes_storage().AddProfile(kLegacyProfile);
+  ASSERT_TRUE(profile_attributes_storage().HasProfileWithName(kLegacyProfile));
+
+  PrefService* local_state = GetApplicationContext()->GetLocalState();
+  local_state->ClearPref(prefs::kLastActiveProfiles);
+  local_state->SetBoolean(prefs::kLegacyProfileHidden, true);
+
+  // Check that the profile is not marked as legacy but it is not loaded.
+  profile_manager().LoadProfiles();
+
+  // Exactly one Profile must be loaded, it must be the last used Profile with
+  // name `kIOSChromeInitialBrowserState`.
+  EXPECT_EQ(GetLoadedProfileNames(),
+            (std::set<std::string>{kIOSChromeInitialBrowserState}));
+
+  // The profile must still be visible in the ProfileAttributesStorageIOS.
+  EXPECT_TRUE(profile_attributes_storage().HasProfileWithName(kLegacyProfile));
+
+  base::RunLoop run_loop;
+  ProfileIOS* created_profile = nullptr;
+  ProfileIOS* loaded_profile = nullptr;
+
+  // Trying to load the profile should succeed.
+  const bool success = profile_manager().CreateProfileAsync(
+      kLegacyProfile,
+      CaptureParam(&loaded_profile).Then(run_loop.QuitClosure()),
+      CaptureParam(&created_profile));
+
+  EXPECT_NE(created_profile, nullptr);
+  EXPECT_TRUE(success);
+
+  run_loop.Run();
+
+  EXPECT_NE(loaded_profile, nullptr);
+}
+
+using ProfileManagerIOSImplTest_KeepLegacyProfile =
+    ConfigurableProfileManagerIOSImplTest<FeatureState::kDisabled>;
+
+// Tests that legacy profile are not touched if kHideLegacyProfiles feature
+// is disabled.
+TEST_F(ProfileManagerIOSImplTest_KeepLegacyProfile, Keep) {
+  PrefService* local_state = GetApplicationContext()->GetLocalState();
+  ASSERT_FALSE(local_state->GetBoolean(prefs::kLegacyProfileHidden));
+
+  // Create profile that is not referenced (i.e. not loaded) thus could be
+  // considered legacy.
+  profile_attributes_storage().AddProfile(kLegacyProfile);
+  ASSERT_TRUE(profile_attributes_storage().HasProfileWithName(kLegacyProfile));
+  local_state->ClearPref(prefs::kLastActiveProfiles);
+
+  // Check that the profile is not marked as legacy but it is not loaded.
+  profile_manager().LoadProfiles();
+
+  // Exactly one Profile must be loaded, it must be the last used Profile with
+  // name `kIOSChromeInitialBrowserState`.
+  EXPECT_EQ(GetLoadedProfileNames(),
+            (std::set<std::string>{kIOSChromeInitialBrowserState}));
+
+  // The profile must still be visible in the ProfileAttributesStorageIOS.
+  EXPECT_TRUE(profile_attributes_storage().HasProfileWithName(kLegacyProfile));
+
+  base::RunLoop run_loop;
+  ProfileIOS* created_profile = nullptr;
+  ProfileIOS* loaded_profile = nullptr;
+
+  // Trying to load the profile should succeed.
+  const bool success = profile_manager().CreateProfileAsync(
+      kLegacyProfile,
+      CaptureParam(&loaded_profile).Then(run_loop.QuitClosure()),
+      CaptureParam(&created_profile));
+
+  EXPECT_NE(created_profile, nullptr);
+  EXPECT_TRUE(success);
+
+  run_loop.Run();
+
+  EXPECT_NE(loaded_profile, nullptr);
+}
+
+// Tests that legacy profile are restored when the feature is disabled and
+// some profile were hidden due to the feature being enabled previously.
+TEST_F(ProfileManagerIOSImplTest_KeepLegacyProfile, Restore) {
+  // Pretend a legacy profile was hidden.
+  PrefService* local_state = GetApplicationContext()->GetLocalState();
+  local_state->SetBoolean(prefs::kLegacyProfileHidden, true);
+  local_state->SetDict(
+      prefs::kLegacyProfileMap,
+      base::Value::Dict().Set(kLegacyProfile, base::Value::Dict()));
+  EXPECT_FALSE(profile_attributes_storage().HasProfileWithName(kLegacyProfile));
+
+  // Check that the profile is not loaded but is restored.
+  profile_manager().LoadProfiles();
+
+  // Exactly one Profile must be loaded, it must be the last used Profile with
+  // name `kIOSChromeInitialBrowserState`.
+  EXPECT_EQ(GetLoadedProfileNames(),
+            (std::set<std::string>{kIOSChromeInitialBrowserState}));
+
+  // The profile must now be visible in the ProfileAttributesStorageIOS.
+  EXPECT_TRUE(profile_attributes_storage().HasProfileWithName(kLegacyProfile));
+
+  base::RunLoop run_loop;
+  ProfileIOS* created_profile = nullptr;
+  ProfileIOS* loaded_profile = nullptr;
+
+  // Trying to load the profile should succeed.
+  const bool success = profile_manager().CreateProfileAsync(
+      kLegacyProfile,
+      CaptureParam(&loaded_profile).Then(run_loop.QuitClosure()),
+      CaptureParam(&created_profile));
+
+  EXPECT_NE(created_profile, nullptr);
+  EXPECT_TRUE(success);
+
+  run_loop.Run();
+
+  EXPECT_NE(loaded_profile, nullptr);
+}
diff --git a/ios/chrome/browser/settings/model/sync/utils/sync_fake_server_egtest.mm b/ios/chrome/browser/settings/model/sync/utils/sync_fake_server_egtest.mm
index 99df858..f14fa154 100644
--- a/ios/chrome/browser/settings/model/sync/utils/sync_fake_server_egtest.mm
+++ b/ios/chrome/browser/settings/model/sync/utils/sync_fake_server_egtest.mm
@@ -1117,6 +1117,10 @@
   GREYAssertEqual([ChromeEarlGrey browsingHistoryEntryCount], 4,
                   @"History did not contain the expected entries");
 
+  // Signout doesn't close the current tab. Switch to the first tab, so the
+  // second tab can close.
+  [ChromeEarlGrey selectTabAtIndex:0];
+
   // Open settings and tap "Sign Out".
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI
diff --git a/ios/chrome/browser/shared/model/prefs/BUILD.gn b/ios/chrome/browser/shared/model/prefs/BUILD.gn
index 77d7f57e..4fb6511 100644
--- a/ios/chrome/browser/shared/model/prefs/BUILD.gn
+++ b/ios/chrome/browser/shared/model/prefs/BUILD.gn
@@ -117,6 +117,7 @@
     "//ios/chrome/browser/ui/authentication/history_sync",
     "//ios/chrome/browser/ui/authentication/signin",
     "//ios/chrome/browser/ui/content_suggestions",
+    "//ios/chrome/browser/ui/content_suggestions/price_tracking_promo:price_tracking_promo",
     "//ios/chrome/browser/ui/content_suggestions/safety_check:prefs",
     "//ios/chrome/browser/ui/reading_list:reading_list_constants",
     "//ios/chrome/browser/ui/settings/clear_browsing_data:features",
diff --git a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
index 84daedc..98f9965 100644
--- a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
@@ -107,6 +107,7 @@
 #import "ios/chrome/browser/ui/authentication/signin/signin_coordinator.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view_mediator.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h"
+#import "ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_prefs.h"
 #import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_prefs.h"
 #import "ios/chrome/browser/ui/settings/clear_browsing_data/features.h"
 #import "ios/chrome/browser/upgrade/model/upgrade_constants.h"
@@ -536,6 +537,8 @@
 
   // Preferences related to the profile manager.
   registry->RegisterStringPref(prefs::kLastUsedProfile, std::string());
+  registry->RegisterBooleanPref(prefs::kLegacyProfileHidden, false);
+  registry->RegisterDictionaryPref(prefs::kLegacyProfileMap);
 
   [MemoryDebuggerManager registerLocalState:registry];
   [IncognitoReauthSceneAgent registerLocalState:registry];
@@ -787,6 +790,7 @@
   policy::URLBlocklistManager::RegisterProfilePrefs(registry);
   PrefProxyConfigTrackerImpl::RegisterProfilePrefs(registry);
   PushNotificationService::RegisterProfilePrefs(registry);
+  RegisterPriceTrackingPromoPrefs(registry);
   RegisterVoiceSearchBrowserStatePrefs(registry);
   safe_browsing::RegisterProfilePrefs(registry);
   segmentation_platform::SegmentationPlatformService::RegisterProfilePrefs(
@@ -813,8 +817,8 @@
   [BookmarksHomeMediator registerBrowserStatePrefs:registry];
   [ContentSuggestionsMediator registerBrowserStatePrefs:registry];
   [HandoffManager registerBrowserStatePrefs:registry];
-  [SigninCoordinator registerBrowserStatePrefs:registry];
-  [SigninPromoViewMediator registerBrowserStatePrefs:registry];
+  [SigninCoordinator registerProfilePrefs:registry];
+  [SigninPromoViewMediator registerProfilePrefs:registry];
 
   tab_groups::prefs::RegisterProfilePrefs(registry);
 
@@ -962,7 +966,7 @@
 
   registry->RegisterBooleanPref(kPrivacySandboxManuallyControlled, false);
   // Register prefs used to skip too frequent History Sync Opt-In prompt.
-  history_sync::RegisterBrowserStatePrefs(registry);
+  history_sync::RegisterProfilePrefs(registry);
 
   // Deprecated pref, moved to LocalState.
   registry->RegisterTimePref(prefs::kIdentityConfirmationSnackbarLastPromptTime,
diff --git a/ios/chrome/browser/shared/model/prefs/pref_names.h b/ios/chrome/browser/shared/model/prefs/pref_names.h
index c45586ab..0d9c38f 100644
--- a/ios/chrome/browser/shared/model/prefs/pref_names.h
+++ b/ios/chrome/browser/shared/model/prefs/pref_names.h
@@ -62,6 +62,12 @@
 // A string of NSUUID used to access the WebKit storage per Profile.
 inline constexpr char kBrowserStateStorageIdentifier[] = "profile.storage_id";
 
+// A map of legacy profile names to their information.
+inline constexpr char kLegacyProfileMap[] = "profile.legacy_profiles.map";
+
+// A boolean recording whether the legacy profiles have been marked as such.
+inline constexpr char kLegacyProfileHidden[] = "profile.legacy_profiles.hidden";
+
 inline constexpr char kClearBrowsingDataHistoryNoticeShownTimes[] =
     "browser.clear_data.history_notice_shown_times";
 
diff --git a/ios/chrome/browser/shared/public/commands/quick_delete_commands.h b/ios/chrome/browser/shared/public/commands/quick_delete_commands.h
index 0707b5c1..d2c45a620 100644
--- a/ios/chrome/browser/shared/public/commands/quick_delete_commands.h
+++ b/ios/chrome/browser/shared/public/commands/quick_delete_commands.h
@@ -19,6 +19,12 @@
 // Stops Quick Delete.
 - (void)stopQuickDelete;
 
+// Dismisses the Quick Delete UI along with any other UIs that triggered it. In
+// practice, it dismisses everything on top of the BrowserViewController. On
+// dismissal completion, runs `completion` followed by actually stopping Quick
+// Delete and any others UIs, such as History or Privacy Settings.
+- (void)stopQuickDeleteForAnimationWithCompletion:(ProceduralBlock)completion;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_QUICK_DELETE_COMMANDS_H_
diff --git a/ios/chrome/browser/shared/ui/symbols/symbol_helpers.mm b/ios/chrome/browser/shared/ui/symbols/symbol_helpers.mm
index b484ae0..f6fe448 100644
--- a/ios/chrome/browser/shared/ui/symbols/symbol_helpers.mm
+++ b/ios/chrome/browser/shared/ui/symbols/symbol_helpers.mm
@@ -75,13 +75,9 @@
 }
 
 UIImage* MakeSymbolMonochrome(UIImage* symbol) {
-#if defined(__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0
   return [symbol
       imageByApplyingSymbolConfiguration:
           [UIImageSymbolConfiguration configurationPreferringMonochrome]];
-#else
-  return symbol;
-#endif  // defined(__IPHONE_16_0)
 }
 
 UIImage* MakeSymbolMulticolor(UIImage* symbol) {
diff --git a/ios/chrome/browser/shared/ui/util/uikit_ui_util.h b/ios/chrome/browser/shared/ui/util/uikit_ui_util.h
index a8457dd2..9b78279 100644
--- a/ios/chrome/browser/shared/ui/util/uikit_ui_util.h
+++ b/ios/chrome/browser/shared/ui/util/uikit_ui_util.h
@@ -172,12 +172,6 @@
 // trailing view of a group cell with the correct font.
 NSAttributedString* TextForTabGroupCount(int count, CGFloat font_size);
 
-#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_16_0
-// Adds `item` to the global Edit Menu configuration (UIMenuController). No-op
-// if a UIMenuItem with the same selector as `item` has already been registered.
-void RegisterEditMenuItem(UIMenuItem* item);
-#endif
-
 // Finds the root of `view`'s view hierarchy -- its window if it has one, or
 // the first (recursive) superview with no superview.
 UIView* ViewHierarchyRootForView(UIView* view);
diff --git a/ios/chrome/browser/shared/ui/util/uikit_ui_util.mm b/ios/chrome/browser/shared/ui/util/uikit_ui_util.mm
index fade09b..d85f228f1 100644
--- a/ios/chrome/browser/shared/ui/util/uikit_ui_util.mm
+++ b/ios/chrome/browser/shared/ui/util/uikit_ui_util.mm
@@ -374,23 +374,6 @@
                                       attributes:@{NSFontAttributeName : font}];
 }
 
-#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_16_0
-void RegisterEditMenuItem(UIMenuItem* item) {
-  UIMenuController* menu = [UIMenuController sharedMenuController];
-  NSArray<UIMenuItem*>* items = [menu menuItems];
-
-  for (UIMenuItem* existingItem in items) {
-    if ([existingItem action] == [item action]) {
-      return;
-    }
-  }
-
-  items = items ? [items arrayByAddingObject:item] : @[ item ];
-
-  [menu setMenuItems:items];
-}
-#endif
-
 UIView* ViewHierarchyRootForView(UIView* view) {
   if (view.window) {
     return view.window;
diff --git a/ios/chrome/browser/signin/model/ios_chrome_signin_client.mm b/ios/chrome/browser/signin/model/ios_chrome_signin_client.mm
index d3e3e4d..8d845ea5 100644
--- a/ios/chrome/browser/signin/model/ios_chrome_signin_client.mm
+++ b/ios/chrome/browser/signin/model/ios_chrome_signin_client.mm
@@ -105,22 +105,14 @@
           event_details.GetSetPrimaryAccountAccessPoint().value();
 
       size_t tabs_count = 0;
-      size_t groups_count = 0;
-      size_t grouped_tabs_count = 0;
 
       BrowserList* browser_list = BrowserListFactory::GetForProfile(profile_);
       for (Browser* browser : browser_list->BrowsersOfType(
                BrowserList::BrowserType::kRegularAndInactive)) {
-        WebStateList* web_state_list = browser->GetWebStateList();
-        tabs_count += web_state_list->count();
-        for (const TabGroup* group : web_state_list->GetGroups()) {
-          ++groups_count;
-          grouped_tabs_count += group->range().count();
-        }
+        tabs_count += browser->GetWebStateList()->count();
       }
 
-      signin_metrics::RecordTabAndGroupCountsOnSignin(
-          access_point, signin::ConsentLevel::kSignin, tabs_count, groups_count,
-          grouped_tabs_count);
+      signin_metrics::RecordOpenTabCountOnSignin(
+          access_point, signin::ConsentLevel::kSignin, tabs_count);
   }
 }
diff --git a/ios/chrome/browser/supervised_user/model/BUILD.gn b/ios/chrome/browser/supervised_user/model/BUILD.gn
index cfeaeb7..b43184c 100644
--- a/ios/chrome/browser/supervised_user/model/BUILD.gn
+++ b/ios/chrome/browser/supervised_user/model/BUILD.gn
@@ -137,6 +137,7 @@
   ]
   deps = [
     "//base",
+    "//components/browsing_data/core",
     "//components/policy:policy_code_generate",
     "//components/signin/internal/identity_manager",
     "//components/strings",
diff --git a/ios/chrome/browser/supervised_user/model/supervised_user_with_parental_controls_egtest.mm b/ios/chrome/browser/supervised_user/model/supervised_user_with_parental_controls_egtest.mm
index 0a3072f..772f8d6 100644
--- a/ios/chrome/browser/supervised_user/model/supervised_user_with_parental_controls_egtest.mm
+++ b/ios/chrome/browser/supervised_user/model/supervised_user_with_parental_controls_egtest.mm
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #import "base/test/ios/wait_util.h"
+#import "components/browsing_data/core/pref_names.h"
 #import "components/policy/policy_constants.h"
 #import "components/signin/internal/identity_manager/account_capabilities_constants.h"
 #import "components/supervised_user/core/browser/supervised_user_url_filter.h"
@@ -140,6 +141,11 @@
 }
 
 - (void)clearBrowsingData {
+  // Disable closing tabs as it's on by default in delete browsing data, so the
+  // tab closure animation is not run in iPads.
+  [ChromeEarlGrey setBoolValue:false
+                   forUserPref:browsing_data::prefs::kCloseTabs];
+
   // Clear the browsing data.
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI
diff --git a/ios/chrome/browser/ui/authentication/account_menu/account_menu_coordinator.mm b/ios/chrome/browser/ui/authentication/account_menu/account_menu_coordinator.mm
index d4f7cc8..23865f7 100644
--- a/ios/chrome/browser/ui/authentication/account_menu/account_menu_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/account_menu/account_menu_coordinator.mm
@@ -101,14 +101,13 @@
 - (void)start {
   [super start];
 
-  ChromeBrowserState* browserState = self.browser->GetBrowserState();
-  _syncService = SyncServiceFactory::GetForBrowserState(browserState);
-  _authenticationService =
-      AuthenticationServiceFactory::GetForBrowserState(browserState);
+  ProfileIOS* profile = self.browser->GetProfile();
+  _syncService = SyncServiceFactory::GetForProfile(profile);
+  _authenticationService = AuthenticationServiceFactory::GetForProfile(profile);
   _accountManagerService =
-      ChromeAccountManagerServiceFactory::GetForBrowserState(browserState);
-  _identityManager = IdentityManagerFactory::GetForProfile(browserState);
-  _prefService = browserState->GetPrefs();
+      ChromeAccountManagerServiceFactory::GetForProfile(profile);
+  _identityManager = IdentityManagerFactory::GetForProfile(profile);
+  _prefService = profile->GetPrefs();
   _applicationHandler = HandlerForProtocol(self.browser->GetCommandDispatcher(),
                                            ApplicationCommands);
 
@@ -129,7 +128,7 @@
       UIPopoverArrowDirectionUp;
   _navigationController.presentationController.delegate = self;
 
-  PrefService* prefs = browserState->GetPrefs();
+  PrefService* prefs = profile->GetPrefs();
 
   _mediator =
       [[AccountMenuMediator alloc] initWithSyncService:_syncService
diff --git a/ios/chrome/browser/ui/authentication/account_menu/account_menu_coordinator_unittests.mm b/ios/chrome/browser/ui/authentication/account_menu/account_menu_coordinator_unittests.mm
index d7dfcaed..b5bec0d9 100644
--- a/ios/chrome/browser/ui/authentication/account_menu/account_menu_coordinator_unittests.mm
+++ b/ios/chrome/browser/ui/authentication/account_menu/account_menu_coordinator_unittests.mm
@@ -67,12 +67,12 @@
   void SetUp() override {
     PlatformTest::SetUp();
 
-    TestChromeBrowserState::Builder builder;
+    TestProfileIOS::Builder builder;
     builder.AddTestingFactory(
         AuthenticationServiceFactory::GetInstance(),
         AuthenticationServiceFactory::GetDefaultFactory());
-    browser_state_ = std::move(builder).Build();
-    browser_ = std::make_unique<TestBrowser>(browser_state_.get());
+    profile_ = std::move(builder).Build();
+    browser_ = std::make_unique<TestBrowser>(profile_.get());
 
     mock_application_commands_handler_ =
         OCMStrictProtocolMock(@protocol(ApplicationCommands));
@@ -97,14 +97,13 @@
         startDispatchingToTarget:mock_browser_coordinator_commands_handler_
                      forProtocol:@protocol(BrowserCoordinatorCommands)];
 
-    AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
-        browser_state_.get(),
-        std::make_unique<FakeAuthenticationServiceDelegate>());
+    AuthenticationServiceFactory::CreateAndInitializeForProfile(
+        profile_.get(), std::make_unique<FakeAuthenticationServiceDelegate>());
     fake_system_identity_manager_ =
         FakeSystemIdentityManager::FromSystemIdentityManager(
             GetApplicationContext()->GetSystemIdentityManager());
     authentication_service_ =
-        AuthenticationServiceFactory::GetForBrowserState(browser_state_.get());
+        AuthenticationServiceFactory::GetForProfile(profile_.get());
 
     SigninWithPrimaryIdentity();
     AddSecondaryIdentity();
@@ -189,7 +188,7 @@
 
   web::WebTaskEnvironment task_environment_;
   IOSChromeScopedTestingLocalState scoped_testing_local_state_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
   std::unique_ptr<TestBrowser> browser_;
 };
 
diff --git a/ios/chrome/browser/ui/authentication/account_menu/account_menu_mediator.mm b/ios/chrome/browser/ui/authentication/account_menu/account_menu_mediator.mm
index 5a6c0f1d..99f6771 100644
--- a/ios/chrome/browser/ui/authentication/account_menu/account_menu_mediator.mm
+++ b/ios/chrome/browser/ui/authentication/account_menu/account_menu_mediator.mm
@@ -62,6 +62,13 @@
   // The type of account error that is being displayed in the error section for
   // signed in accounts. Is set to kNone when there is no error section.
   syncer::SyncService::UserActionableError _diplayedAccountErrorType;
+
+  // Records the displayed primary account info by the view. Used to limit the
+  // view updates to only when one of these values is updated.
+  NSString* _primaryAccountDisplayedEmail;
+  NSString* _primaryAccountDisplayedUserFullName;
+  UIImage* _primaryAccountDisplayedAvatar;
+  BOOL _primaryAccountDisplayedManaged;
 }
 
 - (instancetype)initWithSyncService:(syncer::SyncService*)syncService
@@ -94,8 +101,6 @@
     _syncService = syncService;
     _syncObserver = std::make_unique<SyncObserverBridge>(self, _syncService);
     _diplayedAccountErrorType = syncer::SyncService::UserActionableError::kNone;
-    _primaryIdentity = _authenticationService->GetPrimaryIdentity(
-        signin::ConsentLevel::kSignin);
     [self updateIdentities];
     _error = GetAccountErrorUIInfo(_syncService);
   }
@@ -390,7 +395,9 @@
   [self.consumer updateAccountListWithGaiaIDsToAdd:gaiaIDsToAdd
                                    gaiaIDsToRemove:gaiaIDsToRemove];
   // In case the primary account information changed.
-  [self.consumer updatePrimaryAccount];
+  if ([self primaryAccountInfoChanged]) {
+    [self.consumer updatePrimaryAccount];
+  }
 }
 
 // Callback for signout.
@@ -434,4 +441,26 @@
   NOTREACHED();
 }
 
+// Updates the displayed values, and returns YES if the primary account info
+// changed from the displayed ones. Otherwise returns NO.
+- (BOOL)primaryAccountInfoChanged {
+  if (_primaryAccountDisplayedAvatar != self.primaryAccountAvatar ||
+      _primaryAccountDisplayedUserFullName != self.primaryAccountUserFullName ||
+      _primaryAccountDisplayedEmail != self.primaryAccountEmail ||
+      _primaryAccountDisplayedManaged !=
+          self.managementState.is_profile_managed()) {
+    [self recordPrimaryAccountDisplayedInfo];
+    return YES;
+  }
+  return NO;
+}
+
+// Records the displayed primary account info.
+- (void)recordPrimaryAccountDisplayedInfo {
+  _primaryAccountDisplayedEmail = self.primaryAccountEmail;
+  _primaryAccountDisplayedUserFullName = self.primaryAccountUserFullName;
+  _primaryAccountDisplayedAvatar = self.primaryAccountAvatar;
+  _primaryAccountDisplayedManaged = self.managementState.is_profile_managed();
+}
+
 @end
diff --git a/ios/chrome/browser/ui/authentication/account_menu/account_menu_mediator_unittests.mm b/ios/chrome/browser/ui/authentication/account_menu/account_menu_mediator_unittests.mm
index 430a1233..e38bc87 100644
--- a/ios/chrome/browser/ui/authentication/account_menu/account_menu_mediator_unittests.mm
+++ b/ios/chrome/browser/ui/authentication/account_menu/account_menu_mediator_unittests.mm
@@ -49,30 +49,27 @@
   void SetUp() override {
     PlatformTest::SetUp();
 
-    // Set the browser state.
-    TestChromeBrowserState::Builder builder;
+    // Set the profile.
+    TestProfileIOS::Builder builder;
     builder.AddTestingFactory(SyncServiceFactory::GetInstance(),
                               base::BindRepeating(&CreateMockSyncService));
     builder.AddTestingFactory(
         AuthenticationServiceFactory::GetInstance(),
         AuthenticationServiceFactory::GetDefaultFactory());
-    browser_state_ = std::move(builder).Build();
+    profile_ = std::move(builder).Build();
 
     // Set the manager and services variables.
-    AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
-        browser_state_.get(),
-        std::make_unique<FakeAuthenticationServiceDelegate>());
+    AuthenticationServiceFactory::CreateAndInitializeForProfile(
+        profile_.get(), std::make_unique<FakeAuthenticationServiceDelegate>());
     fake_system_identity_manager_ =
         FakeSystemIdentityManager::FromSystemIdentityManager(
             GetApplicationContext()->GetSystemIdentityManager());
     authentication_service_ =
-        AuthenticationServiceFactory::GetForBrowserState(browser_state_.get());
+        AuthenticationServiceFactory::GetForProfile(profile_.get());
     account_manager_service_ =
-        ChromeAccountManagerServiceFactory::GetForBrowserState(
-            browser_state_.get());
+        ChromeAccountManagerServiceFactory::GetForProfile(profile_.get());
     test_sync_service_ = std::make_unique<syncer::TestSyncService>();
-    identity_manager_ =
-        IdentityManagerFactory::GetForProfile(browser_state_.get());
+    identity_manager_ = IdentityManagerFactory::GetForProfile(profile_.get());
 
     AddPrimaryIdentity();
     AddSecondaryIdentity();
@@ -85,7 +82,7 @@
         accountManagerService:account_manager_service_
                   authService:authentication_service_
               identityManager:identity_manager_
-                        prefs:browser_state_->GetPrefs()];
+                        prefs:profile_->GetPrefs()];
     mediator_.delegate = delegate_;
     mediator_.consumer = consumer_;
   }
@@ -161,7 +158,7 @@
   web::WebTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   IOSChromeScopedTestingLocalState scoped_testing_local_state_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
 };
 
 #pragma mark - Test for ChromeAccountManagerServiceObserver
@@ -173,17 +170,22 @@
   OCMExpect([consumer_
       updateAccountListWithGaiaIDsToAdd:@[ thirdIdentity.gaiaID ]
                         gaiaIDsToRemove:@[]]);
-  OCMExpect([consumer_ updatePrimaryAccount]);
   fake_system_identity_manager_->AddIdentity(thirdIdentity);
 }
 
 // Checks that removing a secondary identity lead to updating the
 // consumer.
 TEST_F(AccountMenuMediatorTest, TestRemoveSecondaryIdentity) {
+  // Expectations due to ChromeAccountManagerServiceObserver updates.
+  OCMExpect([consumer_ updateAccountListWithGaiaIDsToAdd:@[]
+                                         gaiaIDsToRemove:@[]]);
+  OCMExpect([consumer_ updatePrimaryAccount]);
+  OCMExpect([consumer_ updateAccountListWithGaiaIDsToAdd:@[]
+                                         gaiaIDsToRemove:@[]]);
+
   OCMExpect([consumer_
       updateAccountListWithGaiaIDsToAdd:@[]
                         gaiaIDsToRemove:@[ kSecondaryIdentity.gaiaID ]]);
-  OCMExpect([consumer_ updatePrimaryAccount]);
   {
     base::RunLoop run_loop;
     base::RepeatingClosure closure = run_loop.QuitClosure();
@@ -310,7 +312,6 @@
 
   OCMExpect([consumer_ updateAccountListWithGaiaIDsToAdd:@[]
                                          gaiaIDsToRemove:@[]]);
-  OCMExpect([consumer_ updatePrimaryAccount]);
   SigninCompletionInfo* signinCompletionInfo =
       [SigninCompletionInfo signinCompletionInfoWithIdentity:nil];
   OCMExpect([delegate_ mediatorWantsToDismissTheView:mediator_]);
diff --git a/ios/chrome/browser/ui/authentication/account_menu/account_menu_view_controller.mm b/ios/chrome/browser/ui/authentication/account_menu/account_menu_view_controller.mm
index 65711fc..202f9e75 100644
--- a/ios/chrome/browser/ui/authentication/account_menu/account_menu_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/account_menu/account_menu_view_controller.mm
@@ -519,6 +519,8 @@
   }
   [snapshot deleteItemsWithIdentifiers:accountsIdentifiersToRemove];
   [_accountMenuDataSource applySnapshot:snapshot animatingDifferences:YES];
+
+  [self.tableView reloadData];
 }
 
 #pragma mark - UIResponder
diff --git a/ios/chrome/browser/ui/authentication/account_menu/account_menu_view_controller_unittests.mm b/ios/chrome/browser/ui/authentication/account_menu/account_menu_view_controller_unittests.mm
index 44eb35f..b00c97b 100644
--- a/ios/chrome/browser/ui/authentication/account_menu/account_menu_view_controller_unittests.mm
+++ b/ios/chrome/browser/ui/authentication/account_menu/account_menu_view_controller_unittests.mm
@@ -103,22 +103,20 @@
  public:
   void SetUp() override {
     PlatformTest::SetUp();
-    TestChromeBrowserState::Builder builder;
+    TestProfileIOS::Builder builder;
     builder.AddTestingFactory(
         AuthenticationServiceFactory::GetInstance(),
         AuthenticationServiceFactory::GetDefaultFactory());
-    browser_state_ = std::move(builder).Build();
+    profile_ = std::move(builder).Build();
     fake_system_identity_manager_ =
         FakeSystemIdentityManager::FromSystemIdentityManager(
             GetApplicationContext()->GetSystemIdentityManager());
-    AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
-        browser_state_.get(),
-        std::make_unique<FakeAuthenticationServiceDelegate>());
+    AuthenticationServiceFactory::CreateAndInitializeForProfile(
+        profile_.get(), std::make_unique<FakeAuthenticationServiceDelegate>());
     data_source_.accountManagerService =
-        ChromeAccountManagerServiceFactory::GetForBrowserState(
-            browser_state_.get());
+        ChromeAccountManagerServiceFactory::GetForProfile(profile_.get());
     authentication_service_ =
-        AuthenticationServiceFactory::GetForBrowserState(browser_state_.get());
+        AuthenticationServiceFactory::GetForProfile(profile_.get());
 
     AddPrimaryIdentity();
     AddSecondaryIdentity();
@@ -198,7 +196,7 @@
   web::WebTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   IOSChromeScopedTestingLocalState scoped_testing_local_state_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
 };
 
 // Test the view controller when it starts.
@@ -383,3 +381,4 @@
   // Sign Out
   EXPECT_EQ(1, [TableView() numberOfRowsInSection:1]);
 }
+
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow.mm b/ios/chrome/browser/ui/authentication/authentication_flow.mm
index e8821f2..78d1e6a 100644
--- a/ios/chrome/browser/ui/authentication/authentication_flow.mm
+++ b/ios/chrome/browser/ui/authentication/authentication_flow.mm
@@ -401,7 +401,7 @@
 
 - (void)checkSigninSteps {
   id<SystemIdentity> currentIdentity =
-      AuthenticationServiceFactory::GetForBrowserState([self originalProfile])
+      AuthenticationServiceFactory::GetForProfile([self originalProfile])
           ->GetPrimaryIdentity(signin::ConsentLevel::kSignin);
   if (currentIdentity && ![currentIdentity isEqual:_identityToSignIn]) {
     // If the identity to sign-in is different than the current identity,
@@ -418,7 +418,7 @@
   }
   ProfileIOS* profile = [self originalProfile];
   ChromeAccountManagerService* accountManagerService =
-      ChromeAccountManagerServiceFactory::GetForBrowserState(profile);
+      ChromeAccountManagerServiceFactory::GetForProfile(profile);
 
   if (accountManagerService->IsValidIdentity(identity)) {
     [_performer signInIdentity:identity
@@ -582,7 +582,7 @@
 
 // The original profile used for services that don't exist in incognito mode.
 - (ProfileIOS*)originalProfile {
-  return _browser->GetProfile()->GetOriginalChromeBrowserState();
+  return _browser->GetProfile()->GetOriginalProfile();
 }
 
 - (PrefService*)prefs {
@@ -632,7 +632,7 @@
   }
 
   syncer::SyncService* syncService =
-      SyncServiceFactory::GetForBrowserState([self originalProfile]);
+      SyncServiceFactory::GetForProfile([self originalProfile]);
   syncer::SyncUserSettings* userSettings = syncService->GetUserSettings();
 
   if (userSettings->GetSelectedTypes().HasAll(
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow_performer.h b/ios/chrome/browser/ui/authentication/authentication_flow_performer.h
index 76dabbf..5924da4a 100644
--- a/ios/chrome/browser/ui/authentication/authentication_flow_performer.h
+++ b/ios/chrome/browser/ui/authentication/authentication_flow_performer.h
@@ -35,7 +35,7 @@
 - (void)fetchManagedStatus:(ProfileIOS*)profile
                forIdentity:(id<SystemIdentity>)identity;
 
-// Signs `identity` with `hostedDomain` into `browserState`.
+// Signs `identity` with `hostedDomain` into `profile`.
 - (void)signInIdentity:(id<SystemIdentity>)identity
          atAccessPoint:(signin_metrics::AccessPoint)accessPoint
       withHostedDomain:(NSString*)hostedDomain
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm b/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
index 4e990399..bba8a22 100644
--- a/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
+++ b/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
@@ -158,13 +158,13 @@
          atAccessPoint:(signin_metrics::AccessPoint)accessPoint
       withHostedDomain:(NSString*)hostedDomain
              toProfile:(ProfileIOS*)profile {
-  AuthenticationServiceFactory::GetForBrowserState(profile)->SignIn(
-      identity, accessPoint);
+  AuthenticationServiceFactory::GetForProfile(profile)->SignIn(identity,
+                                                               accessPoint);
 }
 
 - (void)signOutProfile:(ProfileIOS*)profile {
   __weak __typeof(_delegate) weakDelegate = _delegate;
-  AuthenticationServiceFactory::GetForBrowserState(profile)->SignOut(
+  AuthenticationServiceFactory::GetForProfile(profile)->SignOut(
       signin_metrics::ProfileSignout::kUserClickedSignoutSettings,
       /*force_clear_browsing_data=*/false, ^{
         [weakDelegate didSignOut];
@@ -172,7 +172,7 @@
 }
 
 - (void)signOutImmediatelyFromProfile:(ProfileIOS*)profile {
-  AuthenticationServiceFactory::GetForBrowserState(profile)->SignOut(
+  AuthenticationServiceFactory::GetForProfile(profile)->SignOut(
       signin_metrics::ProfileSignout::kAbortSignin,
       /*force_clear_browsing_data=*/false, nil);
 }
@@ -243,7 +243,7 @@
     // potential bad memory accesses.
     Browser* alertedBrowser = weakAlert.browser;
     if (alertedBrowser) {
-      PrefService* prefService = alertedBrowser->GetBrowserState()->GetPrefs();
+      PrefService* prefService = alertedBrowser->GetProfile()->GetPrefs();
       // TODO(crbug.com/40225352): Remove this line once we determined that the
       // notification isn't needed anymore.
       [strongSelf updateUserPolicyNotificationStatusIfNeeded:prefService];
@@ -280,9 +280,8 @@
                           browser:(Browser*)browser {
   DCHECK(browser);
   base::WeakPtr<Browser> weakBrowser = browser->AsWeakPtr();
-  ProfileIOS* profile = browser->GetProfile()->GetOriginalChromeBrowserState();
-  syncer::SyncService* syncService =
-      SyncServiceFactory::GetForBrowserState(profile);
+  ProfileIOS* profile = browser->GetProfile()->GetOriginalProfile();
+  syncer::SyncService* syncService = SyncServiceFactory::GetForProfile(profile);
 
   // Signing in from bookmarks and reading list enables the corresponding
   // type.
@@ -316,7 +315,7 @@
     base::RecordAction(
         base::UserMetricsAction("Mobile.Signin.SnackbarUndoTapped"));
     AuthenticationService* authService =
-        AuthenticationServiceFactory::GetForBrowserState(profile);
+        AuthenticationServiceFactory::GetForProfile(profile);
     if (authService->HasPrimaryIdentity(signin::ConsentLevel::kSignin)) {
       // Signing in from bookmarks and reading list enables the corresponding
       // type. The undo button should handle that before signing out.
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow_performer_delegate.h b/ios/chrome/browser/ui/authentication/authentication_flow_performer_delegate.h
index 50a7e6e..2b0b1112 100644
--- a/ios/chrome/browser/ui/authentication/authentication_flow_performer_delegate.h
+++ b/ios/chrome/browser/ui/authentication/authentication_flow_performer_delegate.h
@@ -15,7 +15,7 @@
 // Handles completion of AuthenticationFlowPerformer steps.
 @protocol AuthenticationFlowPerformerDelegate<NSObject>
 
-// Indicates that a browser state was signed out.
+// Indicates that a profile was signed out.
 - (void)didSignOut;
 
 // Indicates that browsing data finished clearing.
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow_performer_unittest.mm b/ios/chrome/browser/ui/authentication/authentication_flow_performer_unittest.mm
index f1ef2488..664c0186 100644
--- a/ios/chrome/browser/ui/authentication/authentication_flow_performer_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/authentication_flow_performer_unittest.mm
@@ -33,15 +33,14 @@
   void SetUp() override {
     PlatformTest::SetUp();
 
-    TestChromeBrowserState::Builder builder;
+    TestProfileIOS::Builder builder;
     builder.AddTestingFactory(
         AuthenticationServiceFactory::GetInstance(),
         AuthenticationServiceFactory::GetDefaultFactory());
-    browser_state_ = std::move(builder).Build();
-    AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
-        browser_state_.get(),
-        std::make_unique<FakeAuthenticationServiceDelegate>());
-    browser_ = std::make_unique<TestBrowser>(browser_state_.get());
+    profile_ = std::move(builder).Build();
+    AuthenticationServiceFactory::CreateAndInitializeForProfile(
+        profile_.get(), std::make_unique<FakeAuthenticationServiceDelegate>());
+    browser_ = std::make_unique<TestBrowser>(profile_.get());
     fake_identity_ = [FakeSystemIdentity fakeIdentity1];
 
     NSArray<Protocol*>* command_protocols = @[
@@ -74,7 +73,7 @@
 
   web::WebTaskEnvironment task_environment_;
   IOSChromeScopedTestingLocalState scoped_testing_local_state_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
   std::unique_ptr<Browser> browser_;
   AuthenticationFlowPerformer* authentication_flow_performer_ = nil;
   id<AuthenticationFlowPerformerDelegate>
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow_unittest.mm b/ios/chrome/browser/ui/authentication/authentication_flow_unittest.mm
index 7afd5ba..dde8080 100644
--- a/ios/chrome/browser/ui/authentication/authentication_flow_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/authentication_flow_unittest.mm
@@ -70,7 +70,7 @@
         AuthenticationServiceFactory::GetDefaultFactory());
     builder.SetPrefService(CreatePrefService());
     profile_ = std::move(builder).Build();
-    AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
+    AuthenticationServiceFactory::CreateAndInitializeForProfile(
         profile_.get(), std::make_unique<FakeAuthenticationServiceDelegate>());
     browser_ = std::make_unique<TestBrowser>(profile_.get());
 
@@ -209,7 +209,7 @@
 
   void SignOut() {
     AuthenticationService* authentication_service =
-        AuthenticationServiceFactory::GetForBrowserState(profile_.get());
+        AuthenticationServiceFactory::GetForProfile(profile_.get());
     // Can't use a RunLoop multiple times, create a new one.
     run_loop_ = std::make_unique<base::RunLoop>();
     authentication_service->SignOut(
diff --git a/ios/chrome/browser/ui/authentication/authentication_ui_util.mm b/ios/chrome/browser/ui/authentication/authentication_ui_util.mm
index bdd5f32..b0fa866 100644
--- a/ios/chrome/browser/ui/authentication/authentication_ui_util.mm
+++ b/ios/chrome/browser/ui/authentication/authentication_ui_util.mm
@@ -20,7 +20,7 @@
 
 std::u16string HostedDomainForPrimaryAccount(Browser* browser) {
   signin::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForProfile(browser->GetBrowserState());
+      IdentityManagerFactory::GetForProfile(browser->GetProfile());
   return base::UTF8ToUTF16(
       identity_manager
           ->FindExtendedAccountInfo(identity_manager->GetPrimaryAccountInfo(
diff --git a/ios/chrome/browser/ui/authentication/history_sync/history_sync_capabilities_fetcher_unittest.mm b/ios/chrome/browser/ui/authentication/history_sync/history_sync_capabilities_fetcher_unittest.mm
index c23444b..5eb700d9 100644
--- a/ios/chrome/browser/ui/authentication/history_sync/history_sync_capabilities_fetcher_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/history_sync/history_sync_capabilities_fetcher_unittest.mm
@@ -32,8 +32,8 @@
 
   void SetUp() override {
     PlatformTest::SetUp();
-    TestChromeBrowserState::Builder builder;
-    browser_state_ = std::move(builder).Build();
+    TestProfileIOS::Builder builder;
+    profile_ = std::move(builder).Build();
   }
 
   void TearDown() override {
@@ -72,7 +72,7 @@
  protected:
   web::WebTaskEnvironment task_environment_;
   signin::IdentityTestEnvironment identity_test_env_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
   HistorySyncCapabilitiesFetcher* fetcher_ = nil;
 };
 
diff --git a/ios/chrome/browser/ui/authentication/history_sync/history_sync_coordinator.mm b/ios/chrome/browser/ui/authentication/history_sync/history_sync_coordinator.mm
index a604be0..2e3f1f9 100644
--- a/ios/chrome/browser/ui/authentication/history_sync/history_sync_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/history_sync/history_sync_coordinator.mm
@@ -143,13 +143,12 @@
 
 - (void)start {
   [super start];
-  ChromeBrowserState* browserState = self.browser->GetBrowserState();
-  CHECK_EQ(browserState, browserState->GetOriginalChromeBrowserState());
+  ProfileIOS* profile = self.browser->GetProfile();
+  CHECK_EQ(profile, profile->GetOriginalProfile());
   AuthenticationService* authenticationService =
-      AuthenticationServiceFactory::GetForBrowserState(browserState);
-  syncer::SyncService* syncService =
-      SyncServiceFactory::GetForBrowserState(browserState);
-  _prefService = browserState->GetPrefs();
+      AuthenticationServiceFactory::GetForProfile(profile);
+  syncer::SyncService* syncService = SyncServiceFactory::GetForProfile(profile);
+  _prefService = profile->GetPrefs();
   // Check if History Sync Opt-In should be skipped.
   HistorySyncSkipReason skipReason = [HistorySyncCoordinator
       getHistorySyncOptInSkipReason:syncService
@@ -167,9 +166,9 @@
   _viewController.delegate = self;
 
   ChromeAccountManagerService* chromeAccountManagerService =
-      ChromeAccountManagerServiceFactory::GetForBrowserState(browserState);
+      ChromeAccountManagerServiceFactory::GetForProfile(profile);
   signin::IdentityManager* identityManager =
-      IdentityManagerFactory::GetForProfile(browserState);
+      IdentityManagerFactory::GetForProfile(profile);
   _mediator = [[HistorySyncMediator alloc]
       initWithAuthenticationService:authenticationService
         chromeAccountManagerService:chromeAccountManagerService
diff --git a/ios/chrome/browser/ui/authentication/history_sync/history_sync_popup_coordinator.mm b/ios/chrome/browser/ui/authentication/history_sync/history_sync_popup_coordinator.mm
index cbe1125..22a153d3 100644
--- a/ios/chrome/browser/ui/authentication/history_sync/history_sync_popup_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/history_sync/history_sync_popup_coordinator.mm
@@ -63,17 +63,15 @@
 
 - (void)start {
   [super start];
-  ChromeBrowserState* browserState = self.browser->GetBrowserState();
-  CHECK_EQ(browserState, browserState->GetOriginalChromeBrowserState());
-  _authenticationService =
-      AuthenticationServiceFactory::GetForBrowserState(browserState);
-  syncer::SyncService* syncService =
-      SyncServiceFactory::GetForBrowserState(browserState);
+  ProfileIOS* profile = self.browser->GetProfile();
+  CHECK_EQ(profile, profile->GetOriginalProfile());
+  _authenticationService = AuthenticationServiceFactory::GetForProfile(profile);
+  syncer::SyncService* syncService = SyncServiceFactory::GetForProfile(profile);
   // Check if History Sync Opt-In should be skipped.
   HistorySyncSkipReason skipReason = [HistorySyncCoordinator
       getHistorySyncOptInSkipReason:syncService
               authenticationService:_authenticationService
-                        prefService:browserState->GetPrefs()
+                        prefService:profile->GetPrefs()
               isHistorySyncOptional:_isOptional];
   if (skipReason != HistorySyncSkipReason::kNone) {
     [HistorySyncCoordinator recordHistorySyncSkipMetric:skipReason
diff --git a/ios/chrome/browser/ui/authentication/history_sync/history_sync_utils.h b/ios/chrome/browser/ui/authentication/history_sync/history_sync_utils.h
index 4583081..176e6f3 100644
--- a/ios/chrome/browser/ui/authentication/history_sync/history_sync_utils.h
+++ b/ios/chrome/browser/ui/authentication/history_sync/history_sync_utils.h
@@ -14,7 +14,7 @@
 namespace history_sync {
 
 // Registers prefs used to skip too frequent History Sync Opt-In prompt.
-void RegisterBrowserStatePrefs(user_prefs::PrefRegistrySyncable* registry);
+void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
 // Resets prefs related to declined History Sync Opt-In prompt.
 void ResetDeclinePrefs(PrefService* pref_service);
diff --git a/ios/chrome/browser/ui/authentication/history_sync/history_sync_utils.mm b/ios/chrome/browser/ui/authentication/history_sync/history_sync_utils.mm
index 7bf11289..11ea7ff 100644
--- a/ios/chrome/browser/ui/authentication/history_sync/history_sync_utils.mm
+++ b/ios/chrome/browser/ui/authentication/history_sync/history_sync_utils.mm
@@ -23,7 +23,7 @@
 
 namespace history_sync {
 
-void RegisterBrowserStatePrefs(user_prefs::PrefRegistrySyncable* registry) {
+void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterTimePref(
       history_sync_prefs::kHistorySyncLastDeclinedTimestamp, base::Time());
   registry->RegisterIntegerPref(
diff --git a/ios/chrome/browser/ui/authentication/identity_chooser/identity_chooser_coordinator.mm b/ios/chrome/browser/ui/authentication/identity_chooser/identity_chooser_coordinator.mm
index 172c201..bd434ef 100644
--- a/ios/chrome/browser/ui/authentication/identity_chooser/identity_chooser_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/identity_chooser/identity_chooser_coordinator.mm
@@ -79,8 +79,8 @@
   // Creates the mediator.
   self.identityChooserMediator = [[IdentityChooserMediator alloc]
       initWithAccountManagerService:ChromeAccountManagerServiceFactory::
-                                        GetForBrowserState(
-                                            self.browser->GetBrowserState())];
+                                        GetForProfile(
+                                            self.browser->GetProfile())];
 
   self.identityChooserMediator.consumer = self.identityChooserViewController;
   // Setups.
diff --git a/ios/chrome/browser/ui/authentication/identity_chooser/identity_chooser_coordinator_unittest.mm b/ios/chrome/browser/ui/authentication/identity_chooser/identity_chooser_coordinator_unittest.mm
index 100aa0b..bfcd8bf 100644
--- a/ios/chrome/browser/ui/authentication/identity_chooser/identity_chooser_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/identity_chooser/identity_chooser_coordinator_unittest.mm
@@ -25,8 +25,8 @@
 class IdentityChooserCoordinatorTest : public PlatformTest {
  public:
   IdentityChooserCoordinatorTest() {
-    browser_state_ = TestChromeBrowserState::Builder().Build();
-    browser_ = std::make_unique<TestBrowser>(browser_state_.get());
+    profile_ = TestProfileIOS::Builder().Build();
+    browser_ = std::make_unique<TestBrowser>(profile_.get());
     view_controller_ = [[UIViewController alloc] init];
     [scoped_key_window_.Get() setRootViewController:view_controller_];
   }
@@ -53,9 +53,9 @@
   }
 
  protected:
-  // Needed for test browser state created by TestChromeBrowserState().
+  // Needed for test profile created by TestProfileIOS().
   web::WebTaskEnvironment task_environment_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
   std::unique_ptr<TestBrowser> browser_;
   ScopedKeyWindow scoped_key_window_;
 
diff --git a/ios/chrome/browser/ui/authentication/re_signin_infobar_delegate_unittest.mm b/ios/chrome/browser/ui/authentication/re_signin_infobar_delegate_unittest.mm
index 1dbac18..2fbe5ec 100644
--- a/ios/chrome/browser/ui/authentication/re_signin_infobar_delegate_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/re_signin_infobar_delegate_unittest.mm
@@ -45,21 +45,20 @@
     OCMStub([mock_app_state_ initStage]).andDo(^(NSInvocation* invocation) {
       [invocation setReturnValue:&init_stage_];
     });
-    TestChromeBrowserState::Builder builder;
+    TestProfileIOS::Builder builder;
     builder.AddTestingFactory(
         AuthenticationServiceFactory::GetInstance(),
         AuthenticationServiceFactory::GetDefaultFactory());
-    chrome_browser_state_ = std::move(builder).Build();
-    browser_ = std::make_unique<TestBrowser>(chrome_browser_state_.get());
+    profile_ = std::move(builder).Build();
+    browser_ = std::make_unique<TestBrowser>(profile_.get());
     auto fake_web_state = std::make_unique<web::FakeWebState>();
     fake_web_state->SetNavigationManager(
         std::make_unique<web::FakeNavigationManager>());
     browser_->GetWebStateList()->InsertWebState(
         std::move(fake_web_state),
         WebStateList::InsertionParams::Automatic().Activate());
-    AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
-        chrome_browser_state_.get(),
-        std::make_unique<FakeAuthenticationServiceDelegate>());
+    AuthenticationServiceFactory::CreateAndInitializeForProfile(
+        profile_.get(), std::make_unique<FakeAuthenticationServiceDelegate>());
     InfoBarManagerImpl::CreateForWebState(web_state());
   }
 
@@ -68,7 +67,7 @@
     EXPECT_OCMOCK_VERIFY((id)mock_app_state_);
   }
 
-  void SetUpMainChromeBrowserStateWithSignedInUser() {
+  void SetUpMainProfileIOSWithSignedInUser() {
     id<SystemIdentity> chrome_identity = [FakeSystemIdentity fakeIdentity1];
     FakeSystemIdentityManager* system_identity_manager =
         FakeSystemIdentityManager::FromSystemIdentityManager(
@@ -81,13 +80,12 @@
 
   AuthenticationService* authentication_service() {
     // AuthenticationService currently has no good fake, so constructing the
-    // production one via TestChromeBrowserState is the best we can do.
-    return AuthenticationServiceFactory::GetForBrowserState(
-        chrome_browser_state_.get());
+    // production one via TestProfileIOS is the best we can do.
+    return AuthenticationServiceFactory::GetForProfile(profile_.get());
   }
 
   signin::IdentityManager* identity_manager() {
-    return IdentityManagerFactory::GetForProfile(chrome_browser_state_.get());
+    return IdentityManagerFactory::GetForProfile(profile_.get());
   }
 
   OCMockObject<SigninPresenter>* signin_presenter() {
@@ -105,7 +103,7 @@
  private:
   web::WebTaskEnvironment task_environment_;
   IOSChromeScopedTestingLocalState scoped_testing_local_state_;
-  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
   std::unique_ptr<TestBrowser> browser_;
   std::unique_ptr<web::NavigationManager> test_navigation_manager_;
   OCMockObject<SigninPresenter>* signin_presenter_ =
@@ -141,7 +139,7 @@
 
 TEST_F(ReSignInInfoBarDelegateTest, TestCreateWhenAlreadySignedIn) {
   // User is signed in and the "prompt" flag is set.
-  SetUpMainChromeBrowserStateWithSignedInUser();
+  SetUpMainProfileIOSWithSignedInUser();
   authentication_service()->SetReauthPromptForSignInAndSync();
   std::unique_ptr<ReSignInInfoBarDelegate> infobar_delegate =
       ReSignInInfoBarDelegate::Create(authentication_service(),
diff --git a/ios/chrome/browser/ui/authentication/signin/add_account_signin/add_account_signin_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/add_account_signin/add_account_signin_coordinator.mm
index 0593262..ee94a0e 100644
--- a/ios/chrome/browser/ui/authentication/signin/add_account_signin/add_account_signin_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/add_account_signin/add_account_signin_coordinator.mm
@@ -105,18 +105,17 @@
 
 - (void)start {
   [super start];
-  ChromeBrowserState* browserState = self.browser->GetBrowserState();
+  ProfileIOS* profile = self.browser->GetProfile();
   self.accountManagerService =
-      ChromeAccountManagerServiceFactory::GetForBrowserState(browserState);
+      ChromeAccountManagerServiceFactory::GetForProfile(profile);
   id<SystemIdentityInteractionManager> identityInteractionManager =
       GetApplicationContext()
           ->GetSystemIdentityManager()
           ->CreateInteractionManager();
   self.addAccountSigninManager = [[AddAccountSigninManager alloc]
       initWithBaseViewController:self.baseViewController
-                     prefService:browserState->GetPrefs()
-                 identityManager:IdentityManagerFactory::GetForProfile(
-                                     browserState)
+                     prefService:profile->GetPrefs()
+                 identityManager:IdentityManagerFactory::GetForProfile(profile)
       identityInteractionManager:identityInteractionManager];
   self.addAccountSigninManager.delegate = self;
   [self.addAccountSigninManager showSigninWithIntent:self.signinIntent];
@@ -297,8 +296,7 @@
   self.historySyncPopupCoordinator = nil;
 
   AuthenticationService* authService =
-      AuthenticationServiceFactory::GetForBrowserState(
-          self.browser->GetBrowserState());
+      AuthenticationServiceFactory::GetForProfile(self.browser->GetProfile());
   // Even if `result` is not "success" for the history opt-in step, the sign-in
   // step did succeed, so pass SigninCoordinatorResultSuccess.
   [self addAccountDoneWithSigninResult:SigninCoordinatorResultSuccess
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_coordinator.mm
index 515895a..a5c0ebc 100644
--- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_coordinator.mm
@@ -37,8 +37,7 @@
   self.mediator = [[ConsistencyAccountChooserMediator alloc]
       initWithSelectedIdentity:selectedIdentity
          accountManagerService:ChromeAccountManagerServiceFactory::
-                                   GetForBrowserState(
-                                       self.browser->GetBrowserState())];
+                                   GetForProfile(self.browser->GetProfile())];
 
   self.accountChooserViewController =
       [[ConsistencyAccountChooserViewController alloc] init];
@@ -74,8 +73,8 @@
             (ConsistencyAccountChooserTableViewController*)viewController
                          didSelectIdentityWithGaiaID:(NSString*)gaiaID {
   ChromeAccountManagerService* accountManagerService =
-      ChromeAccountManagerServiceFactory::GetForBrowserState(
-          self.browser->GetBrowserState());
+      ChromeAccountManagerServiceFactory::GetForProfile(
+          self.browser->GetProfile());
 
   id<SystemIdentity> identity = accountManagerService->GetIdentityWithGaiaID(
       base::SysNSStringToUTF8(gaiaID));
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_mediator.h b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_mediator.h
index bdacc53..8646ad52 100644
--- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_mediator.h
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_mediator.h
@@ -21,7 +21,7 @@
 @property(nonatomic, strong) id<ConsistencyAccountChooserConsumer> consumer;
 @property(nonatomic, strong) id<SystemIdentity> selectedIdentity;
 
-// See -[SigninPromoViewMediator initWithBrowserState:].
+// See -[SigninPromoViewMediator initWithProfile:].
 - (instancetype)init NS_UNAVAILABLE;
 
 - (instancetype)initWithSelectedIdentity:(id<SystemIdentity>)selectedIdentity
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_default_account/consistency_default_account_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_default_account/consistency_default_account_coordinator.mm
index c218c79a..31418f91 100644
--- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_default_account/consistency_default_account_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_default_account/consistency_default_account_coordinator.mm
@@ -42,12 +42,11 @@
 
 - (void)start {
   [super start];
-  ChromeBrowserState* browserState = self.browser->GetBrowserState();
+  ProfileIOS* profile = self.browser->GetProfile();
   self.mediator = [[ConsistencyDefaultAccountMediator alloc]
       initWithAccountManagerService:ChromeAccountManagerServiceFactory::
-                                        GetForBrowserState(browserState)
-                        syncService:SyncServiceFactory::GetForBrowserState(
-                                        browserState)
+                                        GetForProfile(profile)
+                        syncService:SyncServiceFactory::GetForProfile(profile)
                         accessPoint:self.accessPoint];
   self.defaultAccountViewController =
       [[ConsistencyDefaultAccountViewController alloc] init];
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm
index 6a5bdb1..e6be781 100644
--- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm
@@ -70,9 +70,9 @@
     coordinatorWithBaseViewController:(UIViewController*)viewController
                               browser:(Browser*)browser
                           accessPoint:(signin_metrics::AccessPoint)accessPoint {
-  ChromeBrowserState* browserState = browser->GetBrowserState();
+  ProfileIOS* profile = browser->GetProfile();
   ChromeAccountManagerService* accountManagerService =
-      ChromeAccountManagerServiceFactory::GetForBrowserState(browserState);
+      ChromeAccountManagerServiceFactory::GetForProfile(profile);
   if (!accountManagerService->HasIdentities() &&
       accessPoint == signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN) {
     RecordConsistencyPromoUserAction(
@@ -110,18 +110,18 @@
   signin_metrics::LogSignInStarted(self.accessPoint);
   base::RecordAction(base::UserMetricsAction("Signin_BottomSheet_Opened"));
   // Create ConsistencyPromoSigninMediator.
-  ChromeBrowserState* browserState = self.browser->GetBrowserState();
+  ProfileIOS* profile = self.browser->GetProfile();
   signin::IdentityManager* identityManager =
-      IdentityManagerFactory::GetForProfile(browserState);
+      IdentityManagerFactory::GetForProfile(profile);
   ChromeAccountManagerService* accountManagerService =
-      ChromeAccountManagerServiceFactory::GetForBrowserState(browserState);
+      ChromeAccountManagerServiceFactory::GetForProfile(profile);
   AuthenticationService* authenticationService =
-      AuthenticationServiceFactory::GetForBrowserState(browserState);
+      AuthenticationServiceFactory::GetForProfile(profile);
   self.consistencyPromoSigninMediator = [[ConsistencyPromoSigninMediator alloc]
       initWithAccountManagerService:accountManagerService
               authenticationService:authenticationService
                     identityManager:identityManager
-                    userPrefService:browserState->GetPrefs()
+                    userPrefService:profile->GetPrefs()
                         accessPoint:self.accessPoint];
   self.consistencyPromoSigninMediator.delegate = self;
   // Create ConsistencyDefaultAccountCoordinator.
@@ -320,8 +320,8 @@
 
 - (void)consistencyDefaultAccountCoordinatorSkip:
     (ConsistencyDefaultAccountCoordinator*)coordinator {
-  ChromeBrowserState* browserState = self.browser->GetBrowserState();
-  PrefService* userPrefService = browserState->GetPrefs();
+  ProfileIOS* profile = self.browser->GetProfile();
+  PrefService* userPrefService = profile->GetPrefs();
   if (self.accessPoint ==
       signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN) {
     const int skipCounter =
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_mediator_unittest.mm b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_mediator_unittest.mm
index 0786007..231c72f 100644
--- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_mediator_unittest.mm
@@ -42,16 +42,14 @@
     PlatformTest::SetUp();
     GetSystemIdentityManager()->AddIdentity(kDefaultIdentity);
     GetSystemIdentityManager()->AddIdentity(kNonDefaultIdentity);
-    TestChromeBrowserState::Builder builder;
+    TestProfileIOS::Builder builder;
     builder.AddTestingFactory(
         AuthenticationServiceFactory::GetInstance(),
         AuthenticationServiceFactory::GetDefaultFactory());
-    browser_state_ = std::move(builder).Build();
-    AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
-        browser_state_.get(),
-        std::make_unique<FakeAuthenticationServiceDelegate>());
-    ASSERT_EQ(ChromeAccountManagerServiceFactory::GetForBrowserState(
-                  browser_state_.get())
+    profile_ = std::move(builder).Build();
+    AuthenticationServiceFactory::CreateAndInitializeForProfile(
+        profile_.get(), std::make_unique<FakeAuthenticationServiceDelegate>());
+    ASSERT_EQ(ChromeAccountManagerServiceFactory::GetForProfile(profile_.get())
                   ->GetDefaultIdentity(),
               kDefaultIdentity);
   }
@@ -63,7 +61,7 @@
   }
 
   sync_preferences::TestingPrefServiceSyncable* GetPrefService() {
-    return browser_state_->GetTestingPrefService();
+    return profile_->GetTestingPrefService();
   }
 
   FakeSystemIdentityManager* GetSystemIdentityManager() {
@@ -74,12 +72,11 @@
   ConsistencyPromoSigninMediator* BuildConsistencyPromoSigninMediator(
       signin_metrics::AccessPoint access_point) {
     ChromeAccountManagerService* chrome_account_manager_service =
-        ChromeAccountManagerServiceFactory::GetForBrowserState(
-            browser_state_.get());
+        ChromeAccountManagerServiceFactory::GetForProfile(profile_.get());
     AuthenticationService* auth_service =
-        AuthenticationServiceFactory::GetForBrowserState(browser_state_.get());
+        AuthenticationServiceFactory::GetForProfile(profile_.get());
     signin::IdentityManager* identity_manager =
-        IdentityManagerFactory::GetForProfile(browser_state_.get());
+        IdentityManagerFactory::GetForProfile(profile_.get());
     ConsistencyPromoSigninMediator* mediator =
         [[ConsistencyPromoSigninMediator alloc]
             initWithAccountManagerService:chrome_account_manager_service
@@ -130,7 +127,7 @@
         consistencyPromoSigninMediatorSigninStarted:[OCMArg any]]);
     OCMExpect([authentication_flow_ identity]).andReturn(identity);
     AuthenticationService* auth_service =
-        AuthenticationServiceFactory::GetForBrowserState(browser_state_.get());
+        AuthenticationServiceFactory::GetForProfile(profile_.get());
     OCMExpect([authentication_flow_
         startSignInWithCompletion:[OCMArg
                                       checkWithBlock:^BOOL(
@@ -152,11 +149,11 @@
       OCMStrictProtocolMock(@protocol(ConsistencyPromoSigninMediatorDelegate));
 
  private:
-  // Needed for test browser state.
+  // Needed for test profile.
   web::WebTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   IOSChromeScopedTestingLocalState scoped_testing_local_state_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
 };
 
 // Tests start and cancel by user.
diff --git a/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_coordinator.mm
index 067a922..be03845 100644
--- a/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_coordinator.mm
@@ -36,7 +36,7 @@
                             screenProvider:(ScreenProvider*)screenProvider
                                accessPoint:
                                    (signin_metrics::AccessPoint)accessPoint {
-  DCHECK(!browser->GetBrowserState()->IsOffTheRecord());
+  DCHECK(!browser->GetProfile()->IsOffTheRecord());
   self = [super initWithBaseViewController:viewController
                                    browser:browser
                                accessPoint:accessPoint];
@@ -79,8 +79,7 @@
 - (void)finishPresentingScreens {
   __weak __typeof(self) weakSelf = self;
   AuthenticationService* authService =
-      AuthenticationServiceFactory::GetForBrowserState(
-          self.browser->GetBrowserState());
+      AuthenticationServiceFactory::GetForProfile(self.browser->GetProfile());
   id<SystemIdentity> identity =
       authService->GetPrimaryIdentity(signin::ConsentLevel::kSignin);
   void (^completion)(void) = ^{
diff --git a/ios/chrome/browser/ui/authentication/signin/instant_signin/instant_signin_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/instant_signin/instant_signin_coordinator.mm
index 3dccdd5..7723395 100644
--- a/ios/chrome/browser/ui/authentication/signin/instant_signin/instant_signin_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/instant_signin/instant_signin_coordinator.mm
@@ -73,9 +73,9 @@
 - (void)start {
   [super start];
   signin_metrics::LogSignInStarted(self.accessPoint);
-  ChromeBrowserState* chromeState = self.browser->GetBrowserState();
+  ProfileIOS* profile = self.browser->GetProfile();
   syncer::SyncService* syncService =
-      SyncServiceFactory::GetForBrowserState(chromeState);
+      SyncServiceFactory::GetForProfile(profile);
   _mediator =
       [[InstantSigninMediator alloc] initWithSyncService:syncService
                                              accessPoint:self.accessPoint];
@@ -96,7 +96,7 @@
   }
 
   ChromeAccountManagerService* accountManagerService =
-      ChromeAccountManagerServiceFactory::GetForBrowserState(chromeState);
+      ChromeAccountManagerServiceFactory::GetForProfile(profile);
   if (!accountManagerService->HasIdentities()) {
     signin_metrics::RecordConsistencyPromoUserAction(
         signin_metrics::AccountConsistencyPromoAction::
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_coordinator.h b/ios/chrome/browser/ui/authentication/signin/signin_coordinator.h
index f32a18d..98d5892 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_coordinator.h
+++ b/ios/chrome/browser/ui/authentication/signin/signin_coordinator.h
@@ -52,7 +52,7 @@
                                    browser:(Browser*)browser NS_UNAVAILABLE;
 
 // Registers preferences related to sign-in coordinator.
-+ (void)registerBrowserStatePrefs:(user_prefs::PrefRegistrySyncable*)registry;
++ (void)registerProfilePrefs:(user_prefs::PrefRegistrySyncable*)registry;
 
 // Returns a coordinator to sign-in the user without taps if the identity has
 // been selected with `identity`. Otherwise, it will ask the user to select
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/signin_coordinator.mm
index 4b7f2143..2cfe7b9 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/signin_coordinator.mm
@@ -43,7 +43,7 @@
   return self;
 }
 
-+ (void)registerBrowserStatePrefs:(user_prefs::PrefRegistrySyncable*)registry {
++ (void)registerProfilePrefs:(user_prefs::PrefRegistrySyncable*)registry {
   // ConsistencyPromoSigninCoordinator.
   registry->RegisterIntegerPref(prefs::kSigninWebSignDismissalCount, 0);
   registry->RegisterDictionaryPref(prefs::kSigninHasAcceptedManagementDialog);
@@ -157,7 +157,7 @@
                                                           (signin_metrics::
                                                                AccessPoint)
                                                               accessPoint {
-  DCHECK(!browser->GetBrowserState()->IsOffTheRecord());
+  DCHECK(!browser->GetProfile()->IsOffTheRecord());
   return [[TrustedVaultReauthenticationCoordinator alloc]
       initWithBaseViewController:viewController
                          browser:browser
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_history_sync/signin_and_history_sync_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/signin_history_sync/signin_and_history_sync_coordinator.mm
index 989a8327..1ecd875 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_history_sync/signin_and_history_sync_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/signin_history_sync/signin_and_history_sync_coordinator.mm
@@ -128,8 +128,7 @@
   // If there are no steps remaining, call delegate to stop presenting
   // coordinators.
   AuthenticationService* authService =
-      AuthenticationServiceFactory::GetForBrowserState(
-          self.browser->GetBrowserState());
+      AuthenticationServiceFactory::GetForProfile(self.browser->GetProfile());
   id<SystemIdentity> identity =
       authService->GetPrimaryIdentity(signin::ConsentLevel::kSignin);
   SigninCoordinatorResult result;
@@ -213,8 +212,8 @@
   switch (_currentStep) {
     case SignInHistorySyncStep::kStart: {
       ChromeAccountManagerService* accountManagerService =
-          ChromeAccountManagerServiceFactory::GetForBrowserState(
-              self.browser->GetBrowserState());
+          ChromeAccountManagerServiceFactory::GetForProfile(
+              self.browser->GetProfile());
       if (accountManagerService->HasIdentities()) {
         return SignInHistorySyncStep::kBottomSheetSignin;
       }
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_utils.h b/ios/chrome/browser/ui/authentication/signin/signin_utils.h
index 7c0df27..f4d1460 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_utils.h
+++ b/ios/chrome/browser/ui/authentication/signin/signin_utils.h
@@ -24,13 +24,13 @@
 // Returns the maximum allowed waiting time for the Account Capabilities API.
 base::TimeDelta GetWaitThresholdForCapabilities();
 
-// Returns true if this user sign-in upgrade should be shown for `browserState`.
-bool ShouldPresentUserSigninUpgrade(ChromeBrowserState* browser_state,
+// Returns true if this user sign-in upgrade should be shown for `profile`.
+bool ShouldPresentUserSigninUpgrade(ProfileIOS* profile,
                                     const base::Version& current_version);
 
 // Returns true if the web sign-in dialog can be presented. If false, user
 // actions is recorded to track why the sign-in dialog was not presented.
-bool ShouldPresentWebSignin(ChromeBrowserState* browser_state);
+bool ShouldPresentWebSignin(ProfileIOS* profile);
 
 // This method should be called when sign-in starts from the upgrade promo.
 // It records in user defaults:
@@ -43,8 +43,7 @@
     const base::Version& current_version);
 
 // Returns the current sign-in state of primary identity.
-IdentitySigninState GetPrimaryIdentitySigninState(
-    ChromeBrowserState* browser_state);
+IdentitySigninState GetPrimaryIdentitySigninState(ProfileIOS* profile);
 
 // Converts a SystemIdentityCapabilityResult to a Tribool.
 Tribool TriboolFromCapabilityResult(SystemIdentityCapabilityResult result);
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_utils.mm b/ios/chrome/browser/ui/authentication/signin/signin_utils.mm
index 26dc0008..e59829dc 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_utils.mm
+++ b/ios/chrome/browser/ui/authentication/signin/signin_utils.mm
@@ -89,16 +89,17 @@
   return kShowSigninUpgradePromoMaxDelay;
 }
 
-bool ShouldPresentUserSigninUpgrade(ChromeBrowserState* browser_state,
+bool ShouldPresentUserSigninUpgrade(ProfileIOS* profile,
                                     const base::Version& current_version) {
-  DCHECK(browser_state);
+  DCHECK(profile);
   DCHECK(current_version.IsValid());
 
   if (tests_hook::DisableUpgradeSigninPromo())
     return false;
 
-  if (browser_state->IsOffTheRecord())
+  if (profile->IsOffTheRecord()) {
     return false;
+  }
 
   // There will be an error shown if the user chooses to sign in or select
   // another account while offline.
@@ -107,7 +108,7 @@
 
   // Sign-in can be disabled by policy or through user Settings.
   AuthenticationService* authentication_service =
-      AuthenticationServiceFactory::GetForBrowserState(browser_state);
+      AuthenticationServiceFactory::GetForProfile(profile);
   switch (authentication_service->GetServiceStatus()) {
     case AuthenticationService::ServiceStatus::SigninDisabledByUser:
     case AuthenticationService::ServiceStatus::SigninDisabledByInternal:
@@ -119,17 +120,17 @@
   }
 
   AuthenticationService* auth_service =
-      AuthenticationServiceFactory::GetForBrowserState(browser_state);
+      AuthenticationServiceFactory::GetForProfile(profile);
   if (auth_service->HasPrimaryIdentity(signin::ConsentLevel::kSync)) {
     return false;
   }
   if (auth_service->HasPrimaryIdentity(signin::ConsentLevel::kSignin)) {
     syncer::SyncService* sync_service =
-        SyncServiceFactory::GetForBrowserState(browser_state);
+        SyncServiceFactory::GetForProfile(profile);
     HistorySyncSkipReason skip_reason = [HistorySyncCoordinator
         getHistorySyncOptInSkipReason:sync_service
                 authenticationService:auth_service
-                          prefService:browser_state->GetPrefs()
+                          prefService:profile->GetPrefs()
                 isHistorySyncOptional:YES];
     switch (skip_reason) {
       case HistorySyncSkipReason::kNone:
@@ -146,7 +147,7 @@
 
   // Avoid showing the upgrade sign-in promo when the device restore sign-in
   // promo should be shown instead.
-  if (GetPreRestoreIdentity(browser_state->GetPrefs()).has_value()) {
+  if (GetPreRestoreIdentity(profile->GetPrefs()).has_value()) {
     return false;
   }
 
@@ -154,7 +155,7 @@
   // before ForceStartupSigninPromo() to avoid any DCHECK failures if
   // ForceStartupSigninPromo() returns true.
   ChromeAccountManagerService* account_manager_service =
-      ChromeAccountManagerServiceFactory::GetForBrowserState(browser_state);
+      ChromeAccountManagerServiceFactory::GetForProfile(profile);
   NSArray<id<SystemIdentity>>* identities =
       account_manager_service->GetAllIdentities();
   if (identities.count == 0)
@@ -197,9 +198,9 @@
   return IsStrictSubset(last_known_gaia_id_list, identities);
 }
 
-bool ShouldPresentWebSignin(ChromeBrowserState* browser_state) {
+bool ShouldPresentWebSignin(ProfileIOS* profile) {
   AuthenticationService* authentication_service =
-      AuthenticationServiceFactory::GetForBrowserState(browser_state);
+      AuthenticationServiceFactory::GetForProfile(profile);
   if (authentication_service->HasPrimaryIdentity(
           signin::ConsentLevel::kSignin)) {
     // For some reasons, Gaia might ask for the web sign-in while the user is
@@ -231,7 +232,7 @@
       break;
   }
   // Show the sign-in dialog less than `kSigninWebSignDismissalCount` times.
-  PrefService* user_pref_service = browser_state->GetPrefs();
+  PrefService* user_pref_service = profile->GetPrefs();
   const int current_dismissal_count =
       user_pref_service->GetInteger(prefs::kSigninWebSignDismissalCount);
   if (current_dismissal_count >= kDefaultWebSignInDismissalCount) {
@@ -264,12 +265,10 @@
   [defaults setInteger:display_count forKey:kSigninPromoViewDisplayCountKey];
 }
 
-IdentitySigninState GetPrimaryIdentitySigninState(
-    ChromeBrowserState* browser_state) {
+IdentitySigninState GetPrimaryIdentitySigninState(ProfileIOS* profile) {
   AuthenticationService* auth_service =
-      AuthenticationServiceFactory::GetForBrowserState(browser_state);
-  syncer::SyncService* syncService =
-      SyncServiceFactory::GetForBrowserState(browser_state);
+      AuthenticationServiceFactory::GetForProfile(profile);
+  syncer::SyncService* syncService = SyncServiceFactory::GetForProfile(profile);
   // TODO(crbug.com/40066949): After phase 3 migration of kSync users, Remove
   // this usage.
   if (auth_service->HasPrimaryIdentity(signin::ConsentLevel::kSync) &&
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_utils_unittest.mm b/ios/chrome/browser/ui/authentication/signin/signin_utils_unittest.mm
index 599cffb..495c01a 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_utils_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/signin/signin_utils_unittest.mm
@@ -46,20 +46,18 @@
 
   void SetUp() override {
     PlatformTest::SetUp();
-    TestChromeBrowserState::Builder builder;
+    TestProfileIOS::Builder builder;
     builder.SetPrefService(CreatePrefService());
     builder.AddTestingFactory(
         AuthenticationServiceFactory::GetInstance(),
         AuthenticationServiceFactory::GetDefaultFactory());
     builder.AddTestingFactory(SyncServiceFactory::GetInstance(),
                               SyncServiceFactory::GetDefaultFactory());
-    chrome_browser_state_ = std::move(builder).Build();
-    AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
-        chrome_browser_state_.get(),
-        std::make_unique<FakeAuthenticationServiceDelegate>());
+    profile_ = std::move(builder).Build();
+    AuthenticationServiceFactory::CreateAndInitializeForProfile(
+        profile_.get(), std::make_unique<FakeAuthenticationServiceDelegate>());
     account_manager_service_ =
-        ChromeAccountManagerServiceFactory::GetForBrowserState(
-            chrome_browser_state_.get());
+        ChromeAccountManagerServiceFactory::GetForProfile(profile_.get());
   }
 
   void TearDown() override {
@@ -85,9 +83,7 @@
     return GetApplicationContext()->GetLocalState();
   }
 
-  PrefService* GetProfilePrefs() {
-    return chrome_browser_state_.get()->GetPrefs();
-  }
+  PrefService* GetProfilePrefs() { return profile_.get()->GetPrefs(); }
 
   FakeSystemIdentityManager* fake_system_identity_manager() {
     return FakeSystemIdentityManager::FromSystemIdentityManager(
@@ -97,7 +93,7 @@
  protected:
   web::WebTaskEnvironment task_environment_;
   IOSChromeScopedTestingLocalState scoped_testing_local_state_;
-  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
   raw_ptr<ChromeAccountManagerService> account_manager_service_;
 };
 
@@ -108,8 +104,8 @@
   FakeSystemIdentity* fake_identity2 = [FakeSystemIdentity fakeIdentity2];
   fake_system_identity_manager()->AddIdentity(fake_identity2);
   const base::Version version_1_0("1.0");
-  EXPECT_FALSE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_1_0));
+  EXPECT_FALSE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_1_0));
 }
 
 // Should not show the sign-in upgrade twice on the same version.
@@ -121,8 +117,8 @@
   const base::Version version_1_0("1.0");
   signin::RecordUpgradePromoSigninStarted(account_manager_service_,
                                           version_1_0);
-  EXPECT_FALSE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_1_0));
+  EXPECT_FALSE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_1_0));
 }
 
 // Should not show the sign-in upgrade twice until two major version after.
@@ -135,8 +131,8 @@
   const base::Version version_1_1("1.1");
   signin::RecordUpgradePromoSigninStarted(account_manager_service_,
                                           version_1_0);
-  EXPECT_FALSE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_1_1));
+  EXPECT_FALSE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_1_1));
 }
 
 // Should not show the sign-in upgrade twice until two major version after.
@@ -149,8 +145,8 @@
   const base::Version version_1_2("1.2");
   signin::RecordUpgradePromoSigninStarted(account_manager_service_,
                                           version_1_0);
-  EXPECT_FALSE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_1_2));
+  EXPECT_FALSE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_1_2));
 }
 
 // Should not show the sign-in upgrade twice until two major version after.
@@ -163,8 +159,8 @@
   const base::Version version_2_0("2.0");
   signin::RecordUpgradePromoSigninStarted(account_manager_service_,
                                           version_1_0);
-  EXPECT_FALSE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_2_0));
+  EXPECT_FALSE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_2_0));
 }
 
 // Should show the sign-in upgrade a second time, 2 version after.
@@ -177,8 +173,8 @@
   const base::Version version_3_0("3.0");
   signin::RecordUpgradePromoSigninStarted(account_manager_service_,
                                           version_1_0);
-  EXPECT_TRUE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_3_0));
+  EXPECT_TRUE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_3_0));
 }
 
 // Show the sign-in upgrade on version 1.0.
@@ -197,8 +193,8 @@
                                           version_1_0);
   signin::RecordUpgradePromoSigninStarted(account_manager_service_,
                                           version_3_0);
-  EXPECT_FALSE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_5_0));
+  EXPECT_FALSE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_5_0));
 }
 
 // Show the sign-in upgrade on version 1.0.
@@ -216,8 +212,8 @@
                                           version_3_0);
   FakeSystemIdentity* fake_identity = [FakeSystemIdentity fakeIdentity1];
   fake_system_identity_manager()->AddIdentity(fake_identity);
-  EXPECT_TRUE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_5_0));
+  EXPECT_TRUE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_5_0));
 }
 
 // Add new account.
@@ -238,8 +234,8 @@
                                           version_3_0);
   fake_system_identity_manager()->ForgetIdentity(fake_identity,
                                                  base::DoNothing());
-  EXPECT_FALSE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_5_0));
+  EXPECT_FALSE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_5_0));
 }
 
 // Show the sign-in upgrade on version 1.0.
@@ -257,8 +253,8 @@
                                           version_3_0);
   FakeSystemIdentity* fake_identity = [FakeSystemIdentity fakeIdentity1];
   fake_system_identity_manager()->AddIdentity(fake_identity);
-  EXPECT_FALSE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_4_0));
+  EXPECT_FALSE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_4_0));
 }
 
 // Show the sign-in upgrade on version 1.0.
@@ -273,8 +269,8 @@
                                           version_1_0);
   FakeSystemIdentity* fake_identity = [FakeSystemIdentity fakeIdentity1];
   fake_system_identity_manager()->AddIdentity(fake_identity);
-  EXPECT_FALSE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_2_0));
+  EXPECT_FALSE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_2_0));
 }
 
 // Should not show the sign-in upgrade for first run after post restore.
@@ -285,15 +281,15 @@
                                           version_1_0);
   FakeSystemIdentity* fake_identity = [FakeSystemIdentity fakeIdentity1];
   fake_system_identity_manager()->AddIdentity(fake_identity);
-  ASSERT_TRUE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_3_0));
+  ASSERT_TRUE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_3_0));
 
   AccountInfo accountInfo;
   accountInfo.email = "foo@bar.com";
   StorePreRestoreIdentity(GetProfilePrefs(), accountInfo,
                           /*history_sync_enabled=*/false);
-  EXPECT_FALSE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_3_0));
+  EXPECT_FALSE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_3_0));
 }
 
 // Should not show the sign-in upgrade if sign-in is disabled by policy.
@@ -307,8 +303,8 @@
   GetLocalState()->SetInteger(prefs::kBrowserSigninPolicy,
                               static_cast<int>(BrowserSigninMode::kDisabled));
 
-  EXPECT_FALSE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_3_0));
+  EXPECT_FALSE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_3_0));
 }
 
 // Should show if the user is signed-in without history opt-in.
@@ -316,8 +312,7 @@
   FakeSystemIdentity* identity = [FakeSystemIdentity fakeIdentity1];
   fake_system_identity_manager()->AddIdentity(identity);
   AuthenticationService* authentication_service =
-      AuthenticationServiceFactory::GetForBrowserState(
-          chrome_browser_state_.get());
+      AuthenticationServiceFactory::GetForProfile(profile_.get());
   authentication_service->SignIn(
       identity, signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN);
 
@@ -325,8 +320,8 @@
   const base::Version version_3_0("3.0");
   signin::RecordUpgradePromoSigninStarted(account_manager_service_,
                                           version_1_0);
-  EXPECT_TRUE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_3_0));
+  EXPECT_TRUE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_3_0));
 }
 
 // Should not show if the user is signed-in with history opt-in.
@@ -334,13 +329,12 @@
   FakeSystemIdentity* identity = [FakeSystemIdentity fakeIdentity1];
   fake_system_identity_manager()->AddIdentity(identity);
   AuthenticationService* authentication_service =
-      AuthenticationServiceFactory::GetForBrowserState(
-          chrome_browser_state_.get());
+      AuthenticationServiceFactory::GetForProfile(profile_.get());
   authentication_service->SignIn(
       identity, signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN);
 
   syncer::SyncService* sync_service =
-      SyncServiceFactory::GetForBrowserState(chrome_browser_state_.get());
+      SyncServiceFactory::GetForProfile(profile_.get());
   syncer::SyncUserSettings* sync_user_settings =
       sync_service->GetUserSettings();
   sync_user_settings->SetSelectedType(syncer::UserSelectableType::kHistory,
@@ -351,15 +345,15 @@
   const base::Version version_3_0("3.0");
   signin::RecordUpgradePromoSigninStarted(account_manager_service_,
                                           version_1_0);
-  EXPECT_FALSE(signin::ShouldPresentUserSigninUpgrade(
-      chrome_browser_state_.get(), version_3_0));
+  EXPECT_FALSE(
+      signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_3_0));
 }
 
 // signin::GetPrimaryIdentitySigninState for a signed-out user should
 // return the signed out state.
 TEST_F(SigninUtilsTest, TestGetPrimaryIdentitySigninStateSignedOut) {
   IdentitySigninState state =
-      signin::GetPrimaryIdentitySigninState(chrome_browser_state_.get());
+      signin::GetPrimaryIdentitySigninState(profile_.get());
   EXPECT_EQ(IdentitySigninStateSignedOut, state);
 }
 
@@ -369,13 +363,12 @@
   FakeSystemIdentity* identity = [FakeSystemIdentity fakeIdentity1];
   fake_system_identity_manager()->AddIdentity(identity);
   AuthenticationService* authentication_service =
-      AuthenticationServiceFactory::GetForBrowserState(
-          chrome_browser_state_.get());
+      AuthenticationServiceFactory::GetForProfile(profile_.get());
   authentication_service->SignIn(
       identity, signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN);
 
   IdentitySigninState state =
-      signin::GetPrimaryIdentitySigninState(chrome_browser_state_.get());
+      signin::GetPrimaryIdentitySigninState(profile_.get());
   EXPECT_EQ(IdentitySigninStateSignedInWithSyncDisabled, state);
 }
 
@@ -386,20 +379,19 @@
   FakeSystemIdentity* identity = [FakeSystemIdentity fakeIdentity1];
   fake_system_identity_manager()->AddIdentity(identity);
   AuthenticationService* authentication_service =
-      AuthenticationServiceFactory::GetForBrowserState(
-          chrome_browser_state_.get());
+      AuthenticationServiceFactory::GetForProfile(profile_.get());
   authentication_service->SignIn(
       identity, signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO);
   authentication_service->GrantSyncConsent(
       identity, signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO);
-  chrome_browser_state_->GetPrefs()->SetBoolean(
+  profile_->GetPrefs()->SetBoolean(
       syncer::prefs::internal::kSyncInitialSyncFeatureSetupComplete, true);
 
   IdentitySigninState state =
-      signin::GetPrimaryIdentitySigninState(chrome_browser_state_.get());
+      signin::GetPrimaryIdentitySigninState(profile_.get());
   EXPECT_EQ(IdentitySigninStateSignedInWithSyncEnabled, state);
 
-  chrome_browser_state_->GetPrefs()->ClearPref(
+  profile_->GetPrefs()->ClearPref(
       syncer::prefs::internal::kSyncInitialSyncFeatureSetupComplete);
 }
 
diff --git a/ios/chrome/browser/ui/authentication/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.mm
index 7b3d807..f2803e0 100644
--- a/ios/chrome/browser/ui/authentication/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.mm
@@ -45,7 +45,7 @@
                        trigger:
                            (syncer::TrustedVaultUserActionTriggerForUMA)trigger
                    accessPoint:(signin_metrics::AccessPoint)accessPoint {
-  DCHECK(!browser->GetBrowserState()->IsOffTheRecord());
+  DCHECK(!browser->GetProfile()->IsOffTheRecord());
   self = [super initWithBaseViewController:viewController
                                    browser:browser
                                accessPoint:accessPoint];
@@ -117,8 +117,7 @@
 - (void)start {
   [super start];
   AuthenticationService* authenticationService =
-      AuthenticationServiceFactory::GetForBrowserState(
-          self.browser->GetBrowserState());
+      AuthenticationServiceFactory::GetForProfile(self.browser->GetProfile());
   DCHECK(
       authenticationService->HasPrimaryIdentity(signin::ConsentLevel::kSignin));
   // TODO(crbug.com/40105436): Should test if reauth is still needed. If still
@@ -126,9 +125,9 @@
   // If not, the coordinator can be closed successfuly, by calling
   // -[TrustedVaultReauthenticationCoordinator
   // reauthentificationCompletedWithSuccess:]
-  self.identity = AuthenticationServiceFactory::GetForBrowserState(
-                      self.browser->GetBrowserState())
-                      ->GetPrimaryIdentity(signin::ConsentLevel::kSignin);
+  self.identity =
+      AuthenticationServiceFactory::GetForProfile(self.browser->GetProfile())
+          ->GetPrimaryIdentity(signin::ConsentLevel::kSignin);
   __weak __typeof(self) weakSelf = self;
   void (^callback)(BOOL success, NSError* error) =
       ^(BOOL success, NSError* error) {
@@ -138,14 +137,14 @@
     case SigninTrustedVaultDialogIntentFetchKeys:
       _dialogCancelCallback =
           TrustedVaultClientBackendFactory::GetForProfile(
-              self.browser->GetBrowserState())
+              self.browser->GetProfile())
               ->Reauthentication(self.identity, _securityDomainID,
                                  self.baseViewController, callback);
       break;
     case SigninTrustedVaultDialogIntentDegradedRecoverability:
       _dialogCancelCallback =
           TrustedVaultClientBackendFactory::GetForProfile(
-              self.browser->GetBrowserState())
+              self.browser->GetProfile())
               ->FixDegradedRecoverability(self.identity, _securityDomainID,
                                           self.baseViewController, callback);
       break;
diff --git a/ios/chrome/browser/ui/authentication/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator_unittest.mm b/ios/chrome/browser/ui/authentication/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator_unittest.mm
index da2fc5d..dd3c36ad1 100644
--- a/ios/chrome/browser/ui/authentication/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator_unittest.mm
@@ -36,36 +36,35 @@
     base_view_controller_ = [[UIViewController alloc] init];
     base_view_controller_.view.backgroundColor = UIColor.blueColor;
     [scoped_key_window_.Get() setRootViewController:base_view_controller_];
-    TestChromeBrowserState::Builder builder;
+    TestProfileIOS::Builder builder;
     builder.AddTestingFactory(
         AuthenticationServiceFactory::GetInstance(),
         AuthenticationServiceFactory::GetDefaultFactory());
-    browser_state_ = std::move(builder).Build();
-    AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
-        browser_state_.get(),
-        std::make_unique<FakeAuthenticationServiceDelegate>());
+    profile_ = std::move(builder).Build();
+    AuthenticationServiceFactory::CreateAndInitializeForProfile(
+        profile_.get(), std::make_unique<FakeAuthenticationServiceDelegate>());
     id<SystemIdentity> identity = [FakeSystemIdentity fakeIdentity1];
     FakeSystemIdentityManager* system_identity_manager =
         FakeSystemIdentityManager::FromSystemIdentityManager(
             GetApplicationContext()->GetSystemIdentityManager());
     system_identity_manager->AddIdentity(identity);
     AuthenticationService* authentication_service =
-        AuthenticationServiceFactory::GetForBrowserState(browser_state_.get());
+        AuthenticationServiceFactory::GetForProfile(profile_.get());
     authentication_service->SignIn(
         identity, signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN);
 
-    browser_ = std::make_unique<TestBrowser>(browser_state_.get());
+    browser_ = std::make_unique<TestBrowser>(profile_.get());
   }
 
   Browser* browser() { return browser_.get(); }
 
  protected:
-  // Needed for test browser state created by TestChromeBrowserState().
+  // Needed for test profile created by TestProfileIOS().
   web::WebTaskEnvironment task_environment_;
   IOSChromeScopedTestingLocalState scoped_testing_local_state_;
 
   std::unique_ptr<Browser> browser_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
 
   ScopedKeyWindow scoped_key_window_;
   UIViewController* base_view_controller_ = nil;
@@ -122,7 +121,7 @@
   // This means that it is safe to cast the `TrustedVaultClientBackend` to
   // `FakeTrustedVaultClientBackend` at runtime.
   static_cast<FakeTrustedVaultClientBackend*>(
-      TrustedVaultClientBackendFactory::GetForProfile(browser_state_.get()))
+      TrustedVaultClientBackendFactory::GetForProfile(profile_.get()))
       ->SimulateUserCancel();
 
   // Test the completion block.
diff --git a/ios/chrome/browser/ui/authentication/signin/two_screens_signin/two_screens_signin_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/two_screens_signin/two_screens_signin_coordinator.mm
index 31881c3..1ac8ed3f 100644
--- a/ios/chrome/browser/ui/authentication/signin/two_screens_signin/two_screens_signin_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/two_screens_signin/two_screens_signin_coordinator.mm
@@ -51,7 +51,7 @@
                        browser:(Browser*)browser
                    accessPoint:(signin_metrics::AccessPoint)accessPoint
                    promoAction:(signin_metrics::PromoAction)promoAction {
-  DCHECK(!browser->GetBrowserState()->IsOffTheRecord());
+  DCHECK(!browser->GetProfile()->IsOffTheRecord());
   self = [super initWithBaseViewController:viewController
                                    browser:browser
                                accessPoint:accessPoint];
@@ -70,8 +70,8 @@
   if (self.accessPoint ==
       signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO) {
     ChromeAccountManagerService* accountManagerService =
-        ChromeAccountManagerServiceFactory::GetForBrowserState(
-            self.browser->GetBrowserState());
+        ChromeAccountManagerServiceFactory::GetForProfile(
+            self.browser->GetProfile());
     // TODO(crbug.com/41352590): Need to add `CHECK(accountManagerService)`.
     [UpgradeSigninLogger logSigninStartedWithAccessPoint:self.accessPoint
                                    accountManagerService:accountManagerService];
@@ -114,8 +114,7 @@
 - (void)finishPresentingScreens {
   __weak __typeof(self) weakSelf = self;
   AuthenticationService* authService =
-      AuthenticationServiceFactory::GetForBrowserState(
-          self.browser->GetBrowserState());
+      AuthenticationServiceFactory::GetForProfile(self.browser->GetProfile());
   id<SystemIdentity> identity =
       authService->GetPrimaryIdentity(signin::ConsentLevel::kSignin);
   ProceduralBlock completion = ^{
diff --git a/ios/chrome/browser/ui/authentication/signin/two_screens_signin/two_screens_signin_coordinator_unittest.mm b/ios/chrome/browser/ui/authentication/signin/two_screens_signin/two_screens_signin_coordinator_unittest.mm
index 44cce1f2..3fba16a 100644
--- a/ios/chrome/browser/ui/authentication/signin/two_screens_signin/two_screens_signin_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/signin/two_screens_signin/two_screens_signin_coordinator_unittest.mm
@@ -34,17 +34,16 @@
 class TwoScreensSigninCoordinatorTest : public PlatformTest {
  public:
   TwoScreensSigninCoordinatorTest() {
-    TestChromeBrowserState::Builder builder;
+    TestProfileIOS::Builder builder;
     builder.AddTestingFactory(
         AuthenticationServiceFactory::GetInstance(),
         AuthenticationServiceFactory::GetDefaultFactory());
     builder.AddTestingFactory(SyncServiceFactory::GetInstance(),
                               base::BindRepeating(&CreateMockSyncService));
-    browser_state_ = std::move(builder).Build();
-    AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
-        browser_state_.get(),
-        std::make_unique<FakeAuthenticationServiceDelegate>());
-    browser_ = std::make_unique<TestBrowser>(browser_state_.get());
+    profile_ = std::move(builder).Build();
+    AuthenticationServiceFactory::CreateAndInitializeForProfile(
+        profile_.get(), std::make_unique<FakeAuthenticationServiceDelegate>());
+    browser_ = std::make_unique<TestBrowser>(profile_.get());
 
     NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults];
     [standardDefaults removeObjectForKey:kDisplayedSSORecallPromoCountKey];
@@ -108,8 +107,8 @@
   // Signs in a fake identity.
   void SigninFakeIdentity() {
     AuthenticationService* auth_service = static_cast<AuthenticationService*>(
-        AuthenticationServiceFactory::GetInstance()->GetForBrowserState(
-            browser_state_.get()));
+        AuthenticationServiceFactory::GetInstance()->GetForProfile(
+            profile_.get()));
     auth_service->SignIn(fake_identity_,
                          signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN);
   }
@@ -125,7 +124,7 @@
   web::WebTaskEnvironment task_environment_;
   IOSChromeScopedTestingLocalState scoped_testing_local_state_;
   std::unique_ptr<Browser> browser_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
   TwoScreensSigninCoordinator* coordinator_;
   base::UserActionTester user_actions_;
   UIWindow* window_;
diff --git a/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.mm b/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.mm
index 4257c828..fa49384a 100644
--- a/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.mm
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.mm
@@ -88,37 +88,34 @@
 }
 
 + (NSString*)primaryAccountGaiaID {
-  ChromeBrowserState* browserState =
-      chrome_test_util::GetOriginalBrowserState();
+  ProfileIOS* profile = chrome_test_util::GetOriginalProfile();
   CoreAccountInfo info =
-      IdentityManagerFactory::GetForProfile(browserState)
-          ->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin);
+      IdentityManagerFactory::GetForProfile(profile)->GetPrimaryAccountInfo(
+          signin::ConsentLevel::kSignin);
 
   return base::SysUTF8ToNSString(info.gaia);
 }
 
 + (NSString*)primaryAccountEmailWithConsent:(signin::ConsentLevel)consentLevel {
-  ChromeBrowserState* browserState =
-      chrome_test_util::GetOriginalBrowserState();
-  CoreAccountInfo info = IdentityManagerFactory::GetForProfile(browserState)
-                             ->GetPrimaryAccountInfo(consentLevel);
+  ProfileIOS* profile = chrome_test_util::GetOriginalProfile();
+  CoreAccountInfo info =
+      IdentityManagerFactory::GetForProfile(profile)->GetPrimaryAccountInfo(
+          consentLevel);
 
   return base::SysUTF8ToNSString(info.email);
 }
 
 + (BOOL)isSignedOut {
-  ChromeBrowserState* browserState =
-      chrome_test_util::GetOriginalBrowserState();
+  ProfileIOS* profile = chrome_test_util::GetOriginalProfile();
 
-  return !IdentityManagerFactory::GetForProfile(browserState)
-              ->HasPrimaryAccount(signin::ConsentLevel::kSignin);
+  return !IdentityManagerFactory::GetForProfile(profile)->HasPrimaryAccount(
+      signin::ConsentLevel::kSignin);
 }
 
 + (void)signOut {
-  ChromeBrowserState* browserState =
-      chrome_test_util::GetOriginalBrowserState();
+  ProfileIOS* profile = chrome_test_util::GetOriginalProfile();
   AuthenticationService* authentication_service =
-      AuthenticationServiceFactory::GetForBrowserState(browserState);
+      AuthenticationServiceFactory::GetForProfile(profile);
   authentication_service->SignOut(signin_metrics::ProfileSignout::kTest,
                                   /*force_clear_browsing_data=*/false, nil);
 }
@@ -128,10 +125,9 @@
     // For convenience, add the identity, if it was not added yet.
     [self addFakeIdentity:identity withUnknownCapabilities:NO];
   }
-  ChromeBrowserState* browserState =
-      chrome_test_util::GetOriginalBrowserState();
+  ProfileIOS* profile = chrome_test_util::GetOriginalProfile();
   AuthenticationService* authenticationService =
-      AuthenticationServiceFactory::GetForBrowserState(browserState);
+      AuthenticationServiceFactory::GetForProfile(profile);
   authenticationService->SignIn(
       identity, signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS);
 }
@@ -140,10 +136,9 @@
   [self signinWithFakeIdentity:identity];
 
   // "Upgrade" the account to ConsentLevel::kSync.
-  ChromeBrowserState* browserState =
-      chrome_test_util::GetOriginalBrowserState();
+  ProfileIOS* profile = chrome_test_util::GetOriginalProfile();
   signin::IdentityManager* identityManager =
-      IdentityManagerFactory::GetForProfile(browserState);
+      IdentityManagerFactory::GetForProfile(profile);
   CoreAccountId coreAccountId =
       identityManager->GetPrimaryAccountId(signin::ConsentLevel::kSignin);
   CHECK(!coreAccountId.empty());
@@ -154,8 +149,7 @@
   CHECK_EQ(error, signin::PrimaryAccountMutator::PrimaryAccountError::kNoError);
 
   // Mark Sync-the-feature setup as complete, so it can start up.
-  syncer::SyncService* syncService =
-      SyncServiceFactory::GetForBrowserState(browserState);
+  syncer::SyncService* syncService = SyncServiceFactory::GetForProfile(profile);
   syncService->SetSyncFeatureRequested();
   syncService->GetUserSettings()->SetInitialSyncFeatureSetupComplete(
       syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
@@ -169,8 +163,7 @@
   [FakeSystemIdentityInteractionManager setIdentity:identity
                             withUnknownCapabilities:NO];
   std::string emailAddress = base::SysNSStringToUTF8(identity.userEmail);
-  PrefService* prefService =
-      chrome_test_util::GetOriginalBrowserState()->GetPrefs();
+  PrefService* prefService = chrome_test_util::GetOriginalProfile()->GetPrefs();
   prefService->SetString(prefs::kGoogleServicesLastSyncingUsername,
                          emailAddress);
   ShowSigninCommand* command = [[ShowSigninCommand alloc]
@@ -200,8 +193,7 @@
 
 + (void)setSelectedType:(syncer::UserSelectableType)type enabled:(BOOL)enabled {
   syncer::SyncUserSettings* settings =
-      SyncServiceFactory::GetForBrowserState(
-          chrome_test_util::GetOriginalBrowserState())
+      SyncServiceFactory::GetForProfile(chrome_test_util::GetOriginalProfile())
           ->GetUserSettings();
   settings->SetSelectedTypes(/*sync_everything=*/false,
                              settings->GetSelectedTypes());
@@ -210,8 +202,7 @@
 
 + (BOOL)isSelectedTypeEnabled:(syncer::UserSelectableType)type {
   syncer::SyncUserSettings* settings =
-      SyncServiceFactory::GetForBrowserState(
-          chrome_test_util::GetOriginalBrowserState())
+      SyncServiceFactory::GetForProfile(chrome_test_util::GetOriginalProfile())
           ->GetUserSettings();
   return settings->GetSelectedTypes().Has(type) ? YES : NO;
 }
diff --git a/ios/chrome/browser/ui/authentication/signin_notification_infobar_delegate.h b/ios/chrome/browser/ui/authentication/signin_notification_infobar_delegate.h
index 51ba326..b75767e 100644
--- a/ios/chrome/browser/ui/authentication/signin_notification_infobar_delegate.h
+++ b/ios/chrome/browser/ui/authentication/signin_notification_infobar_delegate.h
@@ -27,7 +27,7 @@
 // Shows a sign-in notification in an infobar.
 class SigninNotificationInfoBarDelegate : public ConfirmInfoBarDelegate {
  public:
-  SigninNotificationInfoBarDelegate(ChromeBrowserState* browser_state,
+  SigninNotificationInfoBarDelegate(ProfileIOS* profile,
                                     id<SettingsCommands> dispatcher,
                                     UIViewController* view_controller);
 
@@ -40,7 +40,7 @@
 
   // Creates a sign-in notification infobar and adds it to `infobar_manager`.
   static bool Create(infobars::InfoBarManager* infobar_manager,
-                     ChromeBrowserState* browser_state,
+                     ProfileIOS* profile,
                      id<SettingsCommands> dispatcher,
                      UIViewController* view_controller);
 
diff --git a/ios/chrome/browser/ui/authentication/signin_notification_infobar_delegate.mm b/ios/chrome/browser/ui/authentication/signin_notification_infobar_delegate.mm
index cd83813..f3fc8d2 100644
--- a/ios/chrome/browser/ui/authentication/signin_notification_infobar_delegate.mm
+++ b/ios/chrome/browser/ui/authentication/signin_notification_infobar_delegate.mm
@@ -33,33 +33,33 @@
 // static
 bool SigninNotificationInfoBarDelegate::Create(
     infobars::InfoBarManager* infobar_manager,
-    ChromeBrowserState* browser_state,
+    ProfileIOS* profile,
     id<SettingsCommands> dispatcher,
     UIViewController* view_controller) {
   DCHECK(infobar_manager);
   std::unique_ptr<ConfirmInfoBarDelegate> delegate(
-      std::make_unique<SigninNotificationInfoBarDelegate>(
-          browser_state, dispatcher, view_controller));
+      std::make_unique<SigninNotificationInfoBarDelegate>(profile, dispatcher,
+                                                          view_controller));
   std::unique_ptr<infobars::InfoBar> infobar =
       CreateHighPriorityConfirmInfoBar(std::move(delegate));
   return !!infobar_manager->AddInfoBar(std::move(infobar));
 }
 
 SigninNotificationInfoBarDelegate::SigninNotificationInfoBarDelegate(
-    ChromeBrowserState* browser_state,
+    ProfileIOS* profile,
     id<SettingsCommands> dispatcher,
     UIViewController* view_controller)
     : dispatcher_(dispatcher), base_view_controller_(view_controller) {
-  DCHECK(!browser_state->IsOffTheRecord());
+  DCHECK(!profile->IsOffTheRecord());
 
   AuthenticationService* auth_service =
-      AuthenticationServiceFactory::GetForBrowserState(browser_state);
+      AuthenticationServiceFactory::GetForProfile(profile);
   DCHECK(auth_service);
   id<SystemIdentity> identity =
       auth_service->GetPrimaryIdentity(signin::ConsentLevel::kSignin);
 
   ChromeAccountManagerService* accountManagerService =
-      ChromeAccountManagerServiceFactory::GetForBrowserState(browser_state);
+      ChromeAccountManagerServiceFactory::GetForProfile(profile);
   UIImage* image = accountManagerService->GetIdentityAvatarWithIdentity(
       identity, IdentityAvatarSize::Regular);
   icon_ = gfx::Image(CircularImageFromImage(image, image.size.width));
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.h b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.h
index 2eab8d0c..cdc45e59 100644
--- a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.h
+++ b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.h
@@ -106,7 +106,7 @@
 @property(nonatomic, assign) syncer::DataType dataTypeToWaitForInitialSync;
 
 // Registers the feature preferences.
-+ (void)registerBrowserStatePrefs:(user_prefs::PrefRegistrySyncable*)registry;
++ (void)registerProfilePrefs:(user_prefs::PrefRegistrySyncable*)registry;
 
 // Tests if the sign-in promo view should be displayed according to the number
 // of times it has been displayed and if the user closed the sign-in promo view.
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm
index c93eed23..45b290b 100644
--- a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm
+++ b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm
@@ -707,7 +707,7 @@
   std::unique_ptr<SyncObserverBridge> _syncObserverBridge;
 }
 
-+ (void)registerBrowserStatePrefs:(user_prefs::PrefRegistrySyncable*)registry {
++ (void)registerProfilePrefs:(user_prefs::PrefRegistrySyncable*)registry {
   // Bookmarks
   registry->RegisterBooleanPref(prefs::kIosBookmarkPromoAlreadySeen, false);
   registry->RegisterBooleanPref(prefs::kIosBookmarkSettingsPromoAlreadySeen,
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator_unittest.mm b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator_unittest.mm
index 3bac231..296d863 100644
--- a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator_unittest.mm
@@ -66,17 +66,16 @@
   void SetUp() override {
     identity_ = [FakeSystemIdentity fakeIdentity1];
 
-    TestChromeBrowserState::Builder builder;
+    TestProfileIOS::Builder builder;
     builder.AddTestingFactory(SyncServiceFactory::GetInstance(),
                               base::BindRepeating(&CreateMockSyncService));
     builder.AddTestingFactory(
         AuthenticationServiceFactory::GetInstance(),
         AuthenticationServiceFactory::GetDefaultFactory());
-    chrome_browser_state_ = std::move(builder).Build();
+    profile_ = std::move(builder).Build();
     // Set up the test browser and attach the browser agents.
-    AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
-        chrome_browser_state_.get(),
-        std::make_unique<FakeAuthenticationServiceDelegate>());
+    AuthenticationServiceFactory::CreateAndInitializeForProfile(
+        profile_.get(), std::make_unique<FakeAuthenticationServiceDelegate>());
   }
 
   void TearDown() override {
@@ -104,10 +103,9 @@
         OCMStrictProtocolMock(@protocol(AccountSettingsPresenter));
     mediator_ = [[SigninPromoViewMediator alloc]
         initWithAccountManagerService:ChromeAccountManagerServiceFactory::
-                                          GetForBrowserState(
-                                              chrome_browser_state_.get())
+                                          GetForProfile(profile_.get())
                           authService:GetAuthenticationService()
-                          prefService:chrome_browser_state_.get()->GetPrefs()
+                          prefService:profile_.get()->GetPrefs()
                           syncService:GetSyncService()
                           accessPoint:access_point
                       signinPresenter:signin_presenter_
@@ -133,12 +131,11 @@
   }
 
   AuthenticationService* GetAuthenticationService() {
-    return AuthenticationServiceFactory::GetForBrowserState(
-        chrome_browser_state_.get());
+    return AuthenticationServiceFactory::GetForProfile(profile_.get());
   }
 
   syncer::SyncService* GetSyncService() {
-    return SyncServiceFactory::GetForBrowserState(chrome_browser_state_.get());
+    return SyncServiceFactory::GetForProfile(profile_.get());
   }
 
   // Creates the default identity and adds it into the ChromeIdentityService.
@@ -404,7 +401,7 @@
   // Task environment.
   WebTaskEnvironment task_environment_;
   IOSChromeScopedTestingLocalState scoped_testing_local_state_;
-  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
 
   // Mediator used for the tests.
   SigninPromoViewMediator* mediator_;
@@ -613,10 +610,9 @@
 TEST_F(SigninPromoViewMediatorTest,
        ShouldNotDisplaySigninPromoViewIfDisabledByPolicy) {
   CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
-  TestChromeBrowserState::Builder builder;
+  TestProfileIOS::Builder builder;
   builder.SetPrefService(CreatePrefService());
-  std::unique_ptr<TestChromeBrowserState> browser_state =
-      std::move(builder).Build();
+  std::unique_ptr<TestProfileIOS> profile = std::move(builder).Build();
   GetLocalState()->SetInteger(prefs::kBrowserSigninPolicy,
                               static_cast<int>(BrowserSigninMode::kDisabled));
   EXPECT_FALSE([SigninPromoViewMediator
@@ -625,7 +621,7 @@
                                 signinPromoAction:SigninPromoAction::
                                                       kInstantSignin
                             authenticationService:GetAuthenticationService()
-                                      prefService:browser_state->GetPrefs()]);
+                                      prefService:profile->GetPrefs()]);
 }
 
 // Tests that the default identity is the primary account, when the user is
@@ -765,26 +761,25 @@
 TEST_F(SigninPromoViewMediatorTest,
        ShouldNotDisplaySigninPromoViewIfAlreadySeen) {
   CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER);
-  TestChromeBrowserState::Builder builder;
+  TestProfileIOS::Builder builder;
   builder.SetPrefService(CreatePrefService());
-  std::unique_ptr<TestChromeBrowserState> browser_state =
-      std::move(builder).Build();
-  browser_state->GetPrefs()->SetBoolean(
-      prefs::kIosBookmarkSettingsPromoAlreadySeen, true);
+  std::unique_ptr<TestProfileIOS> profile = std::move(builder).Build();
+  profile->GetPrefs()->SetBoolean(prefs::kIosBookmarkSettingsPromoAlreadySeen,
+                                  true);
   EXPECT_FALSE([SigninPromoViewMediator
       shouldDisplaySigninPromoViewWithAccessPoint:
           signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER
                                 signinPromoAction:SigninPromoAction::
                                                       kReviewAccountSettings
                             authenticationService:GetAuthenticationService()
-                                      prefService:browser_state->GetPrefs()]);
+                                      prefService:profile->GetPrefs()]);
   EXPECT_TRUE([SigninPromoViewMediator
       shouldDisplaySigninPromoViewWithAccessPoint:
           signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER
                                 signinPromoAction:SigninPromoAction::
                                                       kInstantSignin
                             authenticationService:GetAuthenticationService()
-                                      prefService:browser_state->GetPrefs()]);
+                                      prefService:profile->GetPrefs()]);
 }
 
 }  // namespace
diff --git a/ios/chrome/browser/ui/authentication/signout_action_sheet/signout_action_sheet_coordinator.mm b/ios/chrome/browser/ui/authentication/signout_action_sheet/signout_action_sheet_coordinator.mm
index 5bce9c5..7cc7275 100644
--- a/ios/chrome/browser/ui/authentication/signout_action_sheet/signout_action_sheet_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signout_action_sheet/signout_action_sheet_coordinator.mm
@@ -163,21 +163,21 @@
 #pragma mark - Browser-based properties
 
 - (AuthenticationService*)authenticationService {
-  return AuthenticationServiceFactory::GetForBrowserState(
-      self.browser->GetBrowserState());
+  return AuthenticationServiceFactory::GetForProfile(
+      self.browser->GetProfile());
 }
 
 // Returns the user's sign-in and syncing state.
 - (SignedInUserState)signedInUserState {
   DCHECK(self.browser);
   syncer::SyncService* syncService =
-      SyncServiceFactory::GetForBrowserState(self.browser->GetBrowserState());
-  ChromeBrowserState* browserState = self.browser->GetBrowserState();
+      SyncServiceFactory::GetForProfile(self.browser->GetProfile());
+  ProfileIOS* profile = self.browser->GetProfile();
   AuthenticationService* authenticationService = self.authenticationService;
   const bool is_managed_account_migrated_from_syncing =
       browser_sync::WasPrimaryAccountMigratedFromSyncingToSignedIn(
-          IdentityManagerFactory::GetForProfile(browserState),
-          browserState->GetPrefs()) &&
+          IdentityManagerFactory::GetForProfile(profile),
+          profile->GetPrefs()) &&
       authenticationService->HasPrimaryIdentityManaged(
           signin::ConsentLevel::kSignin);
 
@@ -324,7 +324,7 @@
   constexpr syncer::DataTypeSet kDataTypesToQuery =
       syncer::TypesRequiringUnsyncedDataCheckOnSignout();
   syncer::SyncService* syncService =
-      SyncServiceFactory::GetForBrowserState(self.browser->GetBrowserState());
+      SyncServiceFactory::GetForProfile(self.browser->GetProfile());
   __weak __typeof(self) weakSelf = self;
   auto callback = base::BindOnce(^(syncer::DataTypeSet set) {
     CHECK(kDataTypesToQuery.HasAll(set))
@@ -609,7 +609,7 @@
     return nil;
   }
   syncer::SyncService* syncService =
-      SyncServiceFactory::GetForBrowserState(self.browser->GetBrowserState());
+      SyncServiceFactory::GetForProfile(self.browser->GetProfile());
   int message_id =
       syncService->HasDisableReason(
           syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY) ||
diff --git a/ios/chrome/browser/ui/authentication/signout_action_sheet/signout_action_sheet_coordinator_unittest.mm b/ios/chrome/browser/ui/authentication/signout_action_sheet/signout_action_sheet_coordinator_unittest.mm
index 49d9356b4..e0864149 100644
--- a/ios/chrome/browser/ui/authentication/signout_action_sheet/signout_action_sheet_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/signout_action_sheet/signout_action_sheet_coordinator_unittest.mm
@@ -57,20 +57,19 @@
             GetApplicationContext()->GetSystemIdentityManager());
     system_identity_manager->AddIdentity(identity_);
     system_identity_manager->AddIdentity(managed_identity_);
-    TestChromeBrowserState::Builder builder;
+    TestProfileIOS::Builder builder;
     builder.AddTestingFactory(
         AuthenticationServiceFactory::GetInstance(),
         AuthenticationServiceFactory::GetDefaultFactory());
     builder.AddTestingFactory(SyncServiceFactory::GetInstance(),
                               base::BindRepeating(&CreateMockSyncService));
-    browser_state_ = std::move(builder).Build();
-    AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
-        browser_state_.get(),
-        std::make_unique<FakeAuthenticationServiceDelegate>());
-    browser_ = std::make_unique<TestBrowser>(browser_state_.get());
+    profile_ = std::move(builder).Build();
+    AuthenticationServiceFactory::CreateAndInitializeForProfile(
+        profile_.get(), std::make_unique<FakeAuthenticationServiceDelegate>());
+    browser_ = std::make_unique<TestBrowser>(profile_.get());
 
     sync_service_mock_ = static_cast<syncer::MockSyncService*>(
-        SyncServiceFactory::GetForBrowserState(browser_state_.get()));
+        SyncServiceFactory::GetForProfile(profile_.get()));
 
     [browser_->GetCommandDispatcher()
         startDispatchingToTarget:snackbar_handler_
@@ -85,8 +84,7 @@
 
   // Identity services.
   AuthenticationService* authentication_service() {
-    return AuthenticationServiceFactory::GetForBrowserState(
-        browser_state_.get());
+    return AuthenticationServiceFactory::GetForProfile(profile_.get());
   }
 
   // Sign-out coordinator.
@@ -109,10 +107,10 @@
     return GetApplicationContext()->GetLocalState();
   }
 
-  PrefService* GetPrefs() { return browser_state_->GetPrefs(); }
+  PrefService* GetPrefs() { return profile_->GetPrefs(); }
 
  protected:
-  // Needed for test browser state created by TestChromeBrowserState().
+  // Needed for test profile created by TestProfileIOS().
   base::test::TaskEnvironment task_environment_;
 
   IOSChromeScopedTestingLocalState scoped_testing_local_state_;
@@ -123,7 +121,7 @@
   ScopedKeyWindow scoped_key_window_;
   UIViewController* view_controller_ = nullptr;
   std::unique_ptr<Browser> browser_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<TestProfileIOS> profile_;
   id<SystemIdentity> identity_ = nil;
   id<SystemIdentity> managed_identity_ = nil;
   id<SnackbarCommands> snackbar_handler_ =
diff --git a/ios/chrome/browser/ui/browser_container/browser_container_view_controller.mm b/ios/chrome/browser/ui/browser_container/browser_container_view_controller.mm
index ca567a7..b02f52cf 100644
--- a/ios/chrome/browser/ui/browser_container/browser_container_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_container/browser_container_view_controller.mm
@@ -141,30 +141,6 @@
   }
 }
 
-#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_16_0
-#pragma mark - Link to Text methods
-
-- (void)addLinkToTextInEditMenu {
-  if (!base::FeatureList::IsEnabled(kSharedHighlightingIOS)) {
-    return;
-  }
-
-  NSString* title = l10n_util::GetNSString(IDS_IOS_SHARE_LINK_TO_TEXT);
-  UIMenuItem* menuItem =
-      [[UIMenuItem alloc] initWithTitle:title action:@selector(linkToText:)];
-  RegisterEditMenuItem(menuItem);
-}
-
-- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
-  return [super canPerformAction:action withSender:sender];
-}
-
-- (void)linkToText:(UIMenuItem*)item {
-  DCHECK(base::FeatureList::IsEnabled(kSharedHighlightingIOS));
-  DCHECK(self.linkToTextDelegate);
-  [self.linkToTextDelegate handleLinkToTextSelection];
-}
-#endif
 
 #pragma mark - Private
 
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn
index 85cd5220..e877283 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn
@@ -24,6 +24,10 @@
     "content_suggestions_most_visited_tile_view.mm",
     "content_suggestions_tile_view.h",
     "content_suggestions_tile_view.mm",
+    "icon_detail_view.h",
+    "icon_detail_view.mm",
+    "icon_view.h",
+    "icon_view.mm",
     "most_visited_tiles_commands.h",
     "multi_row_container_view.h",
     "multi_row_container_view.mm",
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/icon_detail_view.h b/ios/chrome/browser/ui/content_suggestions/cells/icon_detail_view.h
new file mode 100644
index 0000000..baba6e8
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/cells/icon_detail_view.h
@@ -0,0 +1,75 @@
+// 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 IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_ICON_DETAIL_VIEW_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_ICON_DETAIL_VIEW_H_
+
+#import <UIKit/UIKit.h>
+
+@class IconDetailView;
+
+// A protocol for handling `IconDetailView` taps. `-didTapIconDetailView:view`
+// will be called when an `IconDetailView` is tapped.
+@protocol IconDetailViewTapDelegate
+
+// Indicates that the user has tapped the given `view`.
+- (void)didTapIconDetailView:(IconDetailView*)view;
+
+@end
+
+// The possible layout types for a given `IconDetailView`. These values
+// determine how the content within the `IconDetailView` is arranged.
+enum class IconDetailViewLayoutType {
+  // A prominent layout with a larger icon and more spacing between
+  // the title and description. Suitable for single-row displays.
+  kHero = 1,
+  // A more condensed layout with a smaller icon and less spacing
+  // between the title and description. Useful for multi-row views.
+  kCompact = 2,
+  kMaxValue = kCompact
+};
+
+// A view to display an icon, title, description, and (optional) chevron. This
+// view can be configured with different layout types to suit various display
+// needs.
+@interface IconDetailView : UIView
+
+// Initializer for creating an `IconDetailView` with the
+// given `title`, `description`, `layoutType`, `symbolName` (and whether it
+// `usesDefaultSymbol`), `symbolWidth`, and `accessibilityIdentifier`.
+// This initializer provides a streamlined way to set up an
+// `IconDetailView` instance. When `showCheckmark` is true, the icon is
+// displayed with a green checkmark to indicate a completed state.
+- (instancetype)initWithTitle:(NSString*)title
+                  description:(NSString*)description
+                   layoutType:(IconDetailViewLayoutType)layoutType
+                   symbolName:(NSString*)symbolName
+            usesDefaultSymbol:(BOOL)usesDefaultSymbol
+                  symbolWidth:(CGFloat)symbolWidth
+                showCheckmark:(BOOL)showCheckmark
+      accessibilityIdentifier:(NSString*)accessibilityIdentifier;
+
+// Initializer for creating an `IconDetailView` with the
+// given `title`, `description`, `layoutType`, `symbolName` (and whether it
+// `usesDefaultSymbol`), and `accessibilityIdentifier`. This initializer
+// provides a streamlined way to set up an `IconDetailView` instance. When
+// `showCheckmark` is true, the icon is displayed with a green checkmark to
+// indicate a completed state.
+- (instancetype)initWithTitle:(NSString*)title
+                  description:(NSString*)description
+                   layoutType:(IconDetailViewLayoutType)layoutType
+                   symbolName:(NSString*)symbolName
+            usesDefaultSymbol:(BOOL)usesDefaultSymbol
+                showCheckmark:(BOOL)showCheckmark
+      accessibilityIdentifier:(NSString*)accessibilityIdentifier;
+
+// The object that should receive a message when this view is tapped.
+@property(nonatomic, weak) id<IconDetailViewTapDelegate> tapDelegate;
+
+// Unique identifier for the item. Can be `nil`.
+@property(nonatomic, copy) NSString* identifier;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_ICON_DETAIL_VIEW_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/icon_detail_view.mm b/ios/chrome/browser/ui/content_suggestions/cells/icon_detail_view.mm
new file mode 100644
index 0000000..1a541c2
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/cells/icon_detail_view.mm
@@ -0,0 +1,324 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/content_suggestions/cells/icon_detail_view.h"
+
+#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
+#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/icon_view.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#import "ios/chrome/common/ui/util/constraints_ui_util.h"
+
+namespace {
+
+// The spacing between the title and description.
+constexpr CGFloat kTitleDescriptionSpacing = 2;
+
+// The spacing between elements within the item.
+constexpr CGFloat kContentStackSpacing = 14;
+
+// Constants related to the icon container view.
+constexpr CGFloat kIconContainerSize = 56;
+constexpr CGFloat kIconContainerCornerRadius = 12;
+
+// The size of the checkmark icon.
+constexpr CGFloat kCheckmarkSize = 19;
+constexpr CGFloat kCheckmarkTopOffset = -6;
+constexpr CGFloat kCheckmarkTrailingOffset = 6;
+
+// Constants related to icon sizing.
+constexpr CGFloat kIconSize = 22;
+
+// Creates and returns a checkmark icon `UIImageView` with a green checkmark
+// symbol. The icon is configured with a specific size and has auto layout
+// constraints activated for its width and height.
+UIImageView* CheckmarkIcon() {
+  UIImageSymbolConfiguration* config = [UIImageSymbolConfiguration
+      configurationWithWeight:UIImageSymbolWeightMedium];
+
+  UIImageSymbolConfiguration* colorConfig =
+      [UIImageSymbolConfiguration configurationWithPaletteColors:@[
+        [UIColor whiteColor], [UIColor colorNamed:kGreen500Color]
+      ]];
+
+  config = [config configurationByApplyingConfiguration:colorConfig];
+
+  UIImage* image =
+      DefaultSymbolWithConfiguration(kCheckmarkCircleFillSymbol, config);
+
+  UIImageView* icon = [[UIImageView alloc] initWithImage:image];
+
+  icon.translatesAutoresizingMaskIntoConstraints = NO;
+
+  [NSLayoutConstraint activateConstraints:@[
+    [icon.widthAnchor constraintEqualToConstant:kCheckmarkSize],
+    [icon.heightAnchor constraintEqualToAnchor:icon.widthAnchor],
+  ]];
+
+  return icon;
+}
+
+}  // namespace
+
+@implementation IconDetailView {
+  // The title to be displayed in the view.
+  NSString* _title;
+
+  // The descriptive text to be displayed in the view.
+  NSString* _description;
+
+  // The item layout type. This determines the spacing of elements within the
+  // view.
+  IconDetailViewLayoutType _layoutType;
+
+  // The symbol to be displayed in the view.
+  NSString* _symbolName;
+
+  // The width of the symbol.
+  CGFloat _symbolWidth;
+
+  // Indicates whether the symbol is a default symbol.
+  BOOL _usesDefaultSymbol;
+
+  // Whether or not the icon should be displayed with a green checkmark to
+  // indicate a completed state.
+  BOOL _showCheckmark;
+
+  // UI tap gesture recognizer. This recognizer detects taps on the view
+  // and triggers the appropriate action.
+  UITapGestureRecognizer* _tapGestureRecognizer;
+
+  // The accessibility identifier for the view
+  NSString* _accessibilityIdentifier;
+}
+
+- (instancetype)initWithTitle:(NSString*)title
+                  description:(NSString*)description
+                   layoutType:(IconDetailViewLayoutType)layoutType
+                   symbolName:(NSString*)symbolName
+            usesDefaultSymbol:(BOOL)usesDefaultSymbol
+                  symbolWidth:(CGFloat)symbolWidth
+                showCheckmark:(BOOL)showCheckmark
+      accessibilityIdentifier:(NSString*)accessibilityIdentifier {
+  if ((self = [super init])) {
+    _title = title;
+    _description = description;
+    _layoutType = layoutType;
+    _symbolName = symbolName;
+    _usesDefaultSymbol = usesDefaultSymbol;
+    _symbolWidth = symbolWidth;
+    _showCheckmark = showCheckmark;
+    _accessibilityIdentifier = accessibilityIdentifier;
+  }
+
+  return self;
+}
+
+- (instancetype)initWithTitle:(NSString*)title
+                  description:(NSString*)description
+                   layoutType:(IconDetailViewLayoutType)layoutType
+                   symbolName:(NSString*)symbolName
+            usesDefaultSymbol:(BOOL)usesDefaultSymbol
+                showCheckmark:(BOOL)showCheckmark
+      accessibilityIdentifier:(NSString*)accessibilityIdentifier {
+  return [self initWithTitle:title
+                  description:description
+                   layoutType:layoutType
+                   symbolName:symbolName
+            usesDefaultSymbol:usesDefaultSymbol
+                  symbolWidth:kIconSize
+                showCheckmark:showCheckmark
+      accessibilityIdentifier:accessibilityIdentifier];
+}
+
+#pragma mark - UIView
+
+- (void)willMoveToSuperview:(UIView*)newSuperview {
+  [super willMoveToSuperview:newSuperview];
+
+  [self createSubviews];
+}
+
+- (NSString*)accessibilityLabel {
+  return [NSString stringWithFormat:@"%@, %@", _title, _description];
+}
+
+#pragma mark - Private
+
+// Creates and configures the subviews for the `IconDetailView`. This method
+// sets up the icon, title, description, and optional chevron, arranging them
+// based on the layout type. It also handles accessibility and adds a tap
+// gesture recognizer.
+- (void)createSubviews {
+  // Return if the subviews have already been created and added.
+  if (!(self.subviews.count == 0)) {
+    return;
+  }
+
+  self.translatesAutoresizingMaskIntoConstraints = NO;
+  self.accessibilityIdentifier = _accessibilityIdentifier;
+  self.isAccessibilityElement = YES;
+  self.accessibilityTraits = UIAccessibilityTraitButton;
+
+  // Add a horizontal stack to contain the icon, text stack, and (optional)
+  // chevron.
+  NSMutableArray* arrangedSubviews = [[NSMutableArray alloc] init];
+
+  BOOL isHeroLayout = _layoutType == IconDetailViewLayoutType::kHero;
+
+  IconView* icon = _usesDefaultSymbol
+                       ? [[IconView alloc] initWithDefaultSymbol:_symbolName
+                                                     symbolWidth:_symbolWidth
+                                                   compactLayout:!isHeroLayout
+                                                        inSquare:YES]
+                       : [[IconView alloc] initWithCustomSymbol:_symbolName
+                                                    symbolWidth:_symbolWidth
+                                                  compactLayout:!isHeroLayout
+                                                       inSquare:YES];
+
+  // When the item is displayed in a hero-style layout, the icon is more
+  // prominently displayed via an icon container view.
+  if (isHeroLayout) {
+    UIView* iconContainerView = [self iconInContainer:icon];
+
+    // Display a green checkmark when the layout is hero-cell complete.
+    if (_showCheckmark) {
+      UIImageView* checkmark = CheckmarkIcon();
+
+      [iconContainerView addSubview:checkmark];
+
+      [NSLayoutConstraint activateConstraints:@[
+        [checkmark.topAnchor constraintEqualToAnchor:iconContainerView.topAnchor
+                                            constant:kCheckmarkTopOffset],
+        [checkmark.trailingAnchor
+            constraintEqualToAnchor:iconContainerView.trailingAnchor
+                           constant:kCheckmarkTrailingOffset],
+      ]];
+    }
+
+    [arrangedSubviews addObject:iconContainerView];
+  } else {
+    [arrangedSubviews addObject:icon];
+  }
+
+  UILabel* titleLabel = [self createTitleLabel];
+
+  [titleLabel
+      setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh
+                                      forAxis:UILayoutConstraintAxisVertical];
+
+  UILabel* descriptionLabel = [self createDescriptionLabel];
+
+  [descriptionLabel
+      setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
+                                      forAxis:UILayoutConstraintAxisVertical];
+
+  // Add a vertical stack for the title and description labels.
+  UIStackView* textStack = [[UIStackView alloc]
+      initWithArrangedSubviews:@[ titleLabel, descriptionLabel ]];
+
+  textStack.axis = UILayoutConstraintAxisVertical;
+  textStack.translatesAutoresizingMaskIntoConstraints = NO;
+  textStack.spacing = kTitleDescriptionSpacing;
+  [textStack setContentHuggingPriority:UILayoutPriorityDefaultLow
+                               forAxis:UILayoutConstraintAxisHorizontal];
+
+  [arrangedSubviews addObject:textStack];
+
+  // For compact layout, display a chevron at the end of the item.
+  if (!isHeroLayout) {
+    UIImageView* chevron = [[UIImageView alloc]
+        initWithImage:[UIImage imageNamed:@"table_view_cell_chevron"]];
+
+    [chevron setContentHuggingPriority:UILayoutPriorityDefaultHigh
+                               forAxis:UILayoutConstraintAxisHorizontal];
+
+    [arrangedSubviews addObject:chevron];
+  }
+
+  UIStackView* contentStack =
+      [[UIStackView alloc] initWithArrangedSubviews:arrangedSubviews];
+
+  contentStack.translatesAutoresizingMaskIntoConstraints = NO;
+  contentStack.axis = UILayoutConstraintAxisHorizontal;
+  contentStack.alignment = UIStackViewAlignmentCenter;
+  contentStack.spacing = kContentStackSpacing;
+
+  [self addSubview:contentStack];
+
+  AddSameConstraints(contentStack, self);
+
+  // Set up the tap gesture recognizer.
+  _tapGestureRecognizer =
+      [[UITapGestureRecognizer alloc] initWithTarget:self
+                                              action:@selector(handleTap:)];
+
+  [self addGestureRecognizer:_tapGestureRecognizer];
+}
+
+// Called when the view is tapped. This method notifies the `tapDelegate`
+// that the `IconDetailView` has been tapped.
+- (void)handleTap:(UITapGestureRecognizer*)sender {
+  if (sender.state == UIGestureRecognizerStateEnded) {
+    [self.tapDelegate didTapIconDetailView:self];
+  }
+}
+
+// Returns `icon` wrapped in a container view. This is used for the hero layout
+// to give the icon a more prominent appearance.
+- (UIView*)iconInContainer:(IconView*)icon {
+  icon.translatesAutoresizingMaskIntoConstraints = NO;
+
+  UIView* iconContainer = [[UIView alloc] init];
+
+  iconContainer.backgroundColor = [UIColor colorNamed:kGrey100Color];
+  iconContainer.layer.cornerRadius = kIconContainerCornerRadius;
+
+  [iconContainer addSubview:icon];
+
+  AddSameCenterConstraints(icon, iconContainer);
+
+  [NSLayoutConstraint activateConstraints:@[
+    [iconContainer.widthAnchor constraintEqualToConstant:kIconContainerSize],
+    [iconContainer.widthAnchor
+        constraintEqualToAnchor:iconContainer.heightAnchor],
+  ]];
+
+  return iconContainer;
+}
+
+// Creates the title label with appropriate font, color, and line break mode.
+- (UILabel*)createTitleLabel {
+  UILabel* label = [[UILabel alloc] init];
+
+  label.text = _title;
+  label.translatesAutoresizingMaskIntoConstraints = NO;
+  label.numberOfLines = 0;
+  label.lineBreakMode = NSLineBreakByWordWrapping;
+  label.font =
+      _layoutType == IconDetailViewLayoutType::kHero
+          ? CreateDynamicFont(UIFontTextStyleFootnote, UIFontWeightSemibold)
+          : [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+  label.adjustsFontForContentSizeCategory = YES;
+  label.textColor = [UIColor colorNamed:kTextPrimaryColor];
+
+  return label;
+}
+
+// Creates the description label with appropriate font, color, and line break
+// mode.
+- (UILabel*)createDescriptionLabel {
+  UILabel* label = [[UILabel alloc] init];
+
+  label.text = _description;
+  label.numberOfLines = 2;
+  label.lineBreakMode = NSLineBreakByTruncatingTail;
+  label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+  label.adjustsFontForContentSizeCategory = YES;
+  label.textColor = [UIColor colorNamed:kTextSecondaryColor];
+
+  return label;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/icon_view.h b/ios/chrome/browser/ui/content_suggestions/cells/icon_view.h
new file mode 100644
index 0000000..07aa90f
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/cells/icon_view.h
@@ -0,0 +1,43 @@
+// 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 IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_ICON_VIEW_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_ICON_VIEW_H_
+
+#import <UIKit/UIKit.h>
+
+// A view which contains an icon for a Safety Check item.
+@interface IconView : UIView
+
+// Instantiates an `IconView` given a `defaultSymbolName`.
+//
+// `symbolWidth` determines the width of the icon.
+//
+// `compactLayout` determines if the icon should be shown in a smaller, compact
+// size.
+//
+// `inSquare` determines if the icon should be shown with a square enclosure
+// surrounding it.
+- (instancetype)initWithDefaultSymbol:(NSString*)defaultSymbolName
+                          symbolWidth:(CGFloat)symbolWidth
+                        compactLayout:(BOOL)compactLayout
+                             inSquare:(BOOL)inSquare;
+
+// Instantiates an `IconView` given a `customSymbolName`.
+//
+// `symbolWidth` determines the width of the icon.
+//
+// `compactLayout` determines if the icon should be shown in a smaller, compact
+// size.
+//
+// `inSquare` determines if the icon should be shown with a square enclosure
+// surrounding it.
+- (instancetype)initWithCustomSymbol:(NSString*)customSymbolName
+                         symbolWidth:(CGFloat)symbolWidth
+                       compactLayout:(BOOL)compactLayout
+                            inSquare:(BOOL)inSquare;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_ICON_VIEW_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/icon_view.mm b/ios/chrome/browser/ui/content_suggestions/cells/icon_view.mm
new file mode 100644
index 0000000..3e98596
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/cells/icon_view.mm
@@ -0,0 +1,170 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/content_suggestions/cells/icon_view.h"
+
+#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#import "ios/chrome/common/ui/util/constraints_ui_util.h"
+
+namespace {
+
+// Constants related to icon container sizing.
+constexpr CGFloat kIconContainerSize = 30;
+constexpr CGFloat kIconSquareContainerRadius = 7;
+
+// Returns a UIImageView for the given SF Symbol with color(s) `color_palette`,
+// using `default_symbol`.
+UIImageView* IconForSymbol(NSString* symbol,
+                           CGFloat symbol_width,
+                           BOOL default_symbol,
+                           NSArray<UIColor*>* color_palette = nil) {
+  UIImageSymbolConfiguration* config = [UIImageSymbolConfiguration
+      configurationWithWeight:UIImageSymbolWeightMedium];
+
+  if (color_palette) {
+    UIImageSymbolConfiguration* colorConfig = [UIImageSymbolConfiguration
+        configurationWithPaletteColors:color_palette];
+
+    config = [config configurationByApplyingConfiguration:colorConfig];
+  }
+
+  UIImage* image = default_symbol
+                       ? DefaultSymbolWithConfiguration(symbol, config)
+                       : CustomSymbolWithConfiguration(symbol, config);
+
+  UIImageView* icon = [[UIImageView alloc] initWithImage:image];
+
+  icon.translatesAutoresizingMaskIntoConstraints = NO;
+
+  [NSLayoutConstraint activateConstraints:@[
+    [icon.widthAnchor constraintEqualToConstant:symbol_width],
+    [icon.heightAnchor constraintEqualToAnchor:icon.widthAnchor],
+  ]];
+
+  return icon;
+}
+
+// Returns a UIView for the given `icon` wrapped in a container with
+// `containerColor`.
+UIView* IconInSquareContainer(UIImageView* icon, NSString* containerColor) {
+  UIView* square_view = [[UIView alloc] init];
+
+  square_view.translatesAutoresizingMaskIntoConstraints = NO;
+  square_view.layer.cornerRadius = kIconSquareContainerRadius;
+  square_view.backgroundColor = [UIColor colorNamed:containerColor];
+
+  icon.contentMode = UIViewContentModeScaleAspectFit;
+
+  [square_view addSubview:icon];
+
+  AddSameCenterConstraints(icon, square_view);
+
+  [NSLayoutConstraint activateConstraints:@[
+    [square_view.widthAnchor constraintEqualToConstant:kIconContainerSize],
+    [square_view.heightAnchor constraintEqualToAnchor:square_view.widthAnchor],
+  ]];
+
+  return square_view;
+}
+
+}  // namespace
+
+@implementation IconView {
+  // The symbol name for the icon.
+  NSString* _symbol;
+  // The width of the symbol.
+  CGFloat _symbolWidth;
+  // YES if `_symbol` is a default symbol name. (NO if `_symbol` is a custom
+  // symbol name.)
+  BOOL _defaultSymbol;
+  // YES if this icon should configure itself in a smaller, compact
+  // size.
+  BOOL _compactLayout;
+  // YES if this icon should place itself within a square enclosure.
+  BOOL _inSquare;
+  // The view containing the icon.
+  UIView* _icon;
+}
+
+- (instancetype)initWithDefaultSymbol:(NSString*)defaultSymbolName
+                          symbolWidth:(CGFloat)symbolWidth
+                        compactLayout:(BOOL)compactLayout
+                             inSquare:(BOOL)inSquare {
+  if ((self = [super init])) {
+    _symbol = defaultSymbolName;
+    _symbolWidth = symbolWidth;
+    _defaultSymbol = YES;
+    _compactLayout = compactLayout;
+    _inSquare = inSquare;
+  }
+
+  return self;
+}
+
+- (instancetype)initWithCustomSymbol:(NSString*)customSymbolName
+                         symbolWidth:(CGFloat)symbolWidth
+                       compactLayout:(BOOL)compactLayout
+                            inSquare:(BOOL)inSquare {
+  if ((self = [super init])) {
+    _symbol = customSymbolName;
+    _symbolWidth = symbolWidth;
+    _defaultSymbol = NO;
+    _compactLayout = compactLayout;
+    _inSquare = inSquare;
+  }
+
+  return self;
+}
+
+#pragma mark - UIView
+
+- (void)willMoveToSuperview:(UIView*)newSuperview {
+  [super willMoveToSuperview:newSuperview];
+
+  [self createSubviews];
+}
+
+#pragma mark - Private
+
+// Creates all views for the icon.
+- (void)createSubviews {
+  // Return if the subviews have already been created and added.
+  if (!(self.subviews.count == 0)) {
+    return;
+  }
+
+  self.tintAdjustmentMode = UIViewTintAdjustmentModeNormal;
+
+  _icon = [self createIcon];
+
+  [self addSubview:_icon];
+
+  AddSameConstraints(self, _icon);
+}
+
+// Creates the type-specific icon.
+- (UIView*)createIcon {
+  // Compact, in-square icons are displayed in light blue.
+  if (_inSquare && _compactLayout) {
+    UIImageView* icon = IconForSymbol(_symbol, _symbolWidth, _defaultSymbol,
+                                      @[ [UIColor colorNamed:kBlue500Color] ]);
+
+    return IconInSquareContainer(icon, kBlueHaloColor);
+  }
+
+  // Non-compact, in-square icons are displayed in white.
+  if (_inSquare) {
+    UIImageView* icon = IconForSymbol(_symbol, _symbolWidth, _defaultSymbol,
+                                      @[ [UIColor whiteColor] ]);
+
+    return IconInSquareContainer(icon, kBlue500Color);
+  }
+
+  // By default, display icons in gray, with a square container.
+  return IconForSymbol(_symbol, _symbolWidth, _defaultSymbol,
+                       @[ [UIColor colorNamed:kGrey500Color] ]);
+}
+
+@end
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
index 2eb1910a..bf6c59e 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -358,7 +358,8 @@
         self.contentSuggestionsMetricsRecorder;
     [moduleMediators addObject:_tabResumptionMediator];
   }
-  if (IsPriceTrackingPromoCardEnabled(shoppingService, authenticationService)) {
+  if (IsPriceTrackingPromoCardEnabled(shoppingService, authenticationService,
+                                      prefs)) {
     _priceTrackingPromoMediator = [[PriceTrackingPromoMediator alloc]
         initWithShoppingService:commerce::ShoppingServiceFactory::
                                     GetForBrowserState(
@@ -660,6 +661,10 @@
       [self presentParcelTrackingAlertCoordinator];
       break;
     }
+    case ContentSuggestionsModuleType::kPriceTrackingPromo: {
+      [_priceTrackingPromoMediator disableModule];
+      break;
+    }
     default:
       break;
   }
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/magic_stack/BUILD.gn
index 1213a92..8a53bb5 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/BUILD.gn
@@ -68,10 +68,12 @@
     "//base",
     "//components/commerce/core:feature_list",
     "//components/commerce/core:shopping_service",
+    "//components/prefs:prefs",
     "//components/segmentation_platform/public",
     "//ios/chrome/browser/push_notification/model:push_notification_client_id",
     "//ios/chrome/browser/push_notification/model:push_notification_settings_util_header",
     "//ios/chrome/browser/signin/model",
+    "//ios/chrome/browser/ui/content_suggestions/price_tracking_promo:price_tracking_promo",
   ]
   frameworks = [ "UIKit.framework" ]
 }
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.mm
index 41b5a939..763fc8db 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.mm
@@ -164,6 +164,7 @@
     case ContentSuggestionsModuleType::kSetUpListNotifications:
     case ContentSuggestionsModuleType::kCompactedSetUpList:
     case ContentSuggestionsModuleType::kParcelTracking:
+    case ContentSuggestionsModuleType::kPriceTrackingPromo:
       return YES;
     default:
       return NO;
@@ -282,6 +283,8 @@
           IDS_IOS_SET_UP_LIST_HIDE_MODULE_CONTEXT_MENU_TITLE);
     case ContentSuggestionsModuleType::kParcelTracking:
       return l10n_util::GetNSString(IDS_IOS_PARCEL_TRACKING_CONTEXT_MENU_TITLE);
+    case ContentSuggestionsModuleType::kPriceTrackingPromo:
+      return @"";
     default:
       NOTREACHED();
   }
@@ -310,6 +313,9 @@
           IDS_IOS_PARCEL_TRACKING_CONTEXT_MENU_DESCRIPTION,
           base::SysNSStringToUTF16(l10n_util::GetNSString(
               IDS_IOS_CONTENT_SUGGESTIONS_PARCEL_TRACKING_MODULE_TITLE)));
+    case ContentSuggestionsModuleType::kPriceTrackingPromo:
+      return l10n_util::GetNSString(
+          IDS_IOS_CONTENT_SUGGESTIONS_PRICE_TRACKING_PROMO_HIDE_CARD);
     default:
       NOTREACHED();
   }
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model.mm
index 5733edb..d77d9aed 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model.mm
@@ -348,7 +348,8 @@
   MagicStackModule* card;
   for (const std::string& label : result.ordered_labels) {
     if (label == segmentation_platform::kPriceTrackingNotificationPromo) {
-      if (IsPriceTrackingPromoCardEnabled(_shoppingService, _authService)) {
+      if (IsPriceTrackingPromoCardEnabled(_shoppingService, _authService,
+                                          _prefService)) {
         _ephemeralCardToShow =
             ContentSuggestionsModuleType::kPriceTrackingPromo;
         card = _priceTrackingPromoMediator.priceTrackingPromoItemToShow;
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.h b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.h
index 6cdf409..c2e8524b 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.h
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.h
@@ -8,6 +8,7 @@
 #import <UIKit/UIKit.h>
 
 class AuthenticationService;
+class PrefService;
 
 namespace commerce {
 class ShoppingService;
@@ -20,6 +21,7 @@
 
 // True if the price tracking notification card feature is enabled.
 bool IsPriceTrackingPromoCardEnabled(commerce::ShoppingService* service,
-                                     AuthenticationService* auth_service);
+                                     AuthenticationService* auth_service,
+                                     PrefService* pref_service);
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_MAGIC_STACK_MAGIC_STACK_UTILS_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.mm
index 3e55fc40..a4cd91b 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.mm
@@ -8,12 +8,14 @@
 #import "base/strings/sys_string_conversions.h"
 #import "components/commerce/core/commerce_feature_list.h"
 #import "components/commerce/core/shopping_service.h"
+#import "components/prefs/pref_service.h"
 #import "components/segmentation_platform/public/features.h"
 #import "ios/chrome/browser/push_notification/model/push_notification_client_id.h"
 #import "ios/chrome/browser/push_notification/model/push_notification_settings_util.h"
 #import "ios/chrome/browser/signin/model/authentication_service.h"
 #import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.h"
+#import "ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_prefs.h"
 
 CGFloat ModuleNarrowerWidthToAllowPeekingForTraitCollection(
     UITraitCollection* traitCollection) {
@@ -32,7 +34,8 @@
 }
 
 bool IsPriceTrackingPromoCardEnabled(commerce::ShoppingService* service,
-                                     AuthenticationService* auth_service) {
+                                     AuthenticationService* auth_service,
+                                     PrefService* pref_service) {
   id<SystemIdentity> identity =
       auth_service->GetPrimaryIdentity(signin::ConsentLevel::kSignin);
   return base::FeatureList::IsEnabled(commerce::kPriceTrackingPromo) &&
@@ -40,6 +43,7 @@
              GetMobileNotificationPermissionStatusForClient(
                  PushNotificationClientId::kCommerce,
                  base::SysNSStringToUTF8(identity.gaiaID)) &&
+         !pref_service->GetBoolean(kPriceTrackingPromoDisabled) &&
          (service->IsShoppingListEligible() ||
           base::GetFieldTrialParamByFeatureAsString(
               segmentation_platform::features::
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
index 9f1df8a..a6a1d1c 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -156,6 +156,7 @@
   [ChromeEarlGrey setBoolValue:NO forUserPref:prefs::kSearchSuggestEnabled];
 
   [self closeAllTabs];
+  [ChromeEarlGrey clearBrowsingHistory];
 }
 
 + (void)tearDown {
diff --git a/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/BUILD.gn
index 2a44a8f..7356a8b 100644
--- a/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/BUILD.gn
@@ -10,6 +10,8 @@
     "price_tracking_promo_item.mm",
     "price_tracking_promo_mediator.h",
     "price_tracking_promo_mediator.mm",
+    "price_tracking_promo_prefs.h",
+    "price_tracking_promo_prefs.mm",
     "price_tracking_promo_view.h",
     "price_tracking_promo_view.mm",
   ]
diff --git a/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_mediator.mm b/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_mediator.mm
index a9860cb..6e632bf 100644
--- a/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_mediator.mm
@@ -20,6 +20,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_action_delegate.h"
 #import "ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_item.h"
+#import "ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_prefs.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ios/web/public/thread/web_task_traits.h"
 #import "ios/web/public/thread/web_thread.h"
@@ -84,8 +85,8 @@
 #pragma mark - Public
 
 - (void)disableModule {
-  // TODO(crbug.com/361404422) implement response to
-  // user choosing to disable module.
+  _prefService->SetBoolean(kPriceTrackingPromoDisabled, true);
+  [self.delegate removePriceTrackingPromo];
 }
 
 #pragma mark - PriceTrackingPromoCommands
diff --git a/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_mediator_unittest.mm b/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_mediator_unittest.mm
index 9f98916..088d286b0 100644
--- a/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_mediator_unittest.mm
@@ -23,6 +23,7 @@
 #import "ios/chrome/browser/signin/model/fake_system_identity.h"
 #import "ios/chrome/browser/signin/model/fake_system_identity_manager.h"
 #import "ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_mediator+testing.h"
+#import "ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_prefs.h"
 #import "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
 #import "ios/public/provider/chrome/browser/push_notification/push_notification_api.h"
 #import "ios/testing/scoped_block_swizzler.h"
@@ -74,6 +75,8 @@
         swizzle_block);
     pref_service_.registry()->RegisterBooleanPref(
         commerce::kPriceEmailNotificationsEnabled, false);
+    pref_service_.registry()->RegisterBooleanPref(kPriceTrackingPromoDisabled,
+                                                  false);
   }
 
   ~PriceTrackingPromoMediatorTest() override {}
diff --git a/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_prefs.h b/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_prefs.h
new file mode 100644
index 0000000..754561c5
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_prefs.h
@@ -0,0 +1,15 @@
+// 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 IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_PRICE_TRACKING_PROMO_PRICE_TRACKING_PROMO_PREFS_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_PRICE_TRACKING_PROMO_PRICE_TRACKING_PROMO_PREFS_H_
+
+class PrefRegistrySimple;
+
+// Whether the Price Tracking Promo module is disabled.
+extern const char kPriceTrackingPromoDisabled[];
+
+void RegisterPriceTrackingPromoPrefs(PrefRegistrySimple* registry);
+
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_PRICE_TRACKING_PROMO_PRICE_TRACKING_PROMO_PREFS_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_prefs.mm b/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_prefs.mm
new file mode 100644
index 0000000..2d316daf
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_prefs.mm
@@ -0,0 +1,13 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_prefs.h"
+
+#import "components/prefs/pref_registry_simple.h"
+
+const char kPriceTrackingPromoDisabled[] = "price_tracking_promo.disabled";
+
+void RegisterPriceTrackingPromoPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterBooleanPref(kPriceTrackingPromoDisabled, false);
+}
diff --git a/ios/chrome/browser/ui/content_suggestions/safety_check/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/safety_check/BUILD.gn
index 3ae70ee6..1dcbd58 100644
--- a/ios/chrome/browser/ui/content_suggestions/safety_check/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/safety_check/BUILD.gn
@@ -8,8 +8,6 @@
     "safety_check_consumer_source.h",
     "safety_check_item_icon.h",
     "safety_check_item_icon.mm",
-    "safety_check_item_view.h",
-    "safety_check_item_view.mm",
     "safety_check_magic_stack_consumer.h",
     "safety_check_magic_stack_mediator.h",
     "safety_check_magic_stack_mediator.mm",
@@ -105,6 +103,7 @@
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile/test",
     "//ios/chrome/browser/ui/content_suggestions:public",
+    "//ios/chrome/browser/ui/content_suggestions/cells",
     "//ios/chrome/test:test_support",
     "//ios/web/public/test",
     "//third_party/ocmock",
diff --git a/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_item_view.h b/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_item_view.h
deleted file mode 100644
index ec9e53e1..0000000
--- a/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_item_view.h
+++ /dev/null
@@ -1,48 +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_UI_CONTENT_SUGGESTIONS_SAFETY_CHECK_SAFETY_CHECK_ITEM_VIEW_H_
-#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_SAFETY_CHECK_SAFETY_CHECK_ITEM_VIEW_H_
-
-#import <UIKit/UIKit.h>
-
-enum class SafetyCheckItemType;
-enum class SafetyCheckItemLayoutType;
-@class SafetyCheckItemView;
-@class SafetyCheckState;
-
-// A protocol for handling `SafetyCheckItemView` taps.
-@protocol SafetyCheckItemViewTapDelegate
-// Indicates that the user has tapped the given `view`.
-- (void)didTapSafetyCheckItemView:(SafetyCheckItemView*)view;
-@end
-
-// A view to display an individual check state (list item) in the Safety Check
-// (Magic Stack) module.
-@interface SafetyCheckItemView : UIView
-
-// Convenience initializer for creating a SafetyCheckItemView with the given
-// `itemType` and `layoutType`, but without specific insecure credentials
-// information.
-- (instancetype)initWithItemType:(SafetyCheckItemType)itemType
-                      layoutType:(SafetyCheckItemLayoutType)layoutType;
-
-// Initialize a SafetyCheckItemView with the given `itemType`, `layoutType`,
-// `weakPasswordsCount`, `reusedPasswordsCount`, and
-// `compromisedPasswordsCount`.
-- (instancetype)initWithItemType:(SafetyCheckItemType)itemType
-                      layoutType:(SafetyCheckItemLayoutType)layoutType
-              weakPasswordsCount:(NSInteger)weakPasswordsCount
-            reusedPasswordsCount:(NSInteger)reusedPasswordsCount
-       compromisedPasswordsCount:(NSInteger)compromisedPasswordsCount;
-
-// Indicates the type of item.
-@property(nonatomic, readonly) SafetyCheckItemType itemType;
-
-// The object that should receive a message when this view is tapped.
-@property(nonatomic, weak) id<SafetyCheckItemViewTapDelegate> tapDelegate;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_SAFETY_CHECK_SAFETY_CHECK_ITEM_VIEW_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_item_view.mm b/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_item_view.mm
deleted file mode 100644
index 653d6cd..0000000
--- a/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_item_view.mm
+++ /dev/null
@@ -1,490 +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/ui/content_suggestions/safety_check/safety_check_item_view.h"
-
-#import "base/strings/string_number_conversions.h"
-#import "components/version_info/version_info.h"
-#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
-#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
-#import "ios/chrome/browser/ui/content_suggestions/safety_check/constants.h"
-#import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_item_icon.h"
-#import "ios/chrome/browser/ui/content_suggestions/safety_check/types.h"
-#import "ios/chrome/common/channel_info.h"
-#import "ios/chrome/common/ui/colors/semantic_color_names.h"
-#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
-#import "ios/chrome/common/ui/util/constraints_ui_util.h"
-#import "ios/chrome/common/ui/util/dynamic_type_util.h"
-#import "ios/chrome/grit/ios_branded_strings.h"
-#import "ios/chrome/grit/ios_strings.h"
-#import "ui/base/l10n/l10n_util.h"
-
-namespace {
-
-// The spacing between the title and description.
-constexpr CGFloat kTitleDescriptionSpacing = 2;
-
-// The spacing between elements within the item.
-constexpr CGFloat kContentStackSpacing = 14;
-
-// Constants related to the icon container view.
-constexpr CGFloat kIconContainerSize = 56;
-constexpr CGFloat kIconContainerCornerRadius = 12;
-
-// The size of the checkmark icon.
-constexpr CGFloat kCheckmarkSize = 19;
-constexpr CGFloat kCheckmarkTopOffset = -6;
-constexpr CGFloat kCheckmarkTrailingOffset = 6;
-
-// The checkmark icon used for a hero-cell complete item.
-UIImageView* CheckmarkIcon() {
-  UIImageSymbolConfiguration* config = [UIImageSymbolConfiguration
-      configurationWithWeight:UIImageSymbolWeightMedium];
-
-  UIImageSymbolConfiguration* colorConfig =
-      [UIImageSymbolConfiguration configurationWithPaletteColors:@[
-        [UIColor whiteColor], [UIColor colorNamed:kGreen500Color]
-      ]];
-
-  config = [config configurationByApplyingConfiguration:colorConfig];
-
-  UIImage* image =
-      DefaultSymbolWithConfiguration(kCheckmarkCircleFillSymbol, config);
-
-  UIImageView* icon = [[UIImageView alloc] initWithImage:image];
-
-  icon.translatesAutoresizingMaskIntoConstraints = NO;
-
-  [NSLayoutConstraint activateConstraints:@[
-    [icon.widthAnchor constraintEqualToConstant:kCheckmarkSize],
-    [icon.heightAnchor constraintEqualToAnchor:icon.widthAnchor],
-  ]];
-
-  return icon;
-}
-
-}  // namespace
-
-@implementation SafetyCheckItemView {
-  // The item layout type.
-  SafetyCheckItemLayoutType _layoutType;
-  // The number of weak passwords found by the Password check.
-  NSInteger _weakPasswordsCount;
-  // The number of reused passwords found by the Password check.
-  NSInteger _reusedPasswordsCount;
-  // The number of compromised passwords found by the Password check.
-  NSInteger _compromisedPasswordsCount;
-  // UI tap gesture recognizer.
-  UITapGestureRecognizer* _tapGestureRecognizer;
-}
-
-- (instancetype)initWithItemType:(SafetyCheckItemType)itemType
-                      layoutType:(SafetyCheckItemLayoutType)layoutType {
-  self = [self initWithItemType:itemType
-                     layoutType:layoutType
-             weakPasswordsCount:0
-           reusedPasswordsCount:0
-      compromisedPasswordsCount:0];
-
-  return self;
-}
-
-- (instancetype)initWithItemType:(SafetyCheckItemType)itemType
-                      layoutType:(SafetyCheckItemLayoutType)layoutType
-              weakPasswordsCount:(NSInteger)weakPasswordsCount
-            reusedPasswordsCount:(NSInteger)reusedPasswordsCount
-       compromisedPasswordsCount:(NSInteger)compromisedPasswordsCount {
-  if ((self = [super init])) {
-    _itemType = itemType;
-    _layoutType = layoutType;
-    _weakPasswordsCount = weakPasswordsCount;
-    _reusedPasswordsCount = reusedPasswordsCount;
-    _compromisedPasswordsCount = compromisedPasswordsCount;
-  }
-
-  return self;
-}
-
-#pragma mark - UIView
-
-- (void)willMoveToSuperview:(UIView*)newSuperview {
-  [super willMoveToSuperview:newSuperview];
-
-  [self createSubviews];
-}
-
-- (NSString*)accessibilityLabel {
-  return
-      [NSString stringWithFormat:@"%@, %@", [self titleText],
-                                 _layoutType == SafetyCheckItemLayoutType::kHero
-                                     ? [self descriptionText]
-                                     : [self compactDescriptionText]];
-}
-
-#pragma mark - Private
-
-- (void)handleTap:(UITapGestureRecognizer*)sender {
-  if (sender.state == UIGestureRecognizerStateEnded) {
-    [self.tapDelegate didTapSafetyCheckItemView:self];
-  }
-}
-
-// Creates all views for an individual check row in the Safety Check (Magic
-// Stack) module.
-- (void)createSubviews {
-  // Return if the subviews have already been created and added.
-  if (!(self.subviews.count == 0)) {
-    return;
-  }
-
-  self.translatesAutoresizingMaskIntoConstraints = NO;
-  self.accessibilityIdentifier =
-      [self accessibilityIdentifierForItemType:_itemType];
-  self.isAccessibilityElement = YES;
-  self.accessibilityTraits = UIAccessibilityTraitButton;
-
-  // Add a horizontal stack to contain the icon, text stack, and (optional)
-  // chevron.
-  NSMutableArray* arrangedSubviews = [[NSMutableArray alloc] init];
-
-  SafetyCheckItemIcon* icon = [self iconForItemType:_itemType
-                                         layoutType:_layoutType];
-
-  // When the item is displayed in a hero-style layout, the icon is more
-  // prominently displayed via an icon container view.
-  if (_layoutType == SafetyCheckItemLayoutType::kHero) {
-    UIView* iconContainerView = [self iconInContainer:icon];
-
-    // Display a green checkmark when the layout is hero-cell complete.
-    if (_itemType == SafetyCheckItemType::kAllSafe) {
-      UIImageView* checkmark = CheckmarkIcon();
-
-      [iconContainerView addSubview:checkmark];
-
-      [NSLayoutConstraint activateConstraints:@[
-        [checkmark.topAnchor constraintEqualToAnchor:iconContainerView.topAnchor
-                                            constant:kCheckmarkTopOffset],
-        [checkmark.trailingAnchor
-            constraintEqualToAnchor:iconContainerView.trailingAnchor
-                           constant:kCheckmarkTrailingOffset],
-      ]];
-    }
-
-    [arrangedSubviews addObject:iconContainerView];
-  } else {
-    [arrangedSubviews addObject:icon];
-  }
-
-  UILabel* titleLabel = [self createTitleLabelForLayoutType:_layoutType];
-
-  [titleLabel
-      setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh
-                                      forAxis:UILayoutConstraintAxisVertical];
-
-  UILabel* descriptionLabel = [self createDescriptionLabel];
-
-  [descriptionLabel
-      setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
-                                      forAxis:UILayoutConstraintAxisVertical];
-  self.accessibilityLabel =
-      [NSString stringWithFormat:@"%@,%@", titleLabel, descriptionLabel];
-
-  // Add a vertical stack for the title and description labels.
-  UIStackView* textStack = [[UIStackView alloc]
-      initWithArrangedSubviews:@[ titleLabel, descriptionLabel ]];
-
-  textStack.axis = UILayoutConstraintAxisVertical;
-  textStack.translatesAutoresizingMaskIntoConstraints = NO;
-  textStack.spacing = kTitleDescriptionSpacing;
-  [textStack setContentHuggingPriority:UILayoutPriorityDefaultLow
-                               forAxis:UILayoutConstraintAxisHorizontal];
-
-  [arrangedSubviews addObject:textStack];
-
-  // For compact layout, display a chevron at the end of the item.
-  if (_layoutType == SafetyCheckItemLayoutType::kCompact) {
-    UIImageView* chevron = [[UIImageView alloc]
-        initWithImage:[UIImage imageNamed:@"table_view_cell_chevron"]];
-
-    [chevron setContentHuggingPriority:UILayoutPriorityDefaultHigh
-                               forAxis:UILayoutConstraintAxisHorizontal];
-
-    [arrangedSubviews addObject:chevron];
-  }
-
-  UIStackView* contentStack =
-      [[UIStackView alloc] initWithArrangedSubviews:arrangedSubviews];
-
-  contentStack.translatesAutoresizingMaskIntoConstraints = NO;
-  contentStack.axis = UILayoutConstraintAxisHorizontal;
-  contentStack.alignment = UIStackViewAlignmentCenter;
-  contentStack.spacing = kContentStackSpacing;
-
-  [self addSubview:contentStack];
-
-  AddSameConstraints(contentStack, self);
-
-  // Set up the tap gesture recognizer.
-  _tapGestureRecognizer =
-      [[UITapGestureRecognizer alloc] initWithTarget:self
-                                              action:@selector(handleTap:)];
-
-  [self addGestureRecognizer:_tapGestureRecognizer];
-}
-
-// Returns the corresponding `SafetyCheckItemIcon*` given an `itemType` and
-// `layoutType`.
-- (SafetyCheckItemIcon*)iconForItemType:(SafetyCheckItemType)itemType
-                             layoutType:(SafetyCheckItemLayoutType)layoutType {
-  BOOL compactLayout = layoutType == SafetyCheckItemLayoutType::kCompact;
-  BOOL inSquare = YES;
-
-  switch (itemType) {
-    case SafetyCheckItemType::kUpdateChrome:
-      return
-          [[SafetyCheckItemIcon alloc] initWithDefaultSymbol:kInfoCircleSymbol
-                                               compactLayout:compactLayout
-                                                    inSquare:inSquare];
-    case SafetyCheckItemType::kPassword:
-      return [[SafetyCheckItemIcon alloc] initWithCustomSymbol:kPasswordSymbol
-                                                 compactLayout:compactLayout
-                                                      inSquare:inSquare];
-    case SafetyCheckItemType::kSafeBrowsing:
-      return [[SafetyCheckItemIcon alloc] initWithCustomSymbol:kPrivacySymbol
-                                                 compactLayout:compactLayout
-                                                      inSquare:inSquare];
-    case SafetyCheckItemType::kAllSafe:
-    case SafetyCheckItemType::kRunning:
-    case SafetyCheckItemType::kDefault:
-      return
-          [[SafetyCheckItemIcon alloc] initWithCustomSymbol:kSafetyCheckSymbol
-                                              compactLayout:compactLayout
-                                                   inSquare:inSquare];
-  }
-}
-
-// Returns `icon` wrapped in a container view.
-- (UIView*)iconInContainer:(SafetyCheckItemIcon*)icon {
-  icon.translatesAutoresizingMaskIntoConstraints = NO;
-
-  UIView* iconContainer = [[UIView alloc] init];
-
-  iconContainer.backgroundColor = [UIColor colorNamed:kGrey100Color];
-  iconContainer.layer.cornerRadius = kIconContainerCornerRadius;
-
-  [iconContainer addSubview:icon];
-
-  AddSameCenterConstraints(icon, iconContainer);
-
-  [NSLayoutConstraint activateConstraints:@[
-    [iconContainer.widthAnchor constraintEqualToConstant:kIconContainerSize],
-    [iconContainer.widthAnchor
-        constraintEqualToAnchor:iconContainer.heightAnchor],
-  ]];
-
-  return iconContainer;
-}
-
-// Creates the title label using `layoutType`.
-- (UILabel*)createTitleLabelForLayoutType:
-    (SafetyCheckItemLayoutType)layoutType {
-  UILabel* label = [[UILabel alloc] init];
-
-  label.text = [self titleText];
-  label.translatesAutoresizingMaskIntoConstraints = NO;
-  label.numberOfLines = 0;
-  label.lineBreakMode = NSLineBreakByWordWrapping;
-  label.font =
-      layoutType == SafetyCheckItemLayoutType::kHero
-          ? CreateDynamicFont(UIFontTextStyleFootnote, UIFontWeightSemibold)
-          : [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
-  label.adjustsFontForContentSizeCategory = YES;
-  label.textColor = [UIColor colorNamed:kTextPrimaryColor];
-
-  return label;
-}
-
-- (NSString*)titleText {
-  switch (_itemType) {
-    case SafetyCheckItemType::kAllSafe:
-      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_TITLE_ALL_SAFE);
-    case SafetyCheckItemType::kRunning:
-      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_RUNNING);
-    case SafetyCheckItemType::kUpdateChrome:
-      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_TITLE_UPDATE_CHROME);
-    case SafetyCheckItemType::kPassword:
-      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_TITLE_PASSWORD);
-    case SafetyCheckItemType::kSafeBrowsing:
-      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_TITLE_SAFE_BROWSING);
-    case SafetyCheckItemType::kDefault:
-      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_TITLE_DEFAULT);
-  }
-}
-
-// Creates the description label.
-- (UILabel*)createDescriptionLabel {
-  UILabel* label = [[UILabel alloc] init];
-
-  label.text = _layoutType == SafetyCheckItemLayoutType::kHero
-                   ? [self descriptionText]
-                   : [self compactDescriptionText];
-  label.numberOfLines = 2;
-  label.lineBreakMode = NSLineBreakByTruncatingTail;
-  label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
-  label.adjustsFontForContentSizeCategory = YES;
-  label.textColor = [UIColor colorNamed:kTextSecondaryColor];
-
-  return label;
-}
-
-- (NSString*)descriptionText {
-  switch (_itemType) {
-    case SafetyCheckItemType::kAllSafe:
-      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_DESCRIPTION_ALL_SAFE);
-    case SafetyCheckItemType::kRunning:
-      // The running state has no description text.
-      return @"";
-    case SafetyCheckItemType::kUpdateChrome:
-      return [self updateChromeItemDescriptionText];
-    case SafetyCheckItemType::kPassword:
-      return [self passwordItemDescriptionText];
-    case SafetyCheckItemType::kSafeBrowsing:
-      return l10n_util::GetNSString(
-          IDS_IOS_SAFETY_CHECK_DESCRIPTION_SAFE_BROWSING);
-    case SafetyCheckItemType::kDefault:
-      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_DESCRIPTION_DEFAULT);
-  }
-}
-
-- (NSString*)updateChromeItemDescriptionText {
-  switch (::GetChannel()) {
-    case version_info::Channel::STABLE:
-    case version_info::Channel::DEV:
-      return l10n_util::GetNSString(
-          IDS_IOS_SAFETY_CHECK_DESCRIPTION_UPDATE_CHROME);
-    case version_info::Channel::BETA:
-      return l10n_util::GetNSString(
-          IDS_IOS_SETTINGS_SAFETY_CHECK_UPDATES_CHANNEL_BETA_DESC);
-    case version_info::Channel::CANARY:
-      return l10n_util::GetNSString(
-          IDS_IOS_SETTINGS_SAFETY_CHECK_UPDATES_CHANNEL_CANARY_DESC);
-    default:
-      return l10n_util::GetNSString(
-          IDS_IOS_SAFETY_CHECK_DESCRIPTION_UPDATE_CHROME);
-  }
-}
-
-- (NSString*)passwordItemDescriptionText {
-  if (_compromisedPasswordsCount > 1) {
-    return l10n_util::GetNSStringF(
-        IDS_IOS_SAFETY_CHECK_DESCRIPTION_MULTIPLE_COMPROMISED_PASSWORDS,
-        base::NumberToString16(_compromisedPasswordsCount));
-  }
-
-  if (_compromisedPasswordsCount == 1) {
-    return l10n_util::GetNSString(
-        IDS_IOS_SAFETY_CHECK_DESCRIPTION_COMPROMISED_PASSWORD);
-  }
-
-  if (_reusedPasswordsCount > 1) {
-    return l10n_util::GetNSStringF(
-        IDS_IOS_SAFETY_CHECK_DESCRIPTION_MULTIPLE_REUSED_PASSWORDS,
-        base::NumberToString16(_reusedPasswordsCount));
-  }
-
-  if (_reusedPasswordsCount == 1) {
-    return l10n_util::GetNSString(
-        IDS_IOS_SAFETY_CHECK_DESCRIPTION_REUSED_PASSWORD);
-  }
-
-  if (_weakPasswordsCount > 1) {
-    return l10n_util::GetNSStringF(
-        IDS_IOS_SAFETY_CHECK_DESCRIPTION_MULTIPLE_WEAK_PASSWORDS,
-        base::NumberToString16(_weakPasswordsCount));
-  }
-
-  if (_weakPasswordsCount == 1) {
-    return l10n_util::GetNSString(
-        IDS_IOS_SAFETY_CHECK_DESCRIPTION_WEAK_PASSWORD);
-  }
-
-  return l10n_util::GetNSString(
-      IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_MULTIPLE_PASSWORD_ISSUES);
-}
-
-- (NSString*)compactDescriptionText {
-  switch (_itemType) {
-    case SafetyCheckItemType::kAllSafe:
-      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_DESCRIPTION_ALL_SAFE);
-    case SafetyCheckItemType::kRunning:
-      // The running state has no description text.
-      return @"";
-    case SafetyCheckItemType::kUpdateChrome:
-      return [self updateChromeItemCompactDescriptionText];
-    case SafetyCheckItemType::kPassword:
-      return [self passwordItemCompactDescriptionText];
-    case SafetyCheckItemType::kSafeBrowsing:
-      return l10n_util::GetNSString(
-          IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_SAFE_BROWSING);
-    case SafetyCheckItemType::kDefault:
-      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_TITLE_DEFAULT);
-  }
-}
-
-- (NSString*)updateChromeItemCompactDescriptionText {
-  switch (::GetChannel()) {
-    case version_info::Channel::STABLE:
-    case version_info::Channel::DEV:
-      return l10n_util::GetNSString(
-          IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_UPDATE_CHROME);
-    case version_info::Channel::BETA:
-      return l10n_util::GetNSString(
-          IDS_IOS_SETTINGS_SAFETY_CHECK_UPDATES_CHANNEL_BETA_DESC);
-    case version_info::Channel::CANARY:
-      return l10n_util::GetNSString(
-          IDS_IOS_SETTINGS_SAFETY_CHECK_UPDATES_CHANNEL_CANARY_DESC);
-    default:
-      return l10n_util::GetNSString(
-          IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_UPDATE_CHROME);
-  }
-}
-
-- (NSString*)passwordItemCompactDescriptionText {
-  if (_compromisedPasswordsCount >= 1) {
-    return l10n_util::GetNSString(
-        IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_COMPROMISED_PASSWORD);
-  }
-
-  if (_reusedPasswordsCount >= 1) {
-    return l10n_util::GetNSString(
-        IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_REUSED_PASSWORD);
-  }
-
-  if (_weakPasswordsCount >= 1) {
-    return l10n_util::GetNSString(
-        IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_WEAK_PASSWORD);
-  }
-
-  return l10n_util::GetNSString(
-      IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_MULTIPLE_PASSWORD_ISSUES);
-}
-
-- (NSString*)accessibilityIdentifierForItemType:(SafetyCheckItemType)itemType {
-  switch (itemType) {
-    case SafetyCheckItemType::kAllSafe:
-      return safety_check::kAllSafeItemID;
-    case SafetyCheckItemType::kRunning:
-      return safety_check::kRunningItemID;
-    case SafetyCheckItemType::kUpdateChrome:
-      return safety_check::kUpdateChromeItemID;
-    case SafetyCheckItemType::kPassword:
-      return safety_check::kPasswordItemID;
-    case SafetyCheckItemType::kSafeBrowsing:
-      return safety_check::kSafeBrowsingItemID;
-    case SafetyCheckItemType::kDefault:
-      return safety_check::kDefaultItemID;
-  }
-}
-
-@end
diff --git a/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_view.mm b/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_view.mm
index 814f349e..6230ee9 100644
--- a/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_view.mm
@@ -4,18 +4,47 @@
 
 #import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_view.h"
 
+#import "base/check.h"
+#import "base/strings/string_number_conversions.h"
+#import "components/version_info/version_info.h"
 #import "ios/chrome/browser/safety_check/model/ios_chrome_safety_check_manager_constants.h"
+#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/icon_detail_view.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/multi_row_container_view.h"
 #import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_content_view_delegate.h"
 #import "ios/chrome/browser/ui/content_suggestions/safety_check/constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_audience.h"
-#import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_item_view.h"
 #import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_state.h"
 #import "ios/chrome/browser/ui/content_suggestions/safety_check/types.h"
 #import "ios/chrome/browser/ui/content_suggestions/safety_check/utils.h"
+#import "ios/chrome/common/channel_info.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
+#import "ios/chrome/grit/ios_branded_strings.h"
+#import "ios/chrome/grit/ios_strings.h"
+#import "ui/base/l10n/l10n_util.h"
 
-@interface SafetyCheckView () <SafetyCheckItemViewTapDelegate>
+namespace {
+
+// Returns `true` if any of the safety check components are currently running.
+bool IsRunning(SafetyCheckState* state) {
+  return state.runningState == RunningSafetyCheckState::kRunning ||
+         state.updateChromeState == UpdateChromeSafetyCheckState::kRunning ||
+         state.passwordState == PasswordSafetyCheckState::kRunning ||
+         state.safeBrowsingState == SafeBrowsingSafetyCheckState::kRunning;
+}
+
+// Returns `true` if all of the safety check components are in the default
+// state.
+bool IsDefault(SafetyCheckState* state) {
+  return state.runningState == RunningSafetyCheckState::kDefault &&
+         state.updateChromeState == UpdateChromeSafetyCheckState::kDefault &&
+         state.passwordState == PasswordSafetyCheckState::kDefault &&
+         state.safeBrowsingState == SafeBrowsingSafetyCheckState::kDefault;
+}
+
+}  // namespace
+
+@interface SafetyCheckView () <IconDetailViewTapDelegate>
 @end
 
 @implementation SafetyCheckView {
@@ -55,10 +84,14 @@
   [self createSubviews];
 }
 
-#pragma mark - SafetyCheckItemViewTapDelegate
+#pragma mark - IconDetailViewTapDelegate
 
-- (void)didTapSafetyCheckItemView:(SafetyCheckItemView*)view {
-  [self.audience didSelectSafetyCheckItem:view.itemType];
+- (void)didTapIconDetailView:(IconDetailView*)view {
+  CHECK(view.identifier != nil);
+
+  SafetyCheckItemType itemType = SafetyCheckItemTypeForName(view.identifier);
+
+  [self.audience didSelectSafetyCheckItem:itemType];
 }
 
 #pragma mark - Private methods
@@ -76,147 +109,288 @@
   [_contentViewDelegate
       setSubtitle:FormatElapsedTimeSinceLastSafetyCheck(_state.lastRunTime)];
 
-  // If any of the checks are running, the module should display its running
-  // state.
-  if (_state.runningState == RunningSafetyCheckState::kRunning ||
-      _state.updateChromeState == UpdateChromeSafetyCheckState::kRunning ||
-      _state.passwordState == PasswordSafetyCheckState::kRunning ||
-      _state.safeBrowsingState == SafeBrowsingSafetyCheckState::kRunning) {
-    SafetyCheckItemView* view = [[SafetyCheckItemView alloc]
-        initWithItemType:SafetyCheckItemType::kRunning
-              layoutType:SafetyCheckItemLayoutType::kHero];
-
-    view.tapDelegate = self;
-
-    _contentView = view;
-    [self addSubview:_contentView];
-
-    AddSameConstraints(_contentView, self);
-
-    return;
-  }
-
-  // If all checks are in the default state, the module should display the
-  // default state.
-  if (_state.runningState == RunningSafetyCheckState::kDefault &&
-      _state.updateChromeState == UpdateChromeSafetyCheckState::kDefault &&
-      _state.passwordState == PasswordSafetyCheckState::kDefault &&
-      _state.safeBrowsingState == SafeBrowsingSafetyCheckState::kDefault) {
-    SafetyCheckItemView* view = [[SafetyCheckItemView alloc]
-        initWithItemType:SafetyCheckItemType::kDefault
-              layoutType:SafetyCheckItemLayoutType::kHero];
-
-    view.tapDelegate = self;
-
-    _contentView = view;
-    [self addSubview:_contentView];
-
-    AddSameConstraints(_contentView, self);
-
-    return;
-  }
-
   int checkIssuesCount = [_state numberOfIssues];
+  BOOL isRunning = IsRunning(_state);
+  BOOL isDefault = IsDefault(_state);
+  SafetyCheckItemType itemType;
 
-  // Show the "All Safe" state if there are no check issues.
-  if (checkIssuesCount == 0) {
-    SafetyCheckItemView* view = [[SafetyCheckItemView alloc]
-        initWithItemType:SafetyCheckItemType::kAllSafe
-              layoutType:SafetyCheckItemLayoutType::kHero];
-
-    view.tapDelegate = self;
-
-    _contentView = view;
-    [self addSubview:_contentView];
-
-    AddSameConstraints(_contentView, self);
-
-    return;
-  }
-
-  if (checkIssuesCount > 1) {
-    NSMutableArray<SafetyCheckItemView*>* safetyCheckItems =
-        [[NSMutableArray alloc] init];
-
-    // Update Chrome check
+  if (isRunning) {
+    itemType = SafetyCheckItemType::kRunning;
+  } else if (isDefault) {
+    itemType = SafetyCheckItemType::kDefault;
+  } else if (checkIssuesCount == 0) {
+    itemType = SafetyCheckItemType::kAllSafe;
+  } else if (checkIssuesCount == 1) {
     if (InvalidUpdateChromeState(_state.updateChromeState)) {
-      SafetyCheckItemView* updateChromeView = [[SafetyCheckItemView alloc]
-          initWithItemType:SafetyCheckItemType::kUpdateChrome
-                layoutType:SafetyCheckItemLayoutType::kCompact];
-
-      updateChromeView.tapDelegate = self;
-
-      [safetyCheckItems addObject:updateChromeView];
+      itemType = SafetyCheckItemType::kUpdateChrome;
+    } else if (InvalidPasswordState(_state.passwordState)) {
+      itemType = SafetyCheckItemType::kPassword;
+    } else if (InvalidSafeBrowsingState(_state.safeBrowsingState)) {
+      itemType = SafetyCheckItemType::kSafeBrowsing;
+    } else {
+      NOTREACHED();
     }
+  }
 
-    // Password check
-    if (InvalidPasswordState(_state.passwordState)) {
-      SafetyCheckItemView* passwordView = [[SafetyCheckItemView alloc]
-                   initWithItemType:SafetyCheckItemType::kPassword
-                         layoutType:SafetyCheckItemLayoutType::kCompact
-                 weakPasswordsCount:_state.weakPasswordsCount
-               reusedPasswordsCount:_state.reusedPasswordsCount
-          compromisedPasswordsCount:_state.compromisedPasswordsCount];
-
-      passwordView.tapDelegate = self;
-
-      [safetyCheckItems addObject:passwordView];
-    }
-
-    // Safe Browsing check
-    //
-    // NOTE: Don't add the Safe Browsing check if two items already exist in
-    // `safetyCheckItems`. At most, the compact view displays two rows of items.
-    if ([safetyCheckItems count] < 2 &&
-        InvalidSafeBrowsingState(_state.safeBrowsingState)) {
-      SafetyCheckItemView* safeBrowsingView = [[SafetyCheckItemView alloc]
-          initWithItemType:SafetyCheckItemType::kSafeBrowsing
-                layoutType:SafetyCheckItemLayoutType::kCompact];
-
-      safeBrowsingView.tapDelegate = self;
-
-      [safetyCheckItems addObject:safeBrowsingView];
-    }
-
-    MultiRowContainerView* multiRowContainer =
-        [[MultiRowContainerView alloc] initWithViews:safetyCheckItems];
-
-    multiRowContainer.translatesAutoresizingMaskIntoConstraints = NO;
-
-    _contentView = multiRowContainer;
+  if (isRunning || isDefault || checkIssuesCount <= 1) {
+    _contentView = [self iconDetailView:itemType
+                             layoutType:IconDetailViewLayoutType::kHero];
     [self addSubview:_contentView];
-
     AddSameConstraints(_contentView, self);
-
     return;
   }
 
-  // Show hero-cell view for single check issue.
-  SafetyCheckItemView* view;
+  NSMutableArray<IconDetailView*>* safetyCheckItems =
+      [[NSMutableArray alloc] init];
 
   if (InvalidUpdateChromeState(_state.updateChromeState)) {
-    view = [[SafetyCheckItemView alloc]
-        initWithItemType:SafetyCheckItemType::kUpdateChrome
-              layoutType:SafetyCheckItemLayoutType::kHero];
-  } else if (InvalidPasswordState(_state.passwordState)) {
-    view = [[SafetyCheckItemView alloc]
-                 initWithItemType:SafetyCheckItemType::kPassword
-                       layoutType:SafetyCheckItemLayoutType::kHero
-               weakPasswordsCount:_state.weakPasswordsCount
-             reusedPasswordsCount:_state.reusedPasswordsCount
-        compromisedPasswordsCount:_state.compromisedPasswordsCount];
-  } else if (InvalidSafeBrowsingState(_state.safeBrowsingState)) {
-    view = [[SafetyCheckItemView alloc]
-        initWithItemType:SafetyCheckItemType::kSafeBrowsing
-              layoutType:SafetyCheckItemLayoutType::kHero];
+    [safetyCheckItems
+        addObject:[self iconDetailView:SafetyCheckItemType::kUpdateChrome
+                            layoutType:IconDetailViewLayoutType::kCompact]];
   }
 
+  if (InvalidPasswordState(_state.passwordState)) {
+    [safetyCheckItems
+        addObject:[self iconDetailView:SafetyCheckItemType::kPassword
+                            layoutType:IconDetailViewLayoutType::kCompact]];
+  }
+
+  // NOTE: Don't add the Safe Browsing check if two items already exist in
+  // `safetyCheckItems`. At most, the compact view displays two rows of items.
+  if ([safetyCheckItems count] < 2 &&
+      InvalidSafeBrowsingState(_state.safeBrowsingState)) {
+    [safetyCheckItems
+        addObject:[self iconDetailView:SafetyCheckItemType::kSafeBrowsing
+                            layoutType:IconDetailViewLayoutType::kCompact]];
+  }
+
+  _contentView = [[MultiRowContainerView alloc] initWithViews:safetyCheckItems];
+  _contentView.translatesAutoresizingMaskIntoConstraints = NO;
+  [self addSubview:_contentView];
+  AddSameConstraints(_contentView, self);
+}
+
+// Creates and returns an `IconDetailView` configured for the given `itemType`
+// and `layoutType`.
+- (IconDetailView*)iconDetailView:(SafetyCheckItemType)itemType
+                       layoutType:(IconDetailViewLayoutType)layoutType {
+  NSString* symbolName = [self symbolNameForItemType:itemType];
+
+  // `kInfoCircleSymbol` is the only default symbol used within the Safety Check
+  // view(s).
+  BOOL usesDefaultSymbol = [symbolName isEqualToString:kInfoCircleSymbol];
+
+  IconDetailView* view = [[IconDetailView alloc]
+                initWithTitle:[self titleText:itemType]
+                  description:(layoutType == IconDetailViewLayoutType::kHero
+                                   ? [self descriptionText:itemType]
+                                   : [self compactDescriptionText:itemType])
+                   layoutType:layoutType
+                   symbolName:symbolName
+            usesDefaultSymbol:usesDefaultSymbol
+                showCheckmark:(itemType == SafetyCheckItemType::kAllSafe)
+      accessibilityIdentifier:[self
+                                  accessibilityIdentifierForItemType:itemType]];
+
+  view.identifier = NameForSafetyCheckItemType(itemType);
+
   view.tapDelegate = self;
 
-  _contentView = view;
-  [self addSubview:_contentView];
+  return view;
+}
 
-  AddSameConstraints(_contentView, self);
+// Returns the title text for the given `itemType`.
+- (NSString*)titleText:(SafetyCheckItemType)itemType {
+  switch (itemType) {
+    case SafetyCheckItemType::kAllSafe:
+      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_TITLE_ALL_SAFE);
+    case SafetyCheckItemType::kRunning:
+      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_RUNNING);
+    case SafetyCheckItemType::kUpdateChrome:
+      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_TITLE_UPDATE_CHROME);
+    case SafetyCheckItemType::kPassword:
+      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_TITLE_PASSWORD);
+    case SafetyCheckItemType::kSafeBrowsing:
+      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_TITLE_SAFE_BROWSING);
+    case SafetyCheckItemType::kDefault:
+      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_TITLE_DEFAULT);
+  }
+}
+
+// Returns the detailed description text for the given `itemType`.
+- (NSString*)descriptionText:(SafetyCheckItemType)itemType {
+  switch (itemType) {
+    case SafetyCheckItemType::kAllSafe:
+      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_DESCRIPTION_ALL_SAFE);
+    case SafetyCheckItemType::kRunning:
+      // The running state has no description text.
+      return @"";
+    case SafetyCheckItemType::kUpdateChrome:
+      return [self updateChromeItemDescriptionText];
+    case SafetyCheckItemType::kPassword:
+      return [self passwordItemDescriptionText];
+    case SafetyCheckItemType::kSafeBrowsing:
+      return l10n_util::GetNSString(
+          IDS_IOS_SAFETY_CHECK_DESCRIPTION_SAFE_BROWSING);
+    case SafetyCheckItemType::kDefault:
+      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_DESCRIPTION_DEFAULT);
+  }
+}
+
+// Returns the compact description text for the given `itemType`.
+- (NSString*)compactDescriptionText:(SafetyCheckItemType)itemType {
+  switch (itemType) {
+    case SafetyCheckItemType::kAllSafe:
+      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_DESCRIPTION_ALL_SAFE);
+    case SafetyCheckItemType::kRunning:
+      // The running state has no description text.
+      return @"";
+    case SafetyCheckItemType::kUpdateChrome:
+      return [self updateChromeItemCompactDescriptionText];
+    case SafetyCheckItemType::kPassword:
+      return [self passwordItemCompactDescriptionText];
+    case SafetyCheckItemType::kSafeBrowsing:
+      return l10n_util::GetNSString(
+          IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_SAFE_BROWSING);
+    case SafetyCheckItemType::kDefault:
+      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_TITLE_DEFAULT);
+  }
+}
+
+// Returns the detailed description text for the Update Chrome item, considering
+// the current channel.
+- (NSString*)updateChromeItemDescriptionText {
+  switch (::GetChannel()) {
+    case version_info::Channel::STABLE:
+    case version_info::Channel::DEV:
+      return l10n_util::GetNSString(
+          IDS_IOS_SAFETY_CHECK_DESCRIPTION_UPDATE_CHROME);
+    case version_info::Channel::BETA:
+      return l10n_util::GetNSString(
+          IDS_IOS_SETTINGS_SAFETY_CHECK_UPDATES_CHANNEL_BETA_DESC);
+    case version_info::Channel::CANARY:
+      return l10n_util::GetNSString(
+          IDS_IOS_SETTINGS_SAFETY_CHECK_UPDATES_CHANNEL_CANARY_DESC);
+    default:
+      return l10n_util::GetNSString(
+          IDS_IOS_SAFETY_CHECK_DESCRIPTION_UPDATE_CHROME);
+  }
+}
+
+// Returns the compact description text for the Update Chrome item, considering
+// the current channel.
+- (NSString*)updateChromeItemCompactDescriptionText {
+  switch (::GetChannel()) {
+    case version_info::Channel::STABLE:
+    case version_info::Channel::DEV:
+      return l10n_util::GetNSString(
+          IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_UPDATE_CHROME);
+    case version_info::Channel::BETA:
+      return l10n_util::GetNSString(
+          IDS_IOS_SETTINGS_SAFETY_CHECK_UPDATES_CHANNEL_BETA_DESC);
+    case version_info::Channel::CANARY:
+      return l10n_util::GetNSString(
+          IDS_IOS_SETTINGS_SAFETY_CHECK_UPDATES_CHANNEL_CANARY_DESC);
+    default:
+      return l10n_util::GetNSString(
+          IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_UPDATE_CHROME);
+  }
+}
+
+// Returns the detailed description text for the Password item, based on the
+// number of compromised, reused, and weak passwords.
+- (NSString*)passwordItemDescriptionText {
+  if (_state.compromisedPasswordsCount > 1) {
+    return l10n_util::GetNSStringF(
+        IDS_IOS_SAFETY_CHECK_DESCRIPTION_MULTIPLE_COMPROMISED_PASSWORDS,
+        base::NumberToString16(_state.compromisedPasswordsCount));
+  }
+
+  if (_state.compromisedPasswordsCount == 1) {
+    return l10n_util::GetNSString(
+        IDS_IOS_SAFETY_CHECK_DESCRIPTION_COMPROMISED_PASSWORD);
+  }
+
+  if (_state.reusedPasswordsCount > 1) {
+    return l10n_util::GetNSStringF(
+        IDS_IOS_SAFETY_CHECK_DESCRIPTION_MULTIPLE_REUSED_PASSWORDS,
+        base::NumberToString16(_state.reusedPasswordsCount));
+  }
+
+  if (_state.reusedPasswordsCount == 1) {
+    return l10n_util::GetNSString(
+        IDS_IOS_SAFETY_CHECK_DESCRIPTION_REUSED_PASSWORD);
+  }
+
+  if (_state.weakPasswordsCount > 1) {
+    return l10n_util::GetNSStringF(
+        IDS_IOS_SAFETY_CHECK_DESCRIPTION_MULTIPLE_WEAK_PASSWORDS,
+        base::NumberToString16(_state.weakPasswordsCount));
+  }
+
+  if (_state.weakPasswordsCount == 1) {
+    return l10n_util::GetNSString(
+        IDS_IOS_SAFETY_CHECK_DESCRIPTION_WEAK_PASSWORD);
+  }
+
+  return l10n_util::GetNSString(
+      IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_MULTIPLE_PASSWORD_ISSUES);
+}
+
+// Returns the compact description text for the Password item, based on the
+// presence of compromised, reused, or weak passwords.
+- (NSString*)passwordItemCompactDescriptionText {
+  if (_state.compromisedPasswordsCount >= 1) {
+    return l10n_util::GetNSString(
+        IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_COMPROMISED_PASSWORD);
+  }
+
+  if (_state.reusedPasswordsCount >= 1) {
+    return l10n_util::GetNSString(
+        IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_REUSED_PASSWORD);
+  }
+
+  if (_state.weakPasswordsCount >= 1) {
+    return l10n_util::GetNSString(
+        IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_WEAK_PASSWORD);
+  }
+
+  return l10n_util::GetNSString(
+      IDS_IOS_SAFETY_CHECK_COMPACT_DESCRIPTION_MULTIPLE_PASSWORD_ISSUES);
+}
+
+// Returns the symbol name for the given `itemType`.
+- (NSString*)symbolNameForItemType:(SafetyCheckItemType)itemType {
+  switch (itemType) {
+    case SafetyCheckItemType::kUpdateChrome:
+      return kInfoCircleSymbol;
+    case SafetyCheckItemType::kPassword:
+      return kPasswordSymbol;
+    case SafetyCheckItemType::kSafeBrowsing:
+      return kPrivacySymbol;
+    case SafetyCheckItemType::kAllSafe:
+    case SafetyCheckItemType::kRunning:
+    case SafetyCheckItemType::kDefault:
+      return kSafetyCheckSymbol;
+  }
+}
+
+// Returns the accessibility identifier for the given `itemType`.
+- (NSString*)accessibilityIdentifierForItemType:(SafetyCheckItemType)itemType {
+  switch (itemType) {
+    case SafetyCheckItemType::kAllSafe:
+      return safety_check::kAllSafeItemID;
+    case SafetyCheckItemType::kRunning:
+      return safety_check::kRunningItemID;
+    case SafetyCheckItemType::kUpdateChrome:
+      return safety_check::kUpdateChromeItemID;
+    case SafetyCheckItemType::kPassword:
+      return safety_check::kPasswordItemID;
+    case SafetyCheckItemType::kSafeBrowsing:
+      return safety_check::kSafeBrowsingItemID;
+    case SafetyCheckItemType::kDefault:
+      return safety_check::kDefaultItemID;
+  }
 }
 
 @end
diff --git a/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_view_unittest.mm b/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_view_unittest.mm
index 46ca00a..b34e5d20 100644
--- a/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_view_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_view_unittest.mm
@@ -8,9 +8,11 @@
 
 #import "base/test/task_environment.h"
 #import "ios/chrome/browser/safety_check/model/ios_chrome_safety_check_manager_constants.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/icon_detail_view.h"
 #import "ios/chrome/browser/ui/content_suggestions/safety_check/constants.h"
-#import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_item_view.h"
 #import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_state.h"
+#import "ios/chrome/browser/ui/content_suggestions/safety_check/types.h"
+#import "ios/chrome/browser/ui/content_suggestions/safety_check/utils.h"
 #import "testing/platform_test.h"
 
 // Tests the SafetyCheckView and subviews.
@@ -115,7 +117,7 @@
 
   // It should initially display one item, i.e. the hero-cell default layout
   // item.
-  ExpectSubviewCount(1, [SafetyCheckItemView class]);
+  ExpectSubviewCount(1, [IconDetailView class]);
 
   ExpectSubview(safety_check::kSafetyCheckViewID, true);
   ExpectSubview(safety_check::kDefaultItemID, true);
@@ -145,7 +147,7 @@
 
   // It should initially display one item, i.e. the hero-cell default layout
   // item.
-  ExpectSubviewCount(1, [SafetyCheckItemView class]);
+  ExpectSubviewCount(1, [IconDetailView class]);
 
   ExpectSubview(safety_check::kSafetyCheckViewID, true);
   ExpectSubview(safety_check::kRunningItemID, true);
@@ -176,7 +178,7 @@
 
   // It should initially display one item, i.e. the hero-cell default layout
   // item.
-  ExpectSubviewCount(1, [SafetyCheckItemView class]);
+  ExpectSubviewCount(1, [IconDetailView class]);
 
   ExpectSubview(safety_check::kSafetyCheckViewID, true);
   ExpectSubview(safety_check::kPasswordItemID, true);
@@ -206,7 +208,7 @@
 
   // It should initially display one item, i.e. the hero-cell default layout
   // item.
-  ExpectSubviewCount(1, [SafetyCheckItemView class]);
+  ExpectSubviewCount(1, [IconDetailView class]);
 
   ExpectSubview(safety_check::kSafetyCheckViewID, true);
   ExpectSubview(safety_check::kSafeBrowsingItemID, true);
@@ -236,7 +238,7 @@
 
   // It should initially display one item, i.e. the hero-cell default layout
   // item.
-  ExpectSubviewCount(1, [SafetyCheckItemView class]);
+  ExpectSubviewCount(1, [IconDetailView class]);
 
   ExpectSubview(safety_check::kSafetyCheckViewID, true);
   ExpectSubview(safety_check::kUpdateChromeItemID, true);
@@ -267,7 +269,7 @@
   ExpectSubviewCount(1, [SafetyCheckView class]);
 
   // It should initially display two items, i.e. the multi-row layout.
-  ExpectSubviewCount(2, [SafetyCheckItemView class]);
+  ExpectSubviewCount(2, [IconDetailView class]);
 
   ExpectSubview(safety_check::kSafetyCheckViewID, true);
   ExpectSubview(safety_check::kUpdateChromeItemID, true);
@@ -298,7 +300,7 @@
   ExpectSubviewCount(1, [SafetyCheckView class]);
 
   // It should initially display two items, i.e. the multi-row layout.
-  ExpectSubviewCount(2, [SafetyCheckItemView class]);
+  ExpectSubviewCount(2, [IconDetailView class]);
 
   ExpectSubview(safety_check::kSafetyCheckViewID, true);
   ExpectSubview(safety_check::kPasswordItemID, true);
@@ -329,7 +331,7 @@
   ExpectSubviewCount(1, [SafetyCheckView class]);
 
   // It should initially display two items, i.e. the multi-row layout.
-  ExpectSubviewCount(2, [SafetyCheckItemView class]);
+  ExpectSubviewCount(2, [IconDetailView class]);
 
   ExpectSubview(safety_check::kSafetyCheckViewID, true);
   ExpectSubview(safety_check::kUpdateChromeItemID, true);
@@ -340,3 +342,36 @@
   ExpectSubview(safety_check::kDefaultItemID, false);
   ExpectSubview(safety_check::kAllSafeItemID, false);
 }
+
+// Tests correctly generating a string representation of `SafetyCheckItemType`.
+TEST_F(SafetyCheckViewTest, CreatesSafetyCheckItemType) {
+  EXPECT_TRUE([NameForSafetyCheckItemType(SafetyCheckItemType::kAllSafe)
+      isEqualToString:@"SafetyCheckItemType::kAllSafe"]);
+  EXPECT_TRUE([NameForSafetyCheckItemType(SafetyCheckItemType::kRunning)
+      isEqualToString:@"SafetyCheckItemType::kRunning"]);
+  EXPECT_TRUE([NameForSafetyCheckItemType(SafetyCheckItemType::kUpdateChrome)
+      isEqualToString:@"SafetyCheckItemType::kUpdateChrome"]);
+  EXPECT_TRUE([NameForSafetyCheckItemType(SafetyCheckItemType::kPassword)
+      isEqualToString:@"SafetyCheckItemType::kPassword"]);
+  EXPECT_TRUE([NameForSafetyCheckItemType(SafetyCheckItemType::kSafeBrowsing)
+      isEqualToString:@"SafetyCheckItemType::kSafeBrowsing"]);
+  EXPECT_TRUE([NameForSafetyCheckItemType(SafetyCheckItemType::kDefault)
+      isEqualToString:@"SafetyCheckItemType::kDefault"]);
+}
+
+// Tests correctly finding the corresponding `SafetyCheckItemType`
+// given its string representation.
+TEST_F(SafetyCheckViewTest, FindsSafetyCheckItemTypeFromName) {
+  EXPECT_EQ(SafetyCheckItemTypeForName(@"SafetyCheckItemType::kAllSafe"),
+            SafetyCheckItemType::kAllSafe);
+  EXPECT_EQ(SafetyCheckItemTypeForName(@"SafetyCheckItemType::kRunning"),
+            SafetyCheckItemType::kRunning);
+  EXPECT_EQ(SafetyCheckItemTypeForName(@"SafetyCheckItemType::kUpdateChrome"),
+            SafetyCheckItemType::kUpdateChrome);
+  EXPECT_EQ(SafetyCheckItemTypeForName(@"SafetyCheckItemType::kPassword"),
+            SafetyCheckItemType::kPassword);
+  EXPECT_EQ(SafetyCheckItemTypeForName(@"SafetyCheckItemType::kSafeBrowsing"),
+            SafetyCheckItemType::kSafeBrowsing);
+  EXPECT_EQ(SafetyCheckItemTypeForName(@"SafetyCheckItemType::kDefault"),
+            SafetyCheckItemType::kDefault);
+}
diff --git a/ios/chrome/browser/ui/content_suggestions/safety_check/utils.h b/ios/chrome/browser/ui/content_suggestions/safety_check/utils.h
index 7a4240d..8b6615c 100644
--- a/ios/chrome/browser/ui/content_suggestions/safety_check/utils.h
+++ b/ios/chrome/browser/ui/content_suggestions/safety_check/utils.h
@@ -20,6 +20,7 @@
 struct InsecurePasswordCounts;
 }  // namespace password_manager
 class GURL;
+enum class SafetyCheckItemType;
 @class SafetyCheckState;
 
 // Fires the proper UI command to navigate users to `chrome_upgrade_url` if the
@@ -56,4 +57,11 @@
 NSString* FormatElapsedTimeSinceLastSafetyCheck(
     std::optional<base::Time> last_run_time);
 
+// Returns the corresponding human-readable name (`NSString*`) for a given
+// `item_type`.
+NSString* NameForSafetyCheckItemType(SafetyCheckItemType item_type);
+
+// Returns the `SafetyCheckItemType` given a human-readable name (`NSString*`).
+SafetyCheckItemType SafetyCheckItemTypeForName(NSString* name);
+
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_SAFETY_CHECK_UTILS_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/safety_check/utils.mm b/ios/chrome/browser/ui/content_suggestions/safety_check/utils.mm
index 8a28dd8..7192129f 100644
--- a/ios/chrome/browser/ui/content_suggestions/safety_check/utils.mm
+++ b/ios/chrome/browser/ui/content_suggestions/safety_check/utils.mm
@@ -4,6 +4,7 @@
 
 #import "ios/chrome/browser/ui/content_suggestions/safety_check/utils.h"
 
+#import "base/check.h"
 #import "base/metrics/user_metrics.h"
 #import "components/password_manager/core/browser/ui/credential_ui_entry.h"
 #import "components/password_manager/core/browser/ui/password_check_referrer.h"
@@ -14,6 +15,7 @@
 #import "ios/chrome/browser/shared/public/commands/settings_commands.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_state.h"
+#import "ios/chrome/browser/ui/content_suggestions/safety_check/types.h"
 #import "ios/chrome/browser/ui/content_suggestions/safety_check/utils.h"
 #import "ios/chrome/common/channel_info.h"
 #import "ios/chrome/grit/ios_strings.h"
@@ -188,3 +190,48 @@
   return l10n_util::GetNSStringF(IDS_IOS_SAFETY_CHECK_LAST_COMPLETED_CHECK,
                                  timestamp);
 }
+
+NSString* NameForSafetyCheckItemType(SafetyCheckItemType item_type) {
+  switch (item_type) {
+    case SafetyCheckItemType::kAllSafe:
+      return @"SafetyCheckItemType::kAllSafe";
+    case SafetyCheckItemType::kRunning:
+      return @"SafetyCheckItemType::kRunning";
+    case SafetyCheckItemType::kUpdateChrome:
+      return @"SafetyCheckItemType::kUpdateChrome";
+    case SafetyCheckItemType::kPassword:
+      return @"SafetyCheckItemType::kPassword";
+    case SafetyCheckItemType::kSafeBrowsing:
+      return @"SafetyCheckItemType::kSafeBrowsing";
+    case SafetyCheckItemType::kDefault:
+      return @"SafetyCheckItemType::kDefault";
+  }
+}
+
+SafetyCheckItemType SafetyCheckItemTypeForName(NSString* name) {
+  if ([name isEqualToString:@"SafetyCheckItemType::kAllSafe"]) {
+    return SafetyCheckItemType::kAllSafe;
+  }
+
+  if ([name isEqualToString:@"SafetyCheckItemType::kRunning"]) {
+    return SafetyCheckItemType::kRunning;
+  }
+
+  if ([name isEqualToString:@"SafetyCheckItemType::kUpdateChrome"]) {
+    return SafetyCheckItemType::kUpdateChrome;
+  }
+
+  if ([name isEqualToString:@"SafetyCheckItemType::kPassword"]) {
+    return SafetyCheckItemType::kPassword;
+  }
+
+  if ([name isEqualToString:@"SafetyCheckItemType::kSafeBrowsing"]) {
+    return SafetyCheckItemType::kSafeBrowsing;
+  }
+
+  if (![name isEqualToString:@"SafetyCheckItemType::kDefault"]) {
+    NOTREACHED();
+  }
+
+  return SafetyCheckItemType::kDefault;
+}
diff --git a/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.mm b/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.mm
index 8053a57..aa241da 100644
--- a/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.mm
@@ -125,12 +125,12 @@
 }
 
 int ChromeOmniboxClientIOS::GetHttpsPortForTesting() const {
-  return HttpsUpgradeServiceFactory::GetForBrowserState(browser_state_)
+  return HttpsUpgradeServiceFactory::GetForProfile(browser_state_)
       ->GetHttpsPortForTesting();
 }
 
 bool ChromeOmniboxClientIOS::IsUsingFakeHttpsForHttpsUpgradeTesting() const {
-  return HttpsUpgradeServiceFactory::GetForBrowserState(browser_state_)
+  return HttpsUpgradeServiceFactory::GetForProfile(browser_state_)
       ->IsUsingFakeHttpsForTesting();
 }
 
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
index 39f0cd5..f29a494d5 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
@@ -135,22 +135,6 @@
 
 - (void)viewDidLoad {
   [super viewDidLoad];
-#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_16_0
-  // Add Paste and Go option to the editing menu
-  RegisterEditMenuItem([[UIMenuItem alloc]
-      initWithTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_COPIED_IMAGE)
-             action:@selector(searchCopiedImage:)]);
-  RegisterEditMenuItem([[UIMenuItem alloc]
-      initWithTitle:l10n_util::GetNSString(
-                        IDS_IOS_SEARCH_COPIED_IMAGE_WITH_LENS)
-             action:@selector(lensCopiedImage:)]);
-  RegisterEditMenuItem([[UIMenuItem alloc]
-      initWithTitle:l10n_util::GetNSString(IDS_IOS_VISIT_COPIED_LINK)
-             action:@selector(visitCopiedLink:)]);
-  RegisterEditMenuItem([[UIMenuItem alloc]
-      initWithTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_COPIED_TEXT)
-             action:@selector(searchCopiedText:)]);
-#endif
 
   self.textField.placeholder = [self placeholderText];
 
@@ -234,20 +218,6 @@
   if (_isTextfieldEditing == owns) {
     return;
   }
-#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_16_0
-  if (owns) {
-    [[NSNotificationCenter defaultCenter]
-        addObserver:self
-           selector:@selector(menuControllerWillShow:)
-               name:UIMenuControllerWillShowMenuNotification
-             object:nil];
-  } else {
-    [[NSNotificationCenter defaultCenter]
-        removeObserver:self
-                  name:UIMenuControllerWillShowMenuNotification
-                object:nil];
-  }
-#endif
   _isTextfieldEditing = owns;
 }
 
@@ -641,28 +611,6 @@
       }));
 }
 
-#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_16_0
-- (void)menuControllerWillShow:(NSNotification*)notification {
-  if (self.showingEditMenu || !self.isTextfieldEditing ||
-      !self.textField.window.isKeyWindow) {
-    return;
-  }
-
-  self.showingEditMenu = YES;
-
-  // Cancel original menu opening.
-  UIMenuController* menuController = [UIMenuController sharedMenuController];
-  [menuController hideMenu];
-
-  // Reset where it should open below text field and reopen it.
-  menuController.arrowDirection = UIMenuControllerArrowUp;
-
-  [menuController showMenuFromView:self.textField rect:self.textField.frame];
-
-  self.showingEditMenu = NO;
-}
-#endif
-
 - (void)pasteboardDidChange:(NSNotification*)notification {
   [self updateCachedClipboardState];
 }
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.h b/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.h
index cfb4543..4ff1ed38 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.h
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.h
@@ -26,6 +26,12 @@
                         (BrowsingDataCounterWrapper::UpdateUICallback)
                             updateUiCallback;
 
+- (std::unique_ptr<BrowsingDataCounterWrapper>)
+    createCounterWrapperWithPrefName:(std::string_view)prefName
+                           beginTime:(base::Time)beginTime
+                    updateUiCallback:
+                        (BrowsingDataCounterWrapper::UpdateUICallback)
+                            updateUiCallback;
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_SETTINGS_CLEAR_BROWSING_DATA_BROWSING_DATA_COUNTER_WRAPPER_PRODUCER_H_
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.mm
index fcd3d88..c734cc2 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.mm
@@ -30,12 +30,28 @@
   }
 
   PrefService* prefService = browserState->GetPrefs();
-  if (!prefService) {
-    return nullptr;
-  }
+  CHECK(prefService);
 
   return BrowsingDataCounterWrapper::CreateCounterWrapper(
       prefName, browserState, prefService, updateUiCallback);
 }
 
+- (std::unique_ptr<BrowsingDataCounterWrapper>)
+    createCounterWrapperWithPrefName:(std::string_view)prefName
+                           beginTime:(base::Time)beginTime
+                    updateUiCallback:
+                        (BrowsingDataCounterWrapper::UpdateUICallback)
+                            updateUiCallback {
+  ChromeBrowserState* browserState = _browserState.get();
+  if (!browserState) {
+    return nullptr;
+  }
+
+  PrefService* prefService = browserState->GetPrefs();
+  CHECK(prefService);
+
+  return BrowsingDataCounterWrapper::CreateCounterWrapper(
+      prefName, browserState, prefService, beginTime, updateUiCallback);
+}
+
 @end
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/fake_browsing_data_counter_wrapper_producer.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/fake_browsing_data_counter_wrapper_producer.mm
index d4e0caf..6d1ac1b 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/fake_browsing_data_counter_wrapper_producer.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/fake_browsing_data_counter_wrapper_producer.mm
@@ -22,6 +22,16 @@
   return nullptr;
 }
 
+- (std::unique_ptr<BrowsingDataCounterWrapper>)
+    createCounterWrapperWithPrefName:(std::string_view)prefName
+                           beginTime:(base::Time)beginTime
+                    updateUiCallback:
+                        (BrowsingDataCounterWrapper::UpdateUICallback)
+                            updateUiCallback {
+  _prefsCallback.emplace(std::string(prefName), std::move(updateUiCallback));
+  return nullptr;
+}
+
 - (void)triggerUpdateUICallbackForResult:
     (const browsing_data::BrowsingDataCounter::Result&)result {
   auto callback =
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_coordinator.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_coordinator.mm
index 7451f9f..25ac3e90 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_coordinator.mm
@@ -106,7 +106,8 @@
 }
 
 - (void)stop {
-  [self animateDismissalWithCompletion:nil];
+  [_viewController.presentingViewController dismissViewControllerAnimated:YES
+                                                               completion:nil];
   [self disconnect];
 }
 
@@ -199,6 +200,7 @@
         BrowsingDataRemover::WebStatesReloadPolicy::kForceReload;
   }
 
+  UIWindow* window = _viewController.view.window;
   __weak QuickDeleteCoordinator* weakSelf = self;
   ProceduralBlock dismissCompletionBlock = ^() {
     [weakSelf animateTabsClosureWithBeginTime:beginTime
@@ -207,7 +209,8 @@
                                        groups:tabGroupsWithTabsToClose
                               allInactiveTabs:allInactiveTabsWillClose
                           browsingDataRemover:browsingDataRemover
-                    browsingDataRemoverParams:params];
+                    browsingDataRemoverParams:params
+                                       window:window];
   };
 
   id<ApplicationCommands> applicationCommandsHandler = HandlerForProtocol(
@@ -215,18 +218,20 @@
   [applicationCommandsHandler
       displayTabGridInMode:TabGridOpeningMode::kRegular];
 
+  id<QuickDeleteCommands> quickDeleteHandler = HandlerForProtocol(
+      self.browser->GetCommandDispatcher(), QuickDeleteCommands);
   // Dismissal of the bottom sheet needs to be dispatched asyncronously so the
   // animation to the tab grid and the dismissal animation are not in conflict,
   // and as such avoiding jittering.
   base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          [](__typeof(self) strongSelf,
-             ProceduralBlock dismissCompletionBlock) {
-            [strongSelf animateDismissalWithCompletion:dismissCompletionBlock];
-            [strongSelf disconnect];
-          },
-          weakSelf, dismissCompletionBlock));
+      FROM_HERE, base::BindOnce(
+                     [](id<QuickDeleteCommands> quickDeleteHandler,
+                        ProceduralBlock dismissCompletionBlock) {
+                       [quickDeleteHandler
+                           stopQuickDeleteForAnimationWithCompletion:
+                               dismissCompletionBlock];
+                     },
+                     quickDeleteHandler, dismissCompletionBlock));
 }
 
 - (void)blockOtherWindows {
@@ -262,19 +267,12 @@
 
 #pragma mark - Private
 
-// Dismisses the `_viewController` with animation. `completionBlock` is run when
-// the dismissal has completed.
-- (void)animateDismissalWithCompletion:(ProceduralBlock)completionBlock {
-  [_viewController.presentingViewController
-      dismissViewControllerAnimated:YES
-                         completion:completionBlock];
-}
-
 // Triggers the tabs closure animation on the tab grid for the WebStates in
 // `tabsToClose`, for the groups in `groupsWithTabsToClose`, and if
 // `animateAllInactiveTabs` is true, then for the inactive tabs banner. It also
 // closes all WebStates with a last navigation between [`beginTime`, `endTime`[
-// in all browsers through `browsingDataRemover` after the animation has run.
+// in all browsers through `browsingDataRemover` with `params` after the
+// animation has run. Finally, it re-enables user action for `window`.
 - (void)
     animateTabsClosureWithBeginTime:(base::Time)beginTime
                             endTime:(base::Time)endTime
@@ -284,7 +282,8 @@
                                         tabGroupsWithTabsToClose
                     allInactiveTabs:(BOOL)animateAllInactiveTabs
                 browsingDataRemover:(BrowsingDataRemover*)browsingDataRemover
-          browsingDataRemoverParams:(BrowsingDataRemover::RemovalParams)params {
+          browsingDataRemoverParams:(BrowsingDataRemover::RemovalParams)params
+                             window:(UIWindow*)window {
   base::OnceClosure onRemoverCompletion = base::BindOnce(
       [](UIWindow* window, std::unique_ptr<ScopedUIBlocker> uiBlocker) {
         uiBlocker.reset();
@@ -302,25 +301,21 @@
         // rearrange.
         TriggerHapticFeedbackForNotification(UINotificationFeedbackTypeSuccess);
       },
-      self.baseViewController.view.window, std::move(_windowUIBlocker));
+      window, std::move(_windowUIBlocker));
 
   base::OnceClosure onAnimationCompletion = base::BindOnce(
       &BrowsingDataRemover::RemoveInRange, browsingDataRemover->AsWeakPtr(),
       beginTime, endTime, BrowsingDataRemoveMask::CLOSE_TABS,
       std::move(onRemoverCompletion), params);
 
-  id<TabsAnimationCommands> handler = HandlerForProtocol(
+  id<TabsAnimationCommands> tabsAnimationHandler = HandlerForProtocol(
       self.browser->GetCommandDispatcher(), TabsAnimationCommands);
-
-  [handler animateTabsClosureForTabs:activeTabsToClose
-                              groups:tabGroupsWithTabsToClose
-                     allInactiveTabs:animateAllInactiveTabs
-                   completionHandler:base::CallbackToBlock(
-                                         std::move(onAnimationCompletion))];
-
-  // Shutdown this coordinator. We can do this right away since all the
-  // callbacks don't depend on this coordinator being alive.
-  [self dismissQuickDelete];
+  [tabsAnimationHandler
+      animateTabsClosureForTabs:activeTabsToClose
+                         groups:tabGroupsWithTabsToClose
+                allInactiveTabs:animateAllInactiveTabs
+              completionHandler:base::CallbackToBlock(
+                                    std::move(onAnimationCompletion))];
 }
 
 // Disconnects all instances.
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_egtest.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_egtest.mm
index 11c0768..eb59c6e 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_egtest.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_egtest.mm
@@ -510,8 +510,8 @@
       assertWithMatcher:grey_enabled()];
 }
 
-// Tests the selection time range for the browsing data deletion: the time range
-// selection is shown with the pref value and a new selection updates the pref.
+// Tests that the time range first shows the pref value but only updates the
+// pref when the user goes through with the deletion.
 - (void)testTimeRangeForDeletionSelection {
   // Set pref to the last hour.
   [ChromeEarlGrey
@@ -568,7 +568,17 @@
               IDS_IOS_CLEAR_BROWSING_DATA_TIME_RANGE_OPTION_LAST_15_MINUTES))]
       assertWithMatcher:grey_sufficientlyVisible()];
 
-  // Confirm that the pref was saved with the new value of last 15 minutes.
+  // Confirm that the pref has not been changed.
+  GREYAssertEqual(
+      [ChromeEarlGrey userIntegerPref:browsing_data::prefs::kDeleteTimePeriod],
+      static_cast<int>(browsing_data::TimePeriod::LAST_HOUR),
+      @"Incorrect local pref value.");
+
+  // Tap the browsing data button.
+  [ChromeEarlGreyUI tapClearBrowsingDataMenuButton:ClearBrowsingDataButton()];
+
+  // Confirm that only after the user has gone through with the deletion, the
+  // pref has been saved with the new value of last 15 minutes.
   GREYAssertEqual(
       [ChromeEarlGrey userIntegerPref:browsing_data::prefs::kDeleteTimePeriod],
       static_cast<int>(browsing_data::TimePeriod::LAST_15_MINUTES),
@@ -795,10 +805,17 @@
                 @"Privacy.DeleteBrowsingData.Action histogram was not logged.");
 }
 
-// Tests that when Quick Delete is opened from Privacy Settings and tabs are
-// selected as a data type, that the privacy settings are still visible after
-// the deletion.
-- (void)testTabsForDeletionFromPrivacySettings {
+// Tests that when Quick Delete is opened from Privacy Settings, from an iPhone
+// and tabs are selected as a data type, that the privacy settings are still
+// visible after the deletion. This test is only for iPhones. on iPads the
+// animation is still run.
+- (void)testTabsForDeletionFromPrivacySettingsForIphones {
+  if ([ChromeEarlGrey isIPadIdiom]) {
+    EARL_GREY_TEST_SKIPPED(
+        @"Skipped for iPads. In iPads, the tabs clsoure animation is ran even "
+        @"if Quick Delete is triggered on top of privacy settings.");
+  }
+
   // Set pref to close tabs.
   [ChromeEarlGrey setBoolValue:true
                    forUserPref:browsing_data::prefs::kCloseTabs];
@@ -833,6 +850,50 @@
       assertWithMatcher:grey_notNil()];
 }
 
+// Tests that when Quick Delete is opened from Privacy Settings, from an iPad
+// and tabs are selected as a data type, that the privacy settings are not
+// visible after the deletion. This test is only for iPads. on iPhones the
+// animation is not run.
+- (void)testTabsForDeletionFromPrivacySettingsForiPads {
+  if (![ChromeEarlGrey isIPadIdiom]) {
+    EARL_GREY_TEST_SKIPPED(
+        @"Skipped for iPhones. In iPhones, the tabs clsoure animation is not "
+        @"run if Quick Delete is triggered on top of privacy settings.");
+  }
+
+  // Set pref to close tabs.
+  [ChromeEarlGrey setBoolValue:true
+                   forUserPref:browsing_data::prefs::kCloseTabs];
+
+  // Load page in tab.
+  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
+  [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo")];
+  [ChromeEarlGrey waitForWebStateContainingText:"Echo"];
+
+  [self openQuickDeleteFromPrivacySettings];
+
+  // Check that Quick Delete is presented.
+  [[EarlGrey selectElementWithMatcher:ClearBrowsingDataView()]
+      assertWithMatcher:grey_notNil()];
+
+  // Tap the browsing data button.
+  [ChromeEarlGreyUI tapClearBrowsingDataMenuButton:ClearBrowsingDataButton()];
+
+  // Check that the tab has been closed.
+  [ChromeEarlGrey waitForWebStateNotContainingText:"Echo"];
+  GREYAssertTrue([ChromeEarlGrey mainTabCount] == 0, @"Tabs were not closed.");
+
+  // Check that Quick Delete is not opened.
+  [ChromeEarlGrey
+      waitForUIElementToDisappearWithMatcher:ClearBrowsingDataView()];
+
+  // Check that the privacy settings are not opened, since the animation was
+  // run.
+  [ChromeEarlGrey
+      waitForUIElementToDisappearWithMatcher:
+          grey_text(l10n_util::GetNSString(IDS_IOS_SETTINGS_PRIVACY_TITLE))];
+}
+
 // Tests that inactive tabs are shown as a possible type to be deleted on the
 // browsing data row when tabs are selected as a data type for deletion. It also
 // tests that the inactive tabs get closed when the deletion of tabs is
@@ -1068,6 +1129,9 @@
               IDS_IOS_CLEAR_BROWSING_DATA_TIME_RANGE_OPTION_LAST_15_MINUTES))]
       assertWithMatcher:grey_sufficientlyVisible()];
 
+  // Tap the browsing data button so the time range pref is saved.
+  [ChromeEarlGreyUI tapClearBrowsingDataMenuButton:ClearBrowsingDataButton()];
+
   // Focus on the second window.
   [EarlGrey setRootMatcherForSubsequentInteractions:chrome_test_util::
                                                         WindowWithNumber(1)];
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_mediator.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_mediator.mm
index a70ea0bf..2c04fc8c 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_mediator.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_mediator.mm
@@ -30,11 +30,53 @@
 namespace {
 
 using browsing_data::DeleteBrowsingDataDialogAction;
+using browsing_data::kDeleteBrowsingDataDialogHistogram;
+using browsing_data::TimePeriod;
 
 // Delay to observe when triggering further actions after browsing data removal
 // has completed so the progress UI state is not flashed.
 constexpr base::TimeDelta kBrowsingDataRemoveCompletionDelay = base::Seconds(1);
 
+void RecordTimePeriodPrefChange(TimePeriod period) {
+  switch (period) {
+    case TimePeriod::LAST_15_MINUTES:
+      base::UmaHistogramEnumeration(
+          kDeleteBrowsingDataDialogHistogram,
+          DeleteBrowsingDataDialogAction::kLast15MinutesSelected);
+      break;
+    case TimePeriod::LAST_HOUR:
+      base::UmaHistogramEnumeration(
+          kDeleteBrowsingDataDialogHistogram,
+          DeleteBrowsingDataDialogAction::kLastHourSelected);
+      break;
+    case TimePeriod::LAST_DAY:
+      base::UmaHistogramEnumeration(
+          kDeleteBrowsingDataDialogHistogram,
+          DeleteBrowsingDataDialogAction::kLastDaySelected);
+      break;
+    case TimePeriod::LAST_WEEK:
+      base::UmaHistogramEnumeration(
+          kDeleteBrowsingDataDialogHistogram,
+          DeleteBrowsingDataDialogAction::kLastWeekSelected);
+      break;
+    case TimePeriod::FOUR_WEEKS:
+      base::UmaHistogramEnumeration(
+          kDeleteBrowsingDataDialogHistogram,
+          DeleteBrowsingDataDialogAction::kLastFourWeeksSelected);
+      break;
+    case TimePeriod::ALL_TIME:
+      base::UmaHistogramEnumeration(
+          kDeleteBrowsingDataDialogHistogram,
+          DeleteBrowsingDataDialogAction::kAllTimeSelected);
+      break;
+    case TimePeriod::OLDER_THAN_30_DAYS:
+      base::UmaHistogramEnumeration(
+          kDeleteBrowsingDataDialogHistogram,
+          DeleteBrowsingDataDialogAction::kOlderThan30DaysSelected);
+      break;
+  }
+}
+
 }  // namespace
 
 @interface QuickDeleteMediator () <IdentityManagerObserverBridgeDelegate,
@@ -47,6 +89,10 @@
   raw_ptr<BrowsingDataRemover> _browsingDataRemover;
   raw_ptr<DiscoverFeedService> _discoverFeedService;
 
+  // The currently selected time range in the UI. Only saved into the
+  // `kDeleteTimePeriod` pref when the deletion is triggered.
+  browsing_data::TimePeriod _selectedTimeRange;
+
   // Summaries based on the results returned by `_counters`. If they're nil, it
   // means that the counter for the browsing data type in `_counters` has not
   // yet returned.
@@ -107,6 +153,9 @@
     _prefChangeRegistrar.Init(_prefs);
     _prefObserverBridge.reset(new PrefObserverBridge(self));
 
+    _selectedTimeRange = static_cast<browsing_data::TimePeriod>(
+        _prefs->GetInteger(browsing_data::prefs::kDeleteTimePeriod));
+
     // Start observing preferences.
     [self observePreferences];
 
@@ -120,9 +169,8 @@
     return;
   }
   _consumer = consumer;
-  [_consumer
-      setTimeRange:static_cast<browsing_data::TimePeriod>(_prefs->GetInteger(
-                       browsing_data::prefs::kDeleteTimePeriod))];
+
+  [_consumer setTimeRange:_selectedTimeRange];
 
   BOOL shouldShowFooter =
       _identityManager->HasPrimaryAccount(signin::ConsentLevel::kSignin);
@@ -161,18 +209,11 @@
 #pragma mark - QuickDeleteMutator
 
 - (void)timeRangeSelected:(browsing_data::TimePeriod)timeRange {
-  browsing_data::TimePeriod currentTimePeriod =
-      static_cast<browsing_data::TimePeriod>(
-          _prefs->GetInteger(browsing_data::prefs::kDeleteTimePeriod));
-
-  if (currentTimePeriod == timeRange) {
+  if (_selectedTimeRange == timeRange) {
     return;
   }
 
-  browsing_data::RecordTimePeriodChange(timeRange);
-
-  _prefs->SetInteger(browsing_data::prefs::kDeleteTimePeriod,
-                     static_cast<int>(timeRange));
+  _selectedTimeRange = timeRange;
 }
 
 - (void)triggerDeletion {
@@ -183,6 +224,11 @@
   DUMP_WILL_BE_CHECK(!_deletionTriggered);
   _deletionTriggered = YES;
 
+  // Only update the time range pref on deletion.
+  _prefs->SetInteger(browsing_data::prefs::kDeleteTimePeriod,
+                     static_cast<int>(_selectedTimeRange));
+  RecordTimePeriodPrefChange(_selectedTimeRange);
+
   [_consumer deletionInProgress];
 
   BrowsingDataRemoveMask removeMask = BrowsingDataRemoveMask::REMOVE_NOTHING;
@@ -228,10 +274,10 @@
     removeMask |= BrowsingDataRemoveMask::CLOSE_TABS;
   }
 
-  browsing_data::TimePeriod timePeriod = static_cast<browsing_data::TimePeriod>(
-      _prefs->GetInteger(browsing_data::prefs::kDeleteTimePeriod));
-  base::Time beginTime = browsing_data::CalculateBeginDeleteTime(timePeriod);
-  base::Time endTime = browsing_data::CalculateEndDeleteTime(timePeriod);
+  base::Time beginTime =
+      browsing_data::CalculateBeginDeleteTime(_selectedTimeRange);
+  base::Time endTime =
+      browsing_data::CalculateEndDeleteTime(_selectedTimeRange);
   BrowsingDataRemover::RemovalParams params{
       .show_activity_indicator =
           BrowsingDataRemover::ActivityIndicatorPolicy::kNoIndicator,
@@ -405,6 +451,11 @@
 #pragma mark - PrefObserverDelegate
 
 - (void)onPreferenceChanged:(const std::string&)preferenceName {
+  if (preferenceName == browsing_data::prefs::kDeleteTimePeriod) {
+    _selectedTimeRange = static_cast<browsing_data::TimePeriod>(
+        _prefs->GetInteger(browsing_data::prefs::kDeleteTimePeriod));
+  }
+
   if (preferenceName == browsing_data::prefs::kDeleteTimePeriod ||
       preferenceName == browsing_data::prefs::kDeleteBrowsingHistory ||
       preferenceName == browsing_data::prefs::kCloseTabs ||
@@ -414,9 +465,9 @@
       preferenceName == browsing_data::prefs::kDeleteFormData) {
     [self restartCounters];
     [self updateConsumerSelectionForPref:preferenceName];
-
     return;
   }
+
   DCHECK(false) << "Unxpected clear browsing data item type.";
 }
 
@@ -452,6 +503,8 @@
   __weak __typeof(self) weakSelf = self;
   std::unique_ptr<BrowsingDataCounterWrapper> counter = [_counterWrapperProducer
       createCounterWrapperWithPrefName:prefName
+                             beginTime:browsing_data::CalculateBeginDeleteTime(
+                                           _selectedTimeRange)
                       updateUiCallback:
                           base::BindRepeating(^(
                               const browsing_data::BrowsingDataCounter::Result&
@@ -464,8 +517,9 @@
   }
 }
 
-// Restarts the counters created in `createdCounters`. Restarting the counters
-// results on the browsing data summary being updated in the ViewController.
+// Restarts the counters created in `createdCounters` with `_selectedTimeRange`.
+// Restarting the counters results on the browsing data summary being updated in
+// the ViewController.
 - (void)restartCounters {
   _browsingHistorySummary = nil;
   _tabsSummary = nil;
@@ -480,7 +534,9 @@
   for (std::set<std::unique_ptr<BrowsingDataCounterWrapper>>::iterator it =
            _counters.begin();
        it != _counters.end(); ++it) {
-    (*it)->RestartCounter();
+    // Setting a new begin time, restarts the counter.
+    (*it)->SetBeginTime(
+        browsing_data::CalculateBeginDeleteTime(_selectedTimeRange));
   }
 }
 
@@ -730,10 +786,8 @@
 - (void)updateResultOnConsumer:
     (const browsing_data::BrowsingDataCounter::Result*)result {
   std::string prefName = result->source()->GetPrefName();
-  browsing_data::TimePeriod timeRange = static_cast<browsing_data::TimePeriod>(
-      _prefs->GetInteger(browsing_data::prefs::kDeleteTimePeriod));
   NSString* summary =
-      quick_delete_util::GetCounterTextFromResult(*result, timeRange);
+      quick_delete_util::GetCounterTextFromResult(*result, _selectedTimeRange);
 
   if (prefName == browsing_data::prefs::kDeleteBrowsingHistory) {
     [_consumer setHistorySummary:summary];
@@ -763,8 +817,7 @@
 
 - (void)updateConsumerSelectionForPref:(const std::string&)preferenceName {
   if (preferenceName == browsing_data::prefs::kDeleteTimePeriod) {
-    [_consumer setTimeRange:static_cast<browsing_data::TimePeriod>(
-                                _prefs->GetInteger(preferenceName))];
+    [_consumer setTimeRange:_selectedTimeRange];
     // Maybe update cache summary.
     return;
   }
diff --git a/ios/chrome/browser/ui/settings/password/password_details/BUILD.gn b/ios/chrome/browser/ui/settings/password/password_details/BUILD.gn
index cc9766a5..a395da2 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/password_details/BUILD.gn
@@ -70,8 +70,6 @@
     "credential_details.mm",
     "password_details_consumer.h",
     "password_details_handler.h",
-    "password_details_menu_item.h",
-    "password_details_menu_item.mm",
     "password_details_metrics_utils.h",
     "password_details_metrics_utils.mm",
     "password_details_table_view_constants.h",
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_menu_item.h b/ios/chrome/browser/ui/settings/password/password_details/password_details_menu_item.h
deleted file mode 100644
index e685c0e..0000000
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_menu_item.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_MENU_ITEM_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_MENU_ITEM_H_
-
-#import <UIKit/UIKit.h>
-
-// TODO(crbug.com/40284033): Delete this file when cleaning up deprecation for
-// iOS 15.
-#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_16_0
-// Menu item which holds item type. Possible types `website`, `username` or
-// `password`.
-@interface PasswordDetailsMenuItem : UIMenuItem
-
-@property(nonatomic, assign) NSInteger itemType;
-
-@end
-
-#endif
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_DETAILS_PASSWORD_DETAILS_MENU_ITEM_H_
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_menu_item.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_menu_item.mm
deleted file mode 100644
index 691f513..0000000
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_menu_item.mm
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/settings/password/password_details/password_details_menu_item.h"
-
-#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_16_0
-
-@implementation PasswordDetailsMenuItem
-
-@end
-
-#endif
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller+Testing.h b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller+Testing.h
index d1b3722a..82f57e2 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller+Testing.h
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller+Testing.h
@@ -10,10 +10,6 @@
 // Testing category to expose private methods of
 // PasswordDetailsTableViewController for testing.
 @interface PasswordDetailsTableViewController (Testing)
-#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_16_0
-- (void)copyPasswordDetails:(id)sender;
-#endif
-
 - (void)copyPasswordDetailsHelper:(NSInteger)itemType;
 @end
 
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
index 52bc0746..2f00acc 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
@@ -35,7 +35,6 @@
 #import "ios/chrome/browser/ui/settings/password/password_details/credential_details.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_consumer.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_handler.h"
-#import "ios/chrome/browser/ui/settings/password/password_details/password_details_menu_item.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_metrics_utils.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_constants.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller+Testing.h"
@@ -742,19 +741,6 @@
       configurationWithIdentifier:[NSNumber numberWithInt:itemType]
                       sourcePoint:editMenuLocation];
   [self.interactionMenu presentEditMenuWithConfiguration:configuration];
-#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_16_0
-  else {
-    // TODO(crbug.com/40930648): Replace UIMenuController with
-    // UIEditMenuInteraction in iOS 16+.
-    UIMenuController* menu = [UIMenuController sharedMenuController];
-    if (![menu isMenuVisible]) {
-      menu.menuItems = [self menuItemsForItemType:itemType];
-
-      [menu showMenuFromView:tableView
-                        rect:[tableView rectForRowAtIndexPath:indexPath]];
-    }
-  }
-#endif
 }
 
 - (BOOL)tableView:(UITableView*)tableView
@@ -1537,34 +1523,6 @@
   [self presentViewController:errorInfoPopover animated:YES completion:nil];
 }
 
-#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_16_0
-
-// Returns an array of UIMenuItems to display in a context menu on the site
-// cell.
-- (NSArray*)menuItemsForItemType:(NSInteger)itemType {
-  PasswordDetailsMenuItem* copyOption = [[PasswordDetailsMenuItem alloc]
-      initWithTitle:l10n_util::GetNSString(IDS_IOS_SETTINGS_SITE_COPY_MENU_ITEM)
-             action:@selector(copyPasswordDetails:)];
-  copyOption.itemType = itemType;
-  return @[ copyOption ];
-}
-
-// Copies the password information to system pasteboard and shows a toast of
-// success/failure.
-- (void)copyPasswordDetails:(id)sender {
-  base::RecordAction(base::UserMetricsAction("MobilePasswordDetailsCopy"));
-
-  UIMenuController* menu =
-      base::apple::ObjCCastStrict<UIMenuController>(sender);
-  PasswordDetailsMenuItem* menuItem =
-      base::apple::ObjCCastStrict<PasswordDetailsMenuItem>(
-          menu.menuItems.firstObject);
-
-  [self copyPasswordDetailsHelper:menuItem.itemType];
-}
-
-#endif
-
 // A helper function that copies the password information to system pasteboard
 // and shows a toast of success/failure.
 - (void)copyPasswordDetailsHelper:(NSInteger)itemType {
@@ -1648,21 +1606,6 @@
   [self.handler dismissPasswordDetailsTableViewController];
 }
 
-#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_16_0
-#pragma mark - UIResponder
-
-- (BOOL)canBecomeFirstResponder {
-  return YES;
-}
-
-- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
-  if (action == @selector(copyPasswordDetails:)) {
-    return YES;
-  }
-  return NO;
-}
-#endif
-
 #pragma mark - Metrics
 
 - (void)logPasswordAccessWith:(PasswordAccessReason)reason {
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_coordinator.mm b/ios/chrome/browser/ui/settings/privacy/privacy_coordinator.mm
index f7086ad..2c7323c 100644
--- a/ios/chrome/browser/ui/settings/privacy/privacy_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_coordinator.mm
@@ -31,6 +31,7 @@
 #import "ios/chrome/browser/ui/settings/privacy/privacy_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #import "ios/chrome/common/ui/reauthentication/reauthentication_module.h"
+#import "ui/base/device_form_factor.h"
 
 @interface PrivacyCoordinator () <
     ClearBrowsingDataCoordinatorDelegate,
@@ -140,7 +141,9 @@
   if (IsIosQuickDeleteEnabled()) {
     id<QuickDeleteCommands> quickDeleteHandler = HandlerForProtocol(
         self.browser->GetCommandDispatcher(), QuickDeleteCommands);
-    [quickDeleteHandler showQuickDeleteAndCanPerformTabsClosureAnimation:NO];
+    [quickDeleteHandler
+        showQuickDeleteAndCanPerformTabsClosureAnimation:
+            ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET];
   } else {
     self.clearBrowsingDataCoordinator = [[ClearBrowsingDataCoordinator alloc]
         initWithBaseNavigationController:self.baseNavigationController
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
index 57b1221..ef8c3e3 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
@@ -728,8 +728,7 @@
 }
 
 // Tests that the user interface style is respected after a drag and drop.
-// TODO(crbug.com/368385383): Test flaky on iOS.
-- (void)DISABLED_testTraitCollection {
+- (void)testTraitCollection {
   [ChromeEarlGrey loadURL:_URL1];
   [ChromeEarlGrey waitForWebStateContainingText:kResponse1];
   [ChromeEarlGrey openNewTab];
@@ -756,8 +755,10 @@
       [[GREYElementMatcherBlock alloc] initWithMatchesBlock:match
                                            descriptionBlock:describe];
 
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
-                                          IdentifierForCellAtIndex(0))]
+  [[EarlGrey
+      selectElementWithMatcher:grey_allOf(grey_accessibilityID(
+                                              IdentifierForCellAtIndex(0)),
+                                          grey_sufficientlyVisible(), nil)]
       assertWithMatcher:matcher];
 }
 
diff --git a/ios/chrome/browser/ui/whats_new/data_source/whats_new_data_source.mm b/ios/chrome/browser/ui/whats_new/data_source/whats_new_data_source.mm
index 7a2a83ac..2a6909d 100644
--- a/ios/chrome/browser/ui/whats_new/data_source/whats_new_data_source.mm
+++ b/ios/chrome/browser/ui/whats_new/data_source/whats_new_data_source.mm
@@ -171,7 +171,6 @@
 
 NSArray<NSString*>* loadInstructionsForPasswordInOtherApps(
     NSArray<NSString*>* instructions) {
-#if defined(__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0
   return @[
     l10n_util::GetNSString(
         IDS_IOS_WHATS_NEW_CHROME_TIP_PASSWORDS_IN_OTHER_APPS_STEP_1_IOS16),
@@ -180,9 +179,6 @@
     l10n_util::GetNSString(
         IDS_IOS_WHATS_NEW_CHROME_TIP_PASSWORDS_IN_OTHER_APPS_STEP_2),
   ];
-#else
-  return instructions;
-#endif  // defined (__IPHONE_16_0)
 }
 
 }  // namespace
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 9ad0bec..8f00c16 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 @@
-052289195140d071b395fec18b87e52fd017a0a4
\ No newline at end of file
+ed124858f61bcb9a8f92c20ffff0995572dafd94
\ 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 0a6c083..bb5d98d 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 @@
-fe96bc0ca0e620491f7f5ad053d2c48a6d468210
\ No newline at end of file
+53f910765d191a5407f4c56924d1a155c4ac05c6
\ 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 203fd2d..a13223a5 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 @@
-5905f00f66d6da5fceed2fca992d6320ae64b9d4
\ No newline at end of file
+a0b0ac35ce5296c20c63af459745b6aa70678988
\ 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 d5beeb2..49e6d27 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 @@
-61ebc5b62f6b18f5847ba979e4b9fefe8de65174
\ No newline at end of file
+44a7bd758f49fa01338d58d0edeffa5f38b768ae
\ 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 72bb239..cbf9debd 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 @@
-28e79734c41e1e308be3aa1a1c289b72b1566767
\ No newline at end of file
+4c76bb23298a4089acbaddc4d934e97eaf99e329
\ 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 be7738c5..cc28df0 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 @@
-c707bbe7d0765c210c9bde1f92ddbd7bbc802b65
\ No newline at end of file
+c32f2ae2155e180c5739f4385badd4be4cf71612
\ 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 a016fe2..d706094 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 @@
-19c2b61fd8ee42772f4c2a88e407f199ee9acba4
\ No newline at end of file
+d77ce7c2f8c00944876c3fdbd836506fc6b82a39
\ 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 79d07a9..7a7815a 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 @@
-802baac4e5932355d2cf8052e98414bd1b3f04b6
\ No newline at end of file
+e84e7f3fc6eca24f22ea7c679aef7d9e499e9a49
\ 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 972c15c..bee438f 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 @@
-b104c7cbd0f2496d824593f2ec53ff34a98a4fe3
\ No newline at end of file
+1e79574c11376dbe884c0a06a49e1a9f37c932db
\ 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 5d29b76..cab6f2b 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 @@
-78f431c7eac21a71ac489cd57478c4bca3e73b40
\ No newline at end of file
+f978dd90b7ff88fe68fb82a696e06663e0026899
\ 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 514346ae..bf8561ee 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 @@
-55f18a82a28282d4c1741e435a32e9044b722791
\ No newline at end of file
+5dbee3e955966f4353d3cbeede29328b384e1a3b
\ 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 7d76f89..77332ac 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 @@
-5cd5994e02a2f3e3c65ff091f2f51d12361fb58b
\ No newline at end of file
+51a1cc4774d73436bd8d318ddc759bc8ecb9ec16
\ 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 e4641c8..5837981 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 @@
-323e2914de979844b19a277a1d0593f923a36e00
\ No newline at end of file
+968a9f8cdbcb4f701904e8d166b02a63415b2112
\ 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 79d63f7..a328b98 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 @@
-be4f7f4fddc56ca3daf7b04a6ae9f1bc3f09fb7c
\ No newline at end of file
+3a15c0b097e171fb707aa91d082c682e7bb5ce86
\ 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 d19b66c..18a370f8 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 @@
-b50e846e54b0bc616daddde0e51b8f90f3a6f8b3
\ No newline at end of file
+f48da9c84b5ad3fddafcdccdaefa76f74eb12ba0
\ No newline at end of file
diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm
index 587c16d..22b33bac 100644
--- a/ios/web/navigation/crw_wk_navigation_handler.mm
+++ b/ios/web/navigation/crw_wk_navigation_handler.mm
@@ -255,7 +255,6 @@
   BOOL isMainFrameNavigationAction = [self isMainFrameNavigationAction:action];
   auto decisionHandler = ^(WKNavigationActionPolicy policy) {
     preferences.preferredContentMode = contentMode;
-#if defined(__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0
     if (@available(iOS 16.0, *)) {
       if ((policy == WKNavigationActionPolicyAllow) &&
           isMainFrameNavigationAction) {
@@ -277,7 +276,6 @@
         }
       }
     }
-#endif  // defined (__IPHONE_16_0)
     handler(policy, preferences);
   };
 
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 35cbeef5..8e7a232 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -1286,7 +1286,6 @@
   }
 }
 
-#if defined(__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0
 CrFullscreenState CrFullscreenStateFromWKFullscreenState(
     WKFullscreenState state) API_AVAILABLE(ios(16.0)) {
   switch (state) {
@@ -1303,7 +1302,6 @@
       return CrFullscreenState::kNotInFullScreen;
   }
 }
-#endif  // defined (__IPHONE_16_0)
 
 #pragma mark - Security Helpers
 
@@ -1433,12 +1431,10 @@
     return;
 
   CrFullscreenState fullScreenState = CrFullscreenState::kNotInFullScreen;
-#if defined(__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0
   if (@available(iOS 16.0, *)) {
     fullScreenState =
         CrFullscreenStateFromWKFullscreenState(self.webView.fullscreenState);
   }
-#endif
   CRWWebViewContentView* webViewContentView =
       [[CRWWebViewContentView alloc] initWithWebView:self.webView
                                           scrollView:self.webScrollView
@@ -1614,7 +1610,6 @@
 }
 
 - (void)fullscreenStateDidChange {
-#if defined(__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0
   if (@available(iOS 16.0, *)) {
     CrFullscreenState fullScreenState =
         CrFullscreenStateFromWKFullscreenState(self.webView.fullscreenState);
@@ -1625,7 +1620,6 @@
         fullScreenState == CrFullscreenState::kInFullscreen;
     base::UmaHistogramEnumeration(kFullScreenStateHistogram, fullScreenState);
   }
-#endif  // defined (__IPHONE_16_0)
 }
 
 #pragma mark - CRWWebViewHandlerDelegate
diff --git a/ios/web/web_state/ui/wk_web_view_configuration_provider.mm b/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
index e15fe1d2..e751f4d 100644
--- a/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
+++ b/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
@@ -129,13 +129,11 @@
   // Chrome uses Google-provided Safe Browsing.
   [[configuration_ preferences] setFraudulentWebsiteWarningEnabled:NO];
 
-#if defined(__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0
   if (@available(iOS 16.0, *)) {
     if (GetWebClient()->EnableFullscreenAPI()) {
       [[configuration_ preferences] setElementFullscreenEnabled:YES];
     }
   }
-#endif  // defined(__IPHONE_16_0)
 
   [configuration_ setAllowsInlineMediaPlayback:YES];
   // setJavaScriptCanOpenWindowsAutomatically is required to support popups.
diff --git a/ios/web/web_state/web_view_internal_creation_util.mm b/ios/web/web_state/web_view_internal_creation_util.mm
index 6edb33d..43ac26d 100644
--- a/ios/web/web_state/web_view_internal_creation_util.mm
+++ b/ios/web/web_state/web_view_internal_creation_util.mm
@@ -12,14 +12,6 @@
 #import "ios/web/web_state/crw_web_view.h"
 #import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
 
-// Allow usage of iOS 16.4's `inspectable` property on WKWebView in pre-16.4
-// SDK builds.
-#if !defined(__IPHONE_16_4) || __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_16_4
-@interface WKWebView (Additions)
-@property BOOL inspectable API_AVAILABLE(ios(16.4));
-@end
-#endif
-
 namespace web {
 
 namespace {
diff --git a/ios_internal b/ios_internal
index 55fb27a..b8eee2f 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 55fb27a6b3296b3bb716819231e89fa2e2850dc7
+Subproject commit b8eee2f52e65c9a92408c29c8b514414566f7337
diff --git a/remoting/protocol/webrtc_transport.cc b/remoting/protocol/webrtc_transport.cc
index 7c6cc9a..d809693 100644
--- a/remoting/protocol/webrtc_transport.cc
+++ b/remoting/protocol/webrtc_transport.cc
@@ -120,7 +120,7 @@
 }
 
 // Returns true if the selected candidate-pair indicates a relay connection.
-std::optional<bool> IsConnectionRelayed(
+bool IsConnectionRelayed(
     const cricket::CandidatePair& selected_candidate_pair) {
   const cricket::Candidate& local_candidate =
       selected_candidate_pair.local_candidate();
@@ -970,7 +970,6 @@
   if (!connected_ &&
       new_state == webrtc::PeerConnectionInterface::kIceConnectionConnected) {
     connected_ = true;
-    connection_relayed_.reset();
     close_after_disconnect_timer_.Stop();
     event_handler_->OnWebrtcTransportConnected();
   } else if (connected_ &&
@@ -1031,12 +1030,8 @@
       IsConnectionRelayed(event.selected_candidate_pair);
   if (connection_relayed != connection_relayed_) {
     connection_relayed_ = connection_relayed;
-    if (connection_relayed_.has_value()) {
-      VLOG(0) << "Relay connection: "
-              << (*connection_relayed_ ? "true" : "false");
-    } else {
-      LOG(ERROR) << "Connection type unknown, treating as direct.";
-    }
+    VLOG(0) << "Relay connection: "
+            << (*connection_relayed_ ? "true" : "false");
 
     // The max-bitrate needs to be applied even for direct (non-TURN)
     // connections. Otherwise the video-sender b/w estimate is capped to a low
diff --git a/services/tracing/perfetto/privacy_filtered_fields-inl.h b/services/tracing/perfetto/privacy_filtered_fields-inl.h
index 01ef3fd..59e2ce8 100644
--- a/services/tracing/perfetto/privacy_filtered_fields-inl.h
+++ b/services/tracing/perfetto/privacy_filtered_fields-inl.h
@@ -420,10 +420,10 @@
                                          kLocalSurfaceIdComplexMessages};
 
 // Proto Message: ChromeGraphicsPipeline
-constexpr int kChromeGraphicsPipelineIndices[] = {1, 2, 3, 4, 5, 6, 8, -1};
+constexpr int kChromeGraphicsPipelineIndices[] = {1, 2, 3, 4, 5, 6, 8, 9, -1};
 constexpr MessageInfo const* kChromeGraphicsPipelineComplexMessages[] = {
     nullptr, &kFrameSinkId, nullptr, &kLocalSurfaceId,
-    nullptr, nullptr,       nullptr};
+    nullptr, nullptr,       nullptr, nullptr};
 constexpr MessageInfo kChromeGraphicsPipeline = {
     kChromeGraphicsPipelineIndices, kChromeGraphicsPipelineComplexMessages};
 
diff --git a/testing/buildbot/filters/android.14.webview_instrumentation_test_apk.filter b/testing/buildbot/filters/android.14.webview_instrumentation_test_apk.filter
index 9811d004..9a03d39 100644
--- a/testing/buildbot/filters/android.14.webview_instrumentation_test_apk.filter
+++ b/testing/buildbot/filters/android.14.webview_instrumentation_test_apk.filter
@@ -1,6 +1,2 @@
 # crbug.com/351017155
 -org.chromium.android_webview.test.VariationsSeedLoaderTest.testFinchSeedExpirationAgeFlag
-
-# crbug.com/354846826
--org.chromium.android_webview.test.AwAutofillTest.testNoEventSentToAutofillServiceForFocusedDatalist
--org.chromium.android_webview.test.AwAutofillTest.testCrossFrameCommit
diff --git a/testing/buildbot/filters/android.emulator_15.webview_instrumentation_test_apk.filter b/testing/buildbot/filters/android.emulator_15.webview_instrumentation_test_apk.filter
index 43b030d..9a03d39 100644
--- a/testing/buildbot/filters/android.emulator_15.webview_instrumentation_test_apk.filter
+++ b/testing/buildbot/filters/android.emulator_15.webview_instrumentation_test_apk.filter
@@ -1,19 +1,2 @@
 # crbug.com/351017155
 -org.chromium.android_webview.test.VariationsSeedLoaderTest.testFinchSeedExpirationAgeFlag
-
-# crbug.com/354846826
--org.chromium.android_webview.test.AwAutofillTest.testAutofillTriggersAfterReload
--org.chromium.android_webview.test.AwAutofillTest.testTouchingFormWithAdjustResize
--org.chromium.android_webview.test.AwAutofillTest.testTouchingPasswordFieldTriggerQuery
--org.chromium.android_webview.test.AwAutofillTest.testFocusRemovedAndRestored
--org.chromium.android_webview.test.AwAutofillTest.testTouchingFormWithAdjustPan
-
-# crbug.com/362633222
--org.chromium.android_webview.test.ContextMenuTest.testDismissContextMenuOnBack
--org.chromium.android_webview.test.ContextMenuTest.testCopyLinkURL
--org.chromium.android_webview.test.ContextMenuTest.testCopyLinkText
--org.chromium.android_webview.test.ContextMenuTest.testDismissContextMenuOnClick
--org.chromium.android_webview.test.ContextMenuTest.testOpenInBrowser
-
-# crbug.com/362627263
--org.chromium.android_webview.test.PopupWindowTest.testSingleWindowModeJsInjection
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 7adc15c..8bcd2f8 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1342,6 +1342,9 @@
                     "name": "Enabled",
                     "enable_features": [
                         "AudioSelectionImprovement"
+                    ],
+                    "disable_features": [
+                        "ResetAudioSelectionImprovementPref"
                     ]
                 }
             ]
@@ -3478,6 +3481,8 @@
                     "name": "Enabled",
                     "params": {
                         "prioritize_prerendering": "false",
+                        "prioritize_prerendering_only": "false",
+                        "prioritize_renderer_initiated": "true",
                         "target_urls": "[]"
                     },
                     "enable_features": [
@@ -12104,22 +12109,6 @@
             ]
         }
     ],
-    "IOSSaveToDrive": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20240523",
-                    "enable_features": [
-                        "IOSDownloadNoUIUpdateInBackground",
-                        "IOSSaveToDrive"
-                    ]
-                }
-            ]
-        }
-    ],
     "IOSSaveToPhotosImprovements": [
         {
             "platforms": [
@@ -12984,7 +12973,7 @@
             ]
         }
     ],
-    "KidsProfile": [
+    "KidsProfilePhase1": [
         {
             "platforms": [
                 "linux",
@@ -16199,6 +16188,21 @@
             ]
         }
     ],
+    "PWAIconAndTitleInNativeNotificationsWin": [
+        {
+            "platforms": [
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "EnablePWAIconAndTitleInNativeNotificationsWin",
+                    "enable_features": [
+                        "AppSpecificNotifications"
+                    ]
+                }
+            ]
+        }
+    ],
     "PageAllocatorRetryOnCommitFailure": [
         {
             "platforms": [
@@ -20179,7 +20183,6 @@
     "SafetyHub": [
         {
             "platforms": [
-                "android",
                 "chromeos",
                 "chromeos_lacros",
                 "fuchsia",
@@ -20191,11 +20194,27 @@
                 {
                     "name": "Enabled",
                     "params": {
-                        "background-password-check-interval": "30d",
+                        "background-password-check-interval": "30d"
+                    },
+                    "enable_features": [
+                        "SafetyHub"
+                    ]
+                }
+            ]
+        },
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
                         "probability": "0.5",
                         "trigger_id": "BqU7h7abt0tK1KeaPYj0WkmwAz4h"
                     },
                     "enable_features": [
+                        "SafeHubFollowup",
                         "SafetyHub",
                         "SafetyHubAndroidSurvey"
                     ]
@@ -20231,21 +20250,6 @@
             ]
         }
     ],
-    "SafetyHubFollowup": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SafetyHubFollowup"
-                    ]
-                }
-            ]
-        }
-    ],
     "SavePasswordHashFromProfilePicker": [
         {
             "platforms": [
diff --git a/third_party/angle b/third_party/angle
index 7b0212b..7c81171 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 7b0212b337ff1c6bec9dea0a69f4ca42a19a37d7
+Subproject commit 7c811715508175171303b305bc6891f8be9b0567
diff --git a/third_party/blink/public/mojom/loader/navigation_predictor.mojom b/third_party/blink/public/mojom/loader/navigation_predictor.mojom
index 94a3d90..6af68ca 100644
--- a/third_party/blink/public/mojom/loader/navigation_predictor.mojom
+++ b/third_party/blink/public/mojom/loader/navigation_predictor.mojom
@@ -181,11 +181,11 @@
   // This is called for all anchor elements.
   ReportAnchorElementClick(AnchorElementClick clicked);
 
-  // At each layout, HTMLAnchorElements that have been newly created (since the
+  // At each layout, anchor elements that have been newly created (since the
   // last layout) may be reported. Only elements with an HTTPS href attribute
   // that are 'visible' (they have non-empty bounding box, though they may not
   // be in the viewport) are reported. This reports various metrics about each
-  // element. New HTMLAnchorElements that are not visible after the first layout
+  // element. New anchor elements that are not visible after the first layout
   // since their creation will never be reported. This is not ideal, but allows
   // us to avoid keeping references to elements that may never become visible.
   // `removed_elements` contains the anchor ids of anchor elements that have
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 38090961..c4161002 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -116,8 +116,6 @@
 ]
 
 generated_dictionary_sources_in_modules = [
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_language_detection_result.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_language_detection_result.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_aac_encoder_config.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_aac_encoder_config.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ad_auction_data.cc",
@@ -686,6 +684,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_key_system_track_configuration.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_landmark.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_landmark.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_language_detection_result.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_language_detection_result.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_lock_info.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_lock_info.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_lock_manager_snapshot.cc",
@@ -2388,6 +2388,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_keyboard_layout_map.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_khr_parallel_shader_compile.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_khr_parallel_shader_compile.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_language_detector.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_language_detector.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_language_translator.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_language_translator.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_launch_params.cc",
@@ -2827,8 +2829,6 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_track_default_list.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_track_default_list.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_translation.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_language_detector.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_language_detector.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_translation.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_url.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_url.h",
diff --git a/third_party/blink/renderer/core/css/css_value_list.h b/third_party/blink/renderer/core/css/css_value_list.h
index 58a9d55..8226953 100644
--- a/third_party/blink/renderer/core/css/css_value_list.h
+++ b/third_party/blink/renderer/core/css/css_value_list.h
@@ -35,10 +35,7 @@
 
 class CORE_EXPORT CSSValueList : public CSSValue {
  public:
-  using iterator = HeapVector<Member<const CSSValue>, 4>::iterator;
   using const_iterator = HeapVector<Member<const CSSValue>, 4>::const_iterator;
-  using reverse_iterator =
-      HeapVector<Member<const CSSValue>, 4>::reverse_iterator;
   using const_reverse_iterator =
       HeapVector<Member<const CSSValue>, 4>::const_reverse_iterator;
 
@@ -62,12 +59,8 @@
   CSSValueList(const CSSValueList&) = delete;
   CSSValueList& operator=(const CSSValueList&) = delete;
 
-  iterator begin() { return values_.begin(); }
-  iterator end() { return values_.end(); }
   const_iterator begin() const { return values_.begin(); }
   const_iterator end() const { return values_.end(); }
-  reverse_iterator rbegin() { return values_.rbegin(); }
-  reverse_iterator rend() { return values_.rend(); }
   const_reverse_iterator rbegin() const { return values_.rbegin(); }
   const_reverse_iterator rend() const { return values_.rend(); }
 
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 9006801..b47cf59 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "base/numerics/clamped_math.h"
 #include "third_party/blink/renderer/core/css/basic_shape_functions.h"
 #include "third_party/blink/renderer/core/css/css_anchor_query_enums.h"
@@ -863,18 +858,16 @@
   const auto* value_list = DynamicTo<CSSValueList>(value);
   if (value_list && !value.IsImageSetValue()) {
     // Walk each value and put it into a layer, creating new layers as needed.
-    auto curr_val = value_list->begin();
-    while (curr_child || curr_val != value_list->end()) {
-      if (!curr_child) {
-        curr_child = prev_child->EnsureNext();
-      }
-      CSSToStyleMap::MapFillClip(state, curr_child, *curr_val->Get());
-      UseCountBackgroundClip(document, *curr_val->Get());
-      prev_child = curr_child;
-      curr_child = curr_child->Next();
-      // as per https://w3c.github.io/csswg-drafts/css-backgrounds/#layering
-      if (++curr_val == value_list->end() && curr_child) {
-        curr_val = value_list->begin();
+    // As per https://w3c.github.io/csswg-drafts/css-backgrounds/#layering
+    while (curr_child) {
+      for (auto curr_val : *value_list) {
+        if (!curr_child) {
+          curr_child = prev_child->EnsureNext();
+        }
+        CSSToStyleMap::MapFillClip(state, curr_child, *curr_val);
+        UseCountBackgroundClip(document, *curr_val);
+        prev_child = curr_child;
+        curr_child = curr_child->Next();
       }
     }
   } else {
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 70391cf..78d8163 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -4560,6 +4560,7 @@
     // Base URL change changes any relative visited links.
     // FIXME: There are other URLs in the tree that would need to be
     // re-evaluated on dynamic base URL change. Style should be invalidated too.
+    // TODO(crbug.com/369219144): Should this be using HTMLAnchorElementBase?
     for (HTMLAnchorElement& anchor :
          Traversal<HTMLAnchorElement>::StartsAfter(*this))
       anchor.InvalidateCachedVisitedLinkHash();
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index bc28be5..43157ec 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -4529,9 +4529,7 @@
     return;
   }
 
-  HTMLAnchorElement* link = HasTagName(html_names::kATag)
-                                ? To<HTMLAnchorElement>(this)
-                                : To<HTMLAreaElement>(this);
+  HTMLAnchorElementBase* link = To<HTMLAnchorElementBase>(this);
   auto* document_rules = DocumentSpeculationRules::FromIfExists(GetDocument());
   if (!document_rules) {
     return;
diff --git a/third_party/blink/renderer/core/dom/tree_scope.cc b/third_party/blink/renderer/core/dom/tree_scope.cc
index 47f6a19..c00faa9 100644
--- a/third_party/blink/renderer/core/dom/tree_scope.cc
+++ b/third_party/blink/renderer/core/dom/tree_scope.cc
@@ -459,6 +459,7 @@
     return nullptr;
   if (Element* element = getElementById(AtomicString(name)))
     return element;
+  // TODO(crbug.com/369219144): Should this be Traversal<HTMLAnchorElementBase>?
   for (HTMLAnchorElement& anchor :
        Traversal<HTMLAnchorElement>::StartsAfter(RootNode())) {
     if (RootNode().GetDocument().InQuirksMode()) {
diff --git a/third_party/blink/renderer/core/dom/visited_link_state.cc b/third_party/blink/renderer/core/dom/visited_link_state.cc
index a88d1bd..4da241bb 100644
--- a/third_party/blink/renderer/core/dom/visited_link_state.cc
+++ b/third_party/blink/renderer/core/dom/visited_link_state.cc
@@ -69,6 +69,7 @@
 static inline LinkHash UnpartitionedLinkHashForElement(
     const Element& element,
     const AtomicString& attribute) {
+  // TODO(crbug.com/369219144): Should this be DynamicTo<HTMLAnchorElementBase>?
   if (auto* anchor = DynamicTo<HTMLAnchorElement>(element))
     return anchor->VisitedLinkHash();
   return VisitedLinkHash(
@@ -79,6 +80,7 @@
 static inline LinkHash PartitionedLinkHashForElement(
     const Element& element,
     const AtomicString& attribute) {
+  // TODO(crbug.com/369219144): Should this be DynamicTo<HTMLAnchorElementBase>?
   if (auto* anchor = DynamicTo<HTMLAnchorElement>(element)) {
     return anchor->PartitionedVisitedLinkFingerprint();
   }
@@ -125,6 +127,8 @@
     bool invalidate_visited_link_hashes) {
   for (Node& node : NodeTraversal::StartsAt(root_node)) {
     if (node.IsLink()) {
+      // TODO(crbug.com/369219144): Should this be
+      // DynamicTo<HTMLAnchorElementBase>?
       auto* html_anchor_element = DynamicTo<HTMLAnchorElement>(node);
       if (invalidate_visited_link_hashes && html_anchor_element)
         html_anchor_element->InvalidateCachedVisitedLinkHash();
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader.cc b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
index 9f6cf0f..4ecfa45 100644
--- a/third_party/blink/renderer/core/frame/attribution_src_loader.cc
+++ b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
@@ -460,7 +460,7 @@
 std::optional<Impression> AttributionSrcLoader::RegisterNavigationInternal(
     const KURL& navigation_url,
     Vector<KURL> attribution_src_urls,
-    HTMLAnchorElement* element,
+    HTMLAnchorElementBase* element,
     bool has_transient_user_activation,
     network::mojom::ReferrerPolicy referrer_policy) {
   if (!has_transient_user_activation) {
@@ -497,7 +497,7 @@
 std::optional<Impression> AttributionSrcLoader::RegisterNavigation(
     const KURL& navigation_url,
     const AtomicString& attribution_src,
-    HTMLAnchorElement* element,
+    HTMLAnchorElementBase* element,
     bool has_transient_user_activation,
     network::mojom::ReferrerPolicy referrer_policy) {
   CHECK(!attribution_src.IsNull());
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader.h b/third_party/blink/renderer/core/frame/attribution_src_loader.h
index 17011e2..15c45f3 100644
--- a/third_party/blink/renderer/core/frame/attribution_src_loader.h
+++ b/third_party/blink/renderer/core/frame/attribution_src_loader.h
@@ -26,7 +26,7 @@
 
 namespace blink {
 
-class HTMLAnchorElement;
+class HTMLAnchorElementBase;
 class HTMLElement;
 class KURL;
 class LocalFrame;
@@ -79,7 +79,7 @@
   [[nodiscard]] std::optional<Impression> RegisterNavigation(
       const KURL& navigation_url,
       const AtomicString& attribution_src,
-      HTMLAnchorElement* element,
+      HTMLAnchorElementBase* element,
       bool has_transient_user_activation,
       network::mojom::ReferrerPolicy);
 
@@ -121,10 +121,13 @@
                       std::optional<AttributionSrcToken>,
                       network::mojom::ReferrerPolicy);
 
+  // TODO(crbug.com/369219144): The explainer seems to indicate this should
+  // only work with <a>, but the code has always worked for <area> as well.
+  // Either update the explainer, or update the code to use HTMLAnchorElement*.
   [[nodiscard]] std::optional<Impression> RegisterNavigationInternal(
       const KURL& navigation_url,
       Vector<KURL> attribution_src_urls,
-      HTMLAnchorElement*,
+      HTMLAnchorElementBase*,
       bool has_transient_user_activation,
       network::mojom::ReferrerPolicy);
 
diff --git a/third_party/blink/renderer/core/frame/frame_serializer.cc b/third_party/blink/renderer/core/frame/frame_serializer.cc
index 69012e85..0820098 100644
--- a/third_party/blink/renderer/core/frame/frame_serializer.cc
+++ b/third_party/blink/renderer/core/frame/frame_serializer.cc
@@ -317,6 +317,7 @@
 
     // Do not save ping attribute since anyway the ping will be blocked from
     // MHTML.
+    // TODO(crbug.com/369219144): Should this be IsA<HTMLAnchorElementBase>?
     if (IsA<HTMLAnchorElement>(element) &&
         attribute.LocalName() == html_names::kPingAttr) {
       return EmitChoice::kIgnore;
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index f451428..9622c7d 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -2696,6 +2696,8 @@
   DCHECK(GetFrame());
   if (Node* node = ContextMenuNodeInner()) {
     Element* anchor = node->EnclosingLinkEventParentOrSelf();
+    // TODO(crbug.com/369219144): Should this be
+    // DynamicTo<HTMLAnchorElementBase>?
     if (auto* html_anchor = DynamicTo<HTMLAnchorElement>(anchor))
       html_anchor->SendPings(destination_url);
   }
diff --git a/third_party/blink/renderer/core/html/anchor_element_metrics.cc b/third_party/blink/renderer/core/html/anchor_element_metrics.cc
index 40cb821..8de6324c 100644
--- a/third_party/blink/renderer/core/html/anchor_element_metrics.cc
+++ b/third_party/blink/renderer/core/html/anchor_element_metrics.cc
@@ -34,7 +34,7 @@
 
 // Returns the document of the main frame of the frame tree containing `anchor`.
 // This could be null if `anchor` is in an out-of-process iframe.
-Document* GetTopDocument(const HTMLAnchorElement& anchor) {
+Document* GetTopDocument(const HTMLAnchorElementBase& anchor) {
   LocalFrame* frame = anchor.GetDocument().GetFrame();
   if (!frame) {
     return nullptr;
@@ -49,13 +49,13 @@
 }
 
 // Whether the element is inside an iframe.
-bool IsInIFrame(const HTMLAnchorElement& anchor_element) {
+bool IsInIFrame(const HTMLAnchorElementBase& anchor_element) {
   Frame* frame = anchor_element.GetDocument().GetFrame();
   return frame && !frame->IsMainFrame();
 }
 
 // Whether the anchor element contains an image element.
-bool ContainsImage(const HTMLAnchorElement& anchor_element) {
+bool ContainsImage(const HTMLAnchorElementBase& anchor_element) {
   for (Node* node = FlatTreeTraversal::FirstChild(anchor_element); node;
        node = FlatTreeTraversal::Next(*node, &anchor_element)) {
     if (IsA<HTMLImageElement>(*node))
@@ -65,7 +65,7 @@
 }
 
 // Whether the link target has the same host as the root document.
-bool IsSameHost(const HTMLAnchorElement& anchor_element,
+bool IsSameHost(const HTMLAnchorElementBase& anchor_element,
                 const KURL& anchor_href) {
   Document* top_document = GetTopDocument(anchor_element);
   if (!top_document) {
@@ -131,7 +131,7 @@
 }
 
 // Extract source and target link url, and return IsStringIncrementedByOne().
-bool IsUrlIncrementedByOne(const HTMLAnchorElement& anchor_element,
+bool IsUrlIncrementedByOne(const HTMLAnchorElementBase& anchor_element,
                            const KURL& anchor_href) {
   if (!IsSameHost(anchor_element, anchor_href)) {
     return false;
@@ -156,7 +156,7 @@
   return ToEnclosingRect(layout_object.LocalToAbsoluteRect(UnionRect(rects)));
 }
 
-bool HasTextSibling(const HTMLAnchorElement& anchor_element) {
+bool HasTextSibling(const HTMLAnchorElementBase& anchor_element) {
   for (auto* text = DynamicTo<Text>(anchor_element.previousSibling()); text;
        text = DynamicTo<Text>(text->previousSibling())) {
     if (!text->ContainsOnlyWhitespaceOrEmpty()) {
@@ -180,7 +180,7 @@
 // object. Note that this implementation can lead to collisions if an element is
 // destroyed and a new one is created with the same address. We don't mind this
 // issue as the anchor ID is only used for metric collection.
-uint32_t AnchorElementId(const HTMLAnchorElement& element) {
+uint32_t AnchorElementId(const HTMLAnchorElementBase& element) {
   uint32_t id = WTF::GetHash(&element);
   if (WTF::IsHashTraitsEmptyOrDeletedValue<HashTraits<uint32_t>>(id)) {
     // Anchor IDs are used in HashMaps, so we can't have sentinel values. If the
@@ -192,7 +192,7 @@
 }
 
 mojom::blink::AnchorElementMetricsPtr CreateAnchorElementMetrics(
-    const HTMLAnchorElement& anchor_element) {
+    const HTMLAnchorElementBase& anchor_element) {
   const KURL anchor_href = anchor_element.Href();
   if (!anchor_href.ProtocolIsInHTTPFamily()) {
     return nullptr;
diff --git a/third_party/blink/renderer/core/html/anchor_element_metrics.h b/third_party/blink/renderer/core/html/anchor_element_metrics.h
index c044587..8ace4bc9 100644
--- a/third_party/blink/renderer/core/html/anchor_element_metrics.h
+++ b/third_party/blink/renderer/core/html/anchor_element_metrics.h
@@ -10,16 +10,16 @@
 
 namespace blink {
 
-class HTMLAnchorElement;
+class HTMLAnchorElementBase;
 
 // Exported for testing only.
-CORE_EXPORT uint32_t AnchorElementId(const HTMLAnchorElement& element);
+CORE_EXPORT uint32_t AnchorElementId(const HTMLAnchorElementBase& element);
 
 // Returns null if the given element should not be evaluated.
 // Exported for testing only.
 CORE_EXPORT
 mojom::blink::AnchorElementMetricsPtr CreateAnchorElementMetrics(
-    const HTMLAnchorElement&);
+    const HTMLAnchorElementBase&);
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/core/html/anchor_element_metrics_sender.cc b/third_party/blink/renderer/core/html/anchor_element_metrics_sender.cc
index 4b5f6e59..bf3fe82 100644
--- a/third_party/blink/renderer/core/html/anchor_element_metrics_sender.cc
+++ b/third_party/blink/renderer/core/html/anchor_element_metrics_sender.cc
@@ -86,16 +86,6 @@
   return 0.f;
 }
 
-HTMLAnchorElement* ToHTMLAnchorElement(Node* node) {
-  return IsA<HTMLAreaElement>(node) ? To<HTMLAreaElement>(node)
-                                    : To<HTMLAnchorElement>(node);
-}
-
-const HTMLAnchorElement* ToHTMLAnchorElement(const Node* node) {
-  return IsA<HTMLAreaElement>(node) ? To<HTMLAreaElement>(node)
-                                    : To<HTMLAnchorElement>(node);
-}
-
 }  // namespace
 
 // static
@@ -160,7 +150,7 @@
 }
 
 void AnchorElementMetricsSender::MaybeReportClickedMetricsOnClick(
-    const HTMLAnchorElement& anchor_element) {
+    const HTMLAnchorElementBase& anchor_element) {
   DCHECK(base::FeatureList::IsEnabled(features::kNavigationPredictor));
   Document* top_document = GetSupplementable();
   CHECK(top_document);
@@ -180,7 +170,8 @@
   metrics_host_->ReportAnchorElementClick(std::move(click));
 }
 
-void AnchorElementMetricsSender::AddAnchorElement(HTMLAnchorElement& element) {
+void AnchorElementMetricsSender::AddAnchorElement(
+    HTMLAnchorElementBase& element) {
   DCHECK(base::FeatureList::IsEnabled(features::kNavigationPredictor));
   if (!GetSupplementable()->GetFrame()) {
     return;
@@ -197,7 +188,7 @@
 }
 
 void AnchorElementMetricsSender::RemoveAnchorElement(
-    HTMLAnchorElement& element) {
+    HTMLAnchorElementBase& element) {
   DCHECK(base::FeatureList::IsEnabled(features::kNavigationPredictor));
 
   auto it = anchor_elements_to_report_.find(&element);
@@ -260,7 +251,7 @@
     return;
   }
   for (Element* element : *(document.links())) {
-    HTMLAnchorElement* anchor = ToHTMLAnchorElement(element);
+    HTMLAnchorElementBase* anchor = To<HTMLAnchorElementBase>(element);
     RemoveAnchorElement(*anchor);
   }
 }
@@ -375,7 +366,8 @@
 
   for (const auto& entry : entries) {
     const Element* element = entry->target();
-    const HTMLAnchorElement& anchor_element = *ToHTMLAnchorElement(element);
+    const HTMLAnchorElementBase& anchor_element =
+        *To<HTMLAnchorElementBase>(element);
     if (!entry->isIntersecting()) {
       // The anchor is leaving the viewport.
       anchors_in_viewport_.erase(&anchor_element);
@@ -408,7 +400,7 @@
 }
 
 void AnchorElementMetricsSender::MaybeReportAnchorElementPointerEvent(
-    HTMLAnchorElement& element,
+    HTMLAnchorElementBase& element,
     const PointerEvent& pointer_event) {
   if (!AssociateInterface()) {
     return;
@@ -524,7 +516,7 @@
 }
 
 void AnchorElementMetricsSender::EnqueueLeftViewport(
-    const HTMLAnchorElement& element) {
+    const HTMLAnchorElementBase& element) {
   const auto anchor_id = AnchorElementId(element);
   auto it = anchor_elements_timing_stats_.find(anchor_id);
   CHECK(it != anchor_elements_timing_stats_.end(), base::NotFatalUntil::M130);
@@ -545,7 +537,7 @@
 }
 
 void AnchorElementMetricsSender::EnqueueEnteredViewport(
-    const HTMLAnchorElement& element) {
+    const HTMLAnchorElementBase& element) {
   const auto anchor_id = AnchorElementId(element);
   auto it = anchor_elements_timing_stats_.find(anchor_id);
   CHECK(it != anchor_elements_timing_stats_.end(), base::NotFatalUntil::M130);
@@ -604,7 +596,7 @@
   const float browser_controls_height =
       GetBrowserControlsHeight(*GetSupplementable());
 
-  for (const HTMLAnchorElement* anchor : anchors_in_viewport_) {
+  for (const HTMLAnchorElementBase* anchor : anchors_in_viewport_) {
     LocalFrame* frame = anchor->GetDocument().GetFrame();
     if (!frame) {
       continue;
@@ -662,7 +654,7 @@
   }
 
   for (const auto& member_element : anchor_elements_to_report_) {
-    HTMLAnchorElement& anchor_element = *member_element;
+    HTMLAnchorElementBase& anchor_element = *member_element;
 
     mojom::blink::AnchorElementMetricsPtr anchor_element_metrics =
         CreateAnchorElementMetrics(anchor_element);
@@ -686,8 +678,9 @@
       } else if (auto smallest_observed_anchor_it = observed_anchors_.begin();
                  smallest_observed_anchor_it->percent_area < percent_area) {
         should_observe = true;
-        HTMLAnchorElement* smallest_observed_anchor = ToHTMLAnchorElement(
-            DOMNodeIds::NodeForId(smallest_observed_anchor_it->dom_node_id));
+        HTMLAnchorElementBase* smallest_observed_anchor =
+            To<HTMLAnchorElementBase>(DOMNodeIds::NodeForId(
+                smallest_observed_anchor_it->dom_node_id));
         CHECK(smallest_observed_anchor);
         intersection_observer_->unobserve(smallest_observed_anchor);
         EnqueueLeftViewport(*smallest_observed_anchor);
diff --git a/third_party/blink/renderer/core/html/anchor_element_metrics_sender.h b/third_party/blink/renderer/core/html/anchor_element_metrics_sender.h
index bf61e85..36e1c362 100644
--- a/third_party/blink/renderer/core/html/anchor_element_metrics_sender.h
+++ b/third_party/blink/renderer/core/html/anchor_element_metrics_sender.h
@@ -21,7 +21,7 @@
 namespace blink {
 
 class Document;
-class HTMLAnchorElement;
+class HTMLAnchorElementBase;
 class IntersectionObserver;
 class IntersectionObserverEntry;
 class PointerEvent;
@@ -34,7 +34,7 @@
 // Cross-origin iframes do not use any AnchorElementMetricsSender.
 //
 // The high level approach is:
-// 1) When HTMLAnchorElements are inserted into the DOM,
+// 1) When HTMLAnchorElementBases are inserted into the DOM,
 //    AnchorElementMetricsSender::AddAnchorElement is called and a reference to
 //    the element is stored. The first time this happens, the sender is created,
 //    which registers itself for lifecycle callbacks.
@@ -86,7 +86,7 @@
   // Report the link click to the browser process, so long as the anchor
   // is an HTTP(S) link.
   void MaybeReportClickedMetricsOnClick(
-      const HTMLAnchorElement& anchor_element);
+      const HTMLAnchorElementBase& anchor_element);
 
   // Report the on-hover event and anchor element pointer data to the browser
   // process.
@@ -94,8 +94,8 @@
       AnchorId anchor_id,
       mojom::blink::AnchorElementPointerDataPtr mouse_data);
 
-  void AddAnchorElement(HTMLAnchorElement& element);
-  void RemoveAnchorElement(HTMLAnchorElement& element);
+  void AddAnchorElement(HTMLAnchorElementBase& element);
+  void RemoveAnchorElement(HTMLAnchorElementBase& element);
   void DocumentDetached(Document& document);
 
   void SetTickClockForTesting(const base::TickClock* clock);
@@ -109,7 +109,7 @@
       const HeapVector<Member<IntersectionObserverEntry>>& entries);
 
   // Report the pointer event for the anchor element.
-  void MaybeReportAnchorElementPointerEvent(HTMLAnchorElement& element,
+  void MaybeReportAnchorElementPointerEvent(HTMLAnchorElementBase& element,
                                             const PointerEvent& pointer_event);
 
   // Record and send metrics to the browser process about the position of
@@ -134,11 +134,11 @@
 
   // Creates an AnchorElementEnteredViewportPtr for the given element and
   // enqueue it so that it gets reported after the next layout.
-  void EnqueueEnteredViewport(const HTMLAnchorElement& element);
+  void EnqueueEnteredViewport(const HTMLAnchorElementBase& element);
 
   // Creates an AnchorElementLeftViewportPtr for the given element and
   // enqueue it so that it gets reported after the next layout.
-  void EnqueueLeftViewport(const HTMLAnchorElement& element);
+  void EnqueueLeftViewport(const HTMLAnchorElementBase& element);
 
   // Checks how long it has passed since the last call and decides whether to
   // call or reschedule a future call to UpdateMetrics.
@@ -165,7 +165,7 @@
   // layout, they will be used to populate `metrics_` and
   // `metrics_removed_anchors_`.
   // Use WeakMember to make sure we don't leak memory on long-lived pages.
-  HeapHashSet<WeakMember<HTMLAnchorElement>> anchor_elements_to_report_;
+  HeapHashSet<WeakMember<HTMLAnchorElementBase>> anchor_elements_to_report_;
   WTF::Vector<AnchorId> removed_anchors_to_report_;
 
   // `metrics_` and `metrics_removed_anchors_` buffer metrics updates that are
@@ -201,7 +201,7 @@
   const base::TimeDelta intersection_observer_delay_;
 
   Member<IntersectionObserver> intersection_observer_;
-  HeapHashSet<WeakMember<const HTMLAnchorElement>> anchors_in_viewport_;
+  HeapHashSet<WeakMember<const HTMLAnchorElementBase>> anchors_in_viewport_;
 
   WTF::Vector<mojom::blink::AnchorElementEnteredViewportPtr>
       entered_viewport_messages_;
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.cc b/third_party/blink/renderer/core/html/html_anchor_element.cc
index fc9fb4ab..9c87e13f 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.cc
+++ b/third_party/blink/renderer/core/html/html_anchor_element.cc
@@ -114,8 +114,9 @@
   return should_intervene_download;
 }
 
-void EmitDidAnchorElementReceiveMouseEvent(HTMLAnchorElement& anchor_element,
-                                           Event& event) {
+void EmitDidAnchorElementReceiveMouseEvent(
+    HTMLAnchorElementBase& anchor_element,
+    Event& event) {
   if (!event.IsMouseEvent()) {
     return;
   }
@@ -143,19 +144,16 @@
 
 }  // namespace
 
-HTMLAnchorElement::HTMLAnchorElement(Document& document)
-    : HTMLAnchorElement(html_names::kATag, document) {}
-
-HTMLAnchorElement::HTMLAnchorElement(const QualifiedName& tag_name,
-                                     Document& document)
+HTMLAnchorElementBase::HTMLAnchorElementBase(const QualifiedName& tag_name,
+                                             Document& document)
     : HTMLElement(tag_name, document),
       link_relations_(0),
       cached_visited_link_hash_(0),
       rel_list_(MakeGarbageCollected<RelList>(this)) {}
 
-HTMLAnchorElement::~HTMLAnchorElement() = default;
+HTMLAnchorElementBase::~HTMLAnchorElementBase() = default;
 
-FocusableState HTMLAnchorElement::SupportsFocus(
+FocusableState HTMLAnchorElementBase::SupportsFocus(
     UpdateBehavior update_behavior) const {
   if (IsLink() && !IsEditable(*this)) {
     return FocusableState::kFocusable;
@@ -163,14 +161,14 @@
   return HTMLElement::SupportsFocus(update_behavior);
 }
 
-bool HTMLAnchorElement::ShouldHaveFocusAppearance() const {
+bool HTMLAnchorElementBase::ShouldHaveFocusAppearance() const {
   // TODO(crbug.com/1444450): Can't this be done with focus-visible now?
   return (GetDocument().LastFocusType() != mojom::blink::FocusType::kMouse) ||
          HTMLElement::SupportsFocus(UpdateBehavior::kNoneForFocusManagement) !=
              FocusableState::kNotFocusable;
 }
 
-FocusableState HTMLAnchorElement::IsFocusableState(
+FocusableState HTMLAnchorElementBase::IsFocusableState(
     UpdateBehavior update_behavior) const {
   if (!IsFocusableStyle(update_behavior)) {
     return FocusableState::kNotFocusable;
@@ -181,14 +179,14 @@
   return HTMLElement::IsFocusableState(update_behavior);
 }
 
-bool HTMLAnchorElement::IsKeyboardFocusable(
+bool HTMLAnchorElementBase::IsKeyboardFocusable(
     UpdateBehavior update_behavior) const {
   if (!IsFocusableStyle(update_behavior)) {
     return false;
   }
 
   // Anchor is focusable if the base element is focusable. Note that
-  // because HTMLAnchorElement overrides IsFocusable, we need to check
+  // because HTMLAnchorElementBase overrides IsFocusable, we need to check
   // both SupportsFocus and IsFocusable.
   if (Element::SupportsFocus(update_behavior) !=
           FocusableState::kNotFocusable &&
@@ -242,7 +240,7 @@
   url.AppendNumber(clamped_point.y());
 }
 
-void HTMLAnchorElement::DefaultEventHandler(Event& event) {
+void HTMLAnchorElementBase::DefaultEventHandler(Event& event) {
   if (IsLink()) {
     EmitDidAnchorElementReceiveMouseEvent(*this, event);
 
@@ -262,18 +260,18 @@
   HTMLElement::DefaultEventHandler(event);
 }
 
-bool HTMLAnchorElement::HasActivationBehavior() const {
+bool HTMLAnchorElementBase::HasActivationBehavior() const {
   return IsLink();
 }
 
-void HTMLAnchorElement::SetActive(bool active) {
+void HTMLAnchorElementBase::SetActive(bool active) {
   if (active && IsEditable(*this))
     return;
 
   HTMLElement::SetActive(active);
 }
 
-void HTMLAnchorElement::AttributeChanged(
+void HTMLAnchorElementBase::AttributeChanged(
     const AttributeModificationParams& params) {
   HTMLElement::AttributeChanged(params);
 
@@ -285,7 +283,7 @@
     blur();
 }
 
-void HTMLAnchorElement::ParseAttribute(
+void HTMLAnchorElementBase::ParseAttribute(
     const AttributeModificationParams& params) {
   if (params.name == html_names::kHrefAttr) {
     if (params.old_value == params.new_value) {
@@ -348,17 +346,18 @@
   }
 }
 
-bool HTMLAnchorElement::IsURLAttribute(const Attribute& attribute) const {
+bool HTMLAnchorElementBase::IsURLAttribute(const Attribute& attribute) const {
   return attribute.GetName().LocalName() == html_names::kHrefAttr ||
          HTMLElement::IsURLAttribute(attribute);
 }
 
-bool HTMLAnchorElement::HasLegalLinkAttribute(const QualifiedName& name) const {
+bool HTMLAnchorElementBase::HasLegalLinkAttribute(
+    const QualifiedName& name) const {
   return name == html_names::kHrefAttr ||
          HTMLElement::HasLegalLinkAttribute(name);
 }
 
-void HTMLAnchorElement::FinishParsingChildren() {
+void HTMLAnchorElementBase::FinishParsingChildren() {
   Element::FinishParsingChildren();
   if (GetDocument().HasRenderBlockingExpectLinkElements()) {
     DCHECK(GetDocument().GetRenderBlockingResourceManager());
@@ -368,13 +367,13 @@
   }
 }
 
-bool HTMLAnchorElement::CanStartSelection() const {
+bool HTMLAnchorElementBase::CanStartSelection() const {
   if (!IsLink())
     return HTMLElement::CanStartSelection();
   return IsEditable(*this);
 }
 
-bool HTMLAnchorElement::draggable() const {
+bool HTMLAnchorElementBase::draggable() const {
   // Should be draggable if we have an href attribute.
   const AtomicString& value = FastGetAttribute(html_names::kDraggableAttr);
   if (EqualIgnoringASCIICase(value, "true"))
@@ -384,16 +383,16 @@
   return FastHasAttribute(html_names::kHrefAttr);
 }
 
-KURL HTMLAnchorElement::Href() const {
+KURL HTMLAnchorElementBase::Href() const {
   return GetDocument().CompleteURL(StripLeadingAndTrailingHTMLSpaces(
       FastGetAttribute(html_names::kHrefAttr)));
 }
 
-void HTMLAnchorElement::SetHref(const AtomicString& value) {
+void HTMLAnchorElementBase::SetHref(const AtomicString& value) {
   setAttribute(html_names::kHrefAttr, value);
 }
 
-KURL HTMLAnchorElement::Url() const {
+KURL HTMLAnchorElementBase::Url() const {
   KURL href = Href();
   if (!href.IsValid()) {
     return KURL();
@@ -401,23 +400,23 @@
   return href;
 }
 
-void HTMLAnchorElement::SetURL(const KURL& url) {
+void HTMLAnchorElementBase::SetURL(const KURL& url) {
   SetHref(AtomicString(url.GetString()));
 }
 
-String HTMLAnchorElement::Input() const {
+String HTMLAnchorElementBase::Input() const {
   return FastGetAttribute(html_names::kHrefAttr);
 }
 
-void HTMLAnchorElement::setHref(const String& value) {
+void HTMLAnchorElementBase::setHref(const String& value) {
   SetHref(AtomicString(value));
 }
 
-bool HTMLAnchorElement::HasRel(uint32_t relation) const {
+bool HTMLAnchorElementBase::HasRel(uint32_t relation) const {
   return link_relations_ & relation;
 }
 
-void HTMLAnchorElement::SetRel(const AtomicString& value) {
+void HTMLAnchorElementBase::SetRel(const AtomicString& value) {
   link_relations_ = 0;
   SpaceSplitString new_link_relations(value.LowerASCII());
   // FIXME: Add link relations as they are implemented
@@ -449,26 +448,26 @@
   // RelList::SupportedTokensAnchorAndAreaAndForm().
 }
 
-const AtomicString& HTMLAnchorElement::GetName() const {
+const AtomicString& HTMLAnchorElementBase::GetName() const {
   return GetNameAttribute();
 }
 
-const AtomicString& HTMLAnchorElement::GetEffectiveTarget() const {
+const AtomicString& HTMLAnchorElementBase::GetEffectiveTarget() const {
   const AtomicString& target = FastGetAttribute(html_names::kTargetAttr);
   if (!target.empty())
     return target;
   return GetDocument().BaseTarget();
 }
 
-int HTMLAnchorElement::DefaultTabIndex() const {
+int HTMLAnchorElementBase::DefaultTabIndex() const {
   return 0;
 }
 
-bool HTMLAnchorElement::IsLiveLink() const {
+bool HTMLAnchorElementBase::IsLiveLink() const {
   return IsLink() && !IsEditable(*this);
 }
 
-void HTMLAnchorElement::SendPings(const KURL& destination_url) const {
+void HTMLAnchorElementBase::SendPings(const KURL& destination_url) const {
   const AtomicString& ping_value = FastGetAttribute(html_names::kPingAttr);
   if (ping_value.IsNull() || !GetDocument().GetSettings() ||
       !GetDocument().GetSettings()->GetHyperlinkAuditingEnabled()) {
@@ -497,11 +496,12 @@
   }
 }
 
-void HTMLAnchorElement::NavigateToHyperlink(ResourceRequest request,
-                                            NavigationPolicy navigation_policy,
-                                            bool is_trusted,
-                                            base::TimeTicks platform_time_stamp,
-                                            KURL completed_url) {
+void HTMLAnchorElementBase::NavigateToHyperlink(
+    ResourceRequest request,
+    NavigationPolicy navigation_policy,
+    bool is_trusted,
+    base::TimeTicks platform_time_stamp,
+    KURL completed_url) {
   LocalDOMWindow* window = GetDocument().domWindow();
   if (!window) {
     return;
@@ -596,11 +596,11 @@
   }
 }
 
-void HTMLAnchorElement::SetHovered(bool hovered) {
+void HTMLAnchorElementBase::SetHovered(bool hovered) {
   HTMLElement::SetHovered(hovered);
 }
 
-Element* HTMLAnchorElement::interestTargetElement() {
+Element* HTMLAnchorElementBase::interestTargetElement() {
   CHECK(RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled());
 
   if (!IsInTreeScope()) {
@@ -611,7 +611,7 @@
       html_names::kInteresttargetAttr);
 }
 
-AtomicString HTMLAnchorElement::interestAction() const {
+AtomicString HTMLAnchorElementBase::interestAction() const {
   CHECK(RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled());
   const AtomicString& attribute_value =
       FastGetAttribute(html_names::kInterestactionAttr);
@@ -622,7 +622,7 @@
   return g_empty_atom;
 }
 
-void HTMLAnchorElement::HandleClick(MouseEvent& event) {
+void HTMLAnchorElementBase::HandleClick(MouseEvent& event) {
   event.SetDefaultHandled();
 
   LocalDOMWindow* window = GetDocument().domWindow();
@@ -719,7 +719,7 @@
   }
 
   base::OnceClosure navigate_closure = WTF::BindOnce(
-      &HTMLAnchorElement::NavigateToHyperlink, WrapWeakPersistent(this),
+      &HTMLAnchorElementBase::NavigateToHyperlink, WrapWeakPersistent(this),
       std::move(request), navigation_policy, event.isTrusted(),
       event.PlatformTimeStamp(), std::move(completed_url));
 
@@ -758,15 +758,15 @@
               static_cast<int16_t>(WebPointerProperties::Button::kMiddle));
 }
 
-bool HTMLAnchorElement::WillRespondToMouseClickEvents() {
+bool HTMLAnchorElementBase::WillRespondToMouseClickEvents() {
   return IsLink() || HTMLElement::WillRespondToMouseClickEvents();
 }
 
-bool HTMLAnchorElement::IsInteractiveContent() const {
+bool HTMLAnchorElementBase::IsInteractiveContent() const {
   return IsLink();
 }
 
-Node::InsertionNotificationRequest HTMLAnchorElement::InsertedInto(
+Node::InsertionNotificationRequest HTMLAnchorElementBase::InsertedInto(
     ContainerNode& insertion_point) {
   InsertionNotificationRequest request =
       HTMLElement::InsertedInto(insertion_point);
@@ -789,7 +789,7 @@
   return request;
 }
 
-void HTMLAnchorElement::RemovedFrom(ContainerNode& insertion_point) {
+void HTMLAnchorElementBase::RemovedFrom(ContainerNode& insertion_point) {
   HTMLElement::RemovedFrom(insertion_point);
 
   if (insertion_point.isConnected()) {
@@ -807,9 +807,12 @@
   }
 }
 
-void HTMLAnchorElement::Trace(Visitor* visitor) const {
+void HTMLAnchorElementBase::Trace(Visitor* visitor) const {
   visitor->Trace(rel_list_);
   HTMLElement::Trace(visitor);
 }
 
+HTMLAnchorElement::HTMLAnchorElement(Document& document)
+    : HTMLAnchorElementBase(html_names::kATag, document) {}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.h b/third_party/blink/renderer/core/html/html_anchor_element.h
index dd4b1d00..143a8b5 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.h
+++ b/third_party/blink/renderer/core/html/html_anchor_element.h
@@ -69,13 +69,14 @@
   kRelationTermsOfService = 0x00200000,
 };
 
-class CORE_EXPORT HTMLAnchorElement : public HTMLElement, public DOMURLUtils {
-  DEFINE_WRAPPERTYPEINFO();
-
+// Base class for <a> and <area> (HTMLAnchorElement and HTMLAreaElement).
+// Note: If a new element needs to use this as a base, existing callsites and
+// features that use this class should be audited (to see if the new element
+// should also support these features).
+class CORE_EXPORT HTMLAnchorElementBase : public HTMLElement,
+                                          public DOMURLUtils {
  public:
-  HTMLAnchorElement(Document& document);
-  HTMLAnchorElement(const QualifiedName&, Document&);
-  ~HTMLAnchorElement() override;
+  ~HTMLAnchorElementBase() override;
 
   KURL Href() const;
   void SetHref(const AtomicString&);
@@ -118,6 +119,8 @@
   void Trace(Visitor*) const override;
 
  protected:
+  HTMLAnchorElementBase(const QualifiedName& tag_name, Document&);
+
   void ParseAttribute(const AttributeModificationParams&) override;
   FocusableState SupportsFocus(UpdateBehavior update_behavior) const override;
 
@@ -128,8 +131,7 @@
   bool ShouldHaveFocusAppearance() const final;
   FocusableState IsFocusableState(
       UpdateBehavior update_behavior) const override;
-  bool IsKeyboardFocusable(UpdateBehavior update_behavior =
-                               UpdateBehavior::kStyleAndLayout) const override;
+  bool IsKeyboardFocusable(UpdateBehavior update_behavior) const override;
   void DefaultEventHandler(Event&) final;
   bool HasActivationBehavior() const override;
   void SetActive(bool active) final;
@@ -153,7 +155,27 @@
   Member<RelList> rel_list_;
 };
 
-inline LinkHash HTMLAnchorElement::VisitedLinkHash() const {
+class CORE_EXPORT HTMLAnchorElement : public HTMLAnchorElementBase {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  explicit HTMLAnchorElement(Document& document);
+};
+
+template <>
+struct DowncastTraits<HTMLAnchorElementBase> {
+  static bool AllowFrom(const Element& element) {
+    return element.HasTagName(html_names::kATag) ||
+           element.HasTagName(html_names::kAreaTag);
+  }
+
+  static bool AllowFrom(const Node& node) {
+    return node.IsHTMLElement() &&
+           IsA<HTMLAnchorElementBase>(UnsafeTo<HTMLElement>(node));
+  }
+};
+
+inline LinkHash HTMLAnchorElementBase::VisitedLinkHash() const {
   if (!cached_visited_link_hash_) {
     cached_visited_link_hash_ = blink::VisitedLinkHash(
         GetDocument().BaseURL(), FastGetAttribute(html_names::kHrefAttr));
@@ -161,7 +183,8 @@
   return cached_visited_link_hash_;
 }
 
-inline LinkHash HTMLAnchorElement::PartitionedVisitedLinkFingerprint() const {
+inline LinkHash HTMLAnchorElementBase::PartitionedVisitedLinkFingerprint()
+    const {
   if (!cached_visited_link_hash_) {
     // Obtain all three elements of the partition key.
     // (1) Link URL (Base and Relative)
diff --git a/third_party/blink/renderer/core/html/html_area_element.cc b/third_party/blink/renderer/core/html/html_area_element.cc
index 1744651..9ab5aef 100644
--- a/third_party/blink/renderer/core/html/html_area_element.cc
+++ b/third_party/blink/renderer/core/html/html_area_element.cc
@@ -44,7 +44,7 @@
 }
 
 HTMLAreaElement::HTMLAreaElement(Document& document)
-    : HTMLAnchorElement(html_names::kAreaTag, document), shape_(kRect) {}
+    : HTMLAnchorElementBase(html_names::kAreaTag, document), shape_(kRect) {}
 
 // An explicit empty destructor should be in html_area_element.cc, because
 // if an implicit destructor is used or an empty destructor is defined in
@@ -78,7 +78,7 @@
              params.name == html_names::kAccesskeyAttr) {
     // Do nothing.
   } else {
-    HTMLAnchorElement::ParseAttribute(params);
+    HTMLAnchorElementBase::ParseAttribute(params);
   }
 }
 
@@ -187,13 +187,13 @@
 
 bool HTMLAreaElement::IsKeyboardFocusable(
     UpdateBehavior update_behavior) const {
-  // Explicitly skip over the HTMLAnchorElement's keyboard focus behavior.
+  // Explicitly skip over the HTMLAnchorElementBase's keyboard focus behavior.
   return Element::IsKeyboardFocusable(update_behavior);
 }
 
 FocusableState HTMLAreaElement::IsFocusableState(
     UpdateBehavior update_behavior) const {
-  // Explicitly skip over the HTMLAnchorElement's mouse focus behavior.
+  // Explicitly skip over the HTMLAnchorElementBase's mouse focus behavior.
   return HTMLElement::IsFocusableState(update_behavior);
 }
 
@@ -218,7 +218,7 @@
   if (IsFocused() == should_be_focused)
     return;
 
-  HTMLAnchorElement::SetFocused(should_be_focused, focus_type);
+  HTMLAnchorElementBase::SetFocused(should_be_focused, focus_type);
 
   HTMLImageElement* image_element = ImageElement();
   if (!image_element)
diff --git a/third_party/blink/renderer/core/html/html_area_element.h b/third_party/blink/renderer/core/html/html_area_element.h
index 7f4bb368..0d85672 100644
--- a/third_party/blink/renderer/core/html/html_area_element.h
+++ b/third_party/blink/renderer/core/html/html_area_element.h
@@ -34,7 +34,7 @@
 class HTMLImageElement;
 class Path;
 
-class CORE_EXPORT HTMLAreaElement final : public HTMLAnchorElement {
+class CORE_EXPORT HTMLAreaElement final : public HTMLAnchorElementBase {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
diff --git a/third_party/blink/renderer/core/html/html_permission_element.cc b/third_party/blink/renderer/core/html/html_permission_element.cc
index 0744683..41c6ae5b 100644
--- a/third_party/blink/renderer/core/html/html_permission_element.cc
+++ b/third_party/blink/renderer/core/html/html_permission_element.cc
@@ -777,6 +777,10 @@
   auto it = permission_status_map_.find(permission_name);
   CHECK(it != permission_status_map_.end());
   it->value = status;
+  permissions_granted_ =
+      base::ranges::all_of(permission_status_map_, [](const auto& status) {
+        return status.value == MojoPermissionStatus::GRANTED;
+      });
   UpdateAppearance();
 }
 
@@ -1050,15 +1054,11 @@
 void HTMLPermissionElement::UpdateText() {
   CHECK_GT(permission_status_map_.size(), 0U);
   CHECK_LE(permission_status_map_.size(), 2u);
-  bool granted =
-      base::ranges::all_of(permission_status_map_, [](const auto& status) {
-        return status.value == MojoPermissionStatus::GRANTED;
-      });
-
-  int message_id = permission_status_map_.size() == 1
-                       ? GetMessageIDSinglePermission(
-                             permission_status_map_.begin()->key, granted)
-                       : GetMessageIDMultiplePermissions(granted);
+  int message_id =
+      permission_status_map_.size() == 1
+          ? GetMessageIDSinglePermission(permission_status_map_.begin()->key,
+                                         permissions_granted_)
+          : GetMessageIDMultiplePermissions(permissions_granted_);
 
   CHECK(message_id);
   permission_text_span_->setInnerText(GetLocale().QueryString(message_id));
diff --git a/third_party/blink/renderer/core/inspector/inspector_preload_agent.cc b/third_party/blink/renderer/core/inspector/inspector_preload_agent.cc
index 17cd6a6..b1661a84 100644
--- a/third_party/blink/renderer/core/inspector/inspector_preload_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_preload_agent.cc
@@ -138,14 +138,14 @@
       BuildProtocolPreloadingAttemptKey(key, document);
 
   HeapHashSet<Member<SpeculationRuleSet>> unique_rule_sets;
-  HeapHashSet<Member<HTMLAnchorElement>> unique_anchors;
+  HeapHashSet<Member<HTMLAnchorElementBase>> unique_anchors;
   auto rule_set_ids = std::make_unique<protocol::Array<String>>();
   auto node_ids = std::make_unique<protocol::Array<int>>();
   for (SpeculationCandidate* candidate : candidates) {
     if (unique_rule_sets.insert(candidate->rule_set()).is_new_entry) {
       rule_set_ids->push_back(candidate->rule_set()->InspectorId());
     }
-    if (HTMLAnchorElement* anchor = candidate->anchor();
+    if (HTMLAnchorElementBase* anchor = candidate->anchor();
         anchor && unique_anchors.insert(anchor).is_new_entry) {
       node_ids->push_back(anchor->GetDomNodeId());
     }
diff --git a/third_party/blink/renderer/core/layout/block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/block_layout_algorithm.cc
index 35558379..22906e6 100644
--- a/third_party/blink/renderer/core/layout/block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/block_layout_algorithm.cc
@@ -2558,7 +2558,18 @@
       should_text_box_trim_start_ = false;
       container_builder_.SetIsBlockStartTrimmed();
     }
-    if (should_text_box_trim_end_) {
+    if (should_text_box_trim_end_ &&
+        line_clamp_data_.data.state ==
+            LineClampData::kMeasureLinesUntilBfcOffset &&
+        layout_result->GetPhysicalFragment().GetBreakToken()) {
+      // If we trimmed the end only because we're in the first layout of a
+      // line-clamp: auto context, and we might not trim in the relayout, then
+      // we don't reset should_text_box_trim_end_, and we add the trim length to
+      // the logical block offset so next lines are set in the right position.
+      DCHECK(layout_result->TrimBlockEndBy());
+      previous_inflow_position->logical_block_offset +=
+          *layout_result->TrimBlockEndBy();
+    } else if (should_text_box_trim_end_) {
       if (layout_result->IsBlockEndTrimmed()) {
         should_text_box_trim_end_ = false;
         container_builder_.SetIsBlockEndTrimmed();
diff --git a/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm.cc b/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm.cc
index 275a96d..7c94fe3 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm.cc
@@ -553,8 +553,10 @@
   const ConstraintSpace& space = GetConstraintSpace();
   if (space.ShouldTextBoxTrimStart() || space.ShouldTextBoxTrimEnd())
       [[unlikely]] {
-    ApplyTextBoxTrim(*line_info,
-                     line_clamp_state == LineClampState::kEllipsize);
+    bool is_truncated = line_clamp_state == LineClampState::kEllipsize ||
+                        space.GetLineClampData().state ==
+                            LineClampData::kMeasureLinesUntilBfcOffset;
+    ApplyTextBoxTrim(*line_info, is_truncated);
   }
 
   // |container_builder_| is already set up by |PlaceBlockInInline|.
diff --git a/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.cc b/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.cc
index a876e4f7..13a87cf 100644
--- a/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.cc
+++ b/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.cc
@@ -252,7 +252,8 @@
     }
   }
 
-  HTMLAnchorElement* anchor = FirstAnchorElementIncludingSelf(target.ToNode());
+  HTMLAnchorElementBase* anchor =
+      FirstAnchorElementIncludingSelf(target.ToNode());
   if (!anchor) {
     return;
   }
@@ -304,7 +305,7 @@
 }
 
 void AnchorElementInteractionTracker::OnClickEvent(
-    HTMLAnchorElement& anchor,
+    HTMLAnchorElementBase& anchor,
     const MouseEvent& click_event) {
   if (auto* sender =
           AnchorElementMetricsSender::GetForFrame(GetDocument()->GetFrame())) {
@@ -404,18 +405,18 @@
   clock_ = clock;
 }
 
-HTMLAnchorElement*
+HTMLAnchorElementBase*
 AnchorElementInteractionTracker::FirstAnchorElementIncludingSelf(Node* node) {
-  HTMLAnchorElement* anchor = nullptr;
+  HTMLAnchorElementBase* anchor = nullptr;
   while (node && !anchor) {
-    anchor = DynamicTo<HTMLAnchorElement>(node);
+    anchor = DynamicTo<HTMLAnchorElementBase>(node);
     node = node->parentNode();
   }
   return anchor;
 }
 
 KURL AnchorElementInteractionTracker::GetHrefEligibleForPreloading(
-    const HTMLAnchorElement& anchor) {
+    const HTMLAnchorElementBase& anchor) {
   KURL url = anchor.Href();
   if (url.ProtocolIsInHTTPFamily()) {
     return url;
diff --git a/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.h b/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.h
index 36beb0c4..49de485 100644
--- a/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.h
+++ b/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.h
@@ -17,7 +17,7 @@
 
 class Document;
 class EventTarget;
-class HTMLAnchorElement;
+class HTMLAnchorElementBase;
 class KURL;
 class MouseEvent;
 class Node;
@@ -83,7 +83,8 @@
 
   void OnMouseMoveEvent(const WebMouseEvent& mouse_event);
   void OnPointerEvent(EventTarget& target, const PointerEvent& pointer_event);
-  void OnClickEvent(HTMLAnchorElement& anchor, const MouseEvent& click_event);
+  void OnClickEvent(HTMLAnchorElementBase& anchor,
+                    const MouseEvent& click_event);
   void OnScrollEnd();
 
   void HoverTimerFired(TimerBase*);
@@ -94,11 +95,11 @@
   Document* GetDocument() { return document_.Get(); }
 
  private:
-  HTMLAnchorElement* FirstAnchorElementIncludingSelf(Node* node);
+  HTMLAnchorElementBase* FirstAnchorElementIncludingSelf(Node* node);
 
   // Gets the `anchor's` href attribute if it is part
   // of the HTTP family
-  KURL GetHrefEligibleForPreloading(const HTMLAnchorElement& anchor);
+  KURL GetHrefEligibleForPreloading(const HTMLAnchorElementBase& anchor);
 
   Member<MouseMotionEstimator> mouse_motion_estimator_;
   HeapMojoRemote<mojom::blink::AnchorElementInteractionHost> interaction_host_;
diff --git a/third_party/blink/renderer/core/mobile_metrics/tap_friendliness_checker.cc b/third_party/blink/renderer/core/mobile_metrics/tap_friendliness_checker.cc
index cbd4741..c620f75a 100644
--- a/third_party/blink/renderer/core/mobile_metrics/tap_friendliness_checker.cc
+++ b/third_party/blink/renderer/core/mobile_metrics/tap_friendliness_checker.cc
@@ -23,6 +23,7 @@
 // Considers the |target| is a tap-able element which TapFriendlinessChecker
 // focus.
 bool ShouldRegister(Element* target) {
+  // TODO(crbug.com/369219144): Should this be DynamicTo<HTMLAnchorElementBase>?
   if (const auto* anchor = DynamicTo<HTMLAnchorElement>(target)) {
     return !anchor->Href().IsEmpty();
   } else if (auto* element = DynamicTo<HTMLElement>(target);
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc
index 1c543b1d..1f33bb3 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -793,6 +793,7 @@
     data.custom_items = menu_provider_->PopulateContextMenu().ReleaseVector();
   }
 
+  // TODO(crbug.com/369219144): Should this be DynamicTo<HTMLAnchorElementBase>?
   if (auto* anchor = DynamicTo<HTMLAnchorElement>(result.URLElement())) {
     // Extract suggested filename for same-origin URLS for saving file.
     const SecurityOrigin* origin =
diff --git a/third_party/blink/renderer/core/page/drag_controller.cc b/third_party/blink/renderer/core/page/drag_controller.cc
index 546d777..b326b79c5 100644
--- a/third_party/blink/renderer/core/page/drag_controller.cc
+++ b/third_party/blink/renderer/core/page/drag_controller.cc
@@ -899,6 +899,8 @@
         candidate_drag_type = kDragSourceActionDHTML;
         break;
       }
+      // TODO(crbug.com/369219144): Should this be
+      // DynamicTo<HTMLAnchorElementBase>?
       auto* html_anchor_element = DynamicTo<HTMLAnchorElement>(node);
       if (html_anchor_element && html_anchor_element->IsLiveLink()) {
         candidate_drag_type = kDragSourceActionLink;
@@ -984,6 +986,7 @@
   DataTransfer* data_transfer = state.drag_data_transfer_.Get();
   Node* node = state.drag_src_.Get();
 
+  // TODO(crbug.com/369219144): Should this be DynamicTo<HTMLAnchorElementBase>?
   auto* html_anchor_element = DynamicTo<HTMLAnchorElement>(node);
   if (html_anchor_element && html_anchor_element->IsLiveLink() &&
       !link_url.IsEmpty()) {
diff --git a/third_party/blink/renderer/core/speculation_rules/document_rule_predicate.cc b/third_party/blink/renderer/core/speculation_rules/document_rule_predicate.cc
index c957260..3a138e4 100644
--- a/third_party/blink/renderer/core/speculation_rules/document_rule_predicate.cc
+++ b/third_party/blink/renderer/core/speculation_rules/document_rule_predicate.cc
@@ -35,7 +35,7 @@
       : clauses_(std::move(clauses)) {}
   ~Conjunction() override = default;
 
-  bool Matches(const HTMLAnchorElement& el) const override {
+  bool Matches(const HTMLAnchorElementBase& el) const override {
     return base::ranges::all_of(clauses_, [&](DocumentRulePredicate* clause) {
       return clause->Matches(el);
     });
@@ -85,7 +85,7 @@
       : clauses_(std::move(clauses)) {}
   ~Disjunction() override = default;
 
-  bool Matches(const HTMLAnchorElement& el) const override {
+  bool Matches(const HTMLAnchorElementBase& el) const override {
     return base::ranges::any_of(clauses_, [&](DocumentRulePredicate* clause) {
       return clause->Matches(el);
     });
@@ -134,7 +134,7 @@
   explicit Negation(DocumentRulePredicate* clause) : clause_(clause) {}
   ~Negation() override = default;
 
-  bool Matches(const HTMLAnchorElement& el) const override {
+  bool Matches(const HTMLAnchorElementBase& el) const override {
     return !clause_->Matches(el);
   }
 
@@ -179,7 +179,7 @@
       : patterns_(std::move(patterns)), execution_context_(execution_context) {}
   ~URLPatternPredicate() override = default;
 
-  bool Matches(const HTMLAnchorElement& el) const override {
+  bool Matches(const HTMLAnchorElementBase& el) const override {
     // Let href be the result of running el’s href getter steps.
     const KURL href = el.HrefURL();
     // For each pattern of predicate’s patterns:
@@ -232,7 +232,7 @@
   explicit CSSSelectorPredicate(HeapVector<Member<StyleRule>> style_rules)
       : style_rules_(std::move(style_rules)) {}
 
-  bool Matches(const HTMLAnchorElement& link) const override {
+  bool Matches(const HTMLAnchorElementBase& link) const override {
     DCHECK(!link.GetDocument().NeedsLayoutTreeUpdate());
     const ComputedStyle* computed_style = link.GetComputedStyle();
     DCHECK(computed_style);
diff --git a/third_party/blink/renderer/core/speculation_rules/document_rule_predicate.h b/third_party/blink/renderer/core/speculation_rules/document_rule_predicate.h
index 66b16049..52571366 100644
--- a/third_party/blink/renderer/core/speculation_rules/document_rule_predicate.h
+++ b/third_party/blink/renderer/core/speculation_rules/document_rule_predicate.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SPECULATION_RULES_DOCUMENT_RULE_PREDICATE_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/html/html_anchor_element.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
@@ -13,7 +14,7 @@
 
 namespace blink {
 
-class HTMLAnchorElement;
+class HTMLAnchorElementBase;
 class ExceptionState;
 class ExecutionContext;
 class JSONObject;
@@ -36,7 +37,7 @@
   // always returns true).
   static DocumentRulePredicate* MakeDefaultPredicate();
 
-  virtual bool Matches(const HTMLAnchorElement& link) const = 0;
+  virtual bool Matches(const HTMLAnchorElementBase& link) const = 0;
   virtual HeapVector<Member<StyleRule>> GetStyleRules() const = 0;
 
   // Methods for testing.
diff --git a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
index 0f5d040..5c9ad78 100644
--- a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
+++ b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
@@ -116,7 +116,7 @@
                                     const SpeculationRuleSet& rule_set,
                                     Document& document,
                                     mojom::blink::SpeculationAction action,
-                                    HTMLAnchorElement* link,
+                                    HTMLAnchorElementBase* link,
                                     std::optional<KURL> opt_url) {
   ExecutionContext* execution_context = document.GetExecutionContext();
   DCHECK(link || opt_url);
@@ -373,7 +373,7 @@
   speculation_rule_loaders_.erase(speculation_rule_loader);
 }
 
-void DocumentSpeculationRules::LinkInserted(HTMLAnchorElement* link) {
+void DocumentSpeculationRules::LinkInserted(HTMLAnchorElementBase* link) {
   if (!initialized_)
     return;
 
@@ -383,7 +383,7 @@
   QueueUpdateSpeculationCandidates();
 }
 
-void DocumentSpeculationRules::LinkRemoved(HTMLAnchorElement* link) {
+void DocumentSpeculationRules::LinkRemoved(HTMLAnchorElementBase* link) {
   if (!initialized_)
     return;
 
@@ -393,7 +393,7 @@
 }
 
 void DocumentSpeculationRules::HrefAttributeChanged(
-    HTMLAnchorElement* link,
+    HTMLAnchorElementBase* link,
     const AtomicString& old_value,
     const AtomicString& new_value) {
   if (!initialized_)
@@ -413,15 +413,17 @@
 }
 
 void DocumentSpeculationRules::ReferrerPolicyAttributeChanged(
-    HTMLAnchorElement* link) {
+    HTMLAnchorElementBase* link) {
   LinkAttributeChanged(link);
 }
 
-void DocumentSpeculationRules::RelAttributeChanged(HTMLAnchorElement* link) {
+void DocumentSpeculationRules::RelAttributeChanged(
+    HTMLAnchorElementBase* link) {
   LinkAttributeChanged(link);
 }
 
-void DocumentSpeculationRules::TargetAttributeChanged(HTMLAnchorElement* link) {
+void DocumentSpeculationRules::TargetAttributeChanged(
+    HTMLAnchorElementBase* link) {
   LinkAttributeChanged(link);
 }
 
@@ -440,14 +442,14 @@
 }
 
 void DocumentSpeculationRules::LinkMatchedSelectorsUpdated(
-    HTMLAnchorElement* link) {
+    HTMLAnchorElementBase* link) {
   DCHECK(initialized_);
   InvalidateLink(link);
   QueueUpdateSpeculationCandidates();
 }
 
 void DocumentSpeculationRules::LinkGainedOrLostComputedStyle(
-    HTMLAnchorElement* link) {
+    HTMLAnchorElementBase* link) {
   if (!initialized_) {
     return;
   }
@@ -476,9 +478,7 @@
   while (node) {
     if (node->IsLink() && (node->HasTagName(html_names::kATag) ||
                            node->HasTagName(html_names::kAreaTag))) {
-      HTMLAnchorElement* anchor = node->HasTagName(html_names::kATag)
-                                      ? To<HTMLAnchorElement>(node)
-                                      : To<HTMLAreaElement>(node);
+      HTMLAnchorElementBase* anchor = To<HTMLAnchorElementBase>(node);
       if (stale_links_.insert(anchor).is_new_entry) {
         InvalidateLink(anchor);
         queue_update = true;
@@ -517,9 +517,7 @@
   while (node) {
     if (node->IsLink() && (node->HasTagName(html_names::kATag) ||
                            node->HasTagName(html_names::kAreaTag))) {
-      HTMLAnchorElement* anchor = node->HasTagName(html_names::kATag)
-                                      ? To<HTMLAnchorElement>(node)
-                                      : To<HTMLAreaElement>(node);
+      HTMLAnchorElementBase* anchor = To<HTMLAnchorElementBase>(node);
       if (auto it = stale_links_.find(anchor); it != stale_links_.end()) {
         stale_links_.erase(it);
         InvalidateLink(anchor);
@@ -759,7 +757,7 @@
   // Match all the unmatched
   while (!pending_links_.empty()) {
     auto it = pending_links_.begin();
-    HTMLAnchorElement* link = *it;
+    HTMLAnchorElementBase* link = *it;
     HeapVector<Member<SpeculationCandidate>>* link_candidates =
         MakeGarbageCollected<HeapVector<Member<SpeculationCandidate>>>();
     Document& document = *GetSupplementable();
@@ -867,14 +865,14 @@
        ShadowIncludingTreeOrderTraversal::DescendantsOf(*GetSupplementable())) {
     if (!node.IsLink())
       continue;
-    if (auto* anchor = DynamicTo<HTMLAnchorElement>(node))
+    if (auto* anchor = DynamicTo<HTMLAnchorElementBase>(node)) {
       pending_links_.insert(anchor);
-    else if (auto* area = DynamicTo<HTMLAreaElement>(node))
-      pending_links_.insert(area);
+    }
   }
 }
 
-void DocumentSpeculationRules::LinkAttributeChanged(HTMLAnchorElement* link) {
+void DocumentSpeculationRules::LinkAttributeChanged(
+    HTMLAnchorElementBase* link) {
   if (!initialized_) {
     return;
   }
@@ -891,7 +889,7 @@
   QueueUpdateSpeculationCandidates();
 }
 
-void DocumentSpeculationRules::AddLink(HTMLAnchorElement* link) {
+void DocumentSpeculationRules::AddLink(HTMLAnchorElementBase* link) {
   DCHECK(initialized_);
   DCHECK(link->IsLink());
   DCHECK(!base::Contains(unmatched_links_, link));
@@ -907,7 +905,7 @@
   }
 }
 
-void DocumentSpeculationRules::RemoveLink(HTMLAnchorElement* link) {
+void DocumentSpeculationRules::RemoveLink(HTMLAnchorElementBase* link) {
   DCHECK(initialized_);
   stale_links_.erase(link);
 
@@ -930,7 +928,7 @@
   pending_links_.erase(it);
 }
 
-void DocumentSpeculationRules::InvalidateLink(HTMLAnchorElement* link) {
+void DocumentSpeculationRules::InvalidateLink(HTMLAnchorElementBase* link) {
   DCHECK(initialized_);
 
   pending_links_.insert(link);
@@ -950,8 +948,9 @@
     pending_links_.insert(it.key);
   matched_links_.clear();
 
-  for (HTMLAnchorElement* link : unmatched_links_)
+  for (HTMLAnchorElementBase* link : unmatched_links_) {
     pending_links_.insert(link);
+  }
   unmatched_links_.clear();
 }
 
diff --git a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h
index 463e0d97..e704170f 100644
--- a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h
+++ b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h
@@ -17,7 +17,7 @@
 
 namespace blink {
 
-class HTMLAnchorElement;
+class HTMLAnchorElementBase;
 class SpeculationCandidate;
 class SpeculationRuleLoader;
 
@@ -48,19 +48,19 @@
   void AddSpeculationRuleLoader(SpeculationRuleLoader*);
   void RemoveSpeculationRuleLoader(SpeculationRuleLoader*);
 
-  void LinkInserted(HTMLAnchorElement* link);
-  void LinkRemoved(HTMLAnchorElement* link);
-  void HrefAttributeChanged(HTMLAnchorElement* link,
+  void LinkInserted(HTMLAnchorElementBase* link);
+  void LinkRemoved(HTMLAnchorElementBase* link);
+  void HrefAttributeChanged(HTMLAnchorElementBase* link,
                             const AtomicString& old_value,
                             const AtomicString& new_value);
-  void ReferrerPolicyAttributeChanged(HTMLAnchorElement* link);
-  void RelAttributeChanged(HTMLAnchorElement* link);
-  void TargetAttributeChanged(HTMLAnchorElement* link);
+  void ReferrerPolicyAttributeChanged(HTMLAnchorElementBase* link);
+  void RelAttributeChanged(HTMLAnchorElementBase* link);
+  void TargetAttributeChanged(HTMLAnchorElementBase* link);
   void DocumentReferrerPolicyChanged();
   void DocumentBaseURLChanged();
   void DocumentBaseTargetChanged();
-  void LinkMatchedSelectorsUpdated(HTMLAnchorElement* link);
-  void LinkGainedOrLostComputedStyle(HTMLAnchorElement* link);
+  void LinkMatchedSelectorsUpdated(HTMLAnchorElementBase* link);
+  void LinkGainedOrLostComputedStyle(HTMLAnchorElementBase* link);
   void DocumentStyleUpdated();
   void ChildStyleRecalcBlocked(Element* root);
   void DidStyleChildren(Element* root);
@@ -101,13 +101,13 @@
 
   // Helper methods that are used to deal with link/document attribute changes
   // that could invalidate the list of speculation candidates.
-  void LinkAttributeChanged(HTMLAnchorElement* link);
+  void LinkAttributeChanged(HTMLAnchorElementBase* link);
   void DocumentPropertyChanged();
 
   // Helper methods to modify |link_map_|.
-  void AddLink(HTMLAnchorElement* link);
-  void RemoveLink(HTMLAnchorElement* link);
-  void InvalidateLink(HTMLAnchorElement* link);
+  void AddLink(HTMLAnchorElementBase* link);
+  void RemoveLink(HTMLAnchorElementBase* link);
+  void InvalidateLink(HTMLAnchorElementBase* link);
   void InvalidateAllLinks();
 
   // Populates |selectors_| and notifies the StyleEngine.
@@ -154,15 +154,15 @@
   // TODO(crbug.com/1371522): Consider removing |unmatched_links_| and
   // re-traverse the document to find all links when a new ruleset is
   // added/removed.
-  HeapHashMap<Member<HTMLAnchorElement>,
+  HeapHashMap<Member<HTMLAnchorElementBase>,
               Member<HeapVector<Member<SpeculationCandidate>>>>
       matched_links_;
-  HeapHashSet<Member<HTMLAnchorElement>> unmatched_links_;
-  HeapHashSet<Member<HTMLAnchorElement>> pending_links_;
+  HeapHashSet<Member<HTMLAnchorElementBase>> unmatched_links_;
+  HeapHashSet<Member<HTMLAnchorElementBase>> pending_links_;
 
   // Links with ComputedStyle that wasn't updated after the most recent style
   // update (due to having a display-locked ancestor).
-  HeapHashSet<Member<HTMLAnchorElement>> stale_links_;
+  HeapHashSet<Member<HTMLAnchorElementBase>> stale_links_;
   HeapHashSet<Member<Element>> elements_blocking_child_style_recalc_;
 
   // Collects every CSS selector from every CSS selector document rule predicate
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_candidate.cc b/third_party/blink/renderer/core/speculation_rules/speculation_candidate.cc
index e3ae361..447c07c 100644
--- a/third_party/blink/renderer/core/speculation_rules/speculation_candidate.cc
+++ b/third_party/blink/renderer/core/speculation_rules/speculation_candidate.cc
@@ -20,7 +20,7 @@
     network::mojom::blink::NoVarySearchPtr no_vary_search,
     mojom::blink::SpeculationInjectionType injection_type,
     SpeculationRuleSet* rule_set,
-    HTMLAnchorElement* anchor)
+    HTMLAnchorElementBase* anchor)
     : url_(url),
       action_(action),
       referrer_(std::move(referrer)),
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_candidate.h b/third_party/blink/renderer/core/speculation_rules/speculation_candidate.h
index 98692f188..857dbbd 100644
--- a/third_party/blink/renderer/core/speculation_rules/speculation_candidate.h
+++ b/third_party/blink/renderer/core/speculation_rules/speculation_candidate.h
@@ -13,7 +13,7 @@
 
 namespace blink {
 
-class HTMLAnchorElement;
+class HTMLAnchorElementBase;
 class KURL;
 struct Referrer;
 class SpeculationRuleSet;
@@ -34,7 +34,7 @@
                        network::mojom::blink::NoVarySearchPtr no_vary_search,
                        mojom::blink::SpeculationInjectionType injection_type,
                        SpeculationRuleSet* rule_set,
-                       HTMLAnchorElement* anchor);
+                       HTMLAnchorElementBase* anchor);
   virtual ~SpeculationCandidate() = default;
 
   void Trace(Visitor* visitor) const;
@@ -50,7 +50,7 @@
   SpeculationRuleSet* rule_set() const { return rule_set_.Get(); }
   // Only set for candidates derived from a document rule (is null for
   // candidates derived from list rules).
-  HTMLAnchorElement* anchor() const { return anchor_.Get(); }
+  HTMLAnchorElementBase* anchor() const { return anchor_.Get(); }
 
  private:
   const KURL url_;
@@ -62,7 +62,7 @@
   const network::mojom::blink::NoVarySearchPtr no_vary_search_;
   const mojom::blink::SpeculationInjectionType injection_type_;
   const Member<SpeculationRuleSet> rule_set_;
-  const Member<HTMLAnchorElement> anchor_;
+  const Member<HTMLAnchorElementBase> anchor_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 439b591..f6551d4 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -2688,6 +2688,7 @@
     return false;
   }
 
+  // TODO(crbug.com/369219144): Should this be DynamicTo<HTMLAnchorElementBase>?
   if (auto* anchor = DynamicTo<HTMLAnchorElement>(AnchorElement())) {
     return !anchor->Href().IsEmpty();
   }
@@ -3381,6 +3382,7 @@
   // submitted, and could also be overridden by a "formTarget" attribute on e.g.
   // a form's submit button. However, screen reader users have no need to know
   // to which target (browser context) a form would be submitted.
+  // TODO(crbug.com/369219144): Should this be DynamicTo<HTMLAnchorElementBase>?
   const auto* anchor = DynamicTo<HTMLAnchorElement>(GetNode());
   if (anchor) {
     const AtomicString self_value("_self");
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 148d535..ac57a5e 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -2497,18 +2497,6 @@
     return;
   }
 
-  // Add details relation to <figure>, pointing at <figcaption>.
-  if (node_data->role == ax::mojom::blink::Role::kFigure) {
-    AXObject* fig_caption = GetChildFigcaption();
-    if (fig_caption) {
-      std::vector<int32_t> ids;
-      ids.push_back(GetChildFigcaption()->AXObjectID());
-      node_data->AddIntListAttribute(
-          ax::mojom::blink::IntListAttribute::kDetailsIds, ids);
-      return;
-    }
-  }
-
   // Add aria-details for a popover invoker.
   // TODO(https://crbug.com/1426607) Support this for non-plain hint popovers.
   if (AXObject* popover = GetTargetPopoverForInvoker()) {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index b791809..579ffb5 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -1941,7 +1941,12 @@
 
 scoped_refptr<CanvasResource> CanvasResourceProvider::NewOrRecycledResource() {
   if (canvas_resources_.empty()) {
-    RegisterUnusedResource(CreateResource());
+    scoped_refptr<CanvasResource> resource = CreateResource();
+    if (!resource) {
+      return nullptr;
+    }
+
+    RegisterUnusedResource(std::move(resource));
     ++num_inflight_resources_;
     if (num_inflight_resources_ > max_inflight_resources_)
       max_inflight_resources_ = num_inflight_resources_;
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index e721845..b9e110ba 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -1480,3 +1480,6 @@
 
 # Slow because it is transfering a lot of data to catch non-spec compliant behavior with large buffers
 crbug.com/356963272 external/wpt/webrtc/RTCDataChannel-send-close.html [ Slow ]
+
+# Slow because it contains a large number of subtests
+crbug.com/369600187 external/wpt/css/css-anchor-position/anchor-size-parse-valid.html [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 4a01c112..62522f7 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1438,6 +1438,16 @@
 crbug.com/358159989 external/wpt/css/css-images/out-of-range-color-stop-conic.html [ Failure ]
 crbug.com/358159989 external/wpt/css/css-images/tiled-conic-gradients.html [ Failure ]
 
+# :has-slotted
+crbug.com/369883705 external/wpt/css/css-scoping/has-slotted-001.tentative.html [ Failure ]
+crbug.com/369883705 external/wpt/css/css-scoping/has-slotted-004.tentative.html [ Failure ]
+crbug.com/369883705 external/wpt/css/css-scoping/has-slotted-006.tentative.html [ Failure ]
+crbug.com/369883705 external/wpt/css/css-scoping/has-slotted-007.tentative.html [ Failure ]
+crbug.com/369883705 external/wpt/css/css-scoping/has-slotted-flattened-001.tentative.html [ Failure ]
+crbug.com/369883705 external/wpt/css/css-scoping/has-slotted-flattened-004.tentative.html [ Failure ]
+crbug.com/369883705 external/wpt/css/css-scoping/has-slotted-flattened-006.tentative.html [ Failure ]
+crbug.com/369883705 external/wpt/css/css-scoping/has-slotted-flattened-007.tentative.html [ Failure ]
+
 # ====== Style team owned tests to here ======
 
 # Failing WPT html/semantics/* tests. Not all have been investigated.
@@ -2614,17 +2624,6 @@
 crbug.com/369685643 [ Mac12 ] virtual/view-transition-mpa-serialization/external/wpt/css/css-view-transitions/animating-new-content-subset.html [ Failure ]
 crbug.com/369685643 [ Mac14 ] virtual/view-transition-mpa-serialization/external/wpt/css/css-view-transitions/animating-new-content-subset.html [ Failure ]
 crbug.com/369668292 [ Mac12 ] virtual/threaded/external/wpt/css/css-view-transitions/animating-new-content-subset.html [ Failure ]
-crbug.com/369236537 [ Mac12 ] external/wpt/css/css-anchor-position/anchor-size-parse-valid.html [ Failure Timeout ]
-crbug.com/369236537 [ Mac11 ] external/wpt/css/css-anchor-position/anchor-size-parse-valid.html [ Failure Timeout ]
-crbug.com/369236537 [ Mac14 ] external/wpt/css/css-anchor-position/anchor-size-parse-valid.html [ Failure Timeout ]
-crbug.com/369236538 external/wpt/css/css-scoping/has-slotted-001.tentative.html [ Failure ]
-crbug.com/369236538 external/wpt/css/css-scoping/has-slotted-004.tentative.html [ Failure ]
-crbug.com/369236538 external/wpt/css/css-scoping/has-slotted-006.tentative.html [ Failure ]
-crbug.com/369236538 external/wpt/css/css-scoping/has-slotted-007.tentative.html [ Failure ]
-crbug.com/369236538 external/wpt/css/css-scoping/has-slotted-flattened-001.tentative.html [ Failure ]
-crbug.com/369236538 external/wpt/css/css-scoping/has-slotted-flattened-004.tentative.html [ Failure ]
-crbug.com/369236538 external/wpt/css/css-scoping/has-slotted-flattened-006.tentative.html [ Failure ]
-crbug.com/369236538 external/wpt/css/css-scoping/has-slotted-flattened-007.tentative.html [ Failure ]
 crbug.com/369236541 external/wpt/css/css-text-decor/text-underline-position-auto-001.html [ Failure ]
 crbug.com/369236542 [ Mac13-arm64 ] virtual/fsa-incognito/external/wpt/fs/FileSystemDirectoryHandle-getDirectoryHandle.https.any.worker.html [ Timeout ]
 crbug.com/369270133 [ Mac12 ] virtual/view-transition-wide-gamut/external/wpt/css/css-view-transitions/animating-new-content-subset.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index ec19d33..8499068 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1211,7 +1211,8 @@
     "prefix": "block-cross-partition-blob-url-fetching",
     "platforms": ["Linux"],
     "bases": [
-      "external/wpt/FileAPI/BlobURL/cross-partition.tentative.https.html"],
+      "external/wpt/FileAPI/BlobURL/cross-partition.tentative.https.html",
+      "external/wpt/FileAPI/BlobURL/cross-partition-worker-creation.tentative.https.html"],
     "args": ["--enable-features=BlockCrossPartitionBlobUrlFetching"],
     "expires": "Feb 4, 2025",
     "owners": ["janiceliu@chromium.org"]
@@ -3552,6 +3553,7 @@
     "bases": [
       "external/wpt/css/css-overflow/line-clamp",
       "external/wpt/css/css-overflow/parsing",
+      "external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-001.html",
       "fast/overflow"
     ],
     "args": [
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 45a14d9..892667c 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -478605,7 +478605,7 @@
       ]
      ],
      "pseudo-classes-after-part.html": [
-      "a2a2f35db4d7cecd90155bf4d48f428b3d766345",
+      "60292d27a71c26ef2c327a85458a957887275561",
       [
        null,
        {}
diff --git a/third_party/blink/web_tests/external/wpt/FileAPI/BlobURL/cross-partition-worker-creation.tentative.https-expected.txt b/third_party/blink/web_tests/external/wpt/FileAPI/BlobURL/cross-partition-worker-creation.tentative.https-expected.txt
new file mode 100644
index 0000000..d8d909f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/FileAPI/BlobURL/cross-partition-worker-creation.tentative.https-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+[FAIL] Cross-partition Shared worker shouldn't be created from blob URL.
+  promise_test: Unhandled rejection with value: "Shared worker was created in not-same-top-level-site iframe"
+[FAIL] Cross-partition Dedicated worker shouldn't be created from blob URL.
+  promise_test: Unhandled rejection with value: "Dedicated worker was created in not-same-top-level-site iframe"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/FileAPI/BlobURL/cross-partition-worker-creation.tentative.https.html b/third_party/blink/web_tests/external/wpt/FileAPI/BlobURL/cross-partition-worker-creation.tentative.https.html
new file mode 100644
index 0000000..c8234cfb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/FileAPI/BlobURL/cross-partition-worker-creation.tentative.https.html
@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<!-- Pull in executor_path needed by newPopup / newIframe -->
+<script src="/html/cross-origin-embedder-policy/credentialless/resources/common.js"></script>
+<!-- Pull in importScript / newPopup / newIframe -->
+<script src="/html/anonymous-iframe/resources/common.js"></script>
+<body>
+<script>
+
+const create_worker_unsuccessful = "Worker creation failed.";
+const create_worker_successful = "Worker creation succeeded.";
+
+const add_iframe_js = (iframe_origin, response_queue_uuid) => `
+  const importScript = ${importScript};
+  await importScript("/html/cross-origin-embedder-policy/credentialless" +
+                   "/resources/common.js");
+  await importScript("/html/anonymous-iframe/resources/common.js");
+  await importScript("/common/utils.js");
+
+  // dispatcher.js has already been loaded by the popup this is running in.
+  await send("${response_queue_uuid}", newIframe("${iframe_origin}"));
+`;
+
+const same_site_origin = get_host_info().HTTPS_ORIGIN;
+const cross_site_origin = get_host_info().HTTPS_NOTSAMESITE_ORIGIN;
+
+async function create_test_iframes(t, response_queue_uuid) {
+  assert_equals("https://" + window.location.host, same_site_origin,
+  "this test assumes that the page's window.location.host corresponds to " +
+  "get_host_info().HTTPS_ORIGIN");
+
+  // Create a same-origin iframe in a cross-site popup.
+  const not_same_site_popup_uuid = newPopup(t, cross_site_origin);
+  await send(not_same_site_popup_uuid,
+       add_iframe_js(same_site_origin, response_queue_uuid));
+  const cross_site_iframe_uuid = await receive(response_queue_uuid);
+
+  // Create a same-origin iframe in a same-site popup.
+  const same_origin_popup_uuid = newPopup(t, same_site_origin);
+  await send(same_origin_popup_uuid,
+       add_iframe_js(same_site_origin, response_queue_uuid));
+  const same_site_iframe_uuid = await receive(response_queue_uuid);
+
+  return [cross_site_iframe_uuid, same_site_iframe_uuid];
+}
+
+const can_create_blob_url_shared_worker_js = (blob_url, response_queue_name) => `
+  const worker = new SharedWorker("${blob_url}");
+  worker.onerror = (e) => {
+    send("${response_queue_name}", "${create_worker_unsuccessful}");
+  };
+  worker.port.onmessage = (e) => {
+    send("${response_queue_name}", "${create_worker_successful}");
+  };
+`;
+
+// Tests cross-partition Shared Worker creation from blob URL.
+promise_test(t => {
+  return new Promise(async (resolve, reject) => {
+    try {
+      const response_queue_uuid = token();
+
+      const [cross_site_iframe_uuid, same_site_iframe_uuid] =
+        await create_test_iframes(t, response_queue_uuid);
+
+      const worker_js = `
+        onconnect = function(e) {
+          e.ports[0].postMessage('ping');
+          self.close();
+        };
+      `;
+
+      const blob = new Blob([worker_js], {type : "text/javascript"});
+      const blob_url = window.URL.createObjectURL(blob);
+      t.add_cleanup(() => window.URL.revokeObjectURL(blob_url));
+
+      // Create a shared worker in the cross-top-level-site iframe.
+      await send(cross_site_iframe_uuid, can_create_blob_url_shared_worker_js(blob_url, response_queue_uuid));
+      const response_1 = await receive(response_queue_uuid);
+      if (response_1 !== create_worker_unsuccessful) {
+        reject(`Shared worker was created in not-same-top-level-site iframe`);
+      }
+
+      // Create a shared worker in the same-top-level-site iframe.
+      await send(same_site_iframe_uuid, can_create_blob_url_shared_worker_js(blob_url, response_queue_uuid));
+      const response_2 = await receive(response_queue_uuid);
+      if (response_2 !== create_worker_successful) {
+        reject(`Shared worker wasn't created in same-top-level-site iframe`);
+      }
+
+      resolve();
+    } catch (e) {
+      reject(e);
+    }
+  });
+}, "Cross-partition Shared worker shouldn't be created from blob URL.");
+
+const can_create_blob_url_dedicated_worker_js = (blob_url, response_queue_name) => `
+  const worker = new Worker("${blob_url}");
+  worker.onerror = (e) => {
+    send("${response_queue_name}", "${create_worker_unsuccessful}");
+  };
+  worker.onmessage = (e) => {
+    send("${response_queue_name}", "${create_worker_successful}");
+    worker.terminate();
+  };
+`;
+
+// Tests cross-partition Dedicated Worker creation from blob URL.
+promise_test(t => {
+  return new Promise(async (resolve, reject) => {
+    try {
+      const response_queue_uuid = token();
+
+      const [cross_site_iframe_uuid, same_site_iframe_uuid] =
+        await create_test_iframes(t, response_queue_uuid);
+
+      const blob = new Blob(["postMessage('ping');"], {type : "text/javascript"});
+      const blob_url = window.URL.createObjectURL(blob);
+      t.add_cleanup(() => window.URL.revokeObjectURL(blob_url));
+
+      // Create a dedicated worker in the cross-top-level-site iframe.
+      await send(cross_site_iframe_uuid, can_create_blob_url_dedicated_worker_js(blob_url, response_queue_uuid));
+      const response_1 = await receive(response_queue_uuid);
+      if (response_1 !== create_worker_unsuccessful) {
+        reject(`Dedicated worker was created in not-same-top-level-site iframe`);
+      }
+
+      // Create a dedicated worker in the same-top-level-site iframe.
+      await send(same_site_iframe_uuid, can_create_blob_url_dedicated_worker_js(blob_url, response_queue_uuid));
+      const response_2 = await receive(response_queue_uuid);
+      if (response_2 !== create_worker_successful) {
+        reject(`Dedicated worker wasn't created in same-top-level-site iframe`);
+      }
+
+      resolve();
+    } catch (e) {
+      reject(e);
+    }
+  });
+}, "Cross-partition Dedicated worker shouldn't be created from blob URL.");
+
+</script>
+</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-001.html b/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-001.html
index 46c10c8..56af569 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-001.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>text-box-trim applies to the last line after line-clamp</title>
+<title>text-box-trim applies to the last line before line-clamp</title>
 <link rel="help" href="https://drafts.csswg.org/css-inline-3/#text-box-trim">
 <link rel="help" href="https://drafts.csswg.org/css-inline-3/#text-box-edge">
 <link ref="help" href="https://www.w3.org/TR/css-overflow-4/#line-clamp">
diff --git a/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-auto-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-auto-001-ref.html
new file mode 100644
index 0000000..508d262
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-auto-001-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+.spacer {
+  background: lightgray;
+  block-size: 100px;
+}
+.target {
+  font: 50px/2 Ahem;
+  height: 275px;
+}
+</style>
+<div class="spacer"></div>
+<div class="target">
+  A<br>
+  B<br>
+  C…</div>
+<div class="spacer"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-auto-001.html b/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-auto-001.html
new file mode 100644
index 0000000..05987167
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-auto-001.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>text-box-trim applies to the last line before clamp when clamping by a height</title>
+<link rel="help" href="https://drafts.csswg.org/css-inline-3/#propdef-text-box-edge">
+<link rel="help" href="https://drafts.csswg.org/css-inline-3/#propdef-text-box-trim">
+<link rel="match" href="text-box-trim-line-clamp-auto-001-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+.spacer {
+  background: lightgray;
+  block-size: 100px;
+}
+.target {
+  font: 50px/2 Ahem;
+  text-box-trim: trim-end;
+  text-box-edge: text;
+
+  line-clamp: auto;
+  max-height: 285px;
+}
+</style>
+<div class="spacer"></div>
+<div class="target">
+  A<br>
+  B<br>
+  C<br>
+  D</div>
+<div class="spacer"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-auto-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-auto-002-ref.html
new file mode 100644
index 0000000..046d17c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-auto-002-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+.spacer {
+  background: lightgray;
+  block-size: 100px;
+}
+.target {
+  font-family: Ahem;
+  font-size: 50px;
+  line-height: 2;
+  height: 375px;
+}
+</style>
+<div class="spacer"></div>
+<div class="target">
+  A<br>
+  B<br>
+  C<br>
+  D…</div>
+<div class="spacer"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-auto-002.html b/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-auto-002.html
new file mode 100644
index 0000000..ddb5df15
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-line-clamp-auto-002.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>text-box-trim applies to the last line before clamp when clamping by a height</title>
+<link rel="help" href="https://drafts.csswg.org/css-inline-3/#propdef-text-box-edge">
+<link rel="help" href="https://drafts.csswg.org/css-inline-3/#propdef-text-box-trim">
+<link rel="match" href="text-box-trim-line-clamp-auto-002-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+.spacer {
+  background: lightgray;
+  block-size: 100px;
+}
+.target {
+  font: 50px/2 Ahem;
+  text-box-trim: trim-end;
+  text-box-edge: text;
+
+  /* 385px will fit 4 lines if the last line has its end trimmed
+   * (4*100 - 25 = 375px), but it will fit 5 lines if all have their end trimmed
+   * (5*(100 - 25) = 375px). This tests that only the last line gets trimmed. */
+  line-clamp: auto;
+  max-height: 385px;
+}
+</style>
+<div class="spacer"></div>
+<div class="target">
+  A<br>
+  B<br>
+  C<br>
+  D<br>
+  E<br>
+  F</div>
+<div class="spacer"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/pseudo-classes-after-part.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/pseudo-classes-after-part.html
index a2a2f35..60292d2 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/pseudo-classes-after-part.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/pseudo-classes-after-part.html
@@ -88,7 +88,7 @@
 test_valid_selector("::part(mypart):is(:hover)")
 test_valid_selector("::part(mypart):where(:hover)")
 test_invalid_selector("::part(mypart):not(:first-child)")
-test_valid_selector("::part(mypart):is(:first-child)")
-test_valid_selector("::part(mypart):where(:first-child)")
+test_valid_forgiving_selector("::part(mypart):is(:first-child)")
+test_valid_forgiving_selector("::part(mypart):where(:first-child)")
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/tools/gentestutilsunion.py b/third_party/blink/web_tests/external/wpt/html/canvas/tools/gentestutilsunion.py
index ae323a1..760368cf 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/tools/gentestutilsunion.py
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/tools/gentestutilsunion.py
@@ -281,7 +281,7 @@
             r'@assert pixel .* 0,0,0,0;', test['code']):
         print(f'Probable incorrect pixel test in {test["name"]}')
 
-    if 'size' in test and (not isinstance(test['size'], list)
+    if 'size' in test and (not isinstance(test['size'], tuple)
                            or len(test['size']) != 2):
         raise InvalidTestDefinitionError(
             f'Invalid canvas size "{test["size"]}" in test {test["name"]}. '
@@ -363,7 +363,7 @@
         Default values are added for certain parameters, if missing."""
         params = {
             'desc': '',
-            'size': [100, 50],
+            'size': (100, 50),
             # Test name, which ultimately is used as filename. File variant
             # dimension names are appended to this to produce unique filenames.
             'name': '',
@@ -459,6 +459,9 @@
         self._params['canvas_types'] = self._get_canvas_types()
         self._params['template_type'] = self._get_template_type()
 
+        if isinstance(self._params['size'], list):
+            self._params['size'] = tuple(self._params['size'])
+
         if 'reference' in self._params:
             self._params['reference'] = _preprocess_code(
                 jinja_env, self._params['reference'], self._params)
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/granted-selector.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/granted-selector.html
new file mode 100644
index 0000000..3ce1ad7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/granted-selector.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md#locking-the-pepc-style">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<body>
+
+<permission id="permission_element" type="camera">
+
+<script>
+  promise_test(async() => {
+
+    // Set the initial camera state to denied.
+    await test_driver.set_permission({name: "camera"}, "denied");
+    await navigator.permissions.query({name: "camera"});
+
+    assert_false(permission_element.matches(":granted"));
+
+    // Set the camera state to allowed.
+    await test_driver.set_permission({name: "camera"}, "granted");
+    await navigator.permissions.query({name: "camera"});
+
+    // The granted selector should now be applied.
+    assert_true(permission_element.matches(":granted"));
+
+    // Set the camera state to denied.
+    await test_driver.set_permission({name: "camera"}, "denied");
+    await navigator.permissions.query({name: "camera"});
+
+    // The granted selector should now be removed.
+    assert_false(permission_element.matches(":granted"));
+
+  }, "Permission element should not have the granted selector when the \
+      permission is not granted.")
+</script>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-nameSources-img-figure-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-nameSources-img-figure-expected.txt
index 7283f3d8..f9e5cbea 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-nameSources-img-figure-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-nameSources-img-figure-expected.txt
@@ -114,18 +114,6 @@
     nodeId : <string>
     parentId : <string>
     properties : [
-        [0] : {
-            name : details
-            value : {
-                relatedNodes : [
-                    [0] : {
-                        nodeResult : figcaption
-                    }
-                ]
-                type : idrefList
-                value : 
-            }
-        }
     ]
     role : {
         type : role
@@ -182,18 +170,6 @@
     nodeId : <string>
     parentId : <string>
     properties : [
-        [0] : {
-            name : details
-            value : {
-                relatedNodes : [
-                    [0] : {
-                        nodeResult : figcaption
-                    }
-                ]
-                type : idrefList
-                value : 
-            }
-        }
     ]
     role : {
         type : role
@@ -267,18 +243,6 @@
     parentId : <string>
     properties : [
         [0] : {
-            name : details
-            value : {
-                relatedNodes : [
-                    [0] : {
-                        nodeResult : figcaption
-                    }
-                ]
-                type : idrefList
-                value : 
-            }
-        }
-        [1] : {
             name : labelledby
             value : {
                 relatedNodes : [
diff --git a/third_party/crossbench b/third_party/crossbench
index 18c0bc4..64f705b 160000
--- a/third_party/crossbench
+++ b/third_party/crossbench
@@ -1 +1 @@
-Subproject commit 18c0bc491b2e3c71d910377b3656560a0a204db4
+Subproject commit 64f705bef1cbf31c84d6a779e4939c8b79edecde
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal
index 984e509..3ed9eea 160000
--- a/third_party/devtools-frontend-internal
+++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@
-Subproject commit 984e509da297e8ac3366d6579379a1294af73771
+Subproject commit 3ed9eeaf4c42ea7a10232f1df5dcd94ea68452b5
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 862905a..986c1da7 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 862905a4376db9c2964d5178b30d11bf80c6cfdd
+Subproject commit 986c1da746d466f1079e62047f7a176e244b51f4
diff --git a/third_party/fuzztest/BUILD.gn b/third_party/fuzztest/BUILD.gn
index 50a58fc..452355d 100644
--- a/third_party/fuzztest/BUILD.gn
+++ b/third_party/fuzztest/BUILD.gn
@@ -151,7 +151,6 @@
     deps = [
       ":fuzztest_internal",
       "//third_party/abseil-cpp:absl_full",
-      "//third_party/boringssl",
     ]
     sources = [
       "src/centipede/analyze_corpora.cc",
@@ -330,12 +329,11 @@
     "src/common/remote_file.cc",
     "src/common/remote_file.h",
     "src/common/remote_file_oss.cc",
+    "src/common/sha1.cc",
+    "src/common/sha1.h",
   ]
 
-  deps = [
-    "//third_party/abseil-cpp:absl_full",
-    "//third_party/boringssl",
-  ]
+  deps = [ "//third_party/abseil-cpp:absl_full" ]
   public_configs = [ ":fuzztest_internal_config" ]
   configs -= fuzztest_remove_configs
   configs += fuzztest_add_configs
diff --git a/third_party/fuzztest/src b/third_party/fuzztest/src
index a29e31c..f0177b9 160000
--- a/third_party/fuzztest/src
+++ b/third_party/fuzztest/src
@@ -1 +1 @@
-Subproject commit a29e31cb00ec9b123dec5a0c6b8d4bc12c2480c8
+Subproject commit f0177b98d4d5f17a0bd448852d66d3e9a2c8fe86
diff --git a/third_party/libc++abi/src b/third_party/libc++abi/src
index d0c57cb..3c26edd 160000
--- a/third_party/libc++abi/src
+++ b/third_party/libc++abi/src
@@ -1 +1 @@
-Subproject commit d0c57cb48fb5709f02096f411d65a11cbc722944
+Subproject commit 3c26edd0c36a294c74800ac48c17b18d0045e3b6
diff --git a/third_party/skia b/third_party/skia
index 5f4740b..06721a7 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 5f4740b998b39a53127873ed1798c739229b5027
+Subproject commit 06721a72483137ffea1d51309bf681020b5e9e57
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index 50ad0c4..223523f 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit 50ad0c468c61f59b5a5bd6b869c44fc8d45cd51d
+Subproject commit 223523f05dc03140b40cf71915f74311207c3a6e
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src
index b840f9e..8bb1ad96 160000
--- a/third_party/vulkan-validation-layers/src
+++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@
-Subproject commit b840f9e619bc367959bd246d685f4af6b92ae161
+Subproject commit 8bb1ad96a6204dd85b4af293eddc76a6f6ea3a54
diff --git a/third_party/webrtc b/third_party/webrtc
index 4ab100d..95e6971 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 4ab100d4a6c3b7848ae33b4c88fdf995da2f8922
+Subproject commit 95e697190f50f9fe1dce12e9d49cda165efa29b0
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 0cd826ce..250dc6f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -30929,6 +30929,8 @@
   <int value="15"
       label="Choice Screen dialog skipped as app stared by external intent"/>
   <int value="16" label="Dialog is already shown in the same browser"/>
+  <int value="17"
+      label="Skipped by using a choice persisted across guest sessions"/>
 </enum>
 
 <!-- LINT.ThenChange(/components/search_engines/search_engine_choice/search_engine_choice_utils.h:SearchEngineChoiceScreenConditions) -->
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml
index a7d59eb..e2642ed 100644
--- a/tools/metrics/histograms/metadata/enterprise/histograms.xml
+++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -3028,26 +3028,6 @@
   <token key="State" variants="AutoEnrollmentStates"/>
 </histogram>
 
-<histogram
-    name="Enterprise.StateDetermination.KillSwitchFetch.NetworkErrorCode"
-    enum="NetErrorCodes" expires_after="2025-02-02">
-  <owner>sergiyb@chromium.org</owner>
-  <owner>chromeos-commercial-remote-management@google.com</owner>
-  <summary>
-    Network error code for unified state determination kill switch config
-    requests.
-  </summary>
-</histogram>
-
-<histogram name="Enterprise.StateDetermination.KillSwitchFetch.NumTries"
-    units="requests" expires_after="2025-02-02">
-  <owner>sergiyb@chromium.org</owner>
-  <owner>chromeos-commercial-remote-management@google.com</owner>
-  <summary>
-    Number of tries for fetching unified state determination kill switch config.
-  </summary>
-</histogram>
-
 <histogram name="Enterprise.StateDetermination.OnFlex" enum="Boolean"
     expires_after="2025-02-02">
   <owner>sergiyb@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/ios/enums.xml b/tools/metrics/histograms/metadata/ios/enums.xml
index 552586c..f78a094 100644
--- a/tools/metrics/histograms/metadata/ios/enums.xml
+++ b/tools/metrics/histograms/metadata/ios/enums.xml
@@ -957,19 +957,11 @@
   <int value="9" label="Search Passwords widget"/>
 </enum>
 
-<enum name="LanguageDetectionComparison">
-  <int value="0" label="Language detected by TFLite only (CLD3 failed)"/>
-  <int value="1" label="Language detected by CLD3 only (TFLite failed)"/>
-  <int value="2" label="No language detected (both model failed)"/>
-  <int value="3" label="Both model succeeded and agree"/>
-  <int value="4" label="Both model succeeded bug disagreed"/>
-</enum>
-
 <enum name="LanguageDetectionMethod">
   <int value="0" label="TFLite model"/>
   <int value="1" label="CLD3 (TFLite model unavailable)"/>
   <int value="2" label="CLD3 (TFLite model disabled)"/>
-  <int value="3" label="CLD3 (TFLite model run but is ignored)"/>
+  <int value="3" label="CLD3 (TFLite model run but is ignored) (deprecated)"/>
 </enum>
 
 <enum name="LinkToTextShouldOfferResult">
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index e4d10507..39f3aa0 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -3046,8 +3046,9 @@
 </histogram>
 
 <histogram name="IOS.PageLoadCount.LoadingStarted" units="units"
-    expires_after="2024-06-30">
+    expires_after="2025-10-30">
   <owner>gambard@chromium.org</owner>
+  <owner>ajuma@chromium.org</owner>
   <owner>bling-team@google.com</owner>
   <summary>
     The &quot;true&quot; value of this boolean histogram counts the number of
@@ -4842,16 +4843,6 @@
   </summary>
 </histogram>
 
-<histogram name="IOS.Translate.PageLoad.LanguageDetectionComparison"
-    enum="LanguageDetectionComparison" expires_after="2023-03-24">
-  <owner>olivierrobin@chromium.org</owner>
-  <owner>mcrouse@chromium.org</owner>
-  <summary>
-    When both CLD3 and TFLite language detection method are run, compare their
-    results.
-  </summary>
-</histogram>
-
 <histogram name="IOS.Translate.PageLoad.LanguageDetectionMethod"
     enum="LanguageDetectionMethod" expires_after="2023-03-24">
   <owner>olivierrobin@chromium.org</owner>
@@ -5137,8 +5128,9 @@
 </histogram>
 
 <histogram name="IOS.WKWebViewFinishBeforeCommit" enum="Boolean"
-    expires_after="2024-06-30">
+    expires_after="2025-10-30">
   <owner>gambard@chromium.org</owner>
+  <owner>ajuma@chromium.org</owner>
   <owner>bling-team@google.com</owner>
   <summary>
     WKWebView should notify navigation commit before navigation finish, but
diff --git a/tools/metrics/histograms/metadata/mobile/histograms.xml b/tools/metrics/histograms/metadata/mobile/histograms.xml
index 6ba873f..73f4e0a 100644
--- a/tools/metrics/histograms/metadata/mobile/histograms.xml
+++ b/tools/metrics/histograms/metadata/mobile/histograms.xml
@@ -1229,7 +1229,8 @@
     Counts how many times one of the &quot;sign in&quot; buttons (any of the
     signed-out &quot;Sign in to Chrome&quot; button, the &quot;Continue as
     |name|&quot; button, or the &quot;Not |email|?&quot; button) is clicked per
-    impression of the NTP feed top section sign in promo.
+    impression of the NTP feed top section sign in promo. This histogram expired
+    by mistake between 2023-06-01 and 2024-09-16.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/signin/histograms.xml b/tools/metrics/histograms/metadata/signin/histograms.xml
index 5655248..d1ff33e 100644
--- a/tools/metrics/histograms/metadata/signin/histograms.xml
+++ b/tools/metrics/histograms/metadata/signin/histograms.xml
@@ -1532,7 +1532,7 @@
 </histogram>
 
 <histogram name="Signin.IOSDeviceRestoreSignedInState"
-    enum="IOSDeviceRestoreSignedinState" expires_after="2024-11-03">
+    enum="IOSDeviceRestoreSignedinState" expires_after="2025-04-01">
   <owner>jlebel@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
@@ -1796,6 +1796,22 @@
   <summary>Response status received from gaia Multilogin.</summary>
 </histogram>
 
+<histogram name="Signin.OpenTabsCount.{ConsentLevel}" units="count"
+    expires_after="2025-01-19">
+  <owner>treib@chromium.org</owner>
+  <owner>rsult@google.com</owner>
+  <owner>droger@chromium.org</owner>
+  <owner>chrome-signin-team@google.com</owner>
+  <summary>
+    Recording the count of open tabs, at the moment when the user is
+    {ConsentLevel} Chrome.
+  </summary>
+  <token key="ConsentLevel">
+    <variant name="OnSignin" summary="signing in to"/>
+    <variant name="OnSync" summary="enabling sync in"/>
+  </token>
+</histogram>
+
 <histogram name="Signin.PAMInitialize.PrimaryAccountInfoState"
     enum="PAMInitializePrimaryAccountInfoState" expires_after="2025-02-10">
   <owner>msarda@chromium.org</owner>
@@ -2682,28 +2698,6 @@
   </token>
 </histogram>
 
-<histogram name="Signin.{GroupOrTab}.{ConsentLevel}" units="count"
-    expires_after="2025-01-19">
-  <owner>treib@chromium.org</owner>
-  <owner>rsult@google.com</owner>
-  <owner>droger@chromium.org</owner>
-  <owner>chrome-signin-team@google.com</owner>
-  <summary>
-    Recording the count of {GroupOrTab}, at the moment when the user is
-    {ConsentLevel} Chrome.
-  </summary>
-  <token key="GroupOrTab">
-    <variant name="OpenTabsCount" summary="open tabs"/>
-    <variant name="TabGroupsCount" summary="tab groups"/>
-    <variant name="TabGroupsTabsCount"
-        summary="tabs that are part of a tab group"/>
-  </token>
-  <token key="ConsentLevel">
-    <variant name="OnSignin" summary="signing in to"/>
-    <variant name="OnSync" summary="enabling sync in"/>
-  </token>
-</histogram>
-
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/web_rtc/histograms.xml b/tools/metrics/histograms/metadata/web_rtc/histograms.xml
index f2e0f7e..88f2205 100644
--- a/tools/metrics/histograms/metadata/web_rtc/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_rtc/histograms.xml
@@ -1530,15 +1530,17 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.ValidBundledPayloadTypes" enum="Boolean"
-    expires_after="2024-06-30">
+    expires_after="2025-03-01">
   <owner>hbos@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
   <owner>phancke@microsoft.com</owner>
   <summary>
     Measures whether the remote description contains a payload type collision
     that is not allowed by the IETFs BUNDLE specification. Enforcing this would
-    allow simplifying the statistics collector. Recorded for every remote
-    description.
+    allow simplifying the statistics collector, and fix issue
+    https://issues.webrtc.org/42224689. Recorded for every remote description.
+    Warning: this histogram was expired from 2024-06-30 to 2024-09-30; data may
+    be missing.
   </summary>
 </histogram>
 
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 3d121ed..cff6b43 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -385,7 +385,6 @@
  <item id="promise_app_service" added_in_milestone="114" content_hash_code="07856112" os_list="chromeos" file_path="chrome/browser/apps/app_service/promise_apps/promise_app_almanac_connector.cc" />
  <item id="safe_browsing_hashprefix_realtime_lookup_ohttp" added_in_milestone="114" content_hash_code="0817ae7d" os_list="linux,windows,chromeos,android" file_path="components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.cc" />
  <item id="gaia_auth_rotate_bound_cookies" added_in_milestone="115" content_hash_code="04014185" os_list="linux,windows" file_path="chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc" />
- <item id="unified_state_determination_kill_switch" added_in_milestone="115" content_hash_code="01f51d8e" os_list="chromeos" file_path="chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.cc" />
  <item id="crash_file_uploader" added_in_milestone="115" content_hash_code="05f6902f" os_list="linux,windows" file_path="remoting/host/crash/crash_file_uploader.cc" />
  <item id="glanceables_classroom_integration" added_in_milestone="115" content_hash_code="048f5ccb" os_list="chromeos" file_path="chrome/browser/ui/ash/glanceables/glanceables_classroom_client_impl.cc" />
  <item id="promise_app_service_download_icon" added_in_milestone="115" content_hash_code="00521032" os_list="chromeos" file_path="chrome/browser/apps/app_service/promise_apps/promise_app_service.cc" />
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index e6966d8c..07265920 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -261,7 +261,6 @@
       <annotation id="back_navigation_cache_query"/>
       <annotation id="cws_info_service"/>
       <annotation id="promise_app_service"/>
-      <annotation id="unified_state_determination_kill_switch"/>
       <annotation id="crash_file_uploader"/>
       <annotation id="glanceables_classroom_integration"/>
       <annotation id="promise_app_service_download_icon"/>
diff --git a/tools/v8_context_snapshot/v8_context_snapshot_generator.cc b/tools/v8_context_snapshot/v8_context_snapshot_generator.cc
index 472c649..37ee2e0 100644
--- a/tools/v8_context_snapshot/v8_context_snapshot_generator.cc
+++ b/tools/v8_context_snapshot/v8_context_snapshot_generator.cc
@@ -35,6 +35,10 @@
 int main(int argc, char** argv) {
   base::AtExitManager at_exit;
 
+  // Initialize an empty feature list for gin startup.
+  auto early_access_feature_list = std::make_unique<base::FeatureList>();
+  base::FeatureList::SetInstance(std::move(early_access_feature_list));
+
   const bool kRemoveRecognizedFlags = true;
   v8::V8::SetFlagsFromCommandLine(&argc, argv, kRemoveRecognizedFlags);
   base::CommandLine::Init(argc, argv);