diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 5dcdaf1..c2ce175f 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -667,7 +667,6 @@
 0b20ccad48bc63b0e6bf07b8511b32b8e3d963b1
 
 # Reformat .java code with google-java-format. Tagged with Bug: 1491626 (part 2)
-fd85d4fb7579adf095a94232268201342f89d836
 c89f6078b3d03a07e8859e4e2be5dc9baaf1d5f0
 a46fbc6634d0ac2fcdf88f35fa743fa5fd072575
 54443fc172332d6c7053d0d028405e396aa283e2
diff --git a/DEPS b/DEPS
index 8567779..a040289 100644
--- a/DEPS
+++ b/DEPS
@@ -303,7 +303,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': 'e40b61c3dfefe6a5ac8f200b0041148d5108cfa0',
+  'skia_revision': '276075b975675ba92d907dd64247937b7b73245e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -311,7 +311,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': '40f4de8fa1ff252fd57552a6a1d5cf4067f83883',
+  'angle_revision': 'd704273d78967f286c300d445bdc0eb9f43b2389',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -319,7 +319,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'b2d4f1104c43ccd42df2e48902ae857f0d5a188a',
+  'pdfium_revision': '57e284ba20215dfa5cdbf732b4e158c544323d86',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -378,7 +378,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling chromium_variations
   # and whatever else without interference from each other.
-  'chromium_variations_revision': '42bf9b85713027338720203624f1ee98d0982a89',
+  'chromium_variations_revision': '6a94fae344833b2e4c170955c9b10fa3a40c5550',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
@@ -394,7 +394,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': '0a48a3bd9f916f605f225b4e7e2760380d0a9cc9',
+  'devtools_frontend_revision': 'c67fdadcd104ba82740a25feed4fec2931b5f6f3',
   # 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.
@@ -418,7 +418,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '673e8b4639e3e19b97380992f2a82bc4442d9643',
+  'dawn_revision': '0b6ca39d38735c9177dc8f7291dad5314fed3b11',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -462,7 +462,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.
-  'cros_components_revision': '075d293d1a98cce55cdb88185239126eb64f6d55',
+  'cros_components_revision': 'f448b44c03202bdb5fb1305216d4723724d5288b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1196,7 +1196,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' + '@' + '2854d4287a81c0a177ea4d6fa64737f1b9640614',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '457fec8b5b9ff390f9e7a8e64f8a886d97cbe1f9',
     'condition': 'checkout_src_internal',
   },
 
@@ -1701,7 +1701,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': 'w4FEa7Y-q3MIsKwjR0LC_-fPnANe8DlIIAWD_pMF5z0C',
+              'version': 'FxJgCW2a6MbqM5WkyXAxu91pykj7_QAYNerG9ettPPAC',
           },
       ],
       'condition': 'checkout_android',
@@ -1846,7 +1846,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'eb1892c3e4ab734a135e8752f5442e270a4738d1',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '57b06646bee860edb996c9c9f4ea6372a12a9536',
+    Var('webrtc_git') + '/src.git' + '@' + 'acdc89d65328911b4e98afe0757028eca162cbb3',
 
   # 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.
@@ -1969,7 +1969,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'g-I-PwburVWHBeonmaD-13CmqUNK7ON2TgtGs9hqwpYC',
+        'version': 'hcJkUNq4GNucggif13hh9d7PC4peMyeDEjR9rxUoplsC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -2013,7 +2013,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'C7_AlVVK21i8vPumd7UHvaEn2bnZiserzk1ZupW--xAC',
+        'version': 'OsU5JXgg5Dwp_hNGhKpewhghTUEzP7JAImCtVFeSV-0C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4003,7 +4003,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        'e0a71e95e946c2884c2980332683abf210fd3ffc',
+        'f94bc05bf5ee979b36eb2d58b336e469404524c1',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_form_database_service.cc b/android_webview/browser/aw_form_database_service.cc
index affe917b..b2280ff 100644
--- a/android_webview/browser/aw_form_database_service.cc
+++ b/android_webview/browser/aw_form_database_service.cc
@@ -12,6 +12,7 @@
 #include "base/task/thread_pool.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
+#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
 #include "components/autofill/core/browser/webdata/autocomplete_table.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/webdata/common/webdata_constants.h"
@@ -48,6 +49,7 @@
   // `AwFormDatabaseService::ClearFormData()` also clear Autofill-related data.
   // This is likely a bug.
   // Once crbug.com/1501199 is resolved, all tables can be removed.
+  web_database_->AddTable(std::make_unique<autofill::AddressAutofillTable>());
   web_database_->AddTable(std::make_unique<autofill::AutofillTable>());
   web_database_->LoadDatabase();
 
diff --git a/android_webview/browser/gfx/hardware_renderer.cc b/android_webview/browser/gfx/hardware_renderer.cc
index 8649210..4693ef6 100644
--- a/android_webview/browser/gfx/hardware_renderer.cc
+++ b/android_webview/browser/gfx/hardware_renderer.cc
@@ -893,10 +893,6 @@
     std::vector<viz::ReturnedResource> resources,
     const viz::FrameSinkId& frame_sink_id,
     uint32_t layer_tree_frame_sink_id) {
-  if (!base::FeatureList::IsEnabled(features::kWebViewCheckReturnResources) &&
-      layer_tree_frame_sink_id != last_committed_layer_tree_frame_sink_id_) {
-    return;
-  }
   render_thread_manager_->InsertReturnedResourcesOnRT(
       std::move(resources), frame_sink_id, layer_tree_frame_sink_id);
 }
diff --git a/android_webview/common/aw_features.cc b/android_webview/common/aw_features.cc
index 429a75e1..2b922fc 100644
--- a/android_webview/common/aw_features.cc
+++ b/android_webview/common/aw_features.cc
@@ -16,11 +16,6 @@
              "WebViewBrotliSupport",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Check layer_tree_frame_sink_id when return resources to compositor.
-BASE_FEATURE(kWebViewCheckReturnResources,
-             "WebViewCheckReturnResources",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Whether to destroy the WebView rendering functor when after a WebView window
 // becomes invisible.
 //
diff --git a/android_webview/common/aw_features.h b/android_webview/common/aw_features.h
index a3de562..4b8b2536 100644
--- a/android_webview/common/aw_features.h
+++ b/android_webview/common/aw_features.h
@@ -17,7 +17,6 @@
 
 // Alphabetical:
 BASE_DECLARE_FEATURE(kWebViewBrotliSupport);
-BASE_DECLARE_FEATURE(kWebViewCheckReturnResources);
 BASE_DECLARE_FEATURE(kWebViewConnectionlessSafeBrowsing);
 BASE_DECLARE_FEATURE(kWebViewCheckPakFileDescriptors);
 BASE_DECLARE_FEATURE(kWebViewClearFunctorInBackground);
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 bd2d6a4..fdb67fa 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
@@ -318,6 +318,10 @@
                 AutofillFeatures.AUTOFILL_TEXT_AREA_CHANGE_EVENTS,
                 "When enabled, autofill responds to textarea change events."),
         Flag.baseFeature(
+                AutofillFeatures.AUTOFILL_ENABLE_CACHE_FOR_REGEX_MATCHING,
+                "When enabled, autofill uses an extra cache for matching regular expressions "
+                        + "while executing local heuristics."),
+        Flag.baseFeature(
                 FeatureConstants.KEYBOARD_ACCESSORY_PAYMENT_VIRTUAL_CARD_FEATURE,
                 "When enabled, merchant bound virtual cards will be offered in the keyboard "
                         + "accessory."),
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 400f894..583ac09d 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -1337,11 +1337,6 @@
              "HandwritingLegacyRecognition",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Enables downloading the handwriting libraries via DLC.
-BASE_FEATURE(kHandwritingLibraryDlc,
-             "HandwritingLibraryDlc",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // If enabled, the Help app will render the App Detail Page and entry point.
 BASE_FEATURE(kHelpAppAppDetailPage,
              "HelpAppAppDetailPage",
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index de7c105..e4e897d 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -438,7 +438,6 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kProductivityLauncherImageSearch);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kLauncherItemColorSync);
-COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kHandwritingLibraryDlc);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kHelpAppAppDetailPage);
 COMPONENT_EXPORT(ASH_CONSTANTS)
diff --git a/ash/constants/geolocation_access_level.h b/ash/constants/geolocation_access_level.h
index a38d4c2..11c9302 100644
--- a/ash/constants/geolocation_access_level.h
+++ b/ash/constants/geolocation_access_level.h
@@ -11,7 +11,8 @@
 // Affects the entire ChromeOS system and all client applications.
 // Don't modify or reorder the enum elements. New values can be added at the
 // end. These values shall be in sync with the
-// `DeviceLoginScreenGeolocationAccessLevelProto::GeolocationAccessLevel`.
+// `DeviceLoginScreenGeolocationAccessLevelProto::GeolocationAccessLevel` and
+// //tools/metrics/histograms/metadata/chromeos/enums.xml.
 enum class GeolocationAccessLevel {
   kDisallowed = 0,
   kAllowed = 1,
diff --git a/ash/metrics/login_unlock_throughput_recorder.cc b/ash/metrics/login_unlock_throughput_recorder.cc
index 1b87af97..892857e 100644
--- a/ash/metrics/login_unlock_throughput_recorder.cc
+++ b/ash/metrics/login_unlock_throughput_recorder.cc
@@ -46,9 +46,8 @@
 // A class used to wait for animations.
 class AnimationObserver : public views::BoundsAnimatorObserver {
  public:
-  AnimationObserver(ShelfView* shelf_view, base::OnceClosure& on_animation_end)
-      : shelf_view_(shelf_view),
-        on_animation_end_(std::move(on_animation_end)) {}
+  AnimationObserver(base::OnceClosure& on_animation_end)
+      : on_animation_end_(std::move(on_animation_end)) {}
 
   AnimationObserver(const AnimationObserver&) = delete;
   AnimationObserver& operator=(const AnimationObserver&) = delete;
@@ -58,13 +57,14 @@
   // ShelfViewObserver overrides:
   void OnBoundsAnimatorProgressed(views::BoundsAnimator* animator) override {}
   void OnBoundsAnimatorDone(views::BoundsAnimator* animator) override {
-    shelf_view_->RemoveAnimationObserver(this);
+    GetShelfView()->RemoveAnimationObserver(this);
     RunCallbackAndDestroy();
   }
 
   void StartObserving() {
-    if (shelf_view_->IsAnimating()) {
-      shelf_view_->AddAnimationObserver(this);
+    ShelfView* shelf_view = GetShelfView();
+    if (shelf_view->IsAnimating()) {
+      shelf_view->AddAnimationObserver(this);
       return;
     }
     RunCallbackAndDestroy();
@@ -76,7 +76,15 @@
     delete this;
   }
 
-  raw_ptr<ShelfView, LeakedDanglingUntriaged> shelf_view_;
+  ShelfView* GetShelfView() {
+    return RootWindowController::ForWindow(
+               Shell::Get()->window_tree_host_manager()->GetPrimaryRootWindow())
+        ->shelf()
+        ->hotseat_widget()
+        ->scrollable_shelf_view()
+        ->shelf_view();
+  }
+
   base::OnceClosure on_animation_end_;
 };
 
@@ -483,13 +491,6 @@
     shelf_container->SchedulePaintInRect(bounds);
   }
 
-  ShelfView* shelf_view =
-      RootWindowController::ForWindow(
-          Shell::Get()->window_tree_host_manager()->GetPrimaryRootWindow())
-          ->shelf()
-          ->hotseat_widget()
-          ->scrollable_shelf_view()
-          ->shelf_view();
   base::OnceCallback on_animation_end = base::BindOnce(
       [](base::WeakPtr<LoginUnlockThroughputRecorder> self) {
         self->shelf_animation_finished_ = true;
@@ -507,7 +508,7 @@
       },
       weak_ptr_factory_.GetWeakPtr());
 
-  (new AnimationObserver(shelf_view, on_animation_end))->StartObserving();
+  (new AnimationObserver(on_animation_end))->StartObserving();
 }
 
 void LoginUnlockThroughputRecorder::OnAllExpectedShelfIconsLoaded() {
diff --git a/ash/system/privacy_hub/geolocation_privacy_switch_controller_unittest.cc b/ash/system/privacy_hub/geolocation_privacy_switch_controller_unittest.cc
index 83f8280e..055344288 100644
--- a/ash/system/privacy_hub/geolocation_privacy_switch_controller_unittest.cc
+++ b/ash/system/privacy_hub/geolocation_privacy_switch_controller_unittest.cc
@@ -10,6 +10,7 @@
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/constants/ash_switches.h"
+#include "ash/constants/geolocation_access_level.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -139,17 +140,21 @@
   EXPECT_TRUE(features::IsCrosPrivacyHubLocationEnabled());
   EXPECT_TRUE(controller_);
   controller_->TrackGeolocationAttempted(app_name);
+
+  const auto kGeolocationAccessLevels = {
+      GeolocationAccessLevel::kDisallowed, GeolocationAccessLevel::kAllowed,
+      GeolocationAccessLevel::kOnlyAllowedForSystem};
+  ASSERT_EQ(static_cast<unsigned long>(GeolocationAccessLevel::kMaxValue) + 1,
+            kGeolocationAccessLevels.size());
+
   // We didn't log any notification clicks so far.
-  EXPECT_EQ(histogram_tester_.GetBucketCount(
-                privacy_hub_metrics::
-                    kPrivacyHubGeolocationEnabledFromNotificationHistogram,
-                true),
-            0);
-  EXPECT_EQ(histogram_tester_.GetBucketCount(
-                privacy_hub_metrics::
-                    kPrivacyHubGeolocationEnabledFromNotificationHistogram,
-                false),
-            0);
+  for (auto access_level : kGeolocationAccessLevels) {
+    EXPECT_EQ(0,
+              histogram_tester_.GetBucketCount(
+                  privacy_hub_metrics::
+                      kPrivacyHubGeolocationAccessLevelChangedFromNotification,
+                  access_level));
+  }
   EXPECT_TRUE(FindNotification());
   EXPECT_NE(GetUserPref(), GeolocationAccessLevel::kAllowed);
 
@@ -162,16 +167,18 @@
   EXPECT_FALSE(FindNotification());
 
   // The histograms were updated.
-  EXPECT_EQ(histogram_tester_.GetBucketCount(
-                privacy_hub_metrics::
-                    kPrivacyHubGeolocationEnabledFromNotificationHistogram,
-                true),
-            1);
-  EXPECT_EQ(histogram_tester_.GetBucketCount(
-                privacy_hub_metrics::
-                    kPrivacyHubGeolocationEnabledFromNotificationHistogram,
-                false),
-            0);
+  EXPECT_EQ(1, histogram_tester_.GetBucketCount(
+                   privacy_hub_metrics::
+                       kPrivacyHubGeolocationAccessLevelChangedFromNotification,
+                   GeolocationAccessLevel::kAllowed));
+  EXPECT_EQ(0, histogram_tester_.GetBucketCount(
+                   privacy_hub_metrics::
+                       kPrivacyHubGeolocationAccessLevelChangedFromNotification,
+                   GeolocationAccessLevel::kDisallowed));
+  EXPECT_EQ(0, histogram_tester_.GetBucketCount(
+                   privacy_hub_metrics::
+                       kPrivacyHubGeolocationAccessLevelChangedFromNotification,
+                   GeolocationAccessLevel::kOnlyAllowedForSystem));
 }
 
 }  // namespace ash
diff --git a/ash/system/privacy_hub/privacy_hub_metrics.cc b/ash/system/privacy_hub/privacy_hub_metrics.cc
index 98c0d1f3..32ef841 100644
--- a/ash/system/privacy_hub/privacy_hub_metrics.cc
+++ b/ash/system/privacy_hub/privacy_hub_metrics.cc
@@ -4,33 +4,31 @@
 
 #include "ash/system/privacy_hub/privacy_hub_metrics.h"
 
+#include "ash/system/privacy_hub/privacy_hub_controller.h"
+
 #include "base/metrics/histogram_functions.h"
 
 namespace ash::privacy_hub_metrics {
 
-void LogSensorEnabledFromSettings(Sensor sensor, bool enabled) {
-  const char* histogram = nullptr;
-  switch (sensor) {
-    case Sensor::kCamera: {
-      histogram = kPrivacyHubCameraEnabledFromSettingsHistogram;
-      break;
-    }
-    case Sensor::kMicrophone: {
-      histogram = kPrivacyHubMicrophoneEnabledFromSettingsHistogram;
-      break;
-    }
-    case Sensor::kLocation: {
-      histogram = kPrivacyHubGeolocationEnabledFromSettingsHistogram;
-      break;
-    }
-  }
-  CHECK(histogram);
-  base::UmaHistogramBoolean(histogram, enabled);
-}
-
 void LogSensorEnabledFromNotification(Sensor sensor, bool enabled) {
   const char* histogram = nullptr;
   switch (sensor) {
+    // Location needs to be handled separately as it has 3 states.
+    case Sensor::kLocation: {
+      histogram = kPrivacyHubGeolocationAccessLevelChangedFromNotification;
+
+      // Only collect events that trigger system geolocation state changes. When
+      // `enabled==false` it means the user just dismissed the notification, so
+      // no change has happened.
+      if (enabled) {
+        // Enable events originated from PWAs are processed similarly to ARC++.
+        auto access_level =
+            PrivacyHubController::ArcToCrosGeolocationPermissionMapping(
+                enabled);
+        base::UmaHistogramEnumeration(histogram, access_level);
+      }
+      return;
+    }
     case Sensor::kCamera: {
       histogram = kPrivacyHubCameraEnabledFromNotificationHistogram;
       break;
@@ -39,10 +37,6 @@
       histogram = kPrivacyHubMicrophoneEnabledFromNotificationHistogram;
       break;
     }
-    case Sensor::kLocation: {
-      histogram = kPrivacyHubGeolocationEnabledFromNotificationHistogram;
-      break;
-    }
   }
   CHECK(histogram);
   base::UmaHistogramBoolean(histogram, enabled);
diff --git a/ash/system/privacy_hub/privacy_hub_metrics.h b/ash/system/privacy_hub/privacy_hub_metrics.h
index 7498bd2..db43caca4 100644
--- a/ash/system/privacy_hub/privacy_hub_metrics.h
+++ b/ash/system/privacy_hub/privacy_hub_metrics.h
@@ -29,25 +29,20 @@
   kMaxValue = kGeolocation
 };
 
-static constexpr char kPrivacyHubMicrophoneEnabledFromSettingsHistogram[] =
-    "ChromeOS.PrivacyHub.Microphone.Settings.Enabled";
-static constexpr char kPrivacyHubMicrophoneEnabledFromNotificationHistogram[] =
+inline constexpr char kPrivacyHubMicrophoneEnabledFromNotificationHistogram[] =
     "ChromeOS.PrivacyHub.Microphone.Notification.Enabled";
-static constexpr char kPrivacyHubCameraEnabledFromSettingsHistogram[] =
-    "ChromeOS.PrivacyHub.Camera.Settings.Enabled";
-static constexpr char kPrivacyHubCameraEnabledFromNotificationHistogram[] =
+inline constexpr char kPrivacyHubCameraEnabledFromNotificationHistogram[] =
     "ChromeOS.PrivacyHub.Camera.Notification.Enabled";
-static constexpr char kPrivacyHubGeolocationEnabledFromSettingsHistogram[] =
-    "ChromeOS.PrivacyHub.Geolocation.Settings.Enabled";
-static constexpr char kPrivacyHubGeolocationEnabledFromNotificationHistogram[] =
-    "ChromeOS.PrivacyHub.Geolocation.Notification.Enabled";
-static constexpr char kPrivacyHubOpenedHistogram[] =
+inline constexpr char
+    kPrivacyHubGeolocationAccessLevelChangedFromNotification[] =
+        "ChromeOS.PrivacyHub.Geolocation.AccessLevelChanged."
+        "LocationPermissionNotification";
+inline constexpr char kPrivacyHubOpenedHistogram[] =
     "ChromeOS.PrivacyHub.Opened";
-static constexpr char kPrivacyHubLearnMorePageOpenedHistogram[] =
+inline constexpr char kPrivacyHubLearnMorePageOpenedHistogram[] =
     "ChromeOS.PrivacyHub.LearnMorePage.Opened";
 
-// Report sensor events from system and notifications.
-ASH_EXPORT void LogSensorEnabledFromSettings(Sensor sensor, bool enabled);
+// Report sensor events from notifications.
 ASH_EXPORT void LogSensorEnabledFromNotification(Sensor sensor, bool enabled);
 
 // Report that Privacy Hub has been opened from a notification.
diff --git a/ash/system/privacy_hub/privacy_hub_metrics_unittest.cc b/ash/system/privacy_hub/privacy_hub_metrics_unittest.cc
index bf550aa..3d153dd 100644
--- a/ash/system/privacy_hub/privacy_hub_metrics_unittest.cc
+++ b/ash/system/privacy_hub/privacy_hub_metrics_unittest.cc
@@ -4,16 +4,27 @@
 
 #include "ash/system/privacy_hub/privacy_hub_metrics.h"
 
+#include "ash/constants/geolocation_access_level.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash::privacy_hub_metrics {
+namespace {
+
+const auto kGeolocationAccessLevels = {
+    GeolocationAccessLevel::kDisallowed,
+    GeolocationAccessLevel::kAllowed,
+    GeolocationAccessLevel::kOnlyAllowedForSystem,
+};
+
+}  // namespace
 
 using Sensor = SensorDisabledNotificationDelegate::Sensor;
 
 TEST(PrivacyHubMetricsTest, EnableFromNotification) {
   const base::HistogramTester histogram_tester;
 
+  // Test Microphone and Camera:
   for (const bool enabled : {true, false}) {
     histogram_tester.ExpectBucketCount(
         kPrivacyHubCameraEnabledFromNotificationHistogram, enabled, 0);
@@ -26,37 +37,20 @@
     LogSensorEnabledFromNotification(Sensor::kMicrophone, enabled);
     histogram_tester.ExpectBucketCount(
         kPrivacyHubMicrophoneEnabledFromNotificationHistogram, enabled, 1);
-
-    histogram_tester.ExpectBucketCount(
-        kPrivacyHubGeolocationEnabledFromNotificationHistogram, enabled, 0);
-    LogSensorEnabledFromNotification(Sensor::kLocation, enabled);
-    histogram_tester.ExpectBucketCount(
-        kPrivacyHubGeolocationEnabledFromNotificationHistogram, enabled, 1);
   }
-}
 
-TEST(PrivacyHubMetricsTest, EnableFromSettings) {
-  const base::HistogramTester histogram_tester;
-
-  for (const bool enabled : {true, false}) {
+  // Test Location:
+  // Notification dismissal is not recorded for location access level change.
+  LogSensorEnabledFromNotification(Sensor::kLocation, false);
+  for (auto access_level : kGeolocationAccessLevels) {
     histogram_tester.ExpectBucketCount(
-        kPrivacyHubCameraEnabledFromSettingsHistogram, enabled, 0);
-    LogSensorEnabledFromSettings(Sensor::kCamera, enabled);
-    histogram_tester.ExpectBucketCount(
-        kPrivacyHubCameraEnabledFromSettingsHistogram, enabled, 1);
-
-    histogram_tester.ExpectBucketCount(
-        kPrivacyHubMicrophoneEnabledFromSettingsHistogram, enabled, 0);
-    LogSensorEnabledFromSettings(Sensor::kMicrophone, enabled);
-    histogram_tester.ExpectBucketCount(
-        kPrivacyHubMicrophoneEnabledFromSettingsHistogram, enabled, 1);
-
-    histogram_tester.ExpectBucketCount(
-        kPrivacyHubGeolocationEnabledFromSettingsHistogram, enabled, 0);
-    LogSensorEnabledFromSettings(Sensor::kLocation, enabled);
-    histogram_tester.ExpectBucketCount(
-        kPrivacyHubGeolocationEnabledFromSettingsHistogram, enabled, 1);
+        kPrivacyHubGeolocationAccessLevelChangedFromNotification, access_level,
+        0);
   }
+  LogSensorEnabledFromNotification(Sensor::kLocation, true);
+  histogram_tester.ExpectBucketCount(
+      kPrivacyHubGeolocationAccessLevelChangedFromNotification,
+      GeolocationAccessLevel::kAllowed, 1);
 }
 
 TEST(PrivacyHubMetricsTest, OpenFromNotification) {
diff --git a/ash/webui/media_app_ui/BUILD.gn b/ash/webui/media_app_ui/BUILD.gn
index 0373139..4a4f044 100644
--- a/ash/webui/media_app_ui/BUILD.gn
+++ b/ash/webui/media_app_ui/BUILD.gn
@@ -8,7 +8,6 @@
 import("//build/buildflag_header.gni")
 import("//build/config/chromeos/ui_mode.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
-import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/typescript/ts_library.gni")
 
 assert(is_chromeos_ash, "Media App is ash-chrome only")
@@ -63,7 +62,7 @@
 
   deps = [
     ":media_app_ui",
-    ":test_media_app_guest_ui_browsertest_ts",
+    ":test_build_ts",
     "//ash/public/cpp:cpp",
     "//ash/webui/web_applications/test:test_support",
     "//chrome/test:test_support_ui",
@@ -105,10 +104,10 @@
 # ./gen/resources/js. In order for those paths to be merged, the `ts_library`
 # rules must be here, in the common ancestor. Staging in ./test would trigger
 # `Error: root_dir (...) should be within gen/.../test or ../../.../test`.
-ts_library("test_media_app_guest_ui_browsertest_ts") {
+ts_library("test_build_ts") {
   root_dir = ts_root_dir
   testonly = true
-  in_files = media_app_test_ts + media_app_test_unconverted_js_sources
+  in_files = media_app_test_ts
   definitions = media_app_definitions_staged + media_app_test_definitions_staged
   deps = [ ":build_ts" ]
   extra_deps = [
@@ -121,8 +120,6 @@
 group("closure_compile") {
   testonly = true
   deps = [
-    ":closure_compile_browsertests",
-    ":closure_compile_test_lib",
     "resources/js:closure_compile",
     "resources/mock/js:closure_compile",
   ]
@@ -138,75 +135,6 @@
   deps = [ "//ui/gfx/geometry/mojom:mojom" ]
 }
 
-media_test_lib_closure_flags = system_app_closure_flags_strict + [
-                                 "hide_warnings_for=ash/webui/media_app_ui/media_app_ui.mojom-lite-for-compile.js",
-                                 "js_module_root=" +
-                                     rebase_path(".", root_build_dir),
-                                 "js_module_root=" +
-                                     rebase_path(target_gen_dir,
-                                                 root_build_dir),
-                               ]
-
-# Use relaxed flags for the browsertest files themselves. This removes null
-# checks and "could not determine type" errors which don't add a lot of value.
-media_browsertest_closure_flags = system_app_closure_flags + [
-                                    "hide_warnings_for=ash/webui/media_app_ui/media_app_ui.mojom-lite-for-compile.js",
-                                    "js_module_root=" +
-                                        rebase_path(".", root_build_dir),
-                                    "js_module_root=" +
-                                        rebase_path(target_gen_dir,
-                                                    root_build_dir),
-                                  ]
-
-js_type_check("closure_compile_test_lib") {
-  testonly = true
-  closure_flags = media_test_lib_closure_flags
-  deps = [
-    ":test_driver_api_js",
-    ":test_driver_js",
-  ]
-}
-
-js_type_check("closure_compile_browsertests") {
-  testonly = true
-  closure_flags = media_browsertest_closure_flags
-  deps = [ ":test_media_app_ui_browsertest_js" ]
-}
-
-js_library("test_driver_api_js") {
-  testonly = true
-  externs_list =
-      [ "//ash/webui/web_applications/externs/file_handling.externs.js" ]
-  sources = [ "test/driver_api.js" ]
-}
-
-js_library("test_driver_js") {
-  testonly = true
-  sources = [ "test/driver.js" ]
-  externs_list = [ "//third_party/closure_compiler/externs/chai-3.5.js" ]
-  deps = [
-    ":test_driver_api_js",
-    "//ash/webui/media_app_ui/resources/js:launch",
-    "//ash/webui/system_apps/public/js:message_pipe",
-  ]
-}
-
-js_library("test_worker_js") {
-  testonly = true
-  sources = [ "test/test_worker.js" ]
-}
-
-js_library("test_media_app_ui_browsertest_js") {
-  testonly = true
-  sources = [ "test/media_app_ui_browsertest.js" ]
-  externs_list = [ "//ash/webui/web_applications/js2gtest_support.externs.js" ]
-  deps = [
-    ":test_driver_js",
-    "//ash/webui/media_app_ui/resources/js:launch",
-    "//ash/webui/system_apps/public/js:message_pipe",
-  ]
-}
-
 # Used to turn off tests that only work with our CIPD package e.g. loading ink.
 buildflag_header("buildflags") {
   header = "buildflags.h"
diff --git a/ash/webui/media_app_ui/media_app_files.gni b/ash/webui/media_app_ui/media_app_files.gni
index 7e62e784..66c0f8e7b 100644
--- a/ash/webui/media_app_ui/media_app_files.gni
+++ b/ash/webui/media_app_ui/media_app_files.gni
@@ -9,7 +9,10 @@
 # tsc in the staging folder. These are also copied for rollup.
 media_app_unconverted_js_sources = [
   "app_context_test_support.js",
+  "error_reporter.js",
+  "launch.js",
   "message_types.js",
+  "mojo_api_bootstrap.js",
   "mojo_api_bootstrap_untrusted.js",
   "piex_module_loader.js",
 ]
@@ -25,12 +28,7 @@
 
 # Inputs to rollup with no additional deps. These are also fed to tsc in case
 # they ever get converted to .ts in future.
-media_app_static_js_sources = [
-  "error_reporter.js",
-  "launch.js",
-  "mojo_api_bootstrap.js",
-  "piex_module.js",
-]
+media_app_static_js_sources = [ "piex_module.js" ]
 
 media_app_static_defs = [
   "extra_types.d.ts",
@@ -55,27 +53,18 @@
     [ "$root_gen_dir/ash/webui/system_apps/public/js/message_pipe.d.ts" ]
 media_app_defs_for_external_js_staged = [ "$ts_root_dir/message_pipe.d.ts" ]
 
-# Unconverted test JS sources. Since these may potentially be .ts in future,
-# they must be fed to `ts_library` rules to avoid a .ts file potentially
-# poisioning the build folder when rolling back to an earlier commit. Doing this
-# also copies them to the typescript output folder, which allows the test
-# harness to load everything from the same place.
-media_app_test_unconverted_js_sources = [
-  "driver.js",
-  "driver_api.js",
-  "guest_query_receiver.js",
-  "media_app_ui_browsertest.js",
-  "test_worker.js",
-]
-
 # Testing .d.ts and .ts files that are staged and consumed by tsc.
-media_app_test_ts = [ "media_app_guest_ui_browsertest.ts" ]
-
-media_app_test_defs = [
-  "guest_query_receiver.d.ts",
-  "test_api.d.ts",
+media_app_test_ts = [
+  "driver_api.ts",
+  "driver.ts",
+  "guest_query_receiver.ts",
+  "media_app_guest_ui_browsertest.ts",
+  "media_app_ui_browsertest.ts",
+  "test_worker.ts",
 ]
 
+media_app_test_defs = [ "test_api.d.ts" ]
+
 media_app_test_definitions_staged =
     process_file_template(media_app_test_defs,
                           "$ts_root_dir/{{source_file_part}}")
diff --git a/ash/webui/media_app_ui/resources/js/extra_types.d.ts b/ash/webui/media_app_ui/resources/js/extra_types.d.ts
index 115e64e..2d8957f 100644
--- a/ash/webui/media_app_ui/resources/js/extra_types.d.ts
+++ b/ash/webui/media_app_ui/resources/js/extra_types.d.ts
@@ -15,6 +15,32 @@
   setConsumer(consumer: (params: LaunchParams) => void): void;
 }
 
+interface FilePickerAcceptType {
+  description: string;
+  accept: Record<string, string|string[]>;
+}
+
+interface FilePickerOptions {
+  types: FilePickerAcceptType[];
+  excludeAcceptAllOption?: boolean;
+  id?: string;
+  startIn?: string|FileSystemHandle;
+}
+
+interface OpenFilePickerOptions extends FilePickerOptions {
+  multiple?: boolean;
+}
+
 interface Window {
   readonly launchQueue: LaunchQueue;
+
+  // These are cleared in the untrusted context to prevent accidental usage.
+  // (They are guaranteed to fail).
+  showOpenFilePicker: null|
+      ((options: OpenFilePickerOptions) =>
+           Promise<FileSystemFileHandle|FileSystemFileHandle[]>);
+  showSaveFilePicker: null|
+      ((options: FilePickerOptions) => Promise<FileSystemFileHandle>);
+  showDirectoryPicker: null|
+      ((options: FilePickerOptions) => Promise<FileSystemDirectoryHandle>);
 }
diff --git a/ash/webui/media_app_ui/resources/js/receiver.ts b/ash/webui/media_app_ui/resources/js/receiver.ts
index 2f7106d..d23b895 100644
--- a/ash/webui/media_app_ui/resources/js/receiver.ts
+++ b/ash/webui/media_app_ui/resources/js/receiver.ts
@@ -26,9 +26,8 @@
 /**
  * A file received from the privileged context, and decorated with IPC methods
  * added in the untrusted (this) context to communicate back.
- * @implements {mediaApp.AbstractFile}
  */
-class ReceivedFile implements AbstractFile {
+export class ReceivedFile implements AbstractFile {
   blob: Blob;
   name: string;
   token: number;
@@ -184,7 +183,7 @@
   length: number;
   currentFileIndex: number;
 
-  private files: ReceivedFile[];
+  files: ReceivedFile[];  // Public for tests.
   private observers: Array<(files: AbstractFileList) => unknown> = [];
 
   constructor(filesMessage: LoadFilesMessage) {
@@ -418,9 +417,6 @@
 declare global {
   interface Window {
     chooseFileSystemEntries: null;
-    showOpenFilePicker: null;
-    showSaveFilePicker: null;
-    showDirectoryPicker: null;
     addColorChangeListener: (listener: EventListenerOrEventListenerObject|
                              null) => unknown;
     removeColorChangeListener: (listener: EventListenerOrEventListenerObject|
diff --git a/ash/webui/media_app_ui/test/BUILD.gn b/ash/webui/media_app_ui/test/BUILD.gn
index b5768bd..c489a0a5 100644
--- a/ash/webui/media_app_ui/test/BUILD.gn
+++ b/ash/webui/media_app_ui/test/BUILD.gn
@@ -8,7 +8,6 @@
 assert(is_chromeos_ash)
 
 copy("stage_for_test_tsc") {
-  sources = media_app_test_ts + media_app_test_defs +
-            media_app_test_unconverted_js_sources
+  sources = media_app_test_ts + media_app_test_defs
   outputs = [ ts_root_dir + "/{{source_file_part}}" ]
 }
diff --git a/ash/webui/media_app_ui/test/driver.js b/ash/webui/media_app_ui/test/driver.ts
similarity index 62%
rename from ash/webui/media_app_ui/test/driver.js
rename to ash/webui/media_app_ui/test/driver.ts
index 4d75406..cdebdf42 100644
--- a/ash/webui/media_app_ui/test/driver.js
+++ b/ash/webui/media_app_ui/test/driver.ts
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {FileSnapshot, LastLoadedFilesResponse, TestMessageQueryData, TestMessageResponseData, TestMessageRunTestCase} from './driver_api.js';
 import {TEST_ONLY} from './launch.js';
 
 const {
@@ -16,7 +17,7 @@
 } = TEST_ONLY;
 
 // See message_pipe.js.
-function assertCast(condition) {
+function assertCast<A>(condition: A): NonNullable<A> {
   if (!condition) {
     throw new Error('Failed assertion');
   }
@@ -26,7 +27,6 @@
 /**
  * Promise that signals the guest is ready to receive test messages (in addition
  * to messages handled by receiver.js).
- * @type {!Promise<undefined>}
  */
 const testMessageHandlersReady = new Promise(resolve => {
   guestMessagePipe.registerHandler('test-handlers-ready', resolve);
@@ -44,13 +44,12 @@
    * @return Promise<string> JSON.stringify()'d value of the property, or
    *   tagName if unspecified.
    */
-  async waitForElementInGuest(query, opt_property, opt_commands = {}) {
-    /** @type {!TestMessageQueryData} */
-    const message = {testQuery: query, property: opt_property};
+  async waitForElementInGuest(
+      query: string, property?: string, commands: Object = {}) {
+    const message: TestMessageQueryData = {testQuery: query, property};
     await testMessageHandlersReady;
-    const result = /** @type {!TestMessageResponseData} */ (
-        await guestMessagePipe.sendMessage(
-            'test', {...message, ...opt_commands}));
+    const result: TestMessageResponseData =
+        await guestMessagePipe.sendMessage('test', {...message, ...commands});
     return result.testQueryResult;
   }
 }
@@ -59,9 +58,8 @@
  * Runs the given `testCase` in the guest context.
  * @param {string} testCase
  */
-export async function runTestInGuest(testCase) {
-  /** @type {!TestMessageRunTestCase} */
-  const message = {testCase};
+export async function runTestInGuest(testCase: string) {
+  const message: TestMessageRunTestCase = {testCase};
   await testMessageHandlersReady;
   await guestMessagePipe.sendMessage('run-test-case', message);
 }
@@ -70,10 +68,10 @@
  * @param {!Object=} data
  * @return {!Promise<!TestMessageResponseData>}
  */
-export async function sendTestMessage(data = undefined) {
+export async function sendTestMessage(data?: Object):
+    Promise<TestMessageResponseData> {
   await testMessageHandlersReady;
-  return /** @type {!Promise<!TestMessageResponseData>} */ (
-      guestMessagePipe.sendMessage('test', data));
+  return guestMessagePipe.sendMessage('test', data);
 }
 
 /**
@@ -81,37 +79,38 @@
  * currently open file is always at index 0.
  * @return {!Promise<string>}
  */
-export async function getFileErrors() {
+export async function getFileErrors(): Promise<string> {
   await testMessageHandlersReady;
   const message = {getFileErrors: true};
-  const response = /** @type {!TestMessageResponseData} */ (
-      await guestMessagePipe.sendMessage('test', message));
+  const response: TestMessageResponseData =
+      await guestMessagePipe.sendMessage('test', message);
   return response.testQueryResult;
 }
 
 export class FakeWritableFileSink {
-  constructor(/** !Blob= */ data = new Blob()) {
-    this.data = data;
+  writes: Array<{position: number, size?: number}> = [];
+  resolveClose!: (blob: Blob) => void;
+  closePromise = new Promise<Blob>(resolve => {
+    this.resolveClose = resolve;
+  });
 
-    /** @type {!Array<{position: number, size: (number|undefined)}>} */
-    this.writes = [];
+  constructor(public data = new Blob()) {}
 
-    /** @type {function(!Blob)} */
-    this.resolveClose;
 
-    this.closePromise = new Promise((/** function(!Blob) */ resolve) => {
-      this.resolveClose = resolve;
-    });
-  }
   /** @param {?BufferSource|!Blob|string|!WriteParams} data */
-  async write(data) {
+  async write(dataParam: BufferSource|Blob|string|WriteParams) {
     const position = 0;  // Assume no seeks.
-    if (!data) {
+    if (!dataParam) {
       this.writes.push({position, size: 0});
       return;
     }
+    interface HasLengthOrSize {
+      size?: number;
+      length: number;
+    }
+    const data = dataParam as BlobPart & HasLengthOrSize;
     const dataSize = data.size === undefined ? data.length : data.size;
-    this.writes.push({position, size: /** @type {number} */ (dataSize)});
+    this.writes.push({position, size: dataSize});
     this.data = new Blob([
       this.data.slice(0, position),
       data,
@@ -119,7 +118,7 @@
     ]);
   }
   /** @param {number} size */
-  async truncate(size) {
+  async truncate(size: number) {
     this.data = this.data.slice(0, size);
   }
   /** Resolves the close promise. */
@@ -127,60 +126,37 @@
     this.resolveClose(this.data);
   }
   /** @param {number} offset */
-  async seek(offset) {
+  async seek(_offset: number) {
     throw new Error('seek() not implemented.');
   }
 }
 
 /** @implements FileSystemHandle  */
-export class FakeFileSystemHandle {
-  /**
-   * @param {string=} name
-   */
-  constructor(name = 'fake_file.png') {
-    this.kind = 'file';
-    this.name = name;
-  }
-  /** @override */
-  async isSameEntry(other) {
+export class FakeFileSystemHandle implements FileSystemHandle {
+  kind: FileSystemHandleKind = 'file';
+  constructor(public name: string = 'fake_file.png') {}
+  async isSameEntry(other: FileSystemHandle): Promise<boolean> {
     return this === other;
   }
-  /** @override */
-  async queryPermission(descriptor) {}
-  /** @override */
-  async requestPermission(descriptor) {}
 }
 
 /** @implements FileSystemFileHandle  */
-export class FakeFileSystemFileHandle extends FakeFileSystemHandle {
-  /**
-   * @param {string=} name
-   * @param {string=} type
-   * @param {number=} lastModified
-   * @param {!Blob=} blob
-   */
+export class FakeFileSystemFileHandle extends FakeFileSystemHandle implements
+    FileSystemFileHandle {
+  override kind: 'file' = 'file';
+  lastWritable: FakeWritableFileSink;
+  nextCreateWritableError?: DOMException|Error;
+
+  /** Used simulate an error thrown from directory traversal. */
+  errorToFireOnIterate?: null|DOMException;
+
   constructor(
-      name = 'fake_file.png', type = '', lastModified = 0, blob = new Blob()) {
+      name = 'fake_file.png', public type: string = '',
+      public lastModified: number = 0, blob: Blob = new Blob()) {
     super(name);
     this.lastWritable = new FakeWritableFileSink(blob);
-
-    /** @type {string} */
-    this.type = type;
-
-    /** @type {number} */
-    this.lastModified = lastModified;
-
-    /** @type {!DOMException|!Error|undefined} */
-    this.nextCreateWritableError;
-
-    /**
-     * Used simulate an error thrown from directory traversal.
-     * @type {?DOMException|undefined}
-     */
-    this.errorToFireOnIterate;
   }
-  /** @override */
-  async createWritable(options) {
+  async createWritable(_options: FileSystemCreateWritableOptions) {
     if (this.nextCreateWritableError) {
       throw this.nextCreateWritableError;
     }
@@ -189,9 +165,10 @@
 
     // The FileSystemWritableFileStream supports both streams and direct writes.
     // Splice on the direct writing capabilities by delegating to the sink.
-    const writable = /** @type {!FileSystemWritableFileStream} */ (stream);
-    writable.write = (data) => sink.write(data);
-    writable.truncate = (size) => sink.truncate(size);
+    const writable = stream as FileSystemWritableFileStream;
+    writable.write = (data: BufferSource|Blob|string|WriteParams) =>
+        sink.write(data);
+    writable.truncate = (size: number) => sink.truncate(size);
     writable.close = () => sink.close();
     return writable;
   }
@@ -209,40 +186,36 @@
 }
 
 /** @implements FileSystemDirectoryHandle  */
-export class FakeFileSystemDirectoryHandle extends FakeFileSystemHandle {
-  /**
-   * @param {string=} name
-   */
+export class FakeFileSystemDirectoryHandle extends FakeFileSystemHandle
+    implements FileSystemDirectoryHandle {
+  override kind: 'directory' = 'directory';
+
+  /** Internal state mocking file handles in a directory handle. */
+  files: FakeFileSystemFileHandle[] = [];
+
+  /** Used to spy on the last deleted file. */
+  lastDeleted?: null|FakeFileSystemFileHandle = null;
+
   constructor(name = 'fake-dir') {
     super(name);
-    this.kind = 'directory';
-    /**
-     * Internal state mocking file handles in a directory handle.
-     * @type {!Array<!FakeFileSystemFileHandle>}
-     */
-    this.files = [];
-    /**
-     * Used to spy on the last deleted file.
-     * @type {?FakeFileSystemFileHandle}
-     */
-    this.lastDeleted = null;
   }
+
   /**
    * Use to populate `FileSystemFileHandle`s for tests.
    * @param {!FakeFileSystemFileHandle} fileHandle
    */
-  addFileHandleForTest(fileHandle) {
+  addFileHandleForTest(fileHandle: FakeFileSystemFileHandle) {
     this.files.push(fileHandle);
   }
   /**
    * Helper to get all entries as File.
    * @return {!Array<!File>}
    */
-  getFilesSync() {
+  getFilesSync(): File[] {
     return this.files.map(f => f.getFileSync());
   }
-  /** @override */
-  async getFileHandle(name, options) {
+
+  async getFileHandle(name: string, options?: FileSystemGetFileOptions) {
     const fileHandle = this.files.find(f => f.name === name);
     if (!fileHandle && options && options.create === true) {
       // Simulate creating a new file, assume it is an image. This is needed for
@@ -257,14 +230,18 @@
                             'NotFoundError', `File ${name} not found`)));
   }
   /** @override */
-  getDirectoryHandle(name, options) {}
+  async getDirectoryHandle(
+      _name: string, _options?: FileSystemGetDirectoryOptions):
+      Promise<FakeFileSystemDirectoryHandle> {
+    throw new Error('Not implemented');
+  }
   /**
    * @override
    * @return {!AsyncIterable<!Array<string|!FileSystemHandle>>}
    * @suppress {reportUnknownTypes} suppress [JSC_UNKNOWN_EXPR_TYPE] for `yield
    * [file.name, file]`.
    */
-  async * entries() {
+  async * entries(): AsyncIterable<Array<string|FileSystemHandle>> {
     for (const file of this.files) {
       yield [file.name, file];
     }
@@ -275,18 +252,12 @@
    * @suppress {reportUnknownTypes} suppress [JSC_UNKNOWN_EXPR_TYPE] for `yield
    * file.name`.
    */
-  async * keys() {
+  async * keys(): AsyncIterable<string> {
     for (const file of this.files) {
       yield file.name;
     }
   }
-  /**
-   * @override
-   * @return {!AsyncIterable<!FileSystemHandle>}
-   * @suppress {reportUnknownTypes} suppress [JSC_UNKNOWN_EXPR_TYPE] for `yield
-   * file`.
-   */
-  async * values() {
+  async * values(): AsyncIterable<FileSystemHandle> {
     for (const file of this.files) {
       if (file.errorToFireOnIterate) {
         const error = file.errorToFireOnIterate;
@@ -297,7 +268,7 @@
     }
   }
   /** @override */
-  async removeEntry(name, options) {
+  async removeEntry(name: string, _options: FileSystemRemoveOptions) {
     // Remove file handle from internal state.
     const fileHandleIndex = this.files.findIndex(f => f.name === name);
     // Store the file removed for spying in tests.
@@ -319,16 +290,22 @@
  *   arrayBuffer: (function(): (!Promise<!ArrayBuffer>)|undefined)
  * }}
  */
-export let FileDesc;
+export interface FileDesc {
+  name?: string;
+  type?: string;
+  lastModified?: number;
+  arrayBuffer?: () => Promise<ArrayBuffer>;
+}
 
 /**
  * Creates a mock directory with the provided files in it.
  * @param {!Array<!FileDesc>=} files
  * @return {!Promise<!FakeFileSystemDirectoryHandle>}
  */
-export async function createMockTestDirectory(files = [{}]) {
+export async function createMockTestDirectory(files: FileDesc[] = [{}]):
+    Promise<FakeFileSystemDirectoryHandle> {
   const directory = new FakeFileSystemDirectoryHandle();
-  for (const /** !FileDesc */ file of files) {
+  for (const file of files) {
     const fileBlob = file.arrayBuffer !== undefined ?
         new Blob([await file.arrayBuffer()]) :
         new Blob();
@@ -340,81 +317,69 @@
 
 /**
  * Creates a mock LaunchParams object from the provided `files`.
- * @param {!Array<!FileSystemHandle>} files
- * @return {!LaunchParams}
  */
-export function handlesToLaunchParams(files) {
-  return /** @type{!LaunchParams} */ ({files});
+export function handlesToLaunchParams(files: FileSystemHandle[]): LaunchParams {
+  return {files};
 }
 
 /**
  * Helper to "launch" with the given `directoryContents`. Populates a fake
  * directory containing those handles, then launches the app. The focus file is
  * either the first file in `multiSelectionFiles`, or the first directory entry.
- * @param {!Array<!FakeFileSystemFileHandle>} directoryContents
- * @param {!Array<!FakeFileSystemFileHandle>=} multiSelectionFiles If provided,
+ * @param multiSelectionFiles If provided,
  *     holds additional files selected in the files app at launch time.
- * @return {!Promise<!FakeFileSystemDirectoryHandle>}
  */
 export async function launchWithHandles(
-    directoryContents, multiSelectionFiles = []) {
+    directoryContents: FakeFileSystemFileHandle[],
+    multiSelectionFiles: FakeFileSystemFileHandle[] =
+        []): Promise<FakeFileSystemDirectoryHandle> {
   await testMessageHandlersReady;
 
-  /** @type {?FakeFileSystemFileHandle} */
   let focusFile = multiSelectionFiles[0];
   if (!focusFile) {
-    focusFile = directoryContents[0];
+    focusFile = directoryContents[0]!;
   }
   multiSelectionFiles = multiSelectionFiles.slice(1);
   const directory = new FakeFileSystemDirectoryHandle();
   for (const handle of directoryContents) {
     directory.addFileHandleForTest(handle);
   }
-  const files = [directory, focusFile, ...multiSelectionFiles];
+  const files: FileSystemHandle[] =
+      [directory, focusFile, ...multiSelectionFiles];
   await launchConsumer(handlesToLaunchParams(files));
   return directory;
 }
 
 /**
  * Wraps a file in a FakeFileSystemFileHandle.
- * @param {!File} file
- * @return {!FakeFileSystemFileHandle}
  */
-export function fileToFileHandle(file) {
+export function fileToFileHandle(file: File): FakeFileSystemFileHandle {
   return new FakeFileSystemFileHandle(
       file.name, file.type, file.lastModified, file);
 }
 
 /**
  * Helper to invoke launchWithHandles after wrapping `files` in fake handles.
- * @param {!Array<!File>} files
- * @param {!Array<number>=} selectedIndexes
- * @return {!Promise<!FakeFileSystemDirectoryHandle>}
  */
-export async function launchWithFiles(files, selectedIndexes = []) {
+export async function launchWithFiles(
+    files: File[],
+    selectedIndexes: number[] = []): Promise<FakeFileSystemDirectoryHandle> {
   const fileHandles = files.map(fileToFileHandle);
-  const selection =
-      selectedIndexes.map((/** @type {number} */ i) => fileHandles[i]);
+  const selection = selectedIndexes.map(i => fileHandles[i]!);
   return launchWithHandles(fileHandles, selection);
 }
 
 /**
  * Creates an `Error` with the name field set.
- * @param {string} name
- * @param {string} msg
- * @return {!Error}
  */
-export function createNamedError(name, msg) {
+export function createNamedError(name: string, msg: string): Error {
   const error = new Error(msg);
   error.name = name;
   return error;
 }
 
-/**
- * @param {!FileSystemDirectoryHandle} directory
- * @param {!File} file
- */
-export async function loadFilesWithoutSendingToGuest(directory, file) {
+export async function loadFilesWithoutSendingToGuest(
+    directory: FileSystemDirectoryHandle, file: File) {
   const handle = await directory.getFileHandle(file.name);
   const launchNumber = incrementLaunchNumber();
   setCurrentDirectory(directory, {file, handle});
@@ -424,20 +389,18 @@
 /**
  * Checks that the `currentFiles` array maintained by launch.js has the same
  * sequence of files as `expectedFiles`.
- * @param {!Array<!File>} expectedFiles
- * @param {string=} testCase
  */
-export function assertFilesToBe(expectedFiles, testCase = undefined) {
-  assertFilenamesToBe(expectedFiles.map(f => f.name).join(), testCase);
+export function assertFilesToBe(
+    expectedFiles: Array<undefined|{name: string}>, testCase?: string) {
+  assertFilenamesToBe(expectedFiles.map(f => f!.name).join(), testCase);
 }
 
 /**
  * Checks that the `currentFiles` array maintained by launch.js has the same
  * sequence of filenames as `expectedFilenames`.
- * @param {string} expectedFilenames
- * @param {string=} testCase
  */
-export function assertFilenamesToBe(expectedFilenames, testCase = undefined) {
+export function assertFilenamesToBe(
+    expectedFilenames: string, testCase?: string) {
   // Use filenames as an approximation of file uniqueness.
   const currentFilenames = currentFiles.map(d => d.handle.name).join();
   chai.assert.equal(
@@ -448,25 +411,25 @@
 
 /**
  * Wraps `chai.assert.match` allowing tests to use `assertMatch`.
- * @param {string} string the string to match
- * @param {string} regex an escaped regex compatible string
- * @param {string=} opt_message logged if the assertion fails
+ * @param string the string to match
+ * @param regex an escaped regex compatible string
+ * @param message logged if the assertion fails
  */
-export function assertMatch(string, regex, opt_message = undefined) {
-  chai.assert.match(string, new RegExp(regex), opt_message);
+export function assertMatch(string: string, regex: string, message?: string) {
+  chai.assert.match(string, new RegExp(regex), message);
 }
 
 /**
  * Returns the files loaded in the most recent call to `loadFiles()`.
- * @return {!Promise<?Array<!FileSnapshot>>}
  */
-export async function getLoadedFiles() {
-  const response = /** @type {!LastLoadedFilesResponse} */ (
-      await guestMessagePipe.sendMessage('get-last-loaded-files'));
+export async function getLoadedFiles(): Promise<FileSnapshot[]> {
+  const response: LastLoadedFilesResponse =
+      await guestMessagePipe.sendMessage('get-last-loaded-files');
   if (response.fileList) {
     return response.fileList;
   }
-  return null;
+  // No callers currently want this to return null.
+  throw new Error('No last loaded files');
 }
 
 /**
@@ -478,31 +441,25 @@
   setCurrentDirectoryHandle(null);
 }
 
-/**
- * @param {!FakeFileSystemDirectoryHandle} directory
- * @return {{handle: !FakeFileSystemFileHandle, file: !File}}
- */
-export function launchWithFocusFile(directory) {
+export function launchWithFocusFile(directory: FakeFileSystemDirectoryHandle):
+    {handle: FakeFileSystemFileHandle, file: File} {
+  const firstFile = assertCast(directory.files[0]);
   const focusFile = {
-    /** @type {!FakeFileSystemFileHandle} */
-    handle: directory.files[0],
-    file: directory.files[0].getFileSync(),
+    handle: firstFile,
+    file: firstFile.getFileSync(),
   };
   incrementLaunchNumber();
   setCurrentDirectory(directory, focusFile);
   return focusFile;
 }
 
-/**
- * @param {!FakeFileSystemDirectoryHandle} directory
- * @param {number} totalFiles
- */
-export async function assertSingleFileLaunch(directory, totalFiles) {
+export async function assertSingleFileLaunch(
+    directory: FakeFileSystemDirectoryHandle, totalFiles: number) {
   chai.assert.equal(1, currentFiles.length);
 
   await sendFilesToGuest();
 
-  const loadedFiles = assertCast(await getLoadedFiles());
+  const loadedFiles = await getLoadedFiles();
   // The untrusted context only loads the first file.
   chai.assert.equal(1, loadedFiles.length);
   // All files are in the `FileSystemDirectoryHandle`.
@@ -512,16 +469,14 @@
 /**
  * Check files loaded in the trusted context `currentFiles` against the working
  * directory and the untrusted context.
- * @param {!FakeFileSystemDirectoryHandle} directory
- * @param {!Array<string>} fileNames
- * @param {string=} testCase
  */
 export async function assertFilesLoaded(
-    directory, fileNames, testCase = undefined) {
+    directory: FakeFileSystemDirectoryHandle, fileNames: string[],
+    testCase?: string) {
   chai.assert.equal(fileNames.length, directory.files.length);
   chai.assert.equal(fileNames.length, currentFiles.length);
 
-  const loadedFiles = /** @type {!Array<!File>} */ (await getLoadedFiles());
+  const loadedFiles = await getLoadedFiles();
   chai.assert.equal(fileNames.length, loadedFiles.length);
 
   // Check `currentFiles` in the trusted context matches up with files sent
diff --git a/ash/webui/media_app_ui/test/driver_api.js b/ash/webui/media_app_ui/test/driver_api.js
deleted file mode 100644
index 3af142d..0000000
--- a/ash/webui/media_app_ui/test/driver_api.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * Reply to test messages. Contents depend on the test message sent.
- * @typedef {{
- *     testQueryResult: string,
- *     testQueryResultData: (!Object|undefined)
- * }}
- */
-let TestMessageResponseData;
-
-/**
- * Object sent over postMessage to run a command or extract data.
- * @typedef {{
- *     deleteLastFile: (boolean|undefined),
- *     getFileErrors: (boolean|undefined),
- *     navigate: ({direction: string, token: number}|undefined),
- *     openFile: (boolean|undefined),
- *     overwriteLastFile: (string|undefined),
- *     rethrow: (boolean|undefined),
- *     pathToRoot: (!Array<string>|undefined),
- *     property: (string|undefined),
- *     renameLastFile: (string|undefined),
- *     requestFullscreen: (boolean|undefined),
- *     requestSaveFile: (boolean|undefined),
- *     saveAs: (string|undefined),
- *     simple: (string|undefined),
- *     simpleArgs: (Object|undefined),
- *     suppressCrashReports: (boolean|undefined),
- *     testQuery: string,
- * }}
- */
-let TestMessageQueryData;
-
-/** @typedef {{testCase: string}} */
-let TestMessageRunTestCase;
-
-/**
- * Subset of mediaApp.AbstractFile that can be serialized. The fields
- * `hasDelete` and `hasRename` indicate whether the methods are defined.
- * @typedef {{
- *    blob: !Blob,
- *    name: string,
- *    size: number,
- *    mimeType: string,
- *    fromClipboard: (boolean|undefined),
- *    error: (string|undefined),
- *    token: (number|undefined),
- *    lastModified: number,
- *    hasDelete: boolean,
- *    hasRename: boolean,
- * }}
- */
-let FileSnapshot;
-
-/**
- * Return type of `get-last-loaded-files` used to spy on the files sent to the
- * guest app using `loadFiles()`.
- * @typedef {{fileList: ?Array<!FileSnapshot>}}
- */
-let LastLoadedFilesResponse;
diff --git a/ash/webui/media_app_ui/test/driver_api.ts b/ash/webui/media_app_ui/test/driver_api.ts
new file mode 100644
index 0000000..85fd11a9
--- /dev/null
+++ b/ash/webui/media_app_ui/test/driver_api.ts
@@ -0,0 +1,62 @@
+// 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.
+
+/**
+ * Reply to test messages. Contents depend on the test message sent.
+ */
+export interface TestMessageResponseData {
+  testQueryResult: string;
+  testQueryResultData?: any;
+}
+
+/**
+ * Object sent over postMessage to run a command or extract data.
+ */
+export interface TestMessageQueryData {
+  deleteLastFile?: boolean;
+  getFileErrors?: boolean;
+  navigate?: {direction?: string, token?: number};
+  openFile?: boolean;
+  overwriteLastFile?: string;
+  rethrow?: boolean;
+  pathToRoot?: string[];
+  property?: string;
+  renameLastFile?: string;
+  requestFullscreen?: boolean;
+  requestSaveFile?: boolean;
+  saveAs?: string;
+  simple?: string;
+  simpleArgs?: any;
+  suppressCrashReports?: boolean;
+  testQuery: string;
+}
+
+export interface TestMessageRunTestCase {
+  testCase: string;
+}
+
+/**
+ * Subset of mediaApp.AbstractFile that can be serialized. The fields
+ * `hasDelete` and `hasRename` indicate whether the methods are defined.
+ */
+export interface FileSnapshot {
+  blob: Blob;
+  name: string;
+  size: number;
+  mimeType: string;
+  fromClipboard?: boolean;
+  error?: string;
+  token?: number;
+  lastModified: number;
+  hasDelete: boolean;
+  hasRename: boolean;
+}
+
+/**
+ * Return type of `get-last-loaded-files` used to spy on the files sent to the
+ * guest app using `loadFiles()`.
+ */
+export interface LastLoadedFilesResponse {
+  fileList: FileSnapshot[];
+}
diff --git a/ash/webui/media_app_ui/test/guest_query_receiver.d.ts b/ash/webui/media_app_ui/test/guest_query_receiver.d.ts
deleted file mode 100644
index 371b7cc..0000000
--- a/ash/webui/media_app_ui/test/guest_query_receiver.d.ts
+++ /dev/null
@@ -1,12 +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.
-
-/**
- * @fileoverview
- * Temporary export until guest_query_receiver.js is converted to TS. The
- * generated .d.ts doesn't work due to an undefined/void mismatch.
- */
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export function GUEST_TEST(
-    testName: string, testCase: () => void|Promise<void>): void;
diff --git a/ash/webui/media_app_ui/test/guest_query_receiver.js b/ash/webui/media_app_ui/test/guest_query_receiver.ts
similarity index 72%
rename from ash/webui/media_app_ui/test/guest_query_receiver.js
rename to ash/webui/media_app_ui/test/guest_query_receiver.ts
index 35bf0e6..3b8b241 100644
--- a/ash/webui/media_app_ui/test/guest_query_receiver.js
+++ b/ash/webui/media_app_ui/test/guest_query_receiver.ts
@@ -2,40 +2,42 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Note we can only import from 'receiver.js': other modules are rolled-up into
-// it, and already loaded.
-import {TEST_ONLY} from './receiver.js';
+/// <reference path="media_app.d.ts" />
 
-/**
- * @typedef {{
- *     name: string,
- *     message: string,
- *     stack: string,
- * }}
- */
-let GenericErrorResponse;
+import {FileSnapshot, LastLoadedFilesResponse, TestMessageQueryData, TestMessageResponseData, TestMessageRunTestCase} from './driver_api.js';
+import {ReceivedFileList, TEST_ONLY} from './receiver.js';
+
+interface GenericErrorResponse {
+  name: string;
+  message: string;
+  stack: string;
+}
 
 const {
   RenameResult,
   DELEGATE,
-  assertCast,
   parentMessagePipe,
   loadFiles,
   setLoadFiles,
 } = TEST_ONLY;
 
 /**
- * The last file list loaded into the guest, updated via a spy on loadFiles().
- * TODO(b/185734620): This should be type {ReceivedFileList} but closure fails
- * to resolve it properly. See b/185734620 for details.
+ * TODO(b/314827247): Remove this when imports are converted to TypeScript. This
+ * is needed because the generated .d.ts doesn't capture non-nullability.
  */
-let lastLoadedFileList = null;
+function assertCast<A>(arg: A): NonNullable<A> {
+  return TEST_ONLY.assertCast(arg)!;
+}
+
+/**
+ * The last file list loaded into the guest, updated via a spy on loadFiles().
+ */
+let lastLoadedFileList: ReceivedFileList|null = null;
 
 /**
  * Test cases registered by GUEST_TEST.
- * @type {!Map<string, function(): (!Promise<undefined>|undefined)>}
  */
-const guestTestCases = new Map();
+const guestTestCases = new Map<string, () => unknown>();
 
 /**
  * Returns the last file list passed to the guest context over the message pipe.
@@ -47,7 +49,7 @@
  * the received file list on `window.customLaunchData.files`. Support either.
  * This only affects tests: `launchConsumer` cannot be awaited in the real app.
  */
-function assertLastReceivedFileList() {
+function assertLastReceivedFileList(): ReceivedFileList {
   if (lastLoadedFileList) {
     return lastLoadedFileList;
   }
@@ -55,34 +57,27 @@
     throw new Error('No file list received.');
   }
   console.log('Note: app not loaded. Returning customLaunchData.files.');
-  return window.customLaunchData.files;
+  return window.customLaunchData.files as ReceivedFileList;
 }
 
-/**
- * @return {!Array<!mediaApp.AbstractFile>}
- */
-function assertLastReceivedFileArray() {
+function assertLastReceivedFileArray(): AbstractFile[] {
   const fileList = assertLastReceivedFileList();
-  return Array.from({length: fileList.length}, (v, k) => fileList.item(k));
+  return Array.from({length: fileList.length}, (_, k) => fileList.item(k)) as
+      AbstractFile[];
 }
 
-/**
- * @return {!mediaApp.AbstractFile}
- */
-function currentFile() {
+function currentFile(): AbstractFile {
   const fileList = assertLastReceivedFileList();
-  return assertCast(fileList.item(fileList.currentFileIndex));
+  return assertCast(fileList.item(fileList.currentFileIndex))!;
 }
 
 /**
  * Flatten out primitives from the file object for transfer over message pipe.
- * @param {!mediaApp.AbstractFile} file
- * @return {!FileSnapshot}
  */
-function flattenFile(file) {
+function flattenFile(file: AbstractFile): FileSnapshot {
   const hasDelete = !!file.deleteOriginalFile;
   const hasRename = !!file.renameOriginalFile;
-  const lastModified = /** @type{!File} */ (file.blob).lastModified;
+  const lastModified = (file.blob as File).lastModified;
   const {blob, name, size, mimeType, fromClipboard, error, token} = file;
   return {
     blob,
@@ -98,13 +93,14 @@
   };
 }
 
+type QueryHandler = (data: TestMessageQueryData, arg: any) =>
+    Promise<string>|string;
+
 /**
  * Handlers for simple tests run in the guest that return a string result.
- * @type{!Object<string, function(!TestMessageQueryData, !Object):
- * Promise<string>>}
  */
-const SIMPLE_TEST_QUERIES = {
-  requestSaveFile: async (data, resultData) => {
+const SIMPLE_TEST_QUERIES: {[key: string]: QueryHandler} = {
+  requestSaveFile: async (data, _resultData) => {
     // Call requestSaveFile on the delegate.
     const existingFile = assertLastReceivedFileList().item(0);
     if (!existingFile) {
@@ -115,44 +111,45 @@
         data.simpleArgs ? data.simpleArgs.accept : []);
     return assertCast(pickedFile.token).toString();
   },
-  getExportFile: async (data, resultData) => {
+  getExportFile: async (data, _resultData) => {
     const existingFile = assertLastReceivedFileList().item(0);
     if (!existingFile) {
       return 'getExportFile failed, no file loaded';
     }
-    const pickedFile = await existingFile.getExportFile(data.simpleArgs.accept);
-    return pickedFile.token.toString();
+    const pickedFile =
+        await existingFile.getExportFile!(data.simpleArgs.accept);
+    return pickedFile.token!.toString();
   },
-  getLastFile: async (data, resultData) => {
+  getLastFile: async (_data, resultData) => {
     Object.assign(resultData, flattenFile(currentFile()));
     return resultData.name;
   },
-  getAllFiles: async (data, resultData) => {
+  getAllFiles: async (_data, resultData) => {
     Object.assign(resultData, assertLastReceivedFileArray().map(flattenFile));
     return `${resultData.length}`;
   },
-  notifyCurrentFile: (data, resultData) => {
+  notifyCurrentFile: (data, _resultData) => {
     DELEGATE.notifyCurrentFile(data.simpleArgs.name, data.simpleArgs.type);
+    return 'notified';
   },
-  openFileAtIndex: async (data, resultData) => {
+  openFileAtIndex: async (data, _resultData) => {
     const handle = assertLastReceivedFileList().item(data.simpleArgs.index);
-    const domFile = await handle.openFile();
-    handle.updateFile(domFile, domFile.name);
+    const domFile = await handle!.openFile();
+    // Cast to any to access private method.
+    (handle as any).updateFile(domFile, domFile.name);
     return 'opened and updated';
   },
-  openFilesWithFilePicker: async (data, resultData) => {
-    /**
-     * @typedef {{
-     *   acceptTypeKeys: !Array<string>,
-     *   explicitToken: (number|undefined),
-     *   singleFile: ?boolean,
-     * }}
-     */
-    let Args;
-    const args = /** @type {Args} */ (data.simpleArgs);
-    let existingFile = assertLastReceivedFileList().item(0) || null;
+  openFilesWithFilePicker: async (data, _resultData) => {
+    interface Args {
+      acceptTypeKeys: string[];
+      explicitToken?: number;
+      singleFile?: boolean;
+    }
+    const args: Args = data.simpleArgs;
+    let existingFile: AbstractFile|undefined =
+        assertLastReceivedFileList().item(0) || undefined;
     if (args.explicitToken) {
-      existingFile = {token: args.explicitToken};
+      existingFile = {token: args.explicitToken} as AbstractFile;
     }
     await assertLastReceivedFileList().openFilesWithFilePicker(
         args.acceptTypeKeys, existingFile, args.singleFile);
@@ -162,10 +159,9 @@
 
 /**
  * Acts on received TestMessageQueryData.
- * @param {!TestMessageQueryData} data
- * @return {!Promise<!TestMessageResponseData>}
  */
-async function runTestQuery(data) {
+async function runTestQuery(data: TestMessageQueryData):
+    Promise<TestMessageResponseData> {
   let result = 'no result';
   let extraResultData = {};
   if (data.testQuery) {
@@ -173,24 +169,24 @@
     result = element.tagName;
 
     if (data.property) {
-      result = JSON.stringify(element[data.property]);
+      result = JSON.stringify((element as any)[data.property]);
     } else if (data.requestFullscreen) {
       try {
         await element.requestFullscreen();
         result = 'hooray';
-      } catch (/** @type {!TypeError} */ typeError) {
+      } catch (typeError: any) {
         result = typeError.message;
       }
     }
   } else if (data.simple !== undefined && data.simple in SIMPLE_TEST_QUERIES) {
-    result = await SIMPLE_TEST_QUERIES[data.simple](data, extraResultData);
+    result = await SIMPLE_TEST_QUERIES[data.simple]!(data, extraResultData);
   } else if (data.navigate !== undefined) {
     // Simulate a user navigating to the next/prev file.
     if (data.navigate.direction === 'next') {
-      await assertLastReceivedFileList().loadNext(data.navigate.token);
+      await assertLastReceivedFileList().loadNext(data.navigate.token!);
       result = 'loadNext called';
     } else if (data.navigate.direction === 'prev') {
-      await assertLastReceivedFileList().loadPrev(data.navigate.token);
+      await assertLastReceivedFileList().loadPrev(data.navigate.token!);
       result = 'loadPrev called';
     } else {
       result = 'nothing called';
@@ -202,7 +198,7 @@
     try {
       await assertCast(file.overwriteOriginal).call(file, testBlob);
       result = 'overwriteOriginal resolved';
-    } catch (/** @type{!Error} */ error) {
+    } catch (error: unknown) {
       result = `overwriteOriginal failed Error: ${error}`;
       if (data.rethrow) {
         throw error;
@@ -217,7 +213,7 @@
     try {
       await assertCast(currentFile().deleteOriginalFile).call(currentFile());
       result = 'deleteOriginalFile resolved success';
-    } catch (/** @type{!Error} */ error) {
+    } catch (error: unknown) {
       result = `deleteOriginalFile failed Error: ${error}`;
     }
   } else if (data.renameLastFile) {
@@ -235,7 +231,7 @@
       } else {
         result = 'renameOriginalFile resolved success';
       }
-    } catch (/** @type{!Error} */ error) {
+    } catch (error: unknown) {
       result = `renameOriginalFile failed Error: ${error}`;
     }
   } else if (data.saveAs) {
@@ -254,7 +250,7 @@
         await assertCast(file.saveAs).call(file, testBlob, assertCast(token));
         result = file.name;
         extraResultData = {blobText: await file.blob.text()};
-      } catch (/** @type{!Error} */ error) {
+      } catch (error: unknown) {
         result = `saveAs failed Error: ${error}`;
         extraResultData = {filename: file.name};
       }
@@ -268,8 +264,9 @@
     // Remove the implementation of reportError so test code
     // can safely check that the right errors are being thrown without
     // triggering a crash.
-    if (chrome) {
-      chrome.crashReportPrivate.reportError = () => {};
+    const chromeWindow = window as unknown as {chrome: any};
+    if (chromeWindow.chrome) {
+      chromeWindow.chrome.crashReportPrivate.reportError = () => {};
     }
   }
 
@@ -278,10 +275,9 @@
 
 /**
  * Acts on TestMessageRunTestCase.
- * @param {!TestMessageRunTestCase} data
- * @return {!Promise<!TestMessageResponseData>}
  */
-async function runTestCase(data) {
+async function runTestCase(data: TestMessageRunTestCase):
+    Promise<TestMessageResponseData> {
   const testCase = guestTestCases.get(data.testCase);
   if (!testCase) {
     throw new Error(`Unknown test case: '${data.testCase}'`);
@@ -293,10 +289,9 @@
 /**
  * Registers a test that runs in the guest context. To indicate failure, the
  * test throws an exception (e.g. via assertEquals).
- * @param {string} testName
- * @param {function(): (!Promise<undefined>|undefined)} testCase
  */
-export function GUEST_TEST(testName, testCase) {
+// eslint-disable-next-line @typescript-eslint/naming-convention
+export function GUEST_TEST(testName: string, testCase: () => unknown) {
   guestTestCases.set(testName, testCase);
 }
 
@@ -317,7 +312,8 @@
       await new Promise(resolve => setTimeout(resolve, 100));
       await parentMessagePipe.sendMessage('test-handlers-ready', {});
       return;
-    } catch (/** @type {!GenericErrorResponse} */ e) {
+    } catch (error: unknown) {
+      const e = error as GenericErrorResponse;
       if (!EXPECTED_ERROR.test(e.message)) {
         console.error('Unexpected error in signalTestHandlersReady', e);
         return;
@@ -329,22 +325,25 @@
 
 /** Installs the MessagePipe handlers for receiving test queries. */
 function installTestHandlers() {
-  parentMessagePipe.registerHandler('test', (data) => {
-    return runTestQuery(/** @type {!TestMessageQueryData} */ (data));
+  parentMessagePipe.registerHandler('test', (data: TestMessageQueryData) => {
+    return runTestQuery(data);
   });
   // Turn off error rethrowing for tests so the test runner doesn't mark
   // our error handling tests as failed.
   parentMessagePipe.rethrowErrors = false;
 
-  parentMessagePipe.registerHandler('run-test-case', (data) => {
-    return runTestCase(/** @type{!TestMessageRunTestCase} */ (data));
-  });
+  parentMessagePipe.registerHandler(
+      'run-test-case', (data: TestMessageRunTestCase) => {
+        return runTestCase(data);
+      });
 
   parentMessagePipe.registerHandler('get-last-loaded-files', () => {
     //  Note: the `ReceivedFileList` has methods stripped since it gets sent
     //  over a pipe so just send the underlying files.
-    return /** @type {!LastLoadedFilesResponse} */ (
-        {fileList: assertLastReceivedFileList().files.map(flattenFile)});
+    const response: LastLoadedFilesResponse = {
+      fileList: assertLastReceivedFileList().files.map(flattenFile),
+    };
+    return response;
   });
 
   // Log errors, rather than send them to console.error. This allows the error
@@ -355,11 +354,7 @@
 
   // Install spies.
   const realLoadFiles = loadFiles;
-  /**
-   * @param {*} fileList
-   * @return {!Promise<undefined>}
-   */
-  async function watchLoadFiles(fileList) {
+  async function watchLoadFiles(fileList: ReceivedFileList) {
     lastLoadedFileList = fileList;
     return realLoadFiles(fileList);
   }
diff --git a/ash/webui/media_app_ui/test/media_app_ui_browsertest.js b/ash/webui/media_app_ui/test/media_app_ui_browsertest.ts
similarity index 79%
rename from ash/webui/media_app_ui/test/media_app_ui_browsertest.js
rename to ash/webui/media_app_ui/test/media_app_ui_browsertest.ts
index f77a1ed..e7215d1 100644
--- a/ash/webui/media_app_ui/test/media_app_ui_browsertest.js
+++ b/ash/webui/media_app_ui/test/media_app_ui_browsertest.ts
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 import {assertFilenamesToBe, assertFilesLoaded, assertFilesToBe, assertMatch, assertSingleFileLaunch, createMockTestDirectory, FakeFileSystemFileHandle, fileToFileHandle, getFileErrors, getLoadedFiles, GuestDriver, launchWithFiles, launchWithFocusFile, launchWithHandles, loadFilesWithoutSendingToGuest, runTestInGuest, sendTestMessage, simulateLosingAccessToDirectory} from './driver.js';
+import {FileSnapshot} from './driver_api.js';
 import {TEST_ONLY} from './launch.js';
+import type {LoadFilesMessage} from './message_types.js';
 
 const {
   Message,
@@ -37,14 +39,13 @@
  */
 const GENERIC_ERROR_MESSAGE_REGEX = '^".*[A-Za-z].*"$';
 
-/** @type {!GuestDriver} */
 const driver = new GuestDriver();
 
 /**
  * Runs a CSS selector until it detects the "error" UX being loaded.
- * @return {!Promise<string>} alt= text of the element showing the error.
+ * @return alt= text of the element showing the error.
  */
-function waitForErrorUX() {
+function waitForErrorUX(): Promise<string> {
   const ERROR_UX_SELECTOR = 'img[alt^="Unable to decode"]';
   return driver.waitForElementInGuest(ERROR_UX_SELECTOR, 'alt');
 }
@@ -52,10 +53,9 @@
 /**
  * Runs a CSS selector that waits for an image to load with the given alt= text
  * and returns its width.
- * @param {string} altText
- * @return {!Promise<string>} The value of the width attribute.
+ * @return The value of the width attribute.
  */
-function waitForImageAndGetWidth(altText) {
+function waitForImageAndGetWidth(altText: string): Promise<string> {
   return driver.waitForElementInGuest(`img[alt="${altText}"]`, 'naturalWidth');
 }
 
@@ -65,68 +65,62 @@
 const TEST_IMAGE_HEIGHT = 456;
 
 /**
- * @param {number=} width
- * @param {number=} height
- * @param {string=} name
- * @param {number=} lastModified
- * @return {!Promise<!File>} A {width}x{height} transparent encoded image/png.
+ * Returns A {width}x{height} transparent encoded image/png.
  */
 async function createTestImageFile(
     width = TEST_IMAGE_WIDTH, height = TEST_IMAGE_HEIGHT,
-    name = 'test_file.png', lastModified = 0) {
+    name = 'test_file.png', lastModified = 0): Promise<File> {
   const canvas = new OffscreenCanvas(width, height);
   canvas.getContext('2d');  // convertToBlob fails without a rendering context.
   const blob = await canvas.convertToBlob();
   return new File([blob], name, {type: 'image/png', lastModified});
 }
 
-/**
- * @param {!Array<string>} filenames
- * @return {!Promise<!Array<!File>>}
- */
-async function createMultipleImageFiles(filenames) {
-  const filePromise = name => createTestImageFile(1, 1, `${name}.png`);
+async function createMultipleImageFiles(filenames: unknown[]): Promise<File[]> {
+  const filePromise = (name: unknown) =>
+      createTestImageFile(1, 1, `${name}.png`);
   const files = await Promise.all(filenames.map(filePromise));
   return files;
 }
 
-/** @return {!HTMLIFrameElement} */
 function queryIFrame() {
-  return /** @type{!HTMLIFrameElement} */ (document.querySelector('iframe'));
+  return document.querySelector('iframe')!;
 }
 
-/** @return {!HTMLTitleElement} */
 function getTitle() {
-  return /** @type{!HTMLTitleElement} */ (document.querySelector('title'));
+  return document.querySelector('title')!;
 }
 
-/** @return {!HTMLLinkElement} */
 function getIcon() {
-  return /** @type{!HTMLLinkElement} */ (
-      document.querySelector('link[rel=icon]'));
+  return document.querySelector<HTMLLinkElement>('link[rel=icon]')!;
 }
 
 /**
  * Sets up a FakeFileSystemFileHandle to behave like a file which has been
  * deleted or moved to a directory to which we do not have access.
- * @param {!FakeFileSystemFileHandle} handle
  */
-function makeFileNotFound(handle) {
+function makeFileNotFound(handle?: FakeFileSystemFileHandle) {
   // Mimic the exception that would be thrown when attempting to call getFile on
   // a file which has been moved or deleted.
-  handle.getFileSync = () => {
+  handle!.getFileSync = () => {
     throw new DOMException('File not found', 'NotFoundError');
   };
 }
 
-/** @struct */
-const MediaAppUIBrowserTest = {
-  /** @type function(string): !Promise<undefined> */
-  runTestInGuest,
+interface TestSuite {
+  [testName: string]: () => unknown;
+  runTestInGuest: (testName?: string) => unknown;
+}
+
+const MediaAppUIBrowserTest: TestSuite = {
+  // runTestInGuest takes a compulsory string arg, which isn't compatible with
+  // the TestSuite index signature, so cast it here.
+  runTestInGuest: runTestInGuest as () => unknown,
 };
 
 // Expose an export for tests run through `isolatedTestRunner`.
-window['MediaAppUiBrowserTest'] = MediaAppUIBrowserTest;
+(window as unknown as {MediaAppUiBrowserTest: {}})['MediaAppUiBrowserTest'] =
+    MediaAppUIBrowserTest;
 
 // Tests that chrome://media-app is allowed to frame
 // chrome-untrusted://media-app. The URL is set in the html. If that URL can't
@@ -135,7 +129,7 @@
 // chrome-untrusted://media-app/". This test also fails if the guest renderer is
 // terminated, e.g., due to webui performing bad IPC such as network requests
 // (failure detected in content/public/test/no_renderer_crashes_assertion.cc).
-MediaAppUIBrowserTest.GuestCanLoad = async () => {
+MediaAppUIBrowserTest['GuestCanLoad'] = async () => {
   const guest = queryIFrame();
   const app = await driver.waitForElementInGuest('backlight-app', 'tagName');
 
@@ -145,18 +139,18 @@
 };
 
 // Tests that we have localized information in the HTML like title and lang.
-MediaAppUIBrowserTest.HasTitleAndLang = async () => {
+MediaAppUIBrowserTest['HasTitleAndLang'] = async () => {
   assertEquals(document.documentElement.lang, 'en');
   assertEquals(document.title, 'Gallery');
 };
 
 // Tests that regular launch for an image succeeds.
-MediaAppUIBrowserTest.LaunchFile = async () => {
+MediaAppUIBrowserTest['LaunchFile'] = async () => {
   await launchWithFiles([await createTestImageFile()]);
   const result =
       await driver.waitForElementInGuest('img[src^="blob:"]', 'naturalWidth');
   const receivedFiles = await getLoadedFiles();
-  const file = receivedFiles[0];
+  const file = receivedFiles[0]!;
 
   assertEquals(`${TEST_IMAGE_WIDTH}`, result);
   assertEquals(currentFiles.length, 1);
@@ -173,16 +167,22 @@
 // the first arguments.
 // Note: unhandledrejection & onerror tests throw JS Errors regardless and are
 // tested in media_app_integration_browsertest.cc.
-MediaAppUIBrowserTest.ReportsErrorsFromTrustedContext = async () => {
+MediaAppUIBrowserTest['ReportsErrorsFromTrustedContext'] = async () => {
   const originalConsoleError = console.error;
-  const reportedErrors = [];
+  // chrome.crashReportPrivate.ErrorInfo.
+  interface ErrorInfo {
+    message: string;
+    stackTrace?: string;
+  }
+  const reportedErrors: ErrorInfo[] = [];
 
   /**
    * In tests stub out `chrome.crashReportPrivate.reportError`, check
    *`reportedErrors` to make sure they are "sent" to the crash reporter.
    */
   function suppressConsoleErrorsForErrorTesting() {
-    chrome.crashReportPrivate.reportError = function(e) {
+    (window as any as {chrome: any}).chrome.crashReportPrivate.reportError =
+        function(e: ErrorInfo) {
       // Everything should have a non-empty stack.
       assertEquals(!!e.stackTrace, true);
       reportedErrors.push(e);
@@ -200,12 +200,10 @@
   error.name = 'yikes error';
   const extraData = {b: 'b'};
 
-  const loop = {};
+  const loop: {loop?: {}} = {};
   loop.loop = loop;
   class MySpecialException {
-    constructor() {
-      this.loop = loop;
-    }
+    aLoop = loop;
   }
 
   console.error('a');
@@ -219,34 +217,34 @@
 
   assertEquals(8, reportedErrors.length);
   // Test handles console.error(string).
-  assertEquals('Unexpected: "a", (from console)', reportedErrors[0].message);
+  assertEquals('Unexpected: "a", (from console)', reportedErrors[0]!.message);
   // Test handles console.error(Error).
   assertEquals(
       'Error: [yikes error] yikes message, (from console)',
-      reportedErrors[1].message);
+      reportedErrors[1]!.message);
   // Test handles console.error(string, Object).
   assertEquals(
-      'Unexpected: "b"\n{"b":"b"}, (from console)', reportedErrors[2].message);
+      'Unexpected: "b"\n{"b":"b"}, (from console)', reportedErrors[2]!.message);
   // Test handles console.error(Object, Object, Object).
   assertEquals(
       'Object: Unexpected: {"b":"b"}\n{"b":"b"}\n{"b":"b"}, (from console)',
-      reportedErrors[3].message);
+      reportedErrors[3]!.message);
   // Test handles console.error(string, Object, Error, Object).
   assertEquals(
       'Error: [yikes error] yikes message, foo\n{"b":"b"}\n' +
           '{"e":{"name":"yikes error"}}, (from console)',
-      reportedErrors[4].message);
+      reportedErrors[4]!.message);
   // Test arbitrary classes.
   assertEquals(
       'MySpecialException: Unexpected: <object loop?><object loop?>, ' +
           '(from console)',
-      reportedErrors[5].message);
+      reportedErrors[5]!.message);
   // Test non-objects.
   assertEquals(
-      'Unexpected: 1\n2\n3\n4\n5, (from console)', reportedErrors[6].message);
+      'Unexpected: 1\n2\n3\n4\n5, (from console)', reportedErrors[6]!.message);
   assertEquals(
       'Unexpected: null\nnull\nnull, (from console)',
-      reportedErrors[7].message);
+      reportedErrors[7]!.message);
 
   // Note: This is not needed i.e. tests pass without this but it is good
   // practice to reset it since we stub it out for this test.
@@ -257,7 +255,7 @@
 // interact with it by invoking IPC (deletion) that doesn't re-launch the
 // MediaApp i.e. doesn't call `launchWithDirectory`, then the rest of the files
 // in the current directory are loaded in.
-MediaAppUIBrowserTest.NonLaunchableIpcAfterFastLoad = async () => {
+MediaAppUIBrowserTest['NonLaunchableIpcAfterFastLoad'] = async () => {
   setSortOrder(SortOrder.A_FIRST);
   const files =
       await createMultipleImageFiles(['file1', 'file2', 'file3', 'file4']);
@@ -292,7 +290,7 @@
 
 // Tests that we can launch the MediaApp with the selected (first) file,
 // and re-launch it before all files from the first launch are loaded in.
-MediaAppUIBrowserTest.ReLaunchableAfterFastLoad = async () => {
+MediaAppUIBrowserTest['ReLaunchableAfterFastLoad'] = async () => {
   setSortOrder(SortOrder.A_FIRST);
   const files =
       await createMultipleImageFiles(['file1', 'file2', 'file3', 'file4']);
@@ -308,9 +306,9 @@
   await assertSingleFileLaunch(directory, files.length);
 
   // Mutate the second file.
-  directory.files[1].name = 'changed.png';
+  directory.files[1]!.name = 'changed.png';
   // Relaunch the app with the second file.
-  await launchWithDirectory(directory, directory.files[1]);
+  await launchWithDirectory(directory, directory.files[1]!);
 
   // Ensure second launch incremented the `globalLaunchNumber`.
   assertEquals(1, getGlobalLaunchNumber());
@@ -339,32 +337,32 @@
 
   // Focus file (file that the directory was launched with) stays index 0.
   const lastLoadedFiles = await getLoadedFiles();
-  assertEquals('changed.png', lastLoadedFiles[0].name);
-  assertEquals(loadedFilesSecondLaunch[0].name, lastLoadedFiles[0].name);
+  assertEquals('changed.png', lastLoadedFiles[0]!.name);
+  assertEquals(loadedFilesSecondLaunch[0]!.name, lastLoadedFiles[0]!.name);
   // Focus file in the `FileSystemDirectoryHandle` is at index 1.
-  assertEquals(directory.files[1].name, lastLoadedFiles[0].name);
+  assertEquals(directory.files[1]!.name, lastLoadedFiles[0]!.name);
 };
 
 // Tests that a regular
 //  launch for multiple images succeeds, and the files get
 // distinct token mappings.
-MediaAppUIBrowserTest.MultipleFilesHaveTokens = async () => {
+MediaAppUIBrowserTest['MultipleFilesHaveTokens'] = async () => {
   const directory = await launchWithFiles([
     await createTestImageFile(1, 1, 'file1.png'),
     await createTestImageFile(1, 1, 'file2.png'),
   ]);
 
   assertEquals(currentFiles.length, 2);
-  assertGE(currentFiles[0].token, 0);
-  assertGE(currentFiles[1].token, 0);
-  assertNotEquals(currentFiles[0].token, currentFiles[1].token);
-  assertEquals(fileHandleForToken(currentFiles[0].token), directory.files[0]);
-  assertEquals(fileHandleForToken(currentFiles[1].token), directory.files[1]);
+  assertGE(currentFiles[0]!.token, 0);
+  assertGE(currentFiles[1]!.token, 0);
+  assertNotEquals(currentFiles[0]!.token, currentFiles[1]!.token);
+  assertEquals(fileHandleForToken(currentFiles[0]!.token), directory.files[0]);
+  assertEquals(fileHandleForToken(currentFiles[1]!.token), directory.files[1]);
 };
 
 // Tests that a launch with a single audio file selected in the files app loads
 // only that audio file and not the directory.
-MediaAppUIBrowserTest.SingleAudioLaunch = async () => {
+MediaAppUIBrowserTest['SingleAudioLaunch'] = async () => {
   await launchWithFiles([
     // Zero-byte audio. It won't load, but should still be added to DOM.
     new File([], 'audio1.wav', {type: 'audio/wav'}),
@@ -376,10 +374,10 @@
 
 // Tests that a launch with multiple files selected in the files app loads only
 // the files selected.
-MediaAppUIBrowserTest.MultipleSelectionLaunch = async () => {
+MediaAppUIBrowserTest['MultipleSelectionLaunch'] = async () => {
   const directoryContents = await createMultipleImageFiles([0, 1, 2, 3]);
   const selectedIndexes = [1, 3];
-  const directory = await launchWithFiles(directoryContents, selectedIndexes);
+  await launchWithFiles(directoryContents, selectedIndexes);
 
   // Expect filenames to be sorted in the default lexicographical order.
   assertEquals(TEST_ONLY.sortOrder, SortOrder.A_FIRST);
@@ -387,7 +385,7 @@
 };
 
 // Test that each file type has an icon in light mode.
-MediaAppUIBrowserTest.NotifyCurrentFileLight = async () => {
+MediaAppUIBrowserTest['NotifyCurrentFileLight'] = async () => {
   const imageFile = new File([], 'image.png', {type: 'image/png'});
   const audioFile = new File([], 'audio.wav', {type: 'audio/wav'});
   const videoFile = new File([], 'video.mp4', {type: 'video/mp4'});
@@ -415,7 +413,7 @@
 };
 
 // Test that each file type has a corresponding dark icon.
-MediaAppUIBrowserTest.NotifyCurrentFileDark = async () => {
+MediaAppUIBrowserTest['NotifyCurrentFileDark'] = async () => {
   const imageFile = new File([], 'image.png', {type: 'image/png'});
   const audioFile = new File([], 'audio.wav', {type: 'audio/wav'});
   const videoFile = new File([], 'video.mp4', {type: 'video/mp4'});
@@ -441,7 +439,7 @@
 };
 
 // Test that the Gallery app icon does not have a dark variant.
-MediaAppUIBrowserTest.NotifyCurrentFileAppIconDark = async () => {
+MediaAppUIBrowserTest['NotifyCurrentFileAppIconDark'] = async () => {
   await sendTestMessage({
     simple: 'notifyCurrentFile',
     simpleArgs: {name: undefined, type: undefined},
@@ -452,7 +450,7 @@
 };
 
 // Tests that we show error UX when trying to launch an unopenable file.
-MediaAppUIBrowserTest.LaunchUnopenableFile = async () => {
+MediaAppUIBrowserTest['LaunchUnopenableFile'] = async () => {
   const mockFileHandle =
       new FakeFileSystemFileHandle('not_allowed.png', 'image/png');
   mockFileHandle.getFileSync = () => {
@@ -469,7 +467,7 @@
 
 // Tests that directories that are not navigable do not generate crash reports,
 // and the focus file still loads.
-MediaAppUIBrowserTest.LaunchUnnavigableDirectory = async () => {
+MediaAppUIBrowserTest['LaunchUnnavigableDirectory'] = async () => {
   const focus = new FakeFileSystemFileHandle('focus.png', 'image/png');
   const mine = new FakeFileSystemFileHandle('mine.png', 'image/png');
   mine.errorToFireOnIterate = new DOMException('boom', 'NotFoundError');
@@ -481,7 +479,7 @@
 
 // Tests that a file that becomes inaccessible after the initial app launch is
 // ignored on navigation, and shows an error when navigated to itself.
-MediaAppUIBrowserTest.NavigateWithUnopenableSibling = async () => {
+MediaAppUIBrowserTest['NavigateWithUnopenableSibling'] = async () => {
   setSortOrder(SortOrder.A_FIRST);
   const handles = [
     fileToFileHandle(await createTestImageFile(111 /* width */, 10, '1.png')),
@@ -499,8 +497,8 @@
   // Note that if the file is non-null, no "reopen" occurs: launch.js does not
   // open files a second time after examining siblings for relevance to the
   // focus file.
-  assertEquals(currentFiles[2].file, null);
-  handles[2].getFileSync = () => {
+  assertEquals(currentFiles[2]!.file, null);
+  handles[2]!.getFileSync = () => {
     throw new DOMException(
         'Fake NotAllowedError for NavigateToUnopenableSibling test.',
         'NotAllowedError');
@@ -536,7 +534,7 @@
 
 // Tests a hypothetical scenario where a file may be deleted and replaced with
 // an openable directory with the same name while the app is running.
-MediaAppUIBrowserTest.FileThatBecomesDirectory = async () => {
+MediaAppUIBrowserTest['FileThatBecomesDirectory'] = async () => {
   await sendTestMessage({suppressCrashReports: true});
   const handles = [
     fileToFileHandle(await createTestImageFile(111 /* width */, 10, '1.png')),
@@ -547,8 +545,8 @@
   let result = await waitForImageAndGetWidth('1.png');
   assertEquals(await getFileErrors(), ',');
 
-  handles[1].kind = 'directory';
-  handles[1].getFileSync = () => {
+  (handles[1] as {kind: string}).kind = 'directory';
+  handles[1]!.getFileSync = () => {
     throw new Error(
         '(in test) FileThatBecomesDirectory: getFileSync should not be called');
   };
@@ -562,14 +560,14 @@
 
 // Tests that chrome://media-app can successfully send a request to open the
 // feedback dialog and receive a response.
-MediaAppUIBrowserTest.CanOpenFeedbackDialog = async () => {
+MediaAppUIBrowserTest['CanOpenFeedbackDialog'] = async () => {
   const result = await mediaAppPageHandler.openFeedbackDialog();
 
   assertEquals(result.errorMessage, null);
 };
 
 // Tests that video elements in the guest can be full-screened.
-MediaAppUIBrowserTest.CanFullscreenVideo = async () => {
+MediaAppUIBrowserTest['CanFullscreenVideo'] = async () => {
   // Remove `overflow: hidden` to work around a spurious DCHECK in Blink
   // layout. See crbug.com/1052791. Oddly, even though the video is in the guest
   // iframe document (which also has these styles on its body), it is necessary
@@ -593,15 +591,15 @@
 
 // Tests that associated subtitles get not just a handle but a valid open File
 // upon initial file load.
-MediaAppUIBrowserTest.LoadVideoWithSubtitles = async () => {
+MediaAppUIBrowserTest['LoadVideoWithSubtitles'] = async () => {
   // Mock the send message call to prevent actual loading. We just want to see
   // what would be sent.
-  let secondMessageSent;
-  const messageSent = new Promise(resolve => {
-    guestMessagePipe.sendMessage = (messageId, data) => {
+  let secondMessageSent!: Promise<{messageId: number, data: {}}>;
+  const messageSent = new Promise<{messageId: number, data: {}}>(resolve => {
+    guestMessagePipe.sendMessage = (messageId: number, data: {}) => {
       resolve({messageId, data});
       secondMessageSent = new Promise(resolveAgain => {
-        guestMessagePipe.sendMessage = (messageId, data) => {
+        guestMessagePipe.sendMessage = (messageId: number, data: {}) => {
           resolveAgain({messageId, data});
           return Promise.resolve();
         };
@@ -625,30 +623,30 @@
   // on messge_types.js directly because it's rolled up into launch.js. We
   // *should* be able to re-export LoadFilesMessage, but that confuses closure
   // too much. See b/185734620.
-  let data = /** !LoadFilesMessage */ (message.data);
+  let data = message.data as LoadFilesMessage;
   assertEquals(data.files.length, 2);
-  assertEquals(data.files[0].name, 'zero_byte_video.webm');
-  assertNotEquals(data.files[0].file, null);
-  assertEquals(data.files[1].name, 'zero_byte_video.vtt');
-  assertNotEquals(data.files[1].file, null);
+  assertEquals(data.files[0]!.name, 'zero_byte_video.webm');
+  assertNotEquals(data.files[0]!.file, null);
+  assertEquals(data.files[1]!.name, 'zero_byte_video.vtt');
+  assertNotEquals(data.files[1]!.file, null);
 
   // The extra files message shouldn't include any of the old files. And the new
   // file should have a null ref.
   const secondMessage = await secondMessageSent;
   assertEquals(secondMessage.messageId, Message.LOAD_EXTRA_FILES);
 
-  data = /** !LoadFilesMessage */ (secondMessage.data);
+  data = secondMessage.data as LoadFilesMessage;
   assertEquals(data.files.length, 1);
-  assertEquals(data.files[0].name, 'extra_video.webm');
-  assertEquals(data.files[0].file, null);
+  assertEquals(data.files[0]!.name, 'extra_video.webm');
+  assertEquals(data.files[0]!.file, null);
 };
 
 // Tests the IPC behind the implementation of ReceivedFile.overwriteOriginal()
 // in the untrusted context. Ensures it correctly updates the file handle owned
 // by the privileged context.
-MediaAppUIBrowserTest.OverwriteOriginalIPC = async () => {
+MediaAppUIBrowserTest['OverwriteOriginalIPC'] = async () => {
   const directory = await launchWithFiles([await createTestImageFile()]);
-  const handle = directory.files[0];
+  const handle = directory.files[0]!;
 
   // Write should not be called initially.
   assertEquals(handle.lastWritable.writes.length, 0);
@@ -659,8 +657,8 @@
 
   assertEquals(testResponse.testQueryResult, 'overwriteOriginal resolved');
   assertEquals(
-      testResponse.testQueryResultData['receiverFileName'], 'test_file.png');
-  assertEquals(testResponse.testQueryResultData['receiverErrorName'], '');
+      testResponse.testQueryResultData!['receiverFileName'], 'test_file.png');
+  assertEquals(testResponse.testQueryResultData!['receiverErrorName'], '');
   assertEquals(await writeResult.text(), 'Foo');
   assertEquals(handle.lastWritable.writes.length, 1);
   assertDeepEquals(
@@ -671,12 +669,12 @@
   // check that it's defined and is strictly positive.
   const loadedFiles = await getLoadedFiles();
   assertEquals(loadedFiles.length, 1);
-  assertGE(loadedFiles[0].lastModified, 1);
+  assertGE(loadedFiles[0]!.lastModified, 1);
 };
 
-MediaAppUIBrowserTest.RejectZeroByteWrites = async () => {
+MediaAppUIBrowserTest['RejectZeroByteWrites'] = async () => {
   const directory = await launchWithFiles([await createTestImageFile()]);
-  const handle = directory.files[0];
+  const handle = directory.files[0]!;
 
   const EMPTY_DATA = '';
   const message = {overwriteLastFile: EMPTY_DATA};
@@ -692,14 +690,14 @@
 
 // Tests that OverwriteOriginal shows a file picker (and writes to that file) if
 // the write attempt to the original file fails.
-MediaAppUIBrowserTest.OverwriteOriginalPickerFallback = async () => {
+MediaAppUIBrowserTest['OverwriteOriginalPickerFallback'] = async () => {
   const directory = await launchWithFiles([await createTestImageFile()]);
 
-  directory.files[0].nextCreateWritableError =
+  directory.files[0]!.nextCreateWritableError =
       new DOMException('Fake exception to trigger file picker', 'FakeError');
 
   const pickedFile = new FakeFileSystemFileHandle('pickme.png');
-  window.showSaveFilePicker = options => Promise.resolve(pickedFile);
+  window.showSaveFilePicker = () => Promise.resolve(pickedFile);
 
   const message = {overwriteLastFile: 'Foo'};
   const testResponse = await sendTestMessage(message);
@@ -718,21 +716,21 @@
 
 // Tests that extensions in the `accept` option passed to showSaveFilePicker is
 // correctly configured when only a MIME type is provided.
-MediaAppUIBrowserTest.FilePickerValidateExtension = async () => {
+MediaAppUIBrowserTest['FilePickerValidateExtension'] = async () => {
   const JPG_EXTENSIONS =
       ['.jpg', '.jpeg', '.jpe', '.jfif', '.jif', '.jfi', '.pjpeg', '.pjp'];
-  function pick(mimeType) {
+  function pick(mimeType: string) {
     return new Promise(resolve => {
       window.showSaveFilePicker = options => {
         if (options.types) {
           assertEquals(!!options.excludeAcceptAllOption, true);
-          resolve(options.types.map(t => Object.values(t.accept || {})));
+          resolve(options.types.map((t: any) => Object.values(t.accept || {})));
         } else {
           assertEquals(!!options.excludeAcceptAllOption, false);
           resolve(null);
         }
-        // The handle is unused in the test, but needed to keep closure happy.
-        return Promise.resolve(/** @type {!FileSystemFileHandle}*/ (null));
+        // The handle is unused in the test, but needed to keep types happy.
+        return Promise.resolve(null as unknown as FileSystemFileHandle);
       };
       pickWritableFile('foo.foo', mimeType, 0, []);
     });
@@ -747,9 +745,10 @@
 };
 
 // Tests `MessagePipe.sendMessage()` properly propagates errors.
-MediaAppUIBrowserTest.CrossContextErrors = async () => {
+MediaAppUIBrowserTest['CrossContextErrors'] = async () => {
   // Prevent the trusted context throwing errors resulting JS errors.
-  guestMessagePipe.logClientError = error => console.log(JSON.stringify(error));
+  guestMessagePipe.logClientError = (error: {}) =>
+      console.log(JSON.stringify(error));
   guestMessagePipe.rethrowErrors = false;
 
   const directory = await launchWithFiles([await createTestImageFile()]);
@@ -761,17 +760,17 @@
   error.name = 'NotAllowedError';
   const pickedFile = new FakeFileSystemFileHandle();
   pickedFile.nextCreateWritableError = error;
-  window.showSaveFilePicker = options => Promise.resolve(pickedFile);
+  window.showSaveFilePicker = () => Promise.resolve(pickedFile);
 
-  directory.files[0].nextCreateWritableError =
+  directory.files[0]!.nextCreateWritableError =
       new DOMException('Fake exception to trigger file picker', 'FakeError');
 
-  let caughtError = {};
+  let caughtError!: Error;
 
   try {
     const message = {overwriteLastFile: 'Foo', rethrow: true};
     await sendTestMessage(message);
-  } catch (e) {
+  } catch (e: any) {
     caughtError = e;
   }
 
@@ -781,7 +780,7 @@
 
 // Tests the IPC behind the implementation of ReceivedFile.deleteOriginalFile()
 // in the untrusted context.
-MediaAppUIBrowserTest.DeleteOriginalIPC = async () => {
+MediaAppUIBrowserTest['DeleteOriginalIPC'] = async () => {
   let directory = await launchWithFiles(
       [await createTestImageFile(1, 1, 'first_file_name.png')]);
   const testHandle = directory.files[0];
@@ -822,7 +821,8 @@
   assertEquals(1, directory.files.length);
 
   // Prevent the trusted context throwing errors resulting JS errors.
-  guestMessagePipe.logClientError = error => console.log(JSON.stringify(error));
+  guestMessagePipe.logClientError = (error: {}) =>
+      console.log(JSON.stringify(error));
   guestMessagePipe.rethrowErrors = false;
   // Test it throws an error by simulating a failed directory change.
   simulateLosingAccessToDirectory();
@@ -838,7 +838,7 @@
 
 // Tests when a file is deleted, the app tries to open the next available file
 // and reloads with those files.
-MediaAppUIBrowserTest.DeletionOpensNextFile = async () => {
+MediaAppUIBrowserTest['DeletionOpensNextFile'] = async () => {
   setSortOrder(SortOrder.A_FIRST);
   const testFiles = [
     await createTestImageFile(1, 1, 'test_file_1.png'),
@@ -854,9 +854,9 @@
   // Check the app loads all 3 files.
   let lastLoadedFiles = await getLoadedFiles();
   assertEquals(3, lastLoadedFiles.length);
-  assertEquals('test_file_1.png', lastLoadedFiles[0].name);
-  assertEquals('test_file_2.png', lastLoadedFiles[1].name);
-  assertEquals('test_file_3.png', lastLoadedFiles[2].name);
+  assertEquals('test_file_1.png', lastLoadedFiles[0]!.name);
+  assertEquals('test_file_2.png', lastLoadedFiles[1]!.name);
+  assertEquals('test_file_3.png', lastLoadedFiles[2]!.name);
 
   // Delete the first file.
   const messageDelete = {deleteLastFile: true};
@@ -870,11 +870,11 @@
   // Check the app reloads the file list with the remaining two files.
   lastLoadedFiles = await getLoadedFiles();
   assertEquals(2, lastLoadedFiles.length);
-  assertEquals('test_file_2.png', lastLoadedFiles[0].name);
-  assertEquals('test_file_3.png', lastLoadedFiles[1].name);
+  assertEquals('test_file_2.png', lastLoadedFiles[0]!.name);
+  assertEquals('test_file_3.png', lastLoadedFiles[1]!.name);
 
   // Navigate to the last file (originally the third file) and delete it
-  const token = currentFiles[getEntryIndex()].token;
+  const token = currentFiles[getEntryIndex()]!.token;
   await sendTestMessage({navigate: {direction: 'next', token}});
   testResponse = await sendTestMessage(messageDelete);
 
@@ -887,7 +887,7 @@
   // (originally the second file).
   lastLoadedFiles = await getLoadedFiles();
   assertEquals(1, lastLoadedFiles.length);
-  assertEquals(testFiles[1].name, lastLoadedFiles[0].name);
+  assertEquals(testFiles[1]!.name, lastLoadedFiles[0]!.name);
 
   // Delete the last file, should lead to zero state.
   testResponse = await sendTestMessage(messageDelete);
@@ -901,7 +901,7 @@
 
 // Tests that the app gracefully handles a delete request on a file that's
 // been deleted or moved.
-MediaAppUIBrowserTest.DeleteMissingFile = async () => {
+MediaAppUIBrowserTest['DeleteMissingFile'] = async () => {
   const directory = await launchWithFiles(
       [await createTestImageFile(1, 1, 'first_file_name.png')]);
   makeFileNotFound(directory.files[0]);
@@ -917,7 +917,7 @@
 
 // Tests that the app gracefully handles a rename request on a file that's
 // been deleted or moved.
-MediaAppUIBrowserTest.RenameMissingFile = async () => {
+MediaAppUIBrowserTest['RenameMissingFile'] = async () => {
   const directory =
       await launchWithFiles([await createTestImageFile(1, 1, 'file_name.png')]);
   makeFileNotFound(directory.files[0]);
@@ -932,38 +932,36 @@
 
 // Tests the IPC behind the AbstractFile.openFile function to open a file from a
 // file handle token previously communicated to the untrusted context.
-MediaAppUIBrowserTest.OpenAllowedFileIPC = async () => {
+MediaAppUIBrowserTest['OpenAllowedFileIPC'] = async () => {
   await launchWithFiles(
       [await createTestImageFile(), await createTestImageFile()]);
   let testResponse = await sendTestMessage({simple: 'getAllFiles'});
-  let clientFiles =
-      /** @type{!Array<!FileSnapshot>} */ (testResponse.testQueryResultData);
+  let clientFiles: FileSnapshot[] = testResponse.testQueryResultData;
 
   // Second file should be a placeholder with zero size.
   const IMAGE_FILE_SIZE = 1605;
-  assertEquals(clientFiles[0].size, IMAGE_FILE_SIZE);
-  assertEquals(clientFiles[1].size, 0);
+  assertEquals(clientFiles[0]!.size, IMAGE_FILE_SIZE);
+  assertEquals(clientFiles[1]!.size, 0);
 
   testResponse = await sendTestMessage(
       {simple: 'openFileAtIndex', simpleArgs: {index: 1}});
   assertEquals(testResponse.testQueryResult, 'opened and updated');
 
   testResponse = await sendTestMessage({simple: 'getAllFiles'});
-  clientFiles =
-      /** @type{!Array<!FileSnapshot>} */ (testResponse.testQueryResultData);
+  clientFiles = testResponse.testQueryResultData;
 
   // Second file should now be opened and have a valid size.
-  assertEquals(clientFiles[0].size, IMAGE_FILE_SIZE);
-  assertEquals(clientFiles[1].size, IMAGE_FILE_SIZE);
+  assertEquals(clientFiles[0]!.size, IMAGE_FILE_SIZE);
+  assertEquals(clientFiles[1]!.size, IMAGE_FILE_SIZE);
 };
 
 // Tests the IPC behind the loadNext and loadPrev functions on the received file
 // list in the untrusted context.
-MediaAppUIBrowserTest.NavigateIPC = async () => {
+MediaAppUIBrowserTest['NavigateIPC'] = async () => {
   await launchWithFiles(
       [await createTestImageFile(), await createTestImageFile()]);
-  const fileOneToken = currentFiles[0].token;
-  const fileTwoToken = currentFiles[1].token;
+  const fileOneToken = currentFiles[0]!.token;
+  const fileTwoToken = currentFiles[1]!.token;
   assertEquals(getEntryIndex(), 0);
 
   let result = await sendTestMessage(
@@ -985,11 +983,11 @@
 // Tests the loadNext and loadPrev functions on the received file list correctly
 // navigate when they are working with a out of date file list.
 // Regression test for b/163662946
-MediaAppUIBrowserTest.NavigateOutOfSync = async () => {
+MediaAppUIBrowserTest['NavigateOutOfSync'] = async () => {
   await launchWithFiles(
       [await createTestImageFile(), await createTestImageFile()]);
-  const fileOneToken = currentFiles[0].token;
-  const fileTwoToken = currentFiles[1].token;
+  const fileOneToken = currentFiles[0]!.token;
+  const fileTwoToken = currentFiles[1]!.token;
 
   // Simulate some operation updating getEntryIndex() without reloading the
   // media app.
@@ -1021,7 +1019,7 @@
 // in the untrusted context. This test is integration-y making sure we rename
 // the focus file and that gets inserted in the right place in `currentFiles`
 // preserving navigation order.
-MediaAppUIBrowserTest.RenameOriginalIPC = async () => {
+MediaAppUIBrowserTest['RenameOriginalIPC'] = async () => {
   const directory = await launchWithFiles([
     await createTestImageFile(1, 1, 'file1.png'),
     await createTestImageFile(1, 1, 'file2.png'),
@@ -1034,9 +1032,9 @@
   await advance(1);
 
   // Test normal rename flow.
-  const file2Handle = directory.files[getEntryIndex()];
+  const file2Handle = directory.files[getEntryIndex()]!;
   const file2File = file2Handle.getFileSync();
-  const file2Token = currentFiles[getEntryIndex()].token;
+  const file2Token = currentFiles[getEntryIndex()]!.token;
   let messageRename = {renameLastFile: 'new_file_name.png'};
   let testResponse;
 
@@ -1048,38 +1046,38 @@
   assertEquals(file2Handle, directory.lastDeleted);
   // The new file has the right name in the trusted context.
   assertEquals(directory.files.length, 2);
-  assertEquals(directory.files[getEntryIndex()].name, 'new_file_name.png');
-  assertEquals(currentFiles[getEntryIndex()].handle.name, 'new_file_name.png');
+  assertEquals(directory.files[getEntryIndex()]!.name, 'new_file_name.png');
+  assertEquals(currentFiles[getEntryIndex()]!.handle.name, 'new_file_name.png');
 
   // The file doesn't need to be opened yet. Wait for a navigation.
-  assertEquals(currentFiles[getEntryIndex()].file, null);
+  assertEquals(currentFiles[getEntryIndex()]!.file, null);
 
   // The new file has the right name in the untrusted context.
   testResponse = await sendTestMessage({simple: 'getLastFile'});
-  const result = /** @type{!FileSnapshot} */ (testResponse.testQueryResultData);
+  const result: FileSnapshot = testResponse.testQueryResultData;
   assertEquals(result.name, 'new_file_name.png');
   // The new file uses the same token as the old file.
-  assertEquals(currentFiles[getEntryIndex()].token, file2Token);
+  assertEquals(currentFiles[getEntryIndex()]!.token, file2Token);
   // Check the new file written has the correct data.
-  const renamedHandle = directory.files[getEntryIndex()];
+  const renamedHandle = directory.files[getEntryIndex()]!;
   const renamedFile = await renamedHandle.getFile();
   assertEquals(renamedFile.size, file2File.size);
   assertEquals(await renamedFile.text(), await file2File.text());
   // Check the internal representation (token map & currentFiles) is updated.
   assertEquals(tokenMap.get(file2Token), renamedHandle);
-  assertEquals(currentFiles[getEntryIndex()].handle, renamedHandle);
+  assertEquals(currentFiles[getEntryIndex()]!.handle, renamedHandle);
 
   // Check navigation order is preserved.
   assertEquals(getEntryIndex(), 1);
-  assertEquals(currentFiles[getEntryIndex()].handle.name, 'new_file_name.png');
-  assertEquals(currentFiles[0].handle.name, 'file1.png');
+  assertEquals(currentFiles[getEntryIndex()]!.handle.name, 'new_file_name.png');
+  assertEquals(currentFiles[0]!.handle.name, 'file1.png');
 
   // Advancing wraps around back to the first file.
   await advance(1);
 
   assertEquals(getEntryIndex(), 0);
-  assertEquals(currentFiles[getEntryIndex()].handle.name, 'file1.png');
-  assertEquals(currentFiles[1].handle.name, 'new_file_name.png');
+  assertEquals(currentFiles[getEntryIndex()]!.handle.name, 'file1.png');
+  assertEquals(currentFiles[1]!.handle.name, 'new_file_name.png');
 
   // Test renaming when a file with the new name already exists, tries to rename
   // `file1.png` to `new_file_name.png` which already exists.
@@ -1090,13 +1088,13 @@
       testResponse.testQueryResult, 'renameOriginalFile resolved file exists');
   // No change to the existing file.
   assertEquals(directory.files.length, 2);
-  assertEquals(directory.files[getEntryIndex()].name, 'file1.png');
-  assertEquals(directory.files[1].name, 'new_file_name.png');
+  assertEquals(directory.files[getEntryIndex()]!.name, 'file1.png');
+  assertEquals(directory.files[1]!.name, 'new_file_name.png');
 
   // Test renaming when something is out of sync with `currentFiles` and has an
   // expired token.
   const expiredToken = tokenGenerator.next().value;
-  currentFiles[getEntryIndex()].token = expiredToken;
+  currentFiles[getEntryIndex()]!.token = expiredToken;
 
   messageRename = {renameLastFile: 'another_name.png'};
 
@@ -1106,7 +1104,7 @@
   assertEquals(
       testResponse.testQueryResult,
       'renameOriginalFile resolved FILE_NO_LONGER_IN_LAST_OPENED_DIRECTORY');
-  assertEquals(currentFiles[getEntryIndex()].handle.name, 'file1.png');
+  assertEquals(currentFiles[getEntryIndex()]!.handle.name, 'file1.png');
   assertEquals(currentFiles.length, 2);
   assertEquals(directory.files.length, 2);
 
@@ -1114,7 +1112,8 @@
   simulateLosingAccessToDirectory();
 
   // Prevent the trusted context throwing errors resulting JS errors.
-  guestMessagePipe.logClientError = error => console.log(JSON.stringify(error));
+  guestMessagePipe.logClientError = (error: {}) =>
+      console.log(JSON.stringify(error));
   guestMessagePipe.rethrowErrors = false;
 
   const messageRenameNoOp = {renameLastFile: 'new_file_name_2.png'};
@@ -1130,7 +1129,7 @@
 // via trusted user gestures.
 function mockShowSaveFilePicker() {
   const newFileHandle = new FakeFileSystemFileHandle();
-  const chooseEntries = new Promise(resolve => {
+  const chooseEntries = new Promise<FilePickerOptions>(resolve => {
     window.showSaveFilePicker = options => {
       resolve(options);
       return Promise.resolve(newFileHandle);
@@ -1140,38 +1139,38 @@
 }
 
 // Tests the IPC behind the requestSaveFile delegate function.
-MediaAppUIBrowserTest.RequestSaveFileIPC = async () => {
+MediaAppUIBrowserTest['RequestSaveFileIPC'] = async () => {
   let chooseEntries = mockShowSaveFilePicker();
   await launchWithFiles([await createTestImageFile(10, 10)]);
 
   // Initially test with accept `empty`.
   let result = await sendTestMessage({simple: 'requestSaveFile'});
   let options = await chooseEntries;
-  let lastToken = [...tokenMap.keys()].slice(-1)[0];
+  let lastToken = `${[...tokenMap.keys()].slice(-1)[0]}`;
 
   // Check the token matches to confirm the ReceivedFile returned represents the
   // new file created on disk.
   assertMatch(result.testQueryResult, lastToken);
   assertEquals(options.types.length, 1);
-  assertEquals(options.types[0].description, 'PNG');
-  assertDeepEquals(options.types[0].accept['image/png'], ['.png']);
+  assertEquals(options.types[0]!.description, 'PNG');
+  assertDeepEquals(options.types[0]!.accept['image/png'], ['.png']);
 
   chooseEntries = mockShowSaveFilePicker();
   result = await sendTestMessage(
       {simple: 'requestSaveFile', simpleArgs: {accept: ['PDF', 'PNG']}});
   options = await chooseEntries;
-  lastToken = [...tokenMap.keys()].slice(-1)[0];
+  lastToken = `${[...tokenMap.keys()].slice(-1)[0]}`;
 
   assertMatch(result.testQueryResult, lastToken);
   assertEquals(options.types.length, 2);
-  assertEquals(options.types[0].description, 'PDF');
-  assertDeepEquals(options.types[0].accept['application/pdf'], ['.pdf']);
-  assertEquals(options.types[1].description, 'PNG');
-  assertDeepEquals(options.types[1].accept['image/png'], ['.png']);
+  assertEquals(options.types[0]!.description, 'PDF');
+  assertDeepEquals(options.types[0]!.accept['application/pdf'], ['.pdf']);
+  assertEquals(options.types[1]!.description, 'PNG');
+  assertDeepEquals(options.types[1]!.accept['image/png'], ['.png']);
 };
 
 // Tests the IPC behind the getExportFile method.
-MediaAppUIBrowserTest.GetExportFileIPC = async () => {
+MediaAppUIBrowserTest['GetExportFileIPC'] = async () => {
   const chooseEntries = mockShowSaveFilePicker();
   const directory = await launchWithFiles([await createTestImageFile(10, 10)]);
 
@@ -1181,27 +1180,27 @@
   };
   const result = await sendTestMessage(message);
   const options = await chooseEntries;
-  const lastToken = [...tokenMap.keys()].slice(-1)[0];
+  const lastToken = `${[...tokenMap.keys()].slice(-1)[0]}`;
 
   assertMatch(result.testQueryResult, lastToken);
   assertEquals(options.types.length, 3);
 
   // Contents and order of the `types` array should correspond.
-  assertEquals(options.types[0].description, 'PNG');
-  assertEquals(options.types[1].description, 'JPG');
-  assertEquals(options.types[2].description, 'WEBP');
-  assertDeepEquals(options.types[0].accept['image/png'], ['.png']);
-  assertDeepEquals(options.types[2].accept['image/webp'], ['.webp']);
+  assertEquals(options.types[0]!.description, 'PNG');
+  assertEquals(options.types[1]!.description, 'JPG');
+  assertEquals(options.types[2]!.description, 'WEBP');
+  assertDeepEquals(options.types[0]!.accept['image/png'], ['.png']);
+  assertDeepEquals(options.types[2]!.accept['image/webp'], ['.webp']);
 
   // jpg has a bunch of extensions.
-  assertEquals(options.types[1].accept['image/jpeg'].length, 8);
+  assertEquals(options.types[1]!.accept['image/jpeg']!.length, 8);
 
   // The startIn option should be set to the opened file.
   assertEquals(options.startIn, directory.files[0]);
 };
 
 // Tests the IPC behind the saveAs function on received files.
-MediaAppUIBrowserTest.SaveAsIPC = async () => {
+MediaAppUIBrowserTest['SaveAsIPC'] = async () => {
   // Mock out choose file system entries since it can only be interacted with
   // via trusted user gestures.
   const newFileHandle = new FakeFileSystemFileHandle('new_file.jpg');
@@ -1210,7 +1209,7 @@
   const directory = await launchWithFiles(
       [await createTestImageFile(10, 10, 'original_file.jpg')]);
 
-  const originalFileToken = currentFiles[0].token;
+  const originalFileToken = currentFiles[0]!.token;
   assertEquals(getEntryIndex(), 0);
 
   const receivedFilesBefore = await getLoadedFiles();
@@ -1226,33 +1225,34 @@
   // Make sure we have created a new file descriptor, and that
   // the original file is still available.
   assertEquals(getEntryIndex(), 1);
-  assertEquals(currentFiles[0].handle, directory.files[0]);
-  assertEquals(currentFiles[0].handle.name, 'original_file.jpg');
-  assertNotEquals(currentFiles[0].token, originalFileToken);
-  assertEquals(currentFiles[1].handle, newFileHandle);
-  assertEquals(currentFiles[1].handle.name, 'new_file.jpg');
-  assertEquals(currentFiles[1].token, originalFileToken);
-  assertEquals(tokenMap.get(currentFiles[0].token), currentFiles[0].handle);
-  assertEquals(tokenMap.get(currentFiles[1].token), currentFiles[1].handle);
+  assertEquals(currentFiles[0]!.handle, directory.files[0]);
+  assertEquals(currentFiles[0]!.handle.name, 'original_file.jpg');
+  assertNotEquals(currentFiles[0]!.token, originalFileToken);
+  assertEquals(currentFiles[1]!.handle, newFileHandle);
+  assertEquals(currentFiles[1]!.handle.name, 'new_file.jpg');
+  assertEquals(currentFiles[1]!.token, originalFileToken);
+  assertEquals(tokenMap.get(currentFiles[0]!.token), currentFiles[0]!.handle);
+  assertEquals(tokenMap.get(currentFiles[1]!.token), currentFiles[1]!.handle);
 
   // Currently, files obtained from a file picker can not be deleted or renamed.
   // TODO(b/163285659): Try to support delete/rename in this case. For now, we
   // check that the methods go away so that the UI updates to disable buttons.
-  assertEquals(receivedFilesBefore[0].hasRename, true);
-  assertEquals(receivedFilesBefore[0].hasDelete, true);
-  assertEquals(receivedFilesAfter[0].hasRename, false);
-  assertEquals(receivedFilesAfter[0].hasDelete, false);
+  assertEquals(receivedFilesBefore[0]!.hasRename, true);
+  assertEquals(receivedFilesBefore[0]!.hasDelete, true);
+  assertEquals(receivedFilesAfter[0]!.hasRename, false);
+  assertEquals(receivedFilesAfter[0]!.hasDelete, false);
 
   // Ensure there's a last modified property on the file after swapping in the
   // picked file.
-  assertGE(receivedFilesAfter[0].lastModified, 1);
+  assertGE(receivedFilesAfter[0]!.lastModified, 1);
 };
 
 // Tests the error handling behind the saveAs function on received files.
-MediaAppUIBrowserTest.SaveAsErrorHandling = async () => {
+MediaAppUIBrowserTest['SaveAsErrorHandling'] = async () => {
   // Prevent the trusted context from throwing errors which cause the test to
   // fail.
-  guestMessagePipe.logClientError = error => console.log(JSON.stringify(error));
+  guestMessagePipe.logClientError = (error: {}) =>
+      console.log(JSON.stringify(error));
   guestMessagePipe.rethrowErrors = false;
   const newFileHandle = new FakeFileSystemFileHandle('new_file.jpg');
   newFileHandle.nextCreateWritableError =
@@ -1260,7 +1260,7 @@
   window.showSaveFilePicker = () => Promise.resolve(newFileHandle);
   const directory = await launchWithFiles(
       [await createTestImageFile(10, 10, 'original_file.jpg')]);
-  const originalFileToken = currentFiles[0].token;
+  const originalFileToken = currentFiles[0]!.token;
 
   const result = await sendTestMessage({saveAs: 'foo'});
 
@@ -1268,24 +1268,23 @@
   assertEquals(
       result.testQueryResult,
       'saveAs failed Error: FakeError: save-as: Fake exception');
-  assertEquals(result.testQueryResultData['filename'], 'original_file.jpg');
+  assertEquals(result.testQueryResultData!['filename'], 'original_file.jpg');
   assertEquals(getEntryIndex(), 0);
   assertEquals(currentFiles.length, 1);
-  assertEquals(currentFiles[0].handle, directory.files[0]);
-  assertEquals(currentFiles[0].handle.name, 'original_file.jpg');
-  assertEquals(currentFiles[0].token, originalFileToken);
-  assertEquals(tokenMap.get(currentFiles[0].token), currentFiles[0].handle);
+  assertEquals(currentFiles[0]!.handle, directory.files[0]);
+  assertEquals(currentFiles[0]!.handle.name, 'original_file.jpg');
+  assertEquals(currentFiles[0]!.token, originalFileToken);
+  assertEquals(tokenMap.get(currentFiles[0]!.token), currentFiles[0]!.handle);
 };
 
 // Tests the IPC behind the AbstractFileList.openFilesWithFilePicker function to
 // relaunch the app with a new selection of files from a file picker.
-MediaAppUIBrowserTest.OpenFilesWithFilePickerIPC = async () => {
+MediaAppUIBrowserTest['OpenFilesWithFilePickerIPC'] = async () => {
   const pickedFileHandles = [
     new FakeFileSystemFileHandle('picked_file1.jpg'),
     new FakeFileSystemFileHandle('picked_file2.jpg'),
   ];
-  /** @type {!OpenFilePickerOptions|!DraftFilePickerOptions|undefined} */
-  let lastPickerOptions;
+  let lastPickerOptions!: OpenFilePickerOptions;
   window.showOpenFilePicker = (pickerOptions) => {
     lastPickerOptions = pickerOptions;
     return Promise.resolve(pickedFileHandles);
@@ -1293,7 +1292,7 @@
   const directory = await launchWithFiles(
       [await createTestImageFile(10, 10, 'original_file.jpg')]);
 
-  const simpleArgs = {acceptTypeKeys: ['VIDEO', 'IMAGE']};
+  const simpleArgs: any = {acceptTypeKeys: ['VIDEO', 'IMAGE']};
   async function openFilesWithFilePickerWithSimpleArgs() {
     const response =
         await sendTestMessage({simple: 'openFilesWithFilePicker', simpleArgs});
@@ -1309,16 +1308,15 @@
   assertEquals(startIn, directory.files[0]);
   assertEquals(excludeAcceptAllOption, true);
   assertEquals(types.length, 2);
-  assertEquals(types[0].description, 'Video Files');
-  assertEquals(types[1].description, 'Image Files');
+  assertEquals(types[0]!.description, 'Video Files');
+  assertEquals(types[1]!.description, 'Image Files');
 
   testResponse = await sendTestMessage({simple: 'getAllFiles'});
   console.log(JSON.stringify(testResponse));
-  const clientFiles =
-      /** @type{!Array<!FileSnapshot>} */ (testResponse.testQueryResultData);
+  const clientFiles: FileSnapshot[] = testResponse.testQueryResultData;
 
-  assertEquals(clientFiles[0].name, 'picked_file1.jpg');
-  assertEquals(clientFiles[1].name, 'picked_file2.jpg');
+  assertEquals(clientFiles[0]!.name, 'picked_file1.jpg');
+  assertEquals(clientFiles[1]!.name, 'picked_file2.jpg');
 
   // Test to handle invalid tokens (b/209342852). These should leave the
   // `startIn` option unspecified.
@@ -1338,9 +1336,9 @@
   // Spot-check the ALL_EX_TEXT filter key, which groups all extensions.
   simpleArgs.acceptTypeKeys = ['ALL_EX_TEXT'];
   await openFilesWithFilePickerWithSimpleArgs();
-  const extensions = lastPickerOptions.types[0].accept['*/*'];
+  const extensions = lastPickerOptions.types[0]!.accept['*/*']!;
   assertEquals(lastPickerOptions.types.length, 1);
-  assertEquals(lastPickerOptions.types[0].description, 'All');
+  assertEquals(lastPickerOptions.types[0]!.description, 'All');
   assertEquals(extensions.includes('.pdf'), true);
   assertEquals(extensions.includes('.jpeg'), true);
   assertEquals(extensions.includes('.avi'), true);
@@ -1348,7 +1346,7 @@
   assertEquals(extensions.includes('.zip'), false);
 };
 
-MediaAppUIBrowserTest.RelatedFiles = async () => {
+MediaAppUIBrowserTest['RelatedFiles'] = async () => {
   setSortOrder(SortOrder.A_FIRST);
   // These files all have a last modified time of 0 so the order they end up in
   // is their lexicographical order i.e. `jaypeg.jpg, jiff.gif, matroska.mkv,
@@ -1373,32 +1371,32 @@
   ];
   const directory = await createMockTestDirectory(testFiles);
   const files = directory.getFilesSync();
-  const [html, jpg, gif, emkv, mkv, MKV, ext, other, vtt, txt] = files;
+  const [html, jpg, gif, _emkv, mkv, MKV, ext, other, vtt, txt] = files;
   const [webm, avi, y3gp, mpg] = files.slice(10);
 
-  await loadFilesWithoutSendingToGuest(directory, mkv);
+  await loadFilesWithoutSendingToGuest(directory, mkv!);
   assertFilesToBe([mkv, MKV, vtt, webm, avi, y3gp, mpg, jpg, gif], 'mkv');
 
-  await loadFilesWithoutSendingToGuest(directory, jpg);
+  await loadFilesWithoutSendingToGuest(directory, jpg!);
   assertFilesToBe([jpg, gif, mkv, MKV, vtt, webm, avi, y3gp, mpg], 'jpg');
 
-  await loadFilesWithoutSendingToGuest(directory, gif);
+  await loadFilesWithoutSendingToGuest(directory, gif!);
   assertFilesToBe([gif, mkv, MKV, vtt, webm, avi, y3gp, mpg, jpg], 'gif');
 
-  await loadFilesWithoutSendingToGuest(directory, webm);
+  await loadFilesWithoutSendingToGuest(directory, webm!);
   assertFilesToBe([webm, avi, y3gp, mpg, jpg, gif, mkv, MKV, vtt], 'webm');
 
-  await loadFilesWithoutSendingToGuest(directory, txt);
+  await loadFilesWithoutSendingToGuest(directory, txt!);
   assertFilesToBe([txt, other], 'txt');
 
-  await loadFilesWithoutSendingToGuest(directory, html);
+  await loadFilesWithoutSendingToGuest(directory, html!);
   assertFilesToBe([html], 'html');
 
-  await loadFilesWithoutSendingToGuest(directory, ext);
+  await loadFilesWithoutSendingToGuest(directory, ext!);
   assertFilesToBe([ext], 'ext');
 };
 
-MediaAppUIBrowserTest.SortedFilesByTime = async () => {
+MediaAppUIBrowserTest['SortedFilesByTime'] = async () => {
   setSortOrder(SortOrder.NEWEST_FIRST);
   // We want the more recent (i.e. higher timestamp) files first. In the case of
   // equal timestamp, it should sort lexicographically by filename.
@@ -1413,14 +1411,14 @@
   ]);
   const files = [...filesInModifiedOrder];
   // Mix up files so that we can check they get sorted correctly.
-  [files[4], files[2], files[3]] = [files[2], files[3], files[4]];
+  [files[4], files[2], files[3]] = [files[2]!, files[3]!, files[4]!];
 
   await launchWithFiles(files);
 
   assertFilesToBe(filesInModifiedOrder);
 };
 
-MediaAppUIBrowserTest.SortedFilesByName = async () => {
+MediaAppUIBrowserTest['SortedFilesByName'] = async () => {
   // A_FIRST should be the default.
   assertEquals(TEST_ONLY.sortOrder, SortOrder.A_FIRST);
   // Establish some sample files that match the naming style from the Camera app
@@ -1437,7 +1435,7 @@
   ]);
   const files = [...filesInLexicographicOrder];
   // Mix up files so that we can check they get sorted correctly.
-  [files[4], files[2], files[3]] = [files[2], files[3], files[4]];
+  [files[4], files[2], files[3]] = [files[2]!, files[3]!, files[4]!];
 
   await launchWithFiles(files);
 
@@ -1447,17 +1445,18 @@
 // Tests that getFile is not called on all files in a directory on launch with
 // default sort order. This is to avoid a series of slow file system api calls
 // due to b/172529567.
-MediaAppUIBrowserTest.GetFileNotCalledOnAllFiles = async () => {
+MediaAppUIBrowserTest['GetFileNotCalledOnAllFiles'] = async () => {
   const handles = [
     fileToFileHandle(await createTestImageFile(1, 1, '1.png')),
     fileToFileHandle(await createTestImageFile(1, 1, '2.png')),
     fileToFileHandle(await createTestImageFile(1, 1, '3.png')),
     fileToFileHandle(await createTestImageFile(1, 1, '4.png')),
   ];
-  const getFileCalls = [];
+  const getFileCalls: string[] = [];
   for (const handle of handles) {
     handle.getFileSync = () => {
       getFileCalls.push(handle.name);
+      return undefined as unknown as File;  // unused.
     };
   }
 
@@ -1472,7 +1471,7 @@
 };
 
 // Tests that the guest gets focus automatically on start up.
-MediaAppUIBrowserTest.GuestHasFocus = async () => {
+MediaAppUIBrowserTest['GuestHasFocus'] = async () => {
   const guest = queryIFrame();
 
   // By the time this tests runs the iframe should already have been loaded.
@@ -1480,7 +1479,7 @@
 };
 
 // Check the body element's background color when it is light mode.
-MediaAppUIBrowserTest.BodyHasCorrectBackgroundColorInLightMode = () => {
+MediaAppUIBrowserTest['BodyHasCorrectBackgroundColorInLightMode'] = () => {
   const actualBackgroundColor = getComputedStyle(document.body).backgroundColor;
   assertEquals(actualBackgroundColor, 'rgb(255, 255, 255)');  // White.
 };
diff --git a/ash/webui/media_app_ui/test/test_api.d.ts b/ash/webui/media_app_ui/test/test_api.d.ts
index 8527d8e..e7b7463 100644
--- a/ash/webui/media_app_ui/test/test_api.d.ts
+++ b/ash/webui/media_app_ui/test/test_api.d.ts
@@ -19,6 +19,8 @@
 interface ChaiJsAsserts {
   isTrue(value: any): void;
   isDefined(value: any): void;
+  equal(lhs: any, rhs: any, message?: string): void;
+  match(string: string, regex: RegExp, message?: string): void;
 }
 
 interface ChaiJs {
@@ -27,3 +29,6 @@
 }
 
 declare const chai: ChaiJs;
+
+// Things that become available when dom_testing_helpers.js is injected.
+declare function waitForNode(query: string, path?: string[]): Promise<Element>;
diff --git a/ash/webui/media_app_ui/test/test_worker.js b/ash/webui/media_app_ui/test/test_worker.ts
similarity index 100%
rename from ash/webui/media_app_ui/test/test_worker.js
rename to ash/webui/media_app_ui/test/test_worker.ts
diff --git a/base/android/jank_metric_uma_recorder.cc b/base/android/jank_metric_uma_recorder.cc
index 5fce95ad..58a2ce46 100644
--- a/base/android/jank_metric_uma_recorder.cc
+++ b/base/android/jank_metric_uma_recorder.cc
@@ -31,65 +31,88 @@
 
 const char* GetPerScrollHistogramName(JankScenario scenario,
                                       int num_frames,
-                                      PerScrollHistogramType type) {
+                                      PerScrollHistogramType type,
+                                      bool with_scroll_size_suffix) {
 #define HISTOGRAM_NAME(hist_scenario, hist_type, length)     \
   "Android.FrameTimelineJank." #hist_scenario "." #hist_type \
   "."                                                        \
-  "PerScroll." #length
+  "PerScroll" #length
   if (scenario == JankScenario::WEBVIEW_SCROLLING) {
     if (type == PerScrollHistogramType::kPercentage) {
+      if (!with_scroll_size_suffix) {
+        return HISTOGRAM_NAME(WebviewScrolling, DelayedFramesPercentage,
+                              /*no suffix*/);
+      }
       if (num_frames <= 16) {
-        return HISTOGRAM_NAME(WebviewScrolling, DelayedFramesPercentage, Small);
+        return HISTOGRAM_NAME(WebviewScrolling, DelayedFramesPercentage,
+                              .Small);
       } else if (num_frames <= 64) {
         return HISTOGRAM_NAME(WebviewScrolling, DelayedFramesPercentage,
-                              Medium);
+                              .Medium);
       } else {
-        return HISTOGRAM_NAME(WebviewScrolling, DelayedFramesPercentage, Large);
+        return HISTOGRAM_NAME(WebviewScrolling, DelayedFramesPercentage,
+                              .Large);
       }
     } else if (type == PerScrollHistogramType::kMax) {
+      if (!with_scroll_size_suffix) {
+        return HISTOGRAM_NAME(WebviewScrolling, MissedVsyncsMax, /*no suffix*/);
+      }
       if (num_frames <= 16) {
-        return HISTOGRAM_NAME(WebviewScrolling, MissedVsyncsMax, Small);
+        return HISTOGRAM_NAME(WebviewScrolling, MissedVsyncsMax, .Small);
       } else if (num_frames <= 64) {
-        return HISTOGRAM_NAME(WebviewScrolling, MissedVsyncsMax, Medium);
+        return HISTOGRAM_NAME(WebviewScrolling, MissedVsyncsMax, .Medium);
       } else {
-        return HISTOGRAM_NAME(WebviewScrolling, MissedVsyncsMax, Large);
+        return HISTOGRAM_NAME(WebviewScrolling, MissedVsyncsMax, .Large);
       }
     } else {
       DCHECK_EQ(type, PerScrollHistogramType::kSum);
+      if (!with_scroll_size_suffix) {
+        return HISTOGRAM_NAME(WebviewScrolling, MissedVsyncsSum, /*no suffix*/);
+      }
       if (num_frames <= 16) {
-        return HISTOGRAM_NAME(WebviewScrolling, MissedVsyncsSum, Small);
+        return HISTOGRAM_NAME(WebviewScrolling, MissedVsyncsSum, .Small);
       } else if (num_frames <= 64) {
-        return HISTOGRAM_NAME(WebviewScrolling, MissedVsyncsSum, Medium);
+        return HISTOGRAM_NAME(WebviewScrolling, MissedVsyncsSum, .Medium);
       } else {
-        return HISTOGRAM_NAME(WebviewScrolling, MissedVsyncsSum, Large);
+        return HISTOGRAM_NAME(WebviewScrolling, MissedVsyncsSum, .Large);
       }
     }
   } else {
     DCHECK_EQ(scenario, JankScenario::FEED_SCROLLING);
     if (type == PerScrollHistogramType::kPercentage) {
+      if (!with_scroll_size_suffix) {
+        return HISTOGRAM_NAME(FeedScrolling, DelayedFramesPercentage,
+                              /*no suffix*/);
+      }
       if (num_frames <= 16) {
-        return HISTOGRAM_NAME(FeedScrolling, DelayedFramesPercentage, Small);
+        return HISTOGRAM_NAME(FeedScrolling, DelayedFramesPercentage, .Small);
       } else if (num_frames <= 64) {
-        return HISTOGRAM_NAME(FeedScrolling, DelayedFramesPercentage, Medium);
+        return HISTOGRAM_NAME(FeedScrolling, DelayedFramesPercentage, .Medium);
       } else {
-        return HISTOGRAM_NAME(FeedScrolling, DelayedFramesPercentage, Large);
+        return HISTOGRAM_NAME(FeedScrolling, DelayedFramesPercentage, .Large);
       }
     } else if (type == PerScrollHistogramType::kMax) {
+      if (!with_scroll_size_suffix) {
+        return HISTOGRAM_NAME(FeedScrolling, MissedVsyncsMax, /*no suffix*/);
+      }
       if (num_frames <= 16) {
-        return HISTOGRAM_NAME(FeedScrolling, MissedVsyncsMax, Small);
+        return HISTOGRAM_NAME(FeedScrolling, MissedVsyncsMax, .Small);
       } else if (num_frames <= 64) {
-        return HISTOGRAM_NAME(FeedScrolling, MissedVsyncsMax, Medium);
+        return HISTOGRAM_NAME(FeedScrolling, MissedVsyncsMax, .Medium);
       } else {
-        return HISTOGRAM_NAME(FeedScrolling, MissedVsyncsMax, Large);
+        return HISTOGRAM_NAME(FeedScrolling, MissedVsyncsMax, .Large);
       }
     } else {
       DCHECK_EQ(type, PerScrollHistogramType::kSum);
+      if (!with_scroll_size_suffix) {
+        return HISTOGRAM_NAME(FeedScrolling, MissedVsyncsSum, /*no suffix*/);
+      }
       if (num_frames <= 16) {
-        return HISTOGRAM_NAME(FeedScrolling, MissedVsyncsSum, Small);
+        return HISTOGRAM_NAME(FeedScrolling, MissedVsyncsSum, .Small);
       } else if (num_frames <= 64) {
-        return HISTOGRAM_NAME(FeedScrolling, MissedVsyncsSum, Medium);
+        return HISTOGRAM_NAME(FeedScrolling, MissedVsyncsSum, .Medium);
       } else {
-        return HISTOGRAM_NAME(FeedScrolling, MissedVsyncsSum, Large);
+        return HISTOGRAM_NAME(FeedScrolling, MissedVsyncsSum, .Large);
       }
     }
   }
@@ -133,17 +156,39 @@
       scenario != JankScenario::FEED_SCROLLING) {
     return;
   }
+  // Emit non-bucketed per scroll metrics.
   base::UmaHistogramPercentage(
       GetPerScrollHistogramName(scenario, num_presented_frames,
-                                PerScrollHistogramType::kPercentage),
+                                PerScrollHistogramType::kPercentage,
+                                /*with_scroll_size_suffix=*/false),
       delayed_frames_percentage);
   base::UmaHistogramCustomCounts(
       GetPerScrollHistogramName(scenario, num_presented_frames,
-                                PerScrollHistogramType::kMax),
+                                PerScrollHistogramType::kMax,
+                                /*with_scroll_size_suffix=*/false),
       missed_vsyncs_max, kVsyncCountsMin, kVsyncCountsMax, kVsyncCountsBuckets);
   base::UmaHistogramCustomCounts(
       GetPerScrollHistogramName(scenario, num_presented_frames,
-                                PerScrollHistogramType::kSum),
+                                PerScrollHistogramType::kSum,
+                                /*with_scroll_size_suffix=*/false),
+      missed_vsyncs_sum, kVsyncCountsMin, kVsyncCountsMax, kVsyncCountsBuckets);
+
+  // Emit bucketed per scroll metrics where scrolls are divided into three
+  // buckets Small, Medium, Large.
+  base::UmaHistogramPercentage(
+      GetPerScrollHistogramName(scenario, num_presented_frames,
+                                PerScrollHistogramType::kPercentage,
+                                /*with_scroll_size_suffix=*/true),
+      delayed_frames_percentage);
+  base::UmaHistogramCustomCounts(
+      GetPerScrollHistogramName(scenario, num_presented_frames,
+                                PerScrollHistogramType::kMax,
+                                /*with_scroll_size_suffix=*/true),
+      missed_vsyncs_max, kVsyncCountsMin, kVsyncCountsMax, kVsyncCountsBuckets);
+  base::UmaHistogramCustomCounts(
+      GetPerScrollHistogramName(scenario, num_presented_frames,
+                                PerScrollHistogramType::kSum,
+                                /*with_scroll_size_suffix=*/true),
       missed_vsyncs_sum, kVsyncCountsMin, kVsyncCountsMax, kVsyncCountsBuckets);
 }
 
diff --git a/base/android/jank_metric_uma_recorder_unittest.cc b/base/android/jank_metric_uma_recorder_unittest.cc
index deb263d..5f4d6e7e 100644
--- a/base/android/jank_metric_uma_recorder_unittest.cc
+++ b/base/android/jank_metric_uma_recorder_unittest.cc
@@ -145,17 +145,17 @@
     JankMetricUMARecorderPerScrollTests,
     testing::ValuesIn<ScrollTestCase>({
         {JankScenario::WEBVIEW_SCROLLING, "EmitsSmallScrollHistogramInWebview",
-         10, "Small"},
+         10, ".Small"},
         {JankScenario::WEBVIEW_SCROLLING, "EmitsMediumScrollHistogramInWebview",
-         50, "Medium"},
+         50, ".Medium"},
         {JankScenario::WEBVIEW_SCROLLING, "EmitsLargeScrollHistogramInWebview",
-         65, "Large"},
+         65, ".Large"},
         {JankScenario::FEED_SCROLLING, "EmitsSmallScrollHistogramInFeed", 10,
-         "Small"},
+         ".Small"},
         {JankScenario::FEED_SCROLLING, "EmitsMediumScrollHistogramInFeed", 50,
-         "Medium"},
+         ".Medium"},
         {JankScenario::FEED_SCROLLING, "EmitsLargeScrollHistogramInFeed", 65,
-         "Large"},
+         ".Large"},
     }),
     [](const testing::TestParamInfo<
         JankMetricUMARecorderPerScrollTests::ParamType>& info) {
@@ -202,24 +202,31 @@
   std::string delayed_frames_histogram = "Android.FrameTimelineJank." +
                                          scenario_name +
                                          ".DelayedFramesPercentage."
-                                         "PerScroll." +
-                                         params.suffix;
+                                         "PerScroll";
   std::string missed_vsyncs_max_histogram = "Android.FrameTimelineJank." +
                                             scenario_name +
                                             ".MissedVsyncsMax."
-                                            "PerScroll." +
-                                            params.suffix;
+                                            "PerScroll";
   std::string missed_vsyncs_sum_histogram = "Android.FrameTimelineJank." +
                                             scenario_name +
                                             ".MissedVsyncsSum."
-                                            "PerScroll." +
-                                            params.suffix;
+                                            "PerScroll";
+  // Should emit non-bucketed scroll histograms.
   histogram_tester.ExpectUniqueSample(delayed_frames_histogram,
                                       expected_delayed_frames_percentage, 1);
   histogram_tester.ExpectUniqueSample(missed_vsyncs_max_histogram,
                                       expected_vsyncs_max, 1);
   histogram_tester.ExpectUniqueSample(missed_vsyncs_sum_histogram,
                                       expected_vsyncs_sum, 1);
+
+  // Should emit bucketed scroll histograms, suffixed with scroll size like
+  // Small, Medium, Large.
+  histogram_tester.ExpectUniqueSample(delayed_frames_histogram + params.suffix,
+                                      expected_delayed_frames_percentage, 1);
+  histogram_tester.ExpectUniqueSample(
+      missed_vsyncs_max_histogram + params.suffix, expected_vsyncs_max, 1);
+  histogram_tester.ExpectUniqueSample(
+      missed_vsyncs_sum_histogram + params.suffix, expected_vsyncs_sum, 1);
 }
 
 }  // namespace base::android
diff --git a/cc/trees/layer_tree_host_pixeltest_filters.cc b/cc/trees/layer_tree_host_pixeltest_filters.cc
index 25c39ac6..903f93f4 100644
--- a/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -1405,10 +1405,10 @@
 // For now only run on SkiaRenderer-based compositors.
 INSTANTIATE_TEST_SUITE_P(,
                          MixedFilterZoomAndOffsetTest,
-                         ::testing::ValuesIn(viz::GetRendererTypesSkiaOnly()),
+                         ::testing::ValuesIn(viz::GetGpuRendererTypes()),
                          ::testing::PrintToStringParamName());
 
-// viz::GetRendererTypesSkiaOnly() can return an empty list on some platforms.
+// viz::GetGpuRendererTypes() can return an empty list on some platforms.
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MixedFilterZoomAndOffsetTest);
 
 TEST_P(MixedFilterZoomAndOffsetTest, StandardDpi) {
diff --git a/chrome/VERSION b/chrome/VERSION
index 347c080..27a82d9 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=122
 MINOR=0
-BUILD=6193
+BUILD=6194
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
index dba3bf2a..6989211 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
@@ -48,7 +48,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.util.AndroidTaskUtils;
 import org.chromium.chrome.browser.webapps.WebappLauncherActivity;
-import org.chromium.components.browser_ui.media.MediaNotificationUma;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.ui.widget.Toast;
 
@@ -128,8 +127,6 @@
         if (mIntent != null && BrowserIntentUtils.getStartupRealtimeMillis(mIntent) == -1) {
             BrowserIntentUtils.addStartupTimestampsToIntent(mIntent);
         }
-
-        recordIntentMetrics();
     }
 
     /**
@@ -511,11 +508,6 @@
                 || !TextUtils.isEmpty(getClientPackageNameFromIdentitySharing());
     }
 
-    /** Records metrics gleaned from the Intent. */
-    private void recordIntentMetrics() {
-        MediaNotificationUma.recordClickSource(mIntent);
-    }
-
     private static boolean clearTopIntentsForCustomTabsEnabled(Intent intent) {
         // The new behavior is important for TWAs, but could potentially affect other clients.
         // For now we expose this risky change only to TWAs.
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt
index 218d550d..3bb3a8e 100644
--- a/chrome/android/profiles/arm.newest.txt
+++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-122.0.6190.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-arm-122.0.6191.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index b9308767..9fca194 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-122.0.6190.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-122.0.6191.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 3f06ff7..6d1937f 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -344,6 +344,10 @@
 #include "ui/views/views_switches.h"
 #endif  // defined(TOOLKIT_VIEWS)
 
+#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
+#include "components/unexportable_keys/features.h"  // nogncheck
+#endif
+
 using flags_ui::FeatureEntry;
 using flags_ui::kDeprecated;
 using flags_ui::kOsAndroid;
@@ -2548,26 +2552,6 @@
      std::size(kAddToHomescreen_UseMessage), nullptr}};
 #endif
 
-#if BUILDFLAG(IS_ANDROID)
-const FeatureEntry::FeatureParam
-    kAutofillUseMobileLabelDisambiguationShowAll[] = {
-        {autofill::features::kAutofillUseMobileLabelDisambiguationParameterName,
-         autofill::features::
-             kAutofillUseMobileLabelDisambiguationParameterShowAll}};
-const FeatureEntry::FeatureParam
-    kAutofillUseMobileLabelDisambiguationShowOne[] = {
-        {autofill::features::kAutofillUseMobileLabelDisambiguationParameterName,
-         autofill::features::
-             kAutofillUseMobileLabelDisambiguationParameterShowOne}};
-
-const FeatureEntry::FeatureVariation
-    kAutofillUseMobileLabelDisambiguationVariations[] = {
-        {"(show all)", kAutofillUseMobileLabelDisambiguationShowAll,
-         std::size(kAutofillUseMobileLabelDisambiguationShowAll), nullptr},
-        {"(show one)", kAutofillUseMobileLabelDisambiguationShowOne,
-         std::size(kAutofillUseMobileLabelDisambiguationShowOne), nullptr}};
-#endif  // BUILDFLAG(IS_ANDROID)
-
 constexpr FeatureEntry::FeatureParam kStorageAccessAPI_WithPrompt[] = {
     {"storage_access_api_auto_deny_outside_fps", "false"}};
 
@@ -6405,9 +6389,6 @@
      flag_descriptions::kHandwritingLegacyRecognitionName,
      flag_descriptions::kHandwritingLegacyRecognitionDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kHandwritingLegacyRecognition)},
-    {"handwriting-library-dlc", flag_descriptions::kHandwritingLibraryDlcName,
-     flag_descriptions::kHandwritingLibraryDlcDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kHandwritingLibraryDlc)},
     {"language-packs-in-settings",
      flag_descriptions::kLanguagePacksInSettingsName,
      flag_descriptions::kLanguagePacksInSettingsDescription, kOsCrOS,
@@ -7473,13 +7454,6 @@
      flag_descriptions::kAvifGainmapHdrImagesDescription, kOsAll,
      FEATURE_VALUE_TYPE(blink::features::kAvifGainmapHdrImages)},
 
-    {"autofill-use-improved-label-disambiguation",
-     flag_descriptions::kAutofillUseImprovedLabelDisambiguationName,
-     flag_descriptions::kAutofillUseImprovedLabelDisambiguationDescription,
-     kOsAll,
-     FEATURE_VALUE_TYPE(
-         autofill::features::kAutofillUseImprovedLabelDisambiguation)},
-
     {"file-handling-icons", flag_descriptions::kFileHandlingIconsName,
      flag_descriptions::kFileHandlingIconsDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(blink::features::kFileHandlingIcons)},
@@ -7760,17 +7734,6 @@
      flag_descriptions::kWebGpuDeveloperFeaturesDescription, kOsAll,
      SINGLE_VALUE_TYPE(switches::kEnableWebGPUDeveloperFeatures)},
 
-#if BUILDFLAG(IS_ANDROID)
-    {"autofill-use-mobile-label-disambiguation",
-     flag_descriptions::kAutofillUseMobileLabelDisambiguationName,
-     flag_descriptions::kAutofillUseMobileLabelDisambiguationDescription,
-     kOsAndroid,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(
-         autofill::features::kAutofillUseMobileLabelDisambiguation,
-         kAutofillUseMobileLabelDisambiguationVariations,
-         "AutofillUseMobileLabelDisambiguation")},
-#endif  // BUILDFLAG(IS_ANDROID)
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     {"gesture-properties-dbus-service",
      flag_descriptions::kEnableGesturePropertiesDBusServiceName,
@@ -10783,6 +10746,15 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(switches::kEnableBoundSessionCredentials,
                                     kEnableBoundSessionCredentialsVariations,
                                     "EnableBoundSessionCredentials")},
+    {"enable-bound-session-credentials-software-keys-for-manual-testing",
+     flag_descriptions::
+         kEnableBoundSessionCredentialsSoftwareKeysForManualTestingName,
+     flag_descriptions::
+         kEnableBoundSessionCredentialsSoftwareKeysForManualTestingDescription,
+     kOsMac | kOsWin | kOsLinux,
+     FEATURE_VALUE_TYPE(
+         unexportable_keys::
+             kEnableBoundSessionCredentialsSoftwareKeysForManualTesting)},
 #endif  // BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/accessibility/live_caption/live_caption_automatic_language_download_browsertest.cc b/chrome/browser/accessibility/live_caption/live_caption_automatic_language_download_browsertest.cc
index a19cd35..68f89840 100644
--- a/chrome/browser/accessibility/live_caption/live_caption_automatic_language_download_browsertest.cc
+++ b/chrome/browser/accessibility/live_caption/live_caption_automatic_language_download_browsertest.cc
@@ -92,20 +92,20 @@
       frame_host, "de-de", media::mojom::ConfidenceLevel::kHighlyConfident,
       media::mojom::AsrSwitchResult::kDefaultNoSwitch);
   OnLanguageIdentificationEvent(
-      frame_host, "fr-fr", media::mojom::ConfidenceLevel::kHighlyConfident,
+      frame_host, "es-es", media::mojom::ConfidenceLevel::kHighlyConfident,
       media::mojom::AsrSwitchResult::kDefaultNoSwitch);
   OnLanguageIdentificationEvent(
-      frame_host, "fr-fr", media::mojom::ConfidenceLevel::kHighlyConfident,
+      frame_host, "es-es", media::mojom::ConfidenceLevel::kHighlyConfident,
       media::mojom::AsrSwitchResult::kDefaultNoSwitch);
 
   OnLanguageIdentificationEvent(
-      frame_host, "es-us", media::mojom::ConfidenceLevel::kHighlyConfident,
+      frame_host, "fr-fr", media::mojom::ConfidenceLevel::kHighlyConfident,
       media::mojom::AsrSwitchResult::kDefaultNoSwitch);
   OnLanguageIdentificationEvent(
-      frame_host, "es-us", media::mojom::ConfidenceLevel::kHighlyConfident,
+      frame_host, "fr-fr", media::mojom::ConfidenceLevel::kHighlyConfident,
       media::mojom::AsrSwitchResult::kDefaultNoSwitch);
   OnLanguageIdentificationEvent(
-      frame_host, "es-us", media::mojom::ConfidenceLevel::kHighlyConfident,
+      frame_host, "fr-fr", media::mojom::ConfidenceLevel::kHighlyConfident,
       media::mojom::AsrSwitchResult::kDefaultNoSwitch);
 
   size_t expected_language_pack_count = 2u;
@@ -113,10 +113,10 @@
   std::set<speech::LanguageCode> installed_languages = GetInstalledLanguages();
   ASSERT_EQ(expected_language_pack_count, installed_languages.size());
 
-  // The en-US language pack is downloaded by default. Only the es-ES language
+  // The en-US language pack is downloaded by default. Only the fr-FR language
   // pack should be automatically downloaded.
   ASSERT_TRUE(base::Contains(installed_languages, speech::LanguageCode::kEnUs));
-  ASSERT_TRUE(base::Contains(installed_languages, speech::LanguageCode::kEsEs));
+  ASSERT_TRUE(base::Contains(installed_languages, speech::LanguageCode::kFrFr));
 }
 
 }  // namespace captions
diff --git a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
index b7cb952..f30baff 100644
--- a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
+++ b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
@@ -329,8 +329,7 @@
     if (language_identification_event_count_ ==
         kLanguageIdentificationEventCountThreshold) {
       std::optional<speech::SodaLanguagePackComponentConfig> language_config =
-          speech::GetLanguageComponentConfigMatchingLanguageSubtag(
-              event->language);
+          speech::GetLanguageComponentConfig(event->language);
 
       if (language_config.has_value() &&
           IsLanguageInstallable(language_config.value().language_name)) {
diff --git a/chrome/browser/apps/link_capturing/DIR_METADATA b/chrome/browser/apps/link_capturing/DIR_METADATA
new file mode 100644
index 0000000..2bb73c4
--- /dev/null
+++ b/chrome/browser/apps/link_capturing/DIR_METADATA
@@ -0,0 +1,5 @@
+monorail: {
+  # Use WebAppInstalls as a catch-all component.
+  # ChromeOS-specific issues should be triaged to Platform>Apps>Foundation.
+  component: "UI>Browser>WebAppInstalls"
+}
diff --git a/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc b/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
index 6d119f3..de47382 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
@@ -63,6 +63,13 @@
 #include "content/public/test/test_utils.h"
 #include "net/http/http_status_code.h"
 
+// TODO(https://crbug.com/1512521): Failing on ASan/Lsan builder on Linux.
+#if defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_LINUX)
+#define MAYBE_ASAN(x) DISABLED_##x
+#else
+#define MAYBE_ASAN(x) x
+#endif
+
 namespace ash {
 namespace {
 
@@ -925,7 +932,8 @@
 }
 
 // FRE explicitly required in VPD, but the state keys are missing.
-IN_PROC_BROWSER_TEST_F(AutoEnrollmentNoStateKeys, FREExplicitlyRequired) {
+IN_PROC_BROWSER_TEST_F(AutoEnrollmentNoStateKeys,
+                       MAYBE_ASAN(FREExplicitlyRequired)) {
   SetFRERequiredKey("1");
   host()->StartWizard(AutoEnrollmentCheckScreenView::kScreenId);
   WaitForOobeUI();
@@ -938,7 +946,7 @@
 
 // FRE explicitly required when kCheckEnrollmentKey is set to an invalid value.
 IN_PROC_BROWSER_TEST_F(AutoEnrollmentNoStateKeys,
-                       FREExplicitlyRequiredInvalid) {
+                       MAYBE_ASAN(FREExplicitlyRequiredInvalid)) {
   SetFRERequiredKey("anything");
   host()->StartWizard(AutoEnrollmentCheckScreenView::kScreenId);
   WaitForOobeUI();
@@ -958,7 +966,8 @@
 
 // FRE explicitly not required in VPD, so it should not even contact the policy
 // server.
-IN_PROC_BROWSER_TEST_F(AutoEnrollmentWithStatistics, ExplicitlyNotRequired) {
+IN_PROC_BROWSER_TEST_F(AutoEnrollmentWithStatistics,
+                       MAYBE_ASAN(ExplicitlyNotRequired)) {
   SetFRERequiredKey("0");
 
   // Should be ignored.
@@ -986,7 +995,8 @@
 }
 
 // FRE is required when VPD is valid and activate date is there.
-IN_PROC_BROWSER_TEST_F(AutoEnrollmentWithStatistics, MachineActivated) {
+IN_PROC_BROWSER_TEST_F(AutoEnrollmentWithStatistics,
+                       MAYBE_ASAN(MachineActivated)) {
   SetActivateDate("1970-01");
 
   EXPECT_TRUE(policy_server_.SetDeviceStateRetrievalResponse(
@@ -1000,7 +1010,7 @@
 }
 
 // FRE is required when VPD in invalid state.
-IN_PROC_BROWSER_TEST_F(AutoEnrollmentWithStatistics, CorruptedVPD) {
+IN_PROC_BROWSER_TEST_F(AutoEnrollmentWithStatistics, MAYBE_ASAN(CorruptedVPD)) {
   SetVPDCorrupted();
 
   EXPECT_TRUE(policy_server_.SetDeviceStateRetrievalResponse(
diff --git a/chrome/browser/ash/login/password_change_browsertest.cc b/chrome/browser/ash/login/password_change_browsertest.cc
index 3ccd149..c5bac3e 100644
--- a/chrome/browser/ash/login/password_change_browsertest.cc
+++ b/chrome/browser/ash/login/password_change_browsertest.cc
@@ -74,6 +74,11 @@
 
 using AuthOp = FakeUserDataAuthClient::Operation;
 
+bool HasPasswordConfirmationPage() {
+  return !base::FeatureList::IsEnabled(
+      ash::features::kCryptohomeRecoveryBeforeFlowSplit);
+}
+
 }  // namespace
 
 class PasswordChangeTestBase : public LoginManagerTest {
@@ -179,6 +184,12 @@
       test_user_info_.auth_config.online_password);
   test::PasswordChangedSubmitOldPassword();
 
+  if (HasPasswordConfirmationPage()) {
+    test::CreatePasswordUpdateNoticePageWaiter()->Wait();
+    test::PasswordUpdateNoticeExpectDone();
+    test::PasswordUpdateNoticeDoneAction();
+  }
+
   // User session should start, and whole OOBE screen is expected to be hidden.
   OobeWindowVisibilityWaiter(false).Wait();
 
@@ -204,6 +215,12 @@
       nullptr, ui::VKEY_RETURN, false /* control */, false /* shift */,
       false /* alt */, false /* command */));
 
+  if (HasPasswordConfirmationPage()) {
+    test::CreatePasswordUpdateNoticePageWaiter()->Wait();
+    test::PasswordUpdateNoticeExpectDone();
+    test::PasswordUpdateNoticeDoneAction();
+  }
+
   // User session should start, and whole OOBE screen is expected to be hidden,
   OobeWindowVisibilityWaiter(false).Wait();
   EXPECT_TRUE(
@@ -234,6 +251,12 @@
       test_user_info_.auth_config.online_password);
   test::PasswordChangedSubmitOldPassword();
 
+  if (HasPasswordConfirmationPage()) {
+    test::CreatePasswordUpdateNoticePageWaiter()->Wait();
+    test::PasswordUpdateNoticeExpectDone();
+    test::PasswordUpdateNoticeDoneAction();
+  }
+
   // User session should start, and whole OOBE screen is expected to be hidden.
   OobeWindowVisibilityWaiter(false).Wait();
   login_mixin_.WaitForActiveSession();
@@ -253,10 +276,10 @@
   test::LocalDataLossWarningPageWaiter()->Wait();
 
   test::LocalDataLossWarningPageExpectGoBack();
-  test::LocalDataLossWarningPageExpectProceed();
+  test::LocalDataLossWarningPageExpectRemove();
 
   // Click "Proceed anyway".
-  test::LocalDataLossWarningPageProceedAction();
+  test::LocalDataLossWarningPageRemoveAction();
 
   // With cryptohome recovery we re-create session and re-run onboarding.
   OobeWindowVisibilityWaiter(true).Wait();
@@ -277,7 +300,8 @@
   test::LocalDataLossWarningPageWaiter()->Wait();
 
   test::LocalDataLossWarningPageExpectGoBack();
-  test::LocalDataLossWarningPageExpectProceed();
+  test::LocalDataLossWarningPageExpectRemove();
+
   // Go back to old password input by clicking Try Again.
   test::LocalDataLossWarningPageGoBackAction();
 
@@ -289,6 +313,12 @@
       test_user_info_.auth_config.online_password);
   test::PasswordChangedSubmitOldPassword();
 
+  if (HasPasswordConfirmationPage()) {
+    test::CreatePasswordUpdateNoticePageWaiter()->Wait();
+    test::PasswordUpdateNoticeExpectDone();
+    test::PasswordUpdateNoticeDoneAction();
+  }
+
   // User session should start, and whole OOBE screen is expected to be hidden,
   OobeWindowVisibilityWaiter(false).Wait();
   EXPECT_TRUE(
@@ -320,8 +350,7 @@
   OpenGaiaDialog(test_account_id_);
   SetGaiaScreenCredentials(test_account_id_, test::kNewPassword);
 
-  OobeWindowVisibilityWaiter(true).Wait();
-  OobeScreenWaiter(GaiaPasswordChangedView::kScreenId).Wait();
+  test::CreateOldPasswordEnterPageWaiter()->Wait();
 }
 
 class PasswordChangeTokenCheck : public PasswordChangeTest {
diff --git a/chrome/browser/ash/login/screens/osauth/apply_online_password_screen.cc b/chrome/browser/ash/login/screens/osauth/apply_online_password_screen.cc
index 7ecdca0..0d16f8c0 100644
--- a/chrome/browser/ash/login/screens/osauth/apply_online_password_screen.cc
+++ b/chrome/browser/ash/login/screens/osauth/apply_online_password_screen.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ash/login/screens/osauth/apply_online_password_screen.h"
 
 #include "ash/constants/ash_features.h"
+#include "base/check.h"
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/login/quick_unlock/quick_unlock_factory.h"
@@ -64,14 +65,14 @@
 }
 
 bool ApplyOnlinePasswordScreen::MaybeSkip(WizardContext& wizard_context) {
-  CHECK(features::AreLocalPasswordsEnabledForConsumers());
   return false;
 }
 
 void ApplyOnlinePasswordScreen::InspectContext(UserContext* user_context) {
   if (!user_context) {
     LOG(ERROR) << "Session expired while waiting for user's decision";
-    exit_callback_.Run(Result::kSuccess);
+    context()->osauth_error = WizardContext::OSAuthErrorKind::kFatal;
+    exit_callback_.Run(Result::kError);
     return;
   }
   CHECK(user_context->HasAuthFactorsConfiguration());
@@ -90,10 +91,22 @@
           quick_unlock::QuickUnlockFactory::GetDelegate(),
           g_browser_process->local_state());
 
-  password_factor_editor.SetOnlinePassword(
-      GetToken(), online_password_.value().value(),
-      base::BindOnce(&ApplyOnlinePasswordScreen::OnOnlinePasswordSet,
-                     weak_ptr_factory_.GetWeakPtr()));
+  if (context()->knowledge_factor_setup.auth_setup_flow ==
+      WizardContext::AuthChangeFlow::kInitialSetup) {
+    CHECK(!auth_factors_config_.HasConfiguredFactor(
+        cryptohome::AuthFactorType::kPassword));
+    password_factor_editor.SetOnlinePassword(
+        GetToken(), online_password_.value().value(),
+        base::BindOnce(&ApplyOnlinePasswordScreen::OnOnlinePasswordSet,
+                       weak_ptr_factory_.GetWeakPtr()));
+  } else {
+    CHECK(auth_factors_config_.HasConfiguredFactor(
+        cryptohome::AuthFactorType::kPassword));
+    password_factor_editor.UpdateOnlinePassword(
+        GetToken(), online_password_.value().value(),
+        base::BindOnce(&ApplyOnlinePasswordScreen::OnOnlinePasswordSet,
+                       weak_ptr_factory_.GetWeakPtr()));
+  }
 }
 
 void ApplyOnlinePasswordScreen::OnOnlinePasswordSet(
@@ -103,6 +116,8 @@
     exit_callback_.Run(Result::kError);
     LOG(ERROR) << "Could not set online password";
   } else {
+    context()->knowledge_factor_setup.modified_factors.Put(
+        AshAuthFactor::kGaiaPassword);
     exit_callback_.Run(Result::kSuccess);
   }
 }
diff --git a/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen.cc b/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen.cc
index 83eaa66..df04f7c 100644
--- a/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen.cc
+++ b/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen.h"
 
 #include "ash/constants/ash_features.h"
+#include "base/check.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/login/reauth_stats.h"
 #include "chrome/browser/ash/login/wizard_context.h"
@@ -12,6 +13,10 @@
 #include "chrome/browser/ui/webui/ash/login/cryptohome_recovery_screen_handler.h"
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
 #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
+#include "chromeos/ash/components/login/auth/public/auth_factors_configuration.h"
+#include "chromeos/ash/components/login/auth/public/user_context.h"
+#include "chromeos/ash/components/osauth/public/auth_session_storage.h"
+#include "chromeos/ash/services/auth_factor_config/auth_factor_config_utils.h"
 #include "components/user_manager/user_manager.h"
 
 namespace {
@@ -31,20 +36,26 @@
 // static
 std::string CryptohomeRecoveryScreen::GetResultString(Result result) {
   switch (result) {
-    case Result::kSucceeded:
+    case Result::kObsoleteSucceeded:
       return "Succeeded";
     case Result::kGaiaLogin:
       return "GaiaLogin";
-    case Result::kManualRecovery:
+    case Result::kObsoleteManualRecovery:
       return "ManualRecovery";
-    case Result::kRetry:
+    case Result::kObsoleteRetry:
       return "Retry";
-    case Result::kNoRecoveryFactor:
+    case Result::kObsoleteNoRecoveryFactor:
       return "NoRecoveryFactor";
-    case Result::kNotApplicable:
-      return BaseScreen::kNotApplicable;
-    case Result::kTimeout:
+    case Result::kObsoleteTimeout:
       return "Timeout";
+    case Result::kAuthenticated:
+      return "Authenticated";
+    case Result::kError:
+      return "Error";
+    case Result::kFallbackLocal:
+      return "FallbackLocal";
+    case Result::kFallbackOnline:
+      return "FallbackOnline";
   }
 }
 
@@ -77,15 +88,15 @@
 void CryptohomeRecoveryScreen::OnUserAction(const base::Value::List& args) {
   const std::string& action_id = args[0].GetString();
   if (action_id == kUserActionDone) {
-    exit_callback_.Run(Result::kSucceeded);
+    exit_callback_.Run(Result::kObsoleteSucceeded);
   } else if (action_id == kUserActionRetry) {
     // TODO(b/257073746): We probably want to differentiate between retry with
     // or without login.
     RecordReauthReason(context()->user_context->GetAccountId(),
                        ReauthReason::kCryptohomeRecovery);
-    exit_callback_.Run(Result::kRetry);
+    exit_callback_.Run(Result::kObsoleteRetry);
   } else if (action_id == kUserActionEnterOldPassword) {
-    exit_callback_.Run(Result::kManualRecovery);
+    exit_callback_.Run(Result::kObsoleteManualRecovery);
   } else if (action_id == kUserActionReauth) {
     exit_callback_.Run(Result::kGaiaLogin);
   } else {
@@ -100,7 +111,13 @@
     LOG(ERROR) << "Failed to get auth factors configuration, code "
                << error->get_cryptohome_code();
     context()->user_context = std::move(user_context);
-    view_->OnRecoveryFailed();
+    if (base::FeatureList::IsEnabled(
+            ash::features::kCryptohomeRecoveryBeforeFlowSplit)) {
+      view_->OnRecoveryFailed();
+      return;
+    }
+    context()->osauth_error = WizardContext::OSAuthErrorKind::kFatal;
+    exit_callback_.Run(Result::kError);
     return;
   }
 
@@ -109,20 +126,28 @@
       config.HasConfiguredFactor(cryptohome::AuthFactorType::kRecovery);
   if (is_configured) {
     if (user_context->GetReauthProofToken().empty()) {
+      auto account_id = user_context->GetAccountId();
+      context()->user_context = std::move(user_context);
       if (was_reauth_proof_token_missing_) {
         LOG(ERROR)
             << "Reauth proof token is still missing after the second attempt";
-        view_->OnRecoveryFailed();
+        if (base::FeatureList::IsEnabled(
+                ash::features::kCryptohomeRecoveryBeforeFlowSplit)) {
+          view_->OnRecoveryFailed();
+          return;
+        }
+        context()->osauth_error = WizardContext::OSAuthErrorKind::kFatal;
+        exit_callback_.Run(Result::kError);
+        return;
       } else {
         LOG(WARNING) << "Reauth proof token is not present";
         was_reauth_proof_token_missing_ = true;
-        RecordReauthReason(user_context->GetAccountId(),
-                           ReauthReason::kCryptohomeRecovery);
+        RecordReauthReason(account_id, ReauthReason::kCryptohomeRecovery);
         view_->ShowReauthNotification();
+        return;
       }
-      context()->user_context = std::move(user_context);
-      return;
     }
+    CHECK(user_context->HasAuthFactorsConfiguration());
     recovery_performer_ = std::make_unique<CryptohomeRecoveryPerformer>(
         UserDataAuthClient::Get(),
         g_browser_process->shared_url_loader_factory());
@@ -131,8 +156,27 @@
         base::BindOnce(&CryptohomeRecoveryScreen::OnAuthenticateWithRecovery,
                        weak_ptr_factory_.GetWeakPtr()));
   } else {
+    if (base::FeatureList::IsEnabled(
+            ash::features::kCryptohomeRecoveryBeforeFlowSplit)) {
+      context()->user_context = std::move(user_context);
+      exit_callback_.Run(Result::kObsoleteNoRecoveryFactor);
+      return;
+    }
+    CHECK(user_context->HasAuthFactorsConfiguration());
+    const auto& auth_config = user_context->GetAuthFactorsConfiguration();
+
+    bool has_online_password = false;
+    if (auth_config.HasConfiguredFactor(
+            cryptohome::AuthFactorType::kPassword)) {
+      has_online_password = auth::IsGaiaPassword(
+          *auth_config.FindFactorByType(cryptohome::AuthFactorType::kPassword));
+    }
     context()->user_context = std::move(user_context);
-    exit_callback_.Run(Result::kNoRecoveryFactor);
+    if (has_online_password) {
+      exit_callback_.Run(Result::kFallbackOnline);
+    } else {
+      exit_callback_.Run(Result::kFallbackLocal);
+    }
   }
 }
 
@@ -143,7 +187,14 @@
     LOG(ERROR) << "Failed to authenticate with recovery, "
                << error->ToDebugString();
     context()->user_context = std::move(user_context);
-    view_->OnRecoveryFailed();
+    if (base::FeatureList::IsEnabled(
+            ash::features::kCryptohomeRecoveryBeforeFlowSplit)) {
+      view_->OnRecoveryFailed();
+      return;
+    }
+    context()->osauth_error =
+        WizardContext::OSAuthErrorKind::kRecoveryAuthenticationFailed;
+    exit_callback_.Run(Result::kError);
     return;
   }
 
@@ -159,7 +210,23 @@
   if (error.has_value()) {
     LOG(ERROR) << "Failed to rotate recovery factor, code "
                << error->get_cryptohome_code();
-    // TODO(b/289472295): handle failure scenario.
+    context()->extra_factors_token =
+        ash::AuthSessionStorage::Get()->Store(std::move(user_context));
+    context()->osauth_error =
+        WizardContext::OSAuthErrorKind::kRecoveryRotationFailed;
+    exit_callback_.Run(Result::kError);
+    return;
+  }
+
+  if (!base::FeatureList::IsEnabled(
+          ash::features::kCryptohomeRecoveryBeforeFlowSplit)) {
+    // Get AuthFactorsConfiguration again, as it was cleared after
+    // rotation.
+    auth_factor_editor_.GetAuthFactorsConfiguration(
+        std::move(user_context),
+        base::BindOnce(&CryptohomeRecoveryScreen::OnRefreshFactorsConfiguration,
+                       weak_ptr_factory_.GetWeakPtr()));
+    return;
   }
 
   std::string key_label;
@@ -184,6 +251,22 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
+void CryptohomeRecoveryScreen::OnRefreshFactorsConfiguration(
+    std::unique_ptr<UserContext> user_context,
+    absl::optional<AuthenticationError> error) {
+  if (error.has_value()) {
+    LOG(ERROR) << "Failed to get auth factors configuration, code "
+               << error->get_cryptohome_code();
+    context()->user_context = std::move(user_context);
+    context()->osauth_error = WizardContext::OSAuthErrorKind::kFatal;
+    exit_callback_.Run(Result::kError);
+    return;
+  }
+  context()->extra_factors_token =
+      ash::AuthSessionStorage::Get()->Store(std::move(user_context));
+  exit_callback_.Run(Result::kAuthenticated);
+}
+
 void CryptohomeRecoveryScreen::OnReplaceContextKey(
     std::unique_ptr<UserContext> user_context,
     std::optional<AuthenticationError> error) {
@@ -210,7 +293,7 @@
 
 void CryptohomeRecoveryScreen::OnAuthSessionExpired() {
   LOG(WARNING) << "Exiting due to expired Auth Session.";
-  exit_callback_.Run(Result::kTimeout);
+  exit_callback_.Run(Result::kObsoleteTimeout);
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen.h b/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen.h
index 1e3efec7..aebf6bec 100644
--- a/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen.h
+++ b/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_ASH_LOGIN_SCREENS_OSAUTH_CRYPTOHOME_RECOVERY_SCREEN_H_
 #define CHROME_BROWSER_ASH_LOGIN_SCREENS_OSAUTH_CRYPTOHOME_RECOVERY_SCREEN_H_
 
+#include <memory>
+
 #include "base/functional/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
@@ -13,6 +15,7 @@
 #include "chromeos/ash/components/login/auth/public/user_context.h"
 #include "chromeos/ash/components/login/auth/recovery/cryptohome_recovery_performer.h"
 #include "components/account_id/account_id.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash {
 
@@ -23,13 +26,17 @@
  public:
   using TView = CryptohomeRecoveryScreenView;
   enum class Result {
-    kSucceeded,
+    kObsoleteSucceeded,
+    kObsoleteManualRecovery,
+    kObsoleteRetry,
+    kObsoleteNoRecoveryFactor,
+    kObsoleteTimeout,
+
     kGaiaLogin,
-    kManualRecovery,
-    kRetry,
-    kNoRecoveryFactor,
-    kNotApplicable,
-    kTimeout,
+    kAuthenticated,
+    kError,
+    kFallbackOnline,
+    kFallbackLocal
   };
   static std::string GetResultString(Result result);
   using ScreenExitCallback = base::RepeatingCallback<void(Result result)>;
@@ -66,6 +73,9 @@
                            std::optional<AuthenticationError> error);
   void OnAuthSessionExpired();
 
+  void OnRefreshFactorsConfiguration(std::unique_ptr<UserContext> user_context,
+                                     absl::optional<AuthenticationError> error);
+
   std::unique_ptr<base::OneShotTimer> expiration_timer_;
   AuthFactorEditor auth_factor_editor_;
   std::unique_ptr<CryptohomeRecoveryPerformer> recovery_performer_;
diff --git a/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen_browsertest.cc b/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen_browsertest.cc
index 558755a..0b56422d 100644
--- a/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen_browsertest.cc
@@ -4,9 +4,11 @@
 
 #include "chrome/browser/ash/login/screens/osauth/cryptohome_recovery_setup_screen.h"
 
+#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/login_screen_test_api.h"
 #include "base/run_loop.h"
 #include "base/test/test_future.h"
+#include "chrome/browser/ash/login/test/auth_ui_utils.h"
 #include "chrome/browser/ash/login/test/cryptohome_mixin.h"
 #include "chrome/browser/ash/login/test/fake_recovery_service_mixin.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
@@ -17,6 +19,7 @@
 #include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ui/webui/ash/login/cryptohome_recovery_screen_handler.h"
+#include "chrome/browser/ui/webui/ash/login/enter_old_password_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_password_changed_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
@@ -30,17 +33,17 @@
 
 namespace {
 
-const test::UIPath kSuccessStep = {"cryptohome-recovery", "successDialog"};
-const test::UIPath kErrorStep = {"cryptohome-recovery", "errorDialog"};
 const test::UIPath kReauthNotificationStep = {"cryptohome-recovery",
                                               "reauthNotificationDialog"};
-const test::UIPath kDoneButton = {"cryptohome-recovery", "doneButton"};
-const test::UIPath kManualRecoveryButton = {"cryptohome-recovery",
-                                            "manualRecoveryButton"};
 const test::UIPath kRetryButton = {"cryptohome-recovery", "retryButton"};
 const test::UIPath kReauthButton = {"cryptohome-recovery", "reauthButton"};
 
 const char kNewPassword[] = "new user password";
+
+bool IsOldFlow() {
+  return base::FeatureList::IsEnabled(
+      ash::features::kCryptohomeRecoveryBeforeFlowSplit);
+}
 }  // namespace
 
 class CryptohomeRecoveryScreenTestBase : public OobeBaseTest {
@@ -202,12 +205,20 @@
   SetUpExitCallback();
   SetGaiaScreenCredentials(test_user_.account_id, kNewPassword);
 
-  OobeScreenWaiter(CryptohomeRecoveryScreenView::kScreenId).Wait();
-  test::OobeJS().CreateVisibilityWaiter(true, kSuccessStep)->Wait();
-  test::OobeJS().ClickOnPath(kDoneButton);
+  if (!IsOldFlow()) {
+    WaitForScreenExit();
+    EXPECT_EQ(result_.value(),
+              CryptohomeRecoveryScreen::Result::kAuthenticated);
+  }
 
-  WaitForScreenExit();
-  EXPECT_EQ(result_.value(), CryptohomeRecoveryScreen::Result::kSucceeded);
+  test::RecoveryPasswordUpdatedPageWaiter()->Wait();
+  test::RecoveryPasswordUpdatedProceedAction();
+
+  if (IsOldFlow()) {
+    WaitForScreenExit();
+    EXPECT_EQ(result_.value(),
+              CryptohomeRecoveryScreen::Result::kObsoleteSucceeded);
+  }
 
   OobeWindowVisibilityWaiter(false).Wait();
   login_manager_mixin_.WaitForActiveSession();
@@ -232,9 +243,15 @@
   OobeScreenWaiter(CryptohomeRecoveryScreenView::kScreenId).Wait();
 
   WaitForScreenExit();
-  EXPECT_EQ(result_.value(),
-            CryptohomeRecoveryScreen::Result::kNoRecoveryFactor);
-  OobeScreenWaiter(GaiaPasswordChangedView::kScreenId).Wait();
+  if (IsOldFlow()) {
+    EXPECT_EQ(result_.value(),
+              CryptohomeRecoveryScreen::Result::kObsoleteNoRecoveryFactor);
+    OobeScreenWaiter(GaiaPasswordChangedView::kScreenId).Wait();
+  } else {
+    EXPECT_EQ(result_.value(),
+              CryptohomeRecoveryScreen::Result::kFallbackOnline);
+    OobeScreenWaiter(EnterOldPasswordScreenView::kScreenId).Wait();
+  }
   EXPECT_FALSE(IsMounted());
 }
 
@@ -249,18 +266,30 @@
   SetUpExitCallback();
   SetGaiaScreenCredentials(test_user_.account_id, kNewPassword);
 
-  OobeScreenWaiter(CryptohomeRecoveryScreenView::kScreenId).Wait();
-  test::OobeJS().CreateVisibilityWaiter(true, kErrorStep)->Wait();
-  test::OobeJS().ClickOnPath(kManualRecoveryButton);
+  if (IsOldFlow()) {
+    test::RecoveryErrorPageWaiter()->Wait();
+    test::RecoveryErrorExpectFallback();
+    test::RecoveryErrorFallbackAction();
 
-  WaitForScreenExit();
-  EXPECT_EQ(result_.value(), CryptohomeRecoveryScreen::Result::kManualRecovery);
-  OobeScreenWaiter(GaiaPasswordChangedView::kScreenId).Wait();
+    WaitForScreenExit();
+    EXPECT_EQ(result_.value(),
+              CryptohomeRecoveryScreen::Result::kObsoleteManualRecovery);
+  } else {
+    WaitForScreenExit();
+    EXPECT_EQ(result_.value(), CryptohomeRecoveryScreen::Result::kError);
+  }
+  test::CreateOldPasswordEnterPageWaiter()->Wait();
   EXPECT_FALSE(IsMounted());
 }
 
 // Verifies that we could retry when there is error during recovery.
 IN_PROC_BROWSER_TEST_F(CryptohomeRecoveryScreenTest, RetryAfterError) {
+  // Ignore this test after Recovery screen split, it became responsibility
+  // of another screen.
+  if (!IsOldFlow()) {
+    return;
+  }
+
   SetupFakeGaia(test_user_);
   fake_recovery_service_.SetErrorResponse("/v1/cryptorecovery",
                                           net::HTTP_BAD_REQUEST);
@@ -269,24 +298,23 @@
   SetUpExitCallback();
   SetGaiaScreenCredentials(test_user_.account_id, kNewPassword);
 
-  OobeScreenWaiter(CryptohomeRecoveryScreenView::kScreenId).Wait();
-  test::OobeJS().CreateVisibilityWaiter(true, kErrorStep)->Wait();
+  test::RecoveryErrorPageWaiter()->Wait();
   test::OobeJS().ClickOnPath(kRetryButton);
 
   WaitForScreenExit();
-  EXPECT_EQ(result_.value(), CryptohomeRecoveryScreen::Result::kRetry);
+  EXPECT_EQ(result_.value(), CryptohomeRecoveryScreen::Result::kObsoleteRetry);
 
   fake_recovery_service_.SetErrorResponse("/v1/cryptorecovery", net::HTTP_OK);
 
   OobeScreenWaiter(GaiaView::kScreenId).Wait();
   SetGaiaScreenCredentials(test_user_.account_id, kNewPassword);
 
-  OobeScreenWaiter(CryptohomeRecoveryScreenView::kScreenId).Wait();
-  test::OobeJS().CreateVisibilityWaiter(true, kSuccessStep)->Wait();
-  test::OobeJS().ClickOnPath(kDoneButton);
+  test::RecoveryPasswordUpdatedPageWaiter()->Wait();
+  test::RecoveryPasswordUpdatedProceedAction();
 
   WaitForScreenExit();
-  EXPECT_EQ(result_.value(), CryptohomeRecoveryScreen::Result::kSucceeded);
+  EXPECT_EQ(result_.value(),
+            CryptohomeRecoveryScreen::Result::kObsoleteSucceeded);
 
   OobeWindowVisibilityWaiter(false).Wait();
   login_manager_mixin_.WaitForActiveSession();
@@ -297,6 +325,11 @@
 // when password change is detected.
 IN_PROC_BROWSER_TEST_F(CryptohomeRecoveryScreenTest,
                        MissingReauthTokenDuringRecovery) {
+  // Ignore this test after Recovery screen split, logic responsibility
+  // of another test.
+  if (!IsOldFlow()) {
+    return;
+  }
   SetupFakeGaia(test_user_);
 
   // Entering the add person flow with an existing account. Reauth token was not
@@ -319,12 +352,12 @@
   OobeScreenWaiter(GaiaView::kScreenId).Wait();
   SetGaiaScreenCredentials(test_user_.account_id, kNewPassword);
 
-  OobeScreenWaiter(CryptohomeRecoveryScreenView::kScreenId).Wait();
-  test::OobeJS().CreateVisibilityWaiter(true, kSuccessStep)->Wait();
-  test::OobeJS().ClickOnPath(kDoneButton);
+  test::RecoveryPasswordUpdatedPageWaiter()->Wait();
+  test::RecoveryPasswordUpdatedProceedAction();
 
   WaitForScreenExit();
-  EXPECT_EQ(result_.value(), CryptohomeRecoveryScreen::Result::kSucceeded);
+  EXPECT_EQ(result_.value(),
+            CryptohomeRecoveryScreen::Result::kObsoleteSucceeded);
 
   OobeWindowVisibilityWaiter(false).Wait();
   login_manager_mixin_.WaitForActiveSession();
@@ -333,6 +366,11 @@
 
 // Recovery is cancelled after timeout.
 IN_PROC_BROWSER_TEST_F(CryptohomeRecoveryScreenTest, CancelledOnTimeout) {
+  // Ignore this test after Recovery screen split, it became responsibility
+  // of another screen.
+  if (!IsOldFlow()) {
+    return;
+  }
   SetupFakeGaia(test_user_);
 
   OpenGaiaDialog(test_user_.account_id);
@@ -343,12 +381,12 @@
   SetUpExitCallback();
   SetGaiaScreenCredentials(test_user_.account_id, kNewPassword);
 
-  OobeScreenWaiter(CryptohomeRecoveryScreenView::kScreenId).Wait();
-  test::OobeJS().CreateVisibilityWaiter(true, kSuccessStep)->Wait();
+  test::RecoveryPasswordUpdatedPageWaiter()->Wait();
   ASSERT_TRUE(FireExpirationTimer());
 
   WaitForScreenExit();
-  EXPECT_EQ(result_.value(), CryptohomeRecoveryScreen::Result::kTimeout);
+  EXPECT_EQ(result_.value(),
+            CryptohomeRecoveryScreen::Result::kObsoleteTimeout);
   EXPECT_FALSE(LoginScreenTestApi::IsOobeDialogVisible());
   EXPECT_FALSE(IsMounted());
 }
@@ -403,12 +441,20 @@
   SetUpExitCallback();
   SetGaiaScreenCredentials(test_user_.account_id, kNewPassword);
 
-  OobeScreenWaiter(CryptohomeRecoveryScreenView::kScreenId).Wait();
-  test::OobeJS().CreateVisibilityWaiter(true, kSuccessStep)->Wait();
-  test::OobeJS().ClickOnPath(kDoneButton);
+  if (!IsOldFlow()) {
+    WaitForScreenExit();
+    EXPECT_EQ(result_.value(),
+              CryptohomeRecoveryScreen::Result::kAuthenticated);
+  }
 
-  WaitForScreenExit();
-  EXPECT_EQ(result_.value(), CryptohomeRecoveryScreen::Result::kSucceeded);
+  test::RecoveryPasswordUpdatedPageWaiter()->Wait();
+  test::RecoveryPasswordUpdatedProceedAction();
+
+  if (IsOldFlow()) {
+    WaitForScreenExit();
+    EXPECT_EQ(result_.value(),
+              CryptohomeRecoveryScreen::Result::kObsoleteSucceeded);
+  }
 
   OobeWindowVisibilityWaiter(false).Wait();
   login_manager_mixin_.WaitForActiveSession();
@@ -432,9 +478,15 @@
   OobeScreenWaiter(CryptohomeRecoveryScreenView::kScreenId).Wait();
 
   WaitForScreenExit();
-  EXPECT_EQ(result_.value(),
-            CryptohomeRecoveryScreen::Result::kNoRecoveryFactor);
-  OobeScreenWaiter(GaiaPasswordChangedView::kScreenId).Wait();
+  if (IsOldFlow()) {
+    EXPECT_EQ(result_.value(),
+              CryptohomeRecoveryScreen::Result::kObsoleteNoRecoveryFactor);
+    OobeScreenWaiter(GaiaPasswordChangedView::kScreenId).Wait();
+  } else {
+    EXPECT_EQ(result_.value(),
+              CryptohomeRecoveryScreen::Result::kFallbackOnline);
+    OobeScreenWaiter(EnterOldPasswordScreenView::kScreenId).Wait();
+  }
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/login/screens/osauth/enter_old_password_screen.cc b/chrome/browser/ash/login/screens/osauth/enter_old_password_screen.cc
index bf8d4ec..339f644 100644
--- a/chrome/browser/ash/login/screens/osauth/enter_old_password_screen.cc
+++ b/chrome/browser/ash/login/screens/osauth/enter_old_password_screen.cc
@@ -34,8 +34,6 @@
       return "CryptohomeError";
     case Result::kAuthenticated:
       return "Authenticated";
-    case Result::kNotApplicable:
-      return BaseScreen::kNotApplicable;
   }
 }
 
@@ -89,13 +87,14 @@
     std::unique_ptr<UserContext> user_context,
     std::optional<AuthenticationError> error) {
   if (error.has_value()) {
+    context()->user_context = std::move(user_context);
     if (cryptohome::ErrorMatches(
             error->get_cryptohome_code(),
             user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED)) {
-      context()->user_context = std::move(user_context);
       view_->ShowWrongPasswordError();
       return;
     }
+    context()->osauth_error = WizardContext::OSAuthErrorKind::kFatal;
     exit_callback_.Run(Result::kCryptohomeError);
     return;
   }
diff --git a/chrome/browser/ash/login/screens/osauth/enter_old_password_screen.h b/chrome/browser/ash/login/screens/osauth/enter_old_password_screen.h
index f973eca..c945f4a6 100644
--- a/chrome/browser/ash/login/screens/osauth/enter_old_password_screen.h
+++ b/chrome/browser/ash/login/screens/osauth/enter_old_password_screen.h
@@ -27,7 +27,6 @@
     kForgotOldPassword,
     kCryptohomeError,
     kAuthenticated,
-    kNotApplicable,
   };
 
   static std::string GetResultString(Result result);
diff --git a/chrome/browser/ash/login/screens/osauth/local_data_loss_warning_screen.cc b/chrome/browser/ash/login/screens/osauth/local_data_loss_warning_screen.cc
index a3f70f9e..b486603 100644
--- a/chrome/browser/ash/login/screens/osauth/local_data_loss_warning_screen.cc
+++ b/chrome/browser/ash/login/screens/osauth/local_data_loss_warning_screen.cc
@@ -40,7 +40,9 @@
   switch (result) {
     case Result::kRemoveUser:
       return "removeUser";
-    case Result::kBack:
+    case Result::kBackToLocalAuth:
+      return "Back";
+    case Result::kBackToOnlineAuth:
       return "Back";
     case Result::kCryptohomeError:
       return "CryptohomeError";
@@ -61,8 +63,11 @@
 LocalDataLossWarningScreen::~LocalDataLossWarningScreen() = default;
 
 void LocalDataLossWarningScreen::ShowImpl() {
+  bool can_go_back = context()->knowledge_factor_setup.data_loss_back_option !=
+                     WizardContext::DataLossBackOptions::kNone;
   view_->Show(isOwner(context()->user_context->GetAccountId()),
-              context()->user_context->GetAccountId().GetUserEmail(), true);
+              context()->user_context->GetAccountId().GetUserEmail(),
+              can_go_back);
 }
 
 void LocalDataLossWarningScreen::OnUserAction(const base::Value::List& args) {
@@ -81,8 +86,17 @@
     SessionManagerClient::Get()->StartDeviceWipe(base::DoNothing());
     return;
   } else if (action_id == kUserActionBack) {
-    exit_callback_.Run(Result::kBack);
-    return;
+    switch (context()->knowledge_factor_setup.data_loss_back_option) {
+      case WizardContext::DataLossBackOptions::kNone:
+        NOTREACHED() << "Back button should not be shown";
+        return;
+      case WizardContext::DataLossBackOptions::kBackToOnlineAuth:
+        exit_callback_.Run(Result::kBackToOnlineAuth);
+        return;
+      case WizardContext::DataLossBackOptions::kBackToLocalAuth:
+        exit_callback_.Run(Result::kBackToLocalAuth);
+        return;
+    }
   } else if (action_id == kUserActionCancel) {
     exit_callback_.Run(Result::kCancel);
     return;
@@ -96,7 +110,7 @@
   context()->user_context = std::move(user_context);
   if (error.has_value()) {
     LOGIN_LOG(ERROR) << "Failed to remove user home directory";
-    // TODO(b/239420684): Send an error to the UI.
+    context()->osauth_error = WizardContext::OSAuthErrorKind::kFatal;
     exit_callback_.Run(Result::kCryptohomeError);
     return;
   }
@@ -113,9 +127,12 @@
   // related to cryptohome state.
   context()->user_context->ResetAuthSessionIds();
   context()->user_context->ClearAuthFactorsConfiguration();
+
   // Move online password back so that it can be used as key.
   // See `ShowImpl()` to see where it was stored.
-  context()->user_context->ReuseReplacementKey();
+  if (context()->user_context->HasReplacementKey()) {
+    context()->user_context->ReuseReplacementKey();
+  }
   exit_callback_.Run(Result::kRemoveUser);
 }
 
diff --git a/chrome/browser/ash/login/screens/osauth/local_data_loss_warning_screen.h b/chrome/browser/ash/login/screens/osauth/local_data_loss_warning_screen.h
index 4e27908..6f95ddb0 100644
--- a/chrome/browser/ash/login/screens/osauth/local_data_loss_warning_screen.h
+++ b/chrome/browser/ash/login/screens/osauth/local_data_loss_warning_screen.h
@@ -24,7 +24,8 @@
 
   enum class Result {
     kRemoveUser,
-    kBack,
+    kBackToOnlineAuth,
+    kBackToLocalAuth,
     kCryptohomeError,
     kCancel,
   };
diff --git a/chrome/browser/ash/login/screens/osauth/osauth_error_screen.cc b/chrome/browser/ash/login/screens/osauth/osauth_error_screen.cc
index 29ab937..de4cc58 100644
--- a/chrome/browser/ash/login/screens/osauth/osauth_error_screen.cc
+++ b/chrome/browser/ash/login/screens/osauth/osauth_error_screen.cc
@@ -16,7 +16,10 @@
 #include "chrome/browser/ash/login/screens/osauth/base_osauth_setup_screen.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ui/webui/ash/login/osauth/osauth_error_screen_handler.h"
+#include "chromeos/ash/components/login/auth/public/auth_factors_configuration.h"
+#include "chromeos/ash/components/login/auth/public/user_context.h"
 #include "chromeos/ash/components/osauth/public/auth_session_storage.h"
+#include "chromeos/ash/services/auth_factor_config/auth_factor_config_utils.h"
 
 namespace ash {
 namespace {
@@ -30,6 +33,12 @@
   switch (result) {
     case Result::kAbortSignin:
       return "AbortSignin";
+    case Result::kFallbackOnline:
+      return "FallbackOnline";
+    case Result::kFallbackLocal:
+      return "FallbackLocal";
+    case Result::kProceedAuthenticated:
+      return "ProceedAuthenticated";
   }
 }
 
@@ -47,6 +56,35 @@
     return;
   }
   CHECK(context()->osauth_error.has_value());
+  if (context()->osauth_error.value() ==
+      WizardContext::OSAuthErrorKind::kRecoveryRotationFailed) {
+    // We don't have UI strings now, so just exit.
+    exit_callback_.Run(Result::kProceedAuthenticated);
+    return;
+  }
+  if (context()->osauth_error.value() ==
+      WizardContext::OSAuthErrorKind::kRecoveryAuthenticationFailed) {
+    // We don't have UI strings now, so just pick right factor and exit.
+    CHECK(context()->user_context);
+    CHECK(context()->user_context->HasAuthFactorsConfiguration());
+    const auto& auth_config =
+        context()->user_context->GetAuthFactorsConfiguration();
+
+    bool has_online_password = false;
+    if (auth_config.HasConfiguredFactor(
+            cryptohome::AuthFactorType::kPassword)) {
+      has_online_password = auth::IsGaiaPassword(
+          *auth_config.FindFactorByType(cryptohome::AuthFactorType::kPassword));
+    }
+    if (has_online_password) {
+      exit_callback_.Run(Result::kFallbackOnline);
+    } else {
+      exit_callback_.Run(Result::kFallbackLocal);
+    }
+    return;
+  }
+  CHECK_EQ(context()->osauth_error.value(),
+           WizardContext::OSAuthErrorKind::kFatal);
   view_->Show();
 }
 
@@ -54,6 +92,9 @@
   CHECK_GE(args.size(), 1u);
   const std::string& action_id = args[0].GetString();
   if (action_id == kUserActionCanel) {
+    if (context()->user_context) {
+      context()->user_context.reset();
+    }
     if (context()->extra_factors_token.has_value()) {
       AuthSessionStorage::Get()->Invalidate(
           GetToken(), base::BindOnce(&OSAuthErrorScreen::OnTokenInvalidated,
diff --git a/chrome/browser/ash/login/screens/osauth/osauth_error_screen.h b/chrome/browser/ash/login/screens/osauth/osauth_error_screen.h
index eb9930bf..2e7b2bb7 100644
--- a/chrome/browser/ash/login/screens/osauth/osauth_error_screen.h
+++ b/chrome/browser/ash/login/screens/osauth/osauth_error_screen.h
@@ -23,6 +23,9 @@
   using TView = OSAuthErrorScreenView;
   enum class Result {
     kAbortSignin,
+    kFallbackOnline,
+    kFallbackLocal,
+    kProceedAuthenticated,
   };
 
   static std::string GetResultString(Result result);
diff --git a/chrome/browser/ash/login/test/auth_ui_utils.cc b/chrome/browser/ash/login/test/auth_ui_utils.cc
index 1470860..7410806 100644
--- a/chrome/browser/ash/login/test/auth_ui_utils.cc
+++ b/chrome/browser/ash/login/test/auth_ui_utils.cc
@@ -13,7 +13,12 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_window_visibility_waiter.h"
 #include "chrome/browser/ash/login/test/test_condition_waiter.h"
+#include "chrome/browser/ui/webui/ash/login/cryptohome_recovery_screen_handler.h"
+#include "chrome/browser/ui/webui/ash/login/enter_old_password_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_password_changed_screen_handler.h"
+#include "chrome/browser/ui/webui/ash/login/osauth/factor_setup_success_screen_handler.h"
+#include "chrome/browser/ui/webui/ash/login/osauth/local_data_loss_warning_screen_handler.h"
+#include "chrome/browser/ui/webui/ash/login/osauth/osauth_error_screen_handler.h"
 
 namespace ash::test {
 
@@ -24,65 +29,229 @@
 const UIPath kForgotPasswordButton = {"gaia-password-changed",
                                       "forgotPasswordButton"};
 
+const UIPath kEnterOldPasswordInputStep = {"enter-old-password",
+                                           "passwordStep"};
+const UIPath kEnterOldPasswordInput = {"enter-old-password",
+                                       "oldPasswordInput"};
+const UIPath kEnterOldPasswordProceedButton = {"enter-old-password", "next"};
+const UIPath kEnterOldPasswordForgotButton = {"enter-old-password",
+                                              "forgotPasswordButton"};
+
 const UIPath kForgotPasswordStep = {"gaia-password-changed", "forgotPassword"};
 const UIPath kForgotCancel = {"gaia-password-changed", "cancelForgot"};
 
 const UIPath kTryAgainRecovery = {"gaia-password-changed", "backButton"};
 const UIPath kProceedAnyway = {"gaia-password-changed", "proceedAnyway"};
+
+const UIPath kDataLossWarningElement = {"local-data-loss-warning"};
+// TODO: why don't we have it?
+const UIPath kDataLossWarningCancel = {"local-data-loss-warning", "cancel"};
+
+const UIPath kDataLossWarningBack = {"local-data-loss-warning", "backButton"};
+const UIPath kDataLossWarningRemove = {"local-data-loss-warning",
+                                       "proceedRemove"};
+const UIPath kDataLossWarningReset = {"local-data-loss-warning", "powerwash"};
+
+const test::UIPath kRecoverySuccessStep = {"cryptohome-recovery",
+                                           "successDialog"};
+const test::UIPath kRecoveryDoneButton = {"cryptohome-recovery", "doneButton"};
+const test::UIPath kRecoveryErrorStep = {"cryptohome-recovery", "errorDialog"};
+const test::UIPath kRecoveryManualRecoveryButton = {"cryptohome-recovery",
+                                                    "manualRecoveryButton"};
+
+const UIPath kFactorSetupSuccessElement = {"factor-setup-success"};
+const UIPath kFactorSetupSuccessDoneButton = {"factor-setup-success",
+                                              "doneButton"};
+const UIPath kFactorSetupSuccessNextButton = {"factor-setup-success",
+                                              "nextButton"};
+
+bool IsOldFlow() {
+  return base::FeatureList::IsEnabled(
+      ash::features::kCryptohomeRecoveryBeforeFlowSplit);
+}
+
 }  // namespace
 
 // Password change scenario
 
 std::unique_ptr<test::TestConditionWaiter> CreateOldPasswordEnterPageWaiter() {
+  if (IsOldFlow()) {
+    return std::make_unique<CompositeWaiter>(
+        std::make_unique<OobeWindowVisibilityWaiter>(true),
+        std::make_unique<OobeScreenWaiter>(GaiaPasswordChangedView::kScreenId),
+        OobeJS().CreateVisibilityWaiter(true, kPasswordStep));
+  }
   return std::make_unique<CompositeWaiter>(
       std::make_unique<OobeWindowVisibilityWaiter>(true),
-      std::make_unique<OobeScreenWaiter>(GaiaPasswordChangedView::kScreenId),
-      OobeJS().CreateVisibilityWaiter(true, kPasswordStep));
+      std::make_unique<OobeScreenWaiter>(
+          ash::EnterOldPasswordScreenView::kScreenId),
+      OobeJS().CreateVisibilityWaiter(true, kEnterOldPasswordInputStep));
 }
 
 void PasswordChangedTypeOldPassword(const std::string& text) {
-  test::OobeJS().TypeIntoPath(text, kOldPasswordInput);
+  if (IsOldFlow()) {
+    test::OobeJS().TypeIntoPath(text, kOldPasswordInput);
+    return;
+  }
+  test::OobeJS().TypeIntoPath(text, kEnterOldPasswordInput);
 }
 
 void PasswordChangedSubmitOldPassword() {
-  test::OobeJS().ClickOnPath(kSendPasswordButton);
+  if (IsOldFlow()) {
+    test::OobeJS().ClickOnPath(kSendPasswordButton);
+    return;
+  }
+  test::OobeJS().ClickOnPath(kEnterOldPasswordProceedButton);
 }
 
 std::unique_ptr<test::TestConditionWaiter>
 PasswordChangedInvalidPasswordFeedback() {
+  if (IsOldFlow()) {
+    return test::OobeJS().CreateWaiter(
+        test::GetOobeElementPath(kOldPasswordInput) + ".invalid");
+  }
   return test::OobeJS().CreateWaiter(
-      test::GetOobeElementPath(kOldPasswordInput) + ".invalid");
+      test::GetOobeElementPath(kEnterOldPasswordInput) + ".invalid");
 }
 
 void PasswordChangedForgotPasswordAction() {
-  test::OobeJS().ClickOnPath(kForgotPasswordButton);
+  if (IsOldFlow()) {
+    test::OobeJS().ClickOnPath(kForgotPasswordButton);
+    return;
+  }
+  test::OobeJS().ClickOnPath(kEnterOldPasswordForgotButton);
 }
 
 std::unique_ptr<test::TestConditionWaiter> LocalDataLossWarningPageWaiter() {
+  if (IsOldFlow()) {
+    return std::make_unique<CompositeWaiter>(
+        std::make_unique<OobeWindowVisibilityWaiter>(true),
+        std::make_unique<OobeScreenWaiter>(GaiaPasswordChangedView::kScreenId),
+        OobeJS().CreateVisibilityWaiter(true, kForgotPasswordStep));
+  }
   return std::make_unique<CompositeWaiter>(
       std::make_unique<OobeWindowVisibilityWaiter>(true),
-      std::make_unique<OobeScreenWaiter>(GaiaPasswordChangedView::kScreenId),
-      OobeJS().CreateVisibilityWaiter(true, kForgotPasswordStep));
+      std::make_unique<OobeScreenWaiter>(
+          LocalDataLossWarningScreenView::kScreenId),
+      OobeJS().CreateVisibilityWaiter(true, kDataLossWarningElement));
 }
 
 void LocalDataLossWarningPageCancelAction() {
-  test::OobeJS().ClickOnPath(kForgotCancel);
+  if (IsOldFlow()) {
+    test::OobeJS().ClickOnPath(kForgotCancel);
+    return;
+  }
+  test::OobeJS().ClickOnPath(kDataLossWarningCancel);
 }
 
 void LocalDataLossWarningPageGoBackAction() {
-  test::OobeJS().ClickOnPath(kTryAgainRecovery);
+  if (IsOldFlow()) {
+    test::OobeJS().ClickOnPath(kTryAgainRecovery);
+    return;
+  }
+  test::OobeJS().ClickOnPath(kDataLossWarningBack);
 }
 
-void LocalDataLossWarningPageProceedAction() {
-  test::OobeJS().ClickOnPath(kProceedAnyway);
+void LocalDataLossWarningPageRemoveAction() {
+  if (IsOldFlow()) {
+    test::OobeJS().ClickOnPath(kProceedAnyway);
+    return;
+  }
+  test::OobeJS().ClickOnPath(kDataLossWarningRemove);
+}
+
+void LocalDataLossWarningPageResetAction() {
+  test::OobeJS().ClickOnPath(kDataLossWarningReset);
 }
 
 void LocalDataLossWarningPageExpectGoBack() {
-  test::OobeJS().ExpectVisiblePath(kTryAgainRecovery);
+  if (IsOldFlow()) {
+    test::OobeJS().ExpectVisiblePath(kTryAgainRecovery);
+    return;
+  }
+  test::OobeJS().ExpectVisiblePath(kDataLossWarningBack);
 }
 
-void LocalDataLossWarningPageExpectProceed() {
-  test::OobeJS().ExpectVisiblePath(kProceedAnyway);
+void LocalDataLossWarningPageExpectRemove() {
+  if (IsOldFlow()) {
+    test::OobeJS().ExpectVisiblePath(kProceedAnyway);
+    return;
+  }
+  test::OobeJS().ExpectVisiblePath(kDataLossWarningRemove);
+}
+
+void LocalDataLossWarningPageExpectReset() {
+  test::OobeJS().ExpectVisiblePath(kDataLossWarningReset);
+}
+
+std::unique_ptr<test::TestConditionWaiter>
+CreatePasswordUpdateNoticePageWaiter() {
+  return std::make_unique<CompositeWaiter>(
+      std::make_unique<OobeWindowVisibilityWaiter>(true),
+      std::make_unique<OobeScreenWaiter>(
+          ash::FactorSetupSuccessScreenView::kScreenId),
+      OobeJS().CreateVisibilityWaiter(true, kFactorSetupSuccessElement));
+}
+
+void PasswordUpdateNoticeExpectNext() {
+  test::OobeJS().ExpectVisiblePath(kFactorSetupSuccessNextButton);
+}
+
+void PasswordUpdateNoticeNextAction() {
+  test::OobeJS().ClickOnPath(kFactorSetupSuccessNextButton);
+}
+
+void PasswordUpdateNoticeExpectDone() {
+  test::OobeJS().ExpectVisiblePath(kFactorSetupSuccessDoneButton);
+}
+
+void PasswordUpdateNoticeDoneAction() {
+  test::OobeJS().ClickOnPath(kFactorSetupSuccessDoneButton);
+}
+
+std::unique_ptr<test::TestConditionWaiter> RecoveryPasswordUpdatedPageWaiter() {
+  if (IsOldFlow()) {
+    return std::make_unique<CompositeWaiter>(
+        std::make_unique<OobeWindowVisibilityWaiter>(true),
+        std::make_unique<OobeScreenWaiter>(
+            CryptohomeRecoveryScreenView::kScreenId),
+        OobeJS().CreateVisibilityWaiter(true, kRecoverySuccessStep));
+  }
+  return CreatePasswordUpdateNoticePageWaiter();
+}
+
+void RecoveryPasswordUpdatedProceedAction() {
+  if (IsOldFlow()) {
+    test::OobeJS().ClickOnPath(kRecoveryDoneButton);
+    return;
+  }
+  PasswordUpdateNoticeDoneAction();
+}
+
+std::unique_ptr<test::TestConditionWaiter> RecoveryErrorPageWaiter() {
+  if (IsOldFlow()) {
+    return std::make_unique<CompositeWaiter>(
+        std::make_unique<OobeWindowVisibilityWaiter>(true),
+        std::make_unique<OobeScreenWaiter>(
+            CryptohomeRecoveryScreenView::kScreenId),
+        OobeJS().CreateVisibilityWaiter(true, kRecoveryErrorStep));
+  }
+  return std::make_unique<CompositeWaiter>(
+      std::make_unique<OobeWindowVisibilityWaiter>(true),
+      std::make_unique<OobeScreenWaiter>(
+          ash::OSAuthErrorScreenView::kScreenId));
+}
+
+void RecoveryErrorExpectFallback() {
+  CHECK(IsOldFlow());
+  test::OobeJS().ExpectVisiblePath(kRecoveryManualRecoveryButton);
+  return;
+}
+
+void RecoveryErrorFallbackAction() {
+  CHECK(IsOldFlow());
+  test::OobeJS().ClickOnPath(kRecoveryManualRecoveryButton);
+  return;
 }
 
 }  // namespace ash::test
diff --git a/chrome/browser/ash/login/test/auth_ui_utils.h b/chrome/browser/ash/login/test/auth_ui_utils.h
index 9682be3..a7acd6e 100644
--- a/chrome/browser/ash/login/test/auth_ui_utils.h
+++ b/chrome/browser/ash/login/test/auth_ui_utils.h
@@ -25,10 +25,26 @@
 std::unique_ptr<test::TestConditionWaiter> LocalDataLossWarningPageWaiter();
 void LocalDataLossWarningPageCancelAction();
 void LocalDataLossWarningPageGoBackAction();
-void LocalDataLossWarningPageProceedAction();
+void LocalDataLossWarningPageRemoveAction();
+void LocalDataLossWarningPageResetAction();
 
 void LocalDataLossWarningPageExpectGoBack();
-void LocalDataLossWarningPageExpectProceed();
+void LocalDataLossWarningPageExpectRemove();
+void LocalDataLossWarningPageExpectReset();
+
+std::unique_ptr<test::TestConditionWaiter>
+CreatePasswordUpdateNoticePageWaiter();
+void PasswordUpdateNoticeExpectNext();
+void PasswordUpdateNoticeNextAction();
+void PasswordUpdateNoticeExpectDone();
+void PasswordUpdateNoticeDoneAction();
+
+std::unique_ptr<test::TestConditionWaiter> RecoveryPasswordUpdatedPageWaiter();
+void RecoveryPasswordUpdatedProceedAction();
+
+std::unique_ptr<test::TestConditionWaiter> RecoveryErrorPageWaiter();
+void RecoveryErrorExpectFallback();
+void RecoveryErrorFallbackAction();
 
 }  // namespace ash::test
 
diff --git a/chrome/browser/ash/login/ui/login_display_host_mojo.cc b/chrome/browser/ash/login/ui/login_display_host_mojo.cc
index cc650b3f..d45e790 100644
--- a/chrome/browser/ash/login/ui/login_display_host_mojo.cc
+++ b/chrome/browser/ash/login/ui/login_display_host_mojo.cc
@@ -356,6 +356,8 @@
     bool success,
     std::unique_ptr<UserContext> user_context) {
   if (!success) {
+    // TODO: pass flow to WizardController, suggest to
+    // remove & re-create user in case of Recovery flow.
     existing_user_controller_->OnLocalAuthenticationCancelled();
     // While dialog itself is already hidden, this call should
     // correctly reset all associated data.
diff --git a/chrome/browser/ash/login/wizard_context.h b/chrome/browser/ash/login/wizard_context.h
index f4cd201..572bcf96 100644
--- a/chrome/browser/ash/login/wizard_context.h
+++ b/chrome/browser/ash/login/wizard_context.h
@@ -80,18 +80,31 @@
   // part of recovery flow, or it it just an reauthentication flow.
   enum class AuthChangeFlow { kInitialSetup, kReauthentication, kRecovery };
 
+  // Indicates the flow path that lead to Data Loss warning screen,
+  // allowing screen to correctly display/handle Back button.
+  enum class DataLossBackOptions { kNone, kBackToOnlineAuth, kBackToLocalAuth };
+
   struct KnowledgeFactorSetup {
     // Whether usage of local password is forced.
     bool local_password_forced = false;
 
     AuthChangeFlow auth_setup_flow = AuthChangeFlow::kInitialSetup;
 
+    DataLossBackOptions data_loss_back_option = DataLossBackOptions::kNone;
+
     AuthFactorsSet modified_factors;
   };
 
   enum class OSAuthErrorKind {
     // Most of the errors
     kFatal,
+    // User is already authenticated, but cryptohome failed to rotate the key.
+    // It is more of a warning.
+    kRecoveryRotationFailed,
+    // There were problems using the recovery key, but it is still
+    // possible to proceed using knowledge-based keys.
+    kRecoveryAuthenticationFailed,
+
   };
 
   // Configuration for automating OOBE screen actions, e.g. during device
diff --git a/chrome/browser/ash/login/wizard_controller.cc b/chrome/browser/ash/login/wizard_controller.cc
index 9bc3334..4921e016 100644
--- a/chrome/browser/ash/login/wizard_controller.cc
+++ b/chrome/browser/ash/login/wizard_controller.cc
@@ -228,6 +228,7 @@
 #include "chromeos/ash/components/language_packs/language_pack_manager.h"
 #include "chromeos/ash/components/network/network_state.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
+#include "chromeos/ash/components/osauth/public/auth_session_storage.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/cros_settings_provider.h"
 #include "chromeos/ash/components/settings/timezone_settings.h"
@@ -913,17 +914,17 @@
                             weak_factory_.GetWeakPtr())));
   }
 
+  append(std::make_unique<PasswordSelectionScreen>(
+      oobe_ui->GetView<PasswordSelectionScreenHandler>()->AsWeakPtr(),
+      base::BindRepeating(&WizardController::OnPasswordSelectionScreenExit,
+                          weak_factory_.GetWeakPtr())));
+
+  append(std::make_unique<ApplyOnlinePasswordScreen>(
+      oobe_ui->GetView<ApplyOnlinePasswordScreenHandler>()->AsWeakPtr(),
+      base::BindRepeating(&WizardController::OnApplyOnlinePasswordScreenExit,
+                          weak_factory_.GetWeakPtr())));
+
   if (features::AreLocalPasswordsEnabledForConsumers()) {
-    append(std::make_unique<PasswordSelectionScreen>(
-        oobe_ui->GetView<PasswordSelectionScreenHandler>()->AsWeakPtr(),
-        base::BindRepeating(&WizardController::OnPasswordSelectionScreenExit,
-                            weak_factory_.GetWeakPtr())));
-
-    append(std::make_unique<ApplyOnlinePasswordScreen>(
-        oobe_ui->GetView<ApplyOnlinePasswordScreenHandler>()->AsWeakPtr(),
-        base::BindRepeating(&WizardController::OnApplyOnlinePasswordScreenExit,
-                            weak_factory_.GetWeakPtr())));
-
     append(std::make_unique<LocalPasswordSetupScreen>(
         oobe_ui->GetView<LocalPasswordSetupHandler>()->AsWeakPtr(),
         base::BindRepeating(&WizardController::OnLocalPasswordSetupScreenExit,
@@ -932,11 +933,13 @@
 
   append(std::make_unique<LocalDataLossWarningScreen>(
       oobe_ui->GetView<LocalDataLossWarningScreenHandler>()->AsWeakPtr(),
-      base::DoNothing()));
+      base::BindRepeating(&WizardController::OnLocalDataLossWarningScreenExit,
+                          weak_factory_.GetWeakPtr())));
 
   append(std::make_unique<EnterOldPasswordScreen>(
       oobe_ui->GetView<EnterOldPasswordScreenHandler>()->AsWeakPtr(),
-      base::DoNothing()));
+      base::BindRepeating(&WizardController::OnEnterOldPasswordScreenExit,
+                          weak_factory_.GetWeakPtr())));
 
   append(std::make_unique<OSAuthErrorScreen>(
       oobe_ui->GetView<OSAuthErrorScreenHandler>()->AsWeakPtr(),
@@ -1034,6 +1037,14 @@
   SetCurrentScreen(GetScreen(ApplyOnlinePasswordScreenView::kScreenId));
 }
 
+void WizardController::ShowLocalDataLossWarningScreen() {
+  SetCurrentScreen(GetScreen(LocalDataLossWarningScreenView::kScreenId));
+}
+
+void WizardController::ShowEnterOldPasswordScreen() {
+  SetCurrentScreen(GetScreen(EnterOldPasswordScreenView::kScreenId));
+}
+
 void WizardController::ShowEnrollmentScreen() {
   // Update the enrollment configuration and start the screen.
   GetLoginDisplayHost()->GetOobeMetricsHelper()->RecordEnrollingUserType();
@@ -1694,27 +1705,117 @@
   OnScreenExit(CryptohomeRecoveryScreenView::kScreenId,
                CryptohomeRecoveryScreen::GetResultString(result));
   switch (result) {
-    case CryptohomeRecoveryScreen::Result::kSucceeded:
-      ash::LoginDisplayHost::default_host()
-          ->GetExistingUserController()
-          ->LoginAuthenticated(std::move(wizard_context_->user_context));
+    case CryptohomeRecoveryScreen::Result::kObsoleteSucceeded:
+      LoginAuthenticatedWithContext(std::move(wizard_context_->user_context));
       break;
+    case CryptohomeRecoveryScreen::Result::kAuthenticated: {
+      switch (wizard_context_->knowledge_factor_setup.auth_setup_flow) {
+        case WizardContext::AuthChangeFlow::kInitialSetup:
+          NOTREACHED() << "Recovery can not be used during initial setup.";
+          return;
+        case WizardContext::AuthChangeFlow::kRecovery:
+          ShowPasswordSelectionScreen();
+          return;
+        case WizardContext::AuthChangeFlow::kReauthentication:
+          // Proceed with login
+          ObtainContextAndLoginAuthenticated();
+          return;
+      }
+    }
     case CryptohomeRecoveryScreen::Result::kGaiaLogin:
-    case CryptohomeRecoveryScreen::Result::kRetry:
+    case CryptohomeRecoveryScreen::Result::kObsoleteRetry:
       // TODO(b/257073746): We probably want to differentiate between retry with
       // or without login.
       wizard_context_->gaia_config.prefilled_account =
           wizard_context_->user_context->GetAccountId();
       AdvanceToScreen(GaiaView::kScreenId);
       break;
-    case CryptohomeRecoveryScreen::Result::kManualRecovery:
-    case CryptohomeRecoveryScreen::Result::kNoRecoveryFactor:
-    case CryptohomeRecoveryScreen::Result::kNotApplicable:
+    case CryptohomeRecoveryScreen::Result::kObsoleteManualRecovery:
+    case CryptohomeRecoveryScreen::Result::kObsoleteNoRecoveryFactor:
       ShowGaiaPasswordChangedScreen(std::move(wizard_context_->user_context));
       break;
-    case CryptohomeRecoveryScreen::Result::kTimeout:
+    case CryptohomeRecoveryScreen::Result::kFallbackOnline:
+      ShowEnterOldPasswordScreen();
+      break;
+    case CryptohomeRecoveryScreen::Result::kFallbackLocal: {
+      switch (wizard_context_->knowledge_factor_setup.auth_setup_flow) {
+        case WizardContext::AuthChangeFlow::kInitialSetup:
+          NOTREACHED() << "Recovery is not used during initial setup";
+          return;
+        case WizardContext::AuthChangeFlow::kReauthentication:
+          AttemptLocalAuthenticationWithContext(
+              std::move(wizard_context_->user_context));
+          return;
+        case WizardContext::AuthChangeFlow::kRecovery:
+          // Recovery flow indicates that user does not remember
+          // their local password, so there is no step to retry.
+          wizard_context_->knowledge_factor_setup.data_loss_back_option =
+              WizardContext::DataLossBackOptions::kNone;
+          ShowLocalDataLossWarningScreen();
+          return;
+      }
+    }
+    case CryptohomeRecoveryScreen::Result::kObsoleteTimeout:
       ShowLoginScreen();
       break;
+    case CryptohomeRecoveryScreen::Result::kError:
+      ShowOSAuthErrorScreen();
+      break;
+  }
+}
+
+void WizardController::OnLocalDataLossWarningScreenExit(
+    LocalDataLossWarningScreen::Result result) {
+  OnScreenExit(LocalDataLossWarningScreenView::kScreenId,
+               LocalDataLossWarningScreen::GetResultString(result));
+  switch (result) {
+    case LocalDataLossWarningScreen::Result::kRemoveUser: {
+      std::unique_ptr<UserContext> context =
+          std::move(wizard_context_->user_context);
+      ash::LoginDisplayHost::default_host()->CompleteLogin(*context);
+      break;
+    }
+    case LocalDataLossWarningScreen::Result::kCryptohomeError:
+      ShowOSAuthErrorScreen();
+      break;
+    case LocalDataLossWarningScreen::Result::kCancel:
+      ShowLoginScreen();
+      break;
+    case LocalDataLossWarningScreen::Result::kBackToOnlineAuth:
+      ShowEnterOldPasswordScreen();
+      break;
+    case LocalDataLossWarningScreen::Result::kBackToLocalAuth:
+      AttemptLocalAuthenticationWithContext(
+          std::move(wizard_context_->user_context));
+      break;
+  }
+}
+
+void WizardController::OnEnterOldPasswordScreenExit(
+    EnterOldPasswordScreen::Result result) {
+  OnScreenExit(EnterOldPasswordScreenView::kScreenId,
+               EnterOldPasswordScreen::GetResultString(result));
+  switch (result) {
+    case EnterOldPasswordScreen::Result::kForgotOldPassword:
+      wizard_context_->knowledge_factor_setup.data_loss_back_option =
+          WizardContext::DataLossBackOptions::kBackToOnlineAuth;
+      ShowLocalDataLossWarningScreen();
+      break;
+    case EnterOldPasswordScreen::Result::kCryptohomeError:
+      ShowOSAuthErrorScreen();
+      break;
+    case EnterOldPasswordScreen::Result::kAuthenticated: {
+      switch (wizard_context_->knowledge_factor_setup.auth_setup_flow) {
+        case WizardContext::AuthChangeFlow::kInitialSetup:
+          NOTREACHED() << "Old password is not used during initial setup";
+          break;
+        case WizardContext::AuthChangeFlow::kRecovery:
+        case WizardContext::AuthChangeFlow::kReauthentication:
+          ShowApplyOnlinePasswordScreen();
+          return;
+      }
+      break;
+    }
   }
 }
 
@@ -2022,7 +2123,8 @@
       OobeMetricsHelper::CompletedPreLoginOobeFlowType::kAutoEnrollment);
 
   // Restart to make the login page pick up the policy changes resulting from
-  // enrollment recovery.  (Not pretty, but this codepath is rarely exercised.)
+  // enrollment recovery.  (Not pretty, but this codepath is rarely
+  // exercised.)
   if (prescribed_enrollment_config_.mode ==
       policy::EnrollmentConfig::MODE_RECOVERY) {
     LOG(WARNING) << "Restart Chrome to pick up the policy changes";
@@ -2249,8 +2351,19 @@
       ShowOSAuthErrorScreen();
       return;
     case ApplyOnlinePasswordScreen::Result::kSuccess:
-    case ApplyOnlinePasswordScreen::Result::kNotApplicable:
-      ShowFingerprintSetupScreen();
+    case ApplyOnlinePasswordScreen::Result::kNotApplicable: {
+      switch (wizard_context_->knowledge_factor_setup.auth_setup_flow) {
+        case WizardContext::AuthChangeFlow::kInitialSetup:
+          ShowFingerprintSetupScreen();
+          return;
+        case WizardContext::AuthChangeFlow::kRecovery:
+          ShowFactorSetupSuccessScreen();
+          return;
+        case WizardContext::AuthChangeFlow::kReauthentication:
+          NOTREACHED() << "Reauthentication should have been switched to "
+                          "Recovery if there was password update";
+      }
+    }
       return;
   }
 }
@@ -2260,6 +2373,39 @@
   OnScreenExit(OSAuthErrorScreenView::kScreenId,
                OSAuthErrorScreen::GetResultString(result));
   switch (result) {
+    case OSAuthErrorScreen::Result::kFallbackOnline:
+      ShowEnterOldPasswordScreen();
+      break;
+    case OSAuthErrorScreen::Result::kFallbackLocal: {
+      switch (wizard_context_->knowledge_factor_setup.auth_setup_flow) {
+        case WizardContext::AuthChangeFlow::kInitialSetup:
+          NOTREACHED() << "Recovery is not used during initial setup";
+          return;
+        case WizardContext::AuthChangeFlow::kReauthentication:
+          AttemptLocalAuthenticationWithContext(
+              std::move(wizard_context_->user_context));
+          return;
+        case WizardContext::AuthChangeFlow::kRecovery:
+          // Recovery flow means that the user forgot their
+          // local password. It does not make sense to ask for it.
+          ShowLocalDataLossWarningScreen();
+          return;
+      }
+    }
+
+    case OSAuthErrorScreen::Result::kProceedAuthenticated: {
+      switch (wizard_context_->knowledge_factor_setup.auth_setup_flow) {
+        case WizardContext::AuthChangeFlow::kInitialSetup:
+        case WizardContext::AuthChangeFlow::kRecovery:
+          ShowPasswordSelectionScreen();
+          return;
+        case WizardContext::AuthChangeFlow::kReauthentication:
+          // Proceed with login
+          ObtainContextAndLoginAuthenticated();
+          return;
+      }
+    }
+      return;
     case OSAuthErrorScreen::Result::kAbortSignin:
       ShowLoginScreen();
       return;
@@ -2272,9 +2418,18 @@
                FactorSetupSuccessScreen::GetResultString(result));
   switch (result) {
     case FactorSetupSuccessScreen::Result::kNotApplicable:
-    case FactorSetupSuccessScreen::Result::kProceed:
-      ShowFingerprintSetupScreen();
-      break;
+    case FactorSetupSuccessScreen::Result::kProceed: {
+      switch (wizard_context_->knowledge_factor_setup.auth_setup_flow) {
+        case WizardContext::AuthChangeFlow::kInitialSetup:
+          ShowFingerprintSetupScreen();
+          return;
+        case WizardContext::AuthChangeFlow::kRecovery:
+        case WizardContext::AuthChangeFlow::kReauthentication:
+          // Proceed with login
+          ObtainContextAndLoginAuthenticated();
+          return;
+      }
+    }
     case FactorSetupSuccessScreen::Result::kTimedOut:
       ShowLoginScreen();
       return;
@@ -2294,6 +2449,47 @@
   FinishAuthFactorsSetup();
 }
 
+void WizardController::ObtainContextAndLoginAuthenticated() {
+  CHECK(wizard_context_->extra_factors_token);
+  auto token = std::move(wizard_context_->extra_factors_token);
+  wizard_context_->extra_factors_token = absl::nullopt;
+
+  ash::AuthSessionStorage::Get()->Withdraw(
+      *token, base::BindOnce(&WizardController::LoginAuthenticatedWithContext,
+                             weak_factory_.GetWeakPtr()));
+}
+
+void WizardController::ObtainContextAndAttemptLocalAuthentication() {
+  CHECK(wizard_context_->extra_factors_token);
+  auto token = std::move(wizard_context_->extra_factors_token);
+  wizard_context_->extra_factors_token = absl::nullopt;
+
+  ash::AuthSessionStorage::Get()->Withdraw(
+      *token,
+      base::BindOnce(&WizardController::AttemptLocalAuthenticationWithContext,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void WizardController::LoginAuthenticatedWithContext(
+    std::unique_ptr<UserContext> context) {
+  if (!context) {
+    // Session has expired.
+    LOG(ERROR) << "Session expired before login could proceed.";
+    wizard_context_->osauth_error = WizardContext::OSAuthErrorKind::kFatal;
+    ShowOSAuthErrorScreen();
+    return;
+  }
+  ash::LoginDisplayHost::default_host()
+      ->GetExistingUserController()
+      ->LoginAuthenticated(std::move(context));
+}
+
+void WizardController::AttemptLocalAuthenticationWithContext(
+    std::unique_ptr<UserContext> context) {
+  ash::LoginDisplayHost::default_host()->GetSigninUI()->RunLocalAuthentication(
+      std::move(context));
+}
+
 void WizardController::FinishAuthFactorsSetup() {
   // TODO(b/238606050): Ensure that AuthSession is terminated after this step.
   ShowRecommendAppsScreen();
@@ -2625,9 +2821,9 @@
                !wizard_context_->is_cloud_ready_update_flow &&
                wizard_context_->screen_after_managed_tos !=
                    ash::OOBE_SCREEN_UNKNOWN) {
-      // If screen_after_managed_tos == SCREEN_UNKNOWN means that the onboarding
-      // has already been finished by the user and we don't need to save the
-      // state here.
+      // If screen_after_managed_tos == SCREEN_UNKNOWN means that the
+      // onboarding has already been finished by the user and we don't need to
+      // save the state here.
       user_manager::KnownUser(GetLocalState())
           .SetPendingOnboardingScreen(
               user_manager::UserManager::Get()->GetActiveUser()->GetAccountId(),
@@ -2646,8 +2842,9 @@
 void WizardController::UpdateStatusAreaVisibilityForScreen(
     OobeScreenId screen_id) {
   if (screen_id == WelcomeView::kScreenId) {
-    // Hide the status area initially; it only appears after OOBE first animates
-    // in. Keep it visible if the user goes back to the existing welcome screen.
+    // Hide the status area initially; it only appears after OOBE first
+    // animates in. Keep it visible if the user goes back to the existing
+    // welcome screen.
     GetLoginDisplayHost()->SetStatusAreaVisible(
         screen_manager_->HasScreen(WelcomeView::kScreenId));
   } else {
@@ -2686,9 +2883,9 @@
     policy::EnrollmentRequisitionManager::SetDeviceRequisition(
         *requisition_value);
   } else if (policy::EnrollmentRequisitionManager::IsMeetDevice()) {
-    VLOG(1)
-        << "Using default Device Requisition value for CFM build configuration"
-        << policy::EnrollmentRequisitionManager::kRemoraRequisition;
+    VLOG(1) << "Using default Device Requisition value for CFM build "
+               "configuration"
+            << policy::EnrollmentRequisitionManager::kRemoraRequisition;
     policy::EnrollmentRequisitionManager::SetDeviceRequisition(
         policy::EnrollmentRequisitionManager::kRemoraRequisition);
   }
@@ -2911,8 +3108,8 @@
   }
 
   if (status == CrosSettingsProvider::PERMANENTLY_UNTRUSTED) {
-    // If the `cros_settings_` are permanently untrusted, show an error message
-    // and refuse to auto-launch the kiosk app.
+    // If the `cros_settings_` are permanently untrusted, show an error
+    // message and refuse to auto-launch the kiosk app.
     AdvanceToScreen(LocalStateErrorScreenView::kScreenId);
     return;
   }
@@ -3102,7 +3299,8 @@
   // Determine the effective enrollment configuration. If there is a valid
   // prescribed configuration, use that. If not, figure out which variant of
   // manual enrollment is taking place.
-  // If OOBE Configuration exits, it might also affect enrollment configuration.
+  // If OOBE Configuration exits, it might also affect enrollment
+  // configuration.
   policy::EnrollmentConfig effective_config = prescribed_enrollment_config_;
   if (!effective_config.should_enroll() ||
       (force_interactive && !effective_config.should_enroll_interactively())) {
diff --git a/chrome/browser/ash/login/wizard_controller.h b/chrome/browser/ash/login/wizard_controller.h
index 51ff7d7..a1b0496c 100644
--- a/chrome/browser/ash/login/wizard_controller.h
+++ b/chrome/browser/ash/login/wizard_controller.h
@@ -58,9 +58,11 @@
 #include "chrome/browser/ash/login/screens/osauth/apply_online_password_screen.h"
 #include "chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen.h"
 #include "chrome/browser/ash/login/screens/osauth/cryptohome_recovery_setup_screen.h"
+#include "chrome/browser/ash/login/screens/osauth/enter_old_password_screen.h"
 #include "chrome/browser/ash/login/screens/osauth/factor_setup_success_screen.h"
 #include "chrome/browser/ash/login/screens/osauth/gaia_password_changed_screen.h"
 #include "chrome/browser/ash/login/screens/osauth/gaia_password_changed_screen_legacy.h"
+#include "chrome/browser/ash/login/screens/osauth/local_data_loss_warning_screen.h"
 #include "chrome/browser/ash/login/screens/osauth/local_password_setup_screen.h"
 #include "chrome/browser/ash/login/screens/osauth/osauth_error_screen.h"
 #include "chrome/browser/ash/login/screens/osauth/password_selection_screen.h"
@@ -346,6 +348,8 @@
   void ShowLocalPasswordSetupScreen();
   void ShowApplyOnlinePasswordScreen();
   void ShowOSAuthErrorScreen();
+  void ShowEnterOldPasswordScreen();
+  void ShowLocalDataLossWarningScreen();
   void ShowFactorSetupSuccessScreen();
 
   // Shows images login screen.
@@ -397,12 +401,23 @@
   void OnTermsOfServiceScreenExit(TermsOfServiceScreen::Result result);
   void OnSyncConsentScreenExit(SyncConsentScreen::Result result);
   // Start of Local authentication setup sub-group
+  // Authentication part
+  void OnCryptohomeRecoveryScreenExit(CryptohomeRecoveryScreen::Result result);
+  void OnEnterOldPasswordScreenExit(EnterOldPasswordScreen::Result result);
+  void OnLocalDataLossWarningScreenExit(
+      LocalDataLossWarningScreen::Result result);
+  // Factor setup part
   void StartAuthFactorsSetup();
   void OnCryptohomeRecoverySetupScreenExit(
       CryptohomeRecoverySetupScreen::Result result);
   void OnPasswordSelectionScreenExit(PasswordSelectionScreen::Result result);
   void OnFingerprintSetupScreenExit(FingerprintSetupScreen::Result result);
   void OnPinSetupScreenExit(PinSetupScreen::Result result);
+  void ObtainContextAndLoginAuthenticated();
+  void LoginAuthenticatedWithContext(std::unique_ptr<UserContext> user_context);
+  void ObtainContextAndAttemptLocalAuthentication();
+  void AttemptLocalAuthenticationWithContext(
+      std::unique_ptr<UserContext> user_context);
   void FinishAuthFactorsSetup();
   // End of Local authentication setup sub-group
   void OnRecommendAppsScreenExit(RecommendAppsScreen::Result result);
@@ -438,7 +453,6 @@
   void OnSmartPrivacyProtectionScreenExit(
       SmartPrivacyProtectionScreen::Result result);
   void OnThemeSelectionScreenExit(ThemeSelectionScreen::Result result);
-  void OnCryptohomeRecoveryScreenExit(CryptohomeRecoveryScreen::Result result);
   void OnChoobeScreenExit(ChoobeScreen::Result result);
   void OnTouchpadScreenExit(TouchpadScrollScreen::Result result);
   void OnDisplaySizeScreenExit(DisplaySizeScreen::Result result);
diff --git a/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller.cc b/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller.cc
index 6c9d31a..6a118d04 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller.cc
+++ b/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller.cc
@@ -634,9 +634,10 @@
 void CrdAdminSessionController::OnHostStopped(
     ExtendedStartCrdSessionResultCode result,
     const std::string& message) {
-  CRD_VLOG(3) << "Destroying CRD host session asynchronously";
-
-  DeleteSoon(std::move(active_session_));
+  if (active_session_) {
+    CRD_VLOG(3) << "Destroying CRD host session asynchronously";
+    DeleteSoon(std::move(active_session_));
+  }
 }
 
 void CrdAdminSessionController::TryToReconnect(
diff --git a/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller_unittest.cc b/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller_unittest.cc
index f404c736..5fe7c92 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller_unittest.cc
+++ b/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller_unittest.cc
@@ -250,10 +250,10 @@
 
   RemotingServiceMock& remoting_service() { return remoting_service_; }
   CrdAdminSessionController& session_controller() {
-    return session_controller_;
+    return *session_controller_;
   }
   StartCrdSessionJobDelegate& delegate() {
-    return session_controller_.GetDelegate();
+    return session_controller_->GetDelegate();
   }
 
   auto success_callback() {
@@ -323,8 +323,10 @@
   }
 
   void InitWithNoReconnectableSession(CrdAdminSessionController& controller) {
-    EXPECT_CALL(remoting_service(), GetReconnectableSessionId)
-        .WillOnce(ReplyWithSessionId(std::nullopt));
+    if (base::FeatureList::IsEnabled(kEnableCrdAdminRemoteAccessV2)) {
+      EXPECT_CALL(remoting_service(), GetReconnectableSessionId)
+          .WillOnce(ReplyWithSessionId(std::nullopt));
+    }
 
     Init(controller);
 
@@ -341,17 +343,12 @@
     ASSERT_TRUE(delegate().HasActiveSession());
   }
 
+  // UI elements (like the remote activity notification) can only be shown once
+  // the login screen is visible.
   void SimulateLoginScreenIsVisible() {
-    // Notifies the observers that the login screen is visible and ensure the
-    // `RemoteActivityNotificationController::Init()` is called.
     session_manager().NotifyLoginOrLockScreenVisible();
   }
 
-  void SimulateRestart() {
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        ash::switches::kFirstExecAfterBoot);
-  }
-
   const aura::Window& GetLockScreenContainersContainer() {
     return CHECK_DEREF(ash::Shell::Get()->GetPrimaryRootWindow()->GetChildById(
         ash::kShellWindowId_LockScreenContainersContainer));
@@ -407,26 +404,42 @@
 
   mojo::Remote<SupportHostObserver>& observer_remote() { return observer_; }
 
- private:
+  void RecreateSessionController() {
+    // It's possible the old session controller has an outstanding delete of
+    // its previously active session (which are deleted asynchronously), so
+    // give this outstanding delete a chance to finish before we delete
+    // the controller itself.
+    if (session_controller_.has_value()) {
+      base::RunLoop().RunUntilIdle();
+    }
+
+    session_controller_.emplace(
+        std::make_unique<RemotingServiceWrapper>(&remoting_service_));
+    result_.Clear();
+    session_finish_result_.Clear();
+  }
+
+ protected:
   void SetUp() override {
     AshTestBase::SetUp();
+    RecreateSessionController();
     session_controller().SetOAuthTokenForTesting("test-oauth-token");
   }
 
   void TearDown() override {
-    session_controller_.Shutdown();
+    session_controller_->Shutdown();
     AshTestBase::TearDown();
   }
 
+ private:
   ScopedTestingLocalState local_state_;
-  testing::StrictMock<ash::MockLoginDisplayHost> mock_login_display_host_;
+  testing::NiceMock<ash::MockLoginDisplayHost> mock_login_display_host_;
   TestFuture<Response> result_;
   TestFuture<base::TimeDelta> session_finish_result_;
   mojo::Remote<SupportHostObserver> observer_;
   testing::StrictMock<RemotingServiceMock> remoting_service_;
   SecurityCurtainControllerFake curtain_controller_fake_;
-  CrdAdminSessionController session_controller_{
-      std::make_unique<RemotingServiceWrapper>(&remoting_service_)};
+  std::optional<CrdAdminSessionController> session_controller_;
   base::test::ScopedFeatureList feature_;
 };
 
@@ -781,127 +794,6 @@
 }
 
 TEST_F(CrdAdminSessionControllerTest,
-       ShouldNotShowActivityNotificationIfDisabledByFeature) {
-  DisableFeature(kEnableCrdAdminRemoteAccessV2);
-  Init(session_controller());
-
-  SessionParameters parameters;
-  parameters.curtain_local_user_session = true;
-  SupportHostObserver& observer = StartCrdHostAndBindObserver(parameters);
-  observer.OnHostStateConnected(kTestUserName);
-  FlushForTesting(observer);
-
-  EXPECT_NO_CALLS(login_display_host(), ShowRemoteActivityNotificationScreen());
-
-  SimulateLoginScreenIsVisible();
-}
-
-TEST_F(CrdAdminSessionControllerTest,
-       ShouldShowActivityNotificationIfThePreviousSessionWasCurtained) {
-  EnableFeature(kEnableCrdAdminRemoteAccessV2);
-  InitWithNoReconnectableSession(session_controller());
-
-  SessionParameters parameters;
-  parameters.curtain_local_user_session = true;
-  SupportHostObserver& observer = StartCrdHostAndBindObserver(parameters);
-  observer.OnHostStateConnected(kTestUserName);
-  FlushForTesting(observer);
-
-  EXPECT_CALL(login_display_host(), ShowRemoteActivityNotificationScreen())
-      .Times(1);
-
-  SimulateLoginScreenIsVisible();
-}
-
-TEST_F(CrdAdminSessionControllerTest,
-       ShouldNotShowActivityNotificationIfThePreviousSessionWasNotCurtained) {
-  EnableFeature(kEnableCrdAdminRemoteAccessV2);
-  InitWithNoReconnectableSession(session_controller());
-
-  SessionParameters parameters;
-  parameters.curtain_local_user_session = false;
-  SupportHostObserver& observer = StartCrdHostAndBindObserver(parameters);
-  observer.OnHostStateConnected(kTestUserName);
-  FlushForTesting(observer);
-
-  EXPECT_NO_CALLS(login_display_host(), ShowRemoteActivityNotificationScreen());
-
-  SimulateLoginScreenIsVisible();
-}
-
-TEST_F(CrdAdminSessionControllerTest,
-       ShouldShowActivityNotificationAgainIfUserDidNotDismissIt) {
-  EnableFeature(kEnableCrdAdminRemoteAccessV2);
-  InitWithNoReconnectableSession(session_controller());
-
-  SessionParameters parameters;
-  parameters.curtain_local_user_session = true;
-  SupportHostObserver& observer = StartCrdHostAndBindObserver(parameters);
-  observer.OnHostStateConnected(kTestUserName);
-  FlushForTesting(observer);
-
-  // The first time the notification is displayed.
-  EXPECT_CALL(login_display_host(), ShowRemoteActivityNotificationScreen())
-      .Times(1);
-  SimulateLoginScreenIsVisible();
-
-  SimulateRestart();
-
-  EXPECT_CALL(login_display_host(), ShowRemoteActivityNotificationScreen())
-      .Times(1);
-  SimulateLoginScreenIsVisible();
-}
-
-TEST_F(CrdAdminSessionControllerTest,
-       ShouldNotShowActivityNotificationAgainIfUserDismissedIt) {
-  EnableFeature(kEnableCrdAdminRemoteAccessV2);
-  InitWithNoReconnectableSession(session_controller());
-
-  SessionParameters parameters;
-  parameters.curtain_local_user_session = true;
-  SupportHostObserver& observer = StartCrdHostAndBindObserver(parameters);
-  observer.OnHostStateConnected(kTestUserName);
-  FlushForTesting(observer);
-  TerminateActiveSession();
-
-  // The first time the notification is displayed.
-  EXPECT_CALL(login_display_host(), ShowRemoteActivityNotificationScreen())
-      .Times(1);
-  SimulateLoginScreenIsVisible();
-
-  DismissNotification();
-  SimulateRestart();
-
-  EXPECT_NO_CALLS(login_display_host(), ShowRemoteActivityNotificationScreen());
-
-  SimulateLoginScreenIsVisible();
-}
-
-TEST_F(
-    CrdAdminSessionControllerTest,
-    ShouldShowActivityNotificationAgainIfUserDismissedItDuringACurtainedSession) {
-  EnableFeature(kEnableCrdAdminRemoteAccessV2);
-  InitWithNoReconnectableSession(session_controller());
-
-  SessionParameters parameters;
-  parameters.curtain_local_user_session = true;
-  SupportHostObserver& observer = StartCrdHostAndBindObserver(parameters);
-  SimulateClientConnects(observer);
-
-  // The first time the notification is displayed.
-  EXPECT_CALL(login_display_host(), ShowRemoteActivityNotificationScreen())
-      .Times(1);
-  SimulateLoginScreenIsVisible();
-
-  DismissNotification();
-  SimulateRestart();
-
-  EXPECT_CALL(login_display_host(), ShowRemoteActivityNotificationScreen())
-      .Times(1);
-  SimulateLoginScreenIsVisible();
-}
-
-TEST_F(CrdAdminSessionControllerTest,
        ShouldUmaLogErrorWhenRemotingServiceReportsStateError) {
   const std::tuple<ErrorCode, ExtendedStartCrdSessionResultCode> test_cases[] =
       {{ErrorCode::OK, ExtendedStartCrdSessionResultCode::kSuccess},
@@ -1187,6 +1079,123 @@
   EXPECT_EQ(GetSessionControllerClient()->request_sign_out_count(), 1);
 }
 
+class CrdAdminSessionControllerTestNotification
+    : public CrdAdminSessionControllerReconnectTest {
+ public:
+  void SetUp() override {
+    EnableFeature(kEnableCrdAdminRemoteAccessV2);
+
+    CrdAdminSessionControllerReconnectTest::SetUp();
+
+    InitWithNoReconnectableSession(session_controller());
+  }
+
+  void SimulateChromeRestart() {
+    RecreateSessionController();
+    observer_remote().reset();
+    InitWithNoReconnectableSession(session_controller());
+    session_controller().SetOAuthTokenForTesting("fake-oauth-token");
+    SimulateLoginScreenIsVisible();
+  }
+
+  void StartCrdHost(bool is_curtained) {
+    SessionParameters parameters;
+    parameters.curtain_local_user_session = is_curtained;
+    StartCrdHostAndBindObserver(parameters);
+  }
+
+  void SimulateCrdClientConnects() {
+    observer_remote()->OnHostStateConnecting();
+    observer_remote()->OnHostStateConnected(kTestUserName);
+    FlushForTesting(*observer_remote());
+  }
+
+  void SimulateCrdClientDisconnects() {
+    observer_remote()->OnHostStateDisconnected(std::nullopt);
+    FlushForTesting(*observer_remote());
+  }
+
+  void SimulateCrdSessionWithClient(bool is_curtained) {
+    StartCrdHost(is_curtained);
+    SimulateCrdClientConnects();
+    SimulateCrdClientDisconnects();
+  }
+};
+
+TEST_F(CrdAdminSessionControllerTestNotification,
+       ShouldNotShowActivityNotificationIfDisabledByFeature) {
+  DisableFeature(kEnableCrdAdminRemoteAccessV2);
+  // Ensure disabling the feature takes effect.
+  SimulateChromeRestart();
+
+  SimulateCrdSessionWithClient(/*is_curtained=*/true);
+
+  EXPECT_NO_CALLS(login_display_host(), ShowRemoteActivityNotificationScreen);
+  SimulateChromeRestart();
+}
+
+TEST_F(CrdAdminSessionControllerTestNotification,
+       ShouldShowActivityNotificationIfThePreviousSessionWasCurtained) {
+  SimulateCrdSessionWithClient(/*is_curtained=*/true);
+
+  EXPECT_CALL(login_display_host(), ShowRemoteActivityNotificationScreen);
+  SimulateChromeRestart();
+}
+
+TEST_F(CrdAdminSessionControllerTestNotification,
+       ShouldNotShowActivityNotificationIfThePreviousSessionWasNotCurtained) {
+  SimulateCrdSessionWithClient(/*is_curtained=*/false);
+
+  EXPECT_NO_CALLS(login_display_host(), ShowRemoteActivityNotificationScreen);
+  SimulateChromeRestart();
+}
+
+TEST_F(CrdAdminSessionControllerTestNotification,
+       ShouldShowActivityNotificationAgainIfUserDidNotDismissIt) {
+  SimulateCrdSessionWithClient(/*is_curtained=*/true);
+
+  // The first time the notification is displayed.
+  EXPECT_CALL(login_display_host(), ShowRemoteActivityNotificationScreen);
+  SimulateChromeRestart();
+
+  // And it is shown again after restarting without dismissing the notification.
+  EXPECT_CALL(login_display_host(), ShowRemoteActivityNotificationScreen);
+  SimulateChromeRestart();
+}
+
+TEST_F(CrdAdminSessionControllerTestNotification,
+       ShouldNotShowActivityNotificationAgainIfUserDidNotDismissIt) {
+  SimulateCrdSessionWithClient(/*is_curtained=*/true);
+
+  // The first time the notification is displayed.
+  EXPECT_CALL(login_display_host(), ShowRemoteActivityNotificationScreen);
+  SimulateChromeRestart();
+
+  // It is *not* shown again after dismissing the notification.
+  DismissNotification();
+
+  EXPECT_NO_CALLS(login_display_host(), ShowRemoteActivityNotificationScreen);
+  SimulateChromeRestart();
+}
+
+TEST_F(CrdAdminSessionControllerTestNotification,
+       ShouldHideActivityNotificationDuringACurtainedCrdSession) {
+  SimulateCrdSessionWithClient(/*is_curtained=*/true);
+
+  // The first time the notification is displayed.
+  EXPECT_CALL(login_display_host(), ShowRemoteActivityNotificationScreen);
+  SimulateChromeRestart();
+
+  StartCrdHost(/*is_curtained=*/true);
+
+  EXPECT_CALL(login_display_host(), HideOobeDialog);
+  SimulateCrdClientConnects();
+
+  // It should be shown again after the CRD session ends.
+  EXPECT_CALL(login_display_host(), ShowRemoteActivityNotificationScreen);
+  SimulateCrdClientDisconnects();
+}
+
 INSTANTIATE_TEST_SUITE_P(CrdAdminSessionControllerTestWithBoolParams,
                          CrdAdminSessionControllerTestWithBoolParams,
                          testing::Bool());
diff --git a/chrome/browser/ash/policy/remote_commands/crd/crd_session_observer.h b/chrome/browser/ash/policy/remote_commands/crd/crd_session_observer.h
index 7368c0a..9a2f6088 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/crd_session_observer.h
+++ b/chrome/browser/ash/policy/remote_commands/crd/crd_session_observer.h
@@ -27,6 +27,7 @@
 
   // Invoked when the remote admin used the access code to actually start a
   // CRD connection.
+  virtual void OnClientConnecting() {}
   virtual void OnClientConnected() {}
 
   // Invoked when the remote admin disconnects.
diff --git a/chrome/browser/ash/policy/remote_commands/crd/crd_support_host_observer_proxy.cc b/chrome/browser/ash/policy/remote_commands/crd/crd_support_host_observer_proxy.cc
index bcc4560..67e038a 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/crd_support_host_observer_proxy.cc
+++ b/chrome/browser/ash/policy/remote_commands/crd/crd_support_host_observer_proxy.cc
@@ -64,6 +64,10 @@
 
 void SupportHostObserverProxy::OnHostStateConnecting() {
   CRD_VLOG(3) << __func__;
+
+  for (auto& observer : observers_) {
+    observer.OnClientConnecting();
+  }
 }
 
 void SupportHostObserverProxy::OnHostStateConnected(
diff --git a/chrome/browser/ash/policy/remote_commands/crd/remote_activity_notification_controller.cc b/chrome/browser/ash/policy/remote_commands/crd/remote_activity_notification_controller.cc
index dc87a12..e6245b4 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/remote_activity_notification_controller.cc
+++ b/chrome/browser/ash/policy/remote_commands/crd/remote_activity_notification_controller.cc
@@ -32,10 +32,21 @@
 RemoteActivityNotificationController::~RemoteActivityNotificationController() =
     default;
 
-// We only can display UI elements (like the notification) after the login
-// screen is properly initialized.
+// Buttons (like the one on the notification) do not correctly respond to
+// on-click events if they are created before the login screen is properly
+// initialized, so only create the notification once that's the case.
 void RemoteActivityNotificationController::OnLoginOrLockScreenVisible() {
-  Init();
+  if (remote_admin_was_present_.GetValue()) {
+    notification_.Show();
+  }
+}
+
+void RemoteActivityNotificationController::OnClientConnecting() {
+  // The notification is meant for a local user not for the remote admin, so
+  // hide it during a CRD connection so we don't annoy them. This also prevents
+  // the notification from showing when the remote admin auto-reconnects after
+  // a Chrome crash/restart.
+  notification_.Hide();
 }
 
 void RemoteActivityNotificationController::OnClientConnected() {
@@ -44,25 +55,45 @@
   }
 }
 
-void RemoteActivityNotificationController::Init() {
+void RemoteActivityNotificationController::OnClientDisconnected() {
+  // Show the notification once the remote admin disconnects.
   if (remote_admin_was_present_.GetValue()) {
-    ShowNotification();
+    notification_.Show();
   }
 }
 
-void RemoteActivityNotificationController::ShowNotification() {
-  CHECK_DEREF(ash::LoginDisplayHost::default_host())
-      .ShowRemoteActivityNotificationScreen();
-}
-
 void RemoteActivityNotificationController::
     OnRemoteAdminWasPresentPrefChanged() {
-  if (is_current_session_curtained_.Run() &&
-      !remote_admin_was_present_.GetValue()) {
+  if (!remote_admin_was_present_.GetValue()) {
+    // Remember the notification was dismissed by the user.
+    notification_.Hide();
+  }
+
+  if (is_current_session_curtained_.Run()) {
     // When the notification is dismissed from inside a curtained session
     // we must ensure the notification is shown again the next time.
     remote_admin_was_present_.SetValue(true);
   }
 }
 
+void RemoteActivityNotificationController::Notification::Show() {
+  if (!is_showing_) {
+    auto* default_host = ash::LoginDisplayHost::default_host();
+    if (default_host) {
+      default_host->ShowRemoteActivityNotificationScreen();
+      is_showing_ = true;
+    }
+  }
+}
+
+void RemoteActivityNotificationController::Notification::Hide() {
+  if (is_showing_) {
+    auto* default_host = ash::LoginDisplayHost::default_host();
+    if (default_host) {
+      default_host->HideOobeDialog();
+      is_showing_ = false;
+    }
+  }
+}
+
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/remote_commands/crd/remote_activity_notification_controller.h b/chrome/browser/ash/policy/remote_commands/crd/remote_activity_notification_controller.h
index 677d6fb..21d875c 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/remote_activity_notification_controller.h
+++ b/chrome/browser/ash/policy/remote_commands/crd/remote_activity_notification_controller.h
@@ -34,12 +34,22 @@
   void OnLoginOrLockScreenVisible() override;
 
   // `CrdSessionObserver` implementation:
+  void OnClientConnecting() override;
   void OnClientConnected() override;
+  void OnClientDisconnected() override;
 
  private:
-  void Init();
+  class Notification {
+   public:
+    void Show();
+    void Hide();
 
-  void ShowNotification();
+   private:
+    // Unfortunately the `LocalHost` has no API to query if the notification
+    // screen is showing, so this must manually be tracked to prevent
+    // dismissing other OOBE screens when trying to hide the notification.
+    bool is_showing_ = false;
+  };
 
   void OnRemoteAdminWasPresentPrefChanged();
 
@@ -48,6 +58,7 @@
                           session_manager::SessionManagerObserver>
       observation_{this};
   BooleanPrefMember remote_admin_was_present_;
+  Notification notification_;
 
   base::WeakPtrFactory<RemoteActivityNotificationController> weak_ptr_factory_{
       this};
diff --git a/chrome/browser/autofill/autofill_captured_sites_interactive_uitest.cc b/chrome/browser/autofill/autofill_captured_sites_interactive_uitest.cc
index 240d826..2c35858 100644
--- a/chrome/browser/autofill/autofill_captured_sites_interactive_uitest.cc
+++ b/chrome/browser/autofill/autofill_captured_sites_interactive_uitest.cc
@@ -216,7 +216,7 @@
       // the "Enter CVC" dialog will pop up for card autofill.
       bool is_credit_card_field =
           triggered_field_type.has_value() &&
-          GroupTypeOfServerFieldType(triggered_field_type.value()) ==
+          GroupTypeOfFieldType(triggered_field_type.value()) ==
               FieldTypeGroup::kCreditCard;
       bool should_cvc_dialog_pop_up = is_credit_card_field && cvc;
 
diff --git a/chrome/browser/devtools/protocol/autofill_handler.cc b/chrome/browser/devtools/protocol/autofill_handler.cc
index f19a758..7cd6e57 100644
--- a/chrome/browser/devtools/protocol/autofill_handler.cc
+++ b/chrome/browser/devtools/protocol/autofill_handler.cc
@@ -34,7 +34,7 @@
 using autofill::FieldGlobalId;
 using autofill::FormData;
 using autofill::FormFieldData;
-using autofill::HtmlFieldTypeToBestCorrespondingServerFieldType;
+using autofill::HtmlFieldTypeToBestCorrespondingFieldType;
 using autofill::mojom::HtmlFieldType;
 using protocol::Maybe;
 using protocol::Response;
@@ -259,7 +259,7 @@
     bool autofill_inferred =
         autofill_field->html_type() == HtmlFieldType::kUnspecified ||
         autofill_field->html_type() == HtmlFieldType::kUnrecognized ||
-        HtmlFieldTypeToBestCorrespondingServerFieldType(
+        HtmlFieldTypeToBestCorrespondingFieldType(
             autofill_field->html_type()) !=
             autofill_field->Type().GetStorableType();
     filled_fields_to_be_sent_to_devtools->push_back(
@@ -287,7 +287,7 @@
   const std::string locale = "en-US";
   autofill::GetAddressComponents(
       base::UTF16ToUTF8(profile_used_to_fill_form->GetInfo(
-          autofill::ServerFieldType::ADDRESS_HOME_COUNTRY, locale)),
+          autofill::FieldType::ADDRESS_HOME_COUNTRY, locale)),
       locale,
       /*include_literals=*/false, &components, nullptr);
 
diff --git a/chrome/browser/devtools/protocol/devtools_autofill_browsertest.cc b/chrome/browser/devtools/protocol/devtools_autofill_browsertest.cc
index fc4a779d..8324989 100644
--- a/chrome/browser/devtools/protocol/devtools_autofill_browsertest.cc
+++ b/chrome/browser/devtools/protocol/devtools_autofill_browsertest.cc
@@ -274,12 +274,12 @@
                                                    .GetPersonalDataManager()
                                                    ->test_addresses();
   ASSERT_EQ(res.size(), 2u);
-  ASSERT_EQ(res[0].GetAddress().GetRawInfo(
-                autofill::ServerFieldType::ADDRESS_HOME_LINE1),
-            u"Erika-mann");
-  ASSERT_EQ(res[1].GetAddress().GetRawInfo(
-                autofill::ServerFieldType::ADDRESS_HOME_LINE2),
-            u"Faria lima");
+  ASSERT_EQ(
+      res[0].GetAddress().GetRawInfo(autofill::FieldType::ADDRESS_HOME_LINE1),
+      u"Erika-mann");
+  ASSERT_EQ(
+      res[1].GetAddress().GetRawInfo(autofill::FieldType::ADDRESS_HOME_LINE2),
+      u"Faria lima");
 }
 
 IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, TriggerCreditCard) {
diff --git a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
index 8ea70a5..e46a227 100644
--- a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
+++ b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
@@ -527,9 +527,11 @@
   features.Append(GenerateFeatureFlag(
       "autocorrectparamstuning",
       base::FeatureList::IsEnabled(ash::features::kAutocorrectParamsTuning)));
-  features.Append(GenerateFeatureFlag(
-      "handwritinglibrarydlc",
-      base::FeatureList::IsEnabled(ash::features::kHandwritingLibraryDlc)));
+
+  // TODO(b/316429185): Remove "handwritinglibrarydlc" when no longer referenced
+  // in CrOS virtual keyboard's internal code base.
+  features.Append(GenerateFeatureFlag("handwritinglibrarydlc", true));
+
   features.Append(
       GenerateFeatureFlag("jelly", chromeos::features::IsJellyEnabled()));
   features.Append(GenerateFeatureFlag(
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 03c12b6b..1c7eb7b 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2151,6 +2151,11 @@
     "expiry_milestone": 125
   },
   {
+    "name": "enable-bound-session-credentials-software-keys-for-manual-testing",
+    "owners": [ "alexilin@chromium.org", "msalama@chromium.org", "chrome-signin-team@google.com" ],
+    "expiry_milestone": 125
+  },
+  {
     "name": "enable-builtin-hls",
     "owners": ["tmathmeyer@chromium.org", "videostack-eng@google.com"],
     "expiry_milestone": 123
@@ -4540,11 +4545,6 @@
     "expiry_milestone": 118
   },
   {
-    "name": "handwriting-library-dlc",
-    "owners": [ "shend@google.com", "essential-inputs-team@google.com" ],
-    "expiry_milestone": 118
-  },
-  {
     "name": "happiness-tracking-surveys-for-desktop-demo",
     "owners": [ "//chrome/browser/ui/hats/OWNERS" ],
     // A debugging and demo flag to allow UI/dev/testing team to always show the UI
@@ -6312,7 +6312,7 @@
   {
     "name": "password-suggestion-bottom-sheet-v2",
     "owners": ["atsvirchkova@google.com", "vasilii@chromium.org", "derinel@google.com"],
-    "expiry_milestone": 122
+    "expiry_milestone": 125
   },
   {
     "name": "pcie-billboard-notification",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index a6914a98..8e1ef13 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -652,12 +652,6 @@
     "When enabled, Chrome Settings link directs to GPay Web rather than "
     "Payments Center for payment methods management.";
 
-const char kAutofillUseImprovedLabelDisambiguationName[] =
-    "Autofill Uses Improved Label Disambiguation";
-const char kAutofillUseImprovedLabelDisambiguationDescription[] =
-    "When enabled, the Autofill dropdown's suggestions' labels are displayed "
-    "using the improved disambiguation format.";
-
 const char kAutofillVirtualCardsOnTouchToFillAndroidName[] =
     "Enable virtual cards on Touch To Fill bottomsheet for credit cards";
 const char kAutofillVirtualCardsOnTouchToFillAndroidDescription[] =
@@ -1955,12 +1949,6 @@
 const char kHandwritingLegacyRecognitionDescription[] =
     "Enables new on-device recognition for handwriting legacy paths.";
 
-const char kHandwritingLibraryDlcName[] =
-    "Handwriting recognition with library from DLC";
-const char kHandwritingLibraryDlcDescription[] =
-    "Enables new on-device recognition with the handwriting library installed "
-    "from DLC";
-
 const char kHardwareMediaKeyHandling[] = "Hardware Media Key Handling";
 const char kHardwareMediaKeyHandlingDescription[] =
     "Enables using media keys to control the active media session. This "
@@ -3866,12 +3854,6 @@
     "Image drag on Android is available when flag touch-drag-and-context-menu "
     "is enabled.";
 
-const char kAutofillUseMobileLabelDisambiguationName[] =
-    "Autofill Uses Mobile Label Disambiguation";
-const char kAutofillUseMobileLabelDisambiguationDescription[] =
-    "When enabled, Autofill suggestions' labels are displayed using a "
-    "mobile-friendly format.";
-
 const char kArchiveTabServiceName[] = "Enable archive tab service";
 const char kArchiveTabServiceDescription[] =
     "Archives suggested tabs for the user that haven't been used beyond a "
@@ -7783,6 +7765,14 @@
     "Enables Google session credentials binding to cryptographic keys that are "
     "practically impossible to extract from the user device. This will mostly "
     "prevent the usage of bound credentials outside of the user device.";
+
+const char kEnableBoundSessionCredentialsSoftwareKeysForManualTestingName[] =
+    "Device Bound Session Credentials with software keys";
+const char
+    kEnableBoundSessionCredentialsSoftwareKeysForManualTestingDescription[] =
+        "Enables mock software-backed cryptographic keys for Google session "
+        "credentials binding (not secure). This is intented to be used for "
+        "manual testing only.";
 #endif  // BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
 
 #if BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 4a8ba80..92c6410 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -376,9 +376,6 @@
 extern const char kAutofillMoreProminentPopupName[];
 extern const char kAutofillMoreProminentPopupDescription[];
 
-extern const char kAutofillUseImprovedLabelDisambiguationName[];
-extern const char kAutofillUseImprovedLabelDisambiguationDescription[];
-
 extern const char kAutofillVirtualCardsOnTouchToFillAndroidName[];
 extern const char kAutofillVirtualCardsOnTouchToFillAndroidDescription[];
 
@@ -1095,9 +1092,6 @@
 extern const char kHandwritingLegacyRecognitionName[];
 extern const char kHandwritingLegacyRecognitionDescription[];
 
-extern const char kHandwritingLibraryDlcName[];
-extern const char kHandwritingLibraryDlcDescription[];
-
 extern const char kHardwareMediaKeyHandling[];
 extern const char kHardwareMediaKeyHandlingDescription[];
 
@@ -2238,9 +2232,6 @@
 extern const char kArchiveTabServiceName[];
 extern const char kArchiveTabServiceDescription[];
 
-extern const char kAutofillUseMobileLabelDisambiguationName[];
-extern const char kAutofillUseMobileLabelDisambiguationDescription[];
-
 extern const char kBackGestureActivityTabProviderName[];
 extern const char kBackGestureActivityTabProviderDescription[];
 
@@ -4506,6 +4497,11 @@
 #if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
 extern const char kEnableBoundSessionCredentialsName[];
 extern const char kEnableBoundSessionCredentialsDescription[];
+
+extern const char
+    kEnableBoundSessionCredentialsSoftwareKeysForManualTestingName[];
+extern const char
+    kEnableBoundSessionCredentialsSoftwareKeysForManualTestingDescription[];
 #endif  // BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
 
 #if BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
diff --git a/chrome/browser/privacy_budget/encountered_surface_tracker.cc b/chrome/browser/privacy_budget/encountered_surface_tracker.cc
index d384ae4..14121956 100644
--- a/chrome/browser/privacy_budget/encountered_surface_tracker.cc
+++ b/chrome/browser/privacy_budget/encountered_surface_tracker.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/privacy_budget/encountered_surface_tracker.h"
 
+#include <set>
 #include <vector>
 
 #include "base/rand_util.h"
@@ -11,24 +12,8 @@
 #include "chrome/browser/privacy_budget/identifiability_study_state.h"
 #include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
 
-namespace {
-
-uint64_t hash(uint64_t x) {
-  // This is should be a reasonable and fast reversible hash.
-  // Based a bit on https://naml.us/post/inverse-of-a-hash-function/
-  x = x * 88388617;  // calculate as (x<<23)+(x<<3)+x
-  x = x ^ (x >> 31);
-  x = x * 257;  // calculate as (x<<8)+x
-  x = x ^ (x >> 13);
-  x = x * 769;  // calculate as (x<<9)+(x<<8)+x
-  x = x ^ (x >> 23);
-  x = x * 127;  // calculate as (x<<7)-x
-  return x;
-}
-
-}  // namespace
-
 const unsigned EncounteredSurfaceTracker::kMaxTrackedSurfaces;
+const unsigned EncounteredSurfaceTracker::kMaxTrackedSources;
 
 EncounteredSurfaceTracker::EncounteredSurfaceTracker() {
   Reset();
@@ -37,7 +22,6 @@
 EncounteredSurfaceTracker::~EncounteredSurfaceTracker() = default;
 
 void EncounteredSurfaceTracker::Reset() {
-  seed_ = base::RandUint64();
   surfaces_.clear();
 }
 
@@ -47,24 +31,24 @@
       blink::IdentifiableSurface::Type::kReservedInternal)
     return false;
 
-  HashKey key = hash(surface ^ seed_);
-  auto it = surfaces_.find(key);
+  auto it = surfaces_.find(surface);
   if (it == surfaces_.end()) {
-    if (surfaces_.size() >= kMaxTrackedSurfaces &&
-        key <= surfaces_.begin()->first) {
-      return false;
-    }
     // We need to add an entry for this surface, possibly bumping an entry out.
-    surfaces_.insert(std::make_pair(key, base::flat_set<uint64_t>{source_id}));
+    surfaces_.insert(std::make_pair(surface, std::set<uint64_t>{source_id}));
     if (surfaces_.size() > kMaxTrackedSurfaces) {
-      // Remove the smallest
-      surfaces_.erase(surfaces_.begin());
+      // Remove a random one.
+      surfaces_.erase(base::RandInt(0, kMaxTrackedSurfaces));
     }
     return true;
   }
 
   if (it->second.contains(source_id))
     return false;
+
   it->second.insert(source_id);
+  if (it->second.size() > kMaxTrackedSources) {
+    // Remove a random one.
+    it->second.erase(base::RandInt(0, kMaxTrackedSources));
+  }
   return true;
 }
diff --git a/chrome/browser/privacy_budget/encountered_surface_tracker.h b/chrome/browser/privacy_budget/encountered_surface_tracker.h
index 32d0ac6..1e0cf54b 100644
--- a/chrome/browser/privacy_budget/encountered_surface_tracker.h
+++ b/chrome/browser/privacy_budget/encountered_surface_tracker.h
@@ -8,8 +8,7 @@
 #include <stdint.h>
 
 #include <map>
-
-#include "base/containers/flat_set.h"
+#include <set>
 
 class EncounteredSurfaceTracker {
  public:
@@ -17,6 +16,10 @@
   // memory growth.
   static constexpr unsigned kMaxTrackedSurfaces = 1000;
 
+  // Maximum number of sources that every surface can track. Prevents unbounded
+  // memory growth.
+  static constexpr unsigned kMaxTrackedSources = 1000;
+
   EncounteredSurfaceTracker();
   ~EncounteredSurfaceTracker();
 
@@ -25,10 +28,9 @@
   void Reset();
 
  private:
-  using HashKey = uint64_t;
-  // We use std::map since it makes it fast to remove the minimum.
-  std::map<HashKey, base::flat_set<uint64_t>> surfaces_;
-  uint64_t seed_;
+  // We use std::map and std::set since these containers are small and we need
+  // to insert and erase frequently.
+  std::map<uint64_t, std::set<uint64_t>> surfaces_;
 };
 
 #endif  // CHROME_BROWSER_PRIVACY_BUDGET_ENCOUNTERED_SURFACE_TRACKER_H_
diff --git a/chrome/browser/privacy_budget/encountered_surface_tracker_unittest.cc b/chrome/browser/privacy_budget/encountered_surface_tracker_unittest.cc
index c5bb4da..41c3b55 100644
--- a/chrome/browser/privacy_budget/encountered_surface_tracker_unittest.cc
+++ b/chrome/browser/privacy_budget/encountered_surface_tracker_unittest.cc
@@ -4,11 +4,13 @@
 
 #include "chrome/browser/privacy_budget/encountered_surface_tracker.h"
 
+#include "base/containers/flat_set.h"
+#include "base/rand_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
 
 namespace {
-uint64_t metric(int i) {
+uint64_t metric(uint64_t i) {
   return blink::IdentifiableSurface::FromTypeAndToken(
              blink::IdentifiableSurface::Type::kWebFeature, i)
       .ToUkmMetricHash();
@@ -22,6 +24,34 @@
   EXPECT_TRUE(t.IsNewEncounter(1, metric(1)));
 }
 
+TEST(EncounteredSurfaceTrackerTest, NeverDropsNewSurface) {
+  EncounteredSurfaceTracker t;
+  std::set<uint64_t> rand_numbers;
+  for (uint64_t i = 0; i < 10000; ++i) {
+    uint64_t new_rand_number;
+    bool inserted;
+    do {
+      new_rand_number = base::RandUint64();
+      inserted = rand_numbers.insert(new_rand_number).second;
+    } while (!inserted);
+    EXPECT_TRUE(t.IsNewEncounter(0, metric(new_rand_number)));
+  }
+}
+
+TEST(EncounteredSurfaceTrackerTest, NeverDropsNewSource) {
+  EncounteredSurfaceTracker t;
+  std::set<uint64_t> rand_numbers;
+  for (uint64_t i = 0; i < 10000; ++i) {
+    uint64_t new_rand_number;
+    bool inserted;
+    do {
+      new_rand_number = base::RandUint64();
+      inserted = rand_numbers.insert(new_rand_number).second;
+    } while (!inserted);
+    EXPECT_TRUE(t.IsNewEncounter(new_rand_number, metric(0)));
+  }
+}
+
 TEST(EncounteredSurfaceTrackerTest, SizeLimit) {
   EncounteredSurfaceTracker t;
   for (uint64_t i = 0; i < EncounteredSurfaceTracker::kMaxTrackedSurfaces;
@@ -36,16 +66,8 @@
   }
 
   // Add an extra one. This should bump one of the surfaces out.
-  t.IsNewEncounter(0, EncounteredSurfaceTracker::kMaxTrackedSurfaces + 1);
-
-  // We expect only kMaxTrackedSurfaces to return true for a new surface.
-  unsigned num_true = 0;
-  for (uint64_t i = 0; i < EncounteredSurfaceTracker::kMaxTrackedSurfaces + 1;
-       i++) {
-    if (t.IsNewEncounter(2, metric(i)))
-      num_true++;
-  }
-  EXPECT_EQ(EncounteredSurfaceTracker::kMaxTrackedSurfaces, num_true);
+  EXPECT_TRUE(
+      t.IsNewEncounter(0, EncounteredSurfaceTracker::kMaxTrackedSurfaces + 1));
 }
 
 TEST(EncounteredSurfaceTrackerTest, Reset) {
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_dialog.ts b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_dialog.ts
index f25a8ac..0648b588 100644
--- a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_dialog.ts
+++ b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_dialog.ts
@@ -17,9 +17,10 @@
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {castExists} from '../assert_extras.js';
-import {GeolocationAccessLevel} from './privacy_hub_geolocation_subpage.js';
 
 import {getTemplate} from './privacy_hub_geolocation_dialog.html.js';
+import {GEOLOCATION_ACCESS_LEVEL_ENUM_SIZE, GeolocationAccessLevel} from './privacy_hub_geolocation_subpage.js';
+import {LOCATION_PERMISSION_CHANGE_FROM_DIALOG_HISTOGRAM_NAME} from './privacy_hub_metrics_util.js';
 
 const PrivacyHubGeolocationDialogBase = PrefsMixin(PolymerElement);
 
@@ -42,6 +43,11 @@
     this.setPrefValue(
         'ash.user.geolocation_access_level',
         GeolocationAccessLevel.ONLY_ALLOWED_FOR_SYSTEM);
+    chrome.metricsPrivate.recordEnumerationValue(
+        LOCATION_PERMISSION_CHANGE_FROM_DIALOG_HISTOGRAM_NAME,
+        GeolocationAccessLevel.ONLY_ALLOWED_FOR_SYSTEM,
+        GEOLOCATION_ACCESS_LEVEL_ENUM_SIZE);
+
     this.getDialog_().close();
   }
 
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.html b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.html
index 4221ad9..171a004 100644
--- a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.html
+++ b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.html
@@ -6,11 +6,13 @@
 
 
 <div
-    id="geolocationDropdownMenu"
+    id="geolocationDropdownDiv"
     class="settings-box first">
   <settings-dropdown-menu
+      id="geolocationDropdown"
       label="Label here"
       menu-options="[[geolocationMapTargets_]]"
+      on-settings-control-change="recordMetric_"
       pref="{{prefs.ash.user.geolocation_access_level}}">
   </settings-dropdown-menu>
 </div>
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.ts b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.ts
index bb00735..62c096f 100644
--- a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.ts
+++ b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_geolocation_subpage.ts
@@ -8,11 +8,13 @@
  * the state of the system geolocation access.
  */
 
-import {DropdownMenuOptionList} from '/shared/settings/controls/settings_dropdown_menu.js';
+import {DropdownMenuOptionList, SettingsDropdownMenuElement} from '/shared/settings/controls/settings_dropdown_menu.js';
+import {PrefsMixin} from 'chrome://resources/cr_components/settings_prefs/prefs_mixin.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './privacy_hub_geolocation_subpage.html.js';
+import {LOCATION_PERMISSION_CHANGE_FROM_SETTINGS_HISTOGRAM_NAME} from './privacy_hub_metrics_util.js';
 
 /**
  * Geolocation access levels for the ChromeOS system.
@@ -25,7 +27,17 @@
   ONLY_ALLOWED_FOR_SYSTEM = 2,
 }
 
-const SettingsPrivacyHubGeolocationSubpageBase = I18nMixin(PolymerElement);
+export const GEOLOCATION_ACCESS_LEVEL_ENUM_SIZE =
+    Object.keys(GeolocationAccessLevel).length;
+
+export interface SettingsPrivacyHubGeolocationSubpage {
+  $: {
+    geolocationDropdown: SettingsDropdownMenuElement,
+  };
+}
+
+const SettingsPrivacyHubGeolocationSubpageBase =
+    PrefsMixin(I18nMixin(PolymerElement));
 
 export class SettingsPrivacyHubGeolocationSubpage extends
     SettingsPrivacyHubGeolocationSubpageBase {
@@ -62,6 +74,14 @@
   }
 
   private geolocationMapTargets_: DropdownMenuOptionList;
+
+  private recordMetric_(): void {
+    const accessLevel = this.$.geolocationDropdown.pref!.value;
+
+    chrome.metricsPrivate.recordEnumerationValue(
+        LOCATION_PERMISSION_CHANGE_FROM_SETTINGS_HISTOGRAM_NAME, accessLevel,
+        GEOLOCATION_ACCESS_LEVEL_ENUM_SIZE);
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_metrics_util.ts b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_metrics_util.ts
index 233dc647..d493800 100644
--- a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_metrics_util.ts
+++ b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_metrics_util.ts
@@ -8,6 +8,14 @@
     'ChromeOS.PrivacyHub.MicrophoneSubpage.UserAction';
 export const LOCATION_SUBPAGE_USER_ACTION_HISTOGRAM_NAME =
     'ChromeOS.PrivacyHub.LocationSubpage.UserAction';
+
+export const LOCATION_PERMISSION_CHANGE_FROM_SETTINGS_HISTOGRAM_NAME =
+    'ChromeOS.PrivacyHub.Geolocation.AccessLevelChanged.SystemSettings';
+export const LOCATION_PERMISSION_CHANGE_FROM_DIALOG_HISTOGRAM_NAME =
+    'ChromeOS.PrivacyHub.Geolocation.AccessLevelChanged.GeolocationDialog';
+export const LOCATION_PERMISSION_CHANGE_FROM_NOTIFICATION_HISTOGRAM_NAME =
+    'ChromeOS.PrivacyHub.Geolocation.AccessLevelChanged.' +
+    'LocationPermissionNotification';
 /**
  * Enumeration of the user actions that can be taken on the Privacy Hub sensor
  * subpages.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index a26861f..139441d3 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -22,7 +22,7 @@
 # chromevox scripts.
 #
 # TS files to compile.
-ts_modules = [ "background/input/keyboard_handler.ts" ]
+ts_modules = []
 
 # TS files needed from ../common/.
 common_ts_modules = [
@@ -107,6 +107,7 @@
   "background/input/command_handler_interface.js",
   "background/input/gesture_command_handler.js",
   "background/input/gesture_interface.js",
+  "background/input/keyboard_handler.js",
   "background/input/smart_sticky_mode.js",
   "background/live_regions.js",
   "background/logging/event_stream_logger.js",
@@ -151,6 +152,7 @@
   "common/key_map.js",
   "common/key_sequence.js",
   "common/key_util.js",
+  "common/keyboard_handler.js",
   "common/learn_mode_bridge.js",
   "common/locale_output_helper.js",
   "common/log_types.js",
@@ -206,23 +208,8 @@
 ts_library("ts_build") {
   root_dir = "../"
   out_dir = tsc_out_dir
-
-  definitions = [
-    "../definitions/tts.d.ts",
-    "//tools/typescript/definitions/metrics_private.d.ts",
-    "//tools/typescript/definitions/context_menus.d.ts",
-    "../definitions/automation.d.ts",
-    "../definitions/extensions.d.ts",
-    "../definitions/runtime.d.ts",
-    "../definitions/i18n.d.ts",
-    "../definitions/tabs.d.ts",
-    "../definitions/accessibility_private_mv2.d.ts",
-    "../definitions/settings_private_mv2.d.ts",
-    "../definitions/storage_mv2.d.ts",
-    "../definitions/clipboard_mv2.d.ts",
-    "../definitions/extension_types.d.ts",
-    "//tools/typescript/definitions/windows.d.ts",
-  ]
+  deps = [ "../common:ts_build" ]
+  definitions = []
 
   in_files = []
   foreach(_js_file, js_deps) {
@@ -231,8 +218,6 @@
   foreach(_ts_file, ts_modules) {
     in_files += [ "chromevox/" + _ts_file ]
   }
-
-  tsconfig_base = "../tsconfig.base.json"
 }
 
 group("build") {
@@ -259,10 +244,7 @@
 run_jsbundler("chromevox_copied_files") {
   mode = "copy"
   dest_dir = chromevox_out_dir
-  deps = [
-    ":ts_build",
-    "../common:ts_build",
-  ]
+  deps = [ "../common:ts_build" ]
   sources = [
     "background/background.html",
     "earcons/chromevox_loaded.ogg",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler.ts b/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler.js
similarity index 69%
rename from chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler.ts
rename to chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler.js
index 87144ea..f7055982 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler.ts
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler.js
@@ -6,6 +6,7 @@
  * @fileoverview ChromeVox keyboard handler.
  */
 import {KeyCode} from '../../../common/key_code.js';
+import {EarconId} from '../../common/earcon_id.js';
 import {EventSourceType} from '../../common/event_source_type.js';
 import {ChromeVoxKbHandler} from '../../common/keyboard_handler.js';
 import {Msgs} from '../../common/msgs.js';
@@ -19,35 +20,36 @@
 import {ChromeVoxPrefs} from '../prefs.js';
 
 /**
+ * @enum {string}
  * Internal pass through mode state (see usage below).
+ * @private
  */
-enum KeyboardPassThroughState {
+const KeyboardPassThroughState_ = {
   // No pass through is in progress.
-  NO_PASS_THROUGH = 'no_pass_through',
+  NO_PASS_THROUGH: 'no_pass_through',
 
   // The pass through shortcut command has been pressed (keydowns), waiting for
   // user to release (keyups) all the shortcut keys.
-  PENDING_PASS_THROUGH_SHORTCUT_KEYUPS = 'pending_pass_through_keyups',
+  PENDING_PASS_THROUGH_SHORTCUT_KEYUPS: 'pending_pass_through_keyups',
 
   // The pass through shortcut command has been pressed and released, waiting
   // for the user to press/release a shortcut to be passed through.
-  PENDING_SHORTCUT_KEYUPS = 'pending_shortcut_keyups',
-}
-
-class InternalKeyEvent extends KeyboardEvent {
-  stickyMode?: boolean;
-}
+  PENDING_SHORTCUT_KEYUPS: 'pending_shortcut_keyups',
+};
 
 export class BackgroundKeyboardHandler {
-  static instance?: BackgroundKeyboardHandler;
-  private static passThroughModeEnabled_: boolean = false;
-  private eatenKeyDowns_: Set<number>;
-  private passThroughState_: KeyboardPassThroughState;
-  private passedThroughKeyDowns_: Set<number>;
-
-  private constructor() {
+  /** @private */
+  constructor() {
+    /** @private {Set} */
     this.eatenKeyDowns_ = new Set();
-    this.passThroughState_ = KeyboardPassThroughState.NO_PASS_THROUGH;
+
+    /** @private {boolean} */
+    this.passThroughModeEnabled_ = false;
+
+    /** @private {!KeyboardPassThroughState_} */
+    this.passThroughState_ = KeyboardPassThroughState_.NO_PASS_THROUGH;
+
+    /** @private {Set} */
     this.passedThroughKeyDowns_ = new Set();
 
     document.addEventListener(
@@ -58,24 +60,25 @@
         true, ChromeVoxPrefs.isStickyPrefOn);
   }
 
-  static init(): void {
+  static init() {
     if (BackgroundKeyboardHandler.instance) {
       throw 'Error: trying to create two instances of singleton BackgroundKeyboardHandler.';
     }
     BackgroundKeyboardHandler.instance = new BackgroundKeyboardHandler();
   }
 
-  static enablePassThroughMode(): void {
+  static enablePassThroughMode() {
     ChromeVox.tts.speak(Msgs.getMsg('pass_through_key'), QueueMode.QUEUE);
-    BackgroundKeyboardHandler.passThroughModeEnabled_ = true;
+    BackgroundKeyboardHandler.instance.passThroughModeEnabled_ = true;
   }
 
   /**
    * Handles key down events.
-   * The return value has no effect since we ignore it in
+   * @param {Event} evt The key down event to process.
+   * @return {boolean} This value has no effect since we ignore it in
    *     SpokenFeedbackEventRewriterDelegate::HandleKeyboardEvent.
    */
-  onKeyDown(evt: InternalKeyEvent): boolean {
+  onKeyDown(evt) {
     EventSource.set(EventSourceType.STANDARD_KEYBOARD);
     evt.stickyMode = ChromeVoxPrefs.isStickyModeOn();
 
@@ -87,7 +90,7 @@
       this.passedThroughKeyDowns_.clear();
     }
 
-    if (BackgroundKeyboardHandler.passThroughModeEnabled_) {
+    if (this.passThroughModeEnabled_) {
       this.passedThroughKeyDowns_.add(evt.keyCode);
       return false;
     }
@@ -99,9 +102,9 @@
 
     if (!this.callOnKeyDownHandlers_(evt) ||
         this.shouldConsumeSearchKey_(evt)) {
-      if (BackgroundKeyboardHandler.passThroughModeEnabled_) {
+      if (this.passThroughModeEnabled_) {
         this.passThroughState_ =
-            KeyboardPassThroughState.PENDING_PASS_THROUGH_SHORTCUT_KEYUPS;
+            KeyboardPassThroughState_.PENDING_PASS_THROUGH_SHORTCUT_KEYUPS;
       }
       evt.preventDefault();
       evt.stopPropagation();
@@ -111,7 +114,12 @@
     return false;
   }
 
-  private callOnKeyDownHandlers_(evt: Event): boolean {
+  /**
+   * @param {Event} evt The key down event to process.
+   * @return {boolean} Whether the event should continue propagating.
+   * @private
+   */
+  callOnKeyDownHandlers_(evt) {
     // Defer first to the math handler, if it exists, then ordinary keyboard
     // commands.
     if (!MathHandler.onKeyDown(evt)) {
@@ -126,7 +134,12 @@
     return ChromeVoxKbHandler.basicKeyDownActionsListener(evt);
   }
 
-  private shouldConsumeSearchKey_(evt: InternalKeyEvent): boolean {
+  /**
+   * @param {Event} evt The key down event to evaluate.
+   * @return {boolean} Whether the event should be consumed.
+   * @private
+   */
+  shouldConsumeSearchKey_(evt) {
     // We natively always capture Search, so we have to be very careful to
     // either eat it here or re-inject it; otherwise, some components, like
     // ARC++ with TalkBack never get it. We only want to re-inject when
@@ -135,44 +148,49 @@
       return false;
     }
 
-    return Boolean(evt.metaKey) || evt.keyCode === KeyCode['SEARCH'];
+    return Boolean(evt.metaKey) || evt.keyCode === KeyCode.SEARCH;
   }
 
   /**
-   * The return value has no effect since we ignore it in
+   * Handles key up events.
+   * @param {Event} evt The key up event to process.
+   * @return {boolean} This value has no effect since we ignore it in
    *     SpokenFeedbackEventRewriterDelegate::HandleKeyboardEvent.
    */
-  onKeyUp(evt: InternalKeyEvent): boolean {
+  onKeyUp(evt) {
     if (this.eatenKeyDowns_.has(evt.keyCode)) {
       evt.preventDefault();
       evt.stopPropagation();
       this.eatenKeyDowns_.delete(evt.keyCode);
     }
 
-    if (BackgroundKeyboardHandler.passThroughModeEnabled_) {
+    if (this.passThroughModeEnabled_) {
       this.passedThroughKeyDowns_.delete(evt.keyCode);
 
       // Assuming we have no keys held (detected by held modifiers + keys we've
       // eaten in key down), we can start pass through for the next keys.
       if (this.passThroughState_ ===
-              KeyboardPassThroughState.PENDING_PASS_THROUGH_SHORTCUT_KEYUPS &&
+              KeyboardPassThroughState_.PENDING_PASS_THROUGH_SHORTCUT_KEYUPS &&
           !evt.altKey && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey &&
           this.eatenKeyDowns_.size === 0) {
         // All keys of the pass through shortcut command have been released.
         // Ready to pass through the next shortcut.
         this.passThroughState_ =
-            KeyboardPassThroughState.PENDING_SHORTCUT_KEYUPS;
+            KeyboardPassThroughState_.PENDING_SHORTCUT_KEYUPS;
       } else if (
           this.passThroughState_ ===
-              KeyboardPassThroughState.PENDING_SHORTCUT_KEYUPS &&
+              KeyboardPassThroughState_.PENDING_SHORTCUT_KEYUPS &&
           this.passedThroughKeyDowns_.size === 0) {
         // All keys of the passed through shortcut have been released. Ready to
         // go back to normal processing (aka no pass through).
-        BackgroundKeyboardHandler.passThroughModeEnabled_ = false;
-        this.passThroughState_ = KeyboardPassThroughState.NO_PASS_THROUGH;
+        this.passThroughModeEnabled_ = false;
+        this.passThroughState_ = KeyboardPassThroughState_.NO_PASS_THROUGH;
       }
     }
 
     return false;
   }
 }
+
+/** @type {BackgroundKeyboardHandler} */
+BackgroundKeyboardHandler.instance;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler_test.js
index 0149195..f693f04 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler_test.js
@@ -57,7 +57,7 @@
     'ChromeVoxBackgroundKeyboardHandlerTest', 'PassThroughMode',
     async function() {
       await this.runWithLoadedTree('<p>test</p>');
-      assertFalse(BackgroundKeyboardHandler.passThroughModeEnabled_);
+      assertFalse(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
       assertEquals('no_pass_through', keyboardHandler.passThroughState_);
       assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
       assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
@@ -69,7 +69,7 @@
       assertEquals(1, keyboardHandler.eatenKeyDowns_.size);
       assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals('no_pass_through', keyboardHandler.passThroughState_);
-      assertFalse(BackgroundKeyboardHandler.passThroughModeEnabled_);
+      assertFalse(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
 
       const searchShift = TestUtils.createMockKeyEvent(
           KeyCode.SHIFT, {metaKey: true, shiftKey: true});
@@ -77,7 +77,7 @@
       assertEquals(2, keyboardHandler.eatenKeyDowns_.size);
       assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals('no_pass_through', keyboardHandler.passThroughState_);
-      assertFalse(BackgroundKeyboardHandler.passThroughModeEnabled_);
+      assertFalse(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
 
       const searchShiftEsc = TestUtils.createMockKeyEvent(
           KeyCode.ESCAPE, {metaKey: true, shiftKey: true});
@@ -86,14 +86,14 @@
       assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_pass_through_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
 
       keyboardHandler.onKeyUp(searchShiftEsc);
       assertEquals(2, keyboardHandler.eatenKeyDowns_.size);
       assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_pass_through_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
 
       const searchShiftUp = TestUtils.createMockKeyEvent(
           KeyCode.SHIFT, {metaKey: true, shiftKey: false});
@@ -102,7 +102,7 @@
       assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_pass_through_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
 
       const searchUp =
           TestUtils.createMockKeyEvent(KeyCode.SEARCH, {metaKey: false});
@@ -111,7 +111,7 @@
       assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_shortcut_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
 
       // Now, the next series of key downs should be passed through.
       // Try Search+Ctrl+M.
@@ -120,7 +120,7 @@
       assertEquals(1, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_shortcut_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
 
       const searchCtrl = TestUtils.createMockKeyEvent(
           KeyCode.CONTROL, {metaKey: true, ctrlKey: true});
@@ -129,7 +129,7 @@
       assertEquals(2, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_shortcut_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
 
       const searchCtrlM = TestUtils.createMockKeyEvent(
           KeyCode.M, {metaKey: true, ctrlKey: true});
@@ -138,27 +138,27 @@
       assertEquals(3, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_shortcut_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
 
       keyboardHandler.onKeyUp(searchCtrlM);
       assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
       assertEquals(2, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_shortcut_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
 
       keyboardHandler.onKeyUp(searchCtrl);
       assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
       assertEquals(1, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_shortcut_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
 
       keyboardHandler.onKeyUp(search);
       assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
       assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals('no_pass_through', keyboardHandler.passThroughState_);
-      assertFalse(BackgroundKeyboardHandler.passThroughModeEnabled_);
+      assertFalse(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
     });
 
 AX_TEST_F(
@@ -166,7 +166,7 @@
     async function() {
       await this.runWithLoadedTree('<p>test</p>');
       function assertNoPassThrough() {
-        assertFalse(BackgroundKeyboardHandler.passThroughModeEnabled_);
+        assertFalse(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
         assertEquals('no_pass_through', keyboardHandler.passThroughState_);
         assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
       }
@@ -246,7 +246,7 @@
     'UnexpectedKeyDownUpPairsPassThrough', async function() {
       await this.runWithLoadedTree('<p>test</p>');
       // Force pass through mode.
-      BackgroundKeyboardHandler.passThroughModeEnabled_ = true;
+      BackgroundKeyboardHandler.instance.passThroughModeEnabled_ = true;
 
       // Send a few key downs (which are passed through).
       const search =
diff --git a/chrome/browser/resources/chromeos/login/login.gni b/chrome/browser/resources/chromeos/login/login.gni
index a9cfea16..58416083 100644
--- a/chrome/browser/resources/chromeos/login/login.gni
+++ b/chrome/browser/resources/chromeos/login/login.gni
@@ -95,7 +95,7 @@
   "screens/oobe/auto_enrollment_check.js",
   "screens/oobe/consumer_update.js",
   "screens/oobe/demo_preferences.ts",
-  "screens/oobe/demo_setup.js",
+  "screens/oobe/demo_setup.ts",
   "screens/oobe/enable_debugging.js",
   "screens/oobe/enterprise_enrollment.js",
   "screens/oobe/hid_detection.js",
diff --git a/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn
index 70b4460e..fad95fb 100644
--- a/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn
@@ -19,7 +19,6 @@
   deps = [
     ":auto_enrollment_check",
     ":consumer_update",
-    ":demo_setup",
     ":enable_debugging",
     ":enterprise_enrollment",
     ":hid_detection",
@@ -58,20 +57,6 @@
   extra_deps = [ ":web_components" ]
 }
 
-js_library("demo_setup") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/oobe/demo_setup.js" ]
-  deps = [
-    "../../components:progress_list_item",
-    "../../components/behaviors:login_screen_behavior",
-    "../../components/behaviors:multi_step_behavior",
-    "../../components/behaviors:oobe_dialog_host_behavior",
-    "../../components/behaviors:oobe_i18n_behavior",
-    "../../components/dialogs:oobe_adaptive_dialog",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-  ]
-  extra_deps = [ ":web_components" ]
-}
-
 js_library("enable_debugging") {
   sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/oobe/enable_debugging.js" ]
   deps = [
diff --git a/chrome/browser/resources/chromeos/login/screens/oobe/demo_setup.js b/chrome/browser/resources/chromeos/login/screens/oobe/demo_setup.js
deleted file mode 100644
index 89489154..0000000
--- a/chrome/browser/resources/chromeos/login/screens/oobe/demo_setup.js
+++ /dev/null
@@ -1,230 +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.
-
-/**
- * @fileoverview Polymer element for displaying material design Demo Setup
- * screen.
- */
-
-import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
-import '../../components/oobe_icons.html.js';
-import '../../components/buttons/oobe_back_button.js';
-import '../../components/buttons/oobe_text_button.js';
-import '../../components/common_styles/oobe_common_styles.css.js';
-import '../../components/common_styles/oobe_dialog_host_styles.css.js';
-import '../../components/dialogs/oobe_adaptive_dialog.js';
-import '../../components/progress_list_item.js';
-
-import {loadTimeData} from '//resources/ash/common/load_time_data.m.js';
-import {mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-import {LoginScreenBehavior, LoginScreenBehaviorInterface} from '../../components/behaviors/login_screen_behavior.js';
-import {MultiStepBehavior, MultiStepBehaviorInterface} from '../../components/behaviors/multi_step_behavior.js';
-import {OobeDialogHostBehavior} from '../../components/behaviors/oobe_dialog_host_behavior.js';
-import {OobeI18nBehavior, OobeI18nBehaviorInterface} from '../../components/behaviors/oobe_i18n_behavior.js';
-
-import {getTemplate} from './demo_setup.html.js';
-
-
-/**
- * UI mode for the dialog.
- * @enum {string}
- */
-const DemoSetupUIState = {
-  PROGRESS: 'progress',
-  ERROR: 'error',
-};
-
-/**
- * @constructor
- * @extends {PolymerElement}
- * @implements {LoginScreenBehaviorInterface}
- * @implements {MultiStepBehaviorInterface}
- * @implements {OobeI18nBehaviorInterface}
- */
-const DemoSetupScreenBase = mixinBehaviors(
-    [
-      OobeI18nBehavior,
-      OobeDialogHostBehavior,
-      LoginScreenBehavior,
-      MultiStepBehavior,
-    ],
-    PolymerElement);
-
-/**
- * @polymer
- */
-class DemoSetupScreen extends DemoSetupScreenBase {
-  static get is() {
-    return 'demo-setup-element';
-  }
-
-  static get template() {
-    return getTemplate();
-  }
-
-  static get properties() {
-    return {
-      /** Object mapping step strings to step indices */
-      setupSteps_: {
-        type: Object,
-        value() {
-          return /** @type {!Object} */ (
-              loadTimeData.getValue('demoSetupSteps'));
-        },
-      },
-
-      /** Which step index is currently running in Demo Mode setup. */
-      currentStepIndex_: {
-        type: Number,
-        value: -1,
-      },
-
-      /** Error message displayed on demoSetupErrorDialog screen. */
-      errorMessage_: {
-        type: String,
-        value: '',
-      },
-
-      /** Whether powerwash is required in case of a setup error. */
-      isPowerwashRequired_: {
-        type: Boolean,
-        value: false,
-      },
-    };
-  }
-
-  constructor() {
-    super();
-  }
-
-  /** @override */
-  ready() {
-    super.ready();
-    this.initializeLoginScreen('DemoSetupScreen');
-  }
-
-  defaultUIStep() {
-    return DemoSetupUIState.PROGRESS;
-  }
-
-  get UI_STEPS() {
-    return DemoSetupUIState;
-  }
-
-  /** Overridden from LoginScreenBehavior. */
-  // clang-format off
-  get EXTERNAL_API() {
-    return ['setCurrentSetupStep',
-            'onSetupSucceeded',
-            'onSetupFailed'];
-  }
-  // clang-format on
-
-
-  onBeforeShow() {
-    this.reset();
-  }
-
-  /** Resets demo setup flow to the initial screen and starts setup. */
-  reset() {
-    this.setUIStep(DemoSetupUIState.PROGRESS);
-    this.userActed('start-setup');
-  }
-
-  /** Called after resources are updated. */
-  updateLocalizedContent() {
-    this.i18nUpdateLocale();
-  }
-
-  /**
-   * Called at the beginning of a setup step.
-   * @param {string} currentStep The new step name.
-   */
-  setCurrentSetupStep(currentStep) {
-    // If new step index not specified, remain unchanged.
-    if (this.setupSteps_.hasOwnProperty(currentStep)) {
-      this.currentStepIndex_ = this.setupSteps_[currentStep];
-    }
-  }
-
-  /** Called when demo mode setup succeeded. */
-  onSetupSucceeded() {
-    this.errorMessage_ = '';
-  }
-
-  /**
-   * Called when demo mode setup failed.
-   * @param {string} message Error message to be displayed to the user.
-   * @param {boolean} isPowerwashRequired Whether powerwash is required to
-   *     recover from the error.
-   */
-  onSetupFailed(message, isPowerwashRequired) {
-    this.errorMessage_ = message;
-    this.isPowerwashRequired_ = isPowerwashRequired;
-    this.setUIStep(DemoSetupUIState.ERROR);
-  }
-
-  /**
-   * Retry button click handler.
-   * @private
-   */
-  onRetryClicked_() {
-    this.reset();
-  }
-
-  /**
-   * Powerwash button click handler.
-   * @private
-   */
-  onPowerwashClicked_() {
-    this.userActed('powerwash');
-  }
-
-  /**
-   * Close button click handler.
-   * @private
-   */
-  onCloseClicked_() {
-    // TODO(wzang): Remove this after crbug.com/900640 is fixed.
-    if (this.isPowerwashRequired_) {
-      return;
-    }
-    this.userActed('close-setup');
-  }
-
-  /**
-   * Whether a given step should be rendered on the UI.
-   * @param {string} stepName The name of the step (from the enum).
-   * @param {!Object} setupSteps
-   * @private
-   */
-  shouldShowStep_(stepName, setupSteps) {
-    return setupSteps.hasOwnProperty(stepName);
-  }
-
-  /**
-   * Whether a given step is active.
-   * @param {string} stepName The name of the step (from the enum).
-   * @param {!Object} setupSteps
-   * @param {number} currentStepIndex
-   * @private
-   */
-  stepIsActive_(stepName, setupSteps, currentStepIndex) {
-    return currentStepIndex === setupSteps[stepName];
-  }
-
-  /**
-   * Whether a given step is completed.
-   * @param {string} stepName The name of the step (from the enum).
-   * @param {!Object} setupSteps
-   * @param {number} currentStepIndex
-   * @private
-   */
-  stepIsCompleted_(stepName, setupSteps, currentStepIndex) {
-    return currentStepIndex > setupSteps[stepName];
-  }
-}
-
-customElements.define(DemoSetupScreen.is, DemoSetupScreen);
diff --git a/chrome/browser/resources/chromeos/login/screens/oobe/demo_setup.ts b/chrome/browser/resources/chromeos/login/screens/oobe/demo_setup.ts
new file mode 100644
index 0000000..c9350478
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/screens/oobe/demo_setup.ts
@@ -0,0 +1,221 @@
+// 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.
+
+/**
+ * @fileoverview Polymer element for displaying material design Demo Setup
+ * screen.
+ */
+
+import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import '../../components/oobe_icons.html.js';
+import '../../components/buttons/oobe_back_button.js';
+import '../../components/buttons/oobe_text_button.js';
+import '../../components/common_styles/oobe_common_styles.css.js';
+import '../../components/common_styles/oobe_dialog_host_styles.css.js';
+import '../../components/dialogs/oobe_adaptive_dialog.js';
+import '../../components/progress_list_item.js';
+
+import {loadTimeData} from '//resources/js/load_time_data.js';
+import {PolymerElementProperties} from '//resources/polymer/v3_0/polymer/interfaces.js';
+import {mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {LoginScreenBehavior, LoginScreenBehaviorInterface} from '../../components/behaviors/login_screen_behavior.js';
+import {MultiStepBehavior, MultiStepBehaviorInterface} from '../../components/behaviors/multi_step_behavior.js';
+import {OobeDialogHostBehavior, OobeDialogHostBehaviorInterface} from '../../components/behaviors/oobe_dialog_host_behavior.js';
+import {OobeI18nBehavior, OobeI18nBehaviorInterface} from '../../components/behaviors/oobe_i18n_behavior.js';
+
+import {getTemplate} from './demo_setup.html.js';
+
+/**
+ * UI mode for the dialog.
+ */
+enum DemoSetupUiState {
+  PROGRESS = 'progress',
+  ERROR = 'error',
+}
+
+export const DemoSetupScreenBase = mixinBehaviors(
+                                       [
+                                         OobeI18nBehavior,
+                                         OobeDialogHostBehavior,
+                                         LoginScreenBehavior,
+                                         MultiStepBehavior,
+                                       ],
+                                       PolymerElement) as {
+  new (): PolymerElement & OobeI18nBehaviorInterface &
+      OobeDialogHostBehaviorInterface & LoginScreenBehaviorInterface &
+      MultiStepBehaviorInterface,
+};
+
+class DemoSetupScreen extends DemoSetupScreenBase {
+  static get is() {
+    return 'demo-setup-element' as const;
+  }
+
+  static get template(): HTMLTemplateElement {
+    return getTemplate();
+  }
+
+  static get properties(): PolymerElementProperties {
+    return {
+      /** Object mapping step strings to step indices */
+      setupSteps_: {
+        type: Object,
+        value() {
+          return loadTimeData.getValue('demoSetupSteps');
+        },
+      },
+
+      /** Which step index is currently running in Demo Mode setup. */
+      currentStepIndex_: {
+        type: Number,
+        value: -1,
+      },
+
+      /** Error message displayed on demoSetupErrorDialog screen. */
+      errorMessage_: {
+        type: String,
+        value: '',
+      },
+
+      /** Whether powerwash is required in case of a setup error. */
+      isPowerwashRequired_: {
+        type: Boolean,
+        value: false,
+      },
+    };
+  }
+
+  private setupSteps_: Record<string, number>;
+  private currentStepIndex_: number;
+  private errorMessage_: string;
+  private isPowerwashRequired_: boolean;
+
+  constructor() {
+    super();
+  }
+
+  override ready(): void {
+    super.ready();
+    this.initializeLoginScreen('DemoSetupScreen');
+  }
+
+  // eslint-disable-next-line @typescript-eslint/naming-convention
+  override defaultUIStep() {
+    return DemoSetupUiState.PROGRESS;
+  }
+
+  override get UI_STEPS() {
+    return DemoSetupUiState;
+  }
+
+  /** Overridden from LoginScreenBehavior. */
+  override get EXTERNAL_API(): string[] {
+    return ['setCurrentSetupStep', 'onSetupSucceeded', 'onSetupFailed'];
+  }
+
+  override onBeforeShow(): void {
+    this.reset();
+  }
+
+  /** Resets demo setup flow to the initial screen and starts setup. */
+  reset(): void {
+    this.setUIStep(DemoSetupUiState.PROGRESS);
+    this.userActed('start-setup');
+  }
+
+  /** Called after resources are updated. */
+  override updateLocalizedContent(): void {
+    this.i18nUpdateLocale();
+  }
+
+  /**
+   * Called at the beginning of a setup step.
+   * @param currentStep The new step name.
+   */
+  setCurrentSetupStep(currentStep: string): void {
+    // If new step index not specified, remain unchanged.
+    if (this.setupSteps_.hasOwnProperty(currentStep)) {
+      this.currentStepIndex_ = this.setupSteps_[currentStep];
+    }
+  }
+
+  /** Called when demo mode setup succeeded. */
+  onSetupSucceeded(): void {
+    this.errorMessage_ = '';
+  }
+
+  /**
+   * Called when demo mode setup failed.
+   * @param message Error message to be displayed to the user.
+   * @param isPowerwashRequired Whether powerwash is required to
+   *     recover from the error.
+   */
+  onSetupFailed(message: string, isPowerwashRequired: boolean): void {
+    this.errorMessage_ = message;
+    this.isPowerwashRequired_ = isPowerwashRequired;
+    this.setUIStep(DemoSetupUiState.ERROR);
+  }
+
+  /**
+   * Retry button click handler.
+   */
+  private onRetryClicked_(): void {
+    this.reset();
+  }
+
+  /**
+   * Powerwash button click handler.
+   */
+  private onPowerwashClicked_(): void {
+    this.userActed('powerwash');
+  }
+
+  /**
+   * Close button click handler.
+   */
+  private onCloseClicked_(): void {
+    // TODO(wzang): Remove this after crbug.com/900640 is fixed.
+    if (this.isPowerwashRequired_) {
+      return;
+    }
+    this.userActed('close-setup');
+  }
+
+  /**
+   * Whether a given step should be rendered on the UI.
+   * @param stepName The name of the step (from the enum).
+   */
+  private shouldShowStep_(stepName: string, setupSteps: Object): boolean {
+    return setupSteps.hasOwnProperty(stepName);
+  }
+
+  /**
+   * Whether a given step is active.
+   * @param stepName The name of the step (from the enum).
+   */
+  private stepIsActive_(
+      stepName: string, setupSteps: Record<string, number>,
+      currentStepIndex: number): boolean {
+    return currentStepIndex === setupSteps[stepName];
+  }
+
+  /**
+   * Whether a given step is completed.
+   * @param stepName The name of the step (from the enum).
+   */
+  private stepIsCompleted_(
+      stepName: string, setupSteps: Record<string, number>,
+      currentStepIndex: number): boolean {
+    return currentStepIndex > setupSteps[stepName];
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    [DemoSetupScreen.is]: DemoSetupScreen;
+  }
+}
+
+customElements.define(DemoSetupScreen.is, DemoSetupScreen);
diff --git a/chrome/browser/resources/chromeos/login/screens/osauth/local_data_loss_warning.html b/chrome/browser/resources/chromeos/login/screens/osauth/local_data_loss_warning.html
index 32b6f71..8ca37310 100644
--- a/chrome/browser/resources/chromeos/login/screens/osauth/local_data_loss_warning.html
+++ b/chrome/browser/resources/chromeos/login/screens/osauth/local_data_loss_warning.html
@@ -43,7 +43,7 @@
     </oobe-back-button>
   </div>
   <div slot="bottom-buttons">
-    <oobe-text-button hidden="[[isOwner]]" id="proceedAnyway"
+    <oobe-text-button hidden="[[isOwner]]" id="proceedRemove"
         on-click="onProceedClicked_" text-key="continueAnywayButtonLabel">
     </oobe-text-button>
     <oobe-text-button hidden= "[[!isOwner]]" id="powerwash"
diff --git a/chrome/browser/resources/search_engine_choice/app.html b/chrome/browser/resources/search_engine_choice/app.html
index e40a217c..7fc1c928 100644
--- a/chrome/browser/resources/search_engine_choice/app.html
+++ b/chrome/browser/resources/search_engine_choice/app.html
@@ -8,6 +8,7 @@
     --search-engine-omnibox-shadow-color: var(--google-grey-300);
     --scrollbar-height: 116px;
     --action-button-margins: 40px;
+    --omnibox-width: 252px;
     color: var(--cr-primary-text-color);
   }
 
@@ -72,7 +73,7 @@
     flex-direction: row;
     gap: 12px;
     padding: var(--dummy-omnibox-vertical-padding) 0;
-    width: 230px;
+    width: var(--omnibox-width);
   }
 
   /* We modify the bottom and transform parameters so that the
@@ -92,7 +93,7 @@
     opacity: 0;
     position: absolute;
     transform: translateY(var(--search-engine-omnibox-bottom-value));
-    width: 234px;
+    width: var(--omnibox-width);
   }
 
   .fade-in-animation {
@@ -145,7 +146,7 @@
   .omnibox-text,
   .choice-title {
     font-family: Roboto, Arial;
-    font-size: 0.875rem;
+    font-size: 0.8125rem;
     font-weight: 400;
     line-height: 20px;
     text-align: start;
@@ -276,12 +277,6 @@
       transform: rotate(5deg);
     }
 
-    /* To add a bouncing effect. */
-    90% {
-      top: -9%;
-      transform: translateY(0);
-    }
-
     100% {
       opacity: 1;
       top: 0;
diff --git a/chrome/browser/sync/test/integration/ash_trusted_vault_keys_sharing_sync_test.cc b/chrome/browser/sync/test/integration/ash_trusted_vault_keys_sharing_sync_test.cc
index d0e0cb7..f1bd1ab9 100644
--- a/chrome/browser/sync/test/integration/ash_trusted_vault_keys_sharing_sync_test.cc
+++ b/chrome/browser/sync/test/integration/ash_trusted_vault_keys_sharing_sync_test.cc
@@ -2,29 +2,95 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <ostream>
+
 #include "base/files/file_path.h"
+#include "base/notreached.h"
 #include "base/test/test_future.h"
 #include "chrome/browser/ash/crosapi/crosapi_ash.h"
 #include "chrome/browser/ash/crosapi/crosapi_manager.h"
+#include "chrome/browser/ash/sync/sync_error_notifier.h"
+#include "chrome/browser/ash/sync/sync_error_notifier_factory.h"
+#include "chrome/browser/notifications/notification_display_service_tester.h"
+#include "chrome/browser/sync/test/integration/encryption_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/trusted_vault/trusted_vault_service_factory.h"
+#include "chrome/browser/ui/webui/trusted_vault/trusted_vault_dialog_delegate.h"
+#include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "chromeos/ash/components/standalone_browser/standalone_browser_features.h"
 #include "chromeos/crosapi/mojom/trusted_vault.mojom.h"
 #include "components/sync/service/sync_service_impl.h"
+#include "components/sync/test/fake_server_nigori_helper.h"
+#include "components/sync/test/nigori_test_utils.h"
 #include "components/trusted_vault/features.h"
 #include "components/trusted_vault/trusted_vault_client.h"
 #include "components/trusted_vault/trusted_vault_server_constants.h"
 #include "components/trusted_vault/trusted_vault_service.h"
 #include "content/public/test/browser_test.h"
+#include "google_apis/gaia/gaia_switches.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/views/test/widget_test.h"
+#include "ui/views/widget/any_widget_observer.h"
 
 namespace {
 
+using testing::ElementsAre;
 using testing::Eq;
+using testing::IsEmpty;
 using testing::NotNull;
 
+class TrustedVaultStateNotifiedToCrosapiObserverChecker
+    : public StatusChangeChecker,
+      public crosapi::mojom::TrustedVaultBackendObserver {
+ public:
+  enum class ExpectedNotification { kKeysChanged, kRecoverabilityStateChanged };
+
+  TrustedVaultStateNotifiedToCrosapiObserverChecker(
+      mojo::Remote<crosapi::mojom::TrustedVaultBackend>* backend_remote,
+      ExpectedNotification expected_notification)
+      : expected_notification_(expected_notification) {
+    CHECK(backend_remote);
+    backend_remote->get()->AddObserver(receiver_.BindNewPipeAndPassRemote());
+    backend_remote->FlushForTesting();
+  }
+
+  // crosapi::mojom::TrustedVaultBackendObserver overrides.
+  void OnTrustedVaultKeysChanged() override {
+    keys_changed_notified_ = true;
+    CheckExitCondition();
+  }
+
+  void OnTrustedVaultRecoverabilityChanged() override {
+    recoverability_state_changed_notified_ = true;
+    CheckExitCondition();
+  }
+
+  // StatusChangeChecker overrides.
+  bool IsExitConditionSatisfied(std::ostream* os) override {
+    switch (expected_notification_) {
+      case ExpectedNotification::kKeysChanged:
+        *os << "Waiting for OnTrustedVaultKeysChanged() call for crosapi "
+               "observer.";
+        return keys_changed_notified_;
+      case ExpectedNotification::kRecoverabilityStateChanged:
+        *os << "Waiting for OnTrustedVaultRecoverabilityChanged() call for "
+               "crosapi observer.";
+        return recoverability_state_changed_notified_;
+    }
+    NOTREACHED_NORETURN();
+  }
+
+ private:
+  bool keys_changed_notified_ = false;
+  bool recoverability_state_changed_notified_ = false;
+
+  ExpectedNotification expected_notification_;
+  mojo::Receiver<crosapi::mojom::TrustedVaultBackendObserver> receiver_{this};
+};
+
 class AshTrustedVaultKeysSharingSyncTest : public SyncTest {
  public:
   AshTrustedVaultKeysSharingSyncTest() : SyncTest(SINGLE_CLIENT) {
@@ -49,6 +115,22 @@
         ash::BrowserContextHelper::kTestUserBrowserContextDirName);
   }
 
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    SyncTest::SetUpCommandLine(command_line);
+
+    ASSERT_TRUE(embedded_https_test_server().InitializeAndListen());
+    const GURL& base_url = embedded_https_test_server().base_url();
+    command_line->AppendSwitchASCII(switches::kGaiaUrl, base_url.spec());
+  }
+
+  void SetUpOnMainThread() override {
+    SyncTest::SetUpOnMainThread();
+    trusted_vault_widget_shown_waiter_ =
+        std::make_unique<views::NamedWidgetShownWaiter>(
+            views::test::AnyWidgetTestPasskey{},
+            TrustedVaultDialogDelegate::kWidgetName);
+  }
+
   void SetupCrosapi() {
     ASSERT_TRUE(crosapi::browser_util::IsLacrosEnabled());
 
@@ -60,6 +142,20 @@
         trusted_vault_backend_remote_.BindNewPipeAndPassReceiver());
   }
 
+  bool SetupSyncAndTrustedVaultFakes() {
+    if (!SetupSync()) {
+      return false;
+    }
+
+    encryption_helper::SetupFakeTrustedVaultPages(
+        GetSyncService(0)->GetAccountInfo().gaia, kTestTrustedVaultKey,
+        /*trusted_vault_key_version=*/1,
+        /*recovery_method_public_key=*/{}, &embedded_https_test_server());
+    embedded_https_test_server().StartAcceptingConnections();
+
+    return true;
+  }
+
   trusted_vault::TrustedVaultClient& GetAshSyncTrustedVaultClient() {
     trusted_vault::TrustedVaultService* trusted_vault_service =
         TrustedVaultServiceFactory::GetForProfile(GetProfile(0));
@@ -83,6 +179,14 @@
     return fetched_keys_future.Take();
   }
 
+  TrustedVaultStateNotifiedToCrosapiObserverChecker
+  CreateTrustedVaultStateNotifiedToCrosapiObserverChecker(
+      TrustedVaultStateNotifiedToCrosapiObserverChecker::ExpectedNotification
+          expected_notification) {
+    return TrustedVaultStateNotifiedToCrosapiObserverChecker(
+        &trusted_vault_backend_remote_, expected_notification);
+  }
+
   CoreAccountInfo GetSyncingUserAccountInfo() {
     return GetSyncService(0)->GetAccountInfo();
   }
@@ -94,26 +198,94 @@
     return account_key;
   }
 
+  bool WaitForTrustedVaultReauthCompletion() {
+    CHECK(trusted_vault_widget_shown_waiter_);
+    views::Widget* trusted_vault_widged =
+        trusted_vault_widget_shown_waiter_->WaitIfNeededAndGet();
+    views::test::WidgetDestroyedWaiter(trusted_vault_widged).Wait();
+    return true;
+  }
+
+ protected:
+  const std::vector<uint8_t> kTestTrustedVaultKey = {1, 2, 3};
+
  private:
   base::test::ScopedFeatureList feature_list_;
 
+  std::unique_ptr<views::NamedWidgetShownWaiter>
+      trusted_vault_widget_shown_waiter_;
+
   mojo::Remote<crosapi::mojom::TrustedVaultBackend>
       trusted_vault_backend_remote_;
 };
 
 IN_PROC_BROWSER_TEST_F(AshTrustedVaultKeysSharingSyncTest,
                        ShouldFetchStoredKeysThroughCrosapi) {
-  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(SetupSyncAndTrustedVaultFakes());
 
   // Mimic that Ash already has trusted vault key.
-  std::vector<std::vector<uint8_t>> trusted_vault_keys = {{1, 2, 3}};
   GetAshSyncTrustedVaultClient().StoreKeys(GetSyncingUserAccountInfo().gaia,
-                                           trusted_vault_keys,
+                                           {kTestTrustedVaultKey},
                                            /*last_key_version*/ 1);
 
   // Mimic that Lacros starts and attempts to fetch keys, it should succeed.
   SetupCrosapi();
-  EXPECT_THAT(FetchKeysThroughCrosapi(), Eq(trusted_vault_keys));
+  EXPECT_THAT(FetchKeysThroughCrosapi(), ElementsAre(kTestTrustedVaultKey));
+}
+
+IN_PROC_BROWSER_TEST_F(AshTrustedVaultKeysSharingSyncTest,
+                       ShouldAcceptKeysFromTheWebAndFetchThemThroughCrosapi) {
+  // Mimic the account being already using a trusted vault passphrase.
+  SetNigoriInFakeServer(
+      syncer::BuildTrustedVaultNigoriSpecifics({kTestTrustedVaultKey}),
+      GetFakeServer());
+
+  // Need to create `display_service` before key missing notification is shown
+  // (it will be shown during SetupSyncAndTrustedVaultFakes()).
+  ASSERT_TRUE(SetupClients());
+  NotificationDisplayServiceTester display_service(GetProfile(0));
+
+  ASSERT_TRUE(SetupSyncAndTrustedVaultFakes());
+  SetupCrosapi();
+
+  // No keys yet available in Ash, Lacros will fetch empty keys.
+  EXPECT_THAT(FetchKeysThroughCrosapi(), IsEmpty());
+
+  EXPECT_TRUE(GetSyncService(0)
+                  ->GetUserSettings()
+                  ->IsTrustedVaultKeyRequiredForPreferredDataTypes());
+  // Key missing notification should be displayed.
+  const std::string notification_id =
+      ash::SyncErrorNotifierFactory::GetForProfile(GetProfile(0))
+          ->GetNotificationIdForTesting();
+  absl::optional<message_center::Notification> notification =
+      display_service.GetNotification(notification_id);
+  ASSERT_TRUE(notification);
+  EXPECT_THAT(notification->title(),
+              Eq(l10n_util::GetStringUTF16(
+                  IDS_SYNC_ERROR_PASSWORDS_BUBBLE_VIEW_TITLE)));
+  EXPECT_THAT(
+      notification->message(),
+      Eq(l10n_util::GetStringUTF16(
+          IDS_SYNC_NEEDS_KEYS_FOR_PASSWORDS_ERROR_BUBBLE_VIEW_MESSAGE)));
+
+  auto keys_changed_notified_checker =
+      CreateTrustedVaultStateNotifiedToCrosapiObserverChecker(
+          TrustedVaultStateNotifiedToCrosapiObserverChecker::
+              ExpectedNotification::kKeysChanged);
+  // Mimic the user going through key retrieval:
+  // 1. User clicks on the notification.
+  // 2. It opens reauth page (note that no actual reauth happens in this test,
+  // page closes automatically as if user did the reauth).
+  // 3. Reauth page supplies Ash with kTestTrustedVaultKey.
+  display_service.SimulateClick(NotificationHandler::Type::TRANSIENT,
+                                notification_id, /*action_index=*/absl::nullopt,
+                                /*reply=*/absl::nullopt);
+  EXPECT_TRUE(WaitForTrustedVaultReauthCompletion());
+
+  // Now Lacros should be able to fetch keys.
+  EXPECT_TRUE(keys_changed_notified_checker.Wait());
+  EXPECT_THAT(FetchKeysThroughCrosapi(), ElementsAre(kTestTrustedVaultKey));
 }
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/encryption_helper.cc b/chrome/browser/sync/test/integration/encryption_helper.cc
index 0e5bfee..da4a67f 100644
--- a/chrome/browser/sync/test/integration/encryption_helper.cc
+++ b/chrome/browser/sync/test/integration/encryption_helper.cc
@@ -7,12 +7,97 @@
 #include <string>
 #include <vector>
 
+#include "base/base64.h"
 #include "base/functional/bind.h"
+#include "base/strings/stringprintf.h"
 #include "components/sync/base/passphrase_enums.h"
 #include "components/sync/service/sync_client.h"
 #include "components/sync/service/sync_service_impl.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_response.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace encryption_helper {
+
+namespace {
+
+GURL GetFakeTrustedVaultRetrievalURL(
+    const net::test_server::EmbeddedTestServer& test_server,
+    const std::string& gaia_id,
+    const std::vector<uint8_t>& encryption_key,
+    int encryption_key_version) {
+  // encryption_keys_retrieval.html would populate encryption key to
+  // TrustedVaultService service upon loading. Key is provided as part of URL
+  // and needs to be encoded with Base64, because it is binary.
+  const std::string base64_encoded_key = base::Base64Encode(encryption_key);
+  return test_server.GetURL(base::StringPrintf(
+      "/sync/encryption_keys_retrieval.html?gaia=%s&key=%s&key_version=%d",
+      gaia_id.c_str(), base64_encoded_key.c_str(), encryption_key_version));
+}
+
+GURL GetFakeTrustedVaultRecoverabilityURL(
+    const net::test_server::EmbeddedTestServer& test_server,
+    const std::string& gaia_id,
+    const std::vector<uint8_t>& public_key) {
+  // encryption_keys_recoverability.html would populate `public_key` to
+  // TrustedVaultService upon loading. Key is provided as part of URL and needs
+  // to be encoded with Base64, because it is binary.
+  const std::string base64_encoded_public_key = base::Base64Encode(public_key);
+  return test_server.GetURL(
+      base::StringPrintf("/sync/encryption_keys_recoverability.html?%s#%s",
+                         gaia_id.c_str(), base64_encoded_public_key.c_str()));
+}
+
+// Helper function to install server redirects in the test HTTP server.
+std::unique_ptr<net::test_server::HttpResponse> HttpServerRedirect(
+    const GURL& from_prefix,
+    const GURL& to,
+    const net::test_server::HttpRequest& request) {
+  if (!base::StartsWith(request.GetURL().spec(), from_prefix.spec())) {
+    return nullptr;
+  }
+  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
+  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
+  http_response->AddCustomHeader("Location", to.spec());
+  http_response->set_content_type("text/html");
+  http_response->set_content(base::StringPrintf(
+      "<html><head></head><body>Redirecting to %s</body></html>",
+      to.spec().c_str()));
+  return http_response;
+}
+
+}  // namespace
+
+void SetupFakeTrustedVaultPages(
+    const std::string& gaia_id,
+    const std::vector<uint8_t>& trusted_vault_key,
+    int trusted_vault_key_version,
+    const std::vector<uint8_t>& recovery_method_public_key,
+    net::test_server::EmbeddedTestServer* test_server) {
+  CHECK(test_server);
+  // Note that this needs to be installed before the analogous below for
+  // retrieval, because they share prefix.
+  const GURL recoverability_url = GetFakeTrustedVaultRecoverabilityURL(
+      *test_server, gaia_id, recovery_method_public_key);
+  test_server->RegisterRequestHandler(base::BindRepeating(
+      &HttpServerRedirect,
+      /*from_prefix=*/
+      GaiaUrls::GetInstance()
+          ->signin_chrome_sync_keys_recoverability_degraded_url(),
+      /*to=*/recoverability_url));
+
+  const GURL retrieval_url = GetFakeTrustedVaultRetrievalURL(
+      *test_server, gaia_id, trusted_vault_key, trusted_vault_key_version);
+  test_server->RegisterRequestHandler(base::BindRepeating(
+      &HttpServerRedirect,
+      /*from_prefix=*/
+      GaiaUrls::GetInstance()->signin_chrome_sync_keys_retrieval_url(),
+      /*to=*/retrieval_url));
+}
+
+}  // namespace encryption_helper
+
 ServerPassphraseTypeChecker::ServerPassphraseTypeChecker(
     syncer::PassphraseType expected_passphrase_type)
     : expected_passphrase_type_(expected_passphrase_type) {}
diff --git a/chrome/browser/sync/test/integration/encryption_helper.h b/chrome/browser/sync/test/integration/encryption_helper.h
index d31a7c37..c183863b 100644
--- a/chrome/browser/sync/test/integration/encryption_helper.h
+++ b/chrome/browser/sync/test/integration/encryption_helper.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/memory/raw_ptr.h"
 #include "base/test/scoped_feature_list.h"
@@ -16,6 +17,25 @@
 #include "components/sync/base/passphrase_enums.h"
 #include "components/sync/test/fake_server.h"
 #include "components/trusted_vault/trusted_vault_client.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+
+namespace encryption_helper {
+
+// Setups `test_server` in a way it will redirect from trusted vault URLs (key
+// retrieval URL and degraded recoverability URL) to fake pages. These pages
+// will populate parameters (`trusted_vault_key` and its version in case of key
+// retrieval, `recovery_method_public_key` in case of degraded recoverability)
+// to Chrome upon loading and will close themselves automatically.
+// Must be called before `test_server` starts to accept connections.
+// `test_server` must not be null.
+void SetupFakeTrustedVaultPages(
+    const std::string& gaia_id,
+    const std::vector<uint8_t>& trusted_vault_key,
+    int trusted_vault_key_version,
+    const std::vector<uint8_t>& recovery_method_public_key,
+    net::test_server::EmbeddedTestServer* test_server);
+
+}  // namespace encryption_helper
 
 // Checker used to block until a Nigori with a given passphrase type is
 // available on the server.
diff --git a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
index 909f2bf..dbb40d5 100644
--- a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
@@ -131,55 +131,12 @@
   return true;
 }
 
-GURL GetFakeTrustedVaultRetrievalURL(
-    const net::test_server::EmbeddedTestServer& test_server,
-    const std::vector<uint8_t>& encryption_key,
-    int encryption_key_version) {
-  // encryption_keys_retrieval.html would populate encryption key to sync
-  // service upon loading. Key is provided as part of URL and needs to be
-  // encoded with Base64, because |encryption_key| is binary.
-  const std::string base64_encoded_key = base::Base64Encode(encryption_key);
-  return test_server.GetURL(base::StringPrintf(
-      "/sync/encryption_keys_retrieval.html?gaia=%s&key=%s&key_version=%d",
-      kGaiaId, base64_encoded_key.c_str(), encryption_key_version));
-}
-
-GURL GetFakeTrustedVaultRecoverabilityURL(
-    const net::test_server::EmbeddedTestServer& test_server,
-    const std::vector<uint8_t>& public_key) {
-  // encryption_keys_recoverability.html would populate encryption key to sync
-  // service upon loading. Key is provided as part of URL and needs to be
-  // encoded with Base64, because |public_key| is binary.
-  const std::string base64_encoded_public_key = base::Base64Encode(public_key);
-  return test_server.GetURL(
-      base::StringPrintf("/sync/encryption_keys_recoverability.html?%s#%s",
-                         kGaiaId, base64_encoded_public_key.c_str()));
-}
-
 std::string ComputeKeyName(const KeyParamsForTesting& key_params) {
   return syncer::Nigori::CreateByDerivation(key_params.derivation_params,
                                             key_params.password)
       ->GetKeyName();
 }
 
-// Helper function to install server redirects in the test HTTP server.
-std::unique_ptr<net::test_server::HttpResponse> HttpServerRedirect(
-    const GURL& from_prefix,
-    const GURL& to,
-    const net::test_server::HttpRequest& request) {
-  if (!base::StartsWith(request.GetURL().spec(), from_prefix.spec())) {
-    return nullptr;
-  }
-  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
-  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
-  http_response->AddCustomHeader("Location", to.spec());
-  http_response->set_content_type("text/html");
-  http_response->set_content(base::StringPrintf(
-      "<html><head></head><body>Redirecting to %s</body></html>",
-      to.spec().c_str()));
-  return http_response;
-}
-
 class WifiConfigurationsSyncActiveChecker
     : public SingleClientStatusChangeChecker {
  public:
@@ -950,30 +907,9 @@
         &trusted_vault::FakeSecurityDomainsServer::HandleRequest,
         base::Unretained(security_domains_server_.get())));
 
-    // Install a redirect from the actual degraded recoverability URL as
-    // determined by GaiaUrls to |recoverability_url|, which runs Javascript
-    // code to mimic adding recovery method with key
-    // |kTestRecoveryMethodPublicKey|. Note that this needs to be installed
-    // before the analogous below for retrieval, because they share prefix.
-    const GURL recoverability_url = GetFakeTrustedVaultRecoverabilityURL(
-        *embedded_test_server(), kTestRecoveryMethodPublicKey);
-    embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
-        &HttpServerRedirect,
-        /*from_prefix=*/
-        GaiaUrls::GetInstance()
-            ->signin_chrome_sync_keys_recoverability_degraded_url(),
-        /*to=*/recoverability_url));
-
-    // Install a redirect from the actual retrieval URL as determined by
-    // GaiaUrls to |retrieval_url|, which runs Javascript code to mimic
-    // retrieval of key |kTestEncryptionKey|.
-    const GURL retrieval_url = GetFakeTrustedVaultRetrievalURL(
-        *embedded_test_server(), kTestEncryptionKey, kTestEncryptionKeyVersion);
-    embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
-        &HttpServerRedirect,
-        /*from_prefix=*/
-        GaiaUrls::GetInstance()->signin_chrome_sync_keys_retrieval_url(),
-        /*to=*/retrieval_url));
+    encryption_helper::SetupFakeTrustedVaultPages(
+        kGaiaId, kTestEncryptionKey, kTestEncryptionKeyVersion,
+        kTestRecoveryMethodPublicKey, embedded_test_server());
 
     embedded_test_server()->StartAcceptingConnections();
   }
diff --git a/chrome/browser/sync/test/lacros/trusted_vault_keys_sharing_lacros_browsertest.cc b/chrome/browser/sync/test/lacros/trusted_vault_keys_sharing_lacros_browsertest.cc
index 6baef72..6d2f6c86 100644
--- a/chrome/browser/sync/test/lacros/trusted_vault_keys_sharing_lacros_browsertest.cc
+++ b/chrome/browser/sync/test/lacros/trusted_vault_keys_sharing_lacros_browsertest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/base64.h"
 #include "base/files/file_path.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/sync/sync_ui_util.h"
@@ -23,7 +22,6 @@
 #include "content/public/browser/browser_main_parts.h"
 #include "content/public/test/browser_test.h"
 #include "google_apis/gaia/gaia_switches.h"
-#include "google_apis/gaia/gaia_urls.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/views/test/widget_test.h"
@@ -67,51 +65,6 @@
   return true;
 }
 
-GURL GetFakeTrustedVaultRetrievalURL(
-    const net::test_server::EmbeddedTestServer& test_server,
-    const std::string& gaia_id,
-    const std::vector<uint8_t>& encryption_key,
-    int encryption_key_version) {
-  // encryption_keys_retrieval.html would populate encryption key to sync
-  // service upon loading. Key is provided as part of URL and needs to be
-  // encoded with Base64, because `encryption_key` is binary.
-  const std::string base64_encoded_key = base::Base64Encode(encryption_key);
-  return test_server.GetURL(base::StringPrintf(
-      "/sync/encryption_keys_retrieval.html?gaia=%s&key=%s&key_version=%d",
-      gaia_id.c_str(), base64_encoded_key.c_str(), encryption_key_version));
-}
-
-GURL GetFakeTrustedVaultRecoverabilityURL(
-    const net::test_server::EmbeddedTestServer& test_server,
-    const std::string& gaia_id,
-    const std::vector<uint8_t>& public_key) {
-  // encryption_keys_recoverability.html would populate encryption key to sync
-  // service upon loading. Key is provided as part of URL and needs to be
-  // encoded with Base64, because |public_key| is binary.
-  const std::string base64_encoded_public_key = base::Base64Encode(public_key);
-  return test_server.GetURL(
-      base::StringPrintf("/sync/encryption_keys_recoverability.html?%s#%s",
-                         gaia_id.c_str(), base64_encoded_public_key.c_str()));
-}
-
-// Helper function to install server redirects in the test HTTP server.
-std::unique_ptr<net::test_server::HttpResponse> HttpServerRedirect(
-    const GURL& from_prefix,
-    const GURL& to,
-    const net::test_server::HttpRequest& request) {
-  if (!base::StartsWith(request.GetURL().spec(), from_prefix.spec())) {
-    return nullptr;
-  }
-  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
-  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
-  http_response->AddCustomHeader("Location", to.spec());
-  http_response->set_content_type("text/html");
-  http_response->set_content(base::StringPrintf(
-      "<html><head></head><body>Redirecting to %s</body></html>",
-      to.spec().c_str()));
-  return http_response;
-}
-
 class TrustedVaultKeysSharingLacrosBrowserTest : public SyncTest {
  public:
   TrustedVaultKeysSharingLacrosBrowserTest()
@@ -172,32 +125,10 @@
     const CoreAccountInfo primary_account_info =
         GetSyncService(0)->GetAccountInfo();
 
-    // Install a redirect from the actual degraded recoverability URL as
-    // determined by GaiaUrls to |recoverability_url|, which runs Javascript
-    // code to mimic adding recovery method with kTestRecoveryMethodPublicKey.
-    // Note that this needs to be installed before the analogous below for
-    // retrieval, because they share prefix.
-    const GURL recoverability_url = GetFakeTrustedVaultRecoverabilityURL(
-        *embedded_test_server(), primary_account_info.gaia,
-        kTestRecoveryMethodPublicKey);
-    embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
-        &HttpServerRedirect,
-        /*from_prefix=*/
-        GaiaUrls::GetInstance()
-            ->signin_chrome_sync_keys_recoverability_degraded_url(),
-        /*to=*/recoverability_url));
-
-    // Install a redirect from the actual retrieval URL as determined by
-    // GaiaUrls to `retrieval_url`, which runs Javascript code to mimic
-    // retrieval of key kTestTrustedVaultKey.
-    const GURL retrieval_url = GetFakeTrustedVaultRetrievalURL(
-        *embedded_test_server(), primary_account_info.gaia,
-        kTestTrustedVaultKey, /*encryption_key_version=*/1);
-    embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
-        &HttpServerRedirect,
-        /*from_prefix=*/
-        GaiaUrls::GetInstance()->signin_chrome_sync_keys_retrieval_url(),
-        /*to=*/retrieval_url));
+    encryption_helper::SetupFakeTrustedVaultPages(
+        primary_account_info.gaia, kTestTrustedVaultKey,
+        /*trusted_vault_key_version=*/1, kTestRecoveryMethodPublicKey,
+        embedded_test_server());
 
     embedded_test_server()->StartAcceptingConnections();
 
diff --git a/chrome/browser/ui/browser_focus_uitest.cc b/chrome/browser/ui/browser_focus_uitest.cc
index 7ad8814..bb72b05 100644
--- a/chrome/browser/ui/browser_focus_uitest.cc
+++ b/chrome/browser/ui/browser_focus_uitest.cc
@@ -51,6 +51,8 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "third_party/blink/public/common/switches.h"
 #include "ui/base/test/ui_controls.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/widget/widget.h"
 
 #if BUILDFLAG(IS_MAC)
 #include "base/mac/mac_util.h"
@@ -58,6 +60,68 @@
 
 namespace {
 
+constexpr char kGetFocusedElementJS[] = "getFocusedElement();";
+
+// Listens to UI and DOM element focus changes.
+class FocusChangeObserver : public views::FocusChangeListener,
+                            public content::WebContentsObserver {
+ public:
+  FocusChangeObserver(views::FocusManager* focus_manager,
+                      content::WebContents* web_contents)
+      : content::WebContentsObserver(web_contents) {
+    obs_.Observe(focus_manager);
+  }
+
+  void WaitForFocusChange() { run_loop_.Run(); }
+
+  // FocusChangeListener:
+  void OnWillChangeFocus(views::View* focused_before,
+                         views::View* focused_now) override {}
+  void OnDidChangeFocus(views::View* focused_before,
+                        views::View* focused_now) override {
+    if (focused_now) {
+      SCOPED_TRACE(base::StrCat(
+          {"View with ID=", base::NumberToString(focused_now->GetID()),
+           " is focused now."}));
+    }
+    run_loop_.Quit();
+  }
+
+  // WebContentsObserver:
+  void OnFocusChangedInPage(content::FocusedNodeDetails* details) override {
+    SCOPED_TRACE(base::StrCat(
+        {"Page element with id=",
+         content::EvalJs(web_contents(), kGetFocusedElementJS).ExtractString(),
+         " is focused now."}));
+    run_loop_.Quit();
+  }
+
+ private:
+  base::ScopedObservation<views::FocusManager, FocusChangeObserver> obs_{this};
+  base::RunLoop run_loop_;
+};
+
+}  // namespace
+
+namespace base {
+
+template <>
+struct ::base::ScopedObservationTraits<views::FocusManager,
+                                       FocusChangeObserver> {
+  static void AddObserver(views::FocusManager* source,
+                          FocusChangeObserver* observer) {
+    source->AddFocusChangeListener(observer);
+  }
+  static void RemoveObserver(views::FocusManager* source,
+                             FocusChangeObserver* observer) {
+    source->RemoveFocusChangeListener(observer);
+  }
+};
+
+}  // namespace base
+
+namespace {
+
 using content::RenderViewHost;
 using content::WebContents;
 
@@ -90,105 +154,48 @@
 
   void ClickOnView(ViewID vid) { ui_test_utils::ClickOnView(browser(), vid); }
 
-  void TestFocusTraversal(WebContents* tab, bool reverse) {
-    const char kGetFocusedElementJS[] = "getFocusedElement();";
-    const char* kExpectedIDs[] = {"textEdit",   "searchButton", "luckyButton",
-                                  "googleLink", "gmailLink",    "gmapLink"};
-    SCOPED_TRACE(base::StringPrintf("TestFocusTraversal: reverse=%d", reverse));
-    ui::KeyboardCode key = ui::VKEY_TAB;
-#if BUILDFLAG(IS_MAC)
-    // TODO(msw): Mac requires ui::VKEY_BACKTAB for reverse cycling. Sigh...
-    key = reverse ? ui::VKEY_BACKTAB : ui::VKEY_TAB;
-#endif
-
-    // Loop through the focus chain twice for good measure.
-    for (size_t i = 0; i < 2; ++i) {
-      SCOPED_TRACE(base::StringPrintf("focus outer loop: %" PRIuS, i));
-      ASSERT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
-
-      // Mac requires an extra Tab key press to traverse the app menu button
-      // iff "Full Keyboard Access" is enabled. In reverse, four Tab key presses
-      // are required to traverse the back/forward buttons and the tab strip.
-#if BUILDFLAG(IS_MAC)
-      constexpr int kFocusableElementsBeforeOmnibox = 4;
-      constexpr int kFocusableElementsAfterOmnibox = 1;
-      if (ui_controls::IsFullKeyboardAccessEnabled()) {
-        for (int j = 0; j < (reverse ? kFocusableElementsBeforeOmnibox
-                                     : kFocusableElementsAfterOmnibox);
-             ++j) {
-          ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
-                                                      reverse, false, false));
-        }
-      }
-#endif
-
-      if (reverse) {
-        ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
-                                                    reverse, false, false));
-      }
-
-      // From the location icon we must traverse backwards one more time to
-      // traverse past the tab search caption button if present.
-      if (WindowFrameUtil::IsWindowsTabSearchCaptionButtonEnabled(browser()) &&
-          reverse) {
-        ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false, true,
-                                                    false, false));
-      }
-
-      for (size_t j = 0; j < std::size(kExpectedIDs); ++j) {
-        SCOPED_TRACE(base::StringPrintf("focus inner loop %" PRIuS, j));
-        const size_t index = reverse ? std::size(kExpectedIDs) - 1 - j : j;
-        // The details are the node's editable state, i.e. true for "textEdit".
-        bool is_editable_node = index == 0;
-
-        // Press Tab (or Shift+Tab) and check the focused element id.
-        content::FocusChangedObserver observer(tab);
-        ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
-                                                    reverse, false, false));
-        auto observed_details = observer.Wait();
-        EXPECT_EQ(is_editable_node, observed_details.is_editable_node);
-
-        EXPECT_EQ(kExpectedIDs[index],
-                  content::EvalJs(tab, kGetFocusedElementJS));
-      }
-
-      // On the last Tab key press, focus returns to the browser.
-      ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
-                                                  reverse, false, false));
-
-      // Except on Mac, where extra tabs are once again required to traverse the
-      // other top chrome elements.
-#if BUILDFLAG(IS_MAC)
-      if (ui_controls::IsFullKeyboardAccessEnabled()) {
-        for (int j = 0; j < (reverse ? kFocusableElementsAfterOmnibox
-                                     : kFocusableElementsBeforeOmnibox);
-             ++j) {
-          ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
-                                                      reverse, false, false));
-        }
-      }
-#endif
-
-      // Traverse over the tab search frame caption button if present.
-      if (WindowFrameUtil::IsWindowsTabSearchCaptionButtonEnabled(browser()) &&
-          !reverse) {
-        ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
-                                                    false, false, false));
-      }
-
-      ui_test_utils::WaitForViewFocus(
-          browser(), reverse ? VIEW_ID_OMNIBOX : VIEW_ID_LOCATION_ICON, true);
-
-      ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
-                                                  reverse, false, false));
-      ui_test_utils::WaitForViewFocus(
-          browser(), reverse ? VIEW_ID_LOCATION_ICON : VIEW_ID_OMNIBOX, true);
-      if (reverse) {
-        ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false,
-                                                    false, false, false));
-      }
-    }
+  void FocusNextElement(bool reverse) {
+    FocusChangeObserver obs{
+        GetFocusManager(),
+        browser()->tab_strip_model()->GetActiveWebContents()};
+    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_TAB, false,
+                                                reverse, false, false));
+    obs.WaitForFocusChange();
   }
+
+  void TestFocusTraversal(bool reverse) {
+    SCOPED_TRACE(base::StrCat(
+        {"Started focus traversal, reverse=", base::ToString(reverse)}));
+
+    // Move focus one element away from the omnibox.
+    ASSERT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
+    FocusNextElement(reverse);
+    EXPECT_FALSE(IsViewFocused(VIEW_ID_OMNIBOX));
+
+    // Traverse the whole focus chain until the omnibox is focused again.
+    size_t c = 0;
+    while (!IsViewFocused(VIEW_ID_OMNIBOX) && c < kMaxIterations) {
+      FocusNextElement(reverse);
+      ++c;
+    }
+    EXPECT_TRUE(c <= kMaxIterations);
+    EXPECT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
+  }
+
+  views::FocusManager* GetFocusManager() {
+    BrowserWindow* browser_window = browser()->window();
+    DCHECK(browser_window);
+    gfx::NativeWindow window = browser_window->GetNativeWindow();
+    DCHECK(window);
+    views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
+    DCHECK(widget);
+    views::FocusManager* focus_manager = widget->GetFocusManager();
+    DCHECK(focus_manager);
+    return focus_manager;
+  }
+
+ private:
+  constexpr static size_t kMaxIterations = 20;
 };
 
 // Flaky on Mac (http://crbug.com/67301).
@@ -420,17 +427,20 @@
 }
 
 // Test forward and reverse focus traversal on a typical page.
-// Flaky everywhere: https://crbug.com/1259721
-IN_PROC_BROWSER_TEST_F(BrowserFocusTest, DISABLED_FocusTraversal) {
+IN_PROC_BROWSER_TEST_F(BrowserFocusTest, FocusTraversal) {
   ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
   const GURL url = embedded_test_server()->GetURL(kTypicalPage);
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-  EXPECT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER));
+  FocusChangeObserver obs{GetFocusManager(),
+                          browser()->tab_strip_model()->GetActiveWebContents()};
   chrome::FocusLocationBar(browser());
-
-  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_NO_FATAL_FAILURE(TestFocusTraversal(tab, false));
-  EXPECT_NO_FATAL_FAILURE(TestFocusTraversal(tab, true));
+  obs.WaitForFocusChange();
+  ASSERT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
+  // Loop through the focus chain twice in each direction for good measure.
+  TestFocusTraversal(false);
+  TestFocusTraversal(false);
+  TestFocusTraversal(true);
+  TestFocusTraversal(true);
 }
 
 // Test that find-in-page UI can request focus, even when it is already open.
diff --git a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_lacros.cc b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_lacros.cc
index c676bb1..0e17f89 100644
--- a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_lacros.cc
+++ b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_lacros.cc
@@ -236,15 +236,11 @@
   UpdateFrameHints();
 }
 
-void BrowserDesktopWindowTreeHostLacros::OnImmersiveModeChanged(bool enabled) {
-  DesktopWindowTreeHostLacros::OnImmersiveModeChanged(enabled);
-  // Update the browser UI, because some fullscreen mode UI updates depend on
-  // immersive mode state. Unlike ash-chrome, Lacros's immersive mode is set
-  // to the system asynchronously.
-  browser_view_->browser()->FullscreenTopUIStateChanged();
-}
+void BrowserDesktopWindowTreeHostLacros::OnFullscreenTypeChanged(
+    ui::PlatformFullscreenType old_type,
+    ui::PlatformFullscreenType new_type) {
+  DesktopWindowTreeHostLacros::OnFullscreenTypeChanged(old_type, new_type);
 
-void BrowserDesktopWindowTreeHostLacros::OnFullscreenModeChanged() {
   // Finalizing full screen mode transition after Ash has also asynchronously
   // entered the full screen mode state for this window.
   browser_view_->FullscreenStateChanged();
diff --git a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_lacros.h b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_lacros.h
index 7c2b9892..de8d94b3 100644
--- a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_lacros.h
+++ b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_lacros.h
@@ -59,8 +59,8 @@
   void OnBoundsChanged(const BoundsChange& change) override;
   void OnWindowStateChanged(ui::PlatformWindowState old_state,
                             ui::PlatformWindowState new_state) override;
-  void OnImmersiveModeChanged(bool enabled) override;
-  void OnFullscreenModeChanged() override;
+  void OnFullscreenTypeChanged(ui::PlatformFullscreenType old_type,
+                               ui::PlatformFullscreenType new_type) override;
   void OnOverviewModeChanged(bool in_overview) override;
 
   const raw_ptr<BrowserView> browser_view_;
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 2af30a6..536ff3d93 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -2589,6 +2589,8 @@
 }
 
 void BrowserView::SynchronizeRenderWidgetHostVisualPropertiesForMainFrame() {
+// TODO(crbug.com/1503145): Investigate and fix on MacOS.
+#if !BUILDFLAG(IS_MAC)
   content::WebContents* web_contents = GetActiveWebContents();
   if (!web_contents || !web_contents->GetPrimaryMainFrame()) {
     return;
@@ -2598,6 +2600,7 @@
           web_contents->GetPrimaryMainFrame()->GetRenderWidgetHost()) {
     render_widget_host->SynchronizeVisualProperties();
   }
+#endif
 }
 
 void BrowserView::OnWidgetSizeConstraintsChanged(views::Widget* widget) {
diff --git a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
index 917fec8..ab401b5 100644
--- a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
+++ b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
@@ -165,6 +165,24 @@
         views::style::CONTEXT_DIALOG_BODY_TEXT, views::style::STYLE_PRIMARY));
   }
 
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
+    if (!controller_) {
+      return;
+    }
+
+    node_data->role = ax::mojom::Role::kListBoxOption;
+    node_data->AddBoolAttribute(ax::mojom::BoolAttribute::kSelected,
+                                controller_->edit_password_selected());
+    node_data->SetNameChecked(
+        l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_EDIT_PASSWORD));
+    const std::u16string help_text = l10n_util::GetStringFUTF16(
+        GetHelpTextMessageId(),
+        l10n_util::GetStringUTF16(
+            IDS_PASSWORD_BUBBLES_PASSWORD_MANAGER_LINK_TEXT_SYNCED_TO_ACCOUNT),
+        controller_->GetPrimaryAccountEmail());
+    node_data->SetDescription(help_text);
+  }
+
  private:
   void OnMouseEntered(const ui::MouseEvent& event) override {
     if (controller_) {
@@ -314,11 +332,24 @@
     node_data->SetNameChecked(base::JoinString(
         {controller_->SuggestedText(), controller_->password()}, u" "));
     const std::u16string help_text = l10n_util::GetStringFUTF16(
-        IDS_PASSWORD_GENERATION_PROMPT_GOOGLE_PASSWORD_MANAGER,
+        GetHelpTextMessageId(),
         l10n_util::GetStringUTF16(
             IDS_PASSWORD_BUBBLES_PASSWORD_MANAGER_LINK_TEXT_SYNCED_TO_ACCOUNT),
         controller_->GetPrimaryAccountEmail());
 
+    if (password_manager::features::kPasswordGenerationExperimentVariationParam
+            .Get() == PasswordGenerationVariation::kCrossDevice) {
+      const std::u16string description = base::JoinString(
+          {l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_BENEFITS),
+           l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_CROSS_DEVICE),
+           l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_SECURITY),
+           l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_PROACTIVE_CHECK),
+           help_text},
+          u", ");
+      node_data->SetDescription(description);
+      return;
+    }
+
     node_data->SetDescription(help_text);
   }
 
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.cc b/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.cc
index cd048ad4..5d10b46 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.cc
+++ b/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.cc
@@ -51,17 +51,12 @@
     Browser* browser,
     base::WeakPtr<permissions::PermissionPrompt::Delegate> delegate,
     base::TimeTicks permission_requested_time,
-    PermissionPromptStyle prompt_style,
-    std::u16string window_title,
-    std::u16string accessible_window_title,
-    std::optional<std::u16string> extra_text)
+    PermissionPromptStyle prompt_style)
     : PermissionPromptBaseView(browser, delegate),
       browser_(browser),
       delegate_(delegate),
       permission_requested_time_(permission_requested_time),
-      is_one_time_permission_(IsOneTimePermission(*delegate.get())),
-      accessible_window_title_(accessible_window_title),
-      window_title_(window_title) {
+      is_one_time_permission_(IsOneTimePermission(*delegate.get())) {
   // Note that browser_ may be null in unit tests.
   SetPromptStyle(prompt_style);
 
@@ -73,18 +68,12 @@
   set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
       views::DISTANCE_BUBBLE_PREFERRED_WIDTH));
 
-  if (extra_text.has_value()) {
-    auto* extra_text_label =
-        AddChildView(std::make_unique<views::Label>(extra_text.value()));
-    extra_text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-    extra_text_label->SetMultiLine(true);
-    extra_text_label->SetID(permissions::PermissionPromptViewID::
-                                VIEW_ID_PERMISSION_PROMPT_EXTRA_TEXT);
-    if (features::IsChromeRefresh2023()) {
-      extra_text_label->SetTextStyle(views::style::STYLE_BODY_3);
-      extra_text_label->SetEnabledColorId(kColorPermissionPromptRequestText);
-    }
-  }
+  SetProperty(views::kElementIdentifierKey, kMainViewId);
+}
+
+PermissionPromptBubbleBaseView::~PermissionPromptBubbleBaseView() = default;
+
+void PermissionPromptBubbleBaseView::CreatePermissionButtons() {
   if (is_one_time_permission_) {
     SetButtons(ui::DIALOG_BUTTON_NONE);
 
@@ -161,11 +150,23 @@
       SetButtonStyle(ui::DIALOG_BUTTON_CANCEL, ui::ButtonStyle::kTonal);
     }
   }
-
-  SetProperty(views::kElementIdentifierKey, kMainViewId);
 }
 
-PermissionPromptBubbleBaseView::~PermissionPromptBubbleBaseView() = default;
+void PermissionPromptBubbleBaseView::CreateExtraTextLabel(
+    const std::u16string& extra_text) {
+  auto extra_text_label = views::Builder<views::Label>()
+                              .SetText(extra_text)
+                              .SetHorizontalAlignment(gfx::ALIGN_LEFT)
+                              .SetMultiLine(true)
+                              .SetID(permissions::PermissionPromptViewID::
+                                         VIEW_ID_PERMISSION_PROMPT_EXTRA_TEXT)
+                              .Build();
+  if (features::IsChromeRefresh2023()) {
+    extra_text_label->SetTextStyle(views::style::STYLE_BODY_3);
+    extra_text_label->SetEnabledColorId(kColorPermissionPromptRequestText);
+  }
+  AddChildView(std::move(extra_text_label));
+}
 
 void PermissionPromptBubbleBaseView::Show() {
   CreateWidget();
@@ -244,15 +245,6 @@
   return true;
 }
 
-std::u16string PermissionPromptBubbleBaseView::GetWindowTitle() const {
-  return window_title_;
-}
-
-std::u16string PermissionPromptBubbleBaseView::GetAccessibleWindowTitle()
-    const {
-  return accessible_window_title_;
-}
-
 void PermissionPromptBubbleBaseView::ClosingPermission() {
   DCHECK_EQ(prompt_style_, PermissionPromptStyle::kBubbleOnly);
   RecordDecision(permissions::PermissionAction::DISMISSED);
@@ -282,10 +274,9 @@
 
 std::u16string PermissionPromptBubbleBaseView::GetPermissionFragmentForTesting()
     const {
-  std::u16string origin =
-      PermissionPromptBaseView::GetUrlIdentity(browser_, *delegate_).name;
-  return accessible_window_title_.substr(accessible_window_title_.find(origin) +
-                                         origin.length());
+  std::u16string origin = GetUrlIdentityObject().name;
+  return GetAccessibleWindowTitle().substr(
+      GetAccessibleWindowTitle().find(origin) + origin.length());
 }
 
 // static
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.h b/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.h
index e0a8bf5..f562d38d 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.h
+++ b/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.h
@@ -48,10 +48,7 @@
       Browser* browser,
       base::WeakPtr<permissions::PermissionPrompt::Delegate> delegate,
       base::TimeTicks permission_requested_time,
-      PermissionPromptStyle prompt_style,
-      std::u16string window_title,
-      std::u16string accessible_window_title_,
-      std::optional<std::u16string> extra_text);
+      PermissionPromptStyle prompt_style);
   PermissionPromptBubbleBaseView(const PermissionPromptBubbleBaseView&) =
       delete;
   PermissionPromptBubbleBaseView& operator=(
@@ -74,14 +71,10 @@
 
   void ShowWidget();
 
-  void SetPromptStyle(PermissionPromptStyle prompt_style);
-
   void ClosingPermission();
 
   // views::BubbleDialogDelegateView:
   bool ShouldShowCloseButton() const override;
-  std::u16string GetAccessibleWindowTitle() const override;
-  std::u16string GetWindowTitle() const override;
 
   // PermissionPromptBaseView:
   void RunButtonCallback(int button_id) override;
@@ -89,9 +82,12 @@
   std::u16string GetPermissionFragmentForTesting() const;
 
  protected:
+  void CreatePermissionButtons();
+  void CreateExtraTextLabel(const std::u16string& extra_text);
+
   void CreateWidget();
 
-  base::WeakPtr<permissions::PermissionPrompt::Delegate> GetDelegate() {
+  base::WeakPtr<permissions::PermissionPrompt::Delegate> delegate() const {
     return delegate_;
   }
 
@@ -102,6 +98,8 @@
       permissions::PermissionPrompt::Delegate& delegate);
 
  private:
+  void SetPromptStyle(PermissionPromptStyle prompt_style);
+
   // Record UMA Permissions.*.TimeToDecision.|action| metric. Can be
   // Permissions.Prompt.TimeToDecision.* or Permissions.Chip.TimeToDecision.*,
   // depending on which UI is used.
@@ -124,8 +122,6 @@
   PermissionPromptStyle prompt_style_;
 
   const bool is_one_time_permission_;
-  const std::u16string accessible_window_title_;
-  const std::u16string window_title_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_PERMISSIONS_PERMISSION_PROMPT_BUBBLE_BASE_VIEW_H_
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_bubble_one_origin_view.cc b/chrome/browser/ui/views/permissions/permission_prompt_bubble_one_origin_view.cc
index 92d3b9d2..c84a271 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_bubble_one_origin_view.cc
+++ b/chrome/browser/ui/views/permissions/permission_prompt_bubble_one_origin_view.cc
@@ -155,23 +155,27 @@
     base::WeakPtr<permissions::PermissionPrompt::Delegate> delegate,
     base::TimeTicks permission_requested_time,
     PermissionPromptStyle prompt_style)
-    : PermissionPromptBubbleBaseView(
-          browser,
-          delegate,
-          permission_requested_time,
-          prompt_style,
-          l10n_util::GetStringFUTF16(
-              IDS_PERMISSIONS_BUBBLE_PROMPT,
-              PermissionPromptBaseView::GetUrlIdentity(browser, *delegate)
-                  .name),
-          GetAccessibleWindowTitleInternal(
-              PermissionPromptBaseView::GetUrlIdentity(browser, *delegate).name,
-              GetVisibleRequests(*delegate.get())),
-          GetExtraText(*delegate.get())) {
-  bool has_camera_request = false;
-  bool has_mic_request = false;
+    : PermissionPromptBubbleBaseView(browser,
+                                     delegate,
+                                     permission_requested_time,
+                                     prompt_style) {
   std::vector<permissions::PermissionRequest*> visible_requests =
       GetVisibleRequests(*delegate.get());
+
+  SetAccessibleTitle(GetAccessibleWindowTitleInternal(
+      GetUrlIdentityObject().name, visible_requests));
+  SetTitle(l10n_util::GetStringFUTF16(IDS_PERMISSIONS_BUBBLE_PROMPT,
+                                      GetUrlIdentityObject().name));
+
+  auto extra_text = GetExtraText(*delegate.get());
+  if (extra_text.has_value()) {
+    CreateExtraTextLabel(extra_text.value());
+  }
+
+  CreatePermissionButtons();
+
+  bool has_camera_request = false;
+  bool has_mic_request = false;
   for (std::size_t i = 0; i < visible_requests.size(); i++) {
     AddRequestLine(visible_requests[i], i);
     if (visible_requests[i]->request_type() ==
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_bubble_two_origins_view.cc b/chrome/browser/ui/views/permissions/permission_prompt_bubble_two_origins_view.cc
index 3d0efc2..a53eeb9 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_bubble_two_origins_view.cc
+++ b/chrome/browser/ui/views/permissions/permission_prompt_bubble_two_origins_view.cc
@@ -24,20 +24,6 @@
 // so we can adjust this delay accordingly.
 constexpr int kMaxShowDelayMs = 200;
 
-std::u16string GetWindowTitleTwoOrigin(
-    permissions::PermissionPrompt::Delegate& delegate) {
-  CHECK_GT(delegate.Requests().size(), 0u);
-  switch (delegate.Requests()[0]->request_type()) {
-    case permissions::RequestType::kStorageAccess:
-      return l10n_util::GetStringFUTF16(
-          IDS_STORAGE_ACCESS_PERMISSION_TWO_ORIGIN_PROMPT_TITLE,
-          url_formatter::FormatUrlForSecurityDisplay(
-              delegate.GetRequestingOrigin(),
-              url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
-    default:
-      NOTREACHED_NORETURN();
-  }
-}
 
 std::optional<std::u16string> GetExtraTextTwoOrigin(
     permissions::PermissionPrompt::Delegate& delegate) {
@@ -73,10 +59,16 @@
     : PermissionPromptBubbleBaseView(browser,
                                      delegate,
                                      permission_requested_time,
-                                     prompt_style,
-                                     GetWindowTitleTwoOrigin(*delegate),
-                                     GetWindowTitleTwoOrigin(*delegate),
-                                     GetExtraTextTwoOrigin(*delegate)) {
+                                     prompt_style) {
+  SetTitle(CreateWindowTitle());
+
+  auto extra_text = GetExtraTextTwoOrigin(*delegate);
+  if (extra_text.has_value()) {
+    CreateExtraTextLabel(extra_text.value());
+  }
+
+  CreatePermissionButtons();
+
   // Only requests for Storage Access should use this prompt.
   CHECK(delegate);
   CHECK_GT(delegate->Requests().size(), 0u);
@@ -162,6 +154,21 @@
                                    base::Unretained(this)));
 }
 
+std::u16string PermissionPromptBubbleTwoOriginsView::CreateWindowTitle() const {
+  CHECK_GT(delegate()->Requests().size(), 0u);
+
+  switch (delegate()->Requests()[0]->request_type()) {
+    case permissions::RequestType::kStorageAccess:
+      return l10n_util::GetStringFUTF16(
+          IDS_STORAGE_ACCESS_PERMISSION_TWO_ORIGIN_PROMPT_TITLE,
+          url_formatter::FormatUrlForSecurityDisplay(
+              delegate()->GetRequestingOrigin(),
+              url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
+    default:
+      NOTREACHED_NORETURN();
+  }
+}
+
 void PermissionPromptBubbleTwoOriginsView::CreateFaviconRow() {
   // Getting default favicon.
   ui::ImageModel default_favicon_ = ui::ImageModel::FromVectorIcon(
@@ -224,7 +231,7 @@
   views::StyledLabel::RangeStyleInfo link_style;
   std::optional<std::u16string> link = GetLink(link_range, link_style);
   if (link.has_value()) {
-    size_t index = HasExtraText(*GetDelegate()) ? 1 : 0;
+    size_t index = HasExtraText(*delegate()) ? 1 : 0;
     auto* link_label =
         AddChildViewAt(std::make_unique<views::StyledLabel>(), index);
     link_label->SetText(link.value());
@@ -239,9 +246,8 @@
 std::optional<std::u16string> PermissionPromptBubbleTwoOriginsView::GetLink(
     gfx::Range& link_range,
     views::StyledLabel::RangeStyleInfo& link_style) {
-  auto delegate = GetDelegate();
-  CHECK_GT(delegate->Requests().size(), 0u);
-  switch (delegate->Requests()[0]->request_type()) {
+  CHECK_GT(delegate()->Requests().size(), 0u);
+  switch (delegate()->Requests()[0]->request_type()) {
     case permissions::RequestType::kStorageAccess:
       return GetLinkStorageAccess(link_range, link_style);
     default:
@@ -266,8 +272,8 @@
 
 void PermissionPromptBubbleTwoOriginsView::HelpCenterLinkClicked(
     const ui::Event& event) {
-  if (auto delegate = GetDelegate()) {
-    delegate->OpenHelpCenterLink(event);
+  if (delegate()) {
+    delegate()->OpenHelpCenterLink(event);
   }
 }
 
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_bubble_two_origins_view.h b/chrome/browser/ui/views/permissions/permission_prompt_bubble_two_origins_view.h
index f6215b0..b45b3e7 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_bubble_two_origins_view.h
+++ b/chrome/browser/ui/views/permissions/permission_prompt_bubble_two_origins_view.h
@@ -48,6 +48,7 @@
 
  private:
   void CreateFaviconRow();
+  std::u16string CreateWindowTitle() const;
 
   void OnEmbeddingOriginFaviconLoaded(
       const favicon_base::FaviconRawBitmapResult& favicon_result);
diff --git a/chrome/browser/ui/webui/ash/app_install/app_install_page_handler.cc b/chrome/browser/ui/webui/ash/app_install/app_install_page_handler.cc
index 789ec724..61a2390e 100644
--- a/chrome/browser/ui/webui/ash/app_install/app_install_page_handler.cc
+++ b/chrome/browser/ui/webui/ash/app_install/app_install_page_handler.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/webui/ash/app_install/app_install_page_handler.h"
 
+#include "base/metrics/user_metrics.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ui/webui/ash/app_install/app_install.mojom.h"
@@ -21,7 +22,10 @@
       dialog_args_{std::move(args)},
       dialog_accepted_callback_{std::move(dialog_accepted_callback)},
       receiver_{this, std::move(pending_page_handler)},
-      close_dialog_callback_{std::move(close_dialog_callback)} {}
+      close_dialog_callback_{std::move(close_dialog_callback)} {
+  base::RecordAction(
+      base::UserMetricsAction("ChromeOS.AppInstallDialog.Shown"));
+}
 
 AppInstallPageHandler::~AppInstallPageHandler() = default;
 
@@ -32,8 +36,11 @@
 
 void AppInstallPageHandler::CloseDialog() {
   if (dialog_accepted_callback_) {
+    base::RecordAction(
+        base::UserMetricsAction("ChromeOS.AppInstallDialog.Cancelled"));
     std::move(dialog_accepted_callback_).Run(false);
   }
+
   // The callback could be null if the close button is clicked a second time
   // before the dialog closes.
   if (close_dialog_callback_) {
@@ -42,6 +49,9 @@
 }
 
 void AppInstallPageHandler::InstallApp(InstallAppCallback callback) {
+  base::RecordAction(
+      base::UserMetricsAction("ChromeOS.AppInstallDialog.Installed"));
+
   install_app_callback_ = std::move(callback);
   std::move(dialog_accepted_callback_).Run(true);
 }
@@ -50,6 +60,7 @@
   if (app_id) {
     app_id_ = *app_id;
   }
+
   if (install_app_callback_) {
     std::move(install_app_callback_).Run(/*success=*/app_id);
   }
@@ -60,6 +71,8 @@
     mojo::ReportBadMessage("Unable to launch app without an app_id.");
     return;
   }
+  base::RecordAction(
+      base::UserMetricsAction("ChromeOS.AppInstallDialog.AppLaunched"));
   apps::AppServiceProxyFactory::GetForProfile(profile_)->Launch(
       app_id_, ui::EF_NONE, apps::LaunchSource::kFromInstaller);
 }
diff --git a/chrome/browser/ui/webui/ash/login/oobe_ui.cc b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
index 33bfef2..4d37212d 100644
--- a/chrome/browser/ui/webui/ash/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
@@ -484,10 +484,10 @@
   AddScreenHandler(std::make_unique<FingerprintSetupScreenHandler>());
 
   if (features::AreLocalPasswordsEnabledForConsumers()) {
-    AddScreenHandler(std::make_unique<PasswordSelectionScreenHandler>());
     AddScreenHandler(std::make_unique<LocalPasswordSetupHandler>());
-    AddScreenHandler(std::make_unique<ApplyOnlinePasswordScreenHandler>());
   }
+  AddScreenHandler(std::make_unique<PasswordSelectionScreenHandler>());
+  AddScreenHandler(std::make_unique<ApplyOnlinePasswordScreenHandler>());
 
   AddScreenHandler(std::make_unique<LocalDataLossWarningScreenHandler>());
   AddScreenHandler(std::make_unique<EnterOldPasswordScreenHandler>());
diff --git a/chrome/browser/web_applications/app_service/browser_shortcuts_unittest.cc b/chrome/browser/web_applications/app_service/browser_shortcuts_unittest.cc
index bdfb29c..c6bd2f2e 100644
--- a/chrome/browser/web_applications/app_service/browser_shortcuts_unittest.cc
+++ b/chrome/browser/web_applications/app_service/browser_shortcuts_unittest.cc
@@ -33,8 +33,10 @@
 #include "ui/display/types/display_constants.h"
 
 namespace {
+
+using webapps::AppId;
+
 const char kUrl[] = "https://example.com/";
-const char kIconUrl[] = "https://example.com/icon";
 }
 
 namespace web_app {
@@ -50,33 +52,13 @@
     test::AwaitStartWebAppProviderAndSubsystems(profile());
   }
 
-  std::string CreateShortcut(const std::string& shortcut_name,
-                             bool with_icon = false) {
-    const GURL kAppUrl(kUrl);
-
-    // Create a web app entry without scope, which would be recognised
-    // as ShortcutApp in the web app system.
-    auto web_app_info = std::make_unique<WebAppInstallInfo>();
-    web_app_info->title = base::UTF8ToUTF16(shortcut_name);
-    web_app_info->start_url = kAppUrl;
-
-    if (with_icon) {
-      const GeneratedIconsInfo icon_info(
-          IconPurpose::ANY, {web_app::icon_size::k32}, {SK_ColorBLACK});
-      web_app::AddIconsToWebAppInstallInfo(web_app_info.get(), GURL(kIconUrl),
-                                           {icon_info});
-    }
-
-    std::string app_id =
-        test::InstallWebApp(profile(), std::move(web_app_info),
-                            /*overwrite_existing_manifest_fields=*/true);
-    CHECK(
-        WebAppProvider::GetForTest(profile())->registrar_unsafe().IsShortcutApp(
-            app_id));
-    return app_id;
+  AppId CreateShortcut(const std::string& shortcut_name,
+                       bool with_icon = true) {
+    return test::InstallShortcut(profile(), shortcut_name, GURL(kUrl),
+                                 with_icon);
   }
 
-  std::string CreateWebApp(const std::string& app_name) {
+  AppId CreateWebApp(const std::string& app_name) {
     const GURL kAppUrl(kUrl);
 
     // Create a web app entry with scope, which would be recognised
@@ -143,7 +125,11 @@
 TEST_F(BrowserShortcutsTest, PublishExistingBrowserShortcut) {
   const std::string kShortcutName = "Shortcut";
 
-  auto local_shortcut_id = CreateShortcut(kShortcutName);
+  // Does not create a default icon with this installation so that we can
+  // verify the icon effect is set to be correct.
+  // TODO(b/315263875): Add a specific test to test the different icon
+  // effect to make this clearer.
+  AppId local_shortcut_id = CreateShortcut(kShortcutName, /*with_icon =*/false);
   apps::ShortcutId expected_shortcut_id =
       apps::GenerateShortcutId(app_constants::kChromeAppId, local_shortcut_id);
 
@@ -176,7 +162,7 @@
 }
 
 TEST_F(BrowserShortcutsTest, WebAppNotPublishedAsShortcut) {
-  auto app_id = CreateWebApp("App");
+  CreateWebApp("App");
 
   InitializeBrowserShortcutPublisher();
 
@@ -185,7 +171,7 @@
           ->ShortcutRegistryCache();
   EXPECT_EQ(cache->GetAllShortcuts().size(), 0u);
 
-  auto new_app_id = CreateWebApp("NewApp");
+  CreateWebApp("NewApp");
   EXPECT_EQ(cache->GetAllShortcuts().size(), 0u);
 }
 
@@ -198,7 +184,7 @@
 
   const std::string kShortcutName = "Shortcut";
 
-  auto local_shortcut_id = CreateShortcut(kShortcutName, /*with_icon = */ true);
+  AppId local_shortcut_id = CreateShortcut(kShortcutName);
   apps::ShortcutId expected_shortcut_id =
       apps::GenerateShortcutId(app_constants::kChromeAppId, local_shortcut_id);
 
@@ -228,7 +214,7 @@
 TEST_F(BrowserShortcutsTest, LaunchShortcut) {
   const std::string kShortcutName = "Shortcut";
 
-  auto local_shortcut_id = CreateShortcut(kShortcutName);
+  AppId local_shortcut_id = CreateShortcut(kShortcutName);
   apps::ShortcutId expected_shortcut_id =
       apps::GenerateShortcutId(app_constants::kChromeAppId, local_shortcut_id);
   InitializeBrowserShortcutPublisher();
@@ -276,7 +262,7 @@
 
   const std::string kShortcutName = "Shortcut";
 
-  auto local_shortcut_id = CreateShortcut(kShortcutName);
+  AppId local_shortcut_id = CreateShortcut(kShortcutName);
   apps::ShortcutId expected_shortcut_id =
       apps::GenerateShortcutId(app_constants::kChromeAppId, local_shortcut_id);
 
@@ -292,7 +278,7 @@
 TEST_F(BrowserShortcutsTest, RemoveShortcut) {
   const std::string kShortcutName = "Shortcut";
 
-  auto local_shortcut_id = CreateShortcut(kShortcutName);
+  AppId local_shortcut_id = CreateShortcut(kShortcutName);
   apps::ShortcutId shortcut_id =
       apps::GenerateShortcutId(app_constants::kChromeAppId, local_shortcut_id);
   InitializeBrowserShortcutPublisher();
@@ -319,7 +305,7 @@
 TEST_F(BrowserShortcutsTest, GetCompressedShortcutIcon) {
   const std::string kShortcutName = "Shortcut";
 
-  auto local_shortcut_id = CreateShortcut(kShortcutName);
+  AppId local_shortcut_id = CreateShortcut(kShortcutName);
   apps::ShortcutId shortcut_id =
       apps::GenerateShortcutId(app_constants::kChromeAppId, local_shortcut_id);
   InitializeBrowserShortcutPublisher();
@@ -372,7 +358,7 @@
           ->AppRegistryCache();
   ASSERT_EQ(shortcut_cache->GetAllShortcuts().size(), 0u);
 
-  auto web_app_id = CreateShortcut("Shortcut");
+  AppId web_app_id = CreateShortcut("Shortcut");
   EXPECT_TRUE(shortcut_cache->HasShortcut(apps::ShortcutId(web_app_id)));
   EXPECT_FALSE(app_cache.IsAppInstalled(web_app_id));
 
diff --git a/chrome/browser/web_applications/app_service/lacros_browser_shortcuts_controller_unittest.cc b/chrome/browser/web_applications/app_service/lacros_browser_shortcuts_controller_unittest.cc
index 0126090..fc6d6cea 100644
--- a/chrome/browser/web_applications/app_service/lacros_browser_shortcuts_controller_unittest.cc
+++ b/chrome/browser/web_applications/app_service/lacros_browser_shortcuts_controller_unittest.cc
@@ -32,6 +32,10 @@
 #include "ui/display/types/display_constants.h"
 #include "url/gurl.h"
 
+namespace {
+using webapps::AppId;
+}
+
 namespace web_app {
 
 class FakeShortcutPublisher : public crosapi::mojom::AppShortcutPublisher {
@@ -93,25 +97,11 @@
     test::AwaitStartWebAppProviderAndSubsystems(profile());
   }
 
-  std::string CreateWebAppBasedShortcut(const GURL& shortcut_url,
-                                        const std::u16string& shortcut_name,
-                                        bool with_icon = false) {
-    // Create web app based shortcut.
-    auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
-    web_app_info->start_url = shortcut_url;
-    web_app_info->title = shortcut_name;
-
-    if (with_icon) {
-      const GeneratedIconsInfo icon_info(
-          IconPurpose::ANY, {web_app::icon_size::k32}, {SK_ColorBLACK});
-      web_app::AddIconsToWebAppInstallInfo(
-          web_app_info.get(), GURL(shortcut_url.spec() + "/icon"), {icon_info});
-    }
-
-    auto local_shortcut_id = web_app::test::InstallWebApp(
-        profile(), std::move(web_app_info),
-        /*overwrite_existing_manifest_fields=*/true);
-    return local_shortcut_id;
+  AppId CreateWebAppBasedShortcut(const GURL& shortcut_url,
+                                  const std::string& shortcut_name,
+                                  bool with_icon = true) {
+    return test::InstallShortcut(profile(), shortcut_name, shortcut_url,
+                                 with_icon);
   }
 
   std::string CreateWebApp(const GURL& app_url,
@@ -153,8 +143,13 @@
 };
 
 TEST_F(LacrosBrowserShortcutsControllerTest, PublishShortcuts) {
-  auto local_id_1 = CreateWebAppBasedShortcut(GURL("https://www.example.com/"),
-                                              u"shortcut name");
+  // Does not create a default icon with this shortcut so that we can
+  // verify the icon effect is set to be correct.
+  // TODO(b/315263875): Add a specific test to test the different icon
+  // effect to make this clearer.
+  AppId local_id_1 =
+      CreateWebAppBasedShortcut(GURL("https://www.example.com/"),
+                                "shortcut name", /*with_icon = */ false);
 
   InitializeLacrosBrowserShortcutsController();
   ASSERT_TRUE(fake_publisher()->controller_registered());
@@ -173,9 +168,8 @@
       apps::IconEffects::kRoundCorners | apps::IconEffects::kCrOsStandardMask);
   EXPECT_TRUE(fake_publisher()->get_deltas().back()->allow_removal);
 
-  auto local_id_2 = CreateWebAppBasedShortcut(
-      GURL("https://www.another-example.com/"), u"another shortcut name",
-      /*with_icon = */ true);
+  AppId local_id_2 = CreateWebAppBasedShortcut(
+      GURL("https://www.another-example.com/"), "another shortcut name");
 
   EXPECT_EQ(fake_publisher()->get_deltas().size(), 2U);
   EXPECT_EQ(fake_publisher()->get_deltas().back()->local_id, local_id_2);
@@ -210,8 +204,8 @@
 TEST_F(LacrosBrowserShortcutsControllerTest, LaunchShortcut) {
   InitializeLacrosBrowserShortcutsController();
 
-  auto shortcut_id = CreateWebAppBasedShortcut(GURL("https://www.example.com/"),
-                                               u"shortcut name");
+  AppId shortcut_id = CreateWebAppBasedShortcut(
+      GURL("https://www.example.com/"), "shortcut name");
 
   base::RunLoop runloop;
   fake_publisher()->controller_->LaunchShortcut(
@@ -223,8 +217,8 @@
 TEST_F(LacrosBrowserShortcutsControllerTest, GetCompressedIcon) {
   InitializeLacrosBrowserShortcutsController();
 
-  auto shortcut_id = CreateWebAppBasedShortcut(GURL("https://www.example.com/"),
-                                               u"shortcut name");
+  AppId shortcut_id = CreateWebAppBasedShortcut(
+      GURL("https://www.example.com/"), "shortcut name");
   const float scale1 = 1.0;
   const float scale2 = 2.0;
   const int kIconSize1 = 64 * scale1;
@@ -259,8 +253,8 @@
 TEST_F(LacrosBrowserShortcutsControllerTest, RemoveShortcut) {
   InitializeLacrosBrowserShortcutsController();
 
-  auto shortcut_id = CreateWebAppBasedShortcut(GURL("https://www.example.com/"),
-                                               u"shortcut name");
+  AppId shortcut_id = CreateWebAppBasedShortcut(
+      GURL("https://www.example.com/"), "shortcut name");
 
   base::RunLoop runloop;
   fake_publisher()->controller_->RemoveShortcut(
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper_unittest.cc b/chrome/browser/web_applications/app_service/web_app_publisher_helper_unittest.cc
index e6ce6256..da4ba8e1 100644
--- a/chrome/browser/web_applications/app_service/web_app_publisher_helper_unittest.cc
+++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper_unittest.cc
@@ -118,18 +118,7 @@
 
   webapps::AppId CreateShortcut(const GURL& shortcut_url,
                                 const std::string& shortcut_name) {
-    // Create a web app entry without scope, which would be recognised
-    // as ShortcutApp in the web app system.
-    auto web_app_info = std::make_unique<WebAppInstallInfo>();
-    web_app_info->title = base::UTF8ToUTF16(shortcut_name);
-    web_app_info->start_url = shortcut_url;
-
-    webapps::AppId app_id =
-        test::InstallWebApp(profile(), std::move(web_app_info));
-    CHECK(
-        WebAppProvider::GetForTest(profile())->registrar_unsafe().IsShortcutApp(
-            app_id));
-    return app_id;
+    return test::InstallShortcut(profile(), shortcut_name, shortcut_url);
   }
 
   content::BrowserTaskEnvironment task_environment_;
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_unittest.cc b/chrome/browser/web_applications/app_service/web_app_publisher_unittest.cc
index 895e91b..c960216 100644
--- a/chrome/browser/web_applications/app_service/web_app_publisher_unittest.cc
+++ b/chrome/browser/web_applications/app_service/web_app_publisher_unittest.cc
@@ -52,20 +52,9 @@
     test::AwaitStartWebAppProviderAndSubsystems(profile());
   }
 
-  std::string CreateShortcut(const GURL& shortcut_url,
-                             const std::string& shortcut_name) {
-    // Create a web app entry without scope, which would be recognised
-    // as ShortcutApp in the web app system.
-    auto web_app_info = std::make_unique<WebAppInstallInfo>();
-    web_app_info->title = base::UTF8ToUTF16(shortcut_name);
-    web_app_info->start_url = shortcut_url;
-
-    std::string app_id =
-        test::InstallWebApp(profile(), std::move(web_app_info));
-    CHECK(
-        WebAppProvider::GetForTest(profile())->registrar_unsafe().IsShortcutApp(
-            app_id));
-    return app_id;
+  webapps::AppId CreateShortcut(const GURL& shortcut_url,
+                                const std::string& shortcut_name) {
+    return test::InstallShortcut(profile(), shortcut_name, shortcut_url);
   }
 
   std::string CreateWebApp(const GURL& app_url, const std::string& app_name) {
diff --git a/chrome/browser/web_applications/commands/launch_web_app_command_browsertest.cc b/chrome/browser/web_applications/commands/launch_web_app_command_browsertest.cc
index b819d65..946b4946 100644
--- a/chrome/browser/web_applications/commands/launch_web_app_command_browsertest.cc
+++ b/chrome/browser/web_applications/commands/launch_web_app_command_browsertest.cc
@@ -395,11 +395,8 @@
 IN_PROC_BROWSER_TEST_F(LaunchWebAppCommandTest_Shortstand,
                        ShortcutLaunchInTab) {
   const GURL kShortcutUrl("https://www.shortcut-example.com");
-  auto web_app_info = std::make_unique<WebAppInstallInfo>();
-  web_app_info->title = u"TestShortcut";
-  web_app_info->start_url = kShortcutUrl;
   webapps::AppId web_shortcut_id =
-      test::InstallWebApp(profile(), std::move(web_app_info));
+      test::InstallShortcut(profile(), "TestShortcut", kShortcutUrl);
 
   {
     apps::AppLaunchParams launch_params = CreateLaunchParams(
diff --git a/chrome/browser/web_applications/test/web_app_install_test_utils.cc b/chrome/browser/web_applications/test/web_app_install_test_utils.cc
index aa47286..7cc6bde 100644
--- a/chrome/browser/web_applications/test/web_app_install_test_utils.cc
+++ b/chrome/browser/web_applications/test/web_app_install_test_utils.cc
@@ -13,6 +13,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
+#include "chrome/browser/web_applications/test/web_app_icon_test_utils.h"
 #include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
@@ -113,6 +114,50 @@
   return app_id;
 }
 
+webapps::AppId InstallShortcut(Profile* profile,
+                               const std::string& shortcut_name,
+                               const GURL& start_url,
+                               bool create_default_icon) {
+  auto web_app_info = std::make_unique<WebAppInstallInfo>();
+
+  web_app_info->start_url = start_url;
+  web_app_info->title = base::UTF8ToUTF16(shortcut_name);
+  web_app_info->user_display_mode = mojom::UserDisplayMode::kBrowser;
+  if (create_default_icon) {
+    const GeneratedIconsInfo icon_info(
+        IconPurpose::ANY, {web_app::icon_size::k32}, {SK_ColorBLACK});
+    web_app::AddIconsToWebAppInstallInfo(web_app_info.get(), start_url,
+                                         {icon_info});
+  }
+  // The sync system requires that sync entity name is never empty.
+  if (web_app_info->title.empty()) {
+    web_app_info->title = u"WebAppInstallInfo Shortcut Name";
+  }
+
+  base::test::TestFuture<const webapps::AppId&, webapps::InstallResultCode>
+      future;
+  auto* provider = WebAppProvider::GetForTest(profile);
+  DCHECK(provider);
+  WaitUntilReady(provider);
+  // In unit tests, we do not have Browser or WebContents instances. Hence we
+  // use `InstallFromInfoCommand` instead of `FetchManifestAndInstallCommand` or
+  // `WebAppInstallCommand` to install the web app.
+  provider->scheduler().InstallFromInfo(
+      std::move(web_app_info), /*overwrite_existing_manifest_fields =*/true,
+      webapps::WebappInstallSource::MENU_CREATE_SHORTCUT, future.GetCallback());
+
+  webapps::AppId app_id = future.Get<0>();
+  webapps::InstallResultCode code = future.Get<1>();
+
+  EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, code);
+
+  // Allow updates to be published to App Service listeners.
+  base::RunLoop().RunUntilIdle();
+
+  CHECK(provider->registrar_unsafe().IsShortcutApp(app_id));
+  return app_id;
+}
+
 void UninstallWebApp(Profile* profile, const webapps::AppId& app_id) {
   WebAppProvider* const provider = WebAppProvider::GetForTest(profile);
   base::test::TestFuture<webapps::UninstallResultCode> future;
diff --git a/chrome/browser/web_applications/test/web_app_install_test_utils.h b/chrome/browser/web_applications/test/web_app_install_test_utils.h
index ded5b504..8f38f226 100644
--- a/chrome/browser/web_applications/test/web_app_install_test_utils.h
+++ b/chrome/browser/web_applications/test/web_app_install_test_utils.h
@@ -57,6 +57,12 @@
     webapps::WebappInstallSource install_source =
         webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON);
 
+// Synchronously install a web-app-based shortcut for testing.
+webapps::AppId InstallShortcut(Profile* profile,
+                               const std::string& shortcut_name,
+                               const GURL& start_url,
+                               bool create_default_icon = true);
+
 // Synchronously uninstall a web app. May be used in unit tests and browser
 // tests.
 void UninstallWebApp(Profile* profile, const webapps::AppId& app_id);
diff --git a/chrome/renderer/net/available_offline_content_helper.cc b/chrome/renderer/net/available_offline_content_helper.cc
index 88f028e..02b46e7 100644
--- a/chrome/renderer/net/available_offline_content_helper.cc
+++ b/chrome/renderer/net/available_offline_content_helper.cc
@@ -132,8 +132,6 @@
 
   for (const AvailableOfflineContentPtr& item : fetched_content_) {
     if (item->id == id && item->name_space == name_space) {
-      UMA_HISTOGRAM_ENUMERATION("Net.ErrorPageCounts.SuggestionClicked",
-                                item->content_type);
       RecordEvent(error_page::NETWORK_ERROR_PAGE_OFFLINE_SUGGESTION_CLICKED);
       provider_->LaunchItem(id, name_space);
       return;
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index 76c4562..4efb1b2 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -289,6 +289,7 @@
     "os_privacy_page/privacy_hub_app_permission_test_util.ts",
     "os_privacy_page/privacy_hub_camera_subpage_test.ts",
     "os_privacy_page/privacy_hub_microphone_subpage_test.ts",
+    "os_privacy_page/privacy_hub_geolocation_subpage_test.ts",
     "os_privacy_page/privacy_hub_subpage_test.ts",
     "os_privacy_page/smart_privacy_subpage_test.ts",
     "os_privacy_page/test_metrics_consent_browser_proxy.ts",
diff --git a/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_geolocation_subpage_test.ts b/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_geolocation_subpage_test.ts
new file mode 100644
index 0000000..3c96098
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/os_privacy_page/privacy_hub_geolocation_subpage_test.ts
@@ -0,0 +1,122 @@
+// 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 'chrome://os-settings/lazy_load.js';
+
+import {SettingsPrivacyHubGeolocationSubpage} from 'chrome://os-settings/lazy_load.js';
+import {GeolocationAccessLevel, Router, routes} from 'chrome://os-settings/os_settings.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assertEquals, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
+
+import {FakeMetricsPrivate} from '../fake_metrics_private.js';
+
+import {createFakeMetricsPrivate} from './privacy_hub_app_permission_test_util.js';
+
+
+suite('<settings-privacy-hub-geolocation-subpage>', () => {
+  let metrics: FakeMetricsPrivate;
+  let privacyHubGeolocationSubpage: SettingsPrivacyHubGeolocationSubpage;
+
+  async function initPage() {
+    privacyHubGeolocationSubpage =
+        document.createElement('settings-privacy-hub-geolocation-subpage');
+    const prefs = {
+      ash: {
+        user: {
+          geolocation_access_level: {
+            key: 'ash.user.geolocation_access_level',
+            type: chrome.settingsPrivate.PrefType.NUMBER,
+            value: GeolocationAccessLevel.ALLOWED,
+          },
+        },
+      },
+    };
+    privacyHubGeolocationSubpage.prefs = prefs;
+    document.body.appendChild(privacyHubGeolocationSubpage);
+    flush();
+  }
+
+
+  setup(() => {
+    metrics = createFakeMetricsPrivate();
+    Router.getInstance().navigateTo(routes.PRIVACY_HUB_GEOLOCATION);
+  });
+
+  teardown(() => {
+    privacyHubGeolocationSubpage.remove();
+    Router.getInstance().resetRouteForTesting();
+  });
+
+  function histogram(): string {
+    return 'ChromeOS.PrivacyHub.Geolocation.AccessLevelChanged.SystemSettings';
+  }
+
+  function setGeolocationAccessLevel(accessLevel: GeolocationAccessLevel) {
+    const dropdown = privacyHubGeolocationSubpage.shadowRoot!
+                         .querySelector('settings-dropdown-menu')!.shadowRoot!
+                         .querySelector<HTMLSelectElement>('#dropdownMenu')!;
+    assertTrue(!!dropdown);
+
+    dropdown.value = accessLevel.toString();
+    dropdown.dispatchEvent(new CustomEvent('change'));
+    flush();
+  }
+
+  function getGeolocationAccessLevel(): GeolocationAccessLevel {
+    const dropdown = privacyHubGeolocationSubpage.shadowRoot!
+                         .querySelector('settings-dropdown-menu')!.shadowRoot!
+                         .querySelector<HTMLSelectElement>('#dropdownMenu')!;
+    assertTrue(!!dropdown);
+
+    switch (dropdown.value) {
+      case '0':
+        return GeolocationAccessLevel.DISALLOWED;
+      case '1':
+        return GeolocationAccessLevel.ALLOWED;
+      case '2':
+        return GeolocationAccessLevel.ONLY_ALLOWED_FOR_SYSTEM;
+    }
+
+    assertNotReached('Invalid GeolocationAccessLevel value detected');
+  }
+
+  test('Metric recorded when clicked', async () => {
+    await initPage();
+
+    assertEquals(
+        0,
+        metrics.countMetricValue(
+            histogram(), GeolocationAccessLevel.DISALLOWED));
+    assertEquals(
+        0,
+        metrics.countMetricValue(histogram(), GeolocationAccessLevel.ALLOWED));
+    assertEquals(
+        0,
+        metrics.countMetricValue(
+            histogram(), GeolocationAccessLevel.ONLY_ALLOWED_FOR_SYSTEM));
+
+    // Default access level should be ALLOWED.
+    assertEquals(GeolocationAccessLevel.ALLOWED, getGeolocationAccessLevel());
+
+    // Change dropdown and check the corresponding metric is recorded.
+    setGeolocationAccessLevel(GeolocationAccessLevel.DISALLOWED);
+    assertEquals(
+        1,
+        metrics.countMetricValue(
+            histogram(), GeolocationAccessLevel.DISALLOWED));
+
+    // Change dropdown and check the corresponding metric is recorded.
+    setGeolocationAccessLevel(GeolocationAccessLevel.ONLY_ALLOWED_FOR_SYSTEM);
+    assertEquals(
+        1,
+        metrics.countMetricValue(
+            histogram(), GeolocationAccessLevel.ONLY_ALLOWED_FOR_SYSTEM));
+
+    // Change dropdown and check the corresponding metric is recorded.
+    setGeolocationAccessLevel(GeolocationAccessLevel.ALLOWED);
+    assertEquals(
+        1,
+        metrics.countMetricValue(histogram(), GeolocationAccessLevel.ALLOWED));
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 19005071..3ad2c959 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -1122,6 +1122,16 @@
    },
  ],
  [
+   'OsPrivacyPagePrivacyHubGeolocationSubpage',
+   'os_privacy_page/privacy_hub_geolocation_subpage_test.js',
+   {
+     enabled: [
+       'ash::features::kCrosPrivacyHubV0',
+       'ash::features::kCrosPrivacyHub',
+     ]
+   },
+ ],
+ [
    'OsPrivacyPagePrivacyHubSubpage',
    'os_privacy_page/privacy_hub_subpage_test.js',
    {enabled: ['ash::features::kCrosPrivacyHubV0']},
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index fa1ae43..f76666d 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-15711.0.0
\ No newline at end of file
+15714.0.0
\ No newline at end of file
diff --git a/chromeos/ash/components/emoji/tools/.style.yapf b/chromeos/ash/components/emoji/tools/.style.yapf
new file mode 100644
index 0000000..3d8b70f
--- /dev/null
+++ b/chromeos/ash/components/emoji/tools/.style.yapf
@@ -0,0 +1,2 @@
+[style]
+based_on_style = pep8
\ No newline at end of file
diff --git a/chromeos/ash/components/emoji/tools/emoji_data.py b/chromeos/ash/components/emoji/tools/emoji_data.py
index 904a72f..78b3941 100755
--- a/chromeos/ash/components/emoji/tools/emoji_data.py
+++ b/chromeos/ash/components/emoji/tools/emoji_data.py
@@ -19,40 +19,40 @@
 
 import action_helpers
 
-
 # LINT.IfChange
 
+
 # `MULTI_TONE` is not defined in ../types.ts because this script removes any
 # multi-tone values before being outputted.
 class Tone(enum.IntEnum):
-  LIGHT = 1
-  MEDIUM_LIGHT = 2
-  MEDIUM = 3
-  MEDIUM_DARK = 4
-  DARK = 5
-  MULTI_TONE = 6
+    LIGHT = 1
+    MEDIUM_LIGHT = 2
+    MEDIUM = 3
+    MEDIUM_DARK = 4
+    DARK = 5
+    MULTI_TONE = 6
 
 
 class Gender(enum.IntEnum):
-  WOMAN = 1
-  MAN = 2
+    WOMAN = 1
+    MAN = 2
+
 
 # LINT.ThenChange(//chrome/browser/resources/chromeos/emoji_picker/types.ts)
 
-
 # Codepoints for the range of skin tone modifiers from lightest to darkest.
 TONE_MODIFIERS = {
-  ord('🏻'): Tone.LIGHT,
-  ord('🏼'): Tone.MEDIUM_LIGHT,
-  ord('🏽'): Tone.MEDIUM,
-  ord('🏾'): Tone.MEDIUM_DARK,
-  ord('🏿'): Tone.DARK,
+    ord('🏻'): Tone.LIGHT,
+    ord('🏼'): Tone.MEDIUM_LIGHT,
+    ord('🏽'): Tone.MEDIUM,
+    ord('🏾'): Tone.MEDIUM_DARK,
+    ord('🏿'): Tone.DARK,
 }
 
 # Codepoints for the woman and man gender modifiers.
 GENDER_MODIFIERS = {
-  ord('♀'): Gender.WOMAN,
-  ord('♂'): Gender.MAN,
+    ord('♀'): Gender.WOMAN,
+    ord('♂'): Gender.MAN,
 }
 
 
@@ -66,10 +66,10 @@
     for tag in root.iterfind('./annotations/annotation'):
         cp = tag.attrib['cp']
         if tag.attrib.get('type') == 'tts':
-            if tag.text.startswith("flag"):
-              names[cp] = tag.text.replace("flag:","flag of")
+            if tag.text.startswith('flag'):
+                names[cp] = tag.text.replace('flag:', 'flag of')
             else:
-              names[cp] = tag.text
+                names[cp] = tag.text
         else:
             keywords[cp] = tag.text.split(' | ')
 
@@ -82,34 +82,33 @@
 
 
 def get_tone(codepoints):
-  tone_matches = [
-    codepoint
-    for codepoint in codepoints
-    if codepoint in TONE_MODIFIERS
-  ]
+    tone_matches = [
+        codepoint for codepoint in codepoints if codepoint in TONE_MODIFIERS
+    ]
 
-  if len(tone_matches) == 0:
-    return None
-  elif len(tone_matches) == 1:
-    return TONE_MODIFIERS[tone_matches[0]]
-  else:
-    return Tone.MULTI_TONE
+    if len(tone_matches) == 0:
+        return None
+    elif len(tone_matches) == 1:
+        return TONE_MODIFIERS[tone_matches[0]]
+    else:
+        return Tone.MULTI_TONE
 
 
 def get_gender(codepoints):
-  for codepoint in codepoints:
-    if codepoint in GENDER_MODIFIERS:
-      return GENDER_MODIFIERS[codepoint]
+    for codepoint in codepoints:
+        if codepoint in GENDER_MODIFIERS:
+            return GENDER_MODIFIERS[codepoint]
 
-  return None
+    return None
 
 
 def transform_emoji_data(metadata, names, keywords, first_only):
-    def transform(codepoints, is_variant, emoticons = None, shortcodes = None):
+
+    def transform(codepoints, is_variant, emoticons=None, shortcodes=None):
         if emoticons is None:
-          emoticons = []
+            emoticons = []
         if shortcodes is None:
-          shortcodes = []
+            shortcodes = []
         # transform array of codepoint values into unicode string.
         string = u''.join(chr(x) for x in codepoints)
 
@@ -118,20 +117,21 @@
             string = string.replace(u'\ufe0f', u'')
         # TODO(b/183440310): Better handle search for non-standard emoji.
         if string in names:
-          name = names[string]
-          keyword_list = keywords[string] + emoticons + shortcodes
+            name = names[string]
+            keyword_list = keywords[string] + emoticons + shortcodes
         else:
-          name = ''
-          keyword_list = emoticons
+            name = ''
+            keyword_list = emoticons
 
         if is_variant:
-          return {'string': string, 'name': name}
+            return {'string': string, 'name': name}
         else:
-          return {'string': string, 'name': name, 'keywords': keyword_list}
+            return {'string': string, 'name': name, 'keywords': keyword_list}
+
     if first_only:
-      metadata = [metadata[0]]
+        metadata = [metadata[0]]
     else:
-      metadata = metadata[1:]
+        metadata = metadata[1:]
 
     # Create a new object for output since they keep adding extra properties to
     # the JSON (rather than just editing the input object).
@@ -140,7 +140,8 @@
         newGroup = []
         for emoji in group['emoji']:
             newobj = {
-                'base': transform(
+                'base':
+                transform(
                     emoji['base'],
                     False,
                     emoji['emoticons'],
@@ -148,35 +149,35 @@
                 ),
             }
             if emoji['alternates']:
-              newobj['alternates'] = []
+                newobj['alternates'] = []
 
-              has_multi_tone = any(
-                get_tone(codepoints) == Tone.MULTI_TONE
-                for codepoints in emoji['alternates']
-              )
+                has_multi_tone = any(
+                    get_tone(codepoints) == Tone.MULTI_TONE
+                    for codepoints in emoji['alternates'])
 
-              for codepoints in emoji['alternates']:
-                variant = transform(codepoints, True)
+                for codepoints in emoji['alternates']:
+                    variant = transform(codepoints, True)
 
-                # Multi-tone preferences are individual, so all tones are
-                # ignored if any variant is multi-tone.
-                tone = get_tone(codepoints) if not has_multi_tone else None
-                gender = get_gender(codepoints)
+                    # Multi-tone preferences are individual, so all tones are
+                    # ignored if any variant is multi-tone.
+                    tone = get_tone(codepoints) if not has_multi_tone else None
+                    gender = get_gender(codepoints)
 
-                if tone:
-                  variant['tone'] = tone
-                  newobj['groupedTone'] = True
+                    if tone:
+                        variant['tone'] = tone
+                        newobj['groupedTone'] = True
 
-                if gender:
-                  variant['gender'] = gender
-                  newobj['groupedGender'] = True
+                    if gender:
+                        variant['gender'] = gender
+                        newobj['groupedGender'] = True
 
-                newobj['alternates'].append(variant)
+                    newobj['alternates'].append(variant)
 
             newGroup.append(newobj)
         out.append({'emoji': newGroup})
     return out
 
+
 def main(args):
     parser = argparse.ArgumentParser()
     parser.add_argument('--metadata',
@@ -189,17 +190,17 @@
                         required=True,
                         nargs='+',
                         help='emoji keyword files as list of XML files')
-    parser.add_argument('--firstgroup',
-                        required = True,
-                        help='Only output the first group, otherwise output all groups'
-    )
+    parser.add_argument(
+        '--firstgroup',
+        required=True,
+        help='Only output the first group, otherwise output all groups')
 
     options = parser.parse_args(args)
 
     metadata_file = options.metadata
     keyword_files = options.keywords
     output_file = options.output
-    first_group = options.firstgroup == "True"
+    first_group = options.firstgroup == 'True'
 
     # iterate through keyword files and combine them
     names = {}
diff --git a/chromeos/ash/components/emoji/tools/emoticon_data.py b/chromeos/ash/components/emoji/tools/emoticon_data.py
index 7125702..3598a68 100644
--- a/chromeos/ash/components/emoji/tools/emoticon_data.py
+++ b/chromeos/ash/components/emoji/tools/emoticon_data.py
@@ -14,7 +14,6 @@
 
 import action_helpers
 
-
 # Set of unicode characters that do not render with fonts available on ChromeOS
 # TODO(b:267370102) add font(s) such that there are no more invalid characters
 INVALID_CHARACTERS = set([
@@ -40,15 +39,15 @@
         list(dict): list of readily used emoticon groups.
     """
     return [{
-        "group":
-        group["group"],
-        "emoji": [{
-            "base": {
-                "string": emoticon["value"],
-                "name": emoticon["description"],
+        'group':
+        group['group'],
+        'emoji': [{
+            'base': {
+                'string': emoticon['value'],
+                'name': emoticon['description'],
             },
-        } for emoticon in group["emoticon"]
-                  if isValidEmoticon(emoticon["value"])]
+        } for emoticon in group['emoticon']
+                  if isValidEmoticon(emoticon['value'])]
     } for group in metadata]
 
 
diff --git a/chromeos/ash/components/emoji/tools/symbol_data.py b/chromeos/ash/components/emoji/tools/symbol_data.py
index a0b678b..4057b4b 100755
--- a/chromeos/ash/components/emoji/tools/symbol_data.py
+++ b/chromeos/ash/components/emoji/tools/symbol_data.py
@@ -24,7 +24,6 @@
 logging.basicConfig(stream=sys.stdout, level=logging.ERROR)
 LOGGER = logging.getLogger(__name__)
 
-
 # List of unicode ranges for each symbol group (ranges are inclusive).
 SYMBOLS_GROUPS = {
     'Arrows': [
@@ -113,7 +112,6 @@
     ],
 }
 
-
 # Set of unicode symbols that do not render with fonts available on ChromeOS
 INVALID_SYMBOLS = set([
     '\u2BBA',
@@ -125,34 +123,33 @@
     '\U0001F8B1',
 ])
 
-
 # Custom search keywords for symbols.
 # By default, symbols do not have search keywords.
 # These are "shortcut" style search keywords based off compose key to provide a
 # fast way to access common symbols.
 CUSTOM_KEYWORDS = {
-    '⅐':['17'],
-    '⅓':['13'],
-    '⅔':['23'],
-    '½':['12'],
-    '¼':['14'],
-    '¾':['34'],
-    '⅕':['15'],
-    '⅖':['25'],
-    '⅗':['35'],
-    '⅘':['45'],
-    '⅙':['16'],
-    '⅛':['18'],
-    '⅜':['38'],
-    '⅝':['58'],
-    '⅞':['78'],
-    '©':['oc', 'co'],
-    '®':['or', 'ro'],
-    '₠':['CE'],
-    '₡':['C/','/C'],
-    '₢':['Cr'],
-    '₣':['Fr'],
-    '¢':['|c', 'c|', 'c/', '/c'],
+    '⅐': ['17'],
+    '⅓': ['13'],
+    '⅔': ['23'],
+    '½': ['12'],
+    '¼': ['14'],
+    '¾': ['34'],
+    '⅕': ['15'],
+    '⅖': ['25'],
+    '⅗': ['35'],
+    '⅘': ['45'],
+    '⅙': ['16'],
+    '⅛': ['18'],
+    '⅜': ['38'],
+    '⅝': ['58'],
+    '⅞': ['78'],
+    '©': ['oc', 'co'],
+    '®': ['or', 'ro'],
+    '₠': ['CE'],
+    '₡': ['C/', '/C'],
+    '₢': ['Cr'],
+    '₣': ['Fr'],
+    '¢': ['|c', 'c|', 'c/', '/c'],
     '£': ['L-', '-L'],
     '₥': ['m/', '/m'],
     '₦': ['N=', '=N'],
@@ -180,8 +177,7 @@
     # Name of the unicode character.
     name: str
     # Search keywords related to the unicode character.
-    keywords: List[str] = dataclasses.field(
-        default_factory=list)
+    keywords: List[str] = dataclasses.field(default_factory=list)
 
 
 @dataclasses.dataclass
@@ -190,8 +186,7 @@
     # Base Emoji.
     base: EmojiPickerChar
     # Base Emoji's variants and alternative emojis.
-    alternates: List[EmojiPickerChar] = dataclasses.field(
-        default_factory=list)
+    alternates: List[EmojiPickerChar] = dataclasses.field(default_factory=list)
 
 
 @dataclasses.dataclass
@@ -235,8 +230,7 @@
     """
     return {
         _convert_snake_case_to_camel_case(key): value
-        for (key, value) in data
-        if not isinstance(value, list) or value
+        for (key, value) in data if not isinstance(value, list) or value
     }
 
 
@@ -286,9 +280,8 @@
 
     LOGGER.info(
         'generating EmojiPickerChar instances for ranges: [%s].',
-        ', '.join(
-            '(U+{:02x}, U+{:02x})'.format(*rng)
-            for rng in unicode_ranges))
+        ', '.join('(U+{:02x}, U+{:02x})'.format(*rng)
+                  for rng in unicode_ranges))
 
     num_chars = 0
     num_ignored = 0
@@ -297,9 +290,7 @@
     for (start_code_point, end_code_point) in unicode_ranges:
         LOGGER.debug(
             'generating EmojiPickerChar instances '
-            'for range (U+%02x to U+%02x).',
-            start_code_point,
-            end_code_point)
+            'for range (U+%02x to U+%02x).', start_code_point, end_code_point)
 
         num_chars += end_code_point + 1 - start_code_point
         # Iterate over all code points in the range.
@@ -319,18 +310,16 @@
                     raise
                 else:
                     num_ignored += 1
-                    LOGGER.warning(
-                        'invalid code point U+%02x.', code_point)
+                    LOGGER.warning('invalid code point U+%02x.', code_point)
 
-    LOGGER.info(
-        'stats: #returned instances: %d, #ignored code points: %d',
-        num_chars,
-        num_ignored)
+    LOGGER.info('stats: #returned instances: %d, #ignored code points: %d',
+                num_chars, num_ignored)
 
 
 def get_symbols_groups(
         group_unicode_ranges: Dict[str, List[Tuple[int, int]]],
-        search_only: bool = False, ignore_errors: bool = True,
+        search_only: bool = False,
+        ignore_errors: bool = True,
         filter_set: Optional[Set[str]] = None) -> List[EmojiPickerGroup]:
     """Creates symbols data from predefined groups and their unicode ranges.
 
@@ -353,27 +342,29 @@
         emoji_chars = _convert_unicode_ranges_to_emoji_chars(
             unicode_ranges, ignore_errors=ignore_errors)
         emoji = [
-            EmojiPickerEmoji(base=emoji_char)
-            for emoji_char in emoji_chars
-            if filter_set is None or emoji_char.string not in filter_set]
+            EmojiPickerEmoji(base=emoji_char) for emoji_char in emoji_chars
+            if filter_set is None or emoji_char.string not in filter_set
+        ]
 
-        emoji_group = EmojiPickerGroup(
-            group=group_name, emoji=emoji, search_only=search_only)
+        emoji_group = EmojiPickerGroup(group=group_name,
+                                       emoji=emoji,
+                                       search_only=search_only)
         emoji_groups.append(emoji_group)
     return emoji_groups
 
 
 def main(argv: List[str]) -> None:
     parser = argparse.ArgumentParser()
-    parser.add_argument(
-        '--output', required=True, type=str,
-        help='Path to write the output JSON file.')
-    parser.add_argument(
-        '--verbose', required=False, default=False,
-        action='store_true',
-        help="Set the logging level to Debug.")
-    parser.add_argument(
-        '--filter-data-paths', action='append', nargs='+')
+    parser.add_argument('--output',
+                        required=True,
+                        type=str,
+                        help='Path to write the output JSON file.')
+    parser.add_argument('--verbose',
+                        required=False,
+                        default=False,
+                        action='store_true',
+                        help='Set the logging level to Debug.')
+    parser.add_argument('--filter-data-paths', action='append', nargs='+')
 
     args = parser.parse_args(argv)
 
@@ -395,35 +386,30 @@
     filter_set |= INVALID_SYMBOLS
 
     # Add symbol groups.
-    symbols_groups = get_symbols_groups(
-        group_unicode_ranges=SYMBOLS_GROUPS,
-        filter_set=filter_set,
-        search_only=False)
+    symbols_groups = get_symbols_groups(group_unicode_ranges=SYMBOLS_GROUPS,
+                                        filter_set=filter_set,
+                                        search_only=False)
 
     # Add search-only symbol groups.
     symbols_groups.extend(
-        get_symbols_groups(
-            group_unicode_ranges=SEARCH_ONLY_SYMBOLS_GROUPS,
-            filter_set=filter_set,
-            search_only=True)
-    )
+        get_symbols_groups(group_unicode_ranges=SEARCH_ONLY_SYMBOLS_GROUPS,
+                           filter_set=filter_set,
+                           search_only=True))
 
     # Create the data and convert them to dict.
     symbols_groups_dicts = []
     for symbol_group in symbols_groups:
         symbol_group_dict = dataclasses.asdict(
-            symbol_group,
-            dict_factory=_emoji_data_dict_factory)
+            symbol_group, dict_factory=_emoji_data_dict_factory)
         symbols_groups_dicts.append(symbol_group_dict)
 
     # Write the result to output path as json file.
     with action_helpers.atomic_output(args.output) as tmp_file:
         tmp_file.write(
-            json.dumps(
-                symbols_groups_dicts,
-                separators=(',', ':'),
-                ensure_ascii=False).encode('utf-8'))
+            json.dumps(symbols_groups_dicts,
+                       separators=(',', ':'),
+                       ensure_ascii=False).encode('utf-8'))
 
 
-if __name__ == "__main__":
+if __name__ == '__main__':
     main(sys.argv[1:])
diff --git a/chromeos/profiles/arm-exp.afdo.newest.txt b/chromeos/profiles/arm-exp.afdo.newest.txt
index 7b84205..75bc69b 100644
--- a/chromeos/profiles/arm-exp.afdo.newest.txt
+++ b/chromeos/profiles/arm-exp.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-exp-121-6167.9-1702295304-benchmark-122.0.6189.0-r1-redacted.afdo.xz
+chromeos-chrome-arm-exp-121-6167.9-1702295304-benchmark-122.0.6191.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/arm.afdo.newest.txt b/chromeos/profiles/arm.afdo.newest.txt
index f383089..9b794e02 100644
--- a/chromeos/profiles/arm.afdo.newest.txt
+++ b/chromeos/profiles/arm.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-none-121-6154.0-1702299075-benchmark-122.0.6190.0-r1-redacted.afdo.xz
+chromeos-chrome-arm-none-121-6154.0-1702299075-benchmark-122.0.6191.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index 742f5d598..6dc13c0 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-121-6167.9-1702295304-benchmark-122.0.6189.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-121-6167.9-1702295304-benchmark-122.0.6190.0-r1-redacted.afdo.xz
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 9a406f8..b5f2e19 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -483,6 +483,7 @@
       "//components/services/paint_preview_compositor:unit_tests",
       "//components/services/storage:tests",
       "//components/session_proto_db:unit_tests",
+      "//components/subresource_filter/content/browser:unit_tests",
       "//components/subresource_filter/content/common:unit_tests",
       "//components/subresource_filter/content/renderer:unit_tests",
       "//components/tracing:unit_tests",
@@ -569,7 +570,6 @@
       "//components/site_isolation:unit_tests",
       "//components/spellcheck/browser:unit_tests",
       "//components/spellcheck/renderer:unit_tests",
-      "//components/subresource_filter/content/browser:unit_tests",
     ]
 
     if (!is_win) {  #!iOS and!Windows
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index dab1077b..7a3dfd6 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -121,6 +121,8 @@
     "data_model/address.h",
     "data_model/autofill_data_model.cc",
     "data_model/autofill_data_model.h",
+    "data_model/autofill_feature_guarded_address_component.cc",
+    "data_model/autofill_feature_guarded_address_component.h",
     "data_model/autofill_i18n_api.cc",
     "data_model/autofill_i18n_api.h",
     "data_model/autofill_i18n_formatting_expressions.h",
@@ -482,29 +484,15 @@
     "ui/accessory_sheet_enums.h",
     "ui/address_combobox_model.cc",
     "ui/address_combobox_model.h",
-    "ui/address_contact_form_label_formatter.cc",
-    "ui/address_contact_form_label_formatter.h",
-    "ui/address_email_form_label_formatter.cc",
-    "ui/address_email_form_label_formatter.h",
-    "ui/address_form_label_formatter.cc",
-    "ui/address_form_label_formatter.h",
-    "ui/address_phone_form_label_formatter.cc",
-    "ui/address_phone_form_label_formatter.h",
     "ui/autofill_image_fetcher_base.h",
     "ui/autofill_popup_delegate.h",
     "ui/autofill_resource_utils.cc",
     "ui/autofill_resource_utils.h",
-    "ui/contact_form_label_formatter.cc",
-    "ui/contact_form_label_formatter.h",
     "ui/country_combobox_model.cc",
     "ui/country_combobox_model.h",
     "ui/fast_checkout_client.h",
     "ui/fast_checkout_delegate.h",
     "ui/fast_checkout_enums.h",
-    "ui/label_formatter.cc",
-    "ui/label_formatter.h",
-    "ui/label_formatter_utils.cc",
-    "ui/label_formatter_utils.h",
     "ui/payments/autofill_error_dialog_controller.h",
     "ui/payments/bubble_show_options.cc",
     "ui/payments/bubble_show_options.h",
@@ -528,6 +516,8 @@
     "ui/touch_to_fill_delegate.h",
     "validation.cc",
     "validation.h",
+    "webdata/addresses/address_autofill_table.cc",
+    "webdata/addresses/address_autofill_table.h",
     "webdata/autocomplete_entry.cc",
     "webdata/autocomplete_entry.h",
     "webdata/autocomplete_sync_bridge.cc",
@@ -598,8 +588,6 @@
       "payments/autofill_virtual_card_enrollment_infobar_delegate_mobile.cc",
       "payments/autofill_virtual_card_enrollment_infobar_delegate_mobile.h",
       "payments/autofill_virtual_card_enrollment_infobar_mobile.h",
-      "ui/mobile_label_formatter.cc",
-      "ui/mobile_label_formatter.h",
       "ui/payments/card_expiration_date_fix_flow_controller.h",
       "ui/payments/card_expiration_date_fix_flow_controller_impl.cc",
       "ui/payments/card_expiration_date_fix_flow_controller_impl.h",
@@ -1157,6 +1145,7 @@
     "ui/payments/virtual_card_enroll_ui_model_unittest.cc",
     "ui/region_combobox_model_unittest.cc",
     "validation_unittest.cc",
+    "webdata/addresses/address_autofill_table_unittest.cc",
     "webdata/autocomplete_sync_bridge_unittest.cc",
     "webdata/autocomplete_table_unittest.cc",
     "webdata/autofill_profile_sync_bridge_unittest.cc",
@@ -1181,7 +1170,6 @@
     sources += [
       "payments/autofill_save_card_delegate_unittest.cc",
       "payments/autofill_save_card_ui_info_unittest.cc",
-      "ui/mobile_label_formatter_unittest.cc",
       "ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc",
       "ui/payments/card_name_fix_flow_controller_impl_unittest.cc",
     ]
@@ -1212,13 +1200,6 @@
     sources += [
       "payments/local_card_migration_manager_unittest.cc",
       "server_prediction_overrides_unittest.cc",
-      "ui/address_contact_form_label_formatter_unittest.cc",
-      "ui/address_email_form_label_formatter_unittest.cc",
-      "ui/address_form_label_formatter_unittest.cc",
-      "ui/address_phone_form_label_formatter_unittest.cc",
-      "ui/contact_form_label_formatter_unittest.cc",
-      "ui/label_formatter_unittest.cc",
-      "ui/label_formatter_utils_unittest.cc",
     ]
   }
 
diff --git a/components/autofill/core/browser/autofill_data_util.cc b/components/autofill/core/browser/autofill_data_util.cc
index 24a9af6..078b513 100644
--- a/components/autofill/core/browser/autofill_data_util.cc
+++ b/components/autofill/core/browser/autofill_data_util.cc
@@ -273,7 +273,7 @@
 }
 
 void AddGroupToBitmask(uint32_t* group_bitmask, FieldType type) {
-  const FieldTypeGroup group = GroupTypeOfServerFieldType(type);
+  const FieldTypeGroup group = GroupTypeOfFieldType(type);
   switch (group) {
     case autofill::FieldTypeGroup::kName:
       *group_bitmask |= kName;
diff --git a/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
index 3a9c1f08..15597900 100644
--- a/components/autofill/core/browser/autofill_external_delegate_unittest.cc
+++ b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -99,7 +99,7 @@
 Suggestion CreateFieldByFieldFillingSuggestion(const std::string& guid,
                                                FieldType fbf_type_used) {
   Suggestion suggestion = test::CreateAutofillSuggestion(
-      GroupTypeOfServerFieldType(fbf_type_used) == FieldTypeGroup::kCreditCard
+      GroupTypeOfFieldType(fbf_type_used) == FieldTypeGroup::kCreditCard
           ? PopupItemId::kCreditCardFieldByFieldFilling
           : PopupItemId::kAddressFieldByFieldFilling,
       u"field by field", Suggestion::Guid(guid));
diff --git a/components/autofill/core/browser/autofill_field.cc b/components/autofill/core/browser/autofill_field.cc
index 38bcaa05..a68921d 100644
--- a/components/autofill/core/browser/autofill_field.cc
+++ b/components/autofill/core/browser/autofill_field.cc
@@ -276,7 +276,7 @@
 }
 
 void AutofillField::add_possible_types_validities(
-    const ServerFieldTypeValidityStateMap& possible_types_validities) {
+    const FieldTypeValidityStateMap& possible_types_validities) {
   for (const auto& possible_type_validity : possible_types_validities) {
     possible_types_validities_[possible_type_validity.first].push_back(
         possible_type_validity.second);
@@ -350,7 +350,7 @@
   // If autocomplete=tel/tel-* and server confirms it really is a phone field,
   // we always use the server prediction as html types are not very reliable.
   if (GroupTypeOfHtmlFieldType(html_type_) == FieldTypeGroup::kPhone &&
-      GroupTypeOfServerFieldType(server_type()) == FieldTypeGroup::kPhone) {
+      GroupTypeOfFieldType(server_type()) == FieldTypeGroup::kPhone) {
     return AutofillType(server_type());
   }
 
@@ -406,7 +406,7 @@
     // Either way, retain a preference for the CVC heuristic over the
     // server's password predictions (http://crbug.com/469007)
     believe_server =
-        believe_server && !(GroupTypeOfServerFieldType(server_type()) ==
+        believe_server && !(GroupTypeOfFieldType(server_type()) ==
                                 FieldTypeGroup::kPasswordField &&
                             heuristic_type() == CREDIT_CARD_VERIFICATION_CODE);
 
@@ -503,10 +503,8 @@
 }
 
 bool AutofillField::IsCreditCardPrediction() const {
-  return GroupTypeOfServerFieldType(server_type()) ==
-             FieldTypeGroup::kCreditCard ||
-         GroupTypeOfServerFieldType(heuristic_type()) ==
-             FieldTypeGroup::kCreditCard;
+  return GroupTypeOfFieldType(server_type()) == FieldTypeGroup::kCreditCard ||
+         GroupTypeOfFieldType(heuristic_type()) == FieldTypeGroup::kCreditCard;
 }
 
 void AutofillField::AppendLogEventIfNotRepeated(
diff --git a/components/autofill/core/browser/autofill_field.h b/components/autofill/core/browser/autofill_field.h
index c8c91f2..db6339b5 100644
--- a/components/autofill/core/browser/autofill_field.h
+++ b/components/autofill/core/browser/autofill_field.h
@@ -27,10 +27,10 @@
 namespace autofill {
 
 typedef std::map<FieldType, std::vector<AutofillDataModel::ValidityState>>
-    ServerFieldTypeValidityStatesMap;
+    FieldTypeValidityStatesMap;
 
 typedef std::map<FieldType, AutofillDataModel::ValidityState>
-    ServerFieldTypeValidityStateMap;
+    FieldTypeValidityStateMap;
 
 // Specifies if the Username First Flow vote has intermediate values.
 enum class IsMostRecentSingleUsernameCandidate {
@@ -92,7 +92,7 @@
   HtmlFieldType html_type() const { return html_type_; }
   HtmlFieldMode html_mode() const { return html_mode_; }
   const FieldTypeSet& possible_types() const { return possible_types_; }
-  const ServerFieldTypeValidityStatesMap& possible_types_validities() const {
+  const FieldTypeValidityStatesMap& possible_types_validities() const {
     return possible_types_validities_;
   }
   bool previously_autofilled() const { return previously_autofilled_; }
@@ -103,7 +103,7 @@
   // Setters for the detected types.
   void set_heuristic_type(HeuristicSource s, FieldType t);
   void add_possible_types_validities(
-      const ServerFieldTypeValidityStateMap& possible_types_validities);
+      const FieldTypeValidityStateMap& possible_types_validities);
   void set_server_predictions(
       std::vector<AutofillQueryResponse::FormSuggestion::FieldSuggestion::
                       FieldPrediction> predictions);
@@ -116,7 +116,7 @@
     possible_types_ = possible_types;
   }
   void set_possible_types_validities(
-      const ServerFieldTypeValidityStatesMap& possible_types_validities) {
+      const FieldTypeValidityStatesMap& possible_types_validities) {
     possible_types_validities_ = possible_types_validities;
   }
   std::vector<AutofillDataModel::ValidityState>
@@ -406,7 +406,7 @@
   FieldTypeSet possible_types_;
 
   // The set of possible types and their validity for this field.
-  ServerFieldTypeValidityStatesMap possible_types_validities_;
+  FieldTypeValidityStatesMap possible_types_validities_;
 
   // A low-entropy hash of the field's initial value before user-interactions or
   // automatic fillings. This field is used to detect static placeholders.
diff --git a/components/autofill/core/browser/autofill_granular_filling_utils.cc b/components/autofill/core/browser/autofill_granular_filling_utils.cc
index dff9d580..6dd253e 100644
--- a/components/autofill/core/browser/autofill_granular_filling_utils.cc
+++ b/components/autofill/core/browser/autofill_granular_filling_utils.cc
@@ -72,7 +72,7 @@
   switch (GetFillingMethodFromTargetedFields(last_targeted_field_types)) {
     case AutofillFillingMethod::kGroupFilling:
       return GetServerFieldsForFieldGroup(
-          GroupTypeOfServerFieldType(triggering_field_type));
+          GroupTypeOfFieldType(triggering_field_type));
     case AutofillFillingMethod::kFullForm:
       return kAllFieldTypes;
     case AutofillFillingMethod::kFieldByFieldFilling:
diff --git a/components/autofill/core/browser/autofill_suggestion_generator.cc b/components/autofill/core/browser/autofill_suggestion_generator.cc
index 8eabb475..97e36a5c 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator.cc
+++ b/components/autofill/core/browser/autofill_suggestion_generator.cc
@@ -40,7 +40,6 @@
 #include "components/autofill/core/browser/payments/autofill_offer_manager.h"
 #include "components/autofill/core/browser/payments/constants.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
-#include "components/autofill/core/browser/ui/label_formatter.h"
 #include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/common/autofill_clock.h"
@@ -432,7 +431,7 @@
                                 const std::string& app_locale,
                                 Suggestion& suggestion) {
   const FieldTypeGroup trigger_field_type_group =
-      GroupTypeOfServerFieldType(trigger_field_type);
+      GroupTypeOfFieldType(trigger_field_type);
 
   bool phone_number_suggestion_added = false;
   if (profile.HasInfo(PHONE_HOME_WHOLE_NUMBER)) {
@@ -606,7 +605,7 @@
   }
 
   const FieldTypeGroup trigger_field_type_group =
-      GroupTypeOfServerFieldType(trigger_field_type);
+      GroupTypeOfFieldType(trigger_field_type);
 
   // Lambda to return the expected `PopupItemId` when
   // `last_targeted_fields` matches one of the granular filling groups.
@@ -703,7 +702,7 @@
   std::vector<std::vector<std::u16string>> labels;
   labels.reserve(profiles.size());
   for (const AutofillProfile* profile : profiles) {
-    switch (GroupTypeOfServerFieldType(triggering_field_type)) {
+    switch (GroupTypeOfFieldType(triggering_field_type)) {
       case FieldTypeGroup::kName:
         labels.push_back({l10n_util::GetStringUTF16(
             IDS_AUTOFILL_FILL_NAME_GROUP_POPUP_OPTION_SELECTED)});
@@ -751,7 +750,7 @@
       !AreFieldsGranularFillingGroup(*last_targeted_fields)) {
     return {triggering_field_type};
   }
-  switch (GroupTypeOfServerFieldType(triggering_field_type)) {
+  switch (GroupTypeOfFieldType(triggering_field_type)) {
     case FieldTypeGroup::kAddress:
       if (ShouldAddAddressLine1ToGranularFillingLabels(triggering_field_type)) {
         // In the case where the `ADDRESS_HOME_LINE1` was added to the granular
@@ -790,34 +789,13 @@
     FieldType trigger_field_type,
     std::optional<FieldTypeSet> last_targeted_fields,
     const std::string& app_locale) {
-  std::unique_ptr<LabelFormatter> formatter;
-  bool use_formatter;
-
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-  use_formatter = base::FeatureList::IsEnabled(
-      features::kAutofillUseImprovedLabelDisambiguation);
-#else
-  use_formatter = base::FeatureList::IsEnabled(
-      features::kAutofillUseMobileLabelDisambiguation);
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-
-  // The formatter stores a constant reference to |profiles|.
-  // This is safe since the formatter is destroyed when this function returns.
-  formatter = use_formatter
-                  ? LabelFormatter::Create(profiles, app_locale,
-                                           trigger_field_type, field_types)
-                  : nullptr;
-
   // Generate disambiguating labels based on the list of matches.
   std::vector<std::u16string> differentiating_labels;
-
   if (!IsAddressType(trigger_field_type) &&
       base::FeatureList::IsEnabled(
           features::kAutofillForUnclassifiedFieldsAvailable)) {
     differentiating_labels =
         GetProfileSuggestionLabelForNonAddressField(profiles, app_locale);
-  } else if (formatter) {
-    differentiating_labels = formatter->GetLabels();
   } else {
     AutofillProfile::CreateInferredLabels(
         profiles, field_types,
@@ -825,12 +803,6 @@
             trigger_field_type, last_targeted_fields),
         1, app_locale, &differentiating_labels);
   }
-
-  if (use_formatter && !profiles.empty()) {
-    AutofillMetrics::LogProfileSuggestionsMadeWithFormatter(formatter !=
-                                                            nullptr);
-  }
-
   return differentiating_labels;
 }
 
@@ -1032,8 +1004,7 @@
                                        bool field_is_autofilled) {
   // Phones should do a substring match because they can be trimmed to remove
   // the first parts (e.g. country code or prefix).
-  if (GroupTypeOfServerFieldType(trigger_field_type) ==
-          FieldTypeGroup::kPhone &&
+  if (GroupTypeOfFieldType(trigger_field_type) == FieldTypeGroup::kPhone &&
       suggestion_canon.find(field_contents_canon) != std::u16string::npos) {
     return true;
   }
@@ -1073,7 +1044,7 @@
 // entered into.
 std::u16string NormalizeForComparisonForType(const std::u16string& text,
                                              FieldType type) {
-  if (GroupTypeOfServerFieldType(type) == FieldTypeGroup::kEmail) {
+  if (GroupTypeOfFieldType(type) == FieldTypeGroup::kEmail) {
     // For emails, keep special characters so that if the user has two emails
     // `test@foo.xyz` and `test1@foo.xyz` saved, only the first one is suggested
     // upon entering `test@` into the email field.
@@ -1215,8 +1186,7 @@
   // This will be used to check if suggestions should be supported with icons.
   const bool contains_profile_related_fields =
       base::ranges::count_if(field_types, [](FieldType field_type) {
-        FieldTypeGroup field_type_group =
-            GroupTypeOfServerFieldType(field_type);
+        FieldTypeGroup field_type_group = GroupTypeOfFieldType(field_type);
         return field_type_group == FieldTypeGroup::kName ||
                field_type_group == FieldTypeGroup::kAddress ||
                field_type_group == FieldTypeGroup::kPhone ||
@@ -1224,7 +1194,7 @@
       }) > 1;
 
   FieldTypeGroup trigger_field_type_group =
-      GroupTypeOfServerFieldType(trigger_field_type);
+      GroupTypeOfFieldType(trigger_field_type);
   for (const AutofillProfile* profile : profiles) {
     // Compute the main text to be displayed in the suggestion bubble.
     std::u16string main_text =
@@ -1434,7 +1404,7 @@
     const AutofillProfile& profile,
     Suggestion& suggestion) const {
   const FieldTypeGroup trigger_field_type_group =
-      GroupTypeOfServerFieldType(trigger_field_type);
+      GroupTypeOfFieldType(trigger_field_type);
   const std::string app_locale = personal_data_->app_locale();
   AddNameChildSuggestions(trigger_field_type_group, profile, app_locale,
                           suggestion);
@@ -1456,8 +1426,7 @@
   std::vector<Suggestion> suggestions;
   // Manual fallback entries are shown for all non credit card fields.
   const bool is_manual_fallback =
-      GroupTypeOfServerFieldType(trigger_field_type) !=
-      FieldTypeGroup::kCreditCard;
+      GroupTypeOfFieldType(trigger_field_type) != FieldTypeGroup::kCreditCard;
   const std::string& app_locale = personal_data_->app_locale();
 
   std::map<std::string, AutofillOfferData*> card_linked_offers_map =
@@ -1780,8 +1749,7 @@
     bool card_linked_offer_available) const {
   // Manual fallback entries are shown for all non credit card fields.
   const bool is_manual_fallback =
-      GroupTypeOfServerFieldType(trigger_field_type) !=
-      FieldTypeGroup::kCreditCard;
+      GroupTypeOfFieldType(trigger_field_type) != FieldTypeGroup::kCreditCard;
 
   Suggestion suggestion;
   suggestion.icon = credit_card.CardIconForAutofillSuggestion();
diff --git a/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc b/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
index 4715dbc..a7ba444 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
@@ -28,7 +28,6 @@
 #include "components/autofill/core/browser/personal_data_manager_test_base.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
 #include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
@@ -58,15 +57,6 @@
 using testing::IsEmpty;
 using testing::Matcher;
 
-constexpr Suggestion::Icon kAddressEntryIcon = Suggestion::Icon::kAccount;
-
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-std::vector<std::vector<Suggestion::Text>> ConstructLabelLineMatrix(
-    const std::vector<std::u16string>& parts) {
-  return {{Suggestion::Text(ConstructLabelLine(parts))}};
-}
-#endif
-
 Matcher<Suggestion> EqualsSuggestion(PopupItemId id) {
   return Field(&Suggestion::popup_item_id, id);
 }
@@ -756,11 +746,7 @@
 }
 
 TEST_F(AutofillSuggestionGeneratorTest,
-       CreateSuggestionsFromProfiles_PhoneSubstring_NoImprovedDisambiguation) {
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitAndDisableFeature(
-      features::kAutofillUseImprovedLabelDisambiguation);
-
+       CreateSuggestionsFromProfiles_PhoneSubstring) {
   AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
   test::SetProfileInfo(&profile, "Marion", "Mitchell", "Morrison",
                        "johnwayne@me.xyz", "Fox",
@@ -776,322 +762,6 @@
   EXPECT_EQ(u"12345678910", suggestions[0].main_text.value);
 }
 
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-TEST_F(AutofillSuggestionGeneratorTest,
-       CreateSuggestionsFromProfiles_LogProfileSuggestionsMadeWithFormatter) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Hoa", "", "Pham", "hoa.pham@comcast.net", "",
-                       "401 Merrimack St", "", "Lowell", "MA", "01852", "US",
-                       "19786744120");
-
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitAndEnableFeature(
-      features::kAutofillUseImprovedLabelDisambiguation);
-
-  base::HistogramTester histogram_tester;
-  EXPECT_THAT(
-      suggestion_generator()->CreateSuggestionsFromProfiles(
-          {&profile},
-          {NAME_FIRST, NAME_LAST, EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER},
-          /*last_targeted_fields=*/std::nullopt, NAME_FIRST,
-          /*trigger_field_max_length=*/0),
-      ElementsAre(testing::Field(
-          &Suggestion::main_text,
-          Suggestion::Text(u"Hoa", Suggestion::Text::IsPrimary(true)))));
-  histogram_tester.ExpectUniqueSample(
-      "Autofill.ProfileSuggestionsMadeWithFormatter", true, 1);
-}
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-TEST_F(AutofillSuggestionGeneratorTest,
-       CreateSuggestionsFromProfiles_ForContactForm) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Hoa", "", "Pham", "hoa.pham@comcast.net", "",
-                       "401 Merrimack St", "", "Lowell", "MA", "01852", "US",
-                       "19786744120");
-
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitAndEnableFeature(
-      features::kAutofillUseImprovedLabelDisambiguation);
-
-  EXPECT_THAT(
-      suggestion_generator()->CreateSuggestionsFromProfiles(
-          {&profile},
-          {NAME_FIRST, NAME_LAST, EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER},
-          /*last_targeted_fields=*/std::nullopt, NAME_FIRST,
-          /*trigger_field_max_length=*/0),
-      ElementsAre(AllOf(
-          testing::Field(&Suggestion::labels,
-                         ConstructLabelLineMatrix(
-                             {u"(978) 674-4120", u"hoa.pham@comcast.net"})),
-          testing::Field(&Suggestion::icon, kAddressEntryIcon))));
-}
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-TEST_F(AutofillSuggestionGeneratorTest,
-       CreateSuggestionsFromProfiles_AddressForm) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Hoa", "", "Pham", "hoa.pham@comcast.net", "",
-                       "401 Merrimack St", "", "Lowell", "MA", "01852", "US",
-                       "19786744120");
-
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitAndEnableFeature(
-      features::kAutofillUseImprovedLabelDisambiguation);
-
-  EXPECT_THAT(suggestion_generator()->CreateSuggestionsFromProfiles(
-                  {&profile},
-                  {NAME_FULL, ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_CITY,
-                   ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP},
-                  /*last_targeted_fields=*/std::nullopt, NAME_FULL,
-                  /*trigger_field_max_length=*/0),
-              ElementsAre(AllOf(
-                  testing::Field(&Suggestion::labels,
-                                 ConstructLabelLineMatrix(
-                                     {u"401 Merrimack St, Lowell, MA 01852"})),
-                  testing::Field(&Suggestion::icon, kAddressEntryIcon))));
-}
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-TEST_F(AutofillSuggestionGeneratorTest,
-       CreateSuggestionsFromProfiles_AddressPhoneForm) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Hoa", "", "Pham", "hoa.pham@comcast.net", "",
-                       "401 Merrimack St", "", "Lowell", "MA", "01852", "US",
-                       "19786744120");
-
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitAndEnableFeature(
-      features::kAutofillUseImprovedLabelDisambiguation);
-
-  EXPECT_THAT(
-      suggestion_generator()->CreateSuggestionsFromProfiles(
-          {&profile},
-          {NAME_FULL, ADDRESS_HOME_STREET_ADDRESS, PHONE_HOME_WHOLE_NUMBER},
-          /*last_targeted_fields=*/std::nullopt, NAME_FULL,
-          /*trigger_field_max_length=*/0),
-      ElementsAre(
-          AllOf(testing::Field(&Suggestion::labels,
-                               ConstructLabelLineMatrix(
-                                   {u"(978) 674-4120", u"401 Merrimack St"})),
-                testing::Field(&Suggestion::icon, kAddressEntryIcon))));
-}
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-TEST_F(AutofillSuggestionGeneratorTest,
-       CreateSuggestionsFromProfiles_AddressEmailForm) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Hoa", "", "Pham", "hoa.pham@comcast.net", "",
-                       "401 Merrimack St", "", "Lowell", "MA", "01852", "US",
-                       "19786744120");
-
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitAndEnableFeature(
-      features::kAutofillUseImprovedLabelDisambiguation);
-
-  EXPECT_THAT(
-      suggestion_generator()->CreateSuggestionsFromProfiles(
-          {&profile}, {NAME_FULL, ADDRESS_HOME_STREET_ADDRESS, EMAIL_ADDRESS},
-          /*last_targeted_fields=*/std::nullopt, NAME_FULL,
-          /*trigger_field_max_length=*/0),
-      ElementsAre(AllOf(
-          testing::Field(&Suggestion::labels,
-                         ConstructLabelLineMatrix(
-                             {u"401 Merrimack St", u"hoa.pham@comcast.net"})),
-          testing::Field(&Suggestion::icon, kAddressEntryIcon))));
-}
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-TEST_F(AutofillSuggestionGeneratorTest,
-       CreateSuggestionsFromProfiles_FormWithOneProfile) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Hoa", "", "Pham", "hoa.pham@comcast.net", "",
-                       "401 Merrimack St", "", "Lowell", "MA", "01852", "US",
-                       "19786744120");
-
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitAndEnableFeature(
-      features::kAutofillUseImprovedLabelDisambiguation);
-
-  EXPECT_THAT(
-      suggestion_generator()->CreateSuggestionsFromProfiles(
-          {&profile},
-          {NAME_FULL, ADDRESS_HOME_STREET_ADDRESS, EMAIL_ADDRESS,
-           PHONE_HOME_WHOLE_NUMBER},
-          /*last_targeted_fields=*/std::nullopt, NAME_FULL,
-          /*trigger_field_max_length=*/0),
-      ElementsAre(
-          AllOf(testing::Field(&Suggestion::labels,
-                               ConstructLabelLineMatrix({u"401 Merrimack St"})),
-                testing::Field(&Suggestion::icon, kAddressEntryIcon))));
-}
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-TEST_F(AutofillSuggestionGeneratorTest,
-       CreateSuggestionsFromProfiles_AddressContactFormWithProfiles) {
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitWithFeatures(
-      /*enabled_features=*/{features::
-                                kAutofillEnableRankingFormulaAddressProfiles,
-                            features::kAutofillUseImprovedLabelDisambiguation},
-      /*disabled_features=*/{});
-
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Hoa", "", "Pham", "hoa.pham@comcast.net", "",
-                       "401 Merrimack St", "", "Lowell", "MA", "01852", "US",
-                       "19786744120");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Hoa", "", "Pham", "hp@aol.com", "",
-                       "216 Broadway St", "", "Lowell", "MA", "01854", "US",
-                       "19784523366");
-
-  EXPECT_THAT(
-      suggestion_generator()->CreateSuggestionsFromProfiles(
-          {&profile1, &profile2},
-          {NAME_FULL, ADDRESS_HOME_STREET_ADDRESS, EMAIL_ADDRESS,
-           PHONE_HOME_WHOLE_NUMBER},
-          /*last_targeted_fields=*/std::nullopt, NAME_FULL,
-          /*trigger_field_max_length=*/0),
-      ElementsAre(
-          AllOf(testing::Field(&Suggestion::labels,
-                               ConstructLabelLineMatrix(
-                                   {u"401 Merrimack St", u"(978) 674-4120",
-                                    u"hoa.pham@comcast.net"})),
-                testing::Field(&Suggestion::icon, kAddressEntryIcon)),
-          AllOf(testing::Field(&Suggestion::labels,
-                               ConstructLabelLineMatrix({u"216 Broadway St",
-                                                         u"(978) 452-3366",
-                                                         u"hp@aol.com"})),
-                testing::Field(&Suggestion::icon, kAddressEntryIcon))));
-}
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-TEST_F(AutofillSuggestionGeneratorTest,
-       CreateSuggestionsFromProfiles_MobileShowOne) {
-  std::map<std::string, std::string> parameters;
-  parameters[features::kAutofillUseMobileLabelDisambiguationParameterName] =
-      features::kAutofillUseMobileLabelDisambiguationParameterShowOne;
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitAndEnableFeatureWithParameters(
-      features::kAutofillUseMobileLabelDisambiguation, parameters);
-
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Hoa", "", "Pham", "hoa.pham@comcast.net", "",
-                       "401 Merrimack St", "", "Lowell", "MA", "01852", "US",
-                       "19786744120");
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "María", "", "Lòpez", "maria@aol.com", "",
-                       "11 Elkins St", "", "Boston", "MA", "02127", "US",
-                       "6172686862");
-
-  // Tests a form with name, email address, and phone number fields.
-  EXPECT_THAT(
-      suggestion_generator()->CreateSuggestionsFromProfiles(
-          {&profile1, &profile2},
-          {NAME_FIRST, NAME_LAST, EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER},
-          /*last_targeted_fields=*/std::nullopt, EMAIL_ADDRESS,
-          /*trigger_field_max_length=*/0),
-      ElementsAre(
-          AllOf(testing::Field(&Suggestion::labels,
-                               std::vector<std::vector<Suggestion::Text>>{
-                                   {Suggestion::Text(u"(978) 674-4120")}}),
-                testing::Field(&Suggestion::icon, kAddressEntryIcon)),
-          AllOf(testing::Field(&Suggestion::labels,
-                               std::vector<std::vector<Suggestion::Text>>{
-                                   {Suggestion::Text(u"(617) 268-6862")}}),
-                testing::Field(&Suggestion::icon, kAddressEntryIcon))));
-
-  // Tests a form with name, address, phone number, and email address fields.
-  EXPECT_THAT(
-      suggestion_generator()->CreateSuggestionsFromProfiles(
-          {&profile1, &profile2},
-          {NAME_FULL, ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_CITY,
-           EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER},
-          /*last_targeted_fields=*/std::nullopt, EMAIL_ADDRESS,
-          /*trigger_field_max_length=*/0),
-      ElementsAre(
-          AllOf(testing::Field(&Suggestion::labels,
-                               std::vector<std::vector<Suggestion::Text>>{
-                                   {Suggestion::Text(u"401 Merrimack St")}}),
-                testing::Field(&Suggestion::icon, kAddressEntryIcon)),
-          AllOf(testing::Field(&Suggestion::labels,
-                               std::vector<std::vector<Suggestion::Text>>{
-                                   {Suggestion::Text(u"11 Elkins St")}}),
-                testing::Field(&Suggestion::icon, kAddressEntryIcon))));
-}
-#endif  // if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-TEST_F(AutofillSuggestionGeneratorTest,
-       CreateSuggestionsFromProfiles_MobileShowAll) {
-  std::map<std::string, std::string> parameters;
-  parameters[features::kAutofillUseMobileLabelDisambiguationParameterName] =
-      features::kAutofillUseMobileLabelDisambiguationParameterShowAll;
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitAndEnableFeatureWithParameters(
-      features::kAutofillUseMobileLabelDisambiguation, parameters);
-
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Hoa", "", "Pham", "hoa.pham@comcast.net", "",
-                       "401 Merrimack St", "", "Lowell", "MA", "01852", "US",
-                       "19786744120");
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "María", "", "Lòpez", "maria@aol.com", "",
-                       "11 Elkins St", "", "Boston", "MA", "02127", "US",
-                       "6172686862");
-
-  // Tests a form with name, email address, and phone number fields.
-  EXPECT_THAT(
-      suggestion_generator()->CreateSuggestionsFromProfiles(
-          {&profile1, &profile2},
-          {NAME_FIRST, NAME_LAST, EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER},
-          /*last_targeted_fields=*/std::nullopt, EMAIL_ADDRESS,
-          /*trigger_field_max_length=*/0),
-      ElementsAre(
-          AllOf(testing::Field(&Suggestion::labels,
-                               std::vector<std::vector<Suggestion::Text>>{
-                                   {Suggestion::Text(ConstructMobileLabelLine(
-                                       {u"Hoa", u"(978) 674-4120"}))}}),
-                testing::Field(&Suggestion::icon, kAddressEntryIcon)),
-          AllOf(testing::Field(&Suggestion::labels,
-                               std::vector<std::vector<Suggestion::Text>>{
-                                   {Suggestion::Text(ConstructMobileLabelLine(
-                                       {u"María", u"(617) 268-6862"}))}}),
-                testing::Field(&Suggestion::icon, kAddressEntryIcon))));
-
-  // Tests a form with name, address, phone number, and email address fields.
-  EXPECT_THAT(
-      suggestion_generator()->CreateSuggestionsFromProfiles(
-          {&profile1, &profile2},
-          {NAME_FULL, ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_CITY,
-           EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER},
-          /*last_targeted_fields=*/std::nullopt, EMAIL_ADDRESS,
-          /*trigger_field_max_length=*/0),
-      ElementsAre(
-          AllOf(
-              testing::Field(
-                  &Suggestion::labels,
-                  std::vector<std::vector<Suggestion::Text>>{
-                      {Suggestion::Text(ConstructMobileLabelLine(
-                          {u"Hoa", u"401 Merrimack St", u"(978) 674-4120"}))}}),
-              testing::Field(&Suggestion::icon, kAddressEntryIcon)),
-          AllOf(testing::Field(
-                    &Suggestion::labels,
-                    std::vector<std::vector<Suggestion::Text>>{
-                        {Suggestion::Text(ConstructMobileLabelLine(
-                            {u"María", u"11 Elkins St", u"(617) 268-6862"}))}}),
-                testing::Field(&Suggestion::icon, kAddressEntryIcon))));
-}
-#endif  // if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-
 class AutofillChildrenSuggestionGeneratorTest
     : public AutofillSuggestionGeneratorTest {
  public:
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc
index 863793b2..bf31d8f 100644
--- a/components/autofill/core/browser/autofill_test_utils.cc
+++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -746,12 +746,11 @@
 
 void InitializePossibleTypesAndValidities(
     std::vector<FieldTypeSet>& possible_field_types,
-    std::vector<ServerFieldTypeValidityStatesMap>&
-        possible_field_types_validities,
+    std::vector<FieldTypeValidityStatesMap>& possible_field_types_validities,
     const std::vector<FieldType>& possible_types,
     const std::vector<AutofillDataModel::ValidityState>& validity_states) {
   possible_field_types.push_back(FieldTypeSet());
-  possible_field_types_validities.push_back(ServerFieldTypeValidityStatesMap());
+  possible_field_types_validities.push_back(FieldTypeValidityStatesMap());
 
   if (validity_states.empty()) {
     for (const auto& possible_type : possible_types) {
@@ -916,7 +915,7 @@
     return CreateFieldPrediction(type, FieldPrediction::SOURCE_UNSPECIFIED);
   }
   return CreateFieldPrediction(
-      type, GroupTypeOfServerFieldType(type) == FieldTypeGroup::kPasswordField
+      type, GroupTypeOfFieldType(type) == FieldTypeGroup::kPasswordField
                 ? FieldPrediction::SOURCE_PASSWORDS_DEFAULT
                 : FieldPrediction::SOURCE_AUTOFILL_DEFAULT);
 }
diff --git a/components/autofill/core/browser/autofill_test_utils.h b/components/autofill/core/browser/autofill_test_utils.h
index a84dd93..ff99174 100644
--- a/components/autofill/core/browser/autofill_test_utils.h
+++ b/components/autofill/core/browser/autofill_test_utils.h
@@ -290,8 +290,7 @@
 // corresponding |validity_state|.
 void InitializePossibleTypesAndValidities(
     std::vector<FieldTypeSet>& possible_field_types,
-    std::vector<ServerFieldTypeValidityStatesMap>&
-        possible_field_types_validities,
+    std::vector<FieldTypeValidityStatesMap>& possible_field_types_validities,
     const std::vector<FieldType>& possible_type,
     const std::vector<AutofillDataModel::ValidityState>& validity_state = {});
 
diff --git a/components/autofill/core/browser/autofill_type.cc b/components/autofill/core/browser/autofill_type.cc
index 8d2fa82..f138765 100644
--- a/components/autofill/core/browser/autofill_type.cc
+++ b/components/autofill/core/browser/autofill_type.cc
@@ -53,7 +53,7 @@
 AutofillType::AutofillType(HtmlFieldType field_type) : html_type_(field_type) {}
 
 FieldTypeGroup AutofillType::group() const {
-  return server_type_ != UNKNOWN_TYPE ? GroupTypeOfServerFieldType(server_type_)
+  return server_type_ != UNKNOWN_TYPE ? GroupTypeOfFieldType(server_type_)
                                       : GroupTypeOfHtmlFieldType(html_type_);
 }
 
@@ -66,7 +66,7 @@
 FieldType AutofillType::GetStorableType() const {
   return server_type_ != UNKNOWN_TYPE
              ? server_type_
-             : HtmlFieldTypeToBestCorrespondingServerFieldType(html_type_);
+             : HtmlFieldTypeToBestCorrespondingFieldType(html_type_);
 }
 
 std::string AutofillType::ToString() const {
diff --git a/components/autofill/core/browser/autofill_type_unittest.cc b/components/autofill/core/browser/autofill_type_unittest.cc
index 87d4edb3..0289e20 100644
--- a/components/autofill/core/browser/autofill_type_unittest.cc
+++ b/components/autofill/core/browser/autofill_type_unittest.cc
@@ -123,7 +123,7 @@
                    base::to_underlying(HtmlFieldType::kMaxValue)));
 
 TEST_P(AutofillTypeTestForHtmlFieldTypes, GroupsOfHtmlFieldTypes) {
-  if (HtmlFieldTypeToBestCorrespondingServerFieldType(html_field_type()) ==
+  if (HtmlFieldTypeToBestCorrespondingFieldType(html_field_type()) ==
       UNKNOWN_TYPE) {
     return;
   }
@@ -132,7 +132,7 @@
                << "html_field_type=" << FieldTypeToStringView(html_field_type())
                << " "
                << "field_type=" << FieldTypeToStringView(t.GetStorableType()));
-  EXPECT_EQ(t.group(), GroupTypeOfServerFieldType(t.GetStorableType()));
+  EXPECT_EQ(t.group(), GroupTypeOfFieldType(t.GetStorableType()));
 }
 
 }  // namespace
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index 6a1aa3d..13ec081f 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -199,14 +199,14 @@
   const auto& old_types = field->possible_types();
 
   for (FieldType type : old_types) {
-    FieldTypeGroup group = GroupTypeOfServerFieldType(type);
+    FieldTypeGroup group = GroupTypeOfFieldType(type);
     if ((is_credit_card && group == FieldTypeGroup::kCreditCard) ||
         (!is_credit_card && group == FieldTypeGroup::kName)) {
       types_to_keep.insert(type);
     }
   }
 
-  ServerFieldTypeValidityStatesMap new_types_validities;
+  FieldTypeValidityStatesMap new_types_validities;
   // Since the disambiguation takes place when we up to four possible types,
   // here we can add up to three remaining types when only one is removed.
   for (auto type_to_keep : types_to_keep) {
@@ -716,8 +716,7 @@
   FieldType field_type = autofill_field
                              ? autofill_field->Type().GetStorableType()
                              : CREDIT_CARD_NUMBER;
-  DCHECK_EQ(FieldTypeGroup::kCreditCard,
-            GroupTypeOfServerFieldType(field_type));
+  DCHECK_EQ(FieldTypeGroup::kCreditCard, GroupTypeOfFieldType(field_type));
 
   bool should_display_gpay_logo = false;
   auto cards = GetCreditCardSuggestions(field_data, field_type,
@@ -3052,7 +3051,7 @@
 
     if (matching_types.empty()) {
       matching_types.insert(UNKNOWN_TYPE);
-      ServerFieldTypeValidityStateMap matching_types_validities;
+      FieldTypeValidityStateMap matching_types_validities;
       matching_types_validities[UNKNOWN_TYPE] = AutofillDataModel::UNVALIDATED;
       field->add_possible_types_validities(matching_types_validities);
     }
diff --git a/components/autofill/core/browser/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
index 5edc50e..5b72bc5 100644
--- a/components/autofill/core/browser/browser_autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
@@ -1407,84 +1407,30 @@
       features::kAutofillParseAsync};
 };
 
-class SuggestionMatchingTest
-    : public BrowserAutofillManagerTest,
-      public testing::WithParamInterface<std::tuple<bool, std::string, bool>> {
+class SuggestionMatchingTest : public BrowserAutofillManagerTest,
+                               public testing::WithParamInterface<bool> {
  protected:
   void SetUp() override {
     BrowserAutofillManagerTest::SetUp();
     InitializeFeatures();
   }
-
-  bool IsMetadataEnabled() const { return std::get<2>(GetParam()); }
-
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+  bool IsMetadataEnabled() const { return GetParam(); }
   void InitializeFeatures();
-#else
-  void InitializeFeatures();
-#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
 
-  std::string MakeLabel(const std::vector<std::string>& parts);
-  std::string MakeMobileLabel(const std::vector<std::string>& parts);
-
-  enum class EnabledFeature { kNone, kDesktop, kMobileShowAll, kMobileShowOne };
-  EnabledFeature enabled_feature_;
   base::test::ScopedFeatureList features_;
 };
 
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-void SuggestionMatchingTest::InitializeFeatures() {
-  if (std::get<0>(GetParam())) {
-    std::string variant = std::get<1>(GetParam());
-
-    if (variant ==
-        features::kAutofillUseMobileLabelDisambiguationParameterShowAll) {
-      enabled_feature_ = EnabledFeature::kMobileShowAll;
-    } else if (variant ==
-               features::
-                   kAutofillUseMobileLabelDisambiguationParameterShowOne) {
-      enabled_feature_ = EnabledFeature::kMobileShowOne;
-    } else {
-      NOTREACHED();
-    }
-
-    std::map<std::string, std::string> parameters;
-    parameters[features::kAutofillUseMobileLabelDisambiguationParameterName] =
-        variant;
-    features_.InitAndEnableFeatureWithParameters(
-        features::kAutofillUseMobileLabelDisambiguation, parameters);
-  } else {
-    enabled_feature_ = EnabledFeature::kNone;
-  }
-}
+void SuggestionMatchingTest::InitializeFeatures() {}
 #else
 void SuggestionMatchingTest::InitializeFeatures() {
-  bool improved_label_feature = std::get<0>(GetParam());
-
-  enabled_feature_ =
-      improved_label_feature ? EnabledFeature::kDesktop : EnabledFeature::kNone;
-
   features_.InitWithFeatureStates(
-      {{features::kAutofillUseImprovedLabelDisambiguation,
-        improved_label_feature},
-       {features::kAutofillEnableVirtualCardMetadata, IsMetadataEnabled()},
+      {{features::kAutofillEnableVirtualCardMetadata, IsMetadataEnabled()},
        {features::kAutofillEnableCardProductName, IsMetadataEnabled()},
        {features::kAutofillEnableCardArtImage, IsMetadataEnabled()}});
 }
 #endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
 
-std::string SuggestionMatchingTest::MakeLabel(
-    const std::vector<std::string>& parts) {
-  return base::JoinString(
-      parts, l10n_util::GetStringUTF8(IDS_AUTOFILL_SUGGESTION_LABEL_SEPARATOR));
-}
-
-std::string SuggestionMatchingTest::MakeMobileLabel(
-    const std::vector<std::string>& parts) {
-  return base::JoinString(
-      parts, l10n_util::GetStringUTF8(IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR));
-}
-
 // Credit card suggestion tests related with keyboard accessory.
 class CreditCardSuggestionTest : public BrowserAutofillManagerTest {
  protected:
@@ -2013,39 +1959,12 @@
   FormsSeen({form});
 
   GetAutofillSuggestions(form, form.fields[0]);
-
-  std::string label1;
-  std::string label2;
-
-  switch (enabled_feature_) {
-      // 23456789012 is not formatted because it is invalid for the app locale.
-      // It has an extra digit.
-    case EnabledFeature::kDesktop:
-      label1 = MakeLabel(
-          {"123 Apple St., unit 6", "23456789012", "buddy@gmail.com"});
-      label2 = MakeLabel({"3734 Elvis Presley Blvd., Apt. 10", "(234) 567-8901",
-                          "theking@gmail.com"});
-      break;
-    case EnabledFeature::kMobileShowAll:
-      label1 = MakeMobileLabel(
-          {"123 Apple St., unit 6", "23456789012", "buddy@gmail.com"});
-      label2 = MakeMobileLabel({"3734 Elvis Presley Blvd., Apt. 10",
-                                "(234) 567-8901", "theking@gmail.com"});
-      break;
-    case EnabledFeature::kMobileShowOne:
-      label1 = "123 Apple St., unit 6";
-      label2 = "3734 Elvis Presley Blvd., Apt. 10";
-      break;
-    case EnabledFeature::kNone:
-      label1 = "123 Apple St.";
-      label2 = "3734 Elvis Presley Blvd.";
-  }
   // Test that we sent the right values to the external delegate.
   CheckSuggestions(form.fields[0].global_id(),
-                   Suggestion("Charles", label1, kAddressEntryIcon,
+                   Suggestion("Charles", "123 Apple St.", kAddressEntryIcon,
                               PopupItemId::kAddressEntry),
-                   Suggestion("Elvis", label2, kAddressEntryIcon,
-                              PopupItemId::kAddressEntry));
+                   Suggestion("Elvis", "3734 Elvis Presley Blvd.",
+                              kAddressEntryIcon, PopupItemId::kAddressEntry));
 }
 
 // Test that we return only matching address profile suggestions when the
@@ -2058,22 +1977,10 @@
   FormFieldData field = CreateTestFormField("First Name", "firstname", "E",
                                             FormControlType::kInputText);
   GetAutofillSuggestions(form, field);
-
-  std::string label;
-
-  switch (enabled_feature_) {
-    case EnabledFeature::kDesktop:
-    case EnabledFeature::kMobileShowAll:
-    case EnabledFeature::kMobileShowOne:
-      label = "3734 Elvis Presley Blvd., Apt. 10";
-      break;
-    case EnabledFeature::kNone:
-      label = "3734 Elvis Presley Blvd.";
-  }
   // Test that we sent the right values to the external delegate.
   CheckSuggestions(field.global_id(),
-                   Suggestion("Elvis", label, kAddressEntryIcon,
-                              PopupItemId::kAddressEntry));
+                   Suggestion("Elvis", "3734 Elvis Presley Blvd.",
+                              kAddressEntryIcon, PopupItemId::kAddressEntry));
 }
 
 // Tests that we return address profile suggestions values when the section
@@ -2136,22 +2043,10 @@
   FormFieldData field = CreateTestFormField("First Name", "firstname", "E",
                                             FormControlType::kInputText);
   GetAutofillSuggestions(form, field);
-
-  std::string label;
-
-  switch (enabled_feature_) {
-    case EnabledFeature::kDesktop:
-    case EnabledFeature::kMobileShowAll:
-    case EnabledFeature::kMobileShowOne:
-      label = "3734 Elvis Presley Blvd., Apt. 10";
-      break;
-    case EnabledFeature::kNone:
-      label = "3734 Elvis Presley Blvd.";
-  }
   // Test that we sent the right values to the external delegate.
   CheckSuggestions(field.global_id(),
-                   Suggestion("Elvis", label, kAddressEntryIcon,
-                              PopupItemId::kAddressEntry));
+                   Suggestion("Elvis", "3734 Elvis Presley Blvd.",
+                              kAddressEntryIcon, PopupItemId::kAddressEntry));
 }
 
 // Test that we return no suggestions when the form has no relevant fields.
@@ -2188,38 +2083,12 @@
 
   GetAutofillSuggestions(form, form.fields[0]);
 
-  std::string label1;
-  std::string label2;
-
-  switch (enabled_feature_) {
-      // 23456789012 is not formatted because it is invalid for the app locale.
-      // It has an extra digit.
-    case EnabledFeature::kDesktop:
-      label1 = MakeLabel(
-          {"123 Apple St., unit 6", "23456789012", "buddy@gmail.com"});
-      label2 = MakeLabel({"3734 Elvis Presley Blvd., Apt. 10", "(234) 567-8901",
-                          "theking@gmail.com"});
-      break;
-    case EnabledFeature::kMobileShowAll:
-      label1 = MakeMobileLabel(
-          {"123 Apple St., unit 6", "23456789012", "buddy@gmail.com"});
-      label2 = MakeMobileLabel({"3734 Elvis Presley Blvd., Apt. 10",
-                                "(234) 567-8901", "theking@gmail.com"});
-      break;
-    case EnabledFeature::kMobileShowOne:
-      label1 = "123 Apple St., unit 6";
-      label2 = "3734 Elvis Presley Blvd., Apt. 10";
-      break;
-    case EnabledFeature::kNone:
-      label1 = "123 Apple St.";
-      label2 = "3734 Elvis Presley Blvd.";
-  }
   // Test that we sent the right values to the external delegate.
   CheckSuggestions(form.fields[0].global_id(),
-                   Suggestion("Charles", label1, kAddressEntryIcon,
+                   Suggestion("Charles", "123 Apple St.", kAddressEntryIcon,
                               PopupItemId::kAddressEntry),
-                   Suggestion("Elvis", label2, kAddressEntryIcon,
-                              PopupItemId::kAddressEntry));
+                   Suggestion("Elvis", "3734 Elvis Presley Blvd.",
+                              kAddressEntryIcon, PopupItemId::kAddressEntry));
 }
 
 // Test that we return no suggestions when autofill is disabled.
@@ -2809,40 +2678,12 @@
   FormsSeen({form});
 
   GetAutofillSuggestions(form, form.fields[0]);
-
-  std::string label1;
-  std::string label2;
-
-  switch (enabled_feature_) {
-    case EnabledFeature::kDesktop:
-      // 23456789012 is not formatted because it is invalid for the app locale.
-      // It has an extra digit.
-      label1 = MakeLabel(
-          {"123 Apple St., unit 6", "23456789012", "buddy@gmail.com"});
-      label2 = MakeLabel({"3734 Elvis Presley Blvd., Apt. 10", "(234) 567-8901",
-                          "theking@gmail.com"});
-      break;
-    case EnabledFeature::kMobileShowAll:
-      label1 = MakeMobileLabel(
-          {"123 Apple St., unit 6", "23456789012", "buddy@gmail.com"});
-      label2 = MakeMobileLabel({"3734 Elvis Presley Blvd., Apt. 10",
-                                "(234) 567-8901", "theking@gmail.com"});
-      break;
-    case EnabledFeature::kMobileShowOne:
-      label1 = "123 Apple St., unit 6";
-      label2 = "3734 Elvis Presley Blvd., Apt. 10";
-      break;
-    case EnabledFeature::kNone:
-      label1 = "123 Apple St.";
-      label2 = "3734 Elvis Presley Blvd.";
-  }
-
   // Test that we sent the right values to the external delegate.
   CheckSuggestions(form.fields[0].global_id(),
-                   Suggestion("Charles", label1, kAddressEntryIcon,
+                   Suggestion("Charles", "123 Apple St.", kAddressEntryIcon,
                               PopupItemId::kAddressEntry),
-                   Suggestion("Elvis", label2, kAddressEntryIcon,
-                              PopupItemId::kAddressEntry));
+                   Suggestion("Elvis", "3734 Elvis Presley Blvd.",
+                              kAddressEntryIcon, PopupItemId::kAddressEntry));
 
   FormFieldData field = CreateTestFormField("Card Number", "cardnumber", "",
                                             FormControlType::kInputText);
@@ -3556,39 +3397,12 @@
   // Mark one of the fields as filled.
   form.fields[2].is_autofilled = true;
   GetAutofillSuggestions(form, form.fields[0]);
-
-  std::string label1;
-  std::string label2;
-
-  switch (enabled_feature_) {
-      // 23456789012 is not formatted because it is invalid for the app locale.
-      // It has an extra digit.
-    case EnabledFeature::kDesktop:
-      label1 = MakeLabel(
-          {"123 Apple St., unit 6", "23456789012", "buddy@gmail.com"});
-      label2 = MakeLabel({"3734 Elvis Presley Blvd., Apt. 10", "(234) 567-8901",
-                          "theking@gmail.com"});
-      break;
-    case EnabledFeature::kMobileShowAll:
-      label1 = MakeMobileLabel(
-          {"123 Apple St., unit 6", "23456789012", "buddy@gmail.com"});
-      label2 = MakeMobileLabel({"3734 Elvis Presley Blvd., Apt. 10",
-                                "(234) 567-8901", "theking@gmail.com"});
-      break;
-    case EnabledFeature::kMobileShowOne:
-      label1 = "123 Apple St., unit 6";
-      label2 = "3734 Elvis Presley Blvd., Apt. 10";
-      break;
-    case EnabledFeature::kNone:
-      label1 = "123 Apple St.";
-      label2 = "3734 Elvis Presley Blvd.";
-  }
   // Test that we sent the right values to the external delegate.
   CheckSuggestions(form.fields[0].global_id(),
-                   Suggestion("Charles", label1, kAddressEntryIcon,
+                   Suggestion("Charles", "123 Apple St.", kAddressEntryIcon,
                               PopupItemId::kAddressEntry),
-                   Suggestion("Elvis", label2, kAddressEntryIcon,
-                              PopupItemId::kAddressEntry));
+                   Suggestion("Elvis", "3734 Elvis Presley Blvd.",
+                              kAddressEntryIcon, PopupItemId::kAddressEntry));
 }
 
 // The method `AutofillSuggestionGenerator::GetPrefixMatchedProfiles` prevents
@@ -3613,22 +3427,10 @@
   field.is_autofilled = true;
   field.value = u"Elvis";
   GetAutofillSuggestions(form, field);
-
-  std::string label;
-
-  switch (enabled_feature_) {
-    case EnabledFeature::kDesktop:
-    case EnabledFeature::kMobileShowAll:
-    case EnabledFeature::kMobileShowOne:
-      label = "3734 Elvis Presley Blvd., Apt. 10";
-      break;
-    case EnabledFeature::kNone:
-      label = "3734 Elvis Presley Blvd.";
-  }
   // Test that we sent the right values to the external delegate.
   CheckSuggestions(field.global_id(),
-                   Suggestion("Elvis", label, kAddressEntryIcon,
-                              PopupItemId::kAddressEntry));
+                   Suggestion("Elvis", "3734 Elvis Presley Blvd.",
+                              kAddressEntryIcon, PopupItemId::kAddressEntry));
 }
 #endif
 
@@ -10429,19 +10231,9 @@
 INSTANTIATE_TEST_SUITE_P(All, OnFocusOnFormFieldTest, testing::Bool());
 
 #if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
-INSTANTIATE_TEST_SUITE_P(
-    ,
-    SuggestionMatchingTest,
-    testing::Values(std::make_tuple(0, "", false),
-                    std::make_tuple(1, "show-all", false),
-                    std::make_tuple(1, "show-one", false)));
+INSTANTIATE_TEST_SUITE_P(, SuggestionMatchingTest, testing::Values(false));
 #else
-INSTANTIATE_TEST_SUITE_P(All,
-                         SuggestionMatchingTest,
-                         testing::Values(std::make_tuple(0, "", false),
-                                         std::make_tuple(0, "", true),
-                                         std::make_tuple(1, "", false),
-                                         std::make_tuple(1, "", true)));
+INSTANTIATE_TEST_SUITE_P(All, SuggestionMatchingTest, testing::Bool());
 #endif  // BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
 
 struct ShareNicknameTestParam {
diff --git a/components/autofill/core/browser/data_model/address.cc b/components/autofill/core/browser/data_model/address.cc
index 255877da..33113c01 100644
--- a/components/autofill/core/browser/data_model/address.cc
+++ b/components/autofill/core/browser/data_model/address.cc
@@ -110,7 +110,7 @@
 }
 
 std::u16string Address::GetRawInfo(FieldType type) const {
-  DCHECK_EQ(FieldTypeGroup::kAddress, GroupTypeOfServerFieldType(type));
+  DCHECK_EQ(FieldTypeGroup::kAddress, GroupTypeOfFieldType(type));
 
   return structured_address_->GetValueForType(type);
 }
@@ -118,7 +118,7 @@
 void Address::SetRawInfoWithVerificationStatus(FieldType type,
                                                const std::u16string& value,
                                                VerificationStatus status) {
-  DCHECK_EQ(FieldTypeGroup::kAddress, GroupTypeOfServerFieldType(type));
+  DCHECK_EQ(FieldTypeGroup::kAddress, GroupTypeOfFieldType(type));
   // The street address has a structure that may have already been set before
   // using the settings dialog. In case the settings dialog was used to change
   // the address to contain different tokens, the structure must be reset.
diff --git a/components/autofill/core/browser/data_model/autofill_feature_guarded_address_component.cc b/components/autofill/core/browser/data_model/autofill_feature_guarded_address_component.cc
new file mode 100644
index 0000000..a10b82a
--- /dev/null
+++ b/components/autofill/core/browser/data_model/autofill_feature_guarded_address_component.cc
@@ -0,0 +1,36 @@
+// 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.
+
+#include "components/autofill/core/browser/data_model/autofill_feature_guarded_address_component.h"
+
+#include "base/feature_list.h"
+
+namespace autofill {
+
+FeatureGuardedAddressComponent::FeatureGuardedAddressComponent(
+    raw_ptr<const base::Feature> feature,
+    FieldType storage_type,
+    SubcomponentsList children,
+    unsigned int merge_mode)
+    : AddressComponent(storage_type, std::move(children), merge_mode),
+      feature_(feature) {}
+
+void FeatureGuardedAddressComponent::SetValue(std::u16string value,
+                                              VerificationStatus status) {
+  if (!base::FeatureList::IsEnabled(*feature_)) {
+    return;
+  }
+  AddressComponent::SetValue(value, status);
+}
+
+void FeatureGuardedAddressComponent::GetTypes(
+    bool storable_only,
+    FieldTypeSet* supported_types) const {
+  if (!base::FeatureList::IsEnabled(*feature_)) {
+    return;
+  }
+  AddressComponent::GetTypes(storable_only, supported_types);
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/data_model/autofill_feature_guarded_address_component.h b/components/autofill/core/browser/data_model/autofill_feature_guarded_address_component.h
new file mode 100644
index 0000000..54c8a56
--- /dev/null
+++ b/components/autofill/core/browser/data_model/autofill_feature_guarded_address_component.h
@@ -0,0 +1,36 @@
+// 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 COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_AUTOFILL_FEATURE_GUARDED_ADDRESS_COMPONENT_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_AUTOFILL_FEATURE_GUARDED_ADDRESS_COMPONENT_H_
+
+#include "base/feature_list.h"
+#include "components/autofill/core/browser/data_model/autofill_structured_address_component.h"
+#include "components/autofill/core/browser/field_types.h"
+
+namespace autofill {
+
+// This class represents a type that is controlled by a feature flag. It
+// overrides the SetValue method to prevent setting values to nodes for which
+// the flag is turned off. It further prevents exposing disabled types as
+// supported.
+class FeatureGuardedAddressComponent : public AddressComponent {
+ public:
+  FeatureGuardedAddressComponent(raw_ptr<const base::Feature> feature,
+                                 FieldType storage_type,
+                                 SubcomponentsList children,
+                                 unsigned int merge_mode);
+
+  // AddressComponent overrides:
+  void SetValue(std::u16string value, VerificationStatus status) override;
+  void GetTypes(bool storable_only,
+                FieldTypeSet* supported_types) const override;
+
+ private:
+  // Feature guarding the rollout of this address component.
+  const raw_ptr<const base::Feature> feature_;
+};
+
+}  // namespace autofill
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_AUTOFILL_FEATURE_GUARDED_ADDRESS_COMPONENT_H_
diff --git a/components/autofill/core/browser/data_model/autofill_i18n_api.cc b/components/autofill/core/browser/data_model/autofill_i18n_api.cc
index 2a5c348..5c14a9a 100644
--- a/components/autofill/core/browser/data_model/autofill_i18n_api.cc
+++ b/components/autofill/core/browser/data_model/autofill_i18n_api.cc
@@ -232,7 +232,7 @@
 std::u16string GetFormattingExpression(FieldType field_type,
                                        AddressCountryCode country_code) {
   if (base::FeatureList::IsEnabled(features::kAutofillUseI18nAddressModel) &&
-      GroupTypeOfServerFieldType(field_type) == FieldTypeGroup::kAddress) {
+      GroupTypeOfFieldType(field_type) == FieldTypeGroup::kAddress) {
     // If `country_code` is specified, return the corresponding formatting
     // expression if they exist. Note that it should not fallback to a legacy
     // expression, as these ones refer to a different hierarchy.
@@ -262,7 +262,7 @@
     std::string_view value,
     FieldType field_type,
     AddressCountryCode country_code) {
-  CHECK(GroupTypeOfServerFieldType(field_type) == FieldTypeGroup::kAddress);
+  CHECK(GroupTypeOfFieldType(field_type) == FieldTypeGroup::kAddress);
   // If `country_code` is specified, attempt to parse the `value` using a
   // custom parsing structure (if exist).
   // Otherwise try using a legacy parsing expression (if exist).
diff --git a/components/autofill/core/browser/data_model/autofill_profile.cc b/components/autofill/core/browser/data_model/autofill_profile.cc
index 60187f8..1a66ee4 100644
--- a/components/autofill/core/browser/data_model/autofill_profile.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile.cc
@@ -88,11 +88,11 @@
 // similarly.
 FieldType GetStorableTypeCollapsingGroups(FieldType type) {
   FieldType storable_type = AutofillType(type).GetStorableType();
-  if (GroupTypeOfServerFieldType(storable_type) == FieldTypeGroup::kName) {
+  if (GroupTypeOfFieldType(storable_type) == FieldTypeGroup::kName) {
     return NAME_FULL;
   }
 
-  if (GroupTypeOfServerFieldType(storable_type) == FieldTypeGroup::kPhone) {
+  if (GroupTypeOfFieldType(storable_type) == FieldTypeGroup::kPhone) {
     return PHONE_HOME_WHOLE_NUMBER;
   }
 
@@ -463,7 +463,7 @@
 }
 
 FieldType AutofillProfile::GetStorableTypeOf(FieldType type) const {
-  const FieldTypeGroup group = GroupTypeOfServerFieldType(type);
+  const FieldTypeGroup group = GroupTypeOfFieldType(type);
   if (group == FieldTypeGroup::kAddress) {
     return address_.GetStructuredAddress().GetStorableTypeOf(type).value_or(
         type);
@@ -1211,13 +1211,8 @@
 
 bool AutofillProfile::FinalizeAfterImport() {
   bool success = true;
-  if (!name_.FinalizeAfterImport()) {
-    success = false;
-  }
-  if (!address_.FinalizeAfterImport()) {
-    success = false;
-  }
-
+  success &= name_.FinalizeAfterImport();
+  success &= address_.FinalizeAfterImport();
   return success;
 }
 
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address.cc b/components/autofill/core/browser/data_model/autofill_structured_address.cc
index 125ca19..119453a 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address.cc
@@ -33,31 +33,6 @@
                              /*keep_white_space=*/true);
 }
 
-FeatureGuardedAddressComponent::FeatureGuardedAddressComponent(
-    raw_ptr<const base::Feature> feature,
-    FieldType storage_type,
-    SubcomponentsList children,
-    unsigned int merge_mode)
-    : AddressComponent(storage_type, std::move(children), merge_mode),
-      feature_(feature) {}
-
-void FeatureGuardedAddressComponent::SetValue(std::u16string value,
-                                              VerificationStatus status) {
-  if (!base::FeatureList::IsEnabled(*feature_)) {
-    return;
-  }
-  AddressComponent::SetValue(value, status);
-}
-
-void FeatureGuardedAddressComponent::GetTypes(
-    bool storable_only,
-    FieldTypeSet* supported_types) const {
-  if (!base::FeatureList::IsEnabled(*feature_)) {
-    return;
-  }
-  AddressComponent::GetTypes(storable_only, supported_types);
-}
-
 StreetNameNode::StreetNameNode(SubcomponentsList children)
     : AddressComponent(ADDRESS_HOME_STREET_NAME,
                        std::move(children),
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address.h b/components/autofill/core/browser/data_model/autofill_structured_address.h
index 808b97a0..cbfddd7 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address.h
+++ b/components/autofill/core/browser/data_model/autofill_structured_address.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/feature_list.h"
+#include "components/autofill/core/browser/data_model/autofill_feature_guarded_address_component.h"
 #include "components/autofill/core/browser/data_model/autofill_structured_address_component.h"
 #include "components/autofill/core/browser/field_types.h"
 
@@ -28,27 +29,6 @@
       const AddressComponent& other) const override;
 };
 
-// This class represents a type that is controlled by a feature flag. It
-// overrides the SetValue method to prevent setting values to nodes for which
-// the flag is turned off. It further prevents exposing disabled types as
-// supported.
-class FeatureGuardedAddressComponent : public AddressComponent {
- public:
-  FeatureGuardedAddressComponent(raw_ptr<const base::Feature> feature,
-                                 FieldType storage_type,
-                                 SubcomponentsList children,
-                                 unsigned int merge_mode);
-
-  // AddressComponent overrides:
-  void SetValue(std::u16string value, VerificationStatus status) override;
-  void GetTypes(bool storable_only,
-                FieldTypeSet* supported_types) const override;
-
- private:
-  // Feature guarding the rollout of this address component.
-  const raw_ptr<const base::Feature> feature_;
-};
-
 // The name of the street.
 class StreetNameNode : public AddressComponent {
  public:
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address_component.cc b/components/autofill/core/browser/data_model/autofill_structured_address_component.cc
index 0218ba6..3028fc2 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address_component.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_component.cc
@@ -462,8 +462,7 @@
 
   bool parsing_successful =
       base::FeatureList::IsEnabled(features::kAutofillUseI18nAddressModel) &&
-              GroupTypeOfServerFieldType(GetStorageType()) ==
-                  FieldTypeGroup::kAddress
+              GroupTypeOfFieldType(GetStorageType()) == FieldTypeGroup::kAddress
           ? ParseValueAndAssignSubcomponentsByI18nParsingRules()
           : ParseValueAndAssignSubcomponentsByRegularExpressions();
 
@@ -505,8 +504,7 @@
 void AddressComponent::
     TryParseValueAndAssignSubcomponentsRespectingSetValues() {
   if (base::FeatureList::IsEnabled(features::kAutofillUseI18nAddressModel) &&
-      GroupTypeOfServerFieldType(GetStorageType()) ==
-          FieldTypeGroup::kAddress) {
+      GroupTypeOfFieldType(GetStorageType()) == FieldTypeGroup::kAddress) {
     const AddressCountryCode country_code = AddressCountryCode(
         base::UTF16ToUTF8(GetRootNode().GetValueForType(ADDRESS_HOME_COUNTRY)));
 
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc b/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc
index a2dd71e..0eb50fa 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc
@@ -1801,6 +1801,9 @@
 }
 
 TEST(AutofillStructuredAddressAddressComponent, TestFillTreeGaps) {
+  base::test::ScopedFeatureList scoped_feature;
+  scoped_feature.InitAndEnableFeature(
+      features::kAutofillEnableSupportForHonorificPrefixes);
   NameFullWithPrefix name;
 
   AddressComponentTestValues name_filled_values = {
@@ -1894,6 +1897,9 @@
 }
 
 TEST(AutofillStructuredAddressAddressComponent, TestFillTreeGapsParsing) {
+  base::test::ScopedFeatureList scoped_feature;
+  scoped_feature.InitAndEnableFeature(
+      features::kAutofillEnableSupportForHonorificPrefixes);
   NameFullWithPrefix name;
 
   AddressComponentTestValues name_filled_values = {
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address_name.cc b/components/autofill/core/browser/data_model/autofill_structured_address_name.cc
index 0d33891..6d30f0d6 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address_name.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_name.cc
@@ -18,6 +18,7 @@
 #include "components/autofill/core/browser/data_model/autofill_structured_address_regex_provider.h"
 #include "components/autofill/core/browser/data_model/autofill_structured_address_utils.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/common/autofill_features.h"
 
 namespace autofill {
 
@@ -123,11 +124,12 @@
 }
 
 NameHonorificPrefix::NameHonorificPrefix()
-    : AddressComponent(NAME_HONORIFIC_PREFIX,
-                       {},
-                       MergeMode::kUseBetterOrNewerForSameValue |
-                           MergeMode::kReplaceEmpty |
-                           MergeMode::kUseBetterOrMostRecentIfDifferent) {}
+    : FeatureGuardedAddressComponent(
+          &features::kAutofillEnableSupportForHonorificPrefixes,
+          NAME_HONORIFIC_PREFIX,
+          {},
+          MergeMode::kUseBetterOrNewerForSameValue | MergeMode::kReplaceEmpty |
+              MergeMode::kUseBetterOrMostRecentIfDifferent) {}
 
 NameHonorificPrefix::~NameHonorificPrefix() = default;
 
@@ -229,9 +231,11 @@
 NameFull::~NameFull() = default;
 
 NameFullWithPrefix::NameFullWithPrefix()
-    : AddressComponent(NAME_FULL_WITH_HONORIFIC_PREFIX,
-                       {},
-                       MergeMode::kMergeChildrenAndReformatIfNeeded) {
+    : FeatureGuardedAddressComponent(
+          &features::kAutofillEnableSupportForHonorificPrefixes,
+          NAME_FULL_WITH_HONORIFIC_PREFIX,
+          {},
+          MergeMode::kMergeChildrenAndReformatIfNeeded) {
   RegisterChildNode(std::make_unique<NameHonorificPrefix>());
   RegisterChildNode(std::make_unique<NameFull>());
 }
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address_name.h b/components/autofill/core/browser/data_model/autofill_structured_address_name.h
index c4b7061..ea65f50 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address_name.h
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_name.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "components/autofill/core/browser/data_model/autofill_feature_guarded_address_component.h"
 #include "components/autofill/core/browser/data_model/autofill_structured_address_component.h"
 #include "components/autofill/core/browser/field_types.h"
 
@@ -132,7 +133,9 @@
 };
 
 // Atomic component that represents a honorific prefix.
-class NameHonorificPrefix : public AddressComponent {
+// Without the second generation of the structured name tree, honorific
+// prefixes and the name including the prefix are unsupported types.
+class NameHonorificPrefix : public FeatureGuardedAddressComponent {
  public:
   NameHonorificPrefix();
   ~NameHonorificPrefix() override;
@@ -164,7 +167,9 @@
 //                                   | _FIRST | | _CONJUNCTION | | _SECOND |
 //                                   +--------+ +--------------+ +---------+
 //
-class NameFullWithPrefix : public AddressComponent {
+// Without the second generation of the structured name tree, honorific
+// prefixes and the name including the prefix are unsupported types.
+class NameFullWithPrefix : public FeatureGuardedAddressComponent {
  public:
   NameFullWithPrefix();
   NameFullWithPrefix(const NameFullWithPrefix& other);
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address_name_unittest.cc b/components/autofill/core/browser/data_model/autofill_structured_address_name_unittest.cc
index 2e42f2d..c4a9f63 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address_name_unittest.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_name_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "components/autofill/core/browser/data_model/autofill_structured_address_test_utils.h"
 #include "components/autofill/core/browser/data_model/autofill_structured_address_utils.h"
 #include "components/autofill/core/common/autofill_features.h"
@@ -104,11 +105,20 @@
 
 }  // namespace
 
+class AutofillStructuredName : public testing::Test {
+ public:
+  AutofillStructuredName() = default;
+
+ private:
+  base::test::ScopedFeatureList features_{
+      features::kAutofillEnableSupportForHonorificPrefixes};
+};
+
 // Tests the parsing of last names into their tree components:
 // * The first part, that is only used in Latinx/Hispanic names.
 // * The conjunction, that is optional in Latinx/Hispanic names.
 // * The second part, for Latinx/Hispanic and all other last names.
-TEST(AutofillStructuredName, ParseLastName) {
+TEST_F(AutofillStructuredName, ParseLastName) {
   LastNameParserTestRecord last_name_tests[] = {
       // "von" is a known prefix for a surname and should be therefore parsed
       // into the second last name
@@ -134,7 +144,7 @@
 }
 
 // Tests the parsing of full names into their subcomponents.
-TEST(AutofillStructuredName, ParseFullName) {
+TEST_F(AutofillStructuredName, ParseFullName) {
   NameParserTestRecord name_tests[] = {
       // Name starting with a last name, followed by a comma and the first and
       // middle name.
@@ -251,7 +261,7 @@
 }
 
 // Tests the detection of CJK name characteristics.
-TEST(AutofillStructuredName, HasCjkNameCharacteristics) {
+TEST_F(AutofillStructuredName, HasCjkNameCharacteristics) {
   EXPECT_FALSE(HasCjkNameCharacteristics("Peterson"));
   EXPECT_TRUE(HasCjkNameCharacteristics("ㅎ"));
   EXPECT_TRUE(HasCjkNameCharacteristics("房仕龙"));
@@ -286,14 +296,14 @@
 }
 
 // Test the detection of Hispanic/Latinx name characteristics.
-TEST(AutofillStructuredName, HasHispanicLatinxNameCharacteristics) {
+TEST_F(AutofillStructuredName, HasHispanicLatinxNameCharacteristics) {
   EXPECT_TRUE(HasHispanicLatinxNameCharacteristics("Pablo Ruiz Picasso"));
   EXPECT_FALSE(HasHispanicLatinxNameCharacteristics("Werner Heisenberg"));
   EXPECT_TRUE(HasHispanicLatinxNameCharacteristics("SomeName y SomeOtherName"));
 }
 
 // Test the detection of middle name initials.
-TEST(AutofillStructuredName, HasMiddleNameInitialsCharacteristics) {
+TEST_F(AutofillStructuredName, HasMiddleNameInitialsCharacteristics) {
   EXPECT_FALSE(HasMiddleNameInitialsCharacteristics("Diego"));
   EXPECT_FALSE(HasMiddleNameInitialsCharacteristics("d"));
   EXPECT_TRUE(HasMiddleNameInitialsCharacteristics("D"));
@@ -305,7 +315,7 @@
 }
 
 // Test the reduction of a name to its initials.
-TEST(AutofillStructuredName, ReduceToInitials) {
+TEST_F(AutofillStructuredName, ReduceToInitials) {
   EXPECT_EQ(ReduceToInitials(u""), u"");
   EXPECT_EQ(ReduceToInitials(u"George"), u"G");
   EXPECT_EQ(ReduceToInitials(u"George Walker"), u"GW");
@@ -314,7 +324,7 @@
 }
 
 // Test getting the field type |NAME_MIDDLE_INITIAL|.
-TEST(AutofillStructuredName, GetNameMiddleInitial) {
+TEST_F(AutofillStructuredName, GetNameMiddleInitial) {
   NameFull full_name;
 
   full_name.SetValueForType(NAME_MIDDLE, u"Michael",
@@ -345,7 +355,7 @@
   EXPECT_EQ(full_name.GetValueForType(NAME_MIDDLE_INITIAL), u"G.-W.");
 }
 
-TEST(AutofillStructuredName, TestGetSupportedTypes_FullNameWithPrefix) {
+TEST_F(AutofillStructuredName, TestGetSupportedTypes_FullNameWithPrefix) {
   NameFullWithPrefix full_name_with_prefix;
   FieldTypeSet supported_types;
   full_name_with_prefix.GetSupportedTypes(&supported_types);
@@ -356,7 +366,7 @@
             supported_types);
 }
 
-TEST(AutofillStructuredName, TestGetSupportedTypes_FullName) {
+TEST_F(AutofillStructuredName, TestGetSupportedTypes_FullName) {
   NameFull full_name;
   FieldTypeSet supported_types;
   full_name.GetSupportedTypes(&supported_types);
@@ -366,7 +376,7 @@
             supported_types);
 }
 
-TEST(AutofillStructuredName, TestSettingMiddleNameInitial) {
+TEST_F(AutofillStructuredName, TestSettingMiddleNameInitial) {
   NameFullWithPrefix full_name_with_prefix;
   EXPECT_EQ(full_name_with_prefix.GetValueForType(NAME_MIDDLE),
             std::u16string());
@@ -377,7 +387,7 @@
   EXPECT_EQ(full_name_with_prefix.GetValueForType(NAME_MIDDLE), u"M");
 }
 
-TEST(AutofillStructuredName, MergePermutedNames) {
+TEST_F(AutofillStructuredName, MergePermutedNames) {
   NameFull one;
   NameFull two;
 
@@ -421,8 +431,8 @@
             VerificationStatus::kObserved);
 }
 
-TEST(AutofillStructuredName,
-     MergeNamesByCombiningSubstructureObservations_WithAdditionalPrefix) {
+TEST_F(AutofillStructuredName,
+       MergeNamesByCombiningSubstructureObservations_WithAdditionalPrefix) {
   NameFullWithPrefix one;
   NameFullWithPrefix two;
 
@@ -529,7 +539,7 @@
 
 // Tests that the root node of NameFullWithPrefix is correctly populated after a
 // migration from a NameFull structure.
-TEST(AutofillStructuredName, TestPopulationOfNameFullWithPrefix) {
+TEST_F(AutofillStructuredName, TestPopulationOfNameFullWithPrefix) {
   NameFullWithPrefix name_full_with_prefix;
 
   // The first name has an incorrect componentization of the last name, but a
@@ -592,8 +602,8 @@
   VerifyTestValues(&name_full_with_prefix, expectation);
 }
 
-TEST(AutofillStructuredName,
-     MergeNamesByCombiningSubstructureObservations_FullName) {
+TEST_F(AutofillStructuredName,
+       MergeNamesByCombiningSubstructureObservations_FullName) {
   NameFull one;
   NameFull two;
 
@@ -683,8 +693,8 @@
   VerifyTestValues(&two, merge_expectation);
 }
 
-TEST(AutofillStructuredName,
-     MergeNamesByCombiningSubstructureObservations_FullNameWithPrefix) {
+TEST_F(AutofillStructuredName,
+       MergeNamesByCombiningSubstructureObservations_FullNameWithPrefix) {
   NameFullWithPrefix one;
   NameFullWithPrefix two;
 
@@ -793,7 +803,7 @@
   VerifyTestValues(&two, merge_expectation);
 }
 
-TEST(AutofillStructuredName, TestCopyConstructor) {
+TEST_F(AutofillStructuredName, TestCopyConstructor) {
   NameFull original;
   // The first name has an incorrect componentization of the last name, but
   // a correctly observed structure of title, first, middle, last.
@@ -813,8 +823,8 @@
   EXPECT_TRUE(original.SameAs(copy));
 }
 
-TEST(AutofillStructuredName,
-     MigrationFromLegacyStructure_WithFullName_Unverified) {
+TEST_F(AutofillStructuredName,
+       MigrationFromLegacyStructure_WithFullName_Unverified) {
   NameFull name;
   name.SetValueForType(NAME_FULL, u"Thomas Neo Anderson",
                        VerificationStatus::kNoStatus);
@@ -844,7 +854,7 @@
             VerificationStatus::kParsed);
 }
 
-TEST(AutofillStructuredName, MigrationFromLegacyStructure_WithoutFullName) {
+TEST_F(AutofillStructuredName, MigrationFromLegacyStructure_WithoutFullName) {
   NameFull name;
   // The first name has an incorrect componentization of the last name, but
   // a correctly observed structure of title, first, middle, last.
@@ -874,7 +884,7 @@
             VerificationStatus::kObserved);
 }
 
-TEST(AutofillStructuredName, MergeSubsetLastname) {
+TEST_F(AutofillStructuredName, MergeSubsetLastname) {
   NameFull name;
   NameFull subset_name;
   name.SetMergeModeForTesting(kRecursivelyMergeSingleTokenSubset |
@@ -934,7 +944,7 @@
   VerifyTestValues(&name, name_values);
 }
 
-TEST(AutofillStructuredName, MergeSubsetLastname_WithNonSpaceSeparators) {
+TEST_F(AutofillStructuredName, MergeSubsetLastname_WithNonSpaceSeparators) {
   NameFull name;
   NameFull subset_name;
   name.SetMergeModeForTesting(kRecursivelyMergeSingleTokenSubset |
@@ -1004,7 +1014,7 @@
   VerifyTestValues(&name, expectation);
 }
 
-TEST(AutofillStructuredName, MergeSubsetLastname2) {
+TEST_F(AutofillStructuredName, MergeSubsetLastname2) {
   NameFullWithPrefix name;
   NameFullWithPrefix subset_name;
   name.SetMergeModeForTesting(kRecursivelyMergeSingleTokenSubset |
diff --git a/components/autofill/core/browser/data_model/birthdate.cc b/components/autofill/core/browser/data_model/birthdate.cc
index 35dfe7e..dcb463e 100644
--- a/components/autofill/core/browser/data_model/birthdate.cc
+++ b/components/autofill/core/browser/data_model/birthdate.cc
@@ -16,7 +16,7 @@
 }
 
 std::u16string Birthdate::GetRawInfo(FieldType type) const {
-  DCHECK_EQ(GroupTypeOfServerFieldType(type), FieldTypeGroup::kBirthdateField);
+  DCHECK_EQ(GroupTypeOfFieldType(type), FieldTypeGroup::kBirthdateField);
 
   switch (type) {
     case BIRTHDATE_DAY:
@@ -48,7 +48,7 @@
 void Birthdate::SetRawInfoWithVerificationStatus(FieldType type,
                                                  const std::u16string& value,
                                                  VerificationStatus status) {
-  DCHECK_EQ(GroupTypeOfServerFieldType(type), FieldTypeGroup::kBirthdateField);
+  DCHECK_EQ(GroupTypeOfFieldType(type), FieldTypeGroup::kBirthdateField);
 
   switch (type) {
     case BIRTHDATE_DAY:
diff --git a/components/autofill/core/browser/data_model/contact_info.cc b/components/autofill/core/browser/data_model/contact_info.cc
index acef678c..646ecb97 100644
--- a/components/autofill/core/browser/data_model/contact_info.cc
+++ b/components/autofill/core/browser/data_model/contact_info.cc
@@ -78,7 +78,7 @@
 }
 
 std::u16string NameInfo::GetRawInfo(FieldType type) const {
-  DCHECK_EQ(FieldTypeGroup::kName, GroupTypeOfServerFieldType(type));
+  DCHECK_EQ(FieldTypeGroup::kName, GroupTypeOfFieldType(type));
 
   // TODO(crbug.com/1141460): Remove once honorific prefixes are launched.
   if (type == NAME_FULL_WITH_HONORIFIC_PREFIX && !HonorificPrefixEnabled()) {
@@ -96,16 +96,8 @@
 void NameInfo::SetRawInfoWithVerificationStatus(FieldType type,
                                                 const std::u16string& value,
                                                 VerificationStatus status) {
-  DCHECK_EQ(FieldTypeGroup::kName, GroupTypeOfServerFieldType(type));
-  // Without the second generation of the structured name tree, honorific
-  // prefixes and the name including the prefix are unsupported types.
-  if ((type == NAME_HONORIFIC_PREFIX ||
-       type == NAME_FULL_WITH_HONORIFIC_PREFIX) &&
-      !HonorificPrefixEnabled()) {
-    return;
-  }
-  bool success = name_->SetValueForType(type, value, status);
-  DCHECK(success) << FieldTypeToStringView(type);
+  DCHECK_EQ(FieldTypeGroup::kName, GroupTypeOfFieldType(type));
+  name_->SetValueForType(type, value, status);
 }
 
 void NameInfo::GetSupportedTypes(FieldTypeSet* supported_types) const {
@@ -150,14 +142,7 @@
 }
 
 VerificationStatus NameInfo::GetVerificationStatusImpl(FieldType type) const {
-  // Without the second generation of the structured name tree, honorific
-  // prefixes and the name including the prefix are unsupported types.
-  if (!((type == NAME_HONORIFIC_PREFIX ||
-         type == NAME_FULL_WITH_HONORIFIC_PREFIX) &&
-        !HonorificPrefixEnabled())) {
-    return name_->GetVerificationStatusForType(type);
-  }
-  return VerificationStatus::kNoStatus;
+  return name_->GetVerificationStatusForType(type);
 }
 
 EmailInfo::EmailInfo() = default;
diff --git a/components/autofill/core/browser/data_model/contact_info_unittest.cc b/components/autofill/core/browser/data_model/contact_info_unittest.cc
index b848812..8ae2f37 100644
--- a/components/autofill/core/browser/data_model/contact_info_unittest.cc
+++ b/components/autofill/core/browser/data_model/contact_info_unittest.cc
@@ -39,7 +39,7 @@
   NameInfo name;
   name.SetInfo(AutofillType(NAME_FULL), ASCIIToUTF16(test_case.full_name_input),
                "en-US");
-  name.FinalizeAfterImport();
+  EXPECT_TRUE(name.FinalizeAfterImport());
   EXPECT_EQ(ASCIIToUTF16(test_case.given_name_output),
             name.GetInfo(AutofillType(NAME_FIRST), "en-US"));
   EXPECT_EQ(ASCIIToUTF16(test_case.middle_name_output),
@@ -178,7 +178,9 @@
         FullNameTestCase{"Mikhail Yevgrafovich Saltykov-Shchedrin", "Mikhail",
                          "Yevgrafovich", "Saltykov-Shchedrin"},
         FullNameTestCase{"Arthur Ignatius Conan Doyle", "Arthur",
-                         "Ignatius Conan", "Doyle"}));
+                         "Ignatius Conan", "Doyle"},
+        FullNameTestCase{"Pablo Diego Ruiz y Picasso", "Pablo Diego", "",
+                         "Ruiz y Picasso"}));
 
 TEST(CompanyTest, SetRawInfo) {
   CompanyInfo company;
diff --git a/components/autofill/core/browser/data_model/credit_card.cc b/components/autofill/core/browser/data_model/credit_card.cc
index 98cffbe..62f5f647 100644
--- a/components/autofill/core/browser/data_model/credit_card.cc
+++ b/components/autofill/core/browser/data_model/credit_card.cc
@@ -606,9 +606,15 @@
       return name_on_card_;
 
     case CREDIT_CARD_NAME_FIRST:
+      if (!temp_card_first_name_.empty()) {
+        return temp_card_first_name_;
+      }
       return data_util::SplitName(name_on_card_).given;
 
     case CREDIT_CARD_NAME_LAST:
+      if (!temp_card_last_name_.empty()) {
+        return temp_card_last_name_;
+      }
       return data_util::SplitName(name_on_card_).family;
 
     case CREDIT_CARD_EXP_MONTH:
@@ -654,10 +660,12 @@
 void CreditCard::SetRawInfoWithVerificationStatus(FieldType type,
                                                   const std::u16string& value,
                                                   VerificationStatus status) {
-  DCHECK_EQ(FieldTypeGroup::kCreditCard, GroupTypeOfServerFieldType(type));
+  DCHECK_EQ(FieldTypeGroup::kCreditCard, GroupTypeOfFieldType(type));
   switch (type) {
     case CREDIT_CARD_NAME_FULL:
       name_on_card_ = value;
+      temp_card_first_name_ = u"";
+      temp_card_last_name_ = u"";
       break;
 
     case CREDIT_CARD_NAME_FIRST:
@@ -1384,6 +1392,8 @@
 void CreditCard::SetNameOnCardFromSeparateParts() {
   DCHECK(!temp_card_first_name_.empty() && !temp_card_last_name_.empty());
   name_on_card_ = temp_card_first_name_ + u" " + temp_card_last_name_;
+  temp_card_first_name_ = u"";
+  temp_card_last_name_ = u"";
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/data_model/phone_number.cc b/components/autofill/core/browser/data_model/phone_number.cc
index 8f71e1a..4e909b4 100644
--- a/components/autofill/core/browser/data_model/phone_number.cc
+++ b/components/autofill/core/browser/data_model/phone_number.cc
@@ -82,7 +82,7 @@
 }
 
 std::u16string PhoneNumber::GetRawInfo(FieldType type) const {
-  DCHECK_EQ(FieldTypeGroup::kPhone, GroupTypeOfServerFieldType(type));
+  DCHECK_EQ(FieldTypeGroup::kPhone, GroupTypeOfFieldType(type));
   if (type == PHONE_HOME_WHOLE_NUMBER)
     return number_;
 
@@ -95,7 +95,7 @@
 void PhoneNumber::SetRawInfoWithVerificationStatus(FieldType type,
                                                    const std::u16string& value,
                                                    VerificationStatus status) {
-  DCHECK_EQ(FieldTypeGroup::kPhone, GroupTypeOfServerFieldType(type));
+  DCHECK_EQ(FieldTypeGroup::kPhone, GroupTypeOfFieldType(type));
   if (type != PHONE_HOME_WHOLE_NUMBER) {
     // Only full phone numbers should be set directly. The browser is
     // intentionally caused to crash to prevent all users from setting raw info
diff --git a/components/autofill/core/browser/data_model/phone_number_unittest.cc b/components/autofill/core/browser/data_model/phone_number_unittest.cc
index 330e23ad..b74bd3e 100644
--- a/components/autofill/core/browser/data_model/phone_number_unittest.cc
+++ b/components/autofill/core/browser/data_model/phone_number_unittest.cc
@@ -300,7 +300,7 @@
   profile.GetSupportedTypes(&types);
   std::vector<FieldType> fields{types.begin(), types.end()};
   std::erase_if(fields, [](FieldType type) {
-    return GroupTypeOfServerFieldType(type) != FieldTypeGroup::kPhone;
+    return GroupTypeOfFieldType(type) != FieldTypeGroup::kPhone;
   });
 
   base::ranges::for_each(fields, [](FieldType type) {
diff --git a/components/autofill/core/browser/field_type_utils.cc b/components/autofill/core/browser/field_type_utils.cc
index ce10600..0b163b7 100644
--- a/components/autofill/core/browser/field_type_utils.cc
+++ b/components/autofill/core/browser/field_type_utils.cc
@@ -58,7 +58,7 @@
 size_t NumberOfPossibleFieldTypesInGroup(const AutofillField& field,
                                          FieldTypeGroup group) {
   return base::ranges::count(field.possible_types(), group,
-                             GroupTypeOfServerFieldType);
+                             GroupTypeOfFieldType);
 }
 
 bool FieldHasMeaningfulPossibleFieldTypes(const AutofillField& field) {
@@ -84,7 +84,7 @@
 }
 
 bool IsAddressType(FieldType type) {
-  switch (GroupTypeOfServerFieldType(type)) {
+  switch (GroupTypeOfFieldType(type)) {
     case FieldTypeGroup::kName:
     case FieldTypeGroup::kEmail:
     case FieldTypeGroup::kCompany:
diff --git a/components/autofill/core/browser/field_types.cc b/components/autofill/core/browser/field_types.cc
index 8f16c5ea..320eacb 100644
--- a/components/autofill/core/browser/field_types.cc
+++ b/components/autofill/core/browser/field_types.cc
@@ -459,14 +459,14 @@
 FieldTypeSet GetFieldTypesOfGroup(FieldTypeGroup group) {
   FieldTypeSet fields_matching_group;
   for (FieldType field_type : kAllFieldTypes) {
-    if (GroupTypeOfServerFieldType(field_type) == group) {
+    if (GroupTypeOfFieldType(field_type) == group) {
       fields_matching_group.insert(field_type);
     }
   }
   return fields_matching_group;
 }
 
-FieldTypeGroup GroupTypeOfServerFieldType(FieldType field_type) {
+FieldTypeGroup GroupTypeOfFieldType(FieldType field_type) {
   switch (field_type) {
     case NAME_HONORIFIC_PREFIX:
     case NAME_FIRST:
@@ -675,8 +675,7 @@
   NOTREACHED_NORETURN();
 }
 
-FieldType HtmlFieldTypeToBestCorrespondingServerFieldType(
-    HtmlFieldType field_type) {
+FieldType HtmlFieldTypeToBestCorrespondingFieldType(HtmlFieldType field_type) {
   switch (field_type) {
     case HtmlFieldType::kUnspecified:
       return UNKNOWN_TYPE;
diff --git a/components/autofill/core/browser/field_types.h b/components/autofill/core/browser/field_types.h
index 0399248..c9cf410 100644
--- a/components/autofill/core/browser/field_types.h
+++ b/components/autofill/core/browser/field_types.h
@@ -504,12 +504,11 @@
 // There's a one-to-many relationship between FieldTypeGroup and
 // FieldType as well as HtmlFieldType.
 FieldTypeSet GetFieldTypesOfGroup(FieldTypeGroup group);
-FieldTypeGroup GroupTypeOfServerFieldType(FieldType field_type);
+FieldTypeGroup GroupTypeOfFieldType(FieldType field_type);
 FieldTypeGroup GroupTypeOfHtmlFieldType(HtmlFieldType field_type);
 
 // Not all HtmlFieldTypes have a corresponding FieldType.
-FieldType HtmlFieldTypeToBestCorrespondingServerFieldType(
-    HtmlFieldType field_type);
+FieldType HtmlFieldTypeToBestCorrespondingFieldType(HtmlFieldType field_type);
 
 // Returns |raw_value| if it corresponds to a non-deprecated enumeration
 // constant of ServerFieldType other than MAX_VALID_FIELD_TYPE. Otherwise,
diff --git a/components/autofill/core/browser/form_data_importer.cc b/components/autofill/core/browser/form_data_importer.cc
index 6b8058b93..0eea923 100644
--- a/components/autofill/core/browser/form_data_importer.cc
+++ b/components/autofill/core/browser/form_data_importer.cc
@@ -77,7 +77,7 @@
   // - phone number components because a form might request several phone
   // numbers.
   // TODO(crbug.com/1156315) Clean up when launched.
-  FieldTypeGroup field_type_group = GroupTypeOfServerFieldType(field_type);
+  FieldTypeGroup field_type_group = GroupTypeOfFieldType(field_type);
   if (observed_types.contains(field_type) && field_type != EMAIL_ADDRESS &&
       (!base::FeatureList::IsEnabled(
            features::kAutofillEnableImportWhenMultiplePhoneNumbers) ||
@@ -959,6 +959,10 @@
 
 FormDataImporter::ExtractCreditCardFromFormResult
 FormDataImporter::ExtractCreditCardFromForm(const FormStructure& form) {
+  if (base::FeatureList::IsEnabled(features::kAutofillRelaxCreditCardImport)) {
+    return ExtractCreditCardFromFormRelaxed(form);
+  }
+
   ExtractCreditCardFromFormResult result;
 
   FieldTypeSet types_seen;
@@ -1024,6 +1028,98 @@
   return result;
 }
 
+FormDataImporter::ExtractCreditCardFromFormResult
+FormDataImporter::ExtractCreditCardFromFormRelaxed(const FormStructure& form) {
+  // Populated by the lambdas below.
+  ExtractCreditCardFromFormResult result;
+
+  // Populates `result` from `field` if it's a credit card field.
+  // For example, if `field` contains credit card number, this sets the number
+  // of `result.card` to the `field`'s value.
+  auto extract_if_credit_card_field = [&result, &app_locale = app_locale_](
+                                          const AutofillField& field) {
+    // The value of interest is `field->value` or `field->user_input`.
+    std::u16string_view value_view =
+        base::TrimWhitespace(field.value, base::TRIM_ALL);
+    std::u16string_view user_input_view =
+        base::TrimWhitespace(field.user_input, base::TRIM_ALL);
+    if (!user_input_view.empty() &&
+        field.Type().GetStorableType() == ServerFieldType::CREDIT_CARD_NUMBER &&
+        base::FeatureList::IsEnabled(
+            features::kAutofillUseTypedCreditCardNumber)) {
+      value_view = user_input_view;
+    }
+    std::u16string value(value_view);
+
+    // If we don't know the type of the field, or the user hasn't entered any
+    // information into the field, then skip it.
+    if (!field.IsFieldFillable() || value.empty() ||
+        field.Type().group() != FieldTypeGroup::kCreditCard) {
+      return;
+    }
+    std::u16string old_value = result.card.GetInfo(field.Type(), app_locale);
+    if (field.form_control_type == FormControlType::kInputMonth) {
+      // If |field| is an HTML5 month input, handle it as a special case.
+      DCHECK_EQ(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR,
+                field.Type().GetStorableType());
+      result.card.SetInfoForMonthInputType(value);
+    } else {
+      bool saved = result.card.SetInfo(field.Type(), value, app_locale);
+      if (!saved && field.IsSelectOrSelectListElement()) {
+        // Saving with the option text (here `value`) may fail for the
+        // expiration month. Attempt to save with the option value. First find
+        // the index of the option text in the select options and try the
+        // corresponding value.
+        if (auto it = base::ranges::find(field.options, value,
+                                         &SelectOption::content);
+            it != field.options.end()) {
+          result.card.SetInfo(field.Type(), it->value, app_locale);
+        }
+      }
+    }
+    std::u16string new_value = result.card.GetInfo(field.Type(), app_locale);
+    result.has_duplicate_credit_card_field_type |=
+        !old_value.empty() && old_value != new_value;
+  };
+
+  // Populates `result` from `fields` that satisfy `pred`, and erases those
+  // fields. Afterwards, it also erases all remaining fields whose type is now
+  // present in `result.card`.
+  // For example, if a `CREDIT_CARD_NAME_FULL` field matches `pred`, this
+  // function sets the credit card first, last, and full name and erases
+  // all `fields` of type `CREDIT_CARD_NAME_{FULL,FIRST,LAST}`.
+  auto extract_data_and_remove_field_if =
+      [&result, &extract_if_credit_card_field, &app_locale = app_locale_](
+          std::vector<const AutofillField*>& fields, const auto& pred) {
+        for (const AutofillField* field : fields) {
+          if (std::invoke(pred, *field)) {
+            extract_if_credit_card_field(*field);
+          }
+        }
+        std::erase_if(fields, [&](const AutofillField* field) {
+          return std::invoke(pred, *field) ||
+                 !result.card.GetInfo(field->Type(), app_locale).empty();
+        });
+      };
+
+  // We split the fields into three priority groups: user-typed values,
+  // autofilled values, other values. The duplicate-value recognition is limited
+  // to values of the respective group.
+  //
+  // Suppose the user first autofills a form, including invisible fields. Then
+  // they edited a visible fields. The priority groups ensure that the invisible
+  // field does not prevent credit card import.
+  std::vector<const AutofillField*> fields;
+  fields.reserve(form.fields().size());
+  for (const std::unique_ptr<AutofillField>& field : form.fields()) {
+    fields.push_back(field.get());
+  }
+  extract_data_and_remove_field_if(fields, &AutofillField::is_user_edited);
+  extract_data_and_remove_field_if(fields, &AutofillField::is_autofilled);
+  extract_data_and_remove_field_if(fields, [](const auto&) { return true; });
+  return result;
+}
+
 Iban FormDataImporter::ExtractIbanFromForm(const FormStructure& form) {
   // Creates an IBAN candidate with `kUnknown` record type as it is currently
   // unknown if this IBAN already exists locally or on the server.
diff --git a/components/autofill/core/browser/form_data_importer.h b/components/autofill/core/browser/form_data_importer.h
index dc1224d6..e731023a 100644
--- a/components/autofill/core/browser/form_data_importer.h
+++ b/components/autofill/core/browser/form_data_importer.h
@@ -91,6 +91,11 @@
   ExtractCreditCardFromFormResult ExtractCreditCardFromForm(
       const FormStructure& form);
 
+  // TODO(crbug.com/1381477): Rename to ExtractCreditCardFromForm() once
+  // `features::kAutofillRelaxCreditCardImport` is launched.
+  ExtractCreditCardFromFormResult ExtractCreditCardFromFormRelaxed(
+      const FormStructure& form);
+
   // Tries to initiate the saving of `extracted_iban` if applicable.
   bool ProcessIbanImportCandidate(const Iban& extracted_iban);
 
diff --git a/components/autofill/core/browser/form_data_importer_unittest.cc b/components/autofill/core/browser/form_data_importer_unittest.cc
index 5b8807f..f9c0b24 100644
--- a/components/autofill/core/browser/form_data_importer_unittest.cc
+++ b/components/autofill/core/browser/form_data_importer_unittest.cc
@@ -531,7 +531,8 @@
          features::kAutofillEnableSupportForAddressOverflow,
          features::kAutofillEnableSupportForBetweenStreetsOrLandmark,
          features::kAutofillEnableSupportForAddressOverflowAndLandmark,
-         features::kAutofillEnableParsingOfStreetLocation},
+         features::kAutofillEnableParsingOfStreetLocation,
+         features::kAutofillRelaxCreditCardImport},
         {});
   }
 
@@ -4230,4 +4231,115 @@
   EXPECT_TRUE(observed_field_types.empty());
 }
 
+// Test case for credit card extraction.
+class FormDataImporterTest_ExtractCreditCardFromForm
+    : public FormDataImporterTest {
+ public:
+  enum class Mode { kDefaultValue, kAutofilled, kUserEdited };
+
+  void PushField(FieldType field_type,
+                 std::u16string value,
+                 Mode mode = Mode::kDefaultValue) {
+    AutofillField& f = test_api(form_).PushField();
+    f.set_server_predictions({test::CreateFieldPrediction(field_type)});
+    f.value = std::move(value);
+    f.is_autofilled = mode == Mode::kAutofilled;
+    f.is_user_edited = mode == Mode::kUserEdited;
+  }
+
+  FormStructure form_{/*form=*/{}};
+};
+
+// Tests that inconsistent values from different priority classes do not prevent
+// import.
+// For example, the user-edited "Donald Trump" has higher priority than the
+// autofilled "Joe Biden", which has still higher priority than default-value
+// "Joe Average".
+TEST_F(FormDataImporterTest_ExtractCreditCardFromForm,
+       IgnoreInconsistentValuesFromDifferentPriorityClasses) {
+  PushField(FieldType::CREDIT_CARD_NAME_FULL, u"Joe Average",
+            Mode::kAutofilled);
+  PushField(FieldType::CREDIT_CARD_NAME_FULL, u"Joe Biden", Mode::kAutofilled);
+  PushField(FieldType::CREDIT_CARD_NAME_FULL, u"Donald Trump",
+            Mode::kUserEdited);
+  PushField(FieldType::CREDIT_CARD_NUMBER, u"4444444444444444",
+            Mode::kDefaultValue);
+  PushField(FieldType::CREDIT_CARD_NUMBER, u"4444333322221111",
+            Mode::kAutofilled);
+  PushField(FieldType::CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, u"01/2020",
+            Mode::kAutofilled);
+  PushField(FieldType::CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, u"01/2021",
+            Mode::kUserEdited);
+  auto r = form_data_importer().ExtractCreditCardFromForm(form_);
+  EXPECT_EQ(r.card.GetInfo(FieldType::CREDIT_CARD_NAME_FULL, kLocale),
+            u"Donald Trump");
+  EXPECT_EQ(r.card.GetInfo(FieldType::CREDIT_CARD_NUMBER, kLocale),
+            u"4444333322221111");
+  EXPECT_EQ(
+      r.card.GetInfo(FieldType::CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, kLocale),
+      u"01/2021");
+  EXPECT_FALSE(r.has_duplicate_credit_card_field_type);
+}
+
+// Tests that equivalent values of different types do not prevent import:
+// - first name + last names = full name;
+// - month + year = expiration date.
+TEST_F(FormDataImporterTest_ExtractCreditCardFromForm, MergeDerivedValues) {
+  PushField(FieldType::CREDIT_CARD_NAME_FIRST, u"Donald", Mode::kUserEdited);
+  PushField(FieldType::CREDIT_CARD_NAME_LAST, u"Trump", Mode::kUserEdited);
+  PushField(FieldType::CREDIT_CARD_NAME_FULL, u"Joe Biden", Mode::kAutofilled);
+  PushField(FieldType::CREDIT_CARD_NUMBER, u"4444333322221111",
+            Mode::kAutofilled);
+  PushField(FieldType::CREDIT_CARD_EXP_MONTH, u"12", Mode::kUserEdited);
+  PushField(FieldType::CREDIT_CARD_EXP_4_DIGIT_YEAR, u"2020",
+            Mode::kUserEdited);
+  PushField(FieldType::CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, u"12/20",
+            Mode::kUserEdited);
+  auto r = form_data_importer().ExtractCreditCardFromForm(form_);
+  EXPECT_EQ(r.card.GetInfo(FieldType::CREDIT_CARD_NAME_FULL, kLocale),
+            u"Donald Trump");
+  EXPECT_EQ(r.card.GetInfo(FieldType::CREDIT_CARD_NUMBER, kLocale),
+            u"4444333322221111");
+  EXPECT_EQ(
+      r.card.GetInfo(FieldType::CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, kLocale),
+      u"12/2020");
+  EXPECT_FALSE(r.has_duplicate_credit_card_field_type);
+}
+
+// Tests detection of inconsistent values (first names "Audrey" and "Katherine")
+// in the same priority class (user-edited fields).
+TEST_F(FormDataImporterTest_ExtractCreditCardFromForm,
+       BlockImportForInconsistentValues) {
+  PushField(FieldType::CREDIT_CARD_NAME_FIRST, u"Katherine", Mode::kUserEdited);
+  PushField(FieldType::CREDIT_CARD_NAME_FIRST, u"Audrey", Mode::kUserEdited);
+  PushField(FieldType::CREDIT_CARD_NAME_LAST, u"Hepburn", Mode::kUserEdited);
+  PushField(FieldType::CREDIT_CARD_NUMBER, u"4444333322221111",
+            Mode::kUserEdited);
+  PushField(FieldType::CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, u"12/2020",
+            Mode::kUserEdited);
+  auto r = form_data_importer().ExtractCreditCardFromForm(form_);
+  ASSERT_TRUE(r.has_duplicate_credit_card_field_type);
+}
+
+// Tests that even editing only a first name (without editing the last name) is
+// is reflected in the import candidate.
+TEST_F(FormDataImporterTest_ExtractCreditCardFromForm, PartialFirstLastNames) {
+  PushField(FieldType::CREDIT_CARD_NAME_FIRST, u"Katherine", Mode::kAutofilled);
+  PushField(FieldType::CREDIT_CARD_NAME_FIRST, u"Audrey", Mode::kUserEdited);
+  PushField(FieldType::CREDIT_CARD_NAME_LAST, u"Hepburn", Mode::kAutofilled);
+  PushField(FieldType::CREDIT_CARD_NUMBER, u"4444333322221111",
+            Mode::kUserEdited);
+  PushField(FieldType::CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, u"12/2020",
+            Mode::kUserEdited);
+  auto r = form_data_importer().ExtractCreditCardFromForm(form_);
+  EXPECT_EQ(r.card.GetInfo(FieldType::CREDIT_CARD_NAME_FULL, kLocale),
+            u"Audrey Hepburn");
+  EXPECT_EQ(r.card.GetInfo(FieldType::CREDIT_CARD_NUMBER, kLocale),
+            u"4444333322221111");
+  EXPECT_EQ(
+      r.card.GetInfo(FieldType::CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, kLocale),
+      u"12/2020");
+  EXPECT_FALSE(r.has_duplicate_credit_card_field_type);
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/form_parsing/form_field.cc b/components/autofill/core/browser/form_parsing/form_field.cc
index 4774b05..d7d3b70 100644
--- a/components/autofill/core/browser/form_parsing/form_field.cc
+++ b/components/autofill/core/browser/form_parsing/form_field.cc
@@ -9,7 +9,6 @@
 #include <iterator>
 #include <numeric>
 
-#include "base/feature_list.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -36,7 +35,6 @@
 #include "components/autofill/core/browser/logging/log_manager.h"
 #include "components/autofill/core/common/autocomplete_parsing_util.h"
 #include "components/autofill/core/common/autofill_constants.h"
-#include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_internals/log_message.h"
 #include "components/autofill/core/common/autofill_internals/logging_scope.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
@@ -63,13 +61,40 @@
 
 }  // namespace
 
+RegexMatchesCache::RegexMatchesCache(int capacity) : cache_(capacity) {}
+RegexMatchesCache::~RegexMatchesCache() = default;
+
+RegexMatchesCache::Key RegexMatchesCache::BuildKey(
+    base::StringPiece16 input,
+    base::StringPiece16 pattern) {
+  return Key(std::hash<std::u16string_view>{}(input),
+             std::hash<std::u16string_view>{}(pattern));
+}
+
+absl::optional<bool> RegexMatchesCache::Get(RegexMatchesCache::Key key) {
+  if (auto it = cache_.Get(key); it != cache_.end()) {
+    return it->second;
+  }
+  return absl::nullopt;
+}
+
+void RegexMatchesCache::Put(RegexMatchesCache::Key key, bool value) {
+  cache_.Put(key, value);
+}
+
 ParsingContext::ParsingContext(GeoIpCountryCode client_country,
                                LanguageCode page_language,
                                PatternSource pattern_source)
     : client_country(std::move(client_country)),
       page_language(std::move(page_language)),
       pattern_source(pattern_source),
-      regex_cache(GetAutofillRegexCache()) {}
+      regex_cache(GetAutofillRegexCache()) {
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillEnableCacheForRegexMatching)) {
+    matches_cache.emplace(
+        features::kAutofillEnableCacheForRegexMatchingCacheSizeParam.Get());
+  }
+}
 
 ParsingContext::~ParsingContext() = default;
 
@@ -78,9 +103,21 @@
                                       base::StringPiece16 input,
                                       base::StringPiece16 pattern,
                                       std::vector<std::u16string>* groups) {
+  RegexMatchesCache::Key key;
+  if (!groups && context.matches_cache) {
+    key = RegexMatchesCache::BuildKey(input, pattern);
+    absl::optional<bool> cache_entry = context.matches_cache->Get(key);
+    if (cache_entry.has_value()) {
+      return cache_entry.value();
+    }
+  }
   const icu::RegexPattern* regex_pattern =
       context.regex_cache->GetRegexPattern(pattern);
-  return autofill::MatchesRegex(input, *regex_pattern, groups);
+  bool result = autofill::MatchesRegex(input, *regex_pattern, groups);
+  if (!groups && context.matches_cache) {
+    context.matches_cache->Put(key, result);
+  }
+  return result;
 }
 
 // static
@@ -568,12 +605,12 @@
   base::StringPiece16 value;
   std::vector<std::u16string> matches;
   std::vector<std::u16string>* capture_destination =
-      logging.log_manager ? &matches : nullptr;
+      logging.log_manager && logging.log_manager->IsLoggingActive() ? &matches
+                                                                    : nullptr;
 
   // TODO(crbug/1165780): Remove once shared labels are launched.
   const std::u16string& label =
-      base::FeatureList::IsEnabled(
-          features::kAutofillEnableSupportForParsingWithSharedLabels)
+      context.autofill_enable_support_for_parsing_with_shared_labels
           ? field->parseable_label()
           : field->label;
 
@@ -593,8 +630,7 @@
     match_type_string = "Match in name";
     value = name;
   } else if (match_label && pattern != kEmptyLabelRegex &&
-             base::FeatureList::IsEnabled(
-                 features::kAutofillAlwaysParsePlaceholders) &&
+             context.autofill_always_parse_placeholders &&
              MatchesRegexWithCache(context, field->placeholder, pattern,
                                    capture_destination)) {
     // Placeholders are matched against the same regexes as labels. However, to
@@ -609,7 +645,7 @@
     value = field->placeholder;
   }
 
-  if (found_match) {
+  if (found_match && capture_destination) {
     LogBuffer table_rows(IsLoggingActive(logging.log_manager));
     LOG_AF(table_rows) << Tr{} << "Match type:" << match_type_string;
     LOG_AF(table_rows) << Tr{} << "RegEx:" << logging.regex_name;
diff --git a/components/autofill/core/browser/form_parsing/form_field.h b/components/autofill/core/browser/form_parsing/form_field.h
index 0b17466f..66b92ce 100644
--- a/components/autofill/core/browser/form_parsing/form_field.h
+++ b/components/autofill/core/browser/form_parsing/form_field.h
@@ -11,15 +11,19 @@
 #include <utility>
 #include <vector>
 
+#include "base/containers/lru_cache.h"
+#include "base/feature_list.h"
 #include "base/functional/callback.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ref.h"
 #include "base/strings/string_piece.h"
 #include "components/autofill/core/browser/country_type.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h"
 #include "components/autofill/core/browser/form_parsing/field_candidates.h"
 #include "components/autofill/core/browser/form_parsing/regex_patterns.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/language_code.h"
 
 namespace autofill {
@@ -43,6 +47,54 @@
   const char* regex_name = "";
 };
 
+// LRU cache to prevent the repetitive evaluation of identical regular
+// expressions (`pattern`) on identical `input` strings.
+class RegexMatchesCache {
+ public:
+  // A good capacity for the cache according to an empirical study of forms
+  // with AllForms/HeuristicClassificationTests.EndToEnd is 300. This needs
+  // to be confirmed in real world experiments.
+  // RegexMatchesCache is not intended as a permanent cache but instantiated
+  // once per form parsing, so this is not allocating a lot of memory
+  // permanently.
+  explicit RegexMatchesCache(int capacity);
+  RegexMatchesCache(const RegexMatchesCache&) = delete;
+  RegexMatchesCache& operator=(const RegexMatchesCache&) = delete;
+  ~RegexMatchesCache();
+
+  // Hash values of an input and a pattern. There is a theoretical risk of
+  // collision which we are accepting here to not store the inputs an patterns
+  // which both may be large. Given that our heuristics are not 100% accurate
+  // the small risk of a collision seems acceptable.
+  // TODO(crbug.com/1121990): Once we don't use autofill_regex_constants.h
+  // anymore, the second `std::size_t` should probably be a MatchPatternRef:
+  // - more accurate (they uniquely identify the pattern across all pattern
+  //   sources),
+  // - more time-efficient (no hashing needed),
+  // - more space-efficient (2 vs 8 bytes)
+  using Key = std::pair<std::size_t, std::size_t>;
+
+  // Creates a key for an `input` string and a `pattern` to be used in the LRU
+  // cache.
+  static Key BuildKey(base::StringPiece16 input, base::StringPiece16 pattern);
+
+  // Returns whether `pattern` in the key matched `input` if this information is
+  // cached. absl::nullopt if the information is not cached.
+  absl::optional<bool> Get(Key key);
+
+  // Stores whether `pattern` in the key matched `input`.
+  void Put(Key key, bool value);
+
+ private:
+  struct Hasher {
+    std::size_t operator()(const Key& key) const noexcept {
+      return std::get<0>(key) ^ std::get<1>(key);
+    }
+  };
+
+  base::HashingLRUCache<Key, bool, Hasher> cache_;
+};
+
 // This is a helper class that is instantiated before form parsing. It contains
 // a) environmental information that is needed in many places and b) caches to
 // prevent repetitive work.
@@ -58,6 +110,19 @@
   const LanguageCode page_language;
   const PatternSource pattern_source;
 
+  // Cache for autofill features that are tested on hot code paths. Testing
+  // whether a feature is enabled is pretty expensive. Caching the status of two
+  // features led to a performance improvement for form field classification of
+  // 19% in release builds.
+  // Note that adding features here may push users into the respective
+  // experiment/control groups earlier than you may want.
+  const bool autofill_enable_support_for_parsing_with_shared_labels{
+      base::FeatureList::IsEnabled(
+          features::kAutofillEnableSupportForParsingWithSharedLabels)};
+  const bool autofill_always_parse_placeholders{
+      base::FeatureList::IsEnabled(features::kAutofillAlwaysParsePlaceholders)};
+
+  std::optional<RegexMatchesCache> matches_cache;
   base::raw_ref<AutofillRegexCache> regex_cache;
 };
 
diff --git a/components/autofill/core/browser/form_parsing/regex_patterns.cc b/components/autofill/core/browser/form_parsing/regex_patterns.cc
index e514db4..7925ce9 100644
--- a/components/autofill/core/browser/form_parsing/regex_patterns.cc
+++ b/components/autofill/core/browser/form_parsing/regex_patterns.cc
@@ -53,13 +53,13 @@
 
 }  // namespace
 
-absl::optional<PatternSource> GetActivePatternSource() {
+std::optional<PatternSource> GetActivePatternSource() {
   return HeuristicSourceToPatternSource(GetActiveHeuristicSource());
 }
 
 base::span<const MatchPatternRef> GetMatchPatterns(
     std::string_view name,
-    absl::optional<LanguageCode> language_code,
+    std::optional<LanguageCode> language_code,
     PatternSource pattern_source) {
   return language_code ? GetMatchPatterns(name, **language_code, pattern_source)
                        : GetMatchPatterns(name, "", pattern_source);
@@ -67,7 +67,7 @@
 
 base::span<const MatchPatternRef> GetMatchPatterns(
     FieldType type,
-    absl::optional<LanguageCode> language_code,
+    std::optional<LanguageCode> language_code,
     PatternSource pattern_source) {
   return GetMatchPatterns(FieldTypeToStringView(type), language_code,
                           pattern_source);
diff --git a/components/autofill/core/browser/form_parsing/regex_patterns.h b/components/autofill/core/browser/form_parsing/regex_patterns.h
index f6d0fb4..f6357f7 100644
--- a/components/autofill/core/browser/form_parsing/regex_patterns.h
+++ b/components/autofill/core/browser/form_parsing/regex_patterns.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_REGEX_PATTERNS_H_
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_REGEX_PATTERNS_H_
 
+#include <optional>
 #include <string_view>
 
 #include "base/containers/span.h"
@@ -12,7 +13,6 @@
 #include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h"
 #include "components/autofill/core/browser/form_parsing/buildflags.h"
 #include "components/autofill/core/common/language_code.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -92,7 +92,7 @@
 // The active pattern and the available patterns depend on the build config and
 // the Finch config. If the active `HeuristicSource` is not a `PatternSource`,
 // then a nullopt is returned.
-absl::optional<PatternSource> GetActivePatternSource();
+std::optional<PatternSource> GetActivePatternSource();
 
 // Looks up the patterns for the given name and language.
 // The name is typically a field type.
@@ -106,12 +106,12 @@
 // decreasing order.
 base::span<const MatchPatternRef> GetMatchPatterns(
     std::string_view name,
-    absl::optional<LanguageCode> language_code,
+    std::optional<LanguageCode> language_code,
     PatternSource pattern_source);
 
 base::span<const MatchPatternRef> GetMatchPatterns(
     FieldType type,
-    absl::optional<LanguageCode> language_code,
+    std::optional<LanguageCode> language_code,
     PatternSource pattern_source);
 
 // Returns true iff there at least one pattern for some PatternSource and
diff --git a/components/autofill/core/browser/form_parsing/regex_patterns_unittest.cc b/components/autofill/core/browser/form_parsing/regex_patterns_unittest.cc
index 1a1f17d..8772c13 100644
--- a/components/autofill/core/browser/form_parsing/regex_patterns_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/regex_patterns_unittest.cc
@@ -39,9 +39,9 @@
 
   explicit MatchPatternRefTestApi(MatchPatternRef p) : p_(p) {}
 
-  absl::optional<MatchPatternRef> MakeSupplementary() const {
+  std::optional<MatchPatternRef> MakeSupplementary() const {
     if (!(*p_).match_field_attributes.contains(MatchAttribute::kName))
-      return absl::nullopt;
+      return std::nullopt;
     return MatchPatternRef(true, index());
   }
 
@@ -206,9 +206,9 @@
   std::erase_if(expected,
                 [](auto p) { return test_api(p).is_supplementary(); });
 
-  EXPECT_THAT(GetMatchPatterns(kSomeName, absl::nullopt, pattern_source()),
+  EXPECT_THAT(GetMatchPatterns(kSomeName, std::nullopt, pattern_source()),
               UnorderedElementsAreArray(expected));
-  EXPECT_THAT(GetMatchPatterns(kSomeName, absl::nullopt, pattern_source()),
+  EXPECT_THAT(GetMatchPatterns(kSomeName, std::nullopt, pattern_source()),
               Each(Not(IsSupplementary)));
 }
 
@@ -220,7 +220,7 @@
   EXPECT_THAT(
       GetMatchPatterns(kSomeName, kNonexistingLanguage, pattern_source()),
       ElementsAreArray(
-          GetMatchPatterns(kSomeName, absl::nullopt, pattern_source())));
+          GetMatchPatterns(kSomeName, std::nullopt, pattern_source())));
 }
 
 // Tests that for a given pattern name, the non-English languages are
diff --git a/components/autofill/core/browser/form_processing/label_processing_util.cc b/components/autofill/core/browser/form_processing/label_processing_util.cc
index 6ee5d75..5bf60cd 100644
--- a/components/autofill/core/browser/form_processing/label_processing_util.cc
+++ b/components/autofill/core/browser/form_processing/label_processing_util.cc
@@ -17,7 +17,7 @@
 // The maximum length of a label that can be shared among fields.
 const int kMaxLengthOfShareableLabel = 40;
 
-absl::optional<std::vector<std::u16string>> GetParseableLabels(
+std::optional<std::vector<std::u16string>> GetParseableLabels(
     const LabelPieces& labels) {
   // Make a copy of the labels.
   LabelPieces shared_labels = labels;
@@ -92,7 +92,7 @@
   }
 
   if (!shared_labels_found) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Otherwise convert the shared label string pieces into strings for memory
@@ -101,7 +101,7 @@
   result.reserve(shared_labels.size());
   base::ranges::transform(shared_labels, std::back_inserter(result),
                           [](auto& s) { return std::u16string(s); });
-  return absl::make_optional(std::move(result));
+  return std::make_optional(std::move(result));
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/form_processing/label_processing_util.h b/components/autofill/core/browser/form_processing/label_processing_util.h
index a2124a43..05f24b55 100644
--- a/components/autofill/core/browser/form_processing/label_processing_util.h
+++ b/components/autofill/core/browser/form_processing/label_processing_util.h
@@ -5,18 +5,19 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PROCESSING_LABEL_PROCESSING_UTIL_H_
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PROCESSING_LABEL_PROCESSING_UTIL_H_
 
+#include <optional>
 #include <vector>
+
 #include "base/strings/string_piece.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
 // If parseable labels can be derived from |labels|, a vector of
 // |std::u16string| is return that is aligned with |labels|.
 // Parseable labels can be derived by splitting one label between multiple
-// adjacent fields. If there aren't any changes to the labels, |absl::nullopt|
+// adjacent fields. If there aren't any changes to the labels, |std::nullopt|
 // is returned.
-absl::optional<std::vector<std::u16string>> GetParseableLabels(
+std::optional<std::vector<std::u16string>> GetParseableLabels(
     const std::vector<base::StringPiece16>& labels);
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/form_processing/label_processing_util_unittest.cc b/components/autofill/core/browser/form_processing/label_processing_util_unittest.cc
index 2cac002..d0d2ffbb 100644
--- a/components/autofill/core/browser/form_processing/label_processing_util_unittest.cc
+++ b/components/autofill/core/browser/form_processing/label_processing_util_unittest.cc
@@ -35,19 +35,19 @@
   EXPECT_EQ(
       GetParseableLabels({u"City", u"Street & House Number & Floor & Stairs",
                           u"", u"", u"", u"Zip"}),
-      absl::nullopt);
+      std::nullopt);
 }
 
 TEST(LabelProcessingUtil, GetParseableNameStringPieces_UnmachtingComponents) {
   EXPECT_EQ(GetParseableLabels(
                 {u"City", u"Street & House Number & Floor", u"", u"Zip"}),
-            absl::nullopt);
+            std::nullopt);
 }
 
 TEST(LabelProcessingUtil, GetParseableNameStringPieces_SplitableLabelAtEnd) {
   EXPECT_EQ(GetParseableLabels(
                 {u"City", u"", u"Zip", u"Street & House Number & Floor"}),
-            absl::nullopt);
+            std::nullopt);
 }
 
 TEST(LabelProcessingUtil, GetParseableNameStringPieces_TooLongLabel) {
@@ -56,7 +56,7 @@
                                 u"additional text that exceeds 40 "
                                 u"characters by far",
                                 u"", u"Zip"}),
-            absl::nullopt);
+            std::nullopt);
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index 3c6afdb..334a561 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -81,7 +81,6 @@
 #include "components/autofill/core/common/unique_ids.h"
 #include "components/security_state/core/security_state.h"
 #include "components/version_info/version_info.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/origin.h"
 
 namespace autofill {
@@ -388,7 +387,7 @@
 bool HasPasswordManagerPrediction(const FieldSuggestion& field_suggestion) {
   return base::ranges::any_of(
       field_suggestion.predictions(), [](const auto& prediction) {
-        auto group_type = GroupTypeOfServerFieldType(
+        auto group_type = GroupTypeOfFieldType(
             ToSafeFieldType(prediction.type(), NO_SERVER_DATA));
         return group_type == FieldTypeGroup::kPasswordField ||
                group_type == FieldTypeGroup::kUsernameField;
@@ -402,7 +401,7 @@
     FieldSuggestion& merge_to_predictions) {
   CHECK_NE(&merge_to_predictions, &merge_from_predictions);
   for (const auto& prediction : merge_from_predictions.predictions()) {
-    FieldTypeGroup group_type = GroupTypeOfServerFieldType(
+    FieldTypeGroup group_type = GroupTypeOfFieldType(
         ToSafeFieldType(prediction.type(), NO_SERVER_DATA));
     // Only add predictions relevant for PasswordManager.
     if (group_type == FieldTypeGroup::kPasswordField ||
@@ -491,7 +490,7 @@
   client_country_ = client_country;
 
   // The active heuristic source might not be a pattern source.
-  if (absl::optional<PatternSource> pattern_source = GetActivePatternSource()) {
+  if (std::optional<PatternSource> pattern_source = GetActivePatternSource()) {
     ParseFieldTypesWithPatterns(*pattern_source, log_manager);
   }
 
@@ -1006,7 +1005,7 @@
   for (const auto* form : forms) {
     for (const auto& field : form->fields_) {
       FieldTypeGroup field_type_group =
-          GroupTypeOfServerFieldType(field->server_type());
+          GroupTypeOfFieldType(field->server_type());
       // In order to trigger the payments bottom sheet that assists users to
       // manually fill the form, credit card form fields are marked eligible for
       // manual filling. Also, if a field is not classified to a type, we can
@@ -1631,8 +1630,7 @@
   for (const auto& field : fields_) {
     const FieldType current_type = field->Type().GetStorableType();
     // Put credit card fields into one, separate credit card section.
-    if (GroupTypeOfServerFieldType(current_type) ==
-        FieldTypeGroup::kCreditCard) {
+    if (GroupTypeOfFieldType(current_type) == FieldTypeGroup::kCreditCard) {
       if (!credit_card_section) {
         credit_card_section =
             Section::FromFieldIdentifier(*field, frame_token_ids);
@@ -1649,7 +1647,7 @@
     // Forms often ask for multiple phone numbers -- e.g. both a daytime and
     // evening phone number.  Our phone number detection is also generally a
     // little off.  Hence, ignore this field type as a signal here.
-    if (GroupTypeOfServerFieldType(current_type) == FieldTypeGroup::kPhone) {
+    if (GroupTypeOfFieldType(current_type) == FieldTypeGroup::kPhone) {
       already_saw_current_type = false;
     }
 
@@ -1798,8 +1796,7 @@
     for (const auto& field : fields_) {
       const FieldType current_type = field->Type().GetStorableType();
       // Credit card fields are already in one, separate credit card section.
-      if (GroupTypeOfServerFieldType(current_type) ==
-          FieldTypeGroup::kCreditCard) {
+      if (GroupTypeOfFieldType(current_type) == FieldTypeGroup::kCreditCard) {
         continue;
       }
 
@@ -1811,7 +1808,7 @@
       // Forms often ask for multiple phone numbers -- e.g. both a daytime and
       // evening phone number.  Our phone number detection is also generally a
       // little off.  Hence, ignore this field type as a signal here.
-      if (GroupTypeOfServerFieldType(current_type) == FieldTypeGroup::kPhone) {
+      if (GroupTypeOfFieldType(current_type) == FieldTypeGroup::kPhone) {
         already_saw_current_type = false;
       }
 
@@ -1910,7 +1907,7 @@
   }
 
   // Determine the parsable labels and write them back.
-  absl::optional<std::vector<std::u16string>> parsable_labels =
+  std::optional<std::vector<std::u16string>> parsable_labels =
       GetParseableLabels(field_labels);
   // If not single label was split, the function can return, because the
   // |parsable_label_| is assigned to |label| by default.
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h
index e1f7344..5dd1a16d 100644
--- a/components/autofill/core/browser/form_structure.h
+++ b/components/autofill/core/browser/form_structure.h
@@ -9,6 +9,7 @@
 
 #include <deque>
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <string_view>
@@ -32,7 +33,6 @@
 #include "components/autofill/core/common/language_code.h"
 #include "components/autofill/core/common/mojom/autofill_types.mojom.h"
 #include "components/autofill/core/common/unique_ids.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -382,7 +382,7 @@
     password_attributes_vote_ = vote;
   }
 
-  absl::optional<std::pair<PasswordAttribute, bool>>
+  std::optional<std::pair<PasswordAttribute, bool>>
   get_password_attributes_vote() const {
     return password_attributes_vote_;
   }
@@ -448,9 +448,9 @@
   // The signatures of forms recently submitted on the same origin within a
   // small period of time.
   struct FormAssociations {
-    absl::optional<FormSignature> last_address_form_submitted;
-    absl::optional<FormSignature> second_last_address_form_submitted;
-    absl::optional<FormSignature> last_credit_card_form_submitted;
+    std::optional<FormSignature> last_address_form_submitted;
+    std::optional<FormSignature> second_last_address_form_submitted;
+    std::optional<FormSignature> last_credit_card_form_submitted;
   };
 
   void set_form_associations(FormAssociations associations) {
@@ -637,7 +637,7 @@
 
   // The vote about password attributes (e.g. whether the password has a numeric
   // character).
-  absl::optional<std::pair<PasswordAttribute, bool>> password_attributes_vote_;
+  std::optional<std::pair<PasswordAttribute, bool>> password_attributes_vote_;
 
   // If |password_attribute_vote_| contains (kHasSpecialSymbol, true), this
   // field contains noisified information about a special symbol in a
diff --git a/components/autofill/core/browser/form_structure_rationalizer.cc b/components/autofill/core/browser/form_structure_rationalizer.cc
index a8956172..66380a81 100644
--- a/components/autofill/core/browser/form_structure_rationalizer.cc
+++ b/components/autofill/core/browser/form_structure_rationalizer.cc
@@ -820,7 +820,7 @@
   // is a country.
   for (int field_index = upper_index - 1; field_index >= 0; --field_index) {
     if ((*fields_)[field_index]->IsFocusable() &&
-        GroupTypeOfServerFieldType(
+        GroupTypeOfFieldType(
             (*fields_)[field_index]->Type().GetStorableType()) ==
             FieldTypeGroup::kAddress &&
         (*fields_)[field_index]->section == (*fields_)[upper_index]->section) {
@@ -1018,7 +1018,7 @@
     const GeoIpCountryCode& client_country,
     const LanguageCode& language_code,
     LogManager* log_manager) {
-  absl::optional<PatternSource> pattern_source = GetActivePatternSource();
+  std::optional<PatternSource> pattern_source = GetActivePatternSource();
   if (!pattern_source.has_value()) {
     pattern_source = PatternSource::kLegacy;
   }
diff --git a/components/autofill/core/browser/form_structure_rationalizer_unittest.cc b/components/autofill/core/browser/form_structure_rationalizer_unittest.cc
index 0909424..ca262b39 100644
--- a/components/autofill/core/browser/form_structure_rationalizer_unittest.cc
+++ b/components/autofill/core/browser/form_structure_rationalizer_unittest.cc
@@ -53,12 +53,12 @@
   // Section name of a field.
   std::string_view section = "";
   FormControlType form_control_type = FormControlType::kInputText;
-  absl::optional<AutocompleteParsingResult> parsed_autocomplete = absl::nullopt;
+  std::optional<AutocompleteParsingResult> parsed_autocomplete = std::nullopt;
   bool is_focusable = true;
   size_t max_length = std::numeric_limits<int>::max();
   FormFieldData::RoleAttribute role = FormFieldData::RoleAttribute::kOther;
-  absl::optional<url::Origin> subframe_origin;
-  absl::optional<FormGlobalId> host_form;
+  std::optional<url::Origin> subframe_origin;
+  std::optional<FormGlobalId> host_form;
   bool field_type_is_override = false;
   // Only appled if BuildFormStructure is called with run_heuristics=false.
   FieldType heuristic_type = UNKNOWN_TYPE;
diff --git a/components/autofill/core/browser/form_structure_sectioning_util.cc b/components/autofill/core/browser/form_structure_sectioning_util.cc
index f731460..8fb89e7b 100644
--- a/components/autofill/core/browser/form_structure_sectioning_util.cc
+++ b/components/autofill/core/browser/form_structure_sectioning_util.cc
@@ -44,8 +44,8 @@
                                  FieldType previous_type) {
   if (previous_type == current_type)
     return true;
-  if (GroupTypeOfServerFieldType(current_type) == FieldTypeGroup::kName &&
-      GroupTypeOfServerFieldType(previous_type) == FieldTypeGroup::kName) {
+  if (GroupTypeOfFieldType(current_type) == FieldTypeGroup::kName &&
+      GroupTypeOfFieldType(previous_type) == FieldTypeGroup::kName) {
     return true;
   }
   if (FieldTypeSet({ADDRESS_HOME_ZIP, ADDRESS_HOME_DEPENDENT_LOCALITY,
@@ -150,7 +150,7 @@
   // There are many phone number field types and their classification is
   // generally a little bit off. Furthermore, forms often ask for multiple phone
   // numbers, e.g. both a daytime and evening phone number.
-  if (GroupTypeOfServerFieldType(current_type) == FieldTypeGroup::kPhone) {
+  if (GroupTypeOfFieldType(current_type) == FieldTypeGroup::kPhone) {
     return true;
   }
 
diff --git a/components/autofill/core/browser/form_structure_sectioning_util_unittest.cc b/components/autofill/core/browser/form_structure_sectioning_util_unittest.cc
index 5a78b5cf..1229de2 100644
--- a/components/autofill/core/browser/form_structure_sectioning_util_unittest.cc
+++ b/components/autofill/core/browser/form_structure_sectioning_util_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/autofill/core/browser/form_structure_sectioning_util.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -22,7 +23,6 @@
 #include "components/autofill/core/common/signatures.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 using autofill::features::kAutofillSectioningModeCreateGaps;
 using autofill::features::kAutofillSectioningModeExpand;
diff --git a/components/autofill/core/browser/form_structure_test_api.cc b/components/autofill/core/browser/form_structure_test_api.cc
index 3a49992..1d13bb6 100644
--- a/components/autofill/core/browser/form_structure_test_api.cc
+++ b/components/autofill/core/browser/form_structure_test_api.cc
@@ -18,7 +18,6 @@
 using FieldPrediction =
     AutofillQueryResponse::FormSuggestion::FieldSuggestion::FieldPrediction;
 
-// static
 void FormStructureTestApi::SetFieldTypes(
     const std::vector<std::vector<std::pair<HeuristicSource, FieldType>>>&
         heuristic_types,
@@ -47,7 +46,7 @@
     const std::vector<FieldType>& server_types) {
   std::vector<FieldPrediction> server_predictions;
   for (FieldType type : server_types) {
-    server_predictions.push_back(::autofill::test::CreateFieldPrediction(type));
+    server_predictions.push_back(test::CreateFieldPrediction(type));
   }
   SetFieldTypes(heuristic_types, server_predictions);
 }
diff --git a/components/autofill/core/browser/form_structure_test_api.h b/components/autofill/core/browser/form_structure_test_api.h
index 410b9ba..d9972695 100644
--- a/components/autofill/core/browser/form_structure_test_api.h
+++ b/components/autofill/core/browser/form_structure_test_api.h
@@ -43,6 +43,11 @@
   explicit FormStructureTestApi(FormStructure* form_structure)
       : form_structure_(*form_structure) {}
 
+  AutofillField& PushField() {
+    form_structure_->fields_.push_back(std::make_unique<AutofillField>());
+    return *form_structure_->fields_.back();
+  }
+
   [[nodiscard]] bool ShouldBeParsed(ShouldBeParsedParams params = {},
                                     LogManager* log_manager = nullptr) {
     return form_structure_->ShouldBeParsed(params, log_manager);
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc
index 3730162..cbefe237 100644
--- a/components/autofill/core/browser/form_structure_unittest.cc
+++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -10,6 +10,7 @@
 #include <algorithm>
 #include <functional>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -46,7 +47,6 @@
 #include "components/version_info/version_info.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/re2/src/re2/re2.h"
 #include "url/gurl.h"
 
@@ -2471,7 +2471,7 @@
   ////////////////
   std::unique_ptr<FormStructure> form_structure;
   std::vector<FieldTypeSet> possible_field_types;
-  std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
+  std::vector<FieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
   form.url = GURL("http://www.foo.com/");
   form.is_form_tag = true;
@@ -2644,7 +2644,7 @@
   ////////////////
   std::unique_ptr<FormStructure> form_structure;
   std::vector<FieldTypeSet> possible_field_types;
-  std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
+  std::vector<FieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
   form.url = GURL("http://www.foo.com/");
 
@@ -2761,7 +2761,7 @@
   ////////////////
   std::unique_ptr<FormStructure> form_structure;
   std::vector<FieldTypeSet> possible_field_types;
-  std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
+  std::vector<FieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
   form.url = GURL("http://www.foo.com/");
   form.is_form_tag = true;
@@ -2879,7 +2879,7 @@
 TEST_F(FormStructureTestImpl, EncodeUploadRequest) {
   std::unique_ptr<FormStructure> form_structure;
   std::vector<FieldTypeSet> possible_field_types;
-  std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
+  std::vector<FieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
   form.url = GURL("http://www.foo.com/");
   form.is_form_tag = true;
@@ -3074,7 +3074,7 @@
        EncodeUploadRequestWithAdditionalPasswordFormSignature) {
   std::unique_ptr<FormStructure> form_structure;
   std::vector<FieldTypeSet> possible_field_types;
-  std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
+  std::vector<FieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
   form.url = GURL("http://www.foo.com/");
   form.is_form_tag = true;
@@ -3181,7 +3181,7 @@
 TEST_F(FormStructureTestImpl, EncodeUploadRequestWithPropertiesMask) {
   std::unique_ptr<FormStructure> form_structure;
   std::vector<FieldTypeSet> possible_field_types;
-  std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
+  std::vector<FieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
   form.url = GURL("http://www.foo.com/");
   form.is_form_tag = true;
@@ -3265,7 +3265,7 @@
 TEST_F(FormStructureTestImpl, EncodeUploadRequest_ObservedSubmissionFalse) {
   std::unique_ptr<FormStructure> form_structure;
   std::vector<FieldTypeSet> possible_field_types;
-  std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
+  std::vector<FieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
   form.url = GURL("http://www.foo.com/");
   form.is_form_tag = true;
@@ -3343,7 +3343,7 @@
 TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithLabels) {
   std::unique_ptr<FormStructure> form_structure;
   std::vector<FieldTypeSet> possible_field_types;
-  std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
+  std::vector<FieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
   form.url = GURL("http://www.foo.com/");
   form.is_form_tag = true;
@@ -3416,7 +3416,7 @@
 TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithSubForms) {
   std::unique_ptr<FormStructure> form_structure;
   std::vector<FieldTypeSet> possible_field_types;
-  std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
+  std::vector<FieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
   form.host_frame = test::MakeLocalFrameToken();
   form.url = GURL("http://www.foo.com/");
@@ -3581,7 +3581,7 @@
     fs_field->host_form_signature = form_structure.form_signature();
 
   std::vector<FieldTypeSet> possible_field_types;
-  std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
+  std::vector<FieldTypeValidityStatesMap> possible_field_types_validities;
 
   for (size_t i = 0; i < form_structure.field_count(); ++i) {
     test::InitializePossibleTypesAndValidities(
@@ -3803,7 +3803,7 @@
 
   // Check that multiple types for the field are processed correctly.
   std::vector<FieldTypeSet> possible_field_types;
-  std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
+  std::vector<FieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
   form.url = GURL("http://www.foo.com/");
   form.is_form_tag = false;
diff --git a/components/autofill/core/browser/geo/alternative_state_name_map.cc b/components/autofill/core/browser/geo/alternative_state_name_map.cc
index ed22752..8df7532 100644
--- a/components/autofill/core/browser/geo/alternative_state_name_map.cc
+++ b/components/autofill/core/browser/geo/alternative_state_name_map.cc
@@ -37,7 +37,7 @@
 }
 
 // static
-absl::optional<AlternativeStateNameMap::CanonicalStateName>
+std::optional<AlternativeStateNameMap::CanonicalStateName>
 AlternativeStateNameMap::GetCanonicalStateName(
     const std::string& country_code,
     const std::u16string& state_name) {
@@ -48,7 +48,7 @@
 
 AlternativeStateNameMap::AlternativeStateNameMap() = default;
 
-absl::optional<AlternativeStateNameMap::CanonicalStateName>
+std::optional<AlternativeStateNameMap::CanonicalStateName>
 AlternativeStateNameMap::GetCanonicalStateName(
     const CountryCode& country_code,
     const StateName& state_name,
@@ -78,15 +78,15 @@
   if (it != localized_state_names_reverse_lookup_map_.end())
     return it->second;
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<StateEntry> AlternativeStateNameMap::GetEntry(
+std::optional<StateEntry> AlternativeStateNameMap::GetEntry(
     const CountryCode& country_code,
     const StateName& state_string_from_profile) const {
   StateName normalized_state_string_from_profile =
       NormalizeStateName(state_string_from_profile);
-  absl::optional<CanonicalStateName> canonical_state_name =
+  std::optional<CanonicalStateName> canonical_state_name =
       GetCanonicalStateName(country_code, normalized_state_string_from_profile,
                             /*is_state_name_normalized=*/true);
 
@@ -98,7 +98,7 @@
       return it->second;
   }
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 void AlternativeStateNameMap::AddEntry(
diff --git a/components/autofill/core/browser/geo/alternative_state_name_map.h b/components/autofill/core/browser/geo/alternative_state_name_map.h
index b4ff808e..c1e48ee8 100644
--- a/components/autofill/core/browser/geo/alternative_state_name_map.h
+++ b/components/autofill/core/browser/geo/alternative_state_name_map.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_GEO_ALTERNATIVE_STATE_NAME_MAP_H_
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_GEO_ALTERNATIVE_STATE_NAME_MAP_H_
 
+#include <optional>
 #include <string>
 
 #include "base/i18n/case_conversion.h"
@@ -13,7 +14,6 @@
 #include "base/thread_annotations.h"
 #include "base/types/strong_alias.h"
 #include "components/autofill/core/browser/proto/states.pb.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -89,7 +89,7 @@
   // Calls |GetCanonicalStateName()| member method of AlternativeStateNameMap
   // and returns the canonical state name corresponding to |country_code| and
   // |state_name| if present.
-  static absl::optional<AlternativeStateNameMap::CanonicalStateName>
+  static std::optional<AlternativeStateNameMap::CanonicalStateName>
   GetCanonicalStateName(const std::string& country_code,
                         const std::u16string& state_name);
 
@@ -102,15 +102,15 @@
   // (|country_code|, |state_name|).
   // |is_state_name_normalized| denotes whether the |state_name| has been
   // normalized or not.
-  absl::optional<CanonicalStateName> GetCanonicalStateName(
+  std::optional<CanonicalStateName> GetCanonicalStateName(
       const CountryCode& country_code,
       const StateName& state_name,
       bool is_state_name_normalized = false) const;
 
   // Returns the value present in |localized_state_names_map_| corresponding
   // to (|country_code|, |state_string_from_profile|). In case, the entry does
-  // not exist in the map, absl::nullopt is returned.
-  absl::optional<StateEntry> GetEntry(
+  // not exist in the map, std::nullopt is returned.
+  std::optional<StateEntry> GetEntry(
       const CountryCode& country_code,
       const StateName& state_string_from_profile) const;
 
diff --git a/components/autofill/core/browser/geo/alternative_state_name_map_unittest.cc b/components/autofill/core/browser/geo/alternative_state_name_map_unittest.cc
index be9f7969..996db24 100644
--- a/components/autofill/core/browser/geo/alternative_state_name_map_unittest.cc
+++ b/components/autofill/core/browser/geo/alternative_state_name_map_unittest.cc
@@ -31,15 +31,15 @@
     SCOPED_TRACE(valid_match);
     EXPECT_NE(AlternativeStateNameMap::GetCanonicalStateName(
                   "DE", base::ASCIIToUTF16(valid_match)),
-              absl::nullopt);
+              std::nullopt);
   }
 
   EXPECT_EQ(AlternativeStateNameMap::GetCanonicalStateName("US", u"Bavaria"),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(AlternativeStateNameMap::GetCanonicalStateName("DE", u""),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(AlternativeStateNameMap::GetCanonicalStateName("", u""),
-            absl::nullopt);
+            std::nullopt);
 }
 
 // Tests that the separate entries are created in the map for the different
@@ -49,9 +49,9 @@
   test::PopulateAlternativeStateNameMapForTesting("DE");
   test::PopulateAlternativeStateNameMapForTesting("US");
   EXPECT_NE(AlternativeStateNameMap::GetCanonicalStateName("DE", u"Bavaria"),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_NE(AlternativeStateNameMap::GetCanonicalStateName("US", u"Bavaria"),
-            absl::nullopt);
+            std::nullopt);
 }
 
 // Tests that |AlternativeStateNameMap::NormalizeStateName()| removes "-", " "
@@ -84,11 +84,11 @@
   EXPECT_EQ(alternative_state_name_map->GetEntry(
                 AlternativeStateNameMap::CountryCode("DE"),
                 AlternativeStateNameMap::StateName(u"Random")),
-            absl::nullopt);
+            std::nullopt);
   auto entry = alternative_state_name_map->GetEntry(
       AlternativeStateNameMap::CountryCode("DE"),
       AlternativeStateNameMap::StateName(u"Bavaria"));
-  EXPECT_NE(entry, absl::nullopt);
+  EXPECT_NE(entry, std::nullopt);
   ASSERT_TRUE(entry->has_canonical_name());
   EXPECT_EQ(entry->canonical_name(), "Bavaria");
   EXPECT_THAT(entry->abbreviations(),
diff --git a/components/autofill/core/browser/geo/alternative_state_name_map_updater_unittest.cc b/components/autofill/core/browser/geo/alternative_state_name_map_updater_unittest.cc
index e75ab01..f619f63 100644
--- a/components/autofill/core/browser/geo/alternative_state_name_map_updater_unittest.cc
+++ b/components/autofill/core/browser/geo/alternative_state_name_map_updater_unittest.cc
@@ -3,6 +3,9 @@
 // found in the LICENSE file.
 
 #include "components/autofill/core/browser/geo/alternative_state_name_map_updater.h"
+
+#include <optional>
+
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -23,7 +26,6 @@
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 using base::ASCIIToUTF16;
 using base::UTF8ToUTF16;
@@ -98,7 +100,7 @@
   for (size_t i = 0; i < test_strings.size(); i++) {
     SCOPED_TRACE(test_strings[i]);
     EXPECT_EQ(AlternativeStateNameMap::GetCanonicalStateName(
-                  "DE", test_strings[i].value()) != absl::nullopt,
+                  "DE", test_strings[i].value()) != std::nullopt,
               state_data_present[i]);
   }
 }
@@ -121,7 +123,7 @@
   run_loop.Run();
 
   EXPECT_NE(AlternativeStateNameMap::GetCanonicalStateName("DE", u"Bavaria"),
-            absl::nullopt);
+            std::nullopt);
 }
 
 // Tests that there is no insertion in the AlternativeStateNameMap when a
@@ -170,22 +172,22 @@
       run_loop.QuitClosure());
   run_loop.Run();
 
-  absl::optional<StateEntry> entry1 =
+  std::optional<StateEntry> entry1 =
       AlternativeStateNameMap::GetInstance()->GetEntry(
           AlternativeStateNameMap::CountryCode("ES"),
           AlternativeStateNameMap::StateName(u"Paraná"));
-  EXPECT_NE(entry1, absl::nullopt);
+  EXPECT_NE(entry1, std::nullopt);
   EXPECT_EQ(entry1->canonical_name(), "Paraná");
   EXPECT_THAT(entry1->abbreviations(),
               testing::UnorderedElementsAreArray({"PR"}));
   EXPECT_THAT(entry1->alternative_names(), testing::UnorderedElementsAreArray(
                                                {"Parana", "State of Parana"}));
 
-  absl::optional<StateEntry> entry2 =
+  std::optional<StateEntry> entry2 =
       AlternativeStateNameMap::GetInstance()->GetEntry(
           AlternativeStateNameMap::CountryCode("ES"),
           AlternativeStateNameMap::StateName(u"Parana"));
-  EXPECT_NE(entry2, absl::nullopt);
+  EXPECT_NE(entry2, std::nullopt);
   EXPECT_EQ(entry2->canonical_name(), "Paraná");
   EXPECT_THAT(entry2->abbreviations(),
               testing::UnorderedElementsAreArray({"PR"}));
@@ -222,22 +224,22 @@
       run_loop.QuitClosure());
   run_loop.Run();
 
-  absl::optional<StateEntry> entry1 =
+  std::optional<StateEntry> entry1 =
       AlternativeStateNameMap::GetInstance()->GetEntry(
           AlternativeStateNameMap::CountryCode("ES"),
           AlternativeStateNameMap::StateName(u"Paraná"));
-  EXPECT_NE(entry1, absl::nullopt);
+  EXPECT_NE(entry1, std::nullopt);
   EXPECT_EQ(entry1->canonical_name(), "Paraná");
   EXPECT_THAT(entry1->abbreviations(),
               testing::UnorderedElementsAreArray({"PR"}));
   EXPECT_THAT(entry1->alternative_names(), testing::UnorderedElementsAreArray(
                                                {"Parana", "State of Parana"}));
 
-  absl::optional<StateEntry> entry2 =
+  std::optional<StateEntry> entry2 =
       AlternativeStateNameMap::GetInstance()->GetEntry(
           AlternativeStateNameMap::CountryCode("DE"),
           AlternativeStateNameMap::StateName(u"Bavaria"));
-  EXPECT_NE(entry2, absl::nullopt);
+  EXPECT_NE(entry2, std::nullopt);
   EXPECT_EQ(entry2->canonical_name(), "Bavaria");
   EXPECT_THAT(entry2->abbreviations(),
               testing::UnorderedElementsAreArray({"BY"}));
diff --git a/components/autofill/core/browser/geo/autofill_country.cc b/components/autofill/core/browser/geo/autofill_country.cc
index 86c2afa..a37c662 100644
--- a/components/autofill/core/browser/geo/autofill_country.cc
+++ b/components/autofill/core/browser/geo/autofill_country.cc
@@ -82,7 +82,7 @@
 }  // namespace
 
 AutofillCountry::AutofillCountry(const std::string& country_code,
-                                 const absl::optional<std::string>& locale) {
+                                 const std::optional<std::string>& locale) {
   CountryDataMap* country_data_map = CountryDataMap::GetInstance();
 
   // If the country code is an alias (e.g. "GB" for "UK") expand the country
diff --git a/components/autofill/core/browser/geo/autofill_country.h b/components/autofill/core/browser/geo/autofill_country.h
index b89bfbaa..3fa6d062 100644
--- a/components/autofill/core/browser/geo/autofill_country.h
+++ b/components/autofill/core/browser/geo/autofill_country.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_GEO_AUTOFILL_COUNTRY_H_
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_GEO_AUTOFILL_COUNTRY_H_
 
+#include <optional>
 #include <string>
 #include <string_view>
 
@@ -13,7 +14,6 @@
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/geo/country_data.h"
 #include "components/autofill/core/common/autofill_features.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_field.h"
 
 namespace autofill {
@@ -30,7 +30,7 @@
   // if the name is not queried.
   explicit AutofillCountry(
       const std::string& country_code,
-      const absl::optional<std::string>& locale = absl::nullopt);
+      const std::optional<std::string>& locale = std::nullopt);
 
   AutofillCountry(const AutofillCountry&) = delete;
   AutofillCountry& operator=(const AutofillCountry&) = delete;
diff --git a/components/autofill/core/browser/heuristic_classification_unittests.cc b/components/autofill/core/browser/heuristic_classification_unittests.cc
index 3e46555c..a4573a0 100644
--- a/components/autofill/core/browser/heuristic_classification_unittests.cc
+++ b/components/autofill/core/browser/heuristic_classification_unittests.cc
@@ -499,7 +499,7 @@
   ASSERT_TRUE(base::ReadFileToString(input_file, &input_json_text));
 
   // Convert to JSON dictionary.
-  absl::optional<base::Value> opt_json_file =
+  std::optional<base::Value> opt_json_file =
       base::JSONReader::Read(input_json_text);
   ASSERT_TRUE(opt_json_file);
   base::Value::Dict* json_file = opt_json_file->GetIfDict();
@@ -538,7 +538,8 @@
       // Other improvements.
       features::kAutofillEnableZipOnlyAddressForms,
       features::kAutofillDefaultToCityAndNumber,
-      features::kAutofillPreferLabelsInSomeCountries};
+      features::kAutofillPreferLabelsInSomeCountries,
+      features::kAutofillEnableCacheForRegexMatching};
   std::vector<base::test::FeatureRef> disabled_features = {};
 
   auto init_feature_to_value = [&](base::test::FeatureRef feature, bool value) {
@@ -590,7 +591,7 @@
   std::string new_stats = SummarizeStatistics(*json_file);
 
   // Serialize the result.
-  absl::optional<std::string> output_json_text =
+  std::optional<std::string> output_json_text =
       base::WriteJsonWithOptions(*opt_json_file, base::OPTIONS_PRETTY_PRINT);
   ASSERT_TRUE(output_json_text);
 
diff --git a/components/autofill/core/browser/heuristic_source.cc b/components/autofill/core/browser/heuristic_source.cc
index d772c95d..5f8d82e 100644
--- a/components/autofill/core/browser/heuristic_source.cc
+++ b/components/autofill/core/browser/heuristic_source.cc
@@ -52,7 +52,7 @@
   return sources;
 }
 
-absl::optional<PatternSource> HeuristicSourceToPatternSource(
+std::optional<PatternSource> HeuristicSourceToPatternSource(
     HeuristicSource source) {
   switch (source) {
     case HeuristicSource::kLegacy:
@@ -66,7 +66,7 @@
       return PatternSource::kNextGen;
 #endif
     case autofill::HeuristicSource::kMachineLearning:
-      return absl::nullopt;
+      return std::nullopt;
   }
   NOTREACHED_NORETURN();
 }
diff --git a/components/autofill/core/browser/heuristic_source.h b/components/autofill/core/browser/heuristic_source.h
index 88ec2a4..90bec6ba9 100644
--- a/components/autofill/core/browser/heuristic_source.h
+++ b/components/autofill/core/browser/heuristic_source.h
@@ -43,7 +43,7 @@
 
 // Converts a `HeuristicSource` to `PatternSource`. If the passed
 // source is not a `PatternSource` then a nullopt is returned.
-absl::optional<PatternSource> HeuristicSourceToPatternSource(
+std::optional<PatternSource> HeuristicSourceToPatternSource(
     HeuristicSource source);
 HeuristicSource PatternSourceToHeuristicSource(PatternSource source);
 
diff --git a/components/autofill/core/browser/heuristic_source_unittest.cc b/components/autofill/core/browser/heuristic_source_unittest.cc
index ef3ce653..6bd5a559 100644
--- a/components/autofill/core/browser/heuristic_source_unittest.cc
+++ b/components/autofill/core/browser/heuristic_source_unittest.cc
@@ -24,8 +24,8 @@
 // as the active heuristic source.
 
 struct HeuristicSourceParams {
-  absl::optional<bool> model_predictions_feature;
-  absl::optional<std::string> pattern_provider_feature;
+  std::optional<bool> model_predictions_feature;
+  std::optional<std::string> pattern_provider_feature;
   const HeuristicSource expected_active_source;
   const DenseSet<HeuristicSource> expected_nonactive_sources;
 };
diff --git a/components/autofill/core/browser/logging/log_buffer_submitter.cc b/components/autofill/core/browser/logging/log_buffer_submitter.cc
index 432ae63..1b818e6 100644
--- a/components/autofill/core/browser/logging/log_buffer_submitter.cc
+++ b/components/autofill/core/browser/logging/log_buffer_submitter.cc
@@ -31,7 +31,7 @@
 LogBufferSubmitter::~LogBufferSubmitter() {
   if (!destruct_with_logging_ || !log_manager_)
     return;
-  absl::optional<base::Value::Dict> message = buffer_.RetrieveResult();
+  std::optional<base::Value::Dict> message = buffer_.RetrieveResult();
   if (!message)
     return;
   log_manager_->ProcessLog(std::move(*message), {});
diff --git a/components/autofill/core/browser/logging/log_buffer_submitter_unittest.cc b/components/autofill/core/browser/logging/log_buffer_submitter_unittest.cc
index 92b97a8..1f020cd4 100644
--- a/components/autofill/core/browser/logging/log_buffer_submitter_unittest.cc
+++ b/components/autofill/core/browser/logging/log_buffer_submitter_unittest.cc
@@ -26,7 +26,7 @@
 TEST(LogBufferSubmitter, VerifySubmissionOnDestruction) {
   LogBuffer buffer;
   buffer << 42;
-  absl::optional<base::Value::Dict> expected = buffer.RetrieveResult();
+  std::optional<base::Value::Dict> expected = buffer.RetrieveResult();
 
   MockLogReceiver receiver;
   LogRouter router;
diff --git a/components/autofill/core/browser/logging/log_manager.cc b/components/autofill/core/browser/logging/log_manager.cc
index 86a6d1d01..f5260648 100644
--- a/components/autofill/core/browser/logging/log_manager.cc
+++ b/components/autofill/core/browser/logging/log_manager.cc
@@ -114,7 +114,7 @@
 
  private:
   std::vector<base::Value::Dict> nodes_;
-  absl::optional<base::PassKey<LogBufferSubmitter>> pass_key_;
+  std::optional<base::PassKey<LogBufferSubmitter>> pass_key_;
 };
 
 void BufferingLogManagerImpl::Flush(LogManager& destination) {
diff --git a/components/autofill/core/browser/logging/text_log_receiver_unittest.cc b/components/autofill/core/browser/logging/text_log_receiver_unittest.cc
index 70ac7ab..ad6d06dc 100644
--- a/components/autofill/core/browser/logging/text_log_receiver_unittest.cc
+++ b/components/autofill/core/browser/logging/text_log_receiver_unittest.cc
@@ -10,7 +10,7 @@
 namespace autofill {
 
 TEST(TextLogReceiver, IntegrationTest) {
-  absl::optional<base::Value> input = base::JSONReader::Read(
+  std::optional<base::Value> input = base::JSONReader::Read(
       R"(
     {
       "type": "element",
diff --git a/components/autofill/core/browser/manual_testing_import.cc b/components/autofill/core/browser/manual_testing_import.cc
index 8e441c1..c2cb910 100644
--- a/components/autofill/core/browser/manual_testing_import.cc
+++ b/components/autofill/core/browser/manual_testing_import.cc
@@ -28,15 +28,15 @@
 namespace {
 
 // Util struct for storing the list of profiles and credit cards to be imported.
-// If any of `profiles` or `credit_cards` are absl::nullopt, then the data used
+// If any of `profiles` or `credit_cards` are std::nullopt, then the data used
 // for import is malformed, and this will cause a crash.
 // When any of `profiles` or `credit_cards` is empty, it means that the JSON
 // file used for import did not include the corresponding key. It'll be treated
 // as valid but won't be imported so that existing data in the PDM isn't
 // cleared without replacement.
 struct AutofillProfilesAndCreditCards {
-  absl::optional<std::vector<AutofillProfile>> profiles;
-  absl::optional<std::vector<CreditCard>> credit_cards;
+  std::optional<std::vector<AutofillProfile>> profiles;
+  std::optional<std::vector<CreditCard>> credit_cards;
 };
 
 constexpr std::string_view kKeyProfiles = "profiles";
@@ -64,8 +64,8 @@
 // Extracts the `kKeySource` value of the `dict` and translates it into an
 // AutofillProfile::Source. If no source is present, Source::kLocalOrSyncable is
 // returned. If a source with invalid value is specified, an error message is
-// logged and absl::nullopt is returned.
-absl::optional<AutofillProfile::Source> GetProfileSourceFromDict(
+// logged and std::nullopt is returned.
+std::optional<AutofillProfile::Source> GetProfileSourceFromDict(
     const base::Value::Dict& dict) {
   if (!dict.contains(kKeySource)) {
     return AutofillProfile::Source::kLocalOrSyncable;
@@ -77,7 +77,7 @@
     }
   }
   LOG(ERROR) << "Invalid " << kKeySource << " value.";
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 // Given a `dict` of "field-type" : "value" mappings, constructs an
@@ -86,12 +86,12 @@
 // `kUserVerified` is problematic, since the data model expects that only root
 // level (= setting-visible) nodes are user verified.
 // If a field type cannot be mapped, or if the resulting profile is not
-// `IsFullyStructuredProfile()`, absl::nullopt is returned.
-absl::optional<AutofillProfile> MakeProfile(const base::Value::Dict& dict) {
-  absl::optional<AutofillProfile::Source> source =
+// `IsFullyStructuredProfile()`, std::nullopt is returned.
+std::optional<AutofillProfile> MakeProfile(const base::Value::Dict& dict) {
+  std::optional<AutofillProfile::Source> source =
       GetProfileSourceFromDict(dict);
   if (!source.has_value()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const std::string* country_code =
       dict.FindString(FieldTypeToStringView(ADDRESS_HOME_COUNTRY));
@@ -105,18 +105,18 @@
       continue;
     }
     if (key == kKeyInitialCreatorId) {
-      if (const absl::optional<int> creator_id = dict.FindInt(key)) {
+      if (const std::optional<int> creator_id = dict.FindInt(key)) {
         profile.set_initial_creator_id(*creator_id);
         continue;
       } else {
         LOG(ERROR) << "Incorrect value for " << key << ".";
-        return absl::nullopt;
+        return std::nullopt;
       }
     }
     const FieldType type = TypeNameToFieldType(key);
     if (type == UNKNOWN_TYPE || !IsAddressType(type)) {
       LOG(ERROR) << "Unknown or non-address type " << key << ".";
-      return absl::nullopt;
+      return std::nullopt;
     }
     profile.SetRawInfoWithVerificationStatus(
         type, base::UTF8ToUTF16(value.GetString()),
@@ -124,12 +124,12 @@
   }
   if (!IsFullyStructuredProfile(profile)) {
     LOG(ERROR) << "Some profile is not fully structured.";
-    return absl::nullopt;
+    return std::nullopt;
   }
   return profile;
 }
 
-absl::optional<CreditCard> MakeCard(const base::Value::Dict& dict) {
+std::optional<CreditCard> MakeCard(const base::Value::Dict& dict) {
   CreditCard card;
   // `dict` is a dictionary of std::string -> base::Value.
   for (const auto [key, value] : dict) {
@@ -139,15 +139,15 @@
     }
     const FieldType type = TypeNameToFieldType(key);
     if (type == UNKNOWN_TYPE ||
-        GroupTypeOfServerFieldType(type) != FieldTypeGroup::kCreditCard) {
+        GroupTypeOfFieldType(type) != FieldTypeGroup::kCreditCard) {
       LOG(ERROR) << "Unknown or non-credit card type " << key << ".";
-      return absl::nullopt;
+      return std::nullopt;
     }
     card.SetRawInfo(type, base::UTF8ToUTF16(value.GetString()));
   }
   if (!card.IsValid()) {
     LOG(ERROR) << "Some credit card is not valid.";
-    return absl::nullopt;
+    return std::nullopt;
   }
   return card;
 }
@@ -168,7 +168,7 @@
 // `credit_cards`, if the `pdm` still exists.
 void SetData(
     base::WeakPtr<PersonalDataManager> pdm,
-    absl::optional<AutofillProfilesAndCreditCards> profiles_or_credit_cards) {
+    std::optional<AutofillProfilesAndCreditCards> profiles_or_credit_cards) {
   // This check intentionally crashes when the data is malformed, to prevent
   // testing with incorrect data.
   LOG_IF(FATAL, !profiles_or_credit_cards.has_value() ||
@@ -194,9 +194,9 @@
 // Converts all `entries of `json_array` to a vector of Ts using
 // `to_data_model`. In case any conversion fails, nullopt is returned.
 template <class T>
-absl::optional<std::vector<T>> DataModelsFromJSON(
+std::optional<std::vector<T>> DataModelsFromJSON(
     const base::Value::List* const json_array,
-    base::RepeatingCallback<absl::optional<T>(const base::Value::Dict&)>
+    base::RepeatingCallback<std::optional<T>(const base::Value::Dict&)>
         to_data_model) {
   if (!json_array) {
     return std::vector<T>{};
@@ -205,11 +205,11 @@
   for (const base::Value& json : *json_array) {
     if (!json.is_dict()) {
       LOG(ERROR) << "Description is not a dictionary.";
-      return absl::nullopt;
+      return std::nullopt;
     }
-    absl::optional<T> data_model = to_data_model.Run(json.GetDict());
+    std::optional<T> data_model = to_data_model.Run(json.GetDict());
     if (!data_model.has_value()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     data_models.push_back(std::move(*data_model));
   }
@@ -218,17 +218,17 @@
 }
 
 // Parses AutofillProfiles from the JSON `content` string.
-// If parsing fails the error is logged and absl::nullopt is returned.
-absl::optional<AutofillProfilesAndCreditCards> LoadDataFromJSONContent(
+// If parsing fails the error is logged and std::nullopt is returned.
+std::optional<AutofillProfilesAndCreditCards> LoadDataFromJSONContent(
     const std::string& file_content) {
-  absl::optional<base::Value> json = base::JSONReader::Read(file_content);
+  std::optional<base::Value> json = base::JSONReader::Read(file_content);
   if (!json.has_value()) {
     LOG(ERROR) << "Failed to parse JSON file.";
-    return absl::nullopt;
+    return std::nullopt;
   }
   if (!json->is_dict()) {
     LOG(ERROR) << "JSON is not a dictionary at it's top level.";
-    return absl::nullopt;
+    return std::nullopt;
   }
   const base::Value::List* const profiles_json =
       json->GetDict().FindList(kKeyProfiles);
@@ -237,49 +237,49 @@
   if (!cards_json && !profiles_json) {
     LOG(ERROR) << "JSON has no " << kKeyProfiles << " or " << kKeyCreditCards
                << " keys.";
-    return absl::nullopt;
+    return std::nullopt;
   }
   return AutofillProfilesAndCreditCards{
       .profiles = AutofillProfilesFromJSON(profiles_json),
       .credit_cards = CreditCardsFromJSON(cards_json)};
 }
 
-absl::optional<AutofillProfilesAndCreditCards> LoadDataFromFile(
+std::optional<AutofillProfilesAndCreditCards> LoadDataFromFile(
     base::FilePath file) {
   std::string file_content;
   if (!base::ReadFileToString(file, &file_content)) {
     LOG(ERROR) << "Failed to read file " << file.MaybeAsASCII() << ".";
-    return absl::nullopt;
+    return std::nullopt;
   }
   return LoadDataFromJSONContent(file_content);
 }
 
 }  // namespace
 
-absl::optional<std::vector<AutofillProfile>> LoadProfilesFromFile(
+std::optional<std::vector<AutofillProfile>> LoadProfilesFromFile(
     base::FilePath file) {
-  if (absl::optional<AutofillProfilesAndCreditCards> profiles_and_credit_cards =
+  if (std::optional<AutofillProfilesAndCreditCards> profiles_and_credit_cards =
           LoadDataFromFile(file)) {
     return profiles_and_credit_cards->profiles;
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<std::vector<CreditCard>> LoadCreditCardsFromFile(
+std::optional<std::vector<CreditCard>> LoadCreditCardsFromFile(
     base::FilePath file) {
-  if (absl::optional<AutofillProfilesAndCreditCards> profiles_and_credit_cards =
+  if (std::optional<AutofillProfilesAndCreditCards> profiles_and_credit_cards =
           LoadDataFromFile(file)) {
     return profiles_and_credit_cards->credit_cards;
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<std::vector<AutofillProfile>> AutofillProfilesFromJSON(
+std::optional<std::vector<AutofillProfile>> AutofillProfilesFromJSON(
     const base::Value::List* const profiles_json) {
   return DataModelsFromJSON(profiles_json, base::BindRepeating(&MakeProfile));
 }
 
-absl::optional<std::vector<CreditCard>> CreditCardsFromJSON(
+std::optional<std::vector<CreditCard>> CreditCardsFromJSON(
     const base::Value::List* const cards_json) {
   return DataModelsFromJSON(cards_json, base::BindRepeating(&MakeCard));
 }
diff --git a/components/autofill/core/browser/manual_testing_import.h b/components/autofill/core/browser/manual_testing_import.h
index f0666182..52adbc0 100644
--- a/components/autofill/core/browser/manual_testing_import.h
+++ b/components/autofill/core/browser/manual_testing_import.h
@@ -5,13 +5,13 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_MANUAL_TESTING_IMPORT_H_
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_MANUAL_TESTING_IMPORT_H_
 
+#include <optional>
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -61,34 +61,34 @@
 
 // Reads the contents of `file`, parses it as a JSON file and converts its
 // content into a list of AutofillProfiles.
-// If any step fails, an error message is logged and absl::nullopt is returned.
-absl::optional<std::vector<AutofillProfile>> LoadProfilesFromFile(
+// If any step fails, an error message is logged and std::nullopt is returned.
+std::optional<std::vector<AutofillProfile>> LoadProfilesFromFile(
     base::FilePath file);
 
 // Reads the contents of `file`, parses it as a JSON file and converts its
 // content into a list of CreditCards.
-// If any step fails, an error message is logged and absl::nullopt is returned.
-absl::optional<std::vector<CreditCard>> LoadCreditCardsFromFile(
+// If any step fails, an error message is logged and std::nullopt is returned.
+std::optional<std::vector<CreditCard>> LoadCreditCardsFromFile(
     base::FilePath file);
 
 // Given the array of descriptions of fully structured profiles in the
 // aforementioned JSON format, converts it to a vector of AutofillProfiles.
 // If the JSON list doesn't adhere to the above format, or if any of the
-// profiles is not fully structured, an error is logged and absl::nullopt is
+// profiles is not fully structured, an error is logged and std::nullopt is
 // returned. A profile is considered "fully structured" if
 // `FinalizeAfterImport()` doesn't change it. This condition exists to prevent
 // profiles from silently changing, since `FinalizeAfterImport()` is called when
 // retrieving a profile from the database. For example, if the structure is
 // invalid because the last name is not part of the full name, the routine will
 // clear this information.
-absl::optional<std::vector<AutofillProfile>> AutofillProfilesFromJSON(
+std::optional<std::vector<AutofillProfile>> AutofillProfilesFromJSON(
     const base::Value::List* const profiles_json);
 
 // Given the array of valid credit cards in the aforementioned JSON format,
 // converts it to a vector of CreditCards.
 // If the JSON list doesn't adhere to the above format, an error message is
-// logged and absl::nullopt is returned.
-absl::optional<std::vector<CreditCard>> CreditCardsFromJSON(
+// logged and std::nullopt is returned.
+std::optional<std::vector<CreditCard>> CreditCardsFromJSON(
     const base::Value::List* const cards_json);
 
 // Checks if the `kManualImportForTestingFlag` flag is present. If so,
diff --git a/components/autofill/core/browser/manual_testing_import_unittest.cc b/components/autofill/core/browser/manual_testing_import_unittest.cc
index 4fb66ef..ae956c46 100644
--- a/components/autofill/core/browser/manual_testing_import_unittest.cc
+++ b/components/autofill/core/browser/manual_testing_import_unittest.cc
@@ -8,6 +8,7 @@
 #include "components/autofill/core/browser/country_type.h"
 #include "components/autofill/core/browser/manual_testing_import.h"
 
+#include <optional>
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -19,7 +20,6 @@
 #include "components/autofill/core/common/autofill_features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -366,7 +366,7 @@
       ADDRESS_HOME_STREET_NAME, u"street", VerificationStatus::kObserved);
   expected_profile2.SetRawInfoWithVerificationStatus(
       ADDRESS_HOME_HOUSE_NUMBER, u"123", VerificationStatus::kObserved);
-  absl::optional<std::vector<AutofillProfile>> loaded_profiles =
+  std::optional<std::vector<AutofillProfile>> loaded_profiles =
       LoadProfilesFromFile(file_path);
   EXPECT_THAT(loaded_profiles, testing::Optional(testing::Pointwise(
                                    DataModelsCompareEqual(),
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.cc b/components/autofill/core/browser/metrics/autofill_metrics.cc
index 19ad8f4..9e9d50c 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics.cc
@@ -158,7 +158,7 @@
   DCHECK_LT(metric, AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
 
   FieldTypeGroupForMetrics group = GROUP_AMBIGUOUS;
-  switch (GroupTypeOfServerFieldType(field_type)) {
+  switch (GroupTypeOfFieldType(field_type)) {
     case FieldTypeGroup::kNoGroup:
       group = GROUP_AMBIGUOUS;
       break;
@@ -746,13 +746,6 @@
 const int kMaxBucketsCount = 50;
 
 // static
-void AutofillMetrics::LogProfileSuggestionsMadeWithFormatter(
-    bool made_with_formatter) {
-  UMA_HISTOGRAM_BOOLEAN("Autofill.ProfileSuggestionsMadeWithFormatter",
-                        made_with_formatter);
-}
-
-// static
 void AutofillMetrics::LogSubmittedCardStateMetric(
     SubmittedCardStateMetric metric) {
   DCHECK_LT(metric, NUM_SUBMITTED_CARD_STATE_METRICS);
@@ -2823,7 +2816,7 @@
     bool suggestion_filled,
     const FormInteractionCounts& form_interaction_counts,
     const FormInteractionsFlowId& flow_id,
-    absl::optional<int64_t> fast_checkout_run_id) {
+    std::optional<int64_t> fast_checkout_run_id) {
   if (!CanLog())
     return;
 
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.h b/components/autofill/core/browser/metrics/autofill_metrics.h
index a7c9722..6a7ce14 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics.h
+++ b/components/autofill/core/browser/metrics/autofill_metrics.h
@@ -776,7 +776,7 @@
                        bool suggestion_filled,
                        const FormInteractionCounts& form_interaction_counts,
                        const FormInteractionsFlowId& flow_id,
-                       absl::optional<int64_t> fast_checkout_run_id);
+                       std::optional<int64_t> fast_checkout_run_id);
     void LogFormEvent(autofill_metrics::FormEvent form_event,
                       const DenseSet<FormType>& form_types,
                       const base::TimeTicks& form_parsed_timestamp);
@@ -813,7 +813,7 @@
     raw_ptr<AutofillClient> autofill_client_;
     raw_ptr<ukm::UkmRecorder> ukm_recorder_;
 
-    absl::optional<ukm::SourceId> source_id_;
+    std::optional<ukm::SourceId> source_id_;
     base::TimeTicks pinned_timestamp_;
   };
 
@@ -867,11 +867,6 @@
   AutofillMetrics(const AutofillMetrics&) = delete;
   AutofillMetrics& operator=(const AutofillMetrics&) = delete;
 
-  // When the autofill-use-improved-label-disambiguation experiment is enabled
-  // and suggestions are available, records if a LabelFormatter successfully
-  // created the suggestions.
-  static void LogProfileSuggestionsMadeWithFormatter(bool made_with_formatter);
-
   static void LogSubmittedCardStateMetric(SubmittedCardStateMetric metric);
 
   // If a credit card that matches a server card (unmasked or not) was submitted
diff --git a/components/autofill/core/browser/metrics/autofill_metrics_test_base.cc b/components/autofill/core/browser/metrics/autofill_metrics_test_base.cc
index 2c4d67b..c5ee169 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics_test_base.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics_test_base.cc
@@ -146,7 +146,7 @@
       ->AllowFidoRegistration(true);
   access_manager.is_authentication_in_progress_ = false;
   access_manager.can_fetch_unmask_details_ = true;
-  access_manager.is_user_verifiable_ = absl::nullopt;
+  access_manager.is_user_verifiable_ = std::nullopt;
 }
 
 void AutofillMetricsBaseTest::OnDidGetRealPan(
diff --git a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
index 9d08702..0bd3958 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
@@ -337,7 +337,7 @@
 struct Field {
   FieldType field_type;
   bool is_autofilled = true;
-  absl::optional<std::u16string> value = absl::nullopt;
+  std::optional<std::u16string> value = std::nullopt;
 };
 
 struct PerfectFillingTestCase {
diff --git a/components/autofill/core/browser/metrics/form_events/form_event_logger_base.h b/components/autofill/core/browser/metrics/form_events/form_event_logger_base.h
index 79c561acd..152b08a 100644
--- a/components/autofill/core/browser/metrics/form_events/form_event_logger_base.h
+++ b/components/autofill/core/browser/metrics/form_events/form_event_logger_base.h
@@ -92,7 +92,7 @@
     return flow_id_;
   }
 
-  const absl::optional<int64_t> fast_checkout_run_id_for_test() const {
+  const std::optional<int64_t> fast_checkout_run_id_for_test() const {
     return fast_checkout_run_id_;
   }
 
@@ -194,7 +194,7 @@
   bool is_heuristic_only_email_form_ = false;
   AblationGroup ablation_group_ = AblationGroup::kDefault;
   AblationGroup conditional_ablation_group_ = AblationGroup::kDefault;
-  absl::optional<base::TimeDelta> time_from_interaction_to_submission_;
+  std::optional<base::TimeDelta> time_from_interaction_to_submission_;
 
   // The last field that was polled for suggestions.
   FormFieldData last_polled_field_;
@@ -208,7 +208,7 @@
   // during the flow.
   FormInteractionsFlowId flow_id_;
   // Unique ID of a Fast Checkout run. Used for metrics.
-  absl::optional<int64_t> fast_checkout_run_id_;
+  std::optional<int64_t> fast_checkout_run_id_;
 
   // Form types of the submitted form.
   DenseSet<FormType> submitted_form_types_;
diff --git a/components/autofill/core/browser/ml_model/autofill_ml_prediction_model_handler.cc b/components/autofill/core/browser/ml_model/autofill_ml_prediction_model_handler.cc
index ce116cc1..7331df2 100644
--- a/components/autofill/core/browser/ml_model/autofill_ml_prediction_model_handler.cc
+++ b/components/autofill/core/browser/ml_model/autofill_ml_prediction_model_handler.cc
@@ -31,10 +31,10 @@
           base::ThreadPool::CreateSequencedTaskRunner(
               {base::MayBlock(), base::TaskPriority::USER_VISIBLE}),
           std::make_unique<AutofillModelExecutor>(),
-          /*model_inference_timeout=*/absl::nullopt,
+          /*model_inference_timeout=*/std::nullopt,
           optimization_guide::proto::OptimizationTarget::
               OPTIMIZATION_TARGET_AUTOFILL_FIELD_CLASSIFICATION,
-          /*model_metadata=*/absl::nullopt) {
+          /*model_metadata=*/std::nullopt) {
   // Store the model in memory as soon as it is available and keep it loaded for
   // the whole browser session since we query predictions very regularly.
   // TODO(crbug.com/1465926): Maybe change both back to default behavior if we
@@ -60,12 +60,10 @@
           [](base::WeakPtr<AutofillMlPredictionModelHandler> self,
              std::unique_ptr<FormStructure> form_structure,
              base::OnceCallback<void(std::unique_ptr<FormStructure>)> callback,
-             const absl::optional<AutofillModelExecutor::ModelOutput>& output) {
-            if (!self) {
-              return;
+             const std::optional<AutofillModelExecutor::ModelOutput>& output) {
+            if (self && output) {
+              self->AssignMostLikelyTypes(*form_structure, *output);
             }
-            CHECK(output);
-            self->AssignMostLikelyTypes(*form_structure, *output);
             std::move(callback).Run(std::move(form_structure));
           },
           weak_ptr_factory_.GetWeakPtr(), std::move(form_structure),
diff --git a/components/autofill/core/browser/ml_model/autofill_model_executor.cc b/components/autofill/core/browser/ml_model/autofill_model_executor.cc
index 6b17dfc..30b1bf4 100644
--- a/components/autofill/core/browser/ml_model/autofill_model_executor.cc
+++ b/components/autofill/core/browser/ml_model/autofill_model_executor.cc
@@ -74,7 +74,7 @@
   return true;
 }
 
-absl::optional<AutofillModelExecutor::ModelOutput>
+std::optional<AutofillModelExecutor::ModelOutput>
 AutofillModelExecutor::Postprocess(
     const std::vector<const TfLiteTensor*>& output_tensors) {
   // `output_tensors` is a 3D vector of floats. The first dimension is used
diff --git a/components/autofill/core/browser/ml_model/autofill_model_executor.h b/components/autofill/core/browser/ml_model/autofill_model_executor.h
index 87a36b0..15300490 100644
--- a/components/autofill/core/browser/ml_model/autofill_model_executor.h
+++ b/components/autofill/core/browser/ml_model/autofill_model_executor.h
@@ -10,7 +10,6 @@
 
 #include "components/autofill/core/browser/ml_model/autofill_model_vectorizer.h"
 #include "components/optimization_guide/core/base_model_executor.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -50,7 +49,7 @@
   // optimization_guide::BaseModelExecutor:
   bool Preprocess(const std::vector<TfLiteTensor*>& input_tensors,
                   const ModelInput& input) override;
-  absl::optional<ModelOutput> Postprocess(
+  std::optional<ModelOutput> Postprocess(
       const std::vector<const TfLiteTensor*>& output_tensors) override;
 
   // Stores the number of fields sent to the model via `Preprocess()`. This will
diff --git a/components/autofill/core/browser/ml_model/autofill_model_executor_unittest.cc b/components/autofill/core/browser/ml_model/autofill_model_executor_unittest.cc
index 7a6c5ae2..939ab58 100644
--- a/components/autofill/core/browser/ml_model/autofill_model_executor_unittest.cc
+++ b/components/autofill/core/browser/ml_model/autofill_model_executor_unittest.cc
@@ -45,7 +45,7 @@
         {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
     model_executor_ = std::make_unique<AutofillModelExecutor>();
     model_executor_->InitializeAndMoveToExecutionThread(
-        /*model_inference_timeout=*/absl::nullopt,
+        /*model_inference_timeout=*/std::nullopt,
         optimization_guide::proto::
             OPTIMIZATION_TARGET_AUTOFILL_FIELD_CLASSIFICATION,
         execution_task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
@@ -79,7 +79,7 @@
       {TokenId(1), TokenId(2), TokenId(3), TokenId(4), TokenId(5)},
       {TokenId(2), TokenId(3), TokenId(4), TokenId(5), TokenId(6)}};
   base::test::TestFuture<
-      const absl::optional<AutofillModelExecutor::ModelOutput>&>
+      const std::optional<AutofillModelExecutor::ModelOutput>&>
       predictions;
   execution_task_runner_->PostTask(
       FROM_HERE,
diff --git a/components/autofill/core/browser/payments/account_info_getter.h b/components/autofill/core/browser/payments/account_info_getter.h
index 59a9f86..3655752 100644
--- a/components/autofill/core/browser/payments/account_info_getter.h
+++ b/components/autofill/core/browser/payments/account_info_getter.h
@@ -15,7 +15,7 @@
   // Returns the account info that should be used when communicating with the
   // Payments server. The AccountInfo could be empty if there is no account to
   // be used by the Payments server.
-  // TODO(crbug.com/1411720): Make it return absl::optional.
+  // TODO(crbug.com/1411720): Make it return std::optional.
   virtual CoreAccountInfo GetAccountInfoForPaymentsServer() const = 0;
 
   // Returns true - When user is both signed-in and enabled sync.
diff --git a/components/autofill/core/browser/payments/autofill_error_dialog_context.h b/components/autofill/core/browser/payments/autofill_error_dialog_context.h
index 461d1a6..8a58a21 100644
--- a/components/autofill/core/browser/payments/autofill_error_dialog_context.h
+++ b/components/autofill/core/browser/payments/autofill_error_dialog_context.h
@@ -5,10 +5,9 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_AUTOFILL_ERROR_DIALOG_CONTEXT_H_
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_AUTOFILL_ERROR_DIALOG_CONTEXT_H_
 
+#include <optional>
 #include <string>
 
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
 namespace autofill {
 
 // Keep in sync with `AutofillErrorDialogType` in enums.xml.
@@ -60,14 +59,14 @@
   // related to the error to the user. This should be preferred for the title of
   // the autofill error dialog if a value is present. The language is based on
   // the client's locale.
-  absl::optional<std::string> server_returned_title;
+  std::optional<std::string> server_returned_title;
 
   // Autofill error dialog description returned from the server. Present in
   // situations where the server returns an error, and wants to display a
   // detailed description related to the error to the user. This should be
   // preferred for the description of the autofill error dialog if a value is
   // present. The language is based on the client's locale.
-  absl::optional<std::string> server_returned_description;
+  std::optional<std::string> server_returned_description;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc
index 16ec9d8b..94ee944 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc
@@ -79,7 +79,7 @@
     if (auto* form_data_importer = client_->GetFormDataImporter()) {
       form_data_importer
           ->SetCardRecordTypeIfNonInteractiveAuthenticationFlowCompleted(
-              absl::nullopt);
+              std::nullopt);
     }
   }
 }
@@ -253,7 +253,7 @@
   // Set delay as fido request timeout if available, otherwise set to default.
   int delay_ms = kDelayForGetUnmaskDetails;
   if (unmask_details_.fido_request_options.has_value()) {
-    const absl::optional<int> request_timeout =
+    const std::optional<int> request_timeout =
         unmask_details_.fido_request_options->FindInt("timeout_millis");
     if (request_timeout.has_value()) {
       delay_ms = *request_timeout;
@@ -283,7 +283,7 @@
   // autofill non-interactive flow successfully completes.
   client_->GetFormDataImporter()
       ->SetCardRecordTypeIfNonInteractiveAuthenticationFlowCompleted(
-          absl::nullopt);
+          std::nullopt);
 
   // Return error if authentication is already in progress, but don't reset
   // status.
@@ -528,7 +528,7 @@
       // UnmaskResponseDetails while for masked server cards, it comes from the
       // UnmaskDetails.
       base::Value::Dict fido_request_options;
-      absl::optional<std::string> context_token;
+      std::optional<std::string> context_token;
       if (card_->record_type() == CreditCard::RecordType::kVirtualCard) {
         context_token = virtual_card_unmask_response_details_.context_token;
         fido_request_options = std::move(
@@ -653,7 +653,7 @@
     unmask_auth_flow_type_ = UnmaskAuthFlowType::kNone;
   } else if (should_register_card_with_fido) {
 #if !BUILDFLAG(IS_IOS)
-    absl::optional<base::Value::Dict> request_options = absl::nullopt;
+    std::optional<base::Value::Dict> request_options = std::nullopt;
     if (unmask_details_.fido_request_options.has_value()) {
       // For opted-in user (CVC then FIDO case), request options are returned in
       // unmask detail response.
@@ -1391,6 +1391,12 @@
             : AutofillClient::PaymentsRpcCardType::kServerCard,
         autofill_metrics::ServerCardUnmaskFlowType::kRiskBased);
 
+    if (!card_->cvc().empty()) {
+      autofill_metrics::LogCvcFilling(
+          autofill_metrics::CvcFillingFlowType::kNoInteractiveAuthentication,
+          record_type);
+    }
+
     // `OnCreditCardFetchedCallback` makes a copy of `card` and `cvc` before it
     // asynchronously fills them into the form. Thus we can safely call
     // `Reset()` here, and we should as from this class' point of view the
@@ -1408,7 +1414,7 @@
         card_selected_without_unmask_details_timestamp_.value());
     autofill_metrics::LogUserPerceivedLatencyOnCardSelectionTimedOut(
         /*did_time_out=*/!get_unmask_details_returned);
-    card_selected_without_unmask_details_timestamp_ = absl::nullopt;
+    card_selected_without_unmask_details_timestamp_ = std::nullopt;
   }
 
   // Start the authentication after the wait ends.
@@ -1506,9 +1512,9 @@
   weak_ptr_factory_.InvalidateWeakPtrs();
   unmask_auth_flow_type_ = UnmaskAuthFlowType::kNone;
   is_authentication_in_progress_ = false;
-  preflight_call_timestamp_ = absl::nullopt;
-  card_selected_without_unmask_details_timestamp_ = absl::nullopt;
-  is_user_verifiable_called_timestamp_ = absl::nullopt;
+  preflight_call_timestamp_ = std::nullopt;
+  card_selected_without_unmask_details_timestamp_ = std::nullopt;
+  is_user_verifiable_called_timestamp_ = std::nullopt;
 #if !BUILDFLAG(IS_IOS)
   opt_in_intention_ = UserOptInIntention::kUnspecified;
 #endif
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.h b/components/autofill/core/browser/payments/credit_card_access_manager.h
index 94f9470..3dedfa02 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.h
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.h
@@ -459,14 +459,14 @@
   const raw_ptr<autofill_metrics::CreditCardFormEventLogger> form_event_logger_;
 
   // Timestamp used for preflight call metrics.
-  absl::optional<base::TimeTicks> preflight_call_timestamp_;
+  std::optional<base::TimeTicks> preflight_call_timestamp_;
 
   // Timestamp used for user-perceived latency metrics.
-  absl::optional<base::TimeTicks>
+  std::optional<base::TimeTicks>
       card_selected_without_unmask_details_timestamp_;
 
   // Timestamp for when fido_authenticator_->IsUserVerifiable() is called.
-  absl::optional<base::TimeTicks> is_user_verifiable_called_timestamp_;
+  std::optional<base::TimeTicks> is_user_verifiable_called_timestamp_;
 
 #if !BUILDFLAG(IS_IOS)
   std::unique_ptr<CreditCardFidoAuthenticator> fido_authenticator_;
@@ -513,7 +513,7 @@
   // Set to true only if user has a verifying platform authenticator.
   // e.g. Touch/Face ID, Windows Hello, Android fingerprint, etc., is available
   // and enabled.
-  absl::optional<bool> is_user_verifiable_;
+  std::optional<bool> is_user_verifiable_;
 
   // True only if currently waiting on unmask details. This avoids making
   // unnecessary calls to payments.
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
index 01a73feb..0c574599 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
@@ -205,7 +205,7 @@
     credit_card_access_manager().is_authentication_in_progress_ = false;
     credit_card_access_manager().can_fetch_unmask_details_ = true;
     credit_card_access_manager().unmask_details_request_in_progress_ = false;
-    credit_card_access_manager().is_user_verifiable_ = absl::nullopt;
+    credit_card_access_manager().is_user_verifiable_ = std::nullopt;
   }
 
   void ClearCards() { personal_data().ClearCreditCards(); }
@@ -957,7 +957,7 @@
 
   // There was no interactive authentication in this flow, so check that this
   // is signaled correctly.
-  absl::optional<CreditCard::RecordType> card_identifier =
+  std::optional<CreditCard::RecordType> card_identifier =
       autofill_client_.GetFormDataImporter()
           ->GetCardRecordTypeIfNonInteractiveAuthenticationFlowCompleted();
   ASSERT_TRUE(card_identifier.has_value());
@@ -2949,7 +2949,7 @@
 
   // There was no interactive authentication in this flow, so check that this
   // is signaled correctly.
-  absl::optional<CreditCard::RecordType> card_identifier =
+  std::optional<CreditCard::RecordType> card_identifier =
       autofill_client_.GetFormDataImporter()
           ->GetCardRecordTypeIfNonInteractiveAuthenticationFlowCompleted();
   ASSERT_TRUE(card_identifier.has_value());
@@ -3222,6 +3222,75 @@
           .has_value());
 }
 
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
+
+// Ensures that CVC filling gets logged after masked server card risk-based
+// unmasking success if the card has CVC.
+TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+       LogCvcFilling_RiskBasedMaskedServerCardUnmaskingSuccess) {
+  base::HistogramTester histogram_tester;
+  CreateServerCard(kTestGUID, kTestNumber, /*masked=*/true, kTestServerId);
+  CreditCard* masked_server_card =
+      personal_data().GetCreditCardByGUID(kTestGUID);
+  masked_server_card->set_cvc(kTestCvc16);
+
+  credit_card_access_manager().FetchCreditCard(
+      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
+                                         accessor_->GetWeakPtr()));
+
+  // Mock CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse to
+  // successfully return the valid card number.
+  CreditCard card = *masked_server_card;
+  card.set_record_type(CreditCard::RecordType::kFullServerCard);
+  // Mock that CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
+  // indicates a green path with valid card number returned.
+  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
+      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
+          .with_result(CreditCardRiskBasedAuthenticator::
+                           RiskBasedAuthenticationResponse::Result::
+                               kNoAuthenticationRequired)
+          .with_card(card));
+
+  // Expect the metrics are logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.CvcStorage.CvcFilling.ServerCard",
+      autofill_metrics::CvcFillingFlowType::kNoInteractiveAuthentication, 1);
+}
+
+// Ensures that CVC filling doesn't get logged after after masked server card
+// risk-based unmasking success if the card doesn't have CVC.
+TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+       DoNotLogCvcFilling_RiskBasedMaskedServerCardUnmaskingSuccess) {
+  base::HistogramTester histogram_tester;
+  CreateServerCard(kTestGUID, kTestNumber, /*masked=*/true, kTestServerId);
+  CreditCard* masked_server_card =
+      personal_data().GetCreditCardByGUID(kTestGUID);
+  masked_server_card->set_cvc(u"");
+
+  credit_card_access_manager().FetchCreditCard(
+      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
+                                         accessor_->GetWeakPtr()));
+
+  // Mock CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse to
+  // successfully return the valid card number.
+  CreditCard card = *masked_server_card;
+  card.set_record_type(CreditCard::RecordType::kFullServerCard);
+  // Mock that CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
+  // indicates a green path with valid card number returned.
+  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
+      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
+          .with_result(CreditCardRiskBasedAuthenticator::
+                           RiskBasedAuthenticationResponse::Result::
+                               kNoAuthenticationRequired)
+          .with_card(card));
+
+  // Expect the metrics are logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.CvcStorage.CvcFilling.ServerCard",
+      autofill_metrics::CvcFillingFlowType::kNoInteractiveAuthentication, 0);
+}
+
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
 // Params of the
 // CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest:
 // -- bool fido_opted_in;
@@ -3336,7 +3405,7 @@
 
   // There was no interactive authentication in this flow, so check that this
   // is signaled correctly.
-  absl::optional<CreditCard::RecordType> card_identifier =
+  std::optional<CreditCard::RecordType> card_identifier =
       autofill_client_.GetFormDataImporter()
           ->GetCardRecordTypeIfNonInteractiveAuthenticationFlowCompleted();
   ASSERT_TRUE(card_identifier.has_value());
diff --git a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc
index 3cb7bb1..ef0a9869 100644
--- a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc
@@ -30,8 +30,8 @@
     const CreditCard* card,
     base::WeakPtr<Requester> requester,
     PersonalDataManager* personal_data_manager,
-    absl::optional<std::string> vcn_context_token,
-    absl::optional<CardUnmaskChallengeOption> selected_challenge_option) {
+    std::optional<std::string> vcn_context_token,
+    std::optional<CardUnmaskChallengeOption> selected_challenge_option) {
   requester_ = requester;
   if (!card) {
     return OnFullCardRequestFailed(
diff --git a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
index 5ab33c4..35243778 100644
--- a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
+++ b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
@@ -45,7 +45,7 @@
       return *this;
     }
     CvcAuthenticationResponse& with_request_options(
-        absl::optional<base::Value::Dict> v) {
+        std::optional<base::Value::Dict> v) {
       request_options = std::move(v);
       return *this;
     }
@@ -57,7 +57,7 @@
     raw_ptr<const CreditCard> card = nullptr;
     // TODO(crbug.com/1475052): Remove CVC.
     std::u16string cvc = std::u16string();
-    absl::optional<base::Value::Dict> request_options;
+    std::optional<base::Value::Dict> request_options;
     std::string card_authorization_token = std::string();
   };
   class Requester {
@@ -91,13 +91,12 @@
   ~CreditCardCvcAuthenticator() override;
 
   // Authentication
-  void Authenticate(
-      const CreditCard* card,
-      base::WeakPtr<Requester> requester,
-      PersonalDataManager* personal_data_manager,
-      absl::optional<std::string> vcn_context_token = absl::nullopt,
-      absl::optional<CardUnmaskChallengeOption> selected_challenge_option =
-          absl::nullopt);
+  void Authenticate(const CreditCard* card,
+                    base::WeakPtr<Requester> requester,
+                    PersonalDataManager* personal_data_manager,
+                    std::optional<std::string> vcn_context_token = std::nullopt,
+                    std::optional<CardUnmaskChallengeOption>
+                        selected_challenge_option = std::nullopt);
 
   // payments::FullCardRequest::ResultDelegate
   void OnFullCardRequestSucceeded(
diff --git a/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc b/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc
index 72543bf8..24f5600 100644
--- a/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc
@@ -199,7 +199,7 @@
 
   payments::FullCardRequest* full_card_request = GetFullCardRequest();
   ASSERT_TRUE(full_card_request->GetShouldUnmaskCardForTesting());
-  absl::optional<CardUnmaskChallengeOption> challenge_option =
+  std::optional<CardUnmaskChallengeOption> challenge_option =
       full_card_request->GetUnmaskRequestDetailsForTesting()
           ->selected_challenge_option;
   ASSERT_TRUE(challenge_option);
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
index 8173861..a835ee62 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
@@ -73,7 +73,7 @@
     CreditCard card,
     base::WeakPtr<Requester> requester,
     base::Value::Dict request_options,
-    absl::optional<std::string> context_token) {
+    std::optional<std::string> context_token) {
   card_ = std::move(card);
   requester_ = requester;
   context_token_ = context_token;
@@ -524,7 +524,7 @@
   DCHECK(challenge);
   options->challenge = Base64ToBytes(*challenge);
 
-  const absl::optional<int> timeout = request_options.FindInt("timeout_millis");
+  const std::optional<int> timeout = request_options.FindInt("timeout_millis");
   options->timeout = base::Milliseconds(timeout.value_or(kWebAuthnTimeoutMs));
 
   options->user_verification = device::UserVerificationRequirement::kRequired;
@@ -576,8 +576,7 @@
     }
   }
 
-  const absl::optional<int> timeout =
-      creation_options.FindInt("timeout_millis");
+  const std::optional<int> timeout = creation_options.FindInt("timeout_millis");
   options->timeout = base::Milliseconds(timeout.value_or(kWebAuthnTimeoutMs));
 
   const auto* attestation =
@@ -624,7 +623,7 @@
       key_info.GetDict().FindList("authenticator_transport_support");
   if (transports && !transports->empty()) {
     for (const base::Value& transport_type : *transports) {
-      absl::optional<device::FidoTransportProtocol> protocol =
+      std::optional<device::FidoTransportProtocol> protocol =
           device::ConvertToFidoTransportProtocol(
               base::ToLowerASCII(transport_type.GetString()));
       if (protocol.has_value())
@@ -755,7 +754,7 @@
           autofill_client_, autofill_client_->GetPaymentsNetworkInterface(),
           autofill_client_->GetPersonalDataManager());
 
-      absl::optional<GURL> last_committed_primary_main_frame_origin;
+      std::optional<GURL> last_committed_primary_main_frame_origin;
       if (card_->record_type() == CreditCard::RecordType::kVirtualCard &&
           autofill_client_->GetLastCommittedPrimaryMainFrameURL().is_valid()) {
         last_committed_primary_main_frame_origin =
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator.h b/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
index 8711831..d84962d8 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_CREDIT_CARD_FIDO_AUTHENTICATOR_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/gtest_prod_util.h"
@@ -20,7 +21,6 @@
 #include "components/autofill/core/browser/strike_databases/payments/fido_authentication_strike_database.h"
 #include "components/webauthn/core/browser/internal_authenticator.h"
 #include "device/fido/fido_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/mojom/webauthn/authenticator.mojom-forward.h"
 
 namespace autofill {
@@ -98,7 +98,7 @@
       CreditCard card,
       base::WeakPtr<Requester> requester,
       base::Value::Dict request_options,
-      absl::optional<std::string> context_token = absl::nullopt);
+      std::optional<std::string> context_token = std::nullopt);
 
   // Invokes Registration flow. Sends credentials created from
   // |creation_options| along with the |card_authorization_token| to Payments in
@@ -252,7 +252,7 @@
   webauthn::InternalAuthenticator* authenticator();
 
   // Card being unmasked.
-  absl::optional<CreditCard> card_;
+  std::optional<CreditCard> card_;
 
   // The current flow in progress.
   Flow current_flow_ = NONE_FLOW;
@@ -294,7 +294,7 @@
 
   // The context token used for sharing context between different server
   // requests. Will be populated only for virtual card unmasking.
-  absl::optional<std::string> context_token_;
+  std::optional<std::string> context_token_;
 
   base::WeakPtrFactory<CreditCardFidoAuthenticator> weak_ptr_factory_{this};
 };
diff --git a/components/autofill/core/browser/payments/credit_card_otp_authenticator.cc b/components/autofill/core/browser/payments/credit_card_otp_authenticator.cc
index ce472b9..8a0fa0b 100644
--- a/components/autofill/core/browser/payments/credit_card_otp_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_otp_authenticator.cc
@@ -40,7 +40,7 @@
   }
 
   if (card_->record_type() == CreditCard::RecordType::kVirtualCard) {
-    absl::optional<GURL> last_committed_primary_main_frame_origin;
+    std::optional<GURL> last_committed_primary_main_frame_origin;
     if (autofill_client_->GetLastCommittedPrimaryMainFrameURL().is_valid()) {
       last_committed_primary_main_frame_origin =
           autofill_client_->GetLastCommittedPrimaryMainFrameURL()
@@ -386,8 +386,8 @@
   selected_challenge_option_request_ongoing_ = false;
   select_challenge_option_request_.reset();
   unmask_request_.reset();
-  select_challenge_option_request_timestamp_ = absl::nullopt;
-  unmask_card_request_timestamp_ = absl::nullopt;
+  select_challenge_option_request_timestamp_ = std::nullopt;
+  unmask_card_request_timestamp_ = std::nullopt;
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/credit_card_otp_authenticator.h b/components/autofill/core/browser/payments/credit_card_otp_authenticator.h
index bac3079..cbc6c132 100644
--- a/components/autofill/core/browser/payments/credit_card_otp_authenticator.h
+++ b/components/autofill/core/browser/payments/credit_card_otp_authenticator.h
@@ -177,8 +177,8 @@
       unmask_request_;
 
   // The timestamps when the requests are sent. Used for logging.
-  absl::optional<base::TimeTicks> select_challenge_option_request_timestamp_;
-  absl::optional<base::TimeTicks> unmask_card_request_timestamp_;
+  std::optional<base::TimeTicks> select_challenge_option_request_timestamp_;
+  std::optional<base::TimeTicks> unmask_card_request_timestamp_;
 
   base::WeakPtrFactory<CreditCardOtpAuthenticator> weak_ptr_factory_{this};
 };
diff --git a/components/autofill/core/browser/payments/credit_card_risk_based_authenticator.h b/components/autofill/core/browser/payments/credit_card_risk_based_authenticator.h
index 659c6829..5c6f959 100644
--- a/components/autofill/core/browser/payments/credit_card_risk_based_authenticator.h
+++ b/components/autofill/core/browser/payments/credit_card_risk_based_authenticator.h
@@ -68,11 +68,11 @@
     AutofillErrorDialogContext error_dialog_context;
     // The card will be set when the server response was successful and the
     // card's real pan was returned from the server side.
-    absl::optional<CreditCard> card;
+    std::optional<CreditCard> card;
     // The items below will be set when the server response was successful and
     // the card's real pan was not returned from the server side.
     // FIDO request options will be present only when FIDO is available.
-    absl::optional<base::Value::Dict> fido_request_options;
+    std::optional<base::Value::Dict> fido_request_options;
     // Stores the latest version of the context token, passed between Payments
     // calls and unmodified by Chrome.
     std::string context_token;
@@ -151,7 +151,7 @@
       unmask_request_details_;
 
   // The timestamp when the unmask request is sent. Used for logging.
-  absl::optional<base::TimeTicks> unmask_card_request_timestamp_;
+  std::optional<base::TimeTicks> unmask_card_request_timestamp_;
 
   base::WeakPtrFactory<CreditCardRiskBasedAuthenticator> weak_ptr_factory_{
       this};
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager.h b/components/autofill/core/browser/payments/credit_card_save_manager.h
index 7dd9da0a..d60a556c 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager.h
+++ b/components/autofill/core/browser/payments/credit_card_save_manager.h
@@ -7,6 +7,7 @@
 
 #include <map>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -26,7 +27,6 @@
 #include "components/autofill/core/browser/strike_databases/payments/credit_card_save_strike_database.h"
 #include "components/autofill/core/browser/strike_databases/payments/cvc_storage_strike_database.h"
 #include "components/autofill/core/browser/strike_databases/payments/local_card_migration_strike_database.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/origin.h"
 
 class SaveCardOfferObserver;
@@ -372,8 +372,8 @@
   int upload_decision_metrics_ = 0;
 
   // |true| if the offer-to-save bubble/infobar should pop-up, |false| if not.
-  // Will be absl::nullopt until data has been retrieved from the StrikeSystem.
-  absl::optional<bool> show_save_prompt_;
+  // Will be std::nullopt until data has been retrieved from the StrikeSystem.
+  std::optional<bool> show_save_prompt_;
 
   // |true| if the card being offered for upload is already a local card on the
   // device; |false| otherwise.
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
index 1b96312..193ded2f 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
@@ -176,8 +176,8 @@
       InitVirtualCardEnroll,
       (const CreditCard& credit_card,
        VirtualCardEnrollmentSource virtual_card_enrollment_source,
-       absl::optional<payments::PaymentsNetworkInterface::
-                          GetDetailsForEnrollmentResponseDetails>
+       std::optional<payments::PaymentsNetworkInterface::
+                         GetDetailsForEnrollmentResponseDetails>
            get_details_for_enrollment_response_details,
        PrefService* user_prefs,
        VirtualCardEnrollmentManager::RiskAssessmentFunction
@@ -5650,8 +5650,8 @@
 
   CreditCard arg_credit_card;
   VirtualCardEnrollmentSource arg_virtual_card_enrollment_source;
-  absl::optional<payments::PaymentsNetworkInterface::
-                     GetDetailsForEnrollmentResponseDetails>
+  std::optional<payments::PaymentsNetworkInterface::
+                    GetDetailsForEnrollmentResponseDetails>
       arg_get_details_for_enrollment_response_details;
   EXPECT_CALL(autofill_client_, GetVirtualCardEnrollmentManager).Times(1);
   EXPECT_CALL(*virtual_card_enrollment_manager_,
diff --git a/components/autofill/core/browser/payments/full_card_request.cc b/components/autofill/core/browser/payments/full_card_request.cc
index 5a69cbc..9cf7c44 100644
--- a/components/autofill/core/browser/payments/full_card_request.cc
+++ b/components/autofill/core/browser/payments/full_card_request.cc
@@ -52,10 +52,10 @@
     const url::Origin& merchant_domain_for_footprints) {
   DCHECK(ui_delegate);
   GetFullCardImpl(card, reason, result_delegate, ui_delegate,
-                  /*fido_assertion_info=*/absl::nullopt,
-                  /*last_committed_primary_main_frame_origin=*/absl::nullopt,
-                  /*context_token=*/absl::nullopt,
-                  /*selected_challenge_option=*/absl::nullopt,
+                  /*fido_assertion_info=*/std::nullopt,
+                  /*last_committed_primary_main_frame_origin=*/std::nullopt,
+                  /*context_token=*/std::nullopt,
+                  /*selected_challenge_option=*/std::nullopt,
                   merchant_domain_for_footprints);
 }
 
@@ -73,7 +73,7 @@
   DCHECK(!vcn_context_token.empty());
   DCHECK(selected_challenge_option.type == CardUnmaskChallengeOptionType::kCvc);
   GetFullCardImpl(card, reason, result_delegate, ui_delegate,
-                  /*fido_assertion_info=*/absl::nullopt,
+                  /*fido_assertion_info=*/std::nullopt,
                   last_committed_primary_main_frame_origin, vcn_context_token,
                   selected_challenge_option, merchant_domain_for_footprints);
 }
@@ -84,12 +84,12 @@
     base::WeakPtr<ResultDelegate> result_delegate,
     base::Value::Dict fido_assertion_info,
     const url::Origin& merchant_domain_for_footprints,
-    absl::optional<GURL> last_committed_primary_main_frame_origin,
-    absl::optional<std::string> context_token) {
+    std::optional<GURL> last_committed_primary_main_frame_origin,
+    std::optional<std::string> context_token) {
   GetFullCardImpl(
       card, reason, result_delegate, nullptr, std::move(fido_assertion_info),
       std::move(last_committed_primary_main_frame_origin),
-      std::move(context_token), /*selected_challenge_option=*/absl::nullopt,
+      std::move(context_token), /*selected_challenge_option=*/std::nullopt,
       merchant_domain_for_footprints);
 }
 
@@ -98,10 +98,10 @@
     AutofillClient::UnmaskCardReason reason,
     base::WeakPtr<ResultDelegate> result_delegate,
     base::WeakPtr<UIDelegate> ui_delegate,
-    absl::optional<base::Value::Dict> fido_assertion_info,
-    absl::optional<GURL> last_committed_primary_main_frame_origin,
-    absl::optional<std::string> context_token,
-    absl::optional<CardUnmaskChallengeOption> selected_challenge_option,
+    std::optional<base::Value::Dict> fido_assertion_info,
+    std::optional<GURL> last_committed_primary_main_frame_origin,
+    std::optional<std::string> context_token,
+    std::optional<CardUnmaskChallengeOption> selected_challenge_option,
     const url::Origin& merchant_domain_for_footprints) {
   // Retrieval of card information should happen via CVC auth or FIDO, but not
   // both. Use |ui_delegate|'s existence as evidence of doing CVC auth and
diff --git a/components/autofill/core/browser/payments/full_card_request.h b/components/autofill/core/browser/payments/full_card_request.h
index 3a794b8..3308eb3 100644
--- a/components/autofill/core/browser/payments/full_card_request.h
+++ b/components/autofill/core/browser/payments/full_card_request.h
@@ -166,9 +166,9 @@
       base::WeakPtr<ResultDelegate> result_delegate,
       base::Value::Dict fido_assertion_info,
       const url::Origin& merchant_domain_for_footprints,
-      absl::optional<GURL> last_committed_primary_main_frame_origin =
-          absl::nullopt,
-      absl::optional<std::string> context_token = absl::nullopt);
+      std::optional<GURL> last_committed_primary_main_frame_origin =
+          std::nullopt,
+      std::optional<std::string> context_token = std::nullopt);
 
   // Called by the PaymentsNetworkInterface when a card has been unmasked.
   void OnDidGetRealPan(
@@ -226,10 +226,10 @@
       AutofillClient::UnmaskCardReason reason,
       base::WeakPtr<ResultDelegate> result_delegate,
       base::WeakPtr<UIDelegate> ui_delegate,
-      absl::optional<base::Value::Dict> fido_assertion_info,
-      absl::optional<GURL> last_committed_primary_main_frame_origin,
-      absl::optional<std::string> context_token,
-      absl::optional<CardUnmaskChallengeOption> selected_challenge_option,
+      std::optional<base::Value::Dict> fido_assertion_info,
+      std::optional<GURL> last_committed_primary_main_frame_origin,
+      std::optional<std::string> context_token,
+      std::optional<CardUnmaskChallengeOption> selected_challenge_option,
       const url::Origin& merchant_domain_for_footprints);
 
   // CardUnmaskDelegate:
diff --git a/components/autofill/core/browser/payments/full_card_request_unittest.cc b/components/autofill/core/browser/payments/full_card_request_unittest.cc
index 051fb8c..497ceb0b 100644
--- a/components/autofill/core/browser/payments/full_card_request_unittest.cc
+++ b/components/autofill/core/browser/payments/full_card_request_unittest.cc
@@ -471,7 +471,7 @@
               url::Origin::Create(GURL("http://example.com"))));
   payments::PaymentsNetworkInterface::UnmaskRequestDetails* request_details =
       request()->GetUnmaskRequestDetailsForTesting();
-  ASSERT_EQ(request_details->merchant_domain_for_footprints, absl::nullopt);
+  ASSERT_EQ(request_details->merchant_domain_for_footprints, std::nullopt);
 
   CardUnmaskDelegate::UserProvidedUnmaskDetails details;
   details.cvc = u"123";
diff --git a/components/autofill/core/browser/payments/legal_message_line_fuzzer.cc b/components/autofill/core/browser/payments/legal_message_line_fuzzer.cc
index f6eb3d16..793c12a 100644
--- a/components/autofill/core/browser/payments/legal_message_line_fuzzer.cc
+++ b/components/autofill/core/browser/payments/legal_message_line_fuzzer.cc
@@ -5,17 +5,17 @@
 #include <stdint.h>
 
 #include <fuzzer/FuzzedDataProvider.h>
+#include <optional>
 
 #include "base/json/json_reader.h"
 #include "base/values.h"
 #include "components/autofill/core/browser/payments/legal_message_line.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   FuzzedDataProvider provider(data, size);
 
   // Prepare fuzzed parameters.
-  absl::optional<base::Value> legal_message =
+  std::optional<base::Value> legal_message =
       base::JSONReader::Read(provider.ConsumeRandomLengthString());
   if (!legal_message || !legal_message->is_dict())
     return 0;
diff --git a/components/autofill/core/browser/payments/legal_message_line_unittest.cc b/components/autofill/core/browser/payments/legal_message_line_unittest.cc
index d1f38b36..db7221ce 100644
--- a/components/autofill/core/browser/payments/legal_message_line_unittest.cc
+++ b/components/autofill/core/browser/payments/legal_message_line_unittest.cc
@@ -292,7 +292,7 @@
 // Verifies that legal message parsing is correct.
 TEST_P(LegalMessageLineTest, Parsing) {
   const TestCase& test_case = TestCaseData()[GetParam()];
-  absl::optional<base::Value> value(
+  std::optional<base::Value> value(
       base::JSONReader::Read(test_case.message_json));
   ASSERT_TRUE(value);
   ASSERT_TRUE(value->is_dict());
diff --git a/components/autofill/core/browser/payments/local_card_migration_manager.cc b/components/autofill/core/browser/payments/local_card_migration_manager.cc
index 04b264f..5a23c12 100644
--- a/components/autofill/core/browser/payments/local_card_migration_manager.cc
+++ b/components/autofill/core/browser/payments/local_card_migration_manager.cc
@@ -60,7 +60,7 @@
 LocalCardMigrationManager::~LocalCardMigrationManager() {}
 
 bool LocalCardMigrationManager::ShouldOfferLocalCardMigration(
-    const absl::optional<CreditCard>& extracted_credit_card,
+    const std::optional<CreditCard>& extracted_credit_card,
     int credit_card_import_type) {
   // Reset and store the extracted credit card info for a later check of whether
   // the extracted card is supported.
diff --git a/components/autofill/core/browser/payments/local_card_migration_manager.h b/components/autofill/core/browser/payments/local_card_migration_manager.h
index 6da69f2b..59c543e 100644
--- a/components/autofill/core/browser/payments/local_card_migration_manager.h
+++ b/components/autofill/core/browser/payments/local_card_migration_manager.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_LOCAL_CARD_MIGRATION_MANAGER_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <unordered_map>
 #include <utility>
@@ -19,7 +20,6 @@
 #include "components/autofill/core/browser/payments/legal_message_line.h"
 #include "components/autofill/core/browser/payments/payments_network_interface.h"
 #include "components/autofill/core/browser/strike_databases/payments/local_card_migration_strike_database.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -107,7 +107,7 @@
   // the imported card is supported. `extracted_credit_card` might be
   // null if a user used server card.
   bool ShouldOfferLocalCardMigration(
-      const absl::optional<CreditCard>& extracted_credit_card,
+      const std::optional<CreditCard>& extracted_credit_card,
       int credit_card_import_type);
 
   // Called from FormDataImporter or settings page when all migration
@@ -236,7 +236,7 @@
   raw_ptr<PersonalDataManager> personal_data_manager_;
 
   // The imported credit card number from the form submission.
-  absl::optional<std::u16string> extracted_credit_card_number_;
+  std::optional<std::u16string> extracted_credit_card_number_;
 
   // The imported credit card record type from the form submission.
   int credit_card_import_type_;
diff --git a/components/autofill/core/browser/payments/mandatory_reauth_manager.cc b/components/autofill/core/browser/payments/mandatory_reauth_manager.cc
index dde0b0d7..2b5c5d36 100644
--- a/components/autofill/core/browser/payments/mandatory_reauth_manager.cc
+++ b/components/autofill/core/browser/payments/mandatory_reauth_manager.cc
@@ -49,7 +49,7 @@
 }
 
 bool MandatoryReauthManager::ShouldOfferOptin(
-    absl::optional<CreditCard::RecordType>
+    std::optional<CreditCard::RecordType>
         card_record_type_if_non_interactive_authentication_flow_completed) {
   opt_in_source_ = autofill_metrics::MandatoryReauthOptInOrOutSource::kUnknown;
   // We should not offer to update a user pref in off the record mode.
diff --git a/components/autofill/core/browser/payments/mandatory_reauth_manager.h b/components/autofill/core/browser/payments/mandatory_reauth_manager.h
index f68adbc5..15058bc0 100644
--- a/components/autofill/core/browser/payments/mandatory_reauth_manager.h
+++ b/components/autofill/core/browser/payments/mandatory_reauth_manager.h
@@ -68,7 +68,7 @@
   // authentication, and will hold the record type of the card that had the most
   // recent non-interactive authentication.
   virtual bool ShouldOfferOptin(
-      absl::optional<CreditCard::RecordType>
+      std::optional<CreditCard::RecordType>
           card_record_type_if_non_interactive_authentication_flow_completed);
 
   // Starts the opt-in flow. This flow includes an opt-in bubble, an
diff --git a/components/autofill/core/browser/payments/mandatory_reauth_manager_unittest.cc b/components/autofill/core/browser/payments/mandatory_reauth_manager_unittest.cc
index 4a5683a..deaa8fa 100644
--- a/components/autofill/core/browser/payments/mandatory_reauth_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/mandatory_reauth_manager_unittest.cc
@@ -310,7 +310,7 @@
 
   // 'card_identifier_if_non_interactive_authentication_flow_completed' is not
   // present, implying interactive authentication happened.
-  EXPECT_FALSE(mandatory_reauth_manager_->ShouldOfferOptin(absl::nullopt));
+  EXPECT_FALSE(mandatory_reauth_manager_->ShouldOfferOptin(std::nullopt));
   ExpectUniqueOfferOptInDecision(
       MandatoryReauthOfferOptInDecision::kWentThroughInteractiveAuthentication);
 }
diff --git a/components/autofill/core/browser/payments/payments_network_interface.cc b/components/autofill/core/browser/payments/payments_network_interface.cc
index 53bd6eda..d121c2c 100644
--- a/components/autofill/core/browser/payments/payments_network_interface.cc
+++ b/components/autofill/core/browser/payments/payments_network_interface.cc
@@ -490,7 +490,7 @@
     case net::HTTP_OK: {
       std::string error_code;
       std::string error_api_error_reason;
-      absl::optional<base::Value> message_value = base::JSONReader::Read(data);
+      std::optional<base::Value> message_value = base::JSONReader::Read(data);
       if (message_value && message_value->is_dict()) {
         const auto* found_error_code =
             message_value->GetDict().FindStringByDottedPath("error.code");
diff --git a/components/autofill/core/browser/payments/payments_network_interface.h b/components/autofill/core/browser/payments/payments_network_interface.h
index eebd26e..07d7a95 100644
--- a/components/autofill/core/browser/payments/payments_network_interface.h
+++ b/components/autofill/core/browser/payments/payments_network_interface.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_NETWORK_INTERFACE_H_
 
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <unordered_map>
@@ -27,7 +28,6 @@
 #include "components/autofill/core/browser/payments/client_behavior_constants.h"
 #include "components/autofill/core/browser/payments/virtual_card_enrollment_flow.h"
 #include "google_apis/gaia/google_service_auth_error.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/origin.h"
 
 namespace signin {
@@ -101,7 +101,7 @@
     bool offer_fido_opt_in = false;
     // Public Key Credential Request Options required for authentication.
     // https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialrequestoptions
-    absl::optional<base::Value::Dict> fido_request_options;
+    std::optional<base::Value::Dict> fido_request_options;
     // Set of credit cards ids that are eligible for FIDO Authentication.
     std::set<std::string> fido_eligible_card_ids;
   };
@@ -118,23 +118,23 @@
     CreditCard card;
     std::string risk_data;
     CardUnmaskDelegate::UserProvidedUnmaskDetails user_response;
-    absl::optional<base::Value::Dict> fido_assertion_info;
+    std::optional<base::Value::Dict> fido_assertion_info;
     std::u16string otp;
     // An opaque token used to chain consecutive payments requests together.
     std::string context_token;
     // The origin of the primary main frame where the unmasking happened.
     // Should be populated when the unmasking is for a virtual-card.
-    absl::optional<GURL> last_committed_primary_main_frame_origin;
+    std::optional<GURL> last_committed_primary_main_frame_origin;
     // The selected challenge option. Should be populated when we are doing CVC
     // unmasking for a virtual card.
-    absl::optional<CardUnmaskChallengeOption> selected_challenge_option;
+    std::optional<CardUnmaskChallengeOption> selected_challenge_option;
     // A vector of signals used to share client behavior with the Payments
     // server.
     std::vector<ClientBehaviorConstants> client_behavior_signals;
     // The origin of the primary main frame where the unmasking happened. Should
     // only be populated when the client is not in incognito mode since it will
     // be used for personalization.
-    absl::optional<url::Origin> merchant_domain_for_footprints;
+    std::optional<url::Origin> merchant_domain_for_footprints;
   };
 
   // Information retrieved from an UnmaskRequest.
@@ -168,7 +168,7 @@
     std::string expiration_year;
     // Challenge required for authorizing user for FIDO authentication for
     // future card unmasking.
-    absl::optional<base::Value::Dict> fido_request_options;
+    std::optional<base::Value::Dict> fido_request_options;
     // An opaque token used to logically chain consecutive UnmaskCard and
     // OptChange calls together.
     std::string card_authorization_token;
@@ -188,7 +188,7 @@
     // If present, that means this response was an error, and these fields
     // should be used for the autofill error dialog as they will provide detail
     // into the specific error that occurred.
-    absl::optional<AutofillErrorDialogContext> autofill_error_dialog_context;
+    std::optional<AutofillErrorDialogContext> autofill_error_dialog_context;
   };
 
   // A collection of information required to make an unmask IBAN request.
@@ -228,7 +228,7 @@
     Reason reason;
     // Signature required for enrolling user into FIDO authentication for future
     // card unmasking.
-    absl::optional<base::Value::Dict> fido_authenticator_response;
+    std::optional<base::Value::Dict> fido_authenticator_response;
     // An opaque token used to logically chain consecutive UnmaskCard and
     // OptChange calls together.
     std::string card_authorization_token = std::string();
@@ -242,13 +242,13 @@
 
     // Unset if response failed. True if user is opted-in for FIDO
     // authentication for card unmasking. False otherwise.
-    absl::optional<bool> user_is_opted_in;
+    std::optional<bool> user_is_opted_in;
     // Challenge required for enrolling user into FIDO authentication for future
     // card unmasking.
-    absl::optional<base::Value::Dict> fido_creation_options;
+    std::optional<base::Value::Dict> fido_creation_options;
     // Challenge required for authorizing user for FIDO authentication for
     // future card unmasking.
-    absl::optional<base::Value::Dict> fido_request_options;
+    std::optional<base::Value::Dict> fido_request_options;
   };
 
   // A collection of the information required to make local credit cards
@@ -305,12 +305,12 @@
     int64_t billing_customer_number = 0;
     // Populated if it is an unenroll request. |instrument_id| lets the server
     // know which card to unenroll from VCN.
-    absl::optional<int64_t> instrument_id;
+    std::optional<int64_t> instrument_id;
     // Populated if it is an enroll request. Based on the |vcn_context_token|
     // the server is able to retrieve the instrument id, and using
     // |vcn_context_token| for enroll allows the server to link a
     // GetDetailsForEnroll call with the corresponding Enroll call.
-    absl::optional<std::string> vcn_context_token;
+    std::optional<std::string> vcn_context_token;
   };
 
   // The struct to hold all detailed information to construct a
@@ -422,7 +422,7 @@
     // enrollment flow. Will only not be populated in the case of an imperfect
     // conversion from string to int64_t, or if the server does not return an
     // instrument id.
-    absl::optional<int64_t> instrument_id;
+    std::optional<int64_t> instrument_id;
     // |virtual_card_enrollment_state| is used to determine whether we want to
     // pursue further action with the credit card that was uploaded regarding
     // virtual card enrollment. For example, if the state is
@@ -443,8 +443,8 @@
     // |get_details_for_enrollment_response_details| will be populated so that
     // we can display the virtual card enrollment bubble without needing to do
     // another GetDetailsForEnroll network call.
-    absl::optional<GetDetailsForEnrollmentResponseDetails>
-        get_details_for_enrollment_response_details = absl::nullopt;
+    std::optional<GetDetailsForEnrollmentResponseDetails>
+        get_details_for_enrollment_response_details = std::nullopt;
   };
 
   // |url_loader_factory| is reference counted so it has no lifetime or
diff --git a/components/autofill/core/browser/payments/payments_network_interface_unittest.cc b/components/autofill/core/browser/payments/payments_network_interface_unittest.cc
index 9224282d..3613998 100644
--- a/components/autofill/core/browser/payments/payments_network_interface_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_network_interface_unittest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <optional>
 #include <set>
 #include <string>
 #include <string_view>
@@ -42,7 +43,6 @@
 #include "services/network/test/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 using ::testing::HasSubstr;
 
@@ -616,9 +616,9 @@
     return profile;
   }
 
-  absl::optional<PaymentsNetworkInterface::UnmaskDetails> unmask_details_;
+  std::optional<PaymentsNetworkInterface::UnmaskDetails> unmask_details_;
   // The UnmaskResponseDetails retrieved from an UnmaskRequest.  Includes PAN.
-  absl::optional<PaymentsNetworkInterface::UnmaskResponseDetails>
+  std::optional<PaymentsNetworkInterface::UnmaskResponseDetails>
       unmask_response_details_;
   base::WeakPtrFactory<PaymentsNetworkInterfaceTest> weak_ptr_factory_{this};
 };
@@ -1392,7 +1392,7 @@
 TEST_F(PaymentsNetworkInterfaceTest, UploadSuccessInstrumentIdPresent) {
   StartUploading(UploadCardOptions());
   IssueOAuthToken();
-  upload_card_response_details_.instrument_id = absl::nullopt;
+  upload_card_response_details_.instrument_id = std::nullopt;
 
   // Test the conversion from string to int64_t using the max value for int64_t.
   ReturnResponse(net::HTTP_OK,
diff --git a/components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request_unittest.cc b/components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request_unittest.cc
index 4726c1c8..7a51bba 100644
--- a/components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request_unittest.cc
@@ -82,7 +82,7 @@
 }
 
 TEST_P(GetDetailsForEnrollmentRequestTest, ParseResponse) {
-  absl::optional<base::Value> response = base::JSONReader::Read(
+  std::optional<base::Value> response = base::JSONReader::Read(
       "{ \"google_legal_message\": {}, \"external_legal_message\": {}, "
       "\"context_token\": \"some_token\" }");
   ASSERT_TRUE(response.has_value());
diff --git a/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.cc b/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.cc
index bc7ca1c..c7e4471 100644
--- a/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.cc
+++ b/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.cc
@@ -63,7 +63,7 @@
     }
   }
 
-  const absl::optional<bool> offer_fido_opt_in =
+  const std::optional<bool> offer_fido_opt_in =
       response.FindBool("offer_fido_opt_in");
   unmask_details_.offer_fido_opt_in = offer_fido_opt_in.value_or(false);
 
diff --git a/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc b/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc
index 75df2474..fd111ad7 100644
--- a/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc
+++ b/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc
@@ -70,7 +70,7 @@
   // Get the OTP length for this challenge. This will be displayed to the user
   // in the OTP input dialog so that the user knows how many digits the OTP
   // should be.
-  absl::optional<int> otp_length =
+  std::optional<int> otp_length =
       defined_challenge_option->FindInt("otp_length");
   parsed_challenge_option->challenge_input_length =
       otp_length ? *otp_length : kDefaultOtpLength;
@@ -95,7 +95,7 @@
   // Get the length of the CVC on the card. In most cases this is 3 digits,
   // but it is possible for this to be 4 digits, for example in the case of
   // the Card Identification Number on the front of an American Express card.
-  absl::optional<int> cvc_length =
+  std::optional<int> cvc_length =
       defined_challenge_option->FindInt("cvc_length");
   parsed_challenge_option->challenge_input_length =
       cvc_length ? *cvc_length : kDefaultCvcLength;
@@ -340,11 +340,11 @@
 
   const base::Value::Dict* expiration = response.FindDict("expiration");
   if (expiration) {
-    if (absl::optional<int> month = expiration->FindInt("month")) {
+    if (std::optional<int> month = expiration->FindInt("month")) {
       response_details_.expiration_month = base::NumberToString(month.value());
     }
 
-    if (absl::optional<int> year = expiration->FindInt("year")) {
+    if (std::optional<int> year = expiration->FindInt("year")) {
       response_details_.expiration_year = base::NumberToString(year.value());
     }
   }
diff --git a/components/autofill/core/browser/payments/payments_requests/unmask_card_request_unittest.cc b/components/autofill/core/browser/payments/payments_requests/unmask_card_request_unittest.cc
index ce408fd..00bff82f 100644
--- a/components/autofill/core/browser/payments/payments_requests/unmask_card_request_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_requests/unmask_card_request_unittest.cc
@@ -133,7 +133,7 @@
       features::kAutofillEnableMerchantDomainInUnmaskCardRequest);
   PaymentsNetworkInterface::UnmaskRequestDetails request_details =
       GetDefaultUnmaskRequestDetails();
-  request_details.merchant_domain_for_footprints = absl::nullopt;
+  request_details.merchant_domain_for_footprints = std::nullopt;
   request_ = std::make_unique<UnmaskCardRequest>(
       request_details, /*full_sync_enabled=*/true,
       /*callback=*/base::DoNothing());
@@ -143,7 +143,7 @@
 // Test to ensure response is correctly parsed when the FIDO challenge is
 // returned with context token.
 TEST_F(UnmaskCardRequestTest, FidoChallengeReturned_ParseResponse) {
-  absl::optional<base::Value> response = base::JSONReader::Read(
+  std::optional<base::Value> response = base::JSONReader::Read(
       "{\"fido_request_options\":{\"challenge\":\"fake_fido_challenge\"},"
       "\"context_token\":\"fake_context_token\"}");
   ASSERT_TRUE(response.has_value());
@@ -163,7 +163,7 @@
 // Test to ensure the response is complete when context token is returned but
 // PAN is not.
 TEST_F(UnmaskCardRequestTest, ContextTokenReturned) {
-  absl::optional<base::Value> response =
+  std::optional<base::Value> response =
       base::JSONReader::Read("{\"context_token\":\"fake_context_token\"}");
   ASSERT_TRUE(response.has_value());
   GetRequest()->ParseResponse(response->GetDict());
@@ -175,7 +175,7 @@
 // Test that the response is not complete when both context token and real PAN
 // are not returned.
 TEST_F(UnmaskCardRequestTest, ContextTokenAndPanNotReturned) {
-  absl::optional<base::Value> response = base::JSONReader::Read("{}");
+  std::optional<base::Value> response = base::JSONReader::Read("{}");
   ASSERT_TRUE(response.has_value());
   GetRequest()->ParseResponse(response->GetDict());
 
@@ -283,7 +283,7 @@
   feature_list_email_otp.InitWithFeatureState(
       features::kAutofillEnableEmailOtpForVcnYellowPath,
       /*enabled=*/IsAutofillEnableEmailOtpForVcnYellowPathTurnedOn());
-  absl::optional<base::Value> response = base::JSONReader::Read(
+  std::optional<base::Value> response = base::JSONReader::Read(
       "{\"fido_request_options\": {\"challenge\": \"fake_fido_challenge\"}, "
       "\"context_token\": \"fake_context_token\", \"idv_challenge_options\": "
       "[{\"sms_otp_challenge_option\": {\"challenge_id\": "
@@ -376,7 +376,7 @@
 
     // Test that `IsRetryableFailure()` returns true if a flow status is
     // present.
-    absl::optional<base::Value> response = base::JSONReader::Read(
+    std::optional<base::Value> response = base::JSONReader::Read(
         "{\"flow_status\": \"FLOW_STATUS_INCORRECT_ACCOUNT_SECURITY_CODE\"}");
     ASSERT_TRUE(response);
     GetRequest()->ParseResponse(response->GetDict());
diff --git a/components/autofill/core/browser/payments/payments_requests/update_virtual_card_enrollment_request.h b/components/autofill/core/browser/payments/payments_requests/update_virtual_card_enrollment_request.h
index 1e0a219..8a36fc4 100644
--- a/components/autofill/core/browser/payments/payments_requests/update_virtual_card_enrollment_request.h
+++ b/components/autofill/core/browser/payments/payments_requests/update_virtual_card_enrollment_request.h
@@ -5,13 +5,13 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_UPDATE_VIRTUAL_CARD_ENROLLMENT_REQUEST_H_
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_UPDATE_VIRTUAL_CARD_ENROLLMENT_REQUEST_H_
 
+#include <optional>
 #include <string>
 
 #include "base/functional/callback.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/payments/payments_network_interface.h"
 #include "components/autofill/core/browser/payments/payments_requests/payments_request.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class Value;
@@ -58,7 +58,7 @@
   PaymentsNetworkInterface::UpdateVirtualCardEnrollmentRequestDetails
       request_details_;
   base::OnceCallback<void(AutofillClient::PaymentsRpcResult)> callback_;
-  absl::optional<std::string> enroll_result_;
+  std::optional<std::string> enroll_result_;
 };
 
 }  // namespace payments
diff --git a/components/autofill/core/browser/payments/payments_requests/update_virtual_card_enrollment_request_unittest.cc b/components/autofill/core/browser/payments/payments_requests/update_virtual_card_enrollment_request_unittest.cc
index 881db2d..93f9f1e 100644
--- a/components/autofill/core/browser/payments/payments_requests/update_virtual_card_enrollment_request_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_requests/update_virtual_card_enrollment_request_unittest.cc
@@ -47,7 +47,7 @@
     return request_.get();
   }
 
-  const absl::optional<std::string>& GetParsedResponse() const {
+  const std::optional<std::string>& GetParsedResponse() const {
     return request_->enroll_result_;
   }
 
@@ -122,7 +122,7 @@
 
 TEST_P(UpdateVirtualCardEnrollmentRequestTest, ParseResponse) {
   if (std::get<0>(GetParam()) == VirtualCardEnrollmentRequestType::kEnroll) {
-    absl::optional<base::Value> response =
+    std::optional<base::Value> response =
         base::JSONReader::Read("{ \"enroll_result\": \"ENROLL_SUCCESS\" }");
     ASSERT_TRUE(response.has_value());
     GetRequest()->ParseResponse(response->GetDict());
@@ -135,7 +135,7 @@
             VirtualCardEnrollmentRequestType::kUnenroll);
   // Unenroll is only available from the settings page.
   if (std::get<1>(GetParam()) == VirtualCardEnrollmentSource::kSettingsPage) {
-    absl::optional<base::Value> response = base::JSONReader::Read("{}");
+    std::optional<base::Value> response = base::JSONReader::Read("{}");
     ASSERT_TRUE(response.has_value());
     GetRequest()->ParseResponse(response->GetDict());
 
diff --git a/components/autofill/core/browser/payments/test/mock_mandatory_reauth_manager.h b/components/autofill/core/browser/payments/test/mock_mandatory_reauth_manager.h
index 5d30249c..a2d564f4 100644
--- a/components/autofill/core/browser/payments/test/mock_mandatory_reauth_manager.h
+++ b/components/autofill/core/browser/payments/test/mock_mandatory_reauth_manager.h
@@ -18,7 +18,7 @@
 
   MOCK_METHOD(bool,
               ShouldOfferOptin,
-              (absl::optional<CreditCard::RecordType>),
+              (std::optional<CreditCard::RecordType>),
               (override));
   MOCK_METHOD(void, StartOptInFlow, (), (override));
   MOCK_METHOD(void, OnUserAcceptedOptInPrompt, (), (override));
diff --git a/components/autofill/core/browser/payments/test/test_credit_card_risk_based_authenticator.h b/components/autofill/core/browser/payments/test/test_credit_card_risk_based_authenticator.h
index 3f8b48f..a762dda 100644
--- a/components/autofill/core/browser/payments/test/test_credit_card_risk_based_authenticator.h
+++ b/components/autofill/core/browser/payments/test/test_credit_card_risk_based_authenticator.h
@@ -32,7 +32,7 @@
 
  private:
   bool authenticate_invoked_ = false;
-  absl::optional<CreditCard> card_;
+  std::optional<CreditCard> card_;
   int64_t billing_customer_id_;
 };
 
diff --git a/components/autofill/core/browser/payments/test_authentication_requester.h b/components/autofill/core/browser/payments/test_authentication_requester.h
index a7d7f65e..942ed622 100644
--- a/components/autofill/core/browser/payments/test_authentication_requester.h
+++ b/components/autofill/core/browser/payments/test_authentication_requester.h
@@ -72,9 +72,9 @@
 
   base::WeakPtr<TestAuthenticationRequester> GetWeakPtr();
 
-  absl::optional<bool> is_user_verifiable() { return is_user_verifiable_; }
+  std::optional<bool> is_user_verifiable() { return is_user_verifiable_; }
 
-  absl::optional<bool> did_succeed() { return did_succeed_; }
+  std::optional<bool> did_succeed() { return did_succeed_; }
 
   std::u16string number() { return number_; }
 
@@ -94,10 +94,10 @@
 
  private:
   // Set when CreditCardFidoAuthenticator invokes IsUserVerifiableCallback().
-  absl::optional<bool> is_user_verifiable_;
+  std::optional<bool> is_user_verifiable_;
 
   // Is set to true if authentication was successful.
-  absl::optional<bool> did_succeed_;
+  std::optional<bool> did_succeed_;
 
   // The failure type of the full card request. Set when the request is
   // finished.
diff --git a/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc b/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc
index db034ff..fa700a7 100644
--- a/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc
+++ b/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc
@@ -24,7 +24,7 @@
     CreditCard card,
     base::WeakPtr<Requester> requester,
     base::Value::Dict request_options,
-    absl::optional<std::string> context_token) {
+    std::optional<std::string> context_token) {
   authenticate_invoked_ = true;
   card_ = std::move(card);
   context_token_ = context_token;
@@ -124,7 +124,7 @@
 
 void TestCreditCardFidoAuthenticator::Reset() {
   is_user_verifiable_ = false;
-  is_user_opted_in_ = absl::nullopt;
+  is_user_opted_in_ = std::nullopt;
   opt_out_called_ = false;
   authenticate_invoked_ = false;
   card_ = CreditCard();
diff --git a/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h b/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h
index 3190d869..5e6e29c2 100644
--- a/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h
+++ b/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_TEST_CREDIT_CARD_FIDO_AUTHENTICATOR_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -13,7 +14,6 @@
 #include "components/autofill/core/browser/autofill_driver.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/payments/credit_card_fido_authenticator.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -34,7 +34,7 @@
   void Authenticate(CreditCard card,
                     base::WeakPtr<Requester> requester,
                     base::Value::Dict request_options,
-                    absl::optional<std::string> context_token) override;
+                    std::optional<std::string> context_token) override;
   void IsUserVerifiable(base::OnceCallback<void(bool)> callback) override;
   bool IsUserOptedIn() override;
   void GetAssertion(blink::mojom::PublicKeyCredentialRequestOptionsPtr
@@ -67,7 +67,7 @@
   bool IsOptOutCalled() { return opt_out_called_; }
   bool authenticate_invoked() { return authenticate_invoked_; }
   const CreditCard& card() { return *card_; }
-  const absl::optional<std::string>& context_token() { return context_token_; }
+  const std::optional<std::string>& context_token() { return context_token_; }
 
   // Resets all the testing related states.
   void Reset();
@@ -79,11 +79,11 @@
   blink::mojom::PublicKeyCredentialRequestOptionsPtr request_options_;
   blink::mojom::PublicKeyCredentialCreationOptionsPtr creation_options_;
   bool is_user_verifiable_ = false;
-  absl::optional<bool> is_user_opted_in_;
+  std::optional<bool> is_user_opted_in_;
   bool opt_out_called_ = false;
   bool authenticate_invoked_ = false;
-  absl::optional<CreditCard> card_;
-  absl::optional<std::string> context_token_;
+  std::optional<CreditCard> card_;
+  std::optional<std::string> context_token_;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/test_payments_network_interface.cc b/components/autofill/core/browser/payments/test_payments_network_interface.cc
index a0a6535..38e52d6 100644
--- a/components/autofill/core/browser/payments/test_payments_network_interface.cc
+++ b/components/autofill/core/browser/payments/test_payments_network_interface.cc
@@ -5,6 +5,7 @@
 #include "components/autofill/core/browser/payments/test_payments_network_interface.h"
 
 #include <memory>
+#include <optional>
 #include <unordered_map>
 
 #include "base/json/json_reader.h"
@@ -15,7 +16,6 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill::payments {
 
@@ -206,7 +206,7 @@
 }
 
 std::unique_ptr<base::Value::Dict> TestPaymentsNetworkInterface::LegalMessage() {
-  absl::optional<base::Value> parsed_json;
+  std::optional<base::Value> parsed_json;
   if (use_invalid_legal_message_) {
     // Legal message is invalid because it's missing the url.
     parsed_json = base::JSONReader::Read(
diff --git a/components/autofill/core/browser/payments/test_payments_network_interface.h b/components/autofill/core/browser/payments/test_payments_network_interface.h
index 377127d..0b034260 100644
--- a/components/autofill/core/browser/payments/test_payments_network_interface.h
+++ b/components/autofill/core/browser/payments/test_payments_network_interface.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_TEST_PAYMENTS_NETWORK_INTERFACE_H_
 
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <unordered_map>
@@ -16,7 +17,6 @@
 #include "build/build_config.h"
 #include "components/autofill/core/browser/payments/payments_network_interface.h"
 #include "components/autofill/core/browser/payments/payments_requests/update_virtual_card_enrollment_request.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace network {
 class SharedURLLoaderFactory;
@@ -129,7 +129,7 @@
   payments::PaymentsNetworkInterface::UnmaskDetails* unmask_details() {
     return &unmask_details_;
   }
-  const absl::optional<payments::PaymentsNetworkInterface::UnmaskRequestDetails>&
+  const std::optional<payments::PaymentsNetworkInterface::UnmaskRequestDetails>&
   unmask_request() const {
     return unmask_request_;
   }
@@ -174,7 +174,7 @@
   // useful to control whether or not GetUnmaskDetails() is responded to.
   bool should_return_unmask_details_ = true;
   payments::PaymentsNetworkInterface::UnmaskDetails unmask_details_;
-  absl::optional<payments::PaymentsNetworkInterface::UnmaskRequestDetails>
+  std::optional<payments::PaymentsNetworkInterface::UnmaskRequestDetails>
       unmask_request_;
   payments::PaymentsNetworkInterface::SelectChallengeOptionRequestDetails
       select_challenge_option_request_;
@@ -191,9 +191,9 @@
   bool use_invalid_legal_message_ = false;
   bool use_legal_message_with_multiple_lines_ = false;
   std::unique_ptr<base::Value::Dict> LegalMessage();
-  absl::optional<AutofillClient::PaymentsRpcResult>
+  std::optional<AutofillClient::PaymentsRpcResult>
       select_challenge_option_result_;
-  absl::optional<AutofillClient::PaymentsRpcResult>
+  std::optional<AutofillClient::PaymentsRpcResult>
       update_virtual_card_enrollment_result_;
   payments::PaymentsNetworkInterface::GetDetailsForEnrollmentRequestDetails
       get_details_for_enrollment_request_details_;
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
index aefc912..2d71350f 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
@@ -66,8 +66,8 @@
 void VirtualCardEnrollmentManager::InitVirtualCardEnroll(
     const CreditCard& credit_card,
     VirtualCardEnrollmentSource virtual_card_enrollment_source,
-    absl::optional<payments::PaymentsNetworkInterface::
-                       GetDetailsForEnrollmentResponseDetails>
+    std::optional<payments::PaymentsNetworkInterface::
+                      GetDetailsForEnrollmentResponseDetails>
         get_details_for_enrollment_response_details,
     PrefService* user_prefs,
     RiskAssessmentFunction risk_assessment_function,
@@ -124,7 +124,7 @@
 }
 
 void VirtualCardEnrollmentManager::Enroll(
-    absl::optional<VirtualCardEnrollmentUpdateResponseCallback>
+    std::optional<VirtualCardEnrollmentUpdateResponseCallback>
         virtual_card_enrollment_update_response_callback) {
   LogUpdateVirtualCardEnrollmentRequestAttempt(
       state_.virtual_card_enrollment_fields.virtual_card_enrollment_source,
@@ -159,7 +159,7 @@
 
 void VirtualCardEnrollmentManager::Unenroll(
     int64_t instrument_id,
-    absl::optional<VirtualCardEnrollmentUpdateResponseCallback>
+    std::optional<VirtualCardEnrollmentUpdateResponseCallback>
         virtual_card_enrollment_update_response_callback) {
   LogUpdateVirtualCardEnrollmentRequestAttempt(
       VirtualCardEnrollmentSource::kSettingsPage,
@@ -353,7 +353,7 @@
       state_.virtual_card_enrollment_fields,
       base::BindOnce(
           &VirtualCardEnrollmentManager::Enroll, weak_ptr_factory_.GetWeakPtr(),
-          /*virtual_card_enrollment_update_response_callback=*/absl::nullopt),
+          /*virtual_card_enrollment_update_response_callback=*/std::nullopt),
       base::BindOnce(
           &VirtualCardEnrollmentManager::OnVirtualCardEnrollmentBubbleCancelled,
           weak_ptr_factory_.GetWeakPtr()));
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
index e45a471..3f9fccbad 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
@@ -74,7 +74,7 @@
       const VirtualCardEnrollmentProcessState&);
   ~VirtualCardEnrollmentProcessState();
   // Only populated once the risk engine responded.
-  absl::optional<std::string> risk_data;
+  std::optional<std::string> risk_data;
   // |virtual_card_enrollment_fields|'s |credit_card| and
   // |virtual_card_enrollment_source| are populated in the beginning of the
   // virtual card enrollment flow, but the rest of the fields are only populated
@@ -85,7 +85,7 @@
   // id, and using |vcn_context_token| for enroll allows the server to link a
   // GetDetailsForEnrollRequest with the corresponding
   // UpdateVirtualCardEnrollmentRequest for the enroll process.
-  absl::optional<std::string> vcn_context_token;
+  std::optional<std::string> vcn_context_token;
 };
 
 // Owned by FormDataImporter. There is one instance of this class per tab. This
@@ -130,9 +130,9 @@
       // GetDetailsForEnrollmentResponseDetails from the
       // UploadCardResponseDetails, so we can then skip the
       // GetDetailsForEnroll request in the Virtual Card Enrollment flow.
-      absl::optional<payments::PaymentsNetworkInterface::
-                         GetDetailsForEnrollmentResponseDetails>
-          get_details_for_enrollment_response_details = absl::nullopt,
+      std::optional<payments::PaymentsNetworkInterface::
+                        GetDetailsForEnrollmentResponseDetails>
+          get_details_for_enrollment_response_details = std::nullopt,
       // |user_prefs| will be populated if we are in the Android settings page,
       // to then be used for loading risk data. Otherwise it will always be
       // nullptr, and we should load risk data through |autofill_client_| as we
@@ -160,12 +160,12 @@
   void Enroll(
       // The callback lets the Android Settings page know whether
       // (un)enrollment was successful.
-      absl::optional<VirtualCardEnrollmentUpdateResponseCallback>
+      std::optional<VirtualCardEnrollmentUpdateResponseCallback>
           virtual_card_enrollment_update_response_callback);
 
   // Unenrolls the card mapped to the given |instrument_id|.
   void Unenroll(int64_t instrument_id,
-                absl::optional<VirtualCardEnrollmentUpdateResponseCallback>
+                std::optional<VirtualCardEnrollmentUpdateResponseCallback>
                     virtual_card_enrollment_update_response_callback);
 
   // Returns true if a credit card identified by its |instrument_id| should be
@@ -252,7 +252,7 @@
 
   // Callback triggered after getting server response about the success of
   // virtual card (un)enrollment.
-  absl::optional<VirtualCardEnrollmentUpdateResponseCallback>
+  std::optional<VirtualCardEnrollmentUpdateResponseCallback>
       virtual_card_enrollment_update_response_callback_;
 
   // Cancels the entire Virtual Card enrollment process.
@@ -372,10 +372,10 @@
   // VirtualCardEnrollBubble, we will take the difference between the current
   // timestamp and |save_card_bubble_accepted_timestamp_| to log as the latency
   // metric. |save_card_bubble_accepted_timestamp_| will then be reset.
-  absl::optional<base::Time> save_card_bubble_accepted_timestamp_;
+  std::optional<base::Time> save_card_bubble_accepted_timestamp_;
 
   // The timestamp when a GetDetailsForEnrollment request is sent.
-  absl::optional<base::Time> get_details_for_enrollment_request_sent_timestamp_;
+  std::optional<base::Time> get_details_for_enrollment_request_sent_timestamp_;
 
   base::WeakPtrFactory<VirtualCardEnrollmentManager> weak_ptr_factory_{this};
 };
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
index 3b344ac4..dc63a79 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
@@ -200,7 +200,7 @@
 #endif
 
       virtual_card_enrollment_manager_->InitVirtualCardEnroll(
-          *card_, virtual_card_enrollment_source, absl::nullopt,
+          *card_, virtual_card_enrollment_source, std::nullopt,
           virtual_card_enrollment_manager_->AutofillClientIsPresent()
               ? user_prefs()
               : nullptr,
@@ -242,8 +242,8 @@
       issuer_test_legal_message_line};
   get_details_for_enrollment_response_details.vcn_context_token =
       "vcn_context_token";
-  absl::optional<payments::PaymentsNetworkInterface::
-                     GetDetailsForEnrollmentResponseDetails>
+  std::optional<payments::PaymentsNetworkInterface::
+                    GetDetailsForEnrollmentResponseDetails>
       get_details_for_enrollment_response_details_optional =
           get_details_for_enrollment_response_details;
   virtual_card_enrollment_manager_->InitVirtualCardEnroll(
@@ -493,7 +493,7 @@
     payments_network_interface().set_update_virtual_card_enrollment_result(
         AutofillClient::PaymentsRpcResult::kSuccess);
     virtual_card_enrollment_manager_->Enroll(
-        /*virtual_card_enrollment_update_response_callback=*/absl::nullopt);
+        /*virtual_card_enrollment_update_response_callback=*/std::nullopt);
 
     payments::PaymentsNetworkInterface::
         UpdateVirtualCardEnrollmentRequestDetails request_details =
@@ -536,7 +536,7 @@
     payments_network_interface().set_update_virtual_card_enrollment_result(
         AutofillClient::PaymentsRpcResult::kVcnRetrievalPermanentFailure);
     virtual_card_enrollment_manager_->Enroll(
-        /*virtual_card_enrollment_update_response_callback=*/absl::nullopt);
+        /*virtual_card_enrollment_update_response_callback=*/std::nullopt);
 
     // Verifies the logging.
     histogram_tester.ExpectUniqueSample(
@@ -557,7 +557,7 @@
 
   virtual_card_enrollment_manager_->Unenroll(
       /*instrument_id=*/9223372036854775807,
-      /*virtual_card_enrollment_update_response_callback=*/absl::nullopt);
+      /*virtual_card_enrollment_update_response_callback=*/std::nullopt);
 
   payments::PaymentsNetworkInterface::UpdateVirtualCardEnrollmentRequestDetails
       request_details = payments_network_interface()
@@ -587,7 +587,7 @@
       AutofillClient::PaymentsRpcResult::kVcnRetrievalPermanentFailure);
   virtual_card_enrollment_manager_->Unenroll(
       /*instrument_id=*/9223372036854775807,
-      /*virtual_card_enrollment_update_response_callback=*/absl::nullopt);
+      /*virtual_card_enrollment_update_response_callback=*/std::nullopt);
 
   // Verifies the logging.
   histogram_tester.ExpectUniqueSample(
@@ -620,7 +620,7 @@
 
   // Ensure a strike has been removed after enrollment accepted.
   virtual_card_enrollment_manager_->Enroll(
-      /*virtual_card_enrollment_update_response_callback=*/absl::nullopt);
+      /*virtual_card_enrollment_update_response_callback=*/std::nullopt);
   EXPECT_EQ(
       virtual_card_enrollment_manager_->GetVirtualCardEnrollmentStrikeDatabase()
           ->GetStrikes(
@@ -702,7 +702,7 @@
        {VirtualCardEnrollmentSource::kUpstream,
         VirtualCardEnrollmentSource::kDownstream}) {
     virtual_card_enrollment_manager_->InitVirtualCardEnroll(
-        *card_, source, absl::nullopt,
+        *card_, source, std::nullopt,
         virtual_card_enrollment_manager_->AutofillClientIsPresent()
             ? user_prefs()
             : nullptr,
@@ -849,7 +849,7 @@
   state->virtual_card_enrollment_fields.credit_card = *card_;
 
   virtual_card_enrollment_manager_->InitVirtualCardEnroll(
-      *card_, VirtualCardEnrollmentSource::kDownstream, absl::nullopt,
+      *card_, VirtualCardEnrollmentSource::kDownstream, std::nullopt,
       virtual_card_enrollment_manager_->AutofillClientIsPresent() ? user_prefs()
                                                                   : nullptr,
       base::DoNothing());
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index 12f98501..b46e7cc8 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -53,8 +53,6 @@
 #include "components/autofill/core/browser/strike_databases/autofill_profile_migration_strike_database.h"
 #include "components/autofill/core/browser/strike_databases/autofill_profile_save_strike_database.h"
 #include "components/autofill/core/browser/ui/autofill_image_fetcher.h"
-#include "components/autofill/core/browser/ui/label_formatter.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/browser/validation.h"
 #include "components/autofill/core/common/autofill_clock.h"
@@ -700,7 +698,7 @@
   prefs::ClearSyncTransportOptIns(pref_service_);
 }
 
-absl::optional<CoreAccountInfo> PersonalDataManager::GetPrimaryAccountInfo()
+std::optional<CoreAccountInfo> PersonalDataManager::GetPrimaryAccountInfo()
     const {
   if (identity_manager_ &&
       identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSignin)) {
@@ -708,7 +706,7 @@
         signin::ConsentLevel::kSignin);
   }
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 bool PersonalDataManager::IsPaymentsDownloadActive() const {
diff --git a/components/autofill/core/browser/personal_data_manager.h b/components/autofill/core/browser/personal_data_manager.h
index 4334598..98ca6e9 100644
--- a/components/autofill/core/browser/personal_data_manager.h
+++ b/components/autofill/core/browser/personal_data_manager.h
@@ -8,6 +8,7 @@
 #include <deque>
 #include <list>
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <string_view>
@@ -53,7 +54,6 @@
 #include "components/sync/base/user_selectable_type.h"
 #include "components/sync/service/sync_service_observer.h"
 #include "components/webdata/common/web_data_service_consumer.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class PaymentsSuggestionBottomSheetMediatorTest;
 class Profile;
@@ -217,9 +217,9 @@
   // signin::IdentityManager::Observer:
   void OnAccountsCookieDeletedByUserAction() override;
 
-  // Returns the account info of currently signed-in user, or absl::nullopt if
+  // Returns the account info of currently signed-in user, or std::nullopt if
   // the user is not signed-in or the identity manager is not available.
-  absl::optional<CoreAccountInfo> GetPrimaryAccountInfo() const;
+  std::optional<CoreAccountInfo> GetPrimaryAccountInfo() const;
 
   // Returns whether credit card download is active (meaning that wallet sync is
   // running at least in transport mode).
diff --git a/components/autofill/core/browser/personal_data_manager_test_base.cc b/components/autofill/core/browser/personal_data_manager_test_base.cc
index 176990d..4eb1d349 100644
--- a/components/autofill/core/browser/personal_data_manager_test_base.cc
+++ b/components/autofill/core/browser/personal_data_manager_test_base.cc
@@ -6,6 +6,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
 #include "components/autofill/core/common/autofill_clock.h"
 
 namespace autofill {
@@ -29,6 +30,7 @@
       path, base::SingleThreadTaskRunner::GetCurrentDefault(),
       base::SingleThreadTaskRunner::GetCurrentDefault());
 
+  profile_web_database_->AddTable(std::make_unique<AddressAutofillTable>());
   // Hacky: hold onto a pointer but pass ownership.
   profile_autofill_table_ = new AutofillTable;
   profile_web_database_->AddTable(
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index 40e8ad4..e2ffb97 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -42,7 +42,6 @@
 #include "components/autofill/core/browser/personal_data_manager_test_base.h"
 #include "components/autofill/core/browser/profile_token_quality_test_api.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/common/autofill_clock.h"
 #include "components/autofill/core/common/autofill_constants.h"
@@ -3360,20 +3359,6 @@
   profile_autofill_table_->GetCreditCards(&cards);
   EXPECT_EQ(1U, cards.size());
   EXPECT_EQ(local_card.LastFourDigits(), cards[0]->LastFourDigits());
-
-  // Add a local profile
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Marion", "Mitchell", "Morrison",
-                       "johnwayne@me.xyz", "Fox", "123 Zoo St", "unit 5",
-                       "Hollywood", "CA", "91601", "US", "12345678910");
-  AddProfileToPersonalDataManager(profile);
-
-  std::vector<std::unique_ptr<AutofillProfile>> profiles;
-  // Expect that a profile is stored in the profile autofill table.
-  profile_autofill_table_->GetAutofillProfiles(
-      AutofillProfile::Source::kLocalOrSyncable, &profiles);
-  EXPECT_EQ(1U, profiles.size());
-  EXPECT_EQ(profile, *profiles[0]);
 }
 
 // Tests that the least recently used profile of two existing profiles is
diff --git a/components/autofill/core/browser/server_prediction_overrides.cc b/components/autofill/core/browser/server_prediction_overrides.cc
index aaa776c..733ca65 100644
--- a/components/autofill/core/browser/server_prediction_overrides.cc
+++ b/components/autofill/core/browser/server_prediction_overrides.cc
@@ -4,6 +4,8 @@
 
 #include "components/autofill/core/browser/server_prediction_overrides.h"
 
+#include <optional>
+
 #include "base/containers/flat_map.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
@@ -12,7 +14,6 @@
 #include "base/values.h"
 #include "components/autofill/core/browser/proto/api_v1.pb.h"
 #include "components/autofill/core/common/signatures.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -27,12 +28,12 @@
 constexpr std::string_view kSeparatorLevel1 = "_";
 
 // Parses a single type prediction for a field. If unsuccessful, it returns
-// `absl::nullopt`.
-absl::optional<FieldPrediction> ParseSingleFieldTypePrediction(
+// `std::nullopt`.
+std::optional<FieldPrediction> ParseSingleFieldTypePrediction(
     std::string_view specification) {
   int32_t type = 0;
   if (!base::StringToInt(specification, &type)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   FieldPrediction result;
@@ -50,7 +51,7 @@
   FieldSuggestion result;
   result.mutable_predictions()->Reserve(specifications.size());
   for (std::string_view spec : specifications) {
-    absl::optional<FieldPrediction> prediction =
+    std::optional<FieldPrediction> prediction =
         ParseSingleFieldTypePrediction(spec);
     if (!prediction) {
       return base::unexpected(
diff --git a/components/autofill/core/browser/strike_databases/payments/credit_card_save_strike_database.h b/components/autofill/core/browser/strike_databases/payments/credit_card_save_strike_database.h
index ead1a9b..76cd7f69 100644
--- a/components/autofill/core/browser/strike_databases/payments/credit_card_save_strike_database.h
+++ b/components/autofill/core/browser/strike_databases/payments/credit_card_save_strike_database.h
@@ -12,9 +12,9 @@
 
 struct CreditCardSaveStrikeDatabaseTraits {
   static constexpr std::string_view kName = "CreditCardSave";
-  static constexpr absl::optional<size_t> kMaxStrikeEntities = absl::nullopt;
-  static constexpr absl::optional<size_t> kMaxStrikeEntitiesAfterCleanup =
-      absl::nullopt;
+  static constexpr std::optional<size_t> kMaxStrikeEntities = std::nullopt;
+  static constexpr std::optional<size_t> kMaxStrikeEntitiesAfterCleanup =
+      std::nullopt;
   static constexpr size_t kMaxStrikeLimit = 3;
   static constexpr base::TimeDelta kExpiryTimeDelta = base::Days(183);
   static constexpr bool kUniqueIdRequired = true;
diff --git a/components/autofill/core/browser/strike_databases/payments/cvc_storage_strike_database.cc b/components/autofill/core/browser/strike_databases/payments/cvc_storage_strike_database.cc
index f14d1a7..5bea036e 100644
--- a/components/autofill/core/browser/strike_databases/payments/cvc_storage_strike_database.cc
+++ b/components/autofill/core/browser/strike_databases/payments/cvc_storage_strike_database.cc
@@ -8,7 +8,7 @@
 
 namespace autofill {
 
-absl::optional<base::TimeDelta>
+std::optional<base::TimeDelta>
 CvcStorageStrikeDatabase::GetRequiredDelaySinceLastStrike() const {
   return base::Days(7);
 }
diff --git a/components/autofill/core/browser/strike_databases/payments/cvc_storage_strike_database.h b/components/autofill/core/browser/strike_databases/payments/cvc_storage_strike_database.h
index 0bf3654..c8d5912 100644
--- a/components/autofill/core/browser/strike_databases/payments/cvc_storage_strike_database.h
+++ b/components/autofill/core/browser/strike_databases/payments/cvc_storage_strike_database.h
@@ -11,9 +11,9 @@
 
 struct CvcStorageStrikeDatabaseTraits {
   static constexpr std::string_view kName = "CvcStorage";
-  static constexpr absl::optional<size_t> kMaxStrikeEntities = absl::nullopt;
-  static constexpr absl::optional<size_t> kMaxStrikeEntitiesAfterCleanup =
-      absl::nullopt;
+  static constexpr std::optional<size_t> kMaxStrikeEntities = std::nullopt;
+  static constexpr std::optional<size_t> kMaxStrikeEntitiesAfterCleanup =
+      std::nullopt;
   static constexpr size_t kMaxStrikeLimit = 3;
   static constexpr base::TimeDelta kExpiryTimeDelta = base::Days(183);
   static constexpr bool kUniqueIdRequired = true;
@@ -25,7 +25,7 @@
   using SimpleAutofillStrikeDatabase<
       CvcStorageStrikeDatabaseTraits>::SimpleAutofillStrikeDatabase;
 
-  absl::optional<base::TimeDelta> GetRequiredDelaySinceLastStrike()
+  std::optional<base::TimeDelta> GetRequiredDelaySinceLastStrike()
       const override;
 };
 
diff --git a/components/autofill/core/browser/strike_databases/payments/fido_authentication_strike_database.h b/components/autofill/core/browser/strike_databases/payments/fido_authentication_strike_database.h
index dbba93a..4a2f9c8a 100644
--- a/components/autofill/core/browser/strike_databases/payments/fido_authentication_strike_database.h
+++ b/components/autofill/core/browser/strike_databases/payments/fido_authentication_strike_database.h
@@ -12,9 +12,9 @@
 
 struct FidoAuthenticationStrikeDatabaseTraits {
   static constexpr std::string_view kName = "FidoAuthentication";
-  static constexpr absl::optional<size_t> kMaxStrikeEntities = absl::nullopt;
-  static constexpr absl::optional<size_t> kMaxStrikeEntitiesAfterCleanup =
-      absl::nullopt;
+  static constexpr std::optional<size_t> kMaxStrikeEntities = std::nullopt;
+  static constexpr std::optional<size_t> kMaxStrikeEntitiesAfterCleanup =
+      std::nullopt;
   static constexpr size_t kMaxStrikeLimit = 3;
   static constexpr base::TimeDelta kExpiryTimeDelta = base::Days(183);
   static constexpr bool kUniqueIdRequired = false;
diff --git a/components/autofill/core/browser/strike_databases/payments/iban_save_strike_database.h b/components/autofill/core/browser/strike_databases/payments/iban_save_strike_database.h
index bdae5216..4003c8d0 100644
--- a/components/autofill/core/browser/strike_databases/payments/iban_save_strike_database.h
+++ b/components/autofill/core/browser/strike_databases/payments/iban_save_strike_database.h
@@ -12,9 +12,9 @@
 
 struct IbanSaveStrikeDatabaseTraits {
   static constexpr std::string_view kName = "IBANSave";
-  static constexpr absl::optional<size_t> kMaxStrikeEntities = absl::nullopt;
-  static constexpr absl::optional<size_t> kMaxStrikeEntitiesAfterCleanup =
-      absl::nullopt;
+  static constexpr std::optional<size_t> kMaxStrikeEntities = std::nullopt;
+  static constexpr std::optional<size_t> kMaxStrikeEntitiesAfterCleanup =
+      std::nullopt;
   static constexpr size_t kMaxStrikeLimit = 3;
   static constexpr base::TimeDelta kExpiryTimeDelta = base::Days(183);
   static constexpr bool kUniqueIdRequired = true;
diff --git a/components/autofill/core/browser/strike_databases/payments/local_card_migration_strike_database.h b/components/autofill/core/browser/strike_databases/payments/local_card_migration_strike_database.h
index 648999f..11316bf 100644
--- a/components/autofill/core/browser/strike_databases/payments/local_card_migration_strike_database.h
+++ b/components/autofill/core/browser/strike_databases/payments/local_card_migration_strike_database.h
@@ -11,12 +11,12 @@
 
 struct LocalCardMigrationStrikeDatabaseTraits {
   static constexpr std::string_view kName = "LocalCardMigration";
-  static constexpr absl::optional<size_t> kMaxStrikeEntities = absl::nullopt;
-  static constexpr absl::optional<size_t> kMaxStrikeEntitiesAfterCleanup =
-      absl::nullopt;
+  static constexpr std::optional<size_t> kMaxStrikeEntities = std::nullopt;
+  static constexpr std::optional<size_t> kMaxStrikeEntitiesAfterCleanup =
+      std::nullopt;
   static constexpr size_t kMaxStrikeLimit = 6;
-  static constexpr absl::optional<base::TimeDelta> kExpiryTimeDelta =
-      absl::nullopt;
+  static constexpr std::optional<base::TimeDelta> kExpiryTimeDelta =
+      std::nullopt;
   static constexpr bool kUniqueIdRequired = false;
 };
 
diff --git a/components/autofill/core/browser/strike_databases/payments/virtual_card_enrollment_strike_database.cc b/components/autofill/core/browser/strike_databases/payments/virtual_card_enrollment_strike_database.cc
index d0dbc20..186344f 100644
--- a/components/autofill/core/browser/strike_databases/payments/virtual_card_enrollment_strike_database.cc
+++ b/components/autofill/core/browser/strike_databases/payments/virtual_card_enrollment_strike_database.cc
@@ -13,9 +13,9 @@
   return GetStrikes(instrument_id) == GetMaxStrikesLimit() - 1;
 }
 
-absl::optional<base::TimeDelta>
+std::optional<base::TimeDelta>
 VirtualCardEnrollmentStrikeDatabase::GetRequiredDelaySinceLastStrike() const {
-  return absl::optional<base::TimeDelta>(
+  return std::optional<base::TimeDelta>(
       base::Days(kEnrollmentEnforcedDelayInDays));
 }
 
diff --git a/components/autofill/core/browser/strike_databases/payments/virtual_card_enrollment_strike_database.h b/components/autofill/core/browser/strike_databases/payments/virtual_card_enrollment_strike_database.h
index a1a368c..2fb82cf 100644
--- a/components/autofill/core/browser/strike_databases/payments/virtual_card_enrollment_strike_database.h
+++ b/components/autofill/core/browser/strike_databases/payments/virtual_card_enrollment_strike_database.h
@@ -35,7 +35,7 @@
   // |instrument_id|.
   bool IsLastOffer(const std::string& instrument_id) const;
 
-  absl::optional<base::TimeDelta> GetRequiredDelaySinceLastStrike()
+  std::optional<base::TimeDelta> GetRequiredDelaySinceLastStrike()
       const override;
 };
 
diff --git a/components/autofill/core/browser/strike_databases/simple_autofill_strike_database.h b/components/autofill/core/browser/strike_databases/simple_autofill_strike_database.h
index 40337a80..3566fdd 100644
--- a/components/autofill/core/browser/strike_databases/simple_autofill_strike_database.h
+++ b/components/autofill/core/browser/strike_databases/simple_autofill_strike_database.h
@@ -6,13 +6,13 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_STRIKE_DATABASES_SIMPLE_AUTOFILL_STRIKE_DATABASE_H_
 
 #include <stddef.h>
+#include <optional>
 #include <string>
 #include <string_view>
 
 #include "base/time/time.h"
 #include "components/autofill/core/browser/strike_databases/strike_database_base.h"
 #include "components/autofill/core/browser/strike_databases/strike_database_integrator_base.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -23,11 +23,11 @@
 //
 // struct MyStrikeDatabaseTraits {
 //   static constexpr std::string_view kName = "MyStrikeDatabase";
-//   static constexpr absl::optional<size_t> kMaxStrikeEntities = 100;
-//   static constexpr absl::optional<size_t>
+//   static constexpr std::optional<size_t> kMaxStrikeEntities = 100;
+//   static constexpr std::optional<size_t>
 //                                kMaxStrikeEntitiesAfterCleanup = 70;
 //   static constexpr size_t kMaxStrikeLimit = 3;
-//   static constexpr absl::optional<base::TimeDelta> kExpiryTimeDelta =
+//   static constexpr std::optional<base::TimeDelta> kExpiryTimeDelta =
 //       base::Days(180);
 //   static constexpr bool kUniqueIdRequired = true;
 // };
@@ -45,17 +45,17 @@
   }
   ~SimpleAutofillStrikeDatabase() override = default;
 
-  absl::optional<size_t> GetMaximumEntries() const override {
+  std::optional<size_t> GetMaximumEntries() const override {
     return Traits::kMaxStrikeEntities;
   }
-  absl::optional<size_t> GetMaximumEntriesAfterCleanup() const override {
+  std::optional<size_t> GetMaximumEntriesAfterCleanup() const override {
     return Traits::kMaxStrikeEntitiesAfterCleanup;
   }
   std::string GetProjectPrefix() const override {
     return std::string(Traits::kName);
   }
   int GetMaxStrikesLimit() const override { return Traits::kMaxStrikeLimit; }
-  absl::optional<base::TimeDelta> GetExpiryTimeDelta() const override {
+  std::optional<base::TimeDelta> GetExpiryTimeDelta() const override {
     return Traits::kExpiryTimeDelta;
   }
   bool UniqueIdsRequired() const override { return Traits::kUniqueIdRequired; }
diff --git a/components/autofill/core/browser/strike_databases/simple_autofill_strike_database_unittest.cc b/components/autofill/core/browser/strike_databases/simple_autofill_strike_database_unittest.cc
index 62660564..b02cb88b 100644
--- a/components/autofill/core/browser/strike_databases/simple_autofill_strike_database_unittest.cc
+++ b/components/autofill/core/browser/strike_databases/simple_autofill_strike_database_unittest.cc
@@ -24,8 +24,8 @@
   static constexpr size_t kMaxStrikeEntities = 100;
   static constexpr size_t kMaxStrikeEntitiesAfterCleanup = 50;
   static constexpr size_t kMaxStrikeLimit = 3;
-  static constexpr absl::optional<base::TimeDelta> kExpiryTimeDelta =
-      absl::nullopt;
+  static constexpr std::optional<base::TimeDelta> kExpiryTimeDelta =
+      std::nullopt;
   static constexpr bool kUniqueIdRequired = true;
 };
 
diff --git a/components/autofill/core/browser/strike_databases/strike_database_integrator_base.cc b/components/autofill/core/browser/strike_databases/strike_database_integrator_base.cc
index e573bd1fb..ab007b8 100644
--- a/components/autofill/core/browser/strike_databases/strike_database_integrator_base.cc
+++ b/components/autofill/core/browser/strike_databases/strike_database_integrator_base.cc
@@ -221,18 +221,18 @@
   return GetProjectPrefix() + kKeyDeliminator + id;
 }
 
-absl::optional<size_t> StrikeDatabaseIntegratorBase::GetMaximumEntries() const {
-  return absl::nullopt;
+std::optional<size_t> StrikeDatabaseIntegratorBase::GetMaximumEntries() const {
+  return std::nullopt;
 }
 
-absl::optional<size_t>
+std::optional<size_t>
 StrikeDatabaseIntegratorBase::GetMaximumEntriesAfterCleanup() const {
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<base::TimeDelta>
+std::optional<base::TimeDelta>
 StrikeDatabaseIntegratorBase::GetRequiredDelaySinceLastStrike() const {
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/strike_databases/strike_database_integrator_base.h b/components/autofill/core/browser/strike_databases/strike_database_integrator_base.h
index 3fc358c..24372d7 100644
--- a/components/autofill/core/browser/strike_databases/strike_database_integrator_base.h
+++ b/components/autofill/core/browser/strike_databases/strike_database_integrator_base.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 #include <map>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -15,7 +16,6 @@
 #include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
 #include "components/autofill/core/browser/strike_databases/strike_database_base.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -145,13 +145,13 @@
   std::string GetKey(const std::string& id) const;
 
   // Returns the maximum number of entries that should be stored for this
-  // project prefix. absl::nullopt means that there is no limit.
-  virtual absl::optional<size_t> GetMaximumEntries() const;
+  // project prefix. std::nullopt means that there is no limit.
+  virtual std::optional<size_t> GetMaximumEntries() const;
 
   // Returns the maximum number of entries that should remain after a cleanup.
   // This number should be smaller then `GetMaximumEntries()` to create some
-  // headroom. absl::nullopt means that `GetMaximumEntries()` should be used.
-  virtual absl::optional<size_t> GetMaximumEntriesAfterCleanup() const;
+  // headroom. std::nullopt means that `GetMaximumEntries()` should be used.
+  virtual std::optional<size_t> GetMaximumEntriesAfterCleanup() const;
 
   // Returns a prefix unique to each project, which will be used to create
   // database key.
@@ -163,7 +163,7 @@
 
   // Returns the time delta after which the most recent strike should expire.
   // If the Optional is empty, then strikes don't expire.
-  virtual absl::optional<base::TimeDelta> GetExpiryTimeDelta() const = 0;
+  virtual std::optional<base::TimeDelta> GetExpiryTimeDelta() const = 0;
 
   // Returns whether or not a unique string identifier is required for every
   // strike in this project.
@@ -172,7 +172,7 @@
   // Returns the time delta to wait for before prompting the feature again. If
   // the Optional is empty, then there is no required delay during which the
   // feature is blocked.
-  virtual absl::optional<base::TimeDelta> GetRequiredDelaySinceLastStrike()
+  virtual std::optional<base::TimeDelta> GetRequiredDelaySinceLastStrike()
       const;
 };
 
diff --git a/components/autofill/core/browser/strike_databases/strike_database_integrator_test_strike_database.cc b/components/autofill/core/browser/strike_databases/strike_database_integrator_test_strike_database.cc
index b03ef1c..68d9785 100644
--- a/components/autofill/core/browser/strike_databases/strike_database_integrator_test_strike_database.cc
+++ b/components/autofill/core/browser/strike_databases/strike_database_integrator_test_strike_database.cc
@@ -13,7 +13,7 @@
 StrikeDatabaseIntegratorTestStrikeDatabase::
     StrikeDatabaseIntegratorTestStrikeDatabase(
         StrikeDatabase* strike_database,
-        absl::optional<base::TimeDelta> expiry_time_delta)
+        std::optional<base::TimeDelta> expiry_time_delta)
     : StrikeDatabaseIntegratorTestStrikeDatabase(strike_database) {
   expiry_time_delta_ = expiry_time_delta;
 }
@@ -27,7 +27,7 @@
 StrikeDatabaseIntegratorTestStrikeDatabase::
     StrikeDatabaseIntegratorTestStrikeDatabase(
         StrikeDatabase* strike_database,
-        absl::optional<base::TimeDelta> expiry_time_delta,
+        std::optional<base::TimeDelta> expiry_time_delta,
         std::string& project_prefix)
     : StrikeDatabaseIntegratorTestStrikeDatabase(strike_database,
                                                  expiry_time_delta) {
@@ -46,7 +46,7 @@
   return kMaxStrikesLimit;
 }
 
-absl::optional<base::TimeDelta>
+std::optional<base::TimeDelta>
 StrikeDatabaseIntegratorTestStrikeDatabase::GetExpiryTimeDelta() const {
   return expiry_time_delta_;
 }
@@ -66,18 +66,18 @@
   required_delay_since_last_strike_ = required_delay_since_last_strike;
 }
 
-absl::optional<size_t>
+std::optional<size_t>
 StrikeDatabaseIntegratorTestStrikeDatabase::GetMaximumEntries() const {
   return maximum_entries_;
 }
 
-absl::optional<size_t>
+std::optional<size_t>
 StrikeDatabaseIntegratorTestStrikeDatabase::GetMaximumEntriesAfterCleanup()
     const {
   return maximum_entries_after_cleanup_;
 }
 
-absl::optional<base::TimeDelta>
+std::optional<base::TimeDelta>
 StrikeDatabaseIntegratorTestStrikeDatabase::GetRequiredDelaySinceLastStrike()
     const {
   return required_delay_since_last_strike_;
diff --git a/components/autofill/core/browser/strike_databases/strike_database_integrator_test_strike_database.h b/components/autofill/core/browser/strike_databases/strike_database_integrator_test_strike_database.h
index 7eab8bb..59add9a 100644
--- a/components/autofill/core/browser/strike_databases/strike_database_integrator_test_strike_database.h
+++ b/components/autofill/core/browser/strike_databases/strike_database_integrator_test_strike_database.h
@@ -21,25 +21,25 @@
  public:
   StrikeDatabaseIntegratorTestStrikeDatabase(
       StrikeDatabase* strike_database,
-      absl::optional<base::TimeDelta> expiry_time_delta);
+      std::optional<base::TimeDelta> expiry_time_delta);
   explicit StrikeDatabaseIntegratorTestStrikeDatabase(
       StrikeDatabase* strike_database);
   // This constructor initializes the TestStrikeDatabase with a non-default
   // project prefix.
   StrikeDatabaseIntegratorTestStrikeDatabase(
       StrikeDatabase* strike_database,
-      absl::optional<base::TimeDelta> expiry_time_delta,
+      std::optional<base::TimeDelta> expiry_time_delta,
       std::string& project_prefix);
   ~StrikeDatabaseIntegratorTestStrikeDatabase() override;
 
-  absl::optional<size_t> GetMaximumEntries() const override;
-  absl::optional<size_t> GetMaximumEntriesAfterCleanup() const override;
+  std::optional<size_t> GetMaximumEntries() const override;
+  std::optional<size_t> GetMaximumEntriesAfterCleanup() const override;
 
   std::string GetProjectPrefix() const override;
   int GetMaxStrikesLimit() const override;
-  absl::optional<base::TimeDelta> GetExpiryTimeDelta() const override;
+  std::optional<base::TimeDelta> GetExpiryTimeDelta() const override;
   bool UniqueIdsRequired() const override;
-  absl::optional<base::TimeDelta> GetRequiredDelaySinceLastStrike()
+  std::optional<base::TimeDelta> GetRequiredDelaySinceLastStrike()
       const override;
 
   void SetUniqueIdsRequired(bool unique_ids_required);
@@ -48,13 +48,13 @@
 
  private:
   bool unique_ids_required_ = false;
-  absl::optional<base::TimeDelta> expiry_time_delta_ = base::Days(365);
+  std::optional<base::TimeDelta> expiry_time_delta_ = base::Days(365);
 
-  absl::optional<size_t> maximum_entries_ = 10;
-  absl::optional<size_t> maximum_entries_after_cleanup_ = 5;
+  std::optional<size_t> maximum_entries_ = 10;
+  std::optional<size_t> maximum_entries_after_cleanup_ = 5;
   std::string project_prefix_ = "StrikeDatabaseIntegratorTest";
-  absl::optional<base::TimeDelta> required_delay_since_last_strike_ =
-      absl::nullopt;
+  std::optional<base::TimeDelta> required_delay_since_last_strike_ =
+      std::nullopt;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/strike_databases/strike_database_integrator_test_strike_database_unittest.cc b/components/autofill/core/browser/strike_databases/strike_database_integrator_test_strike_database_unittest.cc
index 5ada36c..b7d6e8c5 100644
--- a/components/autofill/core/browser/strike_databases/strike_database_integrator_test_strike_database_unittest.cc
+++ b/components/autofill/core/browser/strike_databases/strike_database_integrator_test_strike_database_unittest.cc
@@ -39,7 +39,7 @@
             strike_database_service_.get());
     no_expiry_strike_database_ =
         std::make_unique<StrikeDatabaseIntegratorTestStrikeDatabase>(
-            strike_database_service_.get(), absl::nullopt);
+            strike_database_service_.get(), std::nullopt);
   }
 
   void TearDown() override {
@@ -225,7 +225,7 @@
       other_strike_database =
           std::make_unique<StrikeDatabaseIntegratorTestStrikeDatabase>(
               strike_database_service_.get(),
-              /*expiry_time_micros=*/absl::nullopt, other_project_prefix);
+              /*expiry_time_micros=*/std::nullopt, other_project_prefix);
 
   // Add a strike to both integrators.
   strike_database_->AddStrike();
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h
index a72e4e69..5ce26f4 100644
--- a/components/autofill/core/browser/test_autofill_client.h
+++ b/components/autofill/core/browser/test_autofill_client.h
@@ -837,11 +837,11 @@
   AutofillErrorDialogContext autofill_error_dialog_context_;
 
   // Populated if save was offered. True if bubble was shown, false otherwise.
-  absl::optional<bool> offer_to_save_credit_card_bubble_was_shown_;
+  std::optional<bool> offer_to_save_credit_card_bubble_was_shown_;
 
   // Populated if name fix flow was offered. True if bubble was shown, false
   // otherwise.
-  absl::optional<bool> credit_card_name_fix_flow_bubble_was_shown_;
+  std::optional<bool> credit_card_name_fix_flow_bubble_was_shown_;
 
   version_info::Channel channel_for_testing_ = version_info::Channel::UNKNOWN;
 
@@ -859,7 +859,7 @@
   std::unique_ptr<AutofillCrowdsourcingManager> crowdsourcing_manager_;
 
   // Populated if credit card local save or upload was offered.
-  absl::optional<AutofillClient::SaveCreditCardOptions>
+  std::optional<AutofillClient::SaveCreditCardOptions>
       save_credit_card_options_;
 
   // User decision when credit card / CVC local save or upload was offered.
diff --git a/components/autofill/core/browser/test_autofill_driver.h b/components/autofill/core/browser/test_autofill_driver.h
index e379082d..1d96efa 100644
--- a/components/autofill/core/browser/test_autofill_driver.h
+++ b/components/autofill/core/browser/test_autofill_driver.h
@@ -49,7 +49,7 @@
   // AutofillDriver:
   LocalFrameToken GetFrameToken() const override { return frame_token_; }
   TestAutofillDriverTemplate* GetParent() override { return parent_; }
-  absl::optional<LocalFrameToken> Resolve(FrameToken query) override {
+  std::optional<LocalFrameToken> Resolve(FrameToken query) override {
     if (auto* local_frame_token = absl::get_if<LocalFrameToken>(&query)) {
       return *local_frame_token;
     }
@@ -57,7 +57,7 @@
     if (it != remote_frame_tokens_.end()) {
       return it->second;
     }
-    return absl::nullopt;
+    return std::nullopt;
   }
   bool IsInActiveFrame() const override { return is_in_active_frame_; }
   bool IsInAnyMainFrame() const override { return is_in_any_main_frame_; }
diff --git a/components/autofill/core/browser/test_browser_autofill_manager.cc b/components/autofill/core/browser/test_browser_autofill_manager.cc
index 31474ca..247f82f5 100644
--- a/components/autofill/core/browser/test_browser_autofill_manager.cc
+++ b/components/autofill/core/browser/test_browser_autofill_manager.cc
@@ -119,8 +119,9 @@
     run_loop_->Quit();
   }
 
-  if (expected_observed_submission_ != absl::nullopt)
+  if (expected_observed_submission_ != std::nullopt) {
     EXPECT_EQ(expected_observed_submission_, observed_submission);
+  }
 
   // If we have expected field types set, make sure they match.
   if (!expected_submitted_field_types_.empty()) {
diff --git a/components/autofill/core/browser/test_browser_autofill_manager.h b/components/autofill/core/browser/test_browser_autofill_manager.h
index aae2099b..efb7ff6b 100644
--- a/components/autofill/core/browser/test_browser_autofill_manager.h
+++ b/components/autofill/core/browser/test_browser_autofill_manager.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_BROWSER_AUTOFILL_MANAGER_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -14,7 +15,6 @@
 #include "base/time/time.h"
 #include "components/autofill/core/browser/autofill_trigger_details.h"
 #include "components/autofill/core/browser/browser_autofill_manager.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/image/image_unittest_util.h"
 
 namespace autofill {
@@ -130,7 +130,7 @@
  private:
   bool autofill_profile_enabled_ = true;
   bool autofill_payment_methods_enabled_ = true;
-  absl::optional<bool> expected_observed_submission_;
+  std::optional<bool> expected_observed_submission_;
   const gfx::Image card_image_ = gfx::test::CreateImage(40, 24);
 
   std::unique_ptr<base::RunLoop> run_loop_;
diff --git a/components/autofill/core/browser/test_personal_data_manager.h b/components/autofill/core/browser/test_personal_data_manager.h
index c38906e..7855e7b 100644
--- a/components/autofill/core/browser/test_personal_data_manager.h
+++ b/components/autofill/core/browser/test_personal_data_manager.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_PERSONAL_DATA_MANAGER_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <string_view>
 #include <utility>
@@ -20,7 +21,6 @@
 #include "components/autofill/core/browser/strike_databases/autofill_profile_migration_strike_database.h"
 #include "components/autofill/core/browser/strike_databases/test_inmemory_strike_database.h"
 #include "components/signin/public/identity_manager/account_info.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -176,14 +176,14 @@
   std::string timezone_country_code_;
   std::string default_country_code_;
   int num_times_save_imported_credit_card_called_ = 0;
-  absl::optional<bool> autofill_profile_enabled_;
-  absl::optional<bool> autofill_payment_methods_enabled_;
-  absl::optional<bool> autofill_wallet_import_enabled_;
-  absl::optional<bool> eligible_for_account_storage_;
-  absl::optional<bool> payment_methods_mandatory_reauth_enabled_;
-  absl::optional<bool> payments_wallet_sync_transport_enabled_;
+  std::optional<bool> autofill_profile_enabled_;
+  std::optional<bool> autofill_payment_methods_enabled_;
+  std::optional<bool> autofill_wallet_import_enabled_;
+  std::optional<bool> eligible_for_account_storage_;
+  std::optional<bool> payment_methods_mandatory_reauth_enabled_;
+  std::optional<bool> payments_wallet_sync_transport_enabled_;
   CoreAccountInfo account_info_;
-  absl::optional<bool> payments_cvc_storage_enabled_;
+  std::optional<bool> payments_cvc_storage_enabled_;
 
   TestInMemoryStrikeDatabase inmemory_strike_database_;
   AutofillProfileMigrationStrikeDatabase
diff --git a/components/autofill/core/browser/test_utils/vote_uploads_test_matchers.h b/components/autofill/core/browser/test_utils/vote_uploads_test_matchers.h
index 2c11e858b..7a2aa65 100644
--- a/components/autofill/core/browser/test_utils/vote_uploads_test_matchers.h
+++ b/components/autofill/core/browser/test_utils/vote_uploads_test_matchers.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_UTILS_VOTE_UPLOADS_TEST_MATCHERS_H_
 
 #include <initializer_list>
+#include <optional>
 #include <string>
 
 #include "components/autofill/core/browser/proto/server.pb.h"
diff --git a/components/autofill/core/browser/ui/accessory_sheet_data.h b/components/autofill/core/browser/ui/accessory_sheet_data.h
index b556175..7a715e2 100644
--- a/components/autofill/core/browser/ui/accessory_sheet_data.h
+++ b/components/autofill/core/browser/ui/accessory_sheet_data.h
@@ -5,13 +5,13 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_ACCESSORY_SHEET_DATA_H_
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_ACCESSORY_SHEET_DATA_H_
 
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include "base/types/strong_alias.h"
 #include "components/autofill/core/browser/ui/accessory_sheet_enums.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace autofill {
@@ -280,7 +280,7 @@
   void set_option_toggle(OptionToggle toggle) {
     option_toggle_ = std::move(toggle);
   }
-  const absl::optional<OptionToggle>& option_toggle() const {
+  const std::optional<OptionToggle>& option_toggle() const {
     return option_toggle_;
   }
 
@@ -328,7 +328,7 @@
   AccessoryTabType sheet_type_;
   std::u16string title_;
   std::u16string warning_;
-  absl::optional<OptionToggle> option_toggle_;
+  std::optional<OptionToggle> option_toggle_;
   std::vector<PasskeySection> passkey_section_list_;
   std::vector<UserInfo> user_info_list_;
   std::vector<PromoCodeInfo> promo_code_info_list_;
diff --git a/components/autofill/core/browser/ui/address_combobox_model.cc b/components/autofill/core/browser/ui/address_combobox_model.cc
index 97b636b..817d114 100644
--- a/components/autofill/core/browser/ui/address_combobox_model.cc
+++ b/components/autofill/core/browser/ui/address_combobox_model.cc
@@ -72,7 +72,7 @@
   return index == 1;
 }
 
-absl::optional<size_t> AddressComboboxModel::GetDefaultIndex() const {
+std::optional<size_t> AddressComboboxModel::GetDefaultIndex() const {
   if (!default_selected_guid_.empty()) {
     const auto index = GetIndexOfIdentifier(default_selected_guid_);
     if (index.has_value())
@@ -96,13 +96,13 @@
   return addresses_[index - kNbHeaderEntries].first;
 }
 
-absl::optional<size_t> AddressComboboxModel::GetIndexOfIdentifier(
+std::optional<size_t> AddressComboboxModel::GetIndexOfIdentifier(
     const std::string& identifier) const {
   for (size_t i = 0; i < addresses_.size(); ++i) {
     if (addresses_[i].first == identifier)
       return i + kNbHeaderEntries;
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 void AddressComboboxModel::UpdateAddresses() {
diff --git a/components/autofill/core/browser/ui/address_combobox_model.h b/components/autofill/core/browser/ui/address_combobox_model.h
index ca9e4fd..615f1ba6 100644
--- a/components/autofill/core/browser/ui/address_combobox_model.h
+++ b/components/autofill/core/browser/ui/address_combobox_model.h
@@ -39,7 +39,7 @@
   size_t GetItemCount() const override;
   std::u16string GetItemAt(size_t index) const override;
   bool IsItemSeparatorAt(size_t index) const override;
-  absl::optional<size_t> GetDefaultIndex() const override;
+  std::optional<size_t> GetDefaultIndex() const override;
 
   // Adds |profile| to model and return its combobox index. The lifespan of
   // |profile| beyond this call is undefined so a copy must be made.
@@ -51,7 +51,7 @@
 
   // Returns the combobox index of the item with the given id or nullopt if it's
   // not found.
-  absl::optional<size_t> GetIndexOfIdentifier(
+  std::optional<size_t> GetIndexOfIdentifier(
       const std::string& identifier) const;
 
  private:
diff --git a/components/autofill/core/browser/ui/address_contact_form_label_formatter.cc b/components/autofill/core/browser/ui/address_contact_form_label_formatter.cc
deleted file mode 100644
index cc816c8..0000000
--- a/components/autofill/core/browser/ui/address_contact_form_label_formatter.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/address_contact_form_label_formatter.h"
-
-#include "components/autofill/core/browser/autofill_data_util.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-
-namespace autofill {
-
-AddressContactFormLabelFormatter::AddressContactFormLabelFormatter(
-    const std::vector<const AutofillProfile*>& profiles,
-    const std::string& app_locale,
-    FieldType focused_field_type,
-    uint32_t groups,
-    const FieldTypeSet& field_types)
-    : LabelFormatter(profiles,
-                     app_locale,
-                     focused_field_type,
-                     groups,
-                     field_types),
-      form_has_street_address_(HasStreetAddress(field_types_for_labels())),
-      email_disambiguates_(!HaveSameEmailAddresses(profiles, app_locale)),
-      phone_disambiguates_(!HaveSamePhoneNumbers(profiles, app_locale)) {}
-
-AddressContactFormLabelFormatter::~AddressContactFormLabelFormatter() = default;
-
-// Note that the order in which parts of the label are added--name, street
-// address, phone, and email--ensures that the label is formatted correctly for
-// |focused_group|, |focused_field_type_|, and this kind of formatter.
-std::u16string AddressContactFormLabelFormatter::GetLabelForProfile(
-    const AutofillProfile& profile,
-    FieldTypeGroup focused_group) const {
-  std::vector<std::u16string> label_parts;
-
-  bool street_address_is_focused = focused_group == FieldTypeGroup::kAddress &&
-                                   IsStreetAddressPart(focused_field_type());
-  bool non_street_address_is_focused =
-      focused_group == FieldTypeGroup::kAddress &&
-      !IsStreetAddressPart(focused_field_type());
-
-  if (focused_group != FieldTypeGroup::kName &&
-      !non_street_address_is_focused && data_util::ContainsName(groups())) {
-    AddLabelPartIfNotEmpty(
-        GetLabelName(field_types_for_labels(), profile, app_locale()),
-        &label_parts);
-  }
-
-  if (!street_address_is_focused) {
-    AddLabelPartIfNotEmpty(
-        GetLabelAddress(form_has_street_address_, profile, app_locale(),
-                        field_types_for_labels()),
-        &label_parts);
-  }
-
-  if (focused_group != FieldTypeGroup::kPhone && phone_disambiguates_) {
-    AddLabelPartIfNotEmpty(GetLabelPhone(profile, app_locale()), &label_parts);
-  }
-
-  if (focused_group != FieldTypeGroup::kEmail && email_disambiguates_) {
-    AddLabelPartIfNotEmpty(GetLabelEmail(profile, app_locale()), &label_parts);
-  }
-
-  // |label_parts| is empty if all of the following are true:
-  // (A) The form doesn't have a name field.
-  // (B) The user is focused on the street address.
-  // (C) The user either (i) has one profile or (ii) has multiple profiles and
-  // has the same phone number and email address in the profiles.
-  if (street_address_is_focused && label_parts.empty()) {
-    AddLabelPartIfNotEmpty(GetLabelPhone(profile, app_locale()), &label_parts);
-  }
-
-  return ConstructLabelLine(label_parts);
-}
-
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/address_contact_form_label_formatter.h b/components/autofill/core/browser/ui/address_contact_form_label_formatter.h
deleted file mode 100644
index 55d3c923..0000000
--- a/components/autofill/core/browser/ui/address_contact_form_label_formatter.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_ADDRESS_CONTACT_FORM_LABEL_FORMATTER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_ADDRESS_CONTACT_FORM_LABEL_FORMATTER_H_
-
-#include <string>
-#include <vector>
-
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/ui/label_formatter.h"
-
-namespace autofill {
-
-// A LabelFormatter that creates Suggestions' disambiguating labels for forms
-// with name, address, email, and phone fields.
-class AddressContactFormLabelFormatter : public LabelFormatter {
- public:
-  AddressContactFormLabelFormatter(
-      const std::vector<const AutofillProfile*>& profiles,
-      const std::string& app_locale,
-      FieldType focused_field_type,
-      uint32_t groups,
-      const FieldTypeSet& field_types);
-
-  ~AddressContactFormLabelFormatter() override;
-
-  std::u16string GetLabelForProfile(
-      const AutofillProfile& profile,
-      FieldTypeGroup focused_group) const override;
-
- private:
-  // True if this formatter's associated form has a street address field. A
-  // form may have an address-related field, e.g. zip code, without having a
-  // street address field. If a form does not include a street address field,
-  // street addresses should not appear in labels.
-  bool form_has_street_address_;
-
-  // True if the field disambiguates |profiles_|.
-  bool email_disambiguates_;
-  bool phone_disambiguates_;
-};
-
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_ADDRESS_CONTACT_FORM_LABEL_FORMATTER_H_
diff --git a/components/autofill/core/browser/ui/address_contact_form_label_formatter_unittest.cc b/components/autofill/core/browser/ui/address_contact_form_label_formatter_unittest.cc
deleted file mode 100644
index 6299278..0000000
--- a/components/autofill/core/browser/ui/address_contact_form_label_formatter_unittest.cc
+++ /dev/null
@@ -1,457 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/address_contact_form_label_formatter.h"
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/strings/utf_string_conversions.h"
-#include "base/uuid.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::ElementsAre;
-
-namespace autofill {
-namespace {
-
-FieldTypeSet GetFieldTypes() {
-  return {NO_SERVER_DATA,         NAME_FULL,
-          EMAIL_ADDRESS,          ADDRESS_HOME_LINE1,
-          ADDRESS_HOME_LINE2,     ADDRESS_HOME_DEPENDENT_LOCALITY,
-          ADDRESS_HOME_CITY,      ADDRESS_HOME_STATE,
-          ADDRESS_HOME_ZIP,       ADDRESS_HOME_COUNTRY,
-          PHONE_HOME_WHOLE_NUMBER};
-}
-
-TEST(AddressContactFormLabelFormatterTest, GetLabelsWithMissingProfiles) {
-  const std::vector<const AutofillProfile*> profiles{};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", NAME_FULL, GetFieldTypes());
-  EXPECT_TRUE(formatter->GetLabels().empty());
-}
-
-TEST(AddressContactFormLabelFormatterTest,
-     GetLabelsForUSProfilesAndFocusedName) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Sarah", "", "Revere", "sarah.revere@aol.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "16175232338");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "L", "Kennedy", "", "",
-                       "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "6175141600");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "paul1775@gmail.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "Deborah", "", "Katabi", "deborah@mit.edu",
-                       "", "", "", "", "", "", "US", "6173240000");
-
-  AutofillProfile profile5(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile5, "", "", "", "", "", "Old North Church",
-                       "193 Salem St", "Boston", "MA", "02113", "US", "");
-
-  AutofillProfile profile6(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile6, "", "", "", "", "", "", "", "", "", "", "US",
-                       "");
-
-  const std::vector<const AutofillProfile*> profiles{
-      &profile1, &profile2, &profile3, &profile4, &profile5, &profile6};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", NAME_FULL, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"19 North Sq", u"(617) 523-2338",
-                                      u"sarah.revere@aol.com"}),
-                  ConstructLabelLine({u"151 Irving Ave", u"(617) 514-1600"}),
-                  ConstructLabelLine({u"19 North Sq", u"paul1775@gmail.com"}),
-                  ConstructLabelLine({u"(617) 324-0000", u"deborah@mit.edu"}),
-                  u"Old North Church, 193 Salem St", std::u16string()));
-}
-
-TEST(AddressContactFormLabelFormatterTest,
-     GetLabelsForUSProfilesAndFocusedStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Sarah", "", "Revere", "sarah.revere@aol.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "16175232338");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "L", "Kennedy", "", "",
-                       "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "6175141600");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "paul1775@gmail.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "Deborah", "", "Katabi", "deborah@mit.edu",
-                       "", "", "", "", "", "", "US", "6173240000");
-
-  AutofillProfile profile5(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile5, "", "", "", "", "", "Old North Church",
-                       "193 Salem St", "Boston", "MA", "02113", "US", "");
-
-  AutofillProfile profile6(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile6, "", "", "", "", "", "", "", "", "", "", "US",
-                       "");
-
-  const std::vector<const AutofillProfile*> profiles{
-      &profile1, &profile2, &profile3, &profile4, &profile5, &profile6};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_LINE1, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"Sarah Revere", u"(617) 523-2338",
-                                      u"sarah.revere@aol.com"}),
-                  ConstructLabelLine({u"Jackie L Kennedy", u"(617) 514-1600"}),
-                  ConstructLabelLine({u"Paul Revere", u"paul1775@gmail.com"}),
-                  ConstructLabelLine({u"Deborah Katabi", u"(617) 324-0000",
-                                      u"deborah@mit.edu"}),
-                  u"", std::u16string()));
-}
-
-TEST(AddressContactFormLabelFormatterTest,
-     GetLabelsForUSProfilesAndFocusedNonStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Sarah", "", "Revere", "sarah.revere@aol.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "16175232338");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "L", "Kennedy", "", "",
-                       "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "6175141600");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "paul1775@gmail.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "Deborah", "", "Katabi", "deborah@mit.edu",
-                       "", "", "", "", "", "", "US", "6173240000");
-
-  AutofillProfile profile5(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile5, "", "", "", "", "", "Old North Church",
-                       "193 Salem St", "Boston", "MA", "02113", "US", "");
-
-  AutofillProfile profile6(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile6, "", "", "", "", "", "", "", "", "", "", "US",
-                       "");
-
-  const std::vector<const AutofillProfile*> profiles{
-      &profile1, &profile2, &profile3, &profile4, &profile5, &profile6};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_CITY, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"19 North Sq", u"(617) 523-2338",
-                                      u"sarah.revere@aol.com"}),
-                  ConstructLabelLine({u"151 Irving Ave", u"(617) 514-1600"}),
-                  ConstructLabelLine({u"19 North Sq", u"paul1775@gmail.com"}),
-                  ConstructLabelLine({u"(617) 324-0000", u"deborah@mit.edu"}),
-                  u"Old North Church, 193 Salem St", std::u16string()));
-}
-
-TEST(AddressContactFormLabelFormatterTest,
-     GetLabelsForUSProfilesAndFocusedEmail) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Sarah", "", "Revere", "sarah.revere@aol.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "16175232338");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "L", "Kennedy", "", "",
-                       "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "6175141600");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "paul1775@gmail.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "Deborah", "", "Katabi", "deborah@mit.edu",
-                       "", "", "", "", "", "", "US", "6173240000");
-
-  AutofillProfile profile5(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile5, "", "", "", "", "", "Old North Church",
-                       "193 Salem St", "Boston", "MA", "02113", "US", "");
-
-  AutofillProfile profile6(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile6, "", "", "", "", "", "", "", "", "", "", "US",
-                       "");
-
-  const std::vector<const AutofillProfile*> profiles{
-      &profile1, &profile2, &profile3, &profile4, &profile5, &profile6};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", EMAIL_ADDRESS, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine(
-                      {u"Sarah Revere", u"19 North Sq", u"(617) 523-2338"}),
-                  ConstructLabelLine({u"Jackie L Kennedy", u"151 Irving Ave",
-                                      u"(617) 514-1600"}),
-                  ConstructLabelLine({u"Paul Revere", u"19 North Sq"}),
-                  ConstructLabelLine({u"Deborah Katabi", u"(617) 324-0000"}),
-                  u"Old North Church, 193 Salem St", std::u16string()));
-}
-
-TEST(AddressContactFormLabelFormatterTest,
-     GetLabelsForUSProfilesAndFocusedPhone) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Sarah", "", "Revere", "sarah.revere@aol.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "16175232338");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "L", "Kennedy", "", "",
-                       "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "6175141600");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "paul1775@gmail.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "Deborah", "", "Katabi", "deborah@mit.edu",
-                       "", "", "", "", "", "", "US", "6173240000");
-
-  AutofillProfile profile5(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile5, "", "", "", "", "", "Old North Church",
-                       "193 Salem St", "Boston", "MA", "02113", "US", "");
-
-  AutofillProfile profile6(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile6, "", "", "", "", "", "", "", "", "", "", "US",
-                       "");
-
-  const std::vector<const AutofillProfile*> profiles{
-      &profile1, &profile2, &profile3, &profile4, &profile5, &profile6};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", PHONE_HOME_WHOLE_NUMBER, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"Sarah Revere", u"19 North Sq",
-                                      u"sarah.revere@aol.com"}),
-                  ConstructLabelLine({u"Jackie L Kennedy", u"151 Irving Ave"}),
-                  ConstructLabelLine(
-                      {u"Paul Revere", u"19 North Sq", u"paul1775@gmail.com"}),
-                  ConstructLabelLine({u"Deborah Katabi", u"deborah@mit.edu"}),
-                  u"Old North Church, 193 Salem St", std::u16string()));
-}
-
-TEST(AddressContactFormLabelFormatterTest,
-     GetLabelsForBRProfilesAndFocusedName) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", " SP ", " 04094-050 ", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "pt-BR", NAME_FULL, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(
-          ConstructLabelLine({u"Av. Pedro Álvares Cabral, 1301",
-                              u"(11) 2648-0254", u"tarsila@aol.com"}),
-          ConstructLabelLine({u"Estr. Dona Castorina, 110", u"(21) 98765-0000",
-                              u"aavila@uol.com.br"})));
-}
-
-TEST(AddressContactFormLabelFormatterTest,
-     GetLabelsForBRProfilesAndFocusedStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", " SP ", " 04094-050 ", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", ADDRESS_HOME_LINE1, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"Tarsila do Amaral", u"(11) 2648-0254",
-                                      u"tarsila@aol.com"}),
-                  ConstructLabelLine({u"Artur Avila", u"(21) 98765-0000",
-                                      u"aavila@uol.com.br"})));
-}
-
-TEST(AddressContactFormLabelFormatterTest,
-     GetLabelsForBRProfilesAndFocusedNonStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", " SP ", " 04094-050 ", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", ADDRESS_HOME_ZIP, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(
-          ConstructLabelLine({u"Av. Pedro Álvares Cabral, 1301",
-                              u"(11) 2648-0254", u"tarsila@aol.com"}),
-          ConstructLabelLine({u"Estr. Dona Castorina, 110", u"(21) 98765-0000",
-                              u"aavila@uol.com.br"})));
-}
-
-TEST(AddressContactFormLabelFormatterTest,
-     GetLabelsForBRProfilesAndFocusedEmail) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", " SP ", " 04094-050 ", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "pt-BR", EMAIL_ADDRESS, GetFieldTypes());
-
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(ConstructLabelLine({u"Tarsila do Amaral",
-                                              u"Av. Pedro Álvares Cabral, 1301",
-                                              u"(11) 2648-0254"}),
-                          ConstructLabelLine({u"Artur Avila",
-                                              u"Estr. Dona Castorina, 110",
-                                              u"(21) 98765-0000"})));
-}
-
-TEST(AddressContactFormLabelFormatterTest,
-     GetLabelsForBRProfilesAndFocusedPhone) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", " SP ", " 04094-050 ", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", PHONE_HOME_WHOLE_NUMBER, GetFieldTypes());
-
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(ConstructLabelLine({u"Tarsila do Amaral",
-                                              u"Av. Pedro Álvares Cabral, 1301",
-                                              u"tarsila@aol.com"}),
-                          ConstructLabelLine({u"Artur Avila",
-                                              u"Estr. Dona Castorina, 110",
-                                              u"aavila@uol.com.br"})));
-}
-
-TEST(AddressContactFormLabelFormatterTest,
-     GetLabelsForFormWithPartialAddressFields) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Sarah", "", "Revere", "sarah.revere@aol.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "16175232338");
-
-  const std::vector<const AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", EMAIL_ADDRESS,
-      {NAME_FULL, EMAIL_ADDRESS, ADDRESS_HOME_ZIP, PHONE_HOME_WHOLE_NUMBER});
-
-  // Checks that only address fields in the form are shown in the label.
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(ConstructLabelLine({u"Sarah Revere", u"02113"})));
-}
-
-TEST(AddressContactFormLabelFormatterTest,
-     GetLabelsForFormWithoutName_FocusedStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Sarah", "", "Revere", "sarah.revere@aol.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "16175232338");
-
-  std::vector<const AutofillProfile*> profiles{&profile1};
-  std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_LINE1,
-      {ADDRESS_HOME_ZIP, EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER});
-
-  // Checks that the name is not in the label and that the phone number is for
-  // a unique profile.
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"(617) 523-2338"));
-
-  profiles = {&profile1, &profile1};
-  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1,
-                                     {ADDRESS_HOME_LINE1, ADDRESS_HOME_ZIP,
-                                      EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER});
-
-  // Checks that the name is not in the label and that the phone number is for
-  // multiple profiles with the same phone number and email address.
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"(617) 523-2338", u"(617) 523-2338"));
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Sarah", "", "Revere", "sarah@gmail.com", "",
-                       "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "16175232338");
-
-  profiles = {&profile1, &profile2};
-  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1,
-                                     {ADDRESS_HOME_LINE1, ADDRESS_HOME_ZIP,
-                                      EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER});
-  // Checks that the name is not in the label and that the email address is
-  // shown because the profiles' email addresses are different.
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"sarah.revere@aol.com", u"sarah@gmail.com"));
-}
-
-}  // namespace
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/address_email_form_label_formatter.cc b/components/autofill/core/browser/ui/address_email_form_label_formatter.cc
deleted file mode 100644
index 2b894be..0000000
--- a/components/autofill/core/browser/ui/address_email_form_label_formatter.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/address_email_form_label_formatter.h"
-
-#include "components/autofill/core/browser/autofill_data_util.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-
-namespace autofill {
-
-AddressEmailFormLabelFormatter::AddressEmailFormLabelFormatter(
-    const std::vector<const AutofillProfile*>& profiles,
-    const std::string& app_locale,
-    FieldType focused_field_type,
-    uint32_t groups,
-    const FieldTypeSet& field_types)
-    : LabelFormatter(profiles,
-                     app_locale,
-                     focused_field_type,
-                     groups,
-                     field_types),
-      form_has_street_address_(HasStreetAddress(field_types_for_labels())) {}
-
-AddressEmailFormLabelFormatter::~AddressEmailFormLabelFormatter() = default;
-
-std::u16string AddressEmailFormLabelFormatter::GetLabelForProfile(
-    const AutofillProfile& profile,
-    FieldTypeGroup focused_group) const {
-  return focused_group == FieldTypeGroup::kAddress &&
-                 !IsStreetAddressPart(focused_field_type())
-             ? GetLabelForProfileOnFocusedNonStreetAddress(
-                   form_has_street_address_, profile, app_locale(),
-                   field_types_for_labels(),
-                   GetLabelEmail(profile, app_locale()))
-             : GetLabelForProfileOnFocusedNameEmailOrStreetAddress(
-                   profile, focused_group);
-}
-
-// Note that the order--name, address, and email--in which parts of the label
-// are added ensures that the label is formatted correctly for |focused_group|,
-// |focused_field_type_| and for this kind of formatter.
-std::u16string AddressEmailFormLabelFormatter::
-    GetLabelForProfileOnFocusedNameEmailOrStreetAddress(
-        const AutofillProfile& profile,
-        FieldTypeGroup focused_group) const {
-  std::vector<std::u16string> label_parts;
-
-  if (focused_group != FieldTypeGroup::kName &&
-      data_util::ContainsName(groups())) {
-    AddLabelPartIfNotEmpty(
-        GetLabelName(field_types_for_labels(), profile, app_locale()),
-        &label_parts);
-  }
-
-  if (focused_group != FieldTypeGroup::kAddress) {
-    AddLabelPartIfNotEmpty(
-        GetLabelAddress(form_has_street_address_, profile, app_locale(),
-                        field_types_for_labels()),
-        &label_parts);
-  }
-
-  if (focused_group != FieldTypeGroup::kEmail) {
-    AddLabelPartIfNotEmpty(GetLabelEmail(profile, app_locale()), &label_parts);
-  }
-
-  return ConstructLabelLine(label_parts);
-}
-
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/address_email_form_label_formatter.h b/components/autofill/core/browser/ui/address_email_form_label_formatter.h
deleted file mode 100644
index 658dc4b..0000000
--- a/components/autofill/core/browser/ui/address_email_form_label_formatter.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_ADDRESS_EMAIL_FORM_LABEL_FORMATTER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_ADDRESS_EMAIL_FORM_LABEL_FORMATTER_H_
-
-#include <string>
-#include <vector>
-
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/ui/label_formatter.h"
-
-namespace autofill {
-
-// A LabelFormatter that creates Suggestions' disambiguating labels for forms
-// with name, address, and email fields and without phone fields.
-class AddressEmailFormLabelFormatter : public LabelFormatter {
- public:
-  AddressEmailFormLabelFormatter(
-      const std::vector<const AutofillProfile*>& profiles,
-      const std::string& app_locale,
-      FieldType focused_field_type,
-      uint32_t groups,
-      const FieldTypeSet& field_types);
-
-  ~AddressEmailFormLabelFormatter() override;
-
-  std::u16string GetLabelForProfile(
-      const AutofillProfile& profile,
-      FieldTypeGroup focused_group) const override;
-
- private:
-  // Returns a label to show the user when |focused_field_type_| is a type
-  // other than a non-street-address field type. For example,
-  // |focused_field_type_| could be last name, home street address, or email
-  // address.
-  std::u16string GetLabelForProfileOnFocusedNameEmailOrStreetAddress(
-      const AutofillProfile& profile,
-      FieldTypeGroup focused_group) const;
-
-  // True if this formatter's associated form has a street address field. A
-  // form may have an address-related field, e.g. zip code, without having a
-  // street address field. If a form does not include a street address field,
-  // street addresses should not appear in labels.
-  bool form_has_street_address_;
-};
-
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_ADDRESS_EMAIL_FORM_LABEL_FORMATTER_H_
diff --git a/components/autofill/core/browser/ui/address_email_form_label_formatter_unittest.cc b/components/autofill/core/browser/ui/address_email_form_label_formatter_unittest.cc
deleted file mode 100644
index 7f6b756..0000000
--- a/components/autofill/core/browser/ui/address_email_form_label_formatter_unittest.cc
+++ /dev/null
@@ -1,300 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/address_email_form_label_formatter.h"
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/strings/utf_string_conversions.h"
-#include "base/uuid.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::ElementsAre;
-
-namespace autofill {
-namespace {
-
-FieldTypeSet GetFieldTypes() {
-  return {NAME_FULL,
-          EMAIL_ADDRESS,
-          ADDRESS_HOME_LINE1,
-          ADDRESS_HOME_LINE2,
-          ADDRESS_HOME_CITY,
-          ADDRESS_HOME_STATE,
-          ADDRESS_HOME_DEPENDENT_LOCALITY,
-          ADDRESS_HOME_ZIP,
-          ADDRESS_HOME_COUNTRY};
-}
-
-TEST(AddressEmailFormLabelFormatterTest, GetLabelsWithMissingProfiles) {
-  const std::vector<const AutofillProfile*> profiles{};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", NAME_FULL, GetFieldTypes());
-  EXPECT_TRUE(formatter->GetLabels().empty());
-}
-
-TEST(AddressEmailFormLabelFormatterTest, GetLabelsForUSProfilesAndFocusedName) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "", "",
-                       "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "paul1775@gmail.com",
-                       "", "", "", "", "", "", "US", "");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "John", "", "Adams", "", "", "", "", "Quincy",
-                       "MA", "02169", "US", "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2,
-                                                     &profile3, &profile4};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", NAME_FULL, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"333 Washington St", u"jfk@gmail.com"}),
-                  u"151 Irving Ave", u"paul1775@gmail.com", std::u16string()));
-}
-
-TEST(AddressEmailFormLabelFormatterTest,
-     GetLabelsForUSProfilesAndFocusedStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "", "",
-                       "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "", "", "", "paul1775@gmail.com", "", "", "",
-                       "", "", "", "US", "");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "", "", "", "", "", "141 Franklin St", "",
-                       "Quincy", "MA", "02169", "US", "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2,
-                                                     &profile3, &profile4};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_LINE1, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"John F Kennedy", u"jfk@gmail.com"}),
-                  u"Jackie Kennedy", u"paul1775@gmail.com", std::u16string()));
-}
-
-TEST(AddressEmailFormLabelFormatterTest,
-     GetLabelsForUSProfilesAndFocusedNonStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "", "",
-                       "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "", "", "", "paul1775@gmail.com", "", "", "",
-                       "", "", "", "US", "");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "", "", "", "", "", "", "", "Quincy", "MA",
-                       "02169", "US", "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2,
-                                                     &profile3, &profile4};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_ZIP, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"333 Washington St", u"jfk@gmail.com"}),
-                  u"151 Irving Ave", u"paul1775@gmail.com", std::u16string()));
-}
-
-TEST(AddressEmailFormLabelFormatterTest,
-     GetLabelsForUSProfilesAndFocusedEmail) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "jackie@outlook.com",
-                       "", "", "", "Hyannis", "MA", "02601", "US", "");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "", "", "", "paul1775@gmail.com", "", "", "",
-                       "", "", "", "US", "");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "", "", "", "", "", "141 Franklin St", "",
-                       "Quincy", "MA", "02169", "US", "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2,
-                                                     &profile3, &profile4};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", EMAIL_ADDRESS, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"John F Kennedy", u"333 Washington St"}),
-                  u"Jackie Kennedy", std::u16string(), u"141 Franklin St"));
-}
-
-TEST(AddressEmailFormLabelFormatterTest, GetLabelsForBRProfilesAndFocusedName) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", "SP", "04094-050", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "pt-BR", NAME_FULL, GetFieldTypes());
-
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(ConstructLabelLine({u"Av. Pedro Álvares Cabral, 1301",
-                                              u"tarsila@aol.com"}),
-                          ConstructLabelLine({u"Estr. Dona Castorina, 110",
-                                              u"aavila@uol.com.br"})));
-}
-
-TEST(AddressEmailFormLabelFormatterTest,
-     GetLabelsForBRProfilesAndFocusedStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", "SP", "04094-050", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", ADDRESS_HOME_LINE1, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(
-          ConstructLabelLine({u"Tarsila do Amaral", u"tarsila@aol.com"}),
-          ConstructLabelLine({u"Artur Avila", u"aavila@uol.com.br"})));
-}
-
-TEST(AddressEmailFormLabelFormatterTest,
-     GetLabelsForBRProfilesAndFocusedNonStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", "SP", "04094-050", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", ADDRESS_HOME_DEPENDENT_LOCALITY, GetFieldTypes());
-
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(ConstructLabelLine({u"Av. Pedro Álvares Cabral, 1301",
-                                              u"tarsila@aol.com"}),
-                          ConstructLabelLine({u"Estr. Dona Castorina, 110",
-                                              u"aavila@uol.com.br"})));
-}
-
-TEST(AddressEmailFormLabelFormatterTest,
-     GetLabelsForBRProfilesAndFocusedEmail) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", "SP", "04094-050", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "pt-BR", EMAIL_ADDRESS, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(
-          ConstructLabelLine(
-              {u"Tarsila do Amaral", u"Av. Pedro Álvares Cabral, 1301"}),
-          ConstructLabelLine({u"Artur Avila", u"Estr. Dona Castorina, 110"})));
-}
-
-TEST(AddressEmailFormLabelFormatterTest,
-     GetLabelsForFormWithAddressFieldsMinusStreetAddress) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", EMAIL_ADDRESS,
-      {NAME_FULL, EMAIL_ADDRESS, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE});
-
-  // Checks that only address fields in the form are shown in the label.
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"John F Kennedy", u"Brookline, MA"})));
-}
-
-TEST(AddressEmailFormLabelFormatterTest, GetLabelsForFormWithoutName) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Sarah", "", "Revere", "sarah.revere@aol.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "16175232338");
-
-  const std::vector<const AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_LINE1,
-      {ADDRESS_HOME_LINE1, ADDRESS_HOME_ZIP, EMAIL_ADDRESS});
-
-  // Checks that the name does not appear in the labels.
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"sarah.revere@aol.com"));
-}
-
-}  // namespace
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/address_form_label_formatter.cc b/components/autofill/core/browser/ui/address_form_label_formatter.cc
deleted file mode 100644
index d0f3942d..0000000
--- a/components/autofill/core/browser/ui/address_form_label_formatter.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/address_form_label_formatter.h"
-
-#include "components/autofill/core/browser/autofill_data_util.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-
-namespace autofill {
-
-AddressFormLabelFormatter::AddressFormLabelFormatter(
-    const std::vector<const AutofillProfile*>& profiles,
-    const std::string& app_locale,
-    FieldType focused_field_type,
-    uint32_t groups,
-    const FieldTypeSet& field_types)
-    : LabelFormatter(profiles,
-                     app_locale,
-                     focused_field_type,
-                     groups,
-                     field_types),
-      form_has_street_address_(HasStreetAddress(field_types_for_labels())) {}
-
-AddressFormLabelFormatter::~AddressFormLabelFormatter() = default;
-
-std::u16string AddressFormLabelFormatter::GetLabelForProfile(
-    const AutofillProfile& profile,
-    FieldTypeGroup focused_group) const {
-  if (focused_group != FieldTypeGroup::kAddress) {
-    return GetLabelNationalAddress(field_types_for_labels(), profile,
-                                   app_locale());
-  } else {
-    std::vector<std::u16string> label_parts;
-
-    if (data_util::ContainsName(groups())) {
-      AddLabelPartIfNotEmpty(
-          GetLabelName(field_types_for_labels(), profile, app_locale()),
-          &label_parts);
-    }
-
-    AddLabelPartIfNotEmpty(GetLabelForFocusedAddress(
-                               focused_field_type(), form_has_street_address_,
-                               profile, app_locale(), field_types_for_labels()),
-                           &label_parts);
-    return ConstructLabelLine(label_parts);
-  }
-}
-
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/address_form_label_formatter.h b/components/autofill/core/browser/ui/address_form_label_formatter.h
deleted file mode 100644
index 79cd934..0000000
--- a/components/autofill/core/browser/ui/address_form_label_formatter.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_ADDRESS_FORM_LABEL_FORMATTER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_ADDRESS_FORM_LABEL_FORMATTER_H_
-
-#include <string>
-#include <vector>
-
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/ui/label_formatter.h"
-
-namespace autofill {
-
-// A LabelFormatter that creates Suggestions' disambiguating labels for forms
-// with name and address fields and without email or phone fields.
-class AddressFormLabelFormatter : public LabelFormatter {
- public:
-  AddressFormLabelFormatter(const std::vector<const AutofillProfile*>& profiles,
-                            const std::string& app_locale,
-                            FieldType focused_field_type,
-                            uint32_t groups,
-                            const FieldTypeSet& field_types);
-
-  ~AddressFormLabelFormatter() override;
-
-  std::u16string GetLabelForProfile(
-      const AutofillProfile& profile,
-      FieldTypeGroup focused_group) const override;
-
- private:
-  // True if this formatter's associated form has a street address field. A
-  // form may have an address-related field, e.g. zip code, without having a
-  // street address field. If a form does not include a street address field,
-  // street addresses should not appear in labels.
-  bool form_has_street_address_;
-};
-
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_ADDRESS_FORM_LABEL_FORMATTER_H_
diff --git a/components/autofill/core/browser/ui/address_form_label_formatter_unittest.cc b/components/autofill/core/browser/ui/address_form_label_formatter_unittest.cc
deleted file mode 100644
index ac640d0..0000000
--- a/components/autofill/core/browser/ui/address_form_label_formatter_unittest.cc
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/address_form_label_formatter.h"
-
-#include <string>
-#include <vector>
-
-#include "base/strings/utf_string_conversions.h"
-#include "base/uuid.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::ElementsAre;
-
-namespace autofill {
-namespace {
-
-FieldTypeSet GetFieldTypes() {
-  return {NO_SERVER_DATA,     NAME_FIRST,
-          NAME_LAST,          ADDRESS_HOME_LINE1,
-          ADDRESS_HOME_LINE2, ADDRESS_HOME_DEPENDENT_LOCALITY,
-          ADDRESS_HOME_CITY,  ADDRESS_HOME_STATE,
-          ADDRESS_HOME_ZIP,   ADDRESS_HOME_COUNTRY};
-}
-
-TEST(AddressFormLabelFormatterTest, GetLabelsWithMissingProfiles) {
-  const std::vector<const AutofillProfile*> profiles{};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", NAME_FIRST, GetFieldTypes());
-  EXPECT_TRUE(formatter->GetLabels().empty());
-}
-
-TEST(AddressFormLabelFormatterTest,
-     GetLabelsForUSProfilesAndFocusedStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "", "", "", "jackie@outlook.com", "",
-                       "151 Irving Ave", "", "Hyannis", "MA", "", "US",
-                       "5087717796");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "paul1775@gmail.com",
-                       "", "", "", "", "", "", "US", "");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "", "", "", "", "", "", "", "", "", "", "US",
-                       "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2,
-                                                     &profile3, &profile4};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_LINE1, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"John Kennedy", u"Brookline, MA 02445"}),
-                  u"Hyannis, MA", u"Paul Revere", std::u16string()));
-}
-
-TEST(AddressFormLabelFormatterTest,
-     GetLabelsForUSProfilesAndFocusedNonStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "", "", "", "jackie@outlook.com", "",
-                       "151 Irving Ave", "", "Hyannis", "MA", "", "US",
-                       "5087717796");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "paul1775@gmail.com",
-                       "", "", "", "", "", "", "US", "");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "", "", "", "", "", "", "", "", "", "", "US",
-                       "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2,
-                                                     &profile3, &profile4};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_CITY, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"John Kennedy", u"333 Washington St"}),
-                  u"151 Irving Ave", u"Paul Revere", std::u16string()));
-}
-
-TEST(AddressFormLabelFormatterTest, GetLabelsForUSProfilesAndFocusedName) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "jackie@outlook.com",
-                       "", "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "5087717796");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", NAME_FIRST, GetFieldTypes());
-
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"333 Washington St, Brookline, MA 02445",
-                          u"151 Irving Ave, Hyannis, MA 02601"));
-}
-
-TEST(AddressFormLabelFormatterTest,
-     GetLabelsForBRProfilesAndFocusedStreetAddress) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", "SP", "04094-050", "BR", "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", ADDRESS_HOME_LINE1, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine(
-          {u"Tarsila Amaral", u"Vila Mariana, São Paulo-SP, 04094-050"})));
-}
-
-TEST(AddressFormLabelFormatterTest,
-     GetLabelsForBRProfilesAndFocusedNonStreetAddress) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", "SP", "04094-050", "BR", "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", ADDRESS_HOME_ZIP, GetFieldTypes());
-
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(ConstructLabelLine(
-                  {u"Tarsila Amaral", u"Av. Pedro Álvares Cabral, 1301"})));
-}
-
-TEST(AddressFormLabelFormatterTest, GetLabelsForBRProfilesAndFocusedName) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", "SP", "04094-050", "BR", "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "pt-BR", NAME_FIRST, GetFieldTypes());
-
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"Av. Pedro Álvares Cabral, 1301, Vila Mariana, São "
-                          u"Paulo-SP, 04094-050"));
-}
-
-TEST(AddressFormLabelFormatterTest, GetLabelsForFormWithoutName) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Sarah", "", "Revere", "sarah.revere@aol.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "16175232338");
-
-  const std::vector<const AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_LINE1,
-      {ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, ADDRESS_HOME_DEPENDENT_LOCALITY,
-       ADDRESS_HOME_ZIP});
-
-  // Checks that the name does not appear in the labels.
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"Boston, MA 02113"));
-}
-
-}  // namespace
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/address_phone_form_label_formatter.cc b/components/autofill/core/browser/ui/address_phone_form_label_formatter.cc
deleted file mode 100644
index 97a47ffe9..0000000
--- a/components/autofill/core/browser/ui/address_phone_form_label_formatter.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/address_phone_form_label_formatter.h"
-
-#include "components/autofill/core/browser/autofill_data_util.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-
-namespace autofill {
-
-AddressPhoneFormLabelFormatter::AddressPhoneFormLabelFormatter(
-    const std::vector<const AutofillProfile*>& profiles,
-    const std::string& app_locale,
-    FieldType focused_field_type,
-    uint32_t groups,
-    const FieldTypeSet& field_types)
-    : LabelFormatter(profiles,
-                     app_locale,
-                     focused_field_type,
-                     groups,
-                     field_types),
-      form_has_street_address_(HasStreetAddress(field_types_for_labels())) {}
-
-AddressPhoneFormLabelFormatter::~AddressPhoneFormLabelFormatter() = default;
-
-std::u16string AddressPhoneFormLabelFormatter::GetLabelForProfile(
-    const AutofillProfile& profile,
-    FieldTypeGroup focused_group) const {
-  return focused_group == FieldTypeGroup::kAddress &&
-                 !IsStreetAddressPart(focused_field_type())
-             ? GetLabelForProfileOnFocusedNonStreetAddress(
-                   form_has_street_address_, profile, app_locale(),
-                   field_types_for_labels(),
-                   GetLabelPhone(profile, app_locale()))
-             : GetLabelForProfileOnFocusedNamePhoneOrStreetAddress(
-                   profile, focused_group);
-}
-
-// Note that the order--name, phone, and address--in which parts of the label
-// are added ensures that the label is formatted correctly for |focused_group|,
-// |focused_field_type_| and for this kind of formatter.
-std::u16string AddressPhoneFormLabelFormatter::
-    GetLabelForProfileOnFocusedNamePhoneOrStreetAddress(
-        const AutofillProfile& profile,
-        FieldTypeGroup focused_group) const {
-  std::vector<std::u16string> label_parts;
-  if (focused_group != FieldTypeGroup::kName &&
-      data_util::ContainsName(groups())) {
-    AddLabelPartIfNotEmpty(
-        GetLabelName(field_types_for_labels(), profile, app_locale()),
-        &label_parts);
-  }
-
-  if (focused_group != FieldTypeGroup::kPhone) {
-    AddLabelPartIfNotEmpty(GetLabelPhone(profile, app_locale()), &label_parts);
-  }
-
-  if (focused_group != FieldTypeGroup::kAddress) {
-    AddLabelPartIfNotEmpty(
-        GetLabelAddress(form_has_street_address_, profile, app_locale(),
-                        field_types_for_labels()),
-        &label_parts);
-  }
-
-  return ConstructLabelLine(label_parts);
-}
-
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/address_phone_form_label_formatter.h b/components/autofill/core/browser/ui/address_phone_form_label_formatter.h
deleted file mode 100644
index 0a3ec700..0000000
--- a/components/autofill/core/browser/ui/address_phone_form_label_formatter.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_ADDRESS_PHONE_FORM_LABEL_FORMATTER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_ADDRESS_PHONE_FORM_LABEL_FORMATTER_H_
-
-#include <string>
-#include <vector>
-
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/ui/label_formatter.h"
-
-namespace autofill {
-
-// A LabelFormatter that creates Suggestions' disambiguating labels for forms
-// with name, address, and phone fields and without email fields.
-class AddressPhoneFormLabelFormatter : public LabelFormatter {
- public:
-  AddressPhoneFormLabelFormatter(
-      const std::vector<const AutofillProfile*>& profiles,
-      const std::string& app_locale,
-      FieldType focused_field_type,
-      uint32_t groups,
-      const FieldTypeSet& field_types);
-
-  ~AddressPhoneFormLabelFormatter() override;
-
-  std::u16string GetLabelForProfile(
-      const AutofillProfile& profile,
-      FieldTypeGroup focused_group) const override;
-
- private:
-  // Returns a label to show the user when |focused_field_type_| is a type
-  // other than a non-street-address field type. For example,
-  // |focused_field_type_| could be first name, address line 1, or phone number.
-  std::u16string GetLabelForProfileOnFocusedNamePhoneOrStreetAddress(
-      const AutofillProfile& profile,
-      FieldTypeGroup focused_group) const;
-
-  // True if this formatter's associated form has a street address field. A
-  // form may have an address-related field, e.g. zip code, without having a
-  // street address field. If a form does not include a street address field,
-  // street addresses should not appear in labels.
-  bool form_has_street_address_;
-};
-
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_ADDRESS_PHONE_FORM_LABEL_FORMATTER_H_
diff --git a/components/autofill/core/browser/ui/address_phone_form_label_formatter_unittest.cc b/components/autofill/core/browser/ui/address_phone_form_label_formatter_unittest.cc
deleted file mode 100644
index 4e685eed..0000000
--- a/components/autofill/core/browser/ui/address_phone_form_label_formatter_unittest.cc
+++ /dev/null
@@ -1,295 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/address_phone_form_label_formatter.h"
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/strings/utf_string_conversions.h"
-#include "base/uuid.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::ElementsAre;
-
-namespace autofill {
-namespace {
-
-FieldTypeSet GetFieldTypes() {
-  return {NO_SERVER_DATA,     NAME_FULL,          PHONE_HOME_WHOLE_NUMBER,
-          ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2, ADDRESS_HOME_CITY,
-          ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP,   ADDRESS_HOME_COUNTRY};
-}
-
-TEST(AddressPhoneFormLabelFormatterTest, GetLabelsWithMissingProfiles) {
-  const std::vector<const AutofillProfile*> profiles{};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", NAME_FULL, GetFieldTypes());
-  EXPECT_TRUE(formatter->GetLabels().empty());
-}
-
-TEST(AddressPhoneFormLabelFormatterTest, GetLabelsForUSProfilesAndFocusedName) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "", "",
-                       "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "", "", "", "", "", "",
-                       "", "US", "6175232338");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "John", "", "Adams", "", "", "", "", "", "",
-                       "", "US", "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2,
-                                                     &profile3, &profile4};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", NAME_FULL, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"(617) 730-2000", u"333 Washington St"}),
-                  u"151 Irving Ave", u"(617) 523-2338", std::u16string()));
-}
-
-TEST(AddressPhoneFormLabelFormatterTest,
-     GetLabelsForUSProfilesAndFocusedStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "", "",
-                       "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "", "", "", "", "", "", "", "", "", "", "US",
-                       "6175232338");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "", "", "", "", "", "141 Franklin St", "",
-                       "Quincy", "MA", "02169", "US", "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2,
-                                                     &profile3, &profile4};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_LINE1, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"John F Kennedy", u"(617) 730-2000"}),
-                  u"Jackie Kennedy", u"(617) 523-2338", std::u16string()));
-}
-
-TEST(AddressPhoneFormLabelFormatterTest,
-     GetLabelsForUSProfilesAndFocusedNonStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "", "",
-                       "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "", "", "", "", "", "", "", "", "", "", "US",
-                       "6175232338");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "", "", "", "", "", "", "", "Quincy", "MA",
-                       "02169", "US", "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2,
-                                                     &profile3, &profile4};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_CITY, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"333 Washington St", u"(617) 730-2000"}),
-                  u"151 Irving Ave", u"(617) 523-2338", std::u16string()));
-}
-
-TEST(AddressPhoneFormLabelFormatterTest,
-     GetLabelsForUSProfilesAndFocusedPhone) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "", "", "", "", "",
-                       "", "", "US", "");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "", "", "", "", "", "Paul Revere House",
-                       "19 North Square", "Boston", "MA", "02113", "US",
-                       "6175232338");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "", "", "", "", "", "", "", "", "", "", "US",
-                       "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2,
-                                                     &profile3, &profile4};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", PHONE_HOME_WHOLE_NUMBER, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"John F Kennedy", u"333 Washington St"}),
-                  u"Jackie Kennedy", u"Paul Revere House, 19 North Square",
-                  std::u16string()));
-}
-
-TEST(AddressPhoneFormLabelFormatterTest, GetLabelsForBRProfilesAndFocusedName) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", "SP", "04094-050", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "pt-BR", NAME_FULL, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine(
-                      {u"(11) 2648-0254", u"Av. Pedro Álvares Cabral, 1301"}),
-                  ConstructLabelLine(
-                      {u"(21) 98765-0000", u"Estr. Dona Castorina, 110"})));
-}
-
-TEST(AddressPhoneFormLabelFormatterTest,
-     GetLabelsForBRProfilesAndFocusedStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", "SP", "04094-050", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", ADDRESS_HOME_LINE1, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"Tarsila do Amaral", u"(11) 2648-0254"}),
-                  ConstructLabelLine({u"Artur Avila", u"(21) 98765-0000"})));
-}
-
-TEST(AddressPhoneFormLabelFormatterTest,
-     GetLabelsForBRProfilesAndFocusedNonStreetAddress) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", "SP", "04094-050", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", ADDRESS_HOME_ZIP, GetFieldTypes());
-
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(ConstructLabelLine({u"Av. Pedro Álvares Cabral, 1301",
-                                              u"(11) 2648-0254"}),
-                          ConstructLabelLine({u"Estr. Dona Castorina, 110",
-                                              u"(21) 98765-0000"})));
-}
-
-TEST(AddressPhoneFormLabelFormatterTest,
-     GetLabelsForBRProfilesAndFocusedPhone) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", "SP", "04094-050", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", PHONE_HOME_WHOLE_NUMBER, GetFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(
-          ConstructLabelLine(
-              {u"Tarsila do Amaral", u"Av. Pedro Álvares Cabral, 1301"}),
-          ConstructLabelLine({u"Artur Avila", u"Estr. Dona Castorina, 110"})));
-}
-
-TEST(AddressPhoneFormLabelFormatterTest,
-     GetLabelsForFormWithAddressFieldsMinusStreetAddress) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", PHONE_HOME_WHOLE_NUMBER,
-      {NAME_FULL, PHONE_HOME_WHOLE_NUMBER, ADDRESS_HOME_ZIP});
-
-  // Checks that only address fields in the form are shown in the label.
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(ConstructLabelLine({u"John F Kennedy", u"02445"})));
-}
-
-TEST(AddressPhoneFormLabelFormatterTest, GetLabelsForFormWithoutName) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Sarah", "", "Revere", "sarah.revere@aol.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "16175232338");
-
-  const std::vector<const AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_LINE1,
-      {ADDRESS_HOME_LINE1, ADDRESS_HOME_ZIP, PHONE_HOME_WHOLE_NUMBER});
-
-  // Checks that the name does not appear in the labels.
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"(617) 523-2338"));
-}
-
-}  // namespace
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/autofill_image_fetcher.cc b/components/autofill/core/browser/ui/autofill_image_fetcher.cc
index ebf9fea..b20aeb8f 100644
--- a/components/autofill/core/browser/ui/autofill_image_fetcher.cc
+++ b/components/autofill/core/browser/ui/autofill_image_fetcher.cc
@@ -102,7 +102,7 @@
     base::OnceCallback<void(std::unique_ptr<CreditCardArtImage>)>
         barrier_callback,
     const GURL& card_art_url,
-    const absl::optional<base::TimeTicks>& fetch_image_request_timestamp,
+    const std::optional<base::TimeTicks>& fetch_image_request_timestamp,
     const gfx::Image& card_art_image,
     const image_fetcher::RequestMetadata& metadata) {
   CHECK(fetch_image_request_timestamp.has_value());
diff --git a/components/autofill/core/browser/ui/autofill_image_fetcher.h b/components/autofill/core/browser/ui/autofill_image_fetcher.h
index ec6d795..ad78d70 100644
--- a/components/autofill/core/browser/ui/autofill_image_fetcher.h
+++ b/components/autofill/core/browser/ui/autofill_image_fetcher.h
@@ -8,11 +8,11 @@
 #include "components/autofill/core/browser/ui/autofill_image_fetcher_base.h"
 
 #include <memory>
+#include <optional>
 
 #include "base/barrier_callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class GURL;
 
@@ -73,7 +73,7 @@
       base::OnceCallback<void(std::unique_ptr<CreditCardArtImage>)>
           barrier_callback,
       const GURL& card_art_url,
-      const absl::optional<base::TimeTicks>& fetch_image_request_timestamp,
+      const std::optional<base::TimeTicks>& fetch_image_request_timestamp,
       const gfx::Image& card_art_image,
       const image_fetcher::RequestMetadata& metadata);
 
diff --git a/components/autofill/core/browser/ui/autofill_image_fetcher_unittest.cc b/components/autofill/core/browser/ui/autofill_image_fetcher_unittest.cc
index dbcef9a..6d85ee3 100644
--- a/components/autofill/core/browser/ui/autofill_image_fetcher_unittest.cc
+++ b/components/autofill/core/browser/ui/autofill_image_fetcher_unittest.cc
@@ -50,7 +50,7 @@
       base::OnceCallback<void(std::unique_ptr<CreditCardArtImage>)>
           barrier_callback,
       const GURL& url,
-      const absl::optional<base::TimeTicks>& fetch_image_request_timestamp,
+      const std::optional<base::TimeTicks>& fetch_image_request_timestamp,
       const gfx::Image& image) {
     OnCardArtImageFetched(std::move(barrier_callback), url,
                           fetch_image_request_timestamp, image,
diff --git a/components/autofill/core/browser/ui/contact_form_label_formatter.cc b/components/autofill/core/browser/ui/contact_form_label_formatter.cc
deleted file mode 100644
index 9ffaf1e..0000000
--- a/components/autofill/core/browser/ui/contact_form_label_formatter.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/contact_form_label_formatter.h"
-
-#include "components/autofill/core/browser/autofill_data_util.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-
-namespace autofill {
-
-ContactFormLabelFormatter::ContactFormLabelFormatter(
-    const std::vector<const AutofillProfile*>& profiles,
-    const std::string& app_locale,
-    FieldType focused_field_type,
-    uint32_t groups,
-    const FieldTypeSet& field_types)
-    : LabelFormatter(profiles,
-                     app_locale,
-                     focused_field_type,
-                     groups,
-                     field_types) {}
-
-ContactFormLabelFormatter::~ContactFormLabelFormatter() = default;
-
-// Note that the order--name, phone, and email--in which parts of the label
-// are possibly added ensures that the label is formatted correctly for
-// |focused_group| and for this kind of formatter.
-std::u16string ContactFormLabelFormatter::GetLabelForProfile(
-    const AutofillProfile& profile,
-    FieldTypeGroup focused_group) const {
-  std::vector<std::u16string> label_parts;
-  if (focused_group != FieldTypeGroup::kName &&
-      data_util::ContainsName(groups())) {
-    AddLabelPartIfNotEmpty(
-        GetLabelName(field_types_for_labels(), profile, app_locale()),
-        &label_parts);
-  }
-
-  if (focused_group != FieldTypeGroup::kPhone) {
-    AddLabelPartIfNotEmpty(MaybeGetPhone(profile), &label_parts);
-  }
-
-  if (focused_group != FieldTypeGroup::kEmail) {
-    AddLabelPartIfNotEmpty(MaybeGetEmail(profile), &label_parts);
-  }
-
-  return ConstructLabelLine(label_parts);
-}
-
-std::u16string ContactFormLabelFormatter::MaybeGetEmail(
-    const AutofillProfile& profile) const {
-  return data_util::ContainsEmail(groups())
-             ? GetLabelEmail(profile, app_locale())
-             : std::u16string();
-}
-
-std::u16string ContactFormLabelFormatter::MaybeGetPhone(
-    const AutofillProfile& profile) const {
-  return data_util::ContainsPhone(groups())
-             ? GetLabelPhone(profile, app_locale())
-             : std::u16string();
-}
-
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/contact_form_label_formatter.h b/components/autofill/core/browser/ui/contact_form_label_formatter.h
deleted file mode 100644
index 0967ca65..0000000
--- a/components/autofill/core/browser/ui/contact_form_label_formatter.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_CONTACT_FORM_LABEL_FORMATTER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_CONTACT_FORM_LABEL_FORMATTER_H_
-
-#include <string>
-#include <vector>
-
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/ui/label_formatter.h"
-
-namespace autofill {
-
-// A LabelFormatter that creates Suggestions' disambiguating labels for forms
-// containing name and phone or email fields.
-class ContactFormLabelFormatter : public LabelFormatter {
- public:
-  ContactFormLabelFormatter(const std::vector<const AutofillProfile*>& profiles,
-                            const std::string& app_locale,
-                            FieldType focused_field_type,
-                            uint32_t groups,
-                            const FieldTypeSet& field_types);
-
-  ~ContactFormLabelFormatter() override;
-
-  std::u16string GetLabelForProfile(
-      const AutofillProfile& profile,
-      FieldTypeGroup focused_group) const override;
-
- private:
-  // Returns |profile|'s email address if |profile| has a valid email address
-  // and if this formatter's associated form has an email field.
-  std::u16string MaybeGetEmail(const AutofillProfile& profile) const;
-
-  // Returns |profile|'s phone number if |profile| has a phone number and if
-  // this formatter's associated form has a phone field.
-  std::u16string MaybeGetPhone(const AutofillProfile& profile) const;
-};
-
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_CONTACT_FORM_LABEL_FORMATTER_H_
diff --git a/components/autofill/core/browser/ui/contact_form_label_formatter_unittest.cc b/components/autofill/core/browser/ui/contact_form_label_formatter_unittest.cc
deleted file mode 100644
index 1d7b5eb..0000000
--- a/components/autofill/core/browser/ui/contact_form_label_formatter_unittest.cc
+++ /dev/null
@@ -1,281 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/contact_form_label_formatter.h"
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/strings/utf_string_conversions.h"
-#include "base/uuid.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::ElementsAre;
-
-namespace autofill {
-namespace {
-
-FieldTypeSet GetNamePhoneAndEmailFieldTypes() {
-  return {NAME_FIRST, NAME_LAST, PHONE_HOME_WHOLE_NUMBER, EMAIL_ADDRESS};
-}
-
-TEST(ContactFormLabelFormatterTest, GetLabelsWithMissingProfiles) {
-  const std::vector<const AutofillProfile*> profiles{};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", NAME_FIRST, GetNamePhoneAndEmailFieldTypes());
-  EXPECT_TRUE(formatter->GetLabels().empty());
-}
-
-TEST(ContactFormLabelFormatterTest, GetLabelsForUSProfilesAndFocusedName) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "jackie@outlook.com",
-                       "", "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "", "", "19 N Square",
-                       "", "Boston", "MA", "02113", "US", "+1 (617) 523-2338");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "John", "", "Adams", "", "",
-                       "141 Franklin St.", "", "Quincy", "MA", "02169", "US",
-                       "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2,
-                                                     &profile3, &profile4};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", NAME_LAST, GetNamePhoneAndEmailFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"(617) 730-2000", u"jfk@gmail.com"}),
-                  u"jackie@outlook.com", u"(617) 523-2338", std::u16string()));
-}
-
-TEST(ContactFormLabelFormatterTest, GetLabelsForUSProfilesAndFocusedEmail) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "jackie@outlook.com",
-                       "", "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "", "", "19 N Square",
-                       "", "Boston", "MA", "02113", "US", "+1 (617) 523-2338");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "", "", "", "", "", "141 Franklin St.", "",
-                       "Quincy", "MA", "02169", "US", "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2,
-                                                     &profile3, &profile4};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", EMAIL_ADDRESS, GetNamePhoneAndEmailFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"John Kennedy", u"(617) 730-2000"}),
-                  u"Jackie Kennedy",
-                  ConstructLabelLine({u"Paul Revere", u"(617) 523-2338"}),
-                  std::u16string()));
-}
-
-TEST(ContactFormLabelFormatterTest, GetLabelsForUSProfilesAndFocusedPhone) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Jackie", "", "Kennedy", "jackie@outlook.com",
-                       "", "151 Irving Ave", "", "Hyannis", "MA", "02601", "US",
-                       "");
-
-  AutofillProfile profile3(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile3, "Paul", "", "Revere", "", "", "19 N Square",
-                       "", "Boston", "MA", "02113", "US", "+1 (617) 523-2338");
-
-  AutofillProfile profile4(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile4, "", "", "", "", "", "141 Franklin St.", "",
-                       "Quincy", "MA", "02169", "US", "");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2,
-                                                     &profile3, &profile4};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", PHONE_HOME_WHOLE_NUMBER,
-                             GetNamePhoneAndEmailFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(
-          ConstructLabelLine({u"John Kennedy", u"jfk@gmail.com"}),
-          ConstructLabelLine({u"Jackie Kennedy", u"jackie@outlook.com"}),
-          u"Paul Revere", std::u16string()));
-}
-
-TEST(ContactFormLabelFormatterTest, GetLabelsForBRProfilesAndFocusedName) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", "SP", "04094-050", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", NAME_LAST, GetNamePhoneAndEmailFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(
-          ConstructLabelLine({u"(11) 2648-0254", u"tarsila@aol.com"}),
-          ConstructLabelLine({u"(21) 98765-0000", u"aavila@uol.com.br"})));
-}
-
-TEST(ContactFormLabelFormatterTest, GetLabelsForBRProfilesAndFocusedEmail) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", "SP", "04094-050", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", EMAIL_ADDRESS, GetNamePhoneAndEmailFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"Tarsila Amaral", u"(11) 2648-0254"}),
-                  ConstructLabelLine({u"Artur Avila", u"(21) 98765-0000"})));
-}
-
-TEST(ContactFormLabelFormatterTest, GetLabelsForBRProfilesAndFocusedPhone) {
-  AutofillProfile profile1(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile1, "Tarsila", "do", "Amaral", "tarsila@aol.com",
-                       "", "Av. Pedro Álvares Cabral, 1301", "", "Vila Mariana",
-                       "São Paulo", "SP", "04094-050", "BR",
-                       "+55 11 2648-0254");
-
-  AutofillProfile profile2(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile2, "Artur", "", "Avila", "aavila@uol.com.br", "",
-                       "Estr. Dona Castorina, 110", "", "Jardim Botânico",
-                       "Rio de Janeiro", "RJ", "22460-320", "BR",
-                       "21987650000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "pt-BR", PHONE_HOME_WHOLE_NUMBER,
-                             GetNamePhoneAndEmailFieldTypes());
-
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(ConstructLabelLine({u"Tarsila Amaral", u"tarsila@aol.com"}),
-                  ConstructLabelLine({u"Artur Avila", u"aavila@uol.com.br"})));
-}
-
-TEST(ContactFormLabelFormatterTest, GetLabelsForNameAndPhoneWithFocusedName) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", NAME_LAST,
-                             {NAME_FIRST, NAME_LAST, PHONE_HOME_WHOLE_NUMBER});
-
-  // Checks that the email address is excluded when the form does not contain an
-  // email field.
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"(617) 730-2000"));
-}
-
-TEST(ContactFormLabelFormatterTest, GetLabelsForNameAndPhoneWithFocusedPhone) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", PHONE_HOME_WHOLE_NUMBER,
-                             {NAME_FIRST, NAME_LAST, PHONE_HOME_WHOLE_NUMBER});
-
-  // Checks that the email address is excluded when the form does not contain an
-  // email field.
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"John Kennedy"));
-}
-
-TEST(ContactFormLabelFormatterTest, GetLabelsForNameAndEmailWithFocusedName) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", NAME_LAST, {NAME_FIRST, NAME_LAST, EMAIL_ADDRESS});
-
-  // Checks that the phone number is excluded when the form does not contain a
-  // phone field.
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"jfk@gmail.com"));
-}
-
-TEST(ContactFormLabelFormatterTest, GetLabelsForNameAndEmailWithFocusedEmail) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "John", "F", "Kennedy", "jfk@gmail.com", "",
-                       "333 Washington St", "", "Brookline", "MA", "02445",
-                       "US", "16177302000");
-
-  const std::vector<const AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", EMAIL_ADDRESS, {NAME_FIRST, NAME_LAST, EMAIL_ADDRESS});
-
-  // Checks that the phone number is excluded when the form does not contain a
-  // phone field.
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"John Kennedy"));
-}
-
-TEST(ContactFormLabelFormatterTest, GetLabelsForFormWithoutName) {
-  AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode);
-  test::SetProfileInfo(&profile, "Sarah", "", "Revere", "sarah.revere@aol.com",
-                       "", "19 North Sq", "", "Boston", "MA", "02113", "US",
-                       "16175232338");
-
-  const std::vector<const AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", PHONE_HOME_COUNTRY_CODE,
-      {EMAIL_ADDRESS, PHONE_HOME_COUNTRY_CODE, PHONE_HOME_CITY_AND_NUMBER});
-
-  // Checks that the name does not appear in the labels.
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"sarah.revere@aol.com"));
-}
-
-}  // namespace
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/label_formatter.cc b/components/autofill/core/browser/ui/label_formatter.cc
deleted file mode 100644
index eca72c00..0000000
--- a/components/autofill/core/browser/ui/label_formatter.cc
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/label_formatter.h"
-
-#include <iterator>
-#include <set>
-
-#include "base/ranges/algorithm.h"
-#include "build/build_config.h"
-#include "components/autofill/core/browser/autofill_data_util.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/metrics/autofill_metrics.h"
-#include "components/autofill/core/browser/ui/address_contact_form_label_formatter.h"
-#include "components/autofill/core/browser/ui/address_email_form_label_formatter.h"
-#include "components/autofill/core/browser/ui/address_form_label_formatter.h"
-#include "components/autofill/core/browser/ui/address_phone_form_label_formatter.h"
-#include "components/autofill/core/browser/ui/contact_form_label_formatter.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-#include "components/autofill/core/browser/ui/mobile_label_formatter.h"
-#include "components/autofill/core/common/dense_set.h"
-
-namespace autofill {
-
-using data_util::ContainsAddress;
-using data_util::ContainsEmail;
-using data_util::ContainsName;
-using data_util::ContainsPhone;
-using data_util::bit_field_type_groups::kAddress;
-using data_util::bit_field_type_groups::kEmail;
-using data_util::bit_field_type_groups::kName;
-using data_util::bit_field_type_groups::kPhone;
-
-LabelFormatter::LabelFormatter(
-    const std::vector<const AutofillProfile*>& profiles,
-    const std::string& app_locale,
-    FieldType focused_field_type,
-    uint32_t groups,
-    const FieldTypeSet& field_types)
-    : profiles_(profiles),
-      app_locale_(app_locale),
-      focused_field_type_(focused_field_type),
-      groups_(groups) {
-  const FieldTypeGroup focused_group =
-      GroupTypeOfServerFieldType(focused_field_type);
-  DenseSet<FieldTypeGroup> groups_for_labels{
-      FieldTypeGroup::kName, FieldTypeGroup::kAddress, FieldTypeGroup::kEmail,
-      FieldTypeGroup::kPhone};
-
-  // If a user is focused on an address field, then parts of the address may be
-  // shown in the label. For example, if the user is focusing on a street
-  // address field, then it may be helpful to show the city in the label.
-  // Otherwise, the focused field should not appear in the label.
-  if (focused_group != FieldTypeGroup::kAddress) {
-    groups_for_labels.erase(focused_group);
-  }
-
-  // Countries are excluded to prevent them from appearing in labels with
-  // national addresses.
-  auto can_be_shown_in_label =
-      [&groups_for_labels](FieldType field_type) -> bool {
-    return groups_for_labels.find(GroupTypeOfServerFieldType(field_type)) !=
-               groups_for_labels.end() &&
-           field_type != ADDRESS_HOME_COUNTRY;
-  };
-
-  base::ranges::copy_if(field_types,
-                        std::back_inserter(field_types_for_labels_),
-                        can_be_shown_in_label);
-}
-
-LabelFormatter::~LabelFormatter() = default;
-
-std::vector<std::u16string> LabelFormatter::GetLabels() const {
-  std::vector<std::u16string> labels;
-  for (const AutofillProfile* profile : *profiles_) {
-    labels.push_back(GetLabelForProfile(
-        *profile, GroupTypeOfServerFieldType(focused_field_type_)));
-  }
-  return labels;
-}
-
-// static
-std::unique_ptr<LabelFormatter> LabelFormatter::Create(
-    const std::vector<const AutofillProfile*>& profiles,
-    const std::string& app_locale,
-    FieldType focused_field_type,
-    const FieldTypeSet& field_types) {
-  const uint32_t groups = data_util::DetermineGroups(field_types);
-  if (!data_util::IsSupportedFormType(groups)) {
-    return nullptr;
-  }
-
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-  return std::make_unique<MobileLabelFormatter>(
-      profiles, app_locale, focused_field_type, groups, field_types);
-#else
-  switch (groups) {
-    case kAddress | kEmail | kPhone:
-    case kName | kAddress | kEmail | kPhone:
-      return std::make_unique<AddressContactFormLabelFormatter>(
-          profiles, app_locale, focused_field_type, groups, field_types);
-    case kAddress | kPhone:
-    case kName | kAddress | kPhone:
-      return std::make_unique<AddressPhoneFormLabelFormatter>(
-          profiles, app_locale, focused_field_type, groups, field_types);
-    case kAddress | kEmail:
-    case kName | kAddress | kEmail:
-      return std::make_unique<AddressEmailFormLabelFormatter>(
-          profiles, app_locale, focused_field_type, groups, field_types);
-    case kAddress:
-    case kName | kAddress:
-      return std::make_unique<AddressFormLabelFormatter>(
-          profiles, app_locale, focused_field_type, groups, field_types);
-    case kEmail | kPhone:
-    case kName | kEmail | kPhone:
-    case kName | kEmail:
-    case kName | kPhone:
-      return std::make_unique<ContactFormLabelFormatter>(
-          profiles, app_locale, focused_field_type, groups, field_types);
-    default:
-      return nullptr;
-  }
-#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-}
-
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/label_formatter.h b/components/autofill/core/browser/ui/label_formatter.h
deleted file mode 100644
index 06bc0077..0000000
--- a/components/autofill/core/browser/ui/label_formatter.h
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_LABEL_FORMATTER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_LABEL_FORMATTER_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/memory/raw_ref.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-
-namespace autofill {
-
-// Handles the creation of Suggestions' disambiguating labels.
-class LabelFormatter {
- public:
-  LabelFormatter(const std::vector<const AutofillProfile*>& profiles,
-                 const std::string& app_locale,
-                 FieldType focused_field_type,
-                 uint32_t groups,
-                 const FieldTypeSet& field_types);
-  virtual ~LabelFormatter();
-
-  // Returns the bitmask indicating which FieldTypeGroups are represented in
-  // this formatter's associated form.
-  uint32_t groups() const { return groups_; }
-
-  // Returns a collection of labels formed by extracting useful disambiguating
-  // information from |profiles_|.
-  std::vector<std::u16string> GetLabels() const;
-
-  // Creates a form-specific LabelFormatter according to |field_types|. This
-  // formatter has the ability to build labels with disambiguating information
-  // from the given |profiles|.
-  static std::unique_ptr<LabelFormatter> Create(
-      const std::vector<const AutofillProfile*>& profiles,
-      const std::string& app_locale,
-      FieldType focused_field_type,
-      const FieldTypeSet& field_types);
-
- protected:
-  // Returns a label to show the user. The elements of the label and their
-  // ordering depend on the kind of LabelFormatter, the data in |profile|,
-  // |focused_group|, and |focused_field_type_|.
-  virtual std::u16string GetLabelForProfile(
-      const AutofillProfile& profile,
-      FieldTypeGroup focused_group) const = 0;
-
-  const std::string& app_locale() const { return app_locale_; }
-
-  FieldType focused_field_type() const { return focused_field_type_; }
-
-  const std::vector<FieldType>& field_types_for_labels() const {
-    return field_types_for_labels_;
-  }
-
- private:
-  // The collection of profiles for which to build labels. Storing this
-  // collection ensures that the profiles for which this formatter is created
-  // are the profiles for which the labels are constructed.
-  //
-  // It is safe to store a reference here because the LabelFormatter is
-  // destroyed when the suggestions for which the labels are constructed are
-  // returned.
-  const raw_ref<const std::vector<const AutofillProfile*>> profiles_;
-
-  // The locale for which to generate labels. This reflects the language and
-  // country for which the application is translated, e.g. en-AU for Australian
-  // English.
-  std::string app_locale_;
-
-  // The type of field on which the user is focused, e.g. NAME_FIRST.
-  FieldType focused_field_type_;
-
-  // The bitmask indicating which FieldTypeGroups are represented in this
-  // formatter's associated form.
-  uint32_t groups_;
-
-  // The collection of field types that can be used to make labels. It includes
-  // only types related to names, addresses, email addresses, and phone
-  // numbers. It excludes types related to countries.
-  std::vector<FieldType> field_types_for_labels_;
-};
-
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_LABEL_FORMATTER_H_
diff --git a/components/autofill/core/browser/ui/label_formatter_unittest.cc b/components/autofill/core/browser/ui/label_formatter_unittest.cc
deleted file mode 100644
index 28c9e3a..0000000
--- a/components/autofill/core/browser/ui/label_formatter_unittest.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/label_formatter.h"
-
-#include <vector>
-
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace autofill {
-namespace {
-
-TEST(LabelFormatterTest, CreateWithMissingFieldTypes) {
-  const std::vector<const AutofillProfile*> profiles{};
-  EXPECT_EQ(LabelFormatter::Create(profiles, "en-US", NAME_FIRST, {}), nullptr);
-}
-
-TEST(LabelFormatterTest, CreateWithUnsupportedFieldTypes) {
-  const std::vector<const AutofillProfile*> profiles{};
-  EXPECT_EQ(
-      LabelFormatter::Create(profiles, "en-US", USERNAME, {USERNAME, PASSWORD}),
-      nullptr);
-}
-
-}  // namespace
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/label_formatter_utils.cc b/components/autofill/core/browser/ui/label_formatter_utils.cc
deleted file mode 100644
index c5277db..0000000
--- a/components/autofill/core/browser/ui/label_formatter_utils.cc
+++ /dev/null
@@ -1,398 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-
-#include <iterator>
-#include <memory>
-
-#include "base/functional/bind.h"
-#include "base/functional/callback.h"
-#include "base/ranges/algorithm.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "components/autofill/core/browser/autofill_data_util.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/geo/address_i18n.h"
-#include "components/autofill/core/browser/geo/phone_number_i18n.h"
-#include "components/autofill/core/browser/validation.h"
-#include "components/grit/components_scaled_resources.h"
-#include "components/strings/grit/components_strings.h"
-#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
-#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_formatter.h"
-#include "ui/base/l10n/l10n_util.h"
-
-namespace autofill {
-
-using data_util::ContainsAddress;
-using data_util::ContainsEmail;
-using data_util::ContainsName;
-using data_util::ContainsPhone;
-
-namespace {
-
-// Returns true if all |profiles| have the same value for the data retrieved by
-// |get_data|.
-bool HaveSameData(
-    const std::vector<const AutofillProfile*>& profiles,
-    const std::string& app_locale,
-    base::RepeatingCallback<std::u16string(const AutofillProfile&,
-                                           const std::string&)> get_data,
-    base::RepeatingCallback<bool(const std::u16string& str1,
-                                 const std::u16string& str2)> matches) {
-  if (profiles.size() <= 1) {
-    return true;
-  }
-
-  const std::u16string first_profile_data =
-      get_data.Run(*profiles[0], app_locale);
-  for (size_t i = 1; i < profiles.size(); ++i) {
-    const std::u16string current_profile_data =
-        get_data.Run(*profiles[i], app_locale);
-    if (!matches.Run(first_profile_data, current_profile_data)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-// Used to avoid having the same lambda in HaveSameEmailAddresses,
-// HaveSameFirstNames, HaveSameStreetAddresses.
-bool Equals(const std::u16string& str1, const std::u16string& str2) {
-  return str1 == str2;
-}
-
-}  // namespace
-
-void AddLabelPartIfNotEmpty(const std::u16string& part,
-                            std::vector<std::u16string>* parts) {
-  if (!part.empty()) {
-    parts->push_back(part);
-  }
-}
-
-std::u16string ConstructLabelLine(const std::vector<std::u16string>& parts) {
-  return base::JoinString(parts, l10n_util::GetStringUTF16(
-                                     IDS_AUTOFILL_SUGGESTION_LABEL_SEPARATOR));
-}
-
-std::u16string ConstructMobileLabelLine(
-    const std::vector<std::u16string>& parts) {
-  return base::JoinString(
-      parts, l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR));
-}
-
-bool IsNonStreetAddressPart(FieldType type) {
-  switch (type) {
-    case ADDRESS_HOME_CITY:
-    case ADDRESS_HOME_ZIP:
-    case ADDRESS_HOME_STATE:
-    case ADDRESS_HOME_COUNTRY:
-    case ADDRESS_HOME_SORTING_CODE:
-    case ADDRESS_HOME_DEPENDENT_LOCALITY:
-      return true;
-    default:
-      return false;
-  }
-}
-
-bool IsStreetAddressPart(FieldType type) {
-  switch (type) {
-    case ADDRESS_HOME_LINE1:
-    case ADDRESS_HOME_LINE2:
-    case ADDRESS_HOME_APT_NUM:
-    case ADDRESS_HOME_STREET_ADDRESS:
-    case ADDRESS_HOME_LINE3:
-      return true;
-    default:
-      return false;
-  }
-}
-
-bool HasNonStreetAddress(const std::vector<FieldType>& types) {
-  return base::ranges::any_of(types, IsNonStreetAddressPart);
-}
-
-bool HasStreetAddress(const std::vector<FieldType>& types) {
-  return base::ranges::any_of(types, IsStreetAddressPart);
-}
-
-std::vector<FieldType> ExtractSpecifiedAddressFieldTypes(
-    bool extract_street_address_types,
-    const std::vector<FieldType>& types) {
-  auto should_be_extracted =
-      [&extract_street_address_types](FieldType type) -> bool {
-    return GroupTypeOfServerFieldType(type) == FieldTypeGroup::kAddress &&
-           (extract_street_address_types ? IsStreetAddressPart(type)
-                                         : !IsStreetAddressPart(type));
-  };
-
-  std::vector<FieldType> extracted_address_types;
-  base::ranges::copy_if(types, std::back_inserter(extracted_address_types),
-                        should_be_extracted);
-
-  return extracted_address_types;
-}
-
-std::vector<FieldType> TypesWithoutFocusedField(
-    const std::vector<FieldType>& types,
-    FieldType field_type_to_remove) {
-  std::vector<FieldType> types_without_field;
-  base::ranges::copy_if(types, std::back_inserter(types_without_field),
-                        [&field_type_to_remove](FieldType type) {
-                          return type != field_type_to_remove;
-                        });
-  return types_without_field;
-}
-
-AutofillProfile MakeTrimmedProfile(const AutofillProfile& profile,
-                                   const std::string& app_locale,
-                                   const std::vector<FieldType>& types) {
-  const std::u16string country_code = profile.GetRawInfo(ADDRESS_HOME_COUNTRY);
-
-  AutofillProfile trimmed_profile(
-      profile.guid(), AutofillProfile::Source::kLocalOrSyncable,
-      AddressCountryCode(base::UTF16ToUTF8(country_code)));
-  trimmed_profile.set_language_code(profile.language_code());
-
-  for (const FieldType& type : types) {
-    trimmed_profile.SetInfo(type, profile.GetInfo(type, app_locale),
-                            app_locale);
-  }
-  return trimmed_profile;
-}
-
-std::u16string GetLabelForFocusedAddress(FieldType focused_field_type,
-                                         bool form_has_street_address,
-                                         const AutofillProfile& profile,
-                                         const std::string& app_locale,
-                                         const std::vector<FieldType>& types) {
-  return GetLabelAddress(
-      form_has_street_address && !IsStreetAddressPart(focused_field_type),
-      profile, app_locale, types);
-}
-
-std::u16string GetLabelAddress(bool use_street_address,
-                               const AutofillProfile& profile,
-                               const std::string& app_locale,
-                               const std::vector<FieldType>& types) {
-  return use_street_address
-             ? GetLabelStreetAddress(
-                   ExtractSpecifiedAddressFieldTypes(use_street_address, types),
-                   profile, app_locale)
-             : GetLabelNationalAddress(
-                   ExtractSpecifiedAddressFieldTypes(use_street_address, types),
-                   profile, app_locale);
-}
-
-std::u16string GetLabelNationalAddress(const std::vector<FieldType>& types,
-                                       const AutofillProfile& profile,
-                                       const std::string& app_locale) {
-  std::unique_ptr<::i18n::addressinput::AddressData> address_data =
-      i18n::CreateAddressDataFromAutofillProfile(
-          MakeTrimmedProfile(profile, app_locale, types), app_locale);
-
-  std::string address_line;
-  ::i18n::addressinput::GetFormattedNationalAddressLine(*address_data,
-                                                        &address_line);
-  return base::UTF8ToUTF16(address_line);
-}
-
-std::u16string GetLabelStreetAddress(const std::vector<FieldType>& types,
-                                     const AutofillProfile& profile,
-                                     const std::string& app_locale) {
-  std::unique_ptr<::i18n::addressinput::AddressData> address_data =
-      i18n::CreateAddressDataFromAutofillProfile(
-          MakeTrimmedProfile(profile, app_locale, types), app_locale);
-
-  std::string address_line;
-  ::i18n::addressinput::GetStreetAddressLinesAsSingleLine(*address_data,
-                                                          &address_line);
-  return base::UTF8ToUTF16(address_line);
-}
-
-std::u16string GetLabelForProfileOnFocusedNonStreetAddress(
-    bool form_has_street_address,
-    const AutofillProfile& profile,
-    const std::string& app_locale,
-    const std::vector<FieldType>& types,
-    const std::u16string& contact_info) {
-  std::vector<std::u16string> label_parts;
-  AddLabelPartIfNotEmpty(
-      GetLabelAddress(form_has_street_address, profile, app_locale, types),
-      &label_parts);
-  AddLabelPartIfNotEmpty(contact_info, &label_parts);
-  return ConstructLabelLine(label_parts);
-}
-
-std::u16string GetLabelName(const std::vector<FieldType>& types,
-                            const AutofillProfile& profile,
-                            const std::string& app_locale) {
-  bool has_first_name = false;
-  bool has_last_name = false;
-  bool has_full_name = false;
-
-  for (const FieldType type : types) {
-    if (type == NAME_FULL) {
-      has_full_name = true;
-      break;
-    }
-    if (type == NAME_FIRST) {
-      has_first_name = true;
-    }
-    if (type == NAME_LAST) {
-      has_last_name = true;
-    }
-  }
-
-  if (has_full_name) {
-    return profile.GetInfo(AutofillType(NAME_FULL), app_locale);
-  }
-
-  if (has_first_name && has_last_name) {
-    std::vector<std::u16string> name_parts;
-    AddLabelPartIfNotEmpty(GetLabelFirstName(profile, app_locale), &name_parts);
-    AddLabelPartIfNotEmpty(profile.GetInfo(AutofillType(NAME_LAST), app_locale),
-                           &name_parts);
-    return base::JoinString(name_parts, u" ");
-  }
-
-  if (has_first_name) {
-    return GetLabelFirstName(profile, app_locale);
-  }
-
-  if (has_last_name) {
-    return profile.GetInfo(AutofillType(NAME_LAST), app_locale);
-  }
-
-  // The form contains neither a full name field nor a first name field,
-  // so choose some name field in the form and make it the label text.
-  for (const FieldType type : types) {
-    if (GroupTypeOfServerFieldType(type) == FieldTypeGroup::kName) {
-      return profile.GetInfo(AutofillType(type), app_locale);
-    }
-  }
-  return std::u16string();
-}
-
-std::u16string GetLabelFirstName(const AutofillProfile& profile,
-                                 const std::string& app_locale) {
-  return profile.GetInfo(AutofillType(NAME_FIRST), app_locale);
-}
-
-std::u16string GetLabelEmail(const AutofillProfile& profile,
-                             const std::string& app_locale) {
-  const std::u16string email =
-      profile.GetInfo(AutofillType(EMAIL_ADDRESS), app_locale);
-  return IsValidEmailAddress(email) ? email : std::u16string();
-}
-
-std::u16string GetLabelPhone(const AutofillProfile& profile,
-                             const std::string& app_locale) {
-  const std::string unformatted_phone = base::UTF16ToUTF8(
-      profile.GetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER), app_locale));
-  return unformatted_phone.empty()
-             ? std::u16string()
-             : base::UTF8ToUTF16(i18n::FormatPhoneNationallyForDisplay(
-                   unformatted_phone,
-                   data_util::GetCountryCodeWithFallback(profile, app_locale)));
-}
-
-bool HaveSameEmailAddresses(const std::vector<const AutofillProfile*>& profiles,
-                            const std::string& app_locale) {
-  return HaveSameData(profiles, app_locale, base::BindRepeating(&GetLabelEmail),
-                      base::BindRepeating(base::BindRepeating(&Equals)));
-}
-
-bool HaveSameFirstNames(const std::vector<const AutofillProfile*>& profiles,
-                        const std::string& app_locale) {
-  return HaveSameData(profiles, app_locale,
-                      base::BindRepeating(&GetLabelFirstName),
-                      base::BindRepeating(base::BindRepeating(&Equals)));
-}
-
-bool HaveSameNonStreetAddresses(
-    const std::vector<const AutofillProfile*>& profiles,
-    const std::string& app_locale,
-    const std::vector<FieldType>& types) {
-  // In general, comparing non street addresses with Equals, which uses ==, is
-  // not ideal since Düsseldorf and Dusseldorf will be considered distinct. It's
-  // okay to use it here because near-duplicate non street addresses like this
-  // are filtered out before a LabelFormatter is created.
-  return HaveSameData(profiles, app_locale,
-                      base::BindRepeating(&GetLabelNationalAddress, types),
-                      base::BindRepeating(&Equals));
-}
-
-bool HaveSamePhoneNumbers(const std::vector<const AutofillProfile*>& profiles,
-                          const std::string& app_locale) {
-  // Note that the same country code is used in all comparisons.
-  auto equals = [](const std::string& country_code,
-                   const std::string& app_locale, const std::u16string& phone1,
-                   const std::u16string& phone2) -> bool {
-    return (phone1.empty() && phone2.empty()) ||
-           i18n::PhoneNumbersMatch(phone1, phone2, country_code, app_locale);
-  };
-
-  return profiles.size() <= 1
-             ? true
-             : HaveSameData(
-                   profiles, app_locale, base::BindRepeating(&GetLabelPhone),
-                   base::BindRepeating(equals,
-                                       base::UTF16ToASCII(profiles[0]->GetInfo(
-                                           ADDRESS_HOME_COUNTRY, app_locale)),
-                                       app_locale));
-}
-
-bool HaveSameStreetAddresses(
-    const std::vector<const AutofillProfile*>& profiles,
-    const std::string& app_locale,
-    const std::vector<FieldType>& types) {
-  // In general, comparing street addresses with Equals, which uses ==, is not
-  // ideal since 3 Elm St and 3 Elm St. will be considered distinct. It's okay
-  // to use it here because near-duplicate addresses like this are filtered
-  // out before a LabelFormatter is created.
-  return HaveSameData(profiles, app_locale,
-                      base::BindRepeating(&GetLabelStreetAddress, types),
-                      base::BindRepeating(&Equals));
-}
-
-bool HasUnfocusedEmailField(FieldTypeGroup focused_group,
-                            uint32_t form_groups) {
-  return ContainsEmail(form_groups) && focused_group != FieldTypeGroup::kEmail;
-}
-
-bool HasUnfocusedNameField(FieldTypeGroup focused_group, uint32_t form_groups) {
-  return ContainsName(form_groups) && focused_group != FieldTypeGroup::kName;
-}
-
-bool HasUnfocusedNonStreetAddressField(FieldType focused_field,
-                                       FieldTypeGroup focused_group,
-                                       const std::vector<FieldType>& types) {
-  return HasNonStreetAddress(types) &&
-         (focused_group != FieldTypeGroup::kAddress ||
-          !IsNonStreetAddressPart(focused_field));
-}
-
-bool HasUnfocusedPhoneField(FieldTypeGroup focused_group,
-                            uint32_t form_groups) {
-  return ContainsPhone(form_groups) && focused_group != FieldTypeGroup::kPhone;
-}
-
-bool HasUnfocusedStreetAddressField(FieldType focused_field,
-                                    FieldTypeGroup focused_group,
-                                    const std::vector<FieldType>& types) {
-  return HasStreetAddress(types) &&
-         (focused_group != FieldTypeGroup::kAddress ||
-          !IsStreetAddressPart(focused_field));
-}
-
-bool FormHasOnlyNonStreetAddressFields(const std::vector<FieldType>& types,
-                                       uint32_t form_groups) {
-  return ContainsAddress(form_groups) && !HasStreetAddress(types) &&
-         !(ContainsName(form_groups) || ContainsPhone(form_groups) ||
-           ContainsEmail(form_groups));
-}
-
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/label_formatter_utils.h b/components/autofill/core/browser/ui/label_formatter_utils.h
deleted file mode 100644
index f332e50..0000000
--- a/components/autofill/core/browser/ui/label_formatter_utils.h
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_LABEL_FORMATTER_UTILS_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_LABEL_FORMATTER_UTILS_H_
-
-#include <string>
-#include <vector>
-
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-
-namespace autofill {
-
-// Adds |part| to the end of |parts| if |part| is not an empty string.
-void AddLabelPartIfNotEmpty(const std::u16string& part,
-                            std::vector<std::u16string>* parts);
-
-// Returns the text to show to the user. If there is more than one element in
-// |parts|, then a separator, |IDS_AUTOFILL_SUGGESTION_LABEL_SEPARATOR|, is
-// inserted between them.
-std::u16string ConstructLabelLine(const std::vector<std::u16string>& parts);
-
-// Like ConstructLabelLine, but uses |IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR|
-// instead.
-std::u16string ConstructMobileLabelLine(
-    const std::vector<std::u16string>& parts);
-
-// Returns true if |type| is associated with an address, but not with a street
-// address. For example, if the given type is ADDRESS_HOME_ZIP, then true is
-// returned. If ADDRESS_BILLING_LINE1 is given then false is returned.
-bool IsNonStreetAddressPart(FieldType type);
-
-// Returns true if |type| is associated with a street address.
-bool IsStreetAddressPart(FieldType type);
-
-// Returns true if |types| has a non-street-address-related type.
-bool HasNonStreetAddress(const std::vector<FieldType>& types);
-
-// Returns true if |types| has a street-address-related type.
-bool HasStreetAddress(const std::vector<FieldType>& types);
-
-// Returns a vector of only street-address-related field types in |types| if
-// |extract_street_address_types| is true, e.g. ADDRESS_HOME_LINE1.
-//
-// Returns a vector of only non-street-address-related field types in |types|
-// if |extract_street_address_types| is false, e.g. ADDRESS_BILLING_ZIP.
-std::vector<FieldType> ExtractSpecifiedAddressFieldTypes(
-    bool extract_street_address_types,
-    const std::vector<FieldType>& types);
-
-// Returns a collection of the types in |types| without |field_type_to_remove|.
-std::vector<FieldType> TypesWithoutFocusedField(
-    const std::vector<FieldType>& types,
-    FieldType field_type_to_remove);
-
-// Returns a pared down copy of |profile|. The copy has the same guid, origin,
-// country and language codes, and |field_types| as |profile|.
-AutofillProfile MakeTrimmedProfile(const AutofillProfile& profile,
-                                   const std::string& app_locale,
-                                   const std::vector<FieldType>& types);
-
-// Returns either street-address data or non-street-address data found in
-// |profile|. If |focused_field_type| is a street address field, then returns
-// non-street-address data, e.g. Lowell, MA 01852.
-//
-// If the focused type is not a street address field and if
-// |form_has_street_address| is true, then returns street-address data, e.g. 375
-// Merrimack St.
-//
-// If the focused type is not a street address field and if the form does not
-// have a street address, then returns the parts of the address in the form
-// other than the focused field. For example, if a user focuses on a city
-// field and if the state and zip code can be in the label, then MA 01852 is
-// returned. If there are no other non-street-address fields or if the data is
-// not present in |profile|, then an empty string is returned.
-std::u16string GetLabelForFocusedAddress(FieldType focused_field_type,
-                                         bool form_has_street_address,
-                                         const AutofillProfile& profile,
-                                         const std::string& app_locale,
-                                         const std::vector<FieldType>& types);
-
-// If |use_street_address| is true and if |profile| is associated with a street
-// address, then returns the street address, e.g. 24 Beacon St.
-//
-// If |use_street_address| is false and |profile| is associated with address
-// fields other than street addresses, then returns the non-street-
-// address-related data corresponding to |types|.
-std::u16string GetLabelAddress(bool use_street_address,
-                               const AutofillProfile& profile,
-                               const std::string& app_locale,
-                               const std::vector<FieldType>& types);
-
-// Returns the national address associated with |profile|, e.g.
-// 24 Beacon St., Boston, MA 02133.
-std::u16string GetLabelNationalAddress(const std::vector<FieldType>& types,
-                                       const AutofillProfile& profile,
-                                       const std::string& app_locale);
-
-// Returns the street address associated with |profile|, e.g. 24 Beacon St.
-std::u16string GetLabelStreetAddress(const std::vector<FieldType>& types,
-                                     const AutofillProfile& profile,
-                                     const std::string& app_locale);
-
-// Returns a label to show the user when |focused_field_type_| is not part of
-// a street address. For example, city and postal code are non-street-address
-// field types.
-std::u16string GetLabelForProfileOnFocusedNonStreetAddress(
-    bool form_has_street_address,
-    const AutofillProfile& profile,
-    const std::string& app_locale,
-    const std::vector<FieldType>& types,
-    const std::u16string& contact_info);
-
-// Returns a name that is (A) associated with |profile| and (B) found in
-// |types|; otherwise, returns an empty string.
-std::u16string GetLabelName(const std::vector<FieldType>& types,
-                            const AutofillProfile& profile,
-                            const std::string& app_locale);
-
-// Returns the first name associated with |profile|, if any; otherwise, returns
-// an empty string.
-std::u16string GetLabelFirstName(const AutofillProfile& profile,
-                                 const std::string& app_locale);
-
-// Returns the email address associated with |profile|, if any; otherwise,
-// returns an empty string.
-std::u16string GetLabelEmail(const AutofillProfile& profile,
-                             const std::string& app_locale);
-
-// Returns the phone number associated with |profile|, if any; otherwise,
-// returns an empty string. Phone numbers are given in |profile|'s country's
-// national format, if possible.
-std::u16string GetLabelPhone(const AutofillProfile& profile,
-                             const std::string& app_locale);
-
-// Each HaveSame* function below returns true if all |profiles| have the same
-// specified data. Note that the absence of data and actual data, e.g.
-// joe.bray@aol.com, are considered different pieces of data.
-//
-// Near-duplicate data, such as Düsseldorf and Dusseldorf or 3 Elm St and 3 Elm
-// St., should be filtered out before calling this function.
-bool HaveSameEmailAddresses(const std::vector<const AutofillProfile*>& profiles,
-                            const std::string& app_locale);
-bool HaveSameFirstNames(const std::vector<const AutofillProfile*>& profiles,
-                        const std::string& app_locale);
-bool HaveSameNonStreetAddresses(
-    const std::vector<const AutofillProfile*>& profiles,
-    const std::string& app_locale,
-    const std::vector<FieldType>& types);
-bool HaveSamePhoneNumbers(const std::vector<const AutofillProfile*>& profiles,
-                          const std::string& app_locale);
-bool HaveSameStreetAddresses(
-    const std::vector<const AutofillProfile*>& profiles,
-    const std::string& app_locale,
-    const std::vector<FieldType>& types);
-
-// Each HasUnfocused* function below returns true if the form described by
-// |form_groups| includes a field corresponding to the specified data and if
-// this field is not being interacted with by the user, i.e. the specified field
-// is not associated with the |focused_group|.
-bool HasUnfocusedEmailField(FieldTypeGroup focused_group, uint32_t form_groups);
-bool HasUnfocusedNameField(FieldTypeGroup focused_group, uint32_t form_groups);
-bool HasUnfocusedNonStreetAddressField(FieldType focused_field,
-                                       FieldTypeGroup focused_group,
-                                       const std::vector<FieldType>& types);
-bool HasUnfocusedPhoneField(FieldTypeGroup focused_group, uint32_t form_groups);
-bool HasUnfocusedStreetAddressField(FieldType focused_field,
-                                    FieldTypeGroup focused_group,
-                                    const std::vector<FieldType>& types);
-
-// Returns true if the form has only non-street-address-related fields, such as
-// city, state, and zip code.
-bool FormHasOnlyNonStreetAddressFields(const std::vector<FieldType>& types,
-                                       uint32_t form_groups);
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_LABEL_FORMATTER_UTILS_H_
diff --git a/components/autofill/core/browser/ui/label_formatter_utils_unittest.cc b/components/autofill/core/browser/ui/label_formatter_utils_unittest.cc
deleted file mode 100644
index 059661f..0000000
--- a/components/autofill/core/browser/ui/label_formatter_utils_unittest.cc
+++ /dev/null
@@ -1,216 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-
-#include <string>
-
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/uuid.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/country_type.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/grit/components_scaled_resources.h"
-#include "components/strings/grit/components_strings.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/l10n/l10n_util.h"
-
-namespace autofill {
-namespace {
-
-TEST(LabelFormatterUtilsTest, HaveSameFirstNames_OneProfileAndNoFirstName) {
-  AutofillProfile profile(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile, "", "", "", "", "", "", "", "", "", "", "DE",
-                       "");
-  EXPECT_TRUE(HaveSameFirstNames({&profile}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest, HaveSameFirstNames_OneProfileAndFirstName) {
-  AutofillProfile profile(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile, "Maria", "", "", "", "", "", "", "", "", "",
-                       "DE", "");
-  EXPECT_TRUE(HaveSameFirstNames({&profile}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest, HaveSameFirstNames_NoFirstNames) {
-  AutofillProfile profile1(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile1, "", "", "Kirch", "", "", "", "", "", "", "",
-                       "DE", "");
-  AutofillProfile profile2(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile2, "", "", "Winckelmann", "", "", "", "", "", "",
-                       "", "DE", "");
-  EXPECT_TRUE(HaveSameFirstNames({&profile1, &profile2}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest, HaveSameFirstNames_SameFirstNames) {
-  AutofillProfile profile1(AddressCountryCode("DE"));
-  ;
-  test::SetProfileInfo(&profile1, "Maria", "", "Kirch", "", "", "", "", "", "",
-                       "", "DE", "");
-  AutofillProfile profile2(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile2, "Maria", "", "Winckelmann", "", "", "", "",
-                       "", "", "", "DE", "");
-  EXPECT_TRUE(HaveSameFirstNames({&profile1, &profile2}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest, HaveSameFirstNames_DifferentNonEmptyFirstNames) {
-  AutofillProfile profile1(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile1, "Maria", "", "Kirch", "", "", "", "", "", "",
-                       "", "DE", "");
-  AutofillProfile profile2(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile2, "Mary", "", "Kirch", "", "", "", "", "", "",
-                       "", "DE", "");
-  EXPECT_FALSE(HaveSameFirstNames({&profile1, &profile2}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest, HaveSameFirstNames_NonEmptyAndEmptyFirstNames) {
-  AutofillProfile profile1(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile1, "Maria", "Margaretha", "Kirch", "", "", "",
-                       "", "", "", "", "DE", "");
-  AutofillProfile profile2(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile2, "", "Margaretha", "Winckelmann", "", "", "",
-                       "", "", "", "", "DE", "");
-  EXPECT_FALSE(HaveSameFirstNames({&profile1, &profile2}, "de"));
-  EXPECT_FALSE(HaveSameFirstNames({&profile2, &profile1}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest,
-     HaveSameEmailAddresses_OneProfileAndNoEmailAddress) {
-  AutofillProfile profile(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile, "Maria", "Margaretha", "Kirch",
-                       "mmkirch@gmx.de", "", "", "", "", "", "", "DE", "");
-  EXPECT_TRUE(HaveSameEmailAddresses({&profile}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest,
-     HaveSameEmailAddresses_OneProfileAndEmailAddress) {
-  AutofillProfile profile(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile, "Maria", "Margaretha", "Kirch", "", "", "", "",
-                       "", "", "", "DE", "");
-  EXPECT_TRUE(HaveSameEmailAddresses({&profile}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest, HaveSameEmailAddresses_NoEmailAddresses) {
-  AutofillProfile profile1(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile1, "Maria", "Margaretha", "Kirch", "", "", "",
-                       "", "", "", "", "DE", "");
-  AutofillProfile profile2(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile2, "Maria", "Margaretha", "Winckelmann", "", "",
-                       "", "", "", "", "", "DE", "");
-  EXPECT_TRUE(HaveSameEmailAddresses({&profile1, &profile2}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest, HaveSameEmailAddresses_SameEmailAddresses) {
-  AutofillProfile profile1(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile1, "Maria", "Margaretha", "Kirch",
-                       "mmkirch@gmx.de", "", "", "", "", "", "", "DE", "");
-  AutofillProfile profile2(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile2, "Maria", "Margaretha", "Winckelmann",
-                       "mmkirch@gmx.de", "", "", "", "", "", "", "DE", "");
-  EXPECT_TRUE(HaveSameEmailAddresses({&profile1, &profile2}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest,
-     HaveSameEmailAddresses_DifferentNonEmptyEmailAddresses) {
-  AutofillProfile profile1(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile1, "Maria", "Margaretha", "Kirch",
-                       "mmkirch@gmx.de", "", "", "", "", "", "", "DE", "");
-  AutofillProfile profile2(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile2, "Maria", "Margaretha", "Winckelmann",
-                       "mmw@gmail.com", "", "", "", "", "", "", "DE", "");
-  EXPECT_FALSE(HaveSameEmailAddresses({&profile1, &profile2}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest,
-     HaveSameEmailAddresses_NonEmptyAndEmptyEmailAddresses) {
-  AutofillProfile profile1(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile1, "Maria", "Margaretha", "Kirch",
-                       "mmkirch@gmx.de", "", "", "", "", "", "", "DE", "");
-  AutofillProfile profile2(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile2, "Maria", "Margaretha", "Winckelmann", "", "",
-                       "", "", "", "", "", "DE", "");
-  EXPECT_FALSE(HaveSameEmailAddresses({&profile1, &profile2}, "de"));
-  EXPECT_FALSE(HaveSameEmailAddresses({&profile2, &profile1}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest, HaveSamePhoneNumbers_OneProfileAndNoPhoneNumber) {
-  AutofillProfile profile(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile, "Maria", "Margaretha", "Kirch", "", "", "", "",
-                       "", "", "", "DE", "");
-  EXPECT_TRUE(HaveSamePhoneNumbers({&profile}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest, HaveSamePhoneNumbers_OneProfileAndPhoneNumber) {
-  AutofillProfile profile(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile, "Maria", "Margaretha", "Kirch", "", "", "", "",
-                       "", "", "", "DE", "+49 30 4504-2823");
-  EXPECT_TRUE(HaveSamePhoneNumbers({&profile}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest, HaveSamePhoneNumbers_NoPhoneNumber) {
-  AutofillProfile profile1(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile1, "Maria", "Margaretha", "Kirch", "", "", "",
-                       "", "", "", "", "DE", "");
-  AutofillProfile profile2(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile2, "Maria", "Margaretha", "Winckelmann", "", "",
-                       "", "", "", "", "", "DE", "");
-  EXPECT_TRUE(HaveSamePhoneNumbers({&profile1, &profile2}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest, HaveSamePhoneNumbers_SamePhoneNumbers) {
-  AutofillProfile profile1(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile1, "Maria", "Margaretha", "Kirch", "", "", "",
-                       "", "", "", "", "DE", "+49 30 4504-2823");
-  AutofillProfile profile2(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile2, "Maria", "Margaretha", "Winckelmann", "", "",
-                       "", "", "", "", "", "DE", "493045042823");
-  AutofillProfile profile3(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile3, "Maria", "Margaretha", "Winckelmann", "", "",
-                       "", "", "", "", "", "DE", "03045042823");
-  EXPECT_TRUE(HaveSamePhoneNumbers({&profile1, &profile2, &profile3}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest,
-     HaveSamePhoneNumbers_DifferentNonEmptyPhoneNumbers) {
-  AutofillProfile profile1(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile1, "Maria", "Margaretha", "Kirch", "", "", "",
-                       "", "", "", "", "DE", "+49 30 4504-2823");
-  AutofillProfile profile2(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile2, "Maria", "Margaretha", "Winckelmann", "", "",
-                       "", "", "", "", "", "DE", "+49 221 22123828");
-  EXPECT_FALSE(HaveSamePhoneNumbers({&profile1, &profile2}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest,
-     HaveSamePhoneNumbers_NonEmptyAndEmptyPhoneNumbers) {
-  AutofillProfile profile1(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile1, "Maria", "Margaretha", "Kirch", "", "", "",
-                       "", "", "", "", "DE", "+49 30 4504-2823");
-  AutofillProfile profile2(AddressCountryCode("DE"));
-  test::SetProfileInfo(&profile2, "Maria", "Margaretha", "Winckelmann", "", "",
-                       "", "", "", "", "", "DE", "");
-  EXPECT_FALSE(HaveSamePhoneNumbers({&profile1, &profile2}, "de"));
-  EXPECT_FALSE(HaveSamePhoneNumbers({&profile2, &profile1}, "de"));
-}
-
-TEST(LabelFormatterUtilsTest, GetLabelName) {
-  AutofillProfile profile(AddressCountryCode("DE"));
-  profile.SetInfo(NAME_FULL, u"Maria Margaretha Kirch", "de");
-  profile.FinalizeAfterImport();
-
-  EXPECT_EQ(u"Maria Margaretha Kirch",
-            GetLabelName({NAME_SUFFIX, NAME_FULL}, profile, "de"));
-  EXPECT_EQ(u"Maria Kirch",
-            GetLabelName({NAME_SUFFIX, NAME_FIRST, NAME_LAST}, profile, "de"));
-  EXPECT_EQ(u"Maria", GetLabelName({NAME_SUFFIX, NAME_FIRST}, profile, "de"));
-  EXPECT_EQ(u"Kirch", GetLabelName({NAME_SUFFIX, NAME_LAST}, profile, "de"));
-  EXPECT_EQ(u"Margaretha", GetLabelName({NAME_MIDDLE}, profile, "de"));
-  EXPECT_EQ(std::u16string(), GetLabelName({EMPTY_TYPE}, profile, "de"));
-  EXPECT_EQ(std::u16string(), GetLabelName({}, profile, "de"));
-}
-
-}  // namespace
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/mobile_label_formatter.cc b/components/autofill/core/browser/ui/mobile_label_formatter.cc
deleted file mode 100644
index 8c11b8e..0000000
--- a/components/autofill/core/browser/ui/mobile_label_formatter.cc
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/mobile_label_formatter.h"
-
-#include <algorithm>
-
-#include "base/metrics/field_trial_params.h"
-#include "base/notreached.h"
-#include "base/ranges/algorithm.h"
-#include "components/autofill/core/browser/autofill_data_util.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-#include "components/autofill/core/common/autofill_features.h"
-
-namespace autofill {
-
-using data_util::ContainsAddress;
-using data_util::ContainsEmail;
-using data_util::ContainsName;
-using data_util::ContainsPhone;
-using data_util::bit_field_type_groups::kAddress;
-using data_util::bit_field_type_groups::kEmail;
-using data_util::bit_field_type_groups::kName;
-using data_util::bit_field_type_groups::kPhone;
-
-MobileLabelFormatter::MobileLabelFormatter(
-    const std::vector<const AutofillProfile*>& profiles,
-    const std::string& app_locale,
-    FieldType focused_field_type,
-    uint32_t groups,
-    const FieldTypeSet& field_types)
-    : LabelFormatter(profiles,
-                     app_locale,
-                     focused_field_type,
-                     groups,
-                     field_types) {
-  const FieldTypeGroup focused_group = AutofillType(focused_field_type).group();
-
-  could_show_email_ = HasUnfocusedEmailField(focused_group, groups) &&
-                      !HaveSameEmailAddresses(profiles, app_locale);
-
-  could_show_name_ = HasUnfocusedNameField(focused_group, groups) &&
-                     !HaveSameFirstNames(profiles, app_locale);
-
-  // Non street address elements, e.g. Mountain View or 28199 Bremen, can only
-  // only be included in labels when the form's |field_types| do not have a
-  // street address field.
-  could_show_non_street_address_ =
-      !HasStreetAddress(field_types_for_labels()) &&
-      HasUnfocusedNonStreetAddressField(focused_field_type, focused_group,
-                                        field_types_for_labels()) &&
-      !HaveSameNonStreetAddresses(profiles, app_locale,
-                                  ExtractSpecifiedAddressFieldTypes(
-                                      /*extract_street_address_types=*/false,
-                                      field_types_for_labels()));
-
-  could_show_phone_ = HasUnfocusedPhoneField(focused_group, groups) &&
-                      !HaveSamePhoneNumbers(profiles, app_locale);
-
-  could_show_street_address_ =
-      HasUnfocusedStreetAddressField(focused_field_type, focused_group,
-                                     field_types_for_labels()) &&
-      !HaveSameStreetAddresses(
-          profiles, app_locale,
-          ExtractSpecifiedAddressFieldTypes(
-              /*extract_street_address_types=*/true, field_types_for_labels()));
-}
-
-MobileLabelFormatter::~MobileLabelFormatter() = default;
-
-std::u16string MobileLabelFormatter::GetLabelForProfile(
-    const AutofillProfile& profile,
-    FieldTypeGroup focused_group) const {
-  std::string label_variant = base::GetFieldTrialParamValueByFeature(
-      features::kAutofillUseMobileLabelDisambiguation,
-      features::kAutofillUseMobileLabelDisambiguationParameterName);
-  if (label_variant ==
-      features::kAutofillUseMobileLabelDisambiguationParameterShowOne) {
-    return GetLabelForShowOneVariant(profile, focused_group);
-  } else if (label_variant ==
-             features::kAutofillUseMobileLabelDisambiguationParameterShowAll) {
-    return GetLabelForShowAllVariant(profile, focused_group);
-  }
-  // An unknown parameter was received.
-  NOTREACHED();
-  return std::u16string();
-}
-
-// The order in which pieces of data are considered--address, and then, if the
-// data is not the same across |profiles_|, phone number, email address, and
-// name--ensures that the label contains the most useful information given the
-// |focused_group| and the |focused_field_type_|.
-std::u16string MobileLabelFormatter::GetLabelForShowOneVariant(
-    const AutofillProfile& profile,
-    FieldTypeGroup focused_group) const {
-  if (ShowLabelAddress(focused_group)) {
-    return GetLabelAddress(
-        /*use_street_address=*/HasStreetAddress(field_types_for_labels()),
-        profile, app_locale(),
-        TypesWithoutFocusedField(field_types_for_labels(),
-                                 focused_field_type()));
-  }
-
-  // If an unfocused form field does not have the same data for all
-  // |profiles_|, then it is preferable to show this data in the label.
-  if (could_show_phone_) {
-    return GetLabelPhone(profile, app_locale());
-  }
-
-  if (could_show_email_) {
-    return GetLabelEmail(profile, app_locale());
-  }
-
-  if (could_show_name_) {
-    return GetLabelName(field_types_for_labels(), profile, app_locale());
-  }
-
-  // In the case that data for unfocused form fields is the same for all
-  // |profiles_|, a label with the most important data is returned.
-  return GetDefaultLabel(profile, focused_group);
-}
-
-// The order in which parts of a label are added--name, address, phone number,
-// and email address--ensures that the label is formatted correctly for the
-// |focused_group|, the |focused_field_type_|, and the non-focused form fields
-// whose data is not the same across |profiles_|.
-std::u16string MobileLabelFormatter::GetLabelForShowAllVariant(
-    const AutofillProfile& profile,
-    FieldTypeGroup focused_group) const {
-  if (!(could_show_email_ || could_show_name_ ||
-        could_show_non_street_address_ || could_show_phone_ ||
-        could_show_street_address_)) {
-    // If there is profile data corresponding to unfocused form fields that is
-    // not the same across all |profiles_|, then include this data in the label.
-    // Otherwise, show the most important piece of data.
-    return GetDefaultLabel(profile, focused_group);
-  }
-
-  std::vector<std::u16string> label_parts;
-
-  // TODO(crbug.com/961819): Maybe put name after address for some app locales.
-  if (could_show_name_) {
-    // Due to mobile platforms' space constraints, only the first name is shown
-    // if the form contains a first name field or a full name field.
-    base::ranges::any_of(
-        field_types_for_labels(),
-        [](auto type) { return type == NAME_FIRST || type == NAME_FULL; })
-        ? AddLabelPartIfNotEmpty(GetLabelFirstName(profile, app_locale()),
-                                 &label_parts)
-        : AddLabelPartIfNotEmpty(
-              GetLabelName(field_types_for_labels(), profile, app_locale()),
-              &label_parts);
-  }
-
-  if (could_show_street_address_) {
-    AddLabelPartIfNotEmpty(
-        GetLabelAddress(/*use_street_address=*/true, profile, app_locale(),
-                        field_types_for_labels()),
-        &label_parts);
-  }
-
-  if (could_show_non_street_address_) {
-    AddLabelPartIfNotEmpty(
-        GetLabelAddress(/*use_street_address=*/false, profile, app_locale(),
-                        field_types_for_labels()),
-        &label_parts);
-  }
-
-  if (could_show_phone_) {
-    AddLabelPartIfNotEmpty(GetLabelPhone(profile, app_locale()), &label_parts);
-  }
-
-  if (could_show_email_) {
-    AddLabelPartIfNotEmpty(GetLabelEmail(profile, app_locale()), &label_parts);
-  }
-
-  return ConstructMobileLabelLine(label_parts);
-}
-
-// The order in which pieces of data are considered--address, phone number,
-// email address, and name--ensures that the label contains the most useful
-// information given the |focused_group| and the |focused_field_type_|.
-std::u16string MobileLabelFormatter::GetDefaultLabel(
-    const AutofillProfile& profile,
-    FieldTypeGroup focused_group) const {
-  if (ShowLabelAddress(focused_group)) {
-    return GetLabelAddress(
-        /*use_street_address=*/HasStreetAddress(field_types_for_labels()),
-        profile, app_locale(),
-        TypesWithoutFocusedField(field_types_for_labels(),
-                                 focused_field_type()));
-  }
-
-  if (HasUnfocusedPhoneField(focused_group, groups())) {
-    return GetLabelPhone(profile, app_locale());
-  }
-
-  if (HasUnfocusedEmailField(focused_group, groups())) {
-    return GetLabelEmail(profile, app_locale());
-  }
-
-  return GetLabelName(field_types_for_labels(), profile, app_locale());
-}
-
-bool MobileLabelFormatter::ShowLabelAddress(
-    FieldTypeGroup focused_group) const {
-  if (HasUnfocusedStreetAddressField(focused_field_type(), focused_group,
-                                     field_types_for_labels())) {
-    return true;
-  }
-
-  // If a form lacks a street address field, then a non street address field may
-  // be shown. It is shown in two situations:
-  // 1. A form has an unfocused non street address field.
-  // 2. A form has only non street address fields.
-  return (!HasStreetAddress(field_types_for_labels()) &&
-          HasUnfocusedNonStreetAddressField(focused_field_type(), focused_group,
-                                            field_types_for_labels())) ||
-         FormHasOnlyNonStreetAddressFields(field_types_for_labels(), groups());
-}
-
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/mobile_label_formatter.h b/components/autofill/core/browser/ui/mobile_label_formatter.h
deleted file mode 100644
index 2c89ed5..0000000
--- a/components/autofill/core/browser/ui/mobile_label_formatter.h
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_MOBILE_LABEL_FORMATTER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_MOBILE_LABEL_FORMATTER_H_
-
-#include <string>
-#include <vector>
-
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/ui/label_formatter.h"
-
-namespace autofill {
-
-// A LabelFormatter that creates Suggestions' disambiguating labels for forms
-// on mobile platforms.
-//
-// In constructing a label, a MobileLabelFormatter may consider the following:
-//
-// 1. The type of form, e.g. a form with only name and phone fields or a form
-// with name, address, phone, and email fields
-// 2. The field on which the user is focused, e.g. a first name field
-// 3. The non-focused form fields whose corresponding profile data is not the
-// same across |profiles_|, if any
-// 4. The relative usefulness of form field data that could be shown in labels
-//
-// The usefulness, from greatest to least, is as follows:
-// address > phone > email > name
-//
-// There are two address categories: street address and non street address.
-// Street address example: 44 Lakeview Ln
-// Non street address examples: London, 02113, CA, Rio Comprido, and Germany
-class MobileLabelFormatter : public LabelFormatter {
- public:
-  MobileLabelFormatter(const std::vector<const AutofillProfile*>& profiles,
-                       const std::string& app_locale,
-                       FieldType focused_field_type,
-                       uint32_t groups,
-                       const FieldTypeSet& field_types);
-
-  ~MobileLabelFormatter() override;
-
-  std::u16string GetLabelForProfile(
-      const AutofillProfile& profile,
-      FieldTypeGroup focused_group) const override;
-
- private:
-  // Returns a label for the kAutofillUseMobileLabelDisambiguation feature when
-  // the ShowOne variant is enabled.
-  //
-  // The label has at most one piece of data, e.g. a phone number. For address
-  // data, note that a street address, e.g. 120 Oak Rd #2, and a non street
-  // address, e.g. Palo Alto, CA 94303, are each considered one piece of data.
-  //
-  // It is possible for the label to be an empty string. For example, suppose
-  // (A) a user has two profiles with slightly different names, e.g. Joe and
-  // Joseph, (B) the profile with Joe as its first name lacks an email address
-  // and (C) this user is interacting with a form that has first name, last
-  // name, and email address fields. If this user clicks on the first name
-  // field, then the suggestion with Joe has an empty string as its label.
-  std::u16string GetLabelForShowOneVariant(const AutofillProfile& profile,
-                                           FieldTypeGroup focused_group) const;
-
-  // Returns a label for the kAutofillUseMobileLabelDisambiguation feature when
-  // the ShowAll variant is enabled.
-  //
-  // The label may contain multiple pieces of data, e.g. a street address, a
-  // phone number, and an email address. It contains only data whose values are
-  // not the same across |profiles|.
-  //
-  // As explained in the comment for GetLabelForShowOneVariant, it is possible
-  // for the label to be an empty string.
-  std::u16string GetLabelForShowAllVariant(const AutofillProfile& profile,
-                                           FieldTypeGroup focused_group) const;
-
-  // Returns a label with the most useful piece of data according to the
-  // ordering described in this class' description.
-  //
-  // It is possible for the label to be an empty string. This can happen when
-  // |profile| is missing data corresponding to a field, e.g. a profile without
-  // a phone number.
-  std::u16string GetDefaultLabel(const AutofillProfile& profile,
-                                 FieldTypeGroup focused_group) const;
-
-  // Returns true if the label should be an address part, e.g. 4 Oak Rd or
-  // Boston, MA 02116.
-  bool ShowLabelAddress(FieldTypeGroup focused_group) const;
-
-  // True if the field (A) appears in the form, (B) is not the focused field,
-  // and (C) does not have the same data for all |profiles_|.
-  //
-  // If a field is focused, then its corresponding bool should be false because
-  // the focused field's data is shown in a suggestion's value. Repeating this
-  // data in a suggestion's label is not helpful to users.
-  bool could_show_email_;
-  bool could_show_name_;
-  bool could_show_non_street_address_;
-  bool could_show_phone_;
-  bool could_show_street_address_;
-};
-
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_MOBILE_LABEL_FORMATTER_H_
diff --git a/components/autofill/core/browser/ui/mobile_label_formatter_unittest.cc b/components/autofill/core/browser/ui/mobile_label_formatter_unittest.cc
deleted file mode 100644
index 747985a..0000000
--- a/components/autofill/core/browser/ui/mobile_label_formatter_unittest.cc
+++ /dev/null
@@ -1,411 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/mobile_label_formatter.h"
-#include "components/autofill/core/browser/data_model/address.h"
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/uuid.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/ui/label_formatter_utils.h"
-#include "components/autofill/core/common/autofill_features.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::ElementsAre;
-
-namespace autofill {
-namespace {
-
-FieldTypeSet GetContactOnlyFieldTypes() {
-  return {NO_SERVER_DATA, NAME_FIRST, NAME_LAST, EMAIL_ADDRESS,
-          PHONE_HOME_WHOLE_NUMBER};
-}
-
-FieldTypeSet GetAddressOnlyFieldTypes() {
-  return {NO_SERVER_DATA,     NAME_FIRST,
-          NAME_LAST,          ADDRESS_HOME_LINE1,
-          ADDRESS_HOME_LINE2, ADDRESS_HOME_DEPENDENT_LOCALITY,
-          ADDRESS_HOME_CITY,  ADDRESS_HOME_STATE,
-          ADDRESS_HOME_ZIP,   ADDRESS_HOME_COUNTRY};
-}
-
-FieldTypeSet GetAddressPlusEmailFieldTypes() {
-  return {NO_SERVER_DATA,
-          NAME_FIRST,
-          NAME_LAST,
-          EMAIL_ADDRESS,
-          ADDRESS_HOME_LINE1,
-          ADDRESS_HOME_LINE2,
-          ADDRESS_HOME_DEPENDENT_LOCALITY,
-          ADDRESS_HOME_CITY,
-          ADDRESS_HOME_STATE,
-          ADDRESS_HOME_ZIP,
-          ADDRESS_HOME_COUNTRY};
-}
-
-FieldTypeSet GetAddressPlusContactFieldTypes() {
-  return {NO_SERVER_DATA,
-          NAME_FIRST,
-          NAME_LAST,
-          EMAIL_ADDRESS,
-          ADDRESS_HOME_LINE1,
-          ADDRESS_HOME_LINE2,
-          ADDRESS_HOME_DEPENDENT_LOCALITY,
-          ADDRESS_HOME_CITY,
-          ADDRESS_HOME_STATE,
-          ADDRESS_HOME_ZIP,
-          ADDRESS_HOME_COUNTRY,
-          PHONE_HOME_WHOLE_NUMBER};
-}
-
-AutofillProfile GetProfileA() {
-  AutofillProfile profile(AddressCountryCode("US"));
-  test::SetProfileInfo(&profile, "firstA", "middleA", "lastA",
-                       "emailA@gmail.com", "", "address1A", "address2A",
-                       "cityA", "MA", "02113", "US", "16176660000");
-  return profile;
-}
-
-AutofillProfile GetProfileB() {
-  AutofillProfile profile(AddressCountryCode("US"));
-  test::SetProfileInfo(&profile, "firstB", "middleB", "lastB",
-                       "emailB@gmail.com", "", "address1B", "address2B",
-                       "cityB", "NY", "12224", "US", "15185550000");
-  return profile;
-}
-
-TEST(MobileLabelFormatterTest, GetLabelsWithMissingProfiles) {
-  const std::vector<const AutofillProfile*> profiles{};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", NAME_FIRST, {NAME_FIRST, NAME_LAST, EMAIL_ADDRESS});
-  EXPECT_TRUE(formatter->GetLabels().empty());
-}
-
-TEST(MobileLabelFormatterTest, GetLabelsForUnfocusedAddress_ShowOne) {
-  std::map<std::string, std::string> parameters;
-  parameters[features::kAutofillUseMobileLabelDisambiguationParameterName] =
-      features::kAutofillUseMobileLabelDisambiguationParameterShowOne;
-
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeatureWithParameters(
-      features::kAutofillUseMobileLabelDisambiguation, parameters);
-
-  AutofillProfile profileA = GetProfileA();
-  AutofillProfile profileB = GetProfileB();
-  AutofillProfile profileC(AddressCountryCode("US"));
-  test::SetProfileInfo(&profileC, "firstC", "middleC", "lastC", "", "", "", "",
-                       "", "", "", "US", "");
-  const std::vector<const AutofillProfile*> profiles{&profileA, &profileB,
-                                                     &profileC};
-
-  // Tests that the street address is shown when the form contains a street
-  // address field and the user is not focused on it.
-  std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", NAME_FIRST, GetAddressOnlyFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"address1A, address2A", u"address1B, address2B",
-                          std::u16string()));
-
-  // Tests that the non street address is shown when the form's only address
-  // fields are non street address fields and the user is not focused on any of
-  // them.
-  formatter = LabelFormatter::Create(profiles, "en-US", NAME_FIRST,
-                                     {NAME_FIRST, NAME_LAST, ADDRESS_HOME_ZIP});
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"02113", u"12224", std::u16string()));
-
-  // Like the previous test, but without name.
-  formatter = LabelFormatter::Create(
-      profiles, "en-US", EMAIL_ADDRESS,
-      {ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, EMAIL_ADDRESS});
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"cityA, MA", u"cityB, NY", std::u16string()));
-
-  // Tests that addresses are not shown when the form does not contain an
-  // address field.
-  formatter = LabelFormatter::Create(profiles, "en-US", NAME_FIRST,
-                                     GetContactOnlyFieldTypes());
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(u"(617) 666-0000", u"(518) 555-0000", std::u16string()));
-}
-
-TEST(MobileLabelFormatterTest,
-     GetLabelsForFocusedAddress_MultipleProfiles_ShowOne) {
-  std::map<std::string, std::string> parameters;
-  parameters[features::kAutofillUseMobileLabelDisambiguationParameterName] =
-      features::kAutofillUseMobileLabelDisambiguationParameterShowOne;
-
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeatureWithParameters(
-      features::kAutofillUseMobileLabelDisambiguation, parameters);
-
-  AutofillProfile profileA = GetProfileA();
-
-  // Tests that a street is shown when a form contains an unfocused street
-  // address and a focused non street address.
-  AutofillProfile profileB = GetProfileB();
-  std::vector<const AutofillProfile*> profiles{&profileA, &profileB};
-
-  std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_ZIP, GetAddressPlusContactFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"address1A, address2A", u"address1B, address2B"));
-
-  // Tests that a non street address is shown when a form contains only
-  // non focused street address fields and a focused non street address.
-  formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_ZIP,
-      {ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP});
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"cityA, MA", u"cityB, NY"));
-
-  // Tests that a non street address is not shown when a form contains
-  // non focused street address fields and another kind of field and also has a
-  // focused non street address.
-  formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_CITY,
-      {ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, EMAIL_ADDRESS});
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"emailA@gmail.com", u"emailB@gmail.com"));
-
-  // Tests that a phone number is shown when the address cannot be shown and
-  // there are different phone numbers.
-  profileB = GetProfileA();
-  profileB.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"15185550000", "en-US");
-  profiles = {&profileA, &profileB};
-
-  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1,
-                                     GetAddressPlusContactFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"(617) 666-0000", u"(518) 555-0000"));
-
-  // Tests that an email address is shown when the address cannot be shown and
-  // there are different email addresses.
-  profileB = GetProfileA();
-  profileB.SetInfo(EMAIL_ADDRESS, u"emailB@gmail.com", "en-US");
-  profiles = {&profileA, &profileB};
-
-  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1,
-                                     GetAddressPlusContactFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"emailA@gmail.com", u"emailB@gmail.com"));
-
-  // Tests that a name is shown when the address cannot be shown and there are
-  // different names.
-  profileB = GetProfileA();
-  profileB.SetInfo(NAME_FIRST, u"firstB", "en-US");
-  profileB.SetInfo(NAME_LAST, u"lastB", "en-US");
-  profiles = {&profileA, &profileB};
-
-  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1,
-                                     GetAddressPlusContactFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"firstA lastA", u"firstB lastB"));
-
-  // Tests that a phone number is shown when the address cannot be shown, when
-  // profiles have the same data for unfocused form fields, and when the form
-  // has a phone number field.
-  profileB = GetProfileA();
-  profiles = {&profileA, &profileB};
-  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1,
-                                     GetAddressPlusContactFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"(617) 666-0000", u"(617) 666-0000"));
-
-  // Tests that an email address is shown when the address cannot be shown, when
-  // profiles have the same data for unfocused form fields, and when the form
-  // does not have a phone number field, but has an email address field.
-  profileB = GetProfileA();
-  profiles = {&profileA, &profileB};
-  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1,
-                                     GetAddressPlusEmailFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"emailA@gmail.com", u"emailA@gmail.com"));
-
-  // Tests that a name is shown when the address cannot be shown, when profiles
-  // have the same data for unfocused form fields, and when the form does not
-  // have a phone number or email address field, but has a name field.
-  profileB = GetProfileA();
-  profiles = {&profileA, &profileB};
-  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1,
-                                     GetAddressOnlyFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"firstA lastA", u"firstA lastA"));
-}
-
-TEST(MobileLabelFormatterTest,
-     GetLabelsForFocusedAddress_UniqueProfile_ShowOne) {
-  std::map<std::string, std::string> parameters;
-  parameters[features::kAutofillUseMobileLabelDisambiguationParameterName] =
-      features::kAutofillUseMobileLabelDisambiguationParameterShowOne;
-
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeatureWithParameters(
-      features::kAutofillUseMobileLabelDisambiguation, parameters);
-
-  AutofillProfile profileA = GetProfileA();
-  std::vector<const AutofillProfile*> profiles{&profileA};
-
-  // Tests that the second most important piece of data, phone, is shown when
-  // the form has an unfocused form field corresponding to this data and the
-  // most important piece cannot be shown.
-  std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_LINE1, GetAddressPlusContactFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"(617) 666-0000"));
-
-  // Tests that the third most important piece of data, email, is shown when
-  // the form has an unfocused form field corresponding to this data and the
-  // two most important pieces of data cannot be shown.
-  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1,
-                                     GetAddressPlusEmailFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"emailA@gmail.com"));
-
-  // Tests that the least important piece of data, name, is shown when the form
-  // has an unfocused form field corresponding to this data and the more
-  // important pieces of data cannot be shown.
-  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1,
-                                     GetAddressOnlyFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"firstA lastA"));
-}
-
-TEST(MobileLabelFormatterTest, GetLabels_DistinctProfiles_ShowAll) {
-  std::map<std::string, std::string> parameters;
-  parameters[features::kAutofillUseMobileLabelDisambiguationParameterName] =
-      features::kAutofillUseMobileLabelDisambiguationParameterShowAll;
-
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeatureWithParameters(
-      features::kAutofillUseMobileLabelDisambiguation, parameters);
-
-  AutofillProfile profileA = GetProfileA();
-  AutofillProfile profileB = GetProfileB();
-  AutofillProfile profileC(AddressCountryCode("US"));
-  test::SetProfileInfo(&profileC, "firstC", "middleC", "lastC", "", "", "", "",
-                       "", "", "", "US", "");
-  const std::vector<const AutofillProfile*> profiles{&profileA, &profileB,
-                                                     &profileC};
-
-  // Tests that unfocused data that is not the same across profiles is shown in
-  // the label for forms with addresses.
-  std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", NAME_FIRST, GetAddressPlusContactFieldTypes());
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(u"address1A, address2A, (617) 666-0000, emailA@gmail.com",
-                  u"address1B, address2B, (518) 555-0000, emailB@gmail.com",
-                  std::u16string()));
-
-  // Like the previous test, but focuses on an address field rather than a name
-  // field to check that the name is correctly added to the label.
-  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1,
-                                     GetAddressPlusContactFieldTypes());
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(u"firstA, (617) 666-0000, emailA@gmail.com",
-                  u"firstB, (518) 555-0000, emailB@gmail.com", u"firstC"));
-
-  // Tests that unfocused data that is not the same across profiles is shown in
-  // the label for forms with non street addresses and without street addresses.
-  formatter = LabelFormatter::Create(profiles, "en-US", NAME_FIRST,
-                                     {NAME_FIRST, NAME_LAST, ADDRESS_HOME_ZIP});
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"02113", u"12224", std::u16string()));
-
-  // Like the previous test, but focuses on an address field rather than a name
-  // field to check that the name is correctly added to the label.
-  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_ZIP,
-                                     {NAME_FIRST, NAME_LAST, ADDRESS_HOME_ZIP});
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"firstA", u"firstB", u"firstC"));
-
-  // Tests that unfocused data that is not the same across profiles is shown in
-  // the label for forms without addresses.
-  formatter = LabelFormatter::Create(profiles, "en-US", NAME_FIRST,
-                                     GetContactOnlyFieldTypes());
-  EXPECT_THAT(
-      formatter->GetLabels(),
-      ElementsAre(u"(617) 666-0000, emailA@gmail.com",
-                  u"(518) 555-0000, emailB@gmail.com", std::u16string()));
-
-  // Like the previous test, but focuses on a phone field rather than a name
-  // field to check that the name is correctly added to the label.
-  formatter = LabelFormatter::Create(profiles, "en-US", PHONE_HOME_WHOLE_NUMBER,
-                                     GetContactOnlyFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(u"firstA, emailA@gmail.com",
-                          u"firstB, emailB@gmail.com", u"firstC"));
-}
-
-TEST(MobileLabelFormatterTest, GetDefaultLabel_ShowAll) {
-  std::map<std::string, std::string> parameters;
-  parameters[features::kAutofillUseMobileLabelDisambiguationParameterName] =
-      features::kAutofillUseMobileLabelDisambiguationParameterShowAll;
-
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeatureWithParameters(
-      features::kAutofillUseMobileLabelDisambiguation, parameters);
-
-  AutofillProfile profileA = GetProfileA();
-  const std::vector<const AutofillProfile*> profiles{&profileA};
-
-  // Tests that the most important piece of data, address, is shown when the
-  // form has an unfocused form field corresponding to this data.
-  std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", NAME_FIRST, GetAddressPlusContactFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"address1A, address2A"));
-
-  // Tests that the most important piece of data, address, is shown when the
-  // form has an unfocused form field corresponding to this data.
-  formatter = LabelFormatter::Create(profiles, "en-US", NAME_FIRST,
-                                     {NAME_FIRST, NAME_LAST, ADDRESS_HOME_ZIP});
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"02113"));
-
-  // Tests that the second most important piece of data, phone, is shown when
-  // the form has an unfocused form field corresponding to this data and the
-  // most important piece cannot be shown.
-  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1,
-                                     GetAddressPlusContactFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"(617) 666-0000"));
-
-  // Tests that the third most important piece of data, email, is shown when
-  // the form has an unfocused form field corresponding to this data and the
-  // two more important pieces of data cannot be shown.
-  formatter = LabelFormatter::Create(profiles, "en-US", PHONE_HOME_WHOLE_NUMBER,
-                                     GetContactOnlyFieldTypes());
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"emailA@gmail.com"));
-
-  // Tests that the least important piece of data, name, is shown when
-  // the form has an unfocused form field corresponding to this data and the
-  // more important pieces of data cannot be shown.
-  formatter =
-      LabelFormatter::Create(profiles, "en-US", PHONE_HOME_WHOLE_NUMBER,
-                             {NAME_FIRST, NAME_LAST, PHONE_HOME_WHOLE_NUMBER});
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"firstA lastA"));
-
-  // Tests that a non street address is shown when a form contains only
-  // non focused street address fields and a focused non street address.
-  formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_ZIP,
-      {ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP});
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"cityA, MA"));
-
-  // Tests that a non street address is not shown when a form contains
-  // non focused street address fields and another kind of field and also has a
-  // focused non street address.
-  formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_HOME_CITY,
-      {ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, EMAIL_ADDRESS});
-  EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"emailA@gmail.com"));
-}
-
-}  // namespace
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/mock_autofill_popup_delegate.h b/components/autofill/core/browser/ui/mock_autofill_popup_delegate.h
index 83d511e..297da6c 100644
--- a/components/autofill/core/browser/ui/mock_autofill_popup_delegate.h
+++ b/components/autofill/core/browser/ui/mock_autofill_popup_delegate.h
@@ -7,10 +7,11 @@
 
 #include "components/autofill/core/browser/ui/autofill_popup_delegate.h"
 
+#include <optional>
+
 #include "base/functional/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
diff --git a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc
index 29b16c9..c038678 100644
--- a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc
+++ b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc
@@ -124,8 +124,8 @@
 
   // Shows the Card Unmask Prompt. `challenge_option` being present denotes that
   // we are in the virtual card use-case.
-  void ShowPrompt(const absl::optional<autofill::CardUnmaskChallengeOption>&
-                      challenge_option = absl::nullopt) {
+  void ShowPrompt(const std::optional<autofill::CardUnmaskChallengeOption>&
+                      challenge_option = std::nullopt) {
     card_.set_record_type(challenge_option.has_value()
                               ? CreditCard::RecordType::kVirtualCard
                               : CreditCard::RecordType::kMaskedServerCard);
@@ -150,10 +150,10 @@
                                      bool should_unmask_virtual_card = false,
                                      bool was_checkbox_visible = true) {
     ShowPrompt(should_unmask_virtual_card
-                   ? absl::optional<autofill::CardUnmaskChallengeOption>(
+                   ? std::optional<autofill::CardUnmaskChallengeOption>(
                          test::GetCardUnmaskChallengeOptions(
                              {CardUnmaskChallengeOptionType::kCvc})[0])
-                   : absl::nullopt);
+                   : std::nullopt);
     controller_->OnUnmaskPromptAccepted(u"444", u"01", u"2050",
                                         enable_fido_auth, was_checkbox_visible);
   }
@@ -542,7 +542,7 @@
 
 TEST_P(LoggingValidationTestForNickname, VirtualCard_LogUnmaskPromptShown) {
   base::HistogramTester histogram_tester;
-  ShowPrompt(absl::optional<autofill::CardUnmaskChallengeOption>(
+  ShowPrompt(std::optional<autofill::CardUnmaskChallengeOption>(
       test::GetCardUnmaskChallengeOptions(
           {CardUnmaskChallengeOptionType::kCvc})[0]));
 
diff --git a/components/autofill/core/browser/ui/payments/card_unmask_prompt_options.cc b/components/autofill/core/browser/ui/payments/card_unmask_prompt_options.cc
index 9fdb1a5..4979015 100644
--- a/components/autofill/core/browser/ui/payments/card_unmask_prompt_options.cc
+++ b/components/autofill/core/browser/ui/payments/card_unmask_prompt_options.cc
@@ -9,7 +9,7 @@
 CardUnmaskPromptOptions::CardUnmaskPromptOptions() = default;
 
 CardUnmaskPromptOptions::CardUnmaskPromptOptions(
-    const absl::optional<CardUnmaskChallengeOption>& challenge_option,
+    const std::optional<CardUnmaskChallengeOption>& challenge_option,
     AutofillClient::UnmaskCardReason reason)
     : challenge_option(challenge_option), reason(reason) {}
 
diff --git a/components/autofill/core/browser/ui/payments/card_unmask_prompt_options.h b/components/autofill/core/browser/ui/payments/card_unmask_prompt_options.h
index 45997c7..cbd26e8 100644
--- a/components/autofill/core/browser/ui/payments/card_unmask_prompt_options.h
+++ b/components/autofill/core/browser/ui/payments/card_unmask_prompt_options.h
@@ -17,7 +17,7 @@
 struct CardUnmaskPromptOptions {
   CardUnmaskPromptOptions();
   CardUnmaskPromptOptions(
-      const absl::optional<CardUnmaskChallengeOption>& challenge_option,
+      const std::optional<CardUnmaskChallengeOption>& challenge_option,
       AutofillClient::UnmaskCardReason reason);
   CardUnmaskPromptOptions(const CardUnmaskPromptOptions&);
   ~CardUnmaskPromptOptions();
@@ -26,7 +26,7 @@
   // challenge option the user is presented with. In the
   // CardUnmaskPromptController, only a CardUnmaskChallengeOptionType::kCvc
   // challenge option is supported.
-  absl::optional<CardUnmaskChallengeOption> challenge_option;
+  std::optional<CardUnmaskChallengeOption> challenge_option;
 
   // The origin of the unmask request.
   AutofillClient::UnmaskCardReason reason;
diff --git a/components/autofill/core/browser/ui/suggestion.h b/components/autofill/core/browser/ui/suggestion.h
index 1ab89114..f1c4b6a9 100644
--- a/components/autofill/core/browser/ui/suggestion.h
+++ b/components/autofill/core/browser/ui/suggestion.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_SUGGESTION_H_
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_SUGGESTION_H_
 
+#include <optional>
 #include <ostream>
 #include <string>
 #include <string_view>
@@ -16,7 +17,6 @@
 #include "build/build_config.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/ui/popup_item_ids.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 #include "ui/gfx/image/image.h"
 #include "url/gurl.h"
diff --git a/components/autofill/core/browser/webdata/addresses/address_autofill_table.cc b/components/autofill/core/browser/webdata/addresses/address_autofill_table.cc
new file mode 100644
index 0000000..5cb16538
--- /dev/null
+++ b/components/autofill/core/browser/webdata/addresses/address_autofill_table.cc
@@ -0,0 +1,1192 @@
+// 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.
+
+#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <string_view>
+#include <vector>
+
+#include "base/notreached.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/uuid.h"
+#include "components/autofill/core/browser/field_type_utils.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/webdata/autofill_table_utils.h"
+#include "components/autofill/core/common/autofill_features.h"
+#include "components/webdata/common/web_database.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+
+namespace autofill {
+
+namespace {
+
+constexpr std::string_view kAutofillProfilesTable = "autofill_profiles";
+constexpr std::string_view kGuid = "guid";
+constexpr std::string_view kLabel = "label";
+constexpr std::string_view kCompanyName = "company_name";
+constexpr std::string_view kStreetAddress = "street_address";
+constexpr std::string_view kDependentLocality = "dependent_locality";
+constexpr std::string_view kCity = "city";
+constexpr std::string_view kState = "state";
+constexpr std::string_view kZipcode = "zipcode";
+constexpr std::string_view kSortingCode = "sorting_code";
+constexpr std::string_view kCountryCode = "country_code";
+constexpr std::string_view kUseCount = "use_count";
+constexpr std::string_view kUseDate = "use_date";
+constexpr std::string_view kDateModified = "date_modified";
+constexpr std::string_view kOrigin = "origin";
+constexpr std::string_view kLanguageCode = "language_code";
+constexpr std::string_view kDisallowSettingsVisibleUpdates =
+    "disallow_settings_visible_updates";
+
+constexpr std::string_view kAutofillProfileAddressesTable =
+    "autofill_profile_addresses";
+// kGuid = "guid"
+// kStreetAddress = "street_address"
+constexpr std::string_view kStreetName = "street_name";
+constexpr std::string_view kDependentStreetName = "dependent_street_name";
+constexpr std::string_view kHouseNumber = "house_number";
+constexpr std::string_view kSubpremise = "subpremise";
+// kDependentLocality = "dependent_locality"
+// kCity = "city"
+// kState = "state"
+constexpr std::string_view kZipCode = "zip_code";
+// kCountryCode = "country_code"
+// kSortingCode = "sorting_code"
+constexpr std::string_view kApartmentNumber = "apartment_number";
+constexpr std::string_view kFloor = "floor";
+constexpr std::string_view kStreetAddressStatus = "street_address_status";
+constexpr std::string_view kStreetNameStatus = "street_name_status";
+constexpr std::string_view kDependentStreetNameStatus =
+    "dependent_street_name_status";
+constexpr std::string_view kHouseNumberStatus = "house_number_status";
+constexpr std::string_view kSubpremiseStatus = "subpremise_status";
+constexpr std::string_view kDependentLocalityStatus =
+    "dependent_locality_status";
+constexpr std::string_view kCityStatus = "city_status";
+constexpr std::string_view kStateStatus = "state_status";
+constexpr std::string_view kZipCodeStatus = "zip_code_status";
+constexpr std::string_view kCountryCodeStatus = "country_code_status";
+constexpr std::string_view kSortingCodeStatus = "sorting_code_status";
+constexpr std::string_view kApartmentNumberStatus = "apartment_number_status";
+constexpr std::string_view kFloorStatus = "floor_status";
+
+constexpr std::string_view kAutofillProfileNamesTable =
+    "autofill_profile_names";
+// kGuid = "guid"
+constexpr std::string_view kHonorificPrefix = "honorific_prefix";
+constexpr std::string_view kFirstName = "first_name";
+constexpr std::string_view kMiddleName = "middle_name";
+constexpr std::string_view kLastName = "last_name";
+constexpr std::string_view kFirstLastName = "first_last_name";
+constexpr std::string_view kConjunctionLastName = "conjunction_last_name";
+constexpr std::string_view kSecondLastName = "second_last_name";
+constexpr std::string_view kFullName = "full_name";
+constexpr std::string_view kFullNameWithHonorificPrefix =
+    "full_name_with_honorific_prefix";
+constexpr std::string_view kHonorificPrefixStatus = "honorific_prefix_status";
+constexpr std::string_view kFirstNameStatus = "first_name_status";
+constexpr std::string_view kMiddleNameStatus = "middle_name_status";
+constexpr std::string_view kLastNameStatus = "last_name_status";
+constexpr std::string_view kFirstLastNameStatus = "first_last_name_status";
+constexpr std::string_view kConjunctionLastNameStatus =
+    "conjunction_last_name_status";
+constexpr std::string_view kSecondLastNameStatus = "second_last_name_status";
+constexpr std::string_view kFullNameStatus = "full_name_status";
+constexpr std::string_view kFullNameWithHonorificPrefixStatus =
+    "full_name_with_honorific_prefix_status";
+
+constexpr std::string_view kAutofillProfileEmailsTable =
+    "autofill_profile_emails";
+// kGuid = "guid"
+constexpr std::string_view kEmail = "email";
+
+constexpr std::string_view kAutofillProfilePhonesTable =
+    "autofill_profile_phones";
+// kGuid = "guid"
+constexpr std::string_view kNumber = "number";
+
+constexpr std::string_view kAutofillProfileBirthdatesTable =
+    "autofill_profile_birthdates";
+// kGuid = "guid"
+constexpr std::string_view kDay = "day";
+constexpr std::string_view kMonth = "month";
+constexpr std::string_view kYear = "year";
+
+constexpr std::string_view kContactInfoTable = "contact_info";
+constexpr std::string_view kLocalAddressesTable = "local_addresses";
+// kGuid = "guid"
+// kUseCount = "use_count"
+// kUseDate = "use_date"
+// kDateModified = "date_modified"
+// kLanguageCode = "language_code"
+// kLabel = "label"
+constexpr std::string_view kInitialCreatorId = "initial_creator_id";
+constexpr std::string_view kLastModifierId = "last_modifier_id";
+
+constexpr std::string_view kContactInfoTypeTokensTable =
+    "contact_info_type_tokens";
+constexpr std::string_view kLocalAddressesTypeTokensTable =
+    "local_addresses_type_tokens";
+// kGuid = "guid"
+constexpr std::string_view kType = "type";
+constexpr std::string_view kValue = "value";
+constexpr std::string_view kVerificationStatus = "verification_status";
+constexpr std::string_view kObservations = "observations";
+
+// Truncates `data` to the maximum length that can be stored in a column of the
+// Autofill database. Shorter strings are left as-is.
+std::u16string Truncate(const std::u16string& data) {
+  return data.substr(0, AddressAutofillTable::kMaxDataLength);
+}
+
+void AddAutofillProfileDetailsFromStatement(sql::Statement& s,
+                                            AutofillProfile* profile) {
+  int index = 0;
+  for (FieldType type :
+       {COMPANY_NAME, ADDRESS_HOME_STREET_ADDRESS,
+        ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE,
+        ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE, ADDRESS_HOME_COUNTRY}) {
+    profile->SetRawInfo(type, s.ColumnString16(index++));
+  }
+  profile->set_use_count(s.ColumnInt64(index++));
+  profile->set_use_date(base::Time::FromTimeT(s.ColumnInt64(index++)));
+  profile->set_modification_date(base::Time::FromTimeT(s.ColumnInt64(index++)));
+  profile->set_language_code(s.ColumnString(index++));
+  profile->set_profile_label(s.ColumnString(index++));
+}
+
+bool AddAutofillProfileNamesToProfile(sql::Database* db,
+                                      AutofillProfile* profile) {
+  if (!db->DoesTableExist(kAutofillProfileNamesTable)) {
+    return false;
+  }
+  sql::Statement s;
+  if (SelectByGuid(
+          db, s, kAutofillProfileNamesTable,
+          {kGuid, kHonorificPrefix, kHonorificPrefixStatus, kFirstName,
+           kFirstNameStatus, kMiddleName, kMiddleNameStatus, kFirstLastName,
+           kFirstLastNameStatus, kConjunctionLastName,
+           kConjunctionLastNameStatus, kSecondLastName, kSecondLastNameStatus,
+           kLastName, kLastNameStatus, kFullName, kFullNameStatus,
+           kFullNameWithHonorificPrefix, kFullNameWithHonorificPrefixStatus},
+          profile->guid())) {
+    DCHECK_EQ(profile->guid(), s.ColumnString(0));
+
+    int index = 1;
+    for (FieldType type :
+         {NAME_HONORIFIC_PREFIX, NAME_FIRST, NAME_MIDDLE, NAME_LAST_FIRST,
+          NAME_LAST_CONJUNCTION, NAME_LAST_SECOND, NAME_LAST, NAME_FULL,
+          NAME_FULL_WITH_HONORIFIC_PREFIX}) {
+      profile->SetRawInfoWithVerificationStatusInt(
+          type, s.ColumnString16(index), s.ColumnInt(index + 1));
+      index += 2;
+    }
+  }
+  return s.Succeeded();
+}
+
+bool AddAutofillProfileAddressesToProfile(sql::Database* db,
+                                          AutofillProfile* profile) {
+  if (!db->DoesTableExist(kAutofillProfileAddressesTable)) {
+    return false;
+  }
+  sql::Statement s;
+  if (SelectByGuid(db, s, kAutofillProfileAddressesTable,
+                   {kGuid,
+                    kStreetAddress,
+                    kStreetAddressStatus,
+                    kStreetName,
+                    kStreetNameStatus,
+                    kHouseNumber,
+                    kHouseNumberStatus,
+                    kSubpremise,
+                    kSubpremiseStatus,
+                    kDependentLocality,
+                    kDependentLocalityStatus,
+                    kCity,
+                    kCityStatus,
+                    kState,
+                    kStateStatus,
+                    kZipCode,
+                    kZipCodeStatus,
+                    kSortingCode,
+                    kSortingCodeStatus,
+                    kCountryCode,
+                    kCountryCodeStatus,
+                    kApartmentNumber,
+                    kApartmentNumberStatus,
+                    kFloor,
+                    kFloorStatus},
+                   profile->guid())) {
+    DCHECK_EQ(profile->guid(), s.ColumnString(0));
+    std::u16string street_address = s.ColumnString16(1);
+    std::u16string dependent_locality = s.ColumnString16(13);
+    std::u16string city = s.ColumnString16(15);
+    std::u16string state = s.ColumnString16(17);
+    std::u16string zip_code = s.ColumnString16(19);
+    std::u16string sorting_code = s.ColumnString16(21);
+    std::u16string country = s.ColumnString16(23);
+
+    std::u16string street_address_legacy =
+        profile->GetRawInfo(ADDRESS_HOME_STREET_ADDRESS);
+    std::u16string dependent_locality_legacy =
+        profile->GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY);
+    std::u16string city_legacy = profile->GetRawInfo(ADDRESS_HOME_CITY);
+    std::u16string state_legacy = profile->GetRawInfo(ADDRESS_HOME_STATE);
+    std::u16string zip_code_legacy = profile->GetRawInfo(ADDRESS_HOME_ZIP);
+    std::u16string sorting_code_legacy =
+        profile->GetRawInfo(ADDRESS_HOME_SORTING_CODE);
+    std::u16string country_legacy = profile->GetRawInfo(ADDRESS_HOME_COUNTRY);
+
+    // At this stage, the unstructured address was already written to
+    // the profile. If the address was changed by a legacy client, the
+    // information diverged from the one in this table that is only written by
+    // new clients. In this case remove the corresponding row from this table.
+    // Otherwise, read the new structured tokens and set the verification
+    // statuses for all tokens.
+    if (street_address == street_address_legacy &&
+        dependent_locality == dependent_locality_legacy &&
+        city == city_legacy && state == state_legacy &&
+        zip_code == zip_code_legacy && sorting_code == sorting_code_legacy &&
+        country == country_legacy) {
+      int index = 1;
+      for (FieldType type :
+           {ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_STREET_NAME,
+            ADDRESS_HOME_HOUSE_NUMBER, ADDRESS_HOME_SUBPREMISE,
+            ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_CITY,
+            ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE,
+            ADDRESS_HOME_COUNTRY, ADDRESS_HOME_APT_NUM, ADDRESS_HOME_FLOOR}) {
+        profile->SetRawInfoWithVerificationStatusInt(
+            type, s.ColumnString16(index), s.ColumnInt(index + 1));
+        index += 2;
+      }
+    } else {
+      // Remove the structured information from the table for
+      // eventual deletion consistency.
+      DeleteWhereColumnEq(db, kAutofillProfileAddressesTable, kGuid,
+                          profile->guid());
+    }
+  }
+  return s.Succeeded();
+}
+
+bool AddAutofillProfileEmailsToProfile(sql::Database* db,
+                                       AutofillProfile* profile) {
+  if (!db->DoesTableExist(kAutofillProfileEmailsTable)) {
+    return false;
+  }
+  // TODO(estade): update schema so that multiple emails are not associated
+  // per unique profile guid. Please refer https://crbug.com/497934.
+  sql::Statement s;
+  if (SelectByGuid(db, s, kAutofillProfileEmailsTable, {kGuid, kEmail},
+                   profile->guid())) {
+    DCHECK_EQ(profile->guid(), s.ColumnString(0));
+    profile->SetRawInfo(EMAIL_ADDRESS, s.ColumnString16(1));
+  }
+  return s.Succeeded();
+}
+
+bool AddAutofillProfilePhonesToProfile(sql::Database* db,
+                                       AutofillProfile* profile) {
+  if (!db->DoesTableExist(kAutofillProfilePhonesTable)) {
+    return false;
+  }
+  // TODO(estade): update schema so that multiple phone numbers are not
+  // associated per unique profile guid. Please refer
+  // https://crbug.com/497934.
+  sql::Statement s;
+  if (SelectByGuid(db, s, kAutofillProfilePhonesTable, {kGuid, kNumber},
+                   profile->guid())) {
+    DCHECK_EQ(profile->guid(), s.ColumnString(0));
+    profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, s.ColumnString16(1));
+  }
+  return s.Succeeded();
+}
+
+bool AddAutofillProfileBirthdateToProfile(sql::Database* db,
+                                          AutofillProfile* profile) {
+  if (!db->DoesTableExist(kAutofillProfileBirthdatesTable)) {
+    return false;
+  }
+  sql::Statement s;
+  if (SelectByGuid(db, s, kAutofillProfileBirthdatesTable,
+                   {kGuid, kDay, kMonth, kYear}, profile->guid())) {
+    DCHECK_EQ(profile->guid(), s.ColumnString(0));
+    profile->SetRawInfoAsInt(BIRTHDATE_DAY, s.ColumnInt(1));
+    profile->SetRawInfoAsInt(BIRTHDATE_MONTH, s.ColumnInt(2));
+    profile->SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, s.ColumnInt(3));
+  }
+  return s.Succeeded();
+}
+
+WebDatabaseTable::TypeKey GetKey() {
+  // We just need a unique constant. Use the address of a static that
+  // COMDAT folding won't touch in an optimizing linker.
+  static int table_key = 0;
+  return reinterpret_cast<void*>(&table_key);
+}
+
+time_t GetEndTime(const base::Time& end) {
+  if (end.is_null() || end == base::Time::Max()) {
+    return std::numeric_limits<time_t>::max();
+  }
+
+  return end.ToTimeT();
+}
+
+// This helper function binds the `profile`s properties to the placeholders in
+// `s`, in the order the columns are defined in the header file.
+void BindAutofillProfileToStatement(const AutofillProfile& profile,
+                                    sql::Statement& s) {
+  int index = 0;
+  s.BindString(index++, profile.guid());
+  s.BindInt64(index++, profile.use_count());
+  s.BindInt64(index++, profile.use_date().ToTimeT());
+  s.BindInt64(index++, profile.modification_date().ToTimeT());
+  s.BindString(index++, profile.language_code());
+  s.BindString(index++, profile.profile_label());
+  s.BindInt(index++, profile.initial_creator_id());
+  s.BindInt(index++, profile.last_modifier_id());
+}
+
+// Local and account profiles are stored in different tables with the same
+// layout. One table contains profile-level metadata, while another table
+// contains the values for every relevant FieldType. The following two
+// functions are used to map from a profile's `source` to the correct table.
+std::string_view GetProfileMetadataTable(AutofillProfile::Source source) {
+  switch (source) {
+    case AutofillProfile::Source::kLocalOrSyncable:
+      return kLocalAddressesTable;
+    case AutofillProfile::Source::kAccount:
+      return kContactInfoTable;
+  }
+  NOTREACHED_NORETURN();
+}
+std::string_view GetProfileTypeTokensTable(AutofillProfile::Source source) {
+  switch (source) {
+    case AutofillProfile::Source::kLocalOrSyncable:
+      return kLocalAddressesTypeTokensTable;
+    case AutofillProfile::Source::kAccount:
+      return kContactInfoTypeTokensTable;
+  }
+  NOTREACHED_NORETURN();
+}
+
+// Inserts `profile` into `GetProfileMetadataTable()` and
+// `GetProfileTypeTokensTable()`, depending on the profile's source.
+bool AddAutofillProfileToTable(sql::Database* db,
+                               const AutofillProfile& profile) {
+  sql::Statement s;
+  InsertBuilder(db, s, GetProfileMetadataTable(profile.source()),
+                {kGuid, kUseCount, kUseDate, kDateModified, kLanguageCode,
+                 kLabel, kInitialCreatorId, kLastModifierId});
+  BindAutofillProfileToStatement(profile, s);
+  if (!s.Run()) {
+    return false;
+  }
+  for (FieldType type : GetDatabaseStoredTypesOfAutofillProfile()) {
+    if (!base::FeatureList::IsEnabled(
+            features::kAutofillEnableSupportForAddressOverflowAndLandmark) &&
+        type == ADDRESS_HOME_OVERFLOW_AND_LANDMARK) {
+      continue;
+    }
+    if (!base::FeatureList::IsEnabled(
+            features::kAutofillEnableSupportForBetweenStreetsOrLandmark) &&
+        type == ADDRESS_HOME_BETWEEN_STREETS_OR_LANDMARK) {
+      continue;
+    }
+
+    if (!base::FeatureList::IsEnabled(
+            features::kAutofillEnableSupportForAddressOverflow) &&
+        type == ADDRESS_HOME_OVERFLOW) {
+      continue;
+    }
+    if (!base::FeatureList::IsEnabled(
+            features::kAutofillEnableSupportForLandmark) &&
+        type == ADDRESS_HOME_LANDMARK) {
+      continue;
+    }
+    if (!base::FeatureList::IsEnabled(
+            features::kAutofillEnableSupportForBetweenStreets) &&
+        (type == ADDRESS_HOME_BETWEEN_STREETS ||
+         type == ADDRESS_HOME_BETWEEN_STREETS_1 ||
+         type == ADDRESS_HOME_BETWEEN_STREETS_2)) {
+      continue;
+    }
+    if (!base::FeatureList::IsEnabled(
+            features::kAutofillEnableSupportForAdminLevel2) &&
+        type == ADDRESS_HOME_ADMIN_LEVEL2) {
+      continue;
+    }
+    InsertBuilder(db, s, GetProfileTypeTokensTable(profile.source()),
+                  {kGuid, kType, kValue, kVerificationStatus, kObservations});
+    s.BindString(0, profile.guid());
+    s.BindInt(1, type);
+    s.BindString16(2, Truncate(profile.GetRawInfo(type)));
+    s.BindInt(3, profile.GetVerificationStatusInt(type));
+    s.BindBlob(
+        4, profile.token_quality().SerializeObservationsForStoredType(type));
+    if (!s.Run()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// `MigrateToVersion113MigrateLocalAddressProfilesToNewTable()` migrates
+// profiles from one table layout to another. This function inserts the given
+// `profile` into the `GetProfileMetadataTable()` of schema version 113.
+// `AddAutofillProfileToTable()` can't be reused, since the schema can change in
+// future database versions in ways incompatible with version 113 (e.g. adding
+// a column).
+// The code was copied from `AddAutofillProfileToTable()` in version 113. Like
+// the migration logic, it shouldn't be changed.
+bool AddAutofillProfileToTableVersion113(sql::Database* db,
+                                         const AutofillProfile& profile) {
+  sql::Statement s;
+  InsertBuilder(db, s, GetProfileMetadataTable(profile.source()),
+                {kGuid, kUseCount, kUseDate, kDateModified, kLanguageCode,
+                 kLabel, kInitialCreatorId, kLastModifierId});
+  BindAutofillProfileToStatement(profile, s);
+  if (!s.Run()) {
+    return false;
+  }
+  // Note that `GetDatabaseStoredTypesOfAutofillProfile()` might change in
+  // future versions. Due to the flexible layout of the type tokens table, this
+  // is not a problem.
+  for (FieldType type : GetDatabaseStoredTypesOfAutofillProfile()) {
+    InsertBuilder(db, s, GetProfileTypeTokensTable(profile.source()),
+                  {kGuid, kType, kValue, kVerificationStatus});
+    s.BindString(0, profile.guid());
+    s.BindInt(1, type);
+    s.BindString16(2, Truncate(profile.GetRawInfo(type)));
+    s.BindInt(3, profile.GetVerificationStatusInt(type));
+    if (!s.Run()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace
+
+// static
+const size_t AddressAutofillTable::kMaxDataLength = 1024;
+
+AddressAutofillTable::AddressAutofillTable() = default;
+
+AddressAutofillTable::~AddressAutofillTable() = default;
+
+// static
+AddressAutofillTable* AddressAutofillTable::FromWebDatabase(WebDatabase* db) {
+  return static_cast<AddressAutofillTable*>(db->GetTable(GetKey()));
+}
+
+WebDatabaseTable::TypeKey AddressAutofillTable::GetTypeKey() const {
+  return GetKey();
+}
+
+bool AddressAutofillTable::CreateTablesIfNecessary() {
+  return InitProfileMetadataTable(AutofillProfile::Source::kAccount) &&
+         InitProfileTypeTokensTable(AutofillProfile::Source::kAccount) &&
+         InitProfileMetadataTable(AutofillProfile::Source::kLocalOrSyncable) &&
+         InitProfileTypeTokensTable(AutofillProfile::Source::kLocalOrSyncable);
+}
+
+bool AddressAutofillTable::MigrateToVersion(int version,
+                                            bool* update_compatible_version) {
+  if (!db_->is_open()) {
+    return false;
+  }
+  // Migrate if necessary.
+  switch (version) {
+    case 88:
+      *update_compatible_version = false;
+      return MigrateToVersion88AddNewNameColumns();
+    case 90:
+      *update_compatible_version = false;
+      return MigrateToVersion90AddNewStructuredAddressColumns();
+    case 91:
+      *update_compatible_version = false;
+      return MigrateToVersion91AddMoreStructuredAddressColumns();
+    case 92:
+      *update_compatible_version = false;
+      return MigrateToVersion92AddNewPrefixedNameColumn();
+    case 93:
+      *update_compatible_version = false;
+      return MigrateToVersion93AddAutofillProfileLabelColumn();
+    case 96:
+      *update_compatible_version = false;
+      return MigrateToVersion96AddAutofillProfileDisallowConfirmableMergesColumn();
+    case 99:
+      *update_compatible_version = true;
+      return MigrateToVersion99RemoveAutofillProfilesTrashTable();
+    case 100:
+      *update_compatible_version = true;
+      return MigrateToVersion100RemoveProfileValidityBitfieldColumn();
+    case 102:
+      *update_compatible_version = false;
+      return MigrateToVersion102AddAutofillBirthdatesTable();
+    case 107:
+      *update_compatible_version = false;
+      return MigrateToVersion107AddContactInfoTables();
+    case 110:
+      *update_compatible_version = false;
+      return MigrateToVersion110AddInitialCreatorIdAndLastModifierId();
+    case 113:
+      *update_compatible_version = false;
+      return MigrateToVersion113MigrateLocalAddressProfilesToNewTable();
+    case 114:
+      *update_compatible_version = true;
+      return MigrateToVersion114DropLegacyAddressTables();
+    case 117:
+      *update_compatible_version = false;
+      return MigrateToVersion117AddProfileObservationColumn();
+    case 121:
+      *update_compatible_version = true;
+      return MigrateToVersion121DropServerAddressTables();
+  }
+  return true;
+}
+
+bool AddressAutofillTable::AddAutofillProfile(const AutofillProfile& profile) {
+  sql::Transaction transaction(db_);
+  return transaction.Begin() && AddAutofillProfileToTable(db_, profile) &&
+         transaction.Commit();
+}
+
+bool AddressAutofillTable::UpdateAutofillProfile(
+    const AutofillProfile& profile) {
+  DCHECK(base::Uuid::ParseCaseInsensitive(profile.guid()).is_valid());
+
+  std::unique_ptr<AutofillProfile> old_profile =
+      GetAutofillProfile(profile.guid(), profile.source());
+  if (!old_profile) {
+    return false;
+  }
+
+  // Implementing an update as remove + add has multiple advantages:
+  // - Prevents outdated (FieldType, value) pairs from remaining in the
+  //   `GetProfileTypeTokensTable(profile)`, in case field types are removed.
+  // - Simpler code.
+  // The possible downside is performance. This is not an issue, as updates
+  // happen rarely and asynchronously.
+  sql::Transaction transaction(db_);
+  return transaction.Begin() &&
+         RemoveAutofillProfile(profile.guid(), profile.source()) &&
+         AddAutofillProfileToTable(db_, profile) && transaction.Commit();
+}
+
+bool AddressAutofillTable::RemoveAutofillProfile(
+    const std::string& guid,
+    AutofillProfile::Source profile_source) {
+  DCHECK(base::Uuid::ParseCaseInsensitive(guid).is_valid());
+  sql::Transaction transaction(db_);
+  return transaction.Begin() &&
+         DeleteWhereColumnEq(db_, GetProfileMetadataTable(profile_source),
+                             kGuid, guid) &&
+         DeleteWhereColumnEq(db_, GetProfileTypeTokensTable(profile_source),
+                             kGuid, guid) &&
+         transaction.Commit();
+}
+
+bool AddressAutofillTable::RemoveAllAutofillProfiles(
+    AutofillProfile::Source profile_source) {
+  sql::Transaction transaction(db_);
+  return transaction.Begin() &&
+         Delete(db_, GetProfileMetadataTable(profile_source)) &&
+         Delete(db_, GetProfileTypeTokensTable(profile_source)) &&
+         transaction.Commit();
+}
+
+std::unique_ptr<AutofillProfile> AddressAutofillTable::GetAutofillProfile(
+    const std::string& guid,
+    AutofillProfile::Source profile_source) const {
+  DCHECK(base::Uuid::ParseCaseInsensitive(guid).is_valid());
+  sql::Statement s;
+  if (!SelectByGuid(db_, s, GetProfileMetadataTable(profile_source),
+                    {kUseCount, kUseDate, kDateModified, kLanguageCode, kLabel,
+                     kInitialCreatorId, kLastModifierId},
+                    guid)) {
+    return nullptr;
+  }
+
+  int index = 0;
+  const int64_t use_count = s.ColumnInt64(index++);
+  const base::Time use_date = base::Time::FromTimeT(s.ColumnInt64(index++));
+  const base::Time modification_date =
+      base::Time::FromTimeT(s.ColumnInt64(index++));
+  const std::string language_code = s.ColumnString(index++);
+  const std::string profile_label = s.ColumnString(index++);
+  const int creator_id = s.ColumnInt(index++);
+  const int modifier_id = s.ColumnInt(index++);
+
+  if (!SelectByGuid(db_, s, GetProfileTypeTokensTable(profile_source),
+                    {kType, kValue, kVerificationStatus, kObservations},
+                    guid)) {
+    return nullptr;
+  }
+
+  struct FieldTypeData {
+    // Type corresponding to the data entry.
+    FieldType type;
+    // Value corresponding to the entry type.
+    std::u16string value;
+    // VerificationStatus of the data entry's `value`.
+    int status;
+    // Serialized observations for the stored type.
+    std::vector<uint8_t> serialized_data;
+  };
+
+  std::vector<FieldTypeData> field_type_values;
+  std::string country_code;
+  // As `SelectByGuid()` already calls `s.Step()`, do-while is used here.
+  do {
+    FieldType type = ToSafeFieldType(s.ColumnInt(0), UNKNOWN_TYPE);
+    if (type == UNKNOWN_TYPE) {
+      // This is possible in two cases:
+      // - The database was tampered with by external means.
+      // - The type corresponding to `s.ColumnInt(0)` was deprecated. In this
+      //   case, due to the structure of
+      //   `GetProfileTypeTokensTable(profile_source)`, it is not necessary to
+      //   add database migration logic or drop a column. Instead, during the
+      //   next update, the data will be dropped.
+      continue;
+    }
+
+    base::span<const uint8_t> observations_data = s.ColumnBlob(3);
+    field_type_values.emplace_back(
+        type, s.ColumnString16(1), s.ColumnInt(2),
+        std::vector<uint8_t>(observations_data.begin(),
+                             observations_data.end()));
+
+    if (type == ADDRESS_HOME_COUNTRY) {
+      country_code = base::UTF16ToUTF8(s.ColumnString16(1));
+    }
+
+  } while (s.Step());
+
+  // TODO(crbug.com/1464568): Define a proper migration strategy from stored
+  // legacy profiles into i18n ones.
+  auto profile = std::make_unique<AutofillProfile>(
+      guid, profile_source, AddressCountryCode(country_code));
+  profile->set_use_count(use_count);
+  profile->set_use_date(use_date);
+  profile->set_modification_date(modification_date);
+  profile->set_language_code(language_code);
+  profile->set_profile_label(profile_label);
+  profile->set_initial_creator_id(creator_id);
+  profile->set_last_modifier_id(modifier_id);
+
+  for (const auto& data : field_type_values) {
+    profile->SetRawInfoWithVerificationStatusInt(data.type, data.value,
+                                                 data.status);
+    profile->token_quality().LoadSerializedObservationsForStoredType(
+        data.type, data.serialized_data);
+  }
+
+  profile->FinalizeAfterImport();
+  return profile;
+}
+
+bool AddressAutofillTable::GetAutofillProfiles(
+    AutofillProfile::Source profile_source,
+    std::vector<std::unique_ptr<AutofillProfile>>* profiles) const {
+  CHECK(profiles);
+  profiles->clear();
+
+  sql::Statement s;
+  SelectBuilder(db_, s, GetProfileMetadataTable(profile_source), {kGuid});
+  while (s.Step()) {
+    std::string guid = s.ColumnString(0);
+    std::unique_ptr<AutofillProfile> profile =
+        GetAutofillProfile(guid, profile_source);
+    if (!profile) {
+      continue;
+    }
+    profiles->push_back(std::move(profile));
+  }
+
+  return s.Succeeded();
+}
+
+std::unique_ptr<AutofillProfile>
+AddressAutofillTable::GetAutofillProfileFromLegacyTable(
+    const std::string& guid) const {
+  sql::Statement s;
+  if (!SelectByGuid(db_, s, kAutofillProfilesTable,
+                    {kCompanyName, kStreetAddress, kDependentLocality, kCity,
+                     kState, kZipcode, kSortingCode, kCountryCode, kUseCount,
+                     kUseDate, kDateModified, kLanguageCode, kLabel},
+                    guid)) {
+    return nullptr;
+  }
+
+  auto profile = std::make_unique<AutofillProfile>(
+      guid, AutofillProfile::Source::kLocalOrSyncable,
+      i18n_model_definition::kLegacyHierarchyCountryCode);
+
+  DCHECK(base::Uuid::ParseCaseInsensitive(profile->guid()).is_valid());
+
+  // Get associated name info using guid.
+  AddAutofillProfileNamesToProfile(db_, profile.get());
+
+  // Get associated email info using guid.
+  AddAutofillProfileEmailsToProfile(db_, profile.get());
+
+  // Get associated phone info using guid.
+  AddAutofillProfilePhonesToProfile(db_, profile.get());
+
+  // Get associated birthdate info using guid.
+  AddAutofillProfileBirthdateToProfile(db_, profile.get());
+
+  // The details should be added after the other info to make sure they don't
+  // change when we change the names/emails/phones.
+  AddAutofillProfileDetailsFromStatement(s, profile.get());
+
+  // The structured address information should be added after the street_address
+  // from the query above was  written because this information is used to
+  // detect changes by a legacy client.
+  AddAutofillProfileAddressesToProfile(db_, profile.get());
+
+  // For more-structured profiles, the profile must be finalized to fully
+  // populate the name fields.
+  profile->FinalizeAfterImport();
+
+  return profile;
+}
+
+// TODO(crbug.com/1443393): This function's implementation is very similar to
+// `GetAutofillProfiles()`. Simplify somehow.
+bool AddressAutofillTable::GetAutofillProfilesFromLegacyTable(
+    std::vector<std::unique_ptr<AutofillProfile>>* profiles) const {
+  DCHECK(profiles);
+  profiles->clear();
+
+  sql::Statement s;
+  SelectBuilder(db_, s, kAutofillProfilesTable, {kGuid});
+
+  while (s.Step()) {
+    std::string guid = s.ColumnString(0);
+    std::unique_ptr<AutofillProfile> profile =
+        GetAutofillProfileFromLegacyTable(guid);
+    if (!profile) {
+      continue;
+    }
+    profiles->push_back(std::move(profile));
+  }
+
+  return s.Succeeded();
+}
+
+bool AddressAutofillTable::ClearAllLocalData() {
+  sql::Transaction transaction(db_);
+  if (!transaction.Begin()) {
+    return false;  // Some error, nothing was changed.
+  }
+
+  RemoveAllAutofillProfiles(AutofillProfile::Source::kLocalOrSyncable);
+  bool changed = db_->GetLastChangeCount() > 0;
+
+  transaction.Commit();
+  return changed;
+}
+
+bool AddressAutofillTable::RemoveAutofillDataModifiedBetween(
+    const base::Time& delete_begin,
+    const base::Time& delete_end,
+    std::vector<std::unique_ptr<AutofillProfile>>* profiles) {
+  DCHECK(delete_end.is_null() || delete_begin < delete_end);
+
+  time_t delete_begin_t = delete_begin.ToTimeT();
+  time_t delete_end_t = GetEndTime(delete_end);
+
+  // Remember Autofill profiles in the time range.
+  sql::Statement s_profiles_get;
+  SelectBetween(
+      db_, s_profiles_get,
+      GetProfileMetadataTable(AutofillProfile::Source::kLocalOrSyncable),
+      {kGuid}, kDateModified, delete_begin_t, delete_end_t);
+
+  profiles->clear();
+  while (s_profiles_get.Step()) {
+    std::string guid = s_profiles_get.ColumnString(0);
+    std::unique_ptr<AutofillProfile> profile =
+        GetAutofillProfile(guid, AutofillProfile::Source::kLocalOrSyncable);
+    if (!profile) {
+      return false;
+    }
+    profiles->push_back(std::move(profile));
+  }
+  if (!s_profiles_get.Succeeded()) {
+    return false;
+  }
+
+  // Remove Autofill profiles in the time range.
+  for (const std::unique_ptr<AutofillProfile>& profile : *profiles) {
+    if (!RemoveAutofillProfile(profile->guid(),
+                               AutofillProfile::Source::kLocalOrSyncable)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool AddressAutofillTable::MigrateToVersion88AddNewNameColumns() {
+  for (std::string_view column : {kHonorificPrefix, kFirstLastName,
+                                  kConjunctionLastName, kSecondLastName}) {
+    if (!AddColumnIfNotExists(db_, kAutofillProfileNamesTable, column,
+                              "VARCHAR")) {
+      return false;
+    }
+  }
+
+  for (std::string_view column :
+       {kHonorificPrefixStatus, kFirstNameStatus, kMiddleNameStatus,
+        kLastNameStatus, kFirstLastNameStatus, kConjunctionLastNameStatus,
+        kSecondLastNameStatus, kFullNameStatus}) {
+    // The default value of 0 corresponds to the verification status
+    // |kNoStatus|.
+    if (!AddColumnIfNotExists(db_, kAutofillProfileNamesTable, column,
+                              "INTEGER DEFAULT 0")) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool AddressAutofillTable::MigrateToVersion92AddNewPrefixedNameColumn() {
+  return AddColumnIfNotExists(db_, kAutofillProfileNamesTable,
+                              kFullNameWithHonorificPrefix, "VARCHAR") &&
+         AddColumnIfNotExists(db_, kAutofillProfileNamesTable,
+                              kFullNameWithHonorificPrefixStatus,
+                              "INTEGER DEFAULT 0");
+}
+
+bool AddressAutofillTable::MigrateToVersion90AddNewStructuredAddressColumns() {
+  if (!db_->DoesTableExist("autofill_profile_addresses")) {
+    InitLegacyProfileAddressesTable();
+  }
+
+  for (std::string_view column : {kDependentLocality, kCity, kState, kZipCode,
+                                  kSortingCode, kCountryCode}) {
+    if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column,
+                              "VARCHAR")) {
+      return false;
+    }
+  }
+
+  for (std::string_view column :
+       {kDependentLocalityStatus, kCityStatus, kStateStatus, kZipCodeStatus,
+        kSortingCodeStatus, kCountryCodeStatus}) {
+    // The default value of 0 corresponds to the verification status
+    // |kNoStatus|.
+    if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column,
+                              "INTEGER DEFAULT 0")) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool AddressAutofillTable::MigrateToVersion91AddMoreStructuredAddressColumns() {
+  if (!db_->DoesTableExist(kAutofillProfileAddressesTable)) {
+    InitLegacyProfileAddressesTable();
+  }
+
+  for (std::string_view column : {kApartmentNumber, kFloor}) {
+    if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column,
+                              "VARCHAR")) {
+      return false;
+    }
+  }
+
+  for (std::string_view column : {kApartmentNumberStatus, kFloorStatus}) {
+    // The default value of 0 corresponds to the verification status
+    // |kNoStatus|.
+    if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column,
+                              "INTEGER DEFAULT 0")) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool AddressAutofillTable::MigrateToVersion93AddAutofillProfileLabelColumn() {
+  if (!db_->DoesTableExist(kAutofillProfilesTable)) {
+    InitLegacyProfileAddressesTable();
+  }
+
+  return AddColumnIfNotExists(db_, kAutofillProfilesTable, kLabel, "VARCHAR");
+}
+
+bool AddressAutofillTable::
+    MigrateToVersion96AddAutofillProfileDisallowConfirmableMergesColumn() {
+  if (!db_->DoesTableExist(kAutofillProfilesTable)) {
+    InitLegacyProfileAddressesTable();
+  }
+
+  return AddColumnIfNotExists(db_, kAutofillProfilesTable,
+                              kDisallowSettingsVisibleUpdates,
+                              "INTEGER NOT NULL DEFAULT 0");
+}
+
+bool AddressAutofillTable::
+    MigrateToVersion99RemoveAutofillProfilesTrashTable() {
+  return DropTableIfExists(db_, "autofill_profiles_trash");
+}
+
+bool AddressAutofillTable::
+    MigrateToVersion100RemoveProfileValidityBitfieldColumn() {
+  sql::Transaction transaction(db_);
+  return transaction.Begin() &&
+         DropColumn(db_, kAutofillProfilesTable, "validity_bitfield") &&
+         DropColumn(db_, kAutofillProfilesTable,
+                    "is_client_validity_states_updated") &&
+         transaction.Commit();
+}
+
+bool AddressAutofillTable::MigrateToVersion102AddAutofillBirthdatesTable() {
+  return CreateTable(db_, kAutofillProfileBirthdatesTable,
+                     {{kGuid, "VARCHAR"},
+                      {kDay, "INTEGER DEFAULT 0"},
+                      {kMonth, "INTEGER DEFAULT 0"},
+                      {kYear, "INTEGER DEFAULT 0"}});
+}
+
+bool AddressAutofillTable::MigrateToVersion107AddContactInfoTables() {
+  sql::Transaction transaction(db_);
+  return transaction.Begin() &&
+         CreateTable(db_, kContactInfoTable,
+                     {{kGuid, "VARCHAR PRIMARY KEY"},
+                      {kUseCount, "INTEGER NOT NULL DEFAULT 0"},
+                      {kUseDate, "INTEGER NOT NULL DEFAULT 0"},
+                      {kDateModified, "INTEGER NOT NULL DEFAULT 0"},
+                      {kLanguageCode, "VARCHAR"},
+                      {kLabel, "VARCHAR"}}) &&
+         CreateTable(db_, kContactInfoTypeTokensTable,
+                     {{kGuid, "VARCHAR"},
+                      {kType, "INTEGER"},
+                      {kValue, "VARCHAR"},
+                      {kVerificationStatus, "INTEGER DEFAULT 0"}},
+                     /*composite_primary_key=*/{kGuid, kType}) &&
+         transaction.Commit();
+}
+
+bool AddressAutofillTable::
+    MigrateToVersion110AddInitialCreatorIdAndLastModifierId() {
+  if (!db_->DoesTableExist(kContactInfoTable)) {
+    return false;
+  }
+  sql::Transaction transaction(db_);
+  return transaction.Begin() &&
+         AddColumnIfNotExists(db_, kContactInfoTable, kInitialCreatorId,
+                              "INTEGER DEFAULT 0") &&
+         AddColumnIfNotExists(db_, kContactInfoTable, kLastModifierId,
+                              "INTEGER DEFAULT 0") &&
+         transaction.Commit();
+}
+
+bool AddressAutofillTable::
+    MigrateToVersion113MigrateLocalAddressProfilesToNewTable() {
+  sql::Transaction transaction(db_);
+  if (!transaction.Begin() ||
+      !CreateTableIfNotExists(db_, kLocalAddressesTable,
+                              {{kGuid, "VARCHAR PRIMARY KEY"},
+                               {kUseCount, "INTEGER NOT NULL DEFAULT 0"},
+                               {kUseDate, "INTEGER NOT NULL DEFAULT 0"},
+                               {kDateModified, "INTEGER NOT NULL DEFAULT 0"},
+                               {kLanguageCode, "VARCHAR"},
+                               {kLabel, "VARCHAR"},
+                               {kInitialCreatorId, "INTEGER DEFAULT 0"},
+                               {kLastModifierId, "INTEGER DEFAULT 0"}}) ||
+      !CreateTableIfNotExists(db_, kLocalAddressesTypeTokensTable,
+                              {{kGuid, "VARCHAR"},
+                               {kType, "INTEGER"},
+                               {kValue, "VARCHAR"},
+                               {kVerificationStatus, "INTEGER DEFAULT 0"}},
+                              /*composite_primary_key=*/{kGuid, kType})) {
+    return false;
+  }
+  bool success = true;
+  if (db_->DoesTableExist(kAutofillProfilesTable)) {
+    std::vector<std::unique_ptr<AutofillProfile>> profiles;
+    success = GetAutofillProfilesFromLegacyTable(&profiles);
+    // Migrate profiles to the new tables. Preserve the modification dates.
+    for (const std::unique_ptr<AutofillProfile>& profile : profiles) {
+      success = success && AddAutofillProfileToTableVersion113(db_, *profile);
+    }
+  }
+  // Delete all profiles from the legacy tables. The tables are dropped in
+  // version 114.
+  for (std::string_view deprecated_table :
+       {kAutofillProfilesTable, kAutofillProfileAddressesTable,
+        kAutofillProfileNamesTable, kAutofillProfileEmailsTable,
+        kAutofillProfilePhonesTable, kAutofillProfileBirthdatesTable}) {
+    success = success && (!db_->DoesTableExist(deprecated_table) ||
+                          Delete(db_, deprecated_table));
+  }
+  return success && transaction.Commit();
+}
+
+bool AddressAutofillTable::MigrateToVersion114DropLegacyAddressTables() {
+  sql::Transaction transaction(db_);
+  bool success = transaction.Begin();
+  for (std::string_view deprecated_table :
+       {kAutofillProfilesTable, kAutofillProfileAddressesTable,
+        kAutofillProfileNamesTable, kAutofillProfileEmailsTable,
+        kAutofillProfilePhonesTable, kAutofillProfileBirthdatesTable}) {
+    success = success && DropTableIfExists(db_, deprecated_table);
+  }
+  return success && transaction.Commit();
+}
+
+bool AddressAutofillTable::MigrateToVersion117AddProfileObservationColumn() {
+  sql::Transaction transaction(db_);
+  return transaction.Begin() &&
+         AddColumn(db_, kContactInfoTypeTokensTable, kObservations, "BLOB") &&
+         AddColumn(db_, kLocalAddressesTypeTokensTable, kObservations,
+                   "BLOB") &&
+         transaction.Commit();
+}
+
+bool AddressAutofillTable::MigrateToVersion121DropServerAddressTables() {
+  sql::Transaction transaction(db_);
+  return transaction.Begin() && DropTableIfExists(db_, "server_addresses") &&
+         DropTableIfExists(db_, "server_address_metadata") &&
+         transaction.Commit();
+}
+
+bool AddressAutofillTable::InitLegacyProfilesTable() {
+  return CreateTableIfNotExists(
+      db_, kAutofillProfilesTable,
+      {{kGuid, "VARCHAR PRIMARY KEY"},
+       {kCompanyName, "VARCHAR"},
+       {kStreetAddress, "VARCHAR"},
+       {kDependentLocality, "VARCHAR"},
+       {kCity, "VARCHAR"},
+       {kState, "VARCHAR"},
+       {kZipcode, "VARCHAR"},
+       {kSortingCode, "VARCHAR"},
+       {kCountryCode, "VARCHAR"},
+       {kDateModified, "INTEGER NOT NULL DEFAULT 0"},
+       {kOrigin, "VARCHAR DEFAULT ''"},
+       {kLanguageCode, "VARCHAR"},
+       {kUseCount, "INTEGER NOT NULL DEFAULT 0"},
+       {kUseDate, "INTEGER NOT NULL DEFAULT 0"},
+       {kLabel, "VARCHAR"},
+       {kDisallowSettingsVisibleUpdates, "INTEGER NOT NULL DEFAULT 0"}});
+}
+
+bool AddressAutofillTable::InitLegacyProfileNamesTable() {
+  // The default value of 0 corresponds to the verification status
+  // |kNoStatus|.
+  return CreateTableIfNotExists(
+      db_, kAutofillProfileNamesTable,
+      {{kGuid, "VARCHAR"},
+       {kFirstName, "VARCHAR"},
+       {kMiddleName, "VARCHAR"},
+       {kLastName, "VARCHAR"},
+       {kFullName, "VARCHAR"},
+       {kHonorificPrefix, "VARCHAR"},
+       {kFirstLastName, "VARCHAR"},
+       {kConjunctionLastName, "VARCHAR"},
+       {kSecondLastName, "VARCHAR"},
+       {kHonorificPrefixStatus, "INTEGER DEFAULT 0"},
+       {kFirstNameStatus, "INTEGER DEFAULT 0"},
+       {kMiddleNameStatus, "INTEGER DEFAULT 0"},
+       {kLastNameStatus, "INTEGER DEFAULT 0"},
+       {kFirstLastNameStatus, "INTEGER DEFAULT 0"},
+       {kConjunctionLastNameStatus, "INTEGER DEFAULT 0"},
+       {kSecondLastNameStatus, "INTEGER DEFAULT 0"},
+       {kFullNameStatus, "INTEGER DEFAULT 0"},
+       {kFullNameWithHonorificPrefix, "VARCHAR"},
+       {kFullNameWithHonorificPrefixStatus, "INTEGER DEFAULT 0"}});
+}
+
+bool AddressAutofillTable::InitLegacyProfileAddressesTable() {
+  // The default value of 0 corresponds to the verification status
+  // |kNoStatus|.
+  return CreateTableIfNotExists(
+      db_, kAutofillProfileAddressesTable,
+      {{kGuid, "VARCHAR"},
+       {kStreetAddress, "VARCHAR"},
+       {kStreetName, "VARCHAR"},
+       {kDependentStreetName, "VARCHAR"},
+       {kHouseNumber, "VARCHAR"},
+       {kSubpremise, "VARCHAR"},
+       {"premise_name", "VARCHAR"},
+       {kStreetAddressStatus, "INTEGER DEFAULT 0"},
+       {kStreetNameStatus, "INTEGER DEFAULT 0"},
+       {kDependentStreetNameStatus, "INTEGER DEFAULT 0"},
+       {kHouseNumberStatus, "INTEGER DEFAULT 0"},
+       {kSubpremiseStatus, "INTEGER DEFAULT 0"},
+       {"premise_name_status", "INTEGER DEFAULT 0"},
+       {kDependentLocality, "VARCHAR"},
+       {kCity, "VARCHAR"},
+       {kState, "VARCHAR"},
+       {kZipCode, "VARCHAR"},
+       {kSortingCode, "VARCHAR"},
+       {kCountryCode, "VARCHAR"},
+       {kDependentLocalityStatus, "INTEGER DEFAULT 0"},
+       {kCityStatus, "INTEGER DEFAULT 0"},
+       {kStateStatus, "INTEGER DEFAULT 0"},
+       {kZipCodeStatus, "INTEGER DEFAULT 0"},
+       {kSortingCodeStatus, "INTEGER DEFAULT 0"},
+       {kCountryCodeStatus, "INTEGER DEFAULT 0"},
+       {kApartmentNumber, "VARCHAR"},
+       {kFloor, "VARCHAR"},
+       {kApartmentNumberStatus, "INTEGER DEFAULT 0"},
+       {kFloorStatus, "INTEGER DEFAULT 0"}});
+}
+
+bool AddressAutofillTable::InitLegacyProfileEmailsTable() {
+  return CreateTableIfNotExists(db_, kAutofillProfileEmailsTable,
+                                {{kGuid, "VARCHAR"}, {kEmail, "VARCHAR"}});
+}
+
+bool AddressAutofillTable::InitLegacyProfilePhonesTable() {
+  return CreateTableIfNotExists(db_, kAutofillProfilePhonesTable,
+                                {{kGuid, "VARCHAR"}, {kNumber, "VARCHAR"}});
+}
+
+bool AddressAutofillTable::InitLegacyProfileBirthdatesTable() {
+  return CreateTableIfNotExists(db_, kAutofillProfileBirthdatesTable,
+                                {{kGuid, "VARCHAR"},
+                                 {kDay, "INTEGER DEFAULT 0"},
+                                 {kMonth, "INTEGER DEFAULT 0"},
+                                 {kYear, "INTEGER DEFAULT 0"}});
+}
+
+bool AddressAutofillTable::InitProfileMetadataTable(
+    AutofillProfile::Source source) {
+  return CreateTableIfNotExists(db_, GetProfileMetadataTable(source),
+                                {{kGuid, "VARCHAR PRIMARY KEY"},
+                                 {kUseCount, "INTEGER NOT NULL DEFAULT 0"},
+                                 {kUseDate, "INTEGER NOT NULL DEFAULT 0"},
+                                 {kDateModified, "INTEGER NOT NULL DEFAULT 0"},
+                                 {kLanguageCode, "VARCHAR"},
+                                 {kLabel, "VARCHAR"},
+                                 {kInitialCreatorId, "INTEGER DEFAULT 0"},
+                                 {kLastModifierId, "INTEGER DEFAULT 0"}});
+}
+
+bool AddressAutofillTable::InitProfileTypeTokensTable(
+    AutofillProfile::Source source) {
+  return CreateTableIfNotExists(db_, GetProfileTypeTokensTable(source),
+                                {{kGuid, "VARCHAR"},
+                                 {kType, "INTEGER"},
+                                 {kValue, "VARCHAR"},
+                                 {kVerificationStatus, "INTEGER DEFAULT 0"},
+                                 {kObservations, "BLOB"}},
+                                /*composite_primary_key=*/{kGuid, kType});
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/addresses/address_autofill_table.h b/components/autofill/core/browser/webdata/addresses/address_autofill_table.h
new file mode 100644
index 0000000..f6156856
--- /dev/null
+++ b/components/autofill/core/browser/webdata/addresses/address_autofill_table.h
@@ -0,0 +1,342 @@
+// 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 COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_ADDRESSES_ADDRESS_AUTOFILL_TABLE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_ADDRESSES_ADDRESS_AUTOFILL_TABLE_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/time/time.h"
+#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/webdata/common/web_database_table.h"
+
+class WebDatabase;
+
+namespace autofill {
+
+// This class manages the various address Autofill tables within the SQLite
+// database passed to the constructor. It expects the following schemas:
+//
+// Note: The database stores time in seconds, UTC.
+//
+// DEPRECATED. Use local_addresses instead.
+// autofill_profiles    This table contains Autofill profile data added by the
+//                      user with the Autofill dialog.  Most of the columns are
+//                      standard entries in a contact information form.
+//
+//   guid               A guid string to uniquely identify the profile.
+//                      Added in version 31.
+//   label              A user-chosen and user-visible label for the profile to
+//                      help identifying the semantics of the profile. The user
+//                      can choose an arbitrary string in principle, but the
+//                      values '$HOME$' and '$WORK$' indicate a special meaning.
+//   company_name
+//   street_address     The combined lines of the street address.
+//                      Added in version 54.
+//   dependent_locality
+//                      A sub-classification beneath the city, e.g. an
+//                      inner-city district or suburb.  Added in version 54.
+//   city
+//   state
+//   zipcode
+//   sorting_code       Similar to the zipcode column, but used for businesses
+//                      or organizations that might not be geographically
+//                      contiguous.  The canonical example is CEDEX in France.
+//                      Added in version 54.
+//   country_code
+//   use_count          The number of times this profile has been used to fill
+//                      a form. Added in version 61.
+//   use_date           The date this profile was last used to fill a form,
+//                      in time_t. Added in version 61.
+//   date_modified      The date on which this profile was last modified, in
+//                      time_t. Added in version 30.
+//   origin             The domain of origin for this profile.
+//                      Added in version 50.
+//   language_code      The BCP 47 language code used to format the address for
+//                      display. For example, a JP address with "ja" language
+//                      code starts with the postal code, but a JP address with
+//                      "ja-latn" language code starts with the recipient name.
+//                      Added in version 56.
+//   disallow_settings_visible_updates
+//                      If true, a profile does not qualify to get merged with
+//                      a profile observed in a form submission.
+//
+// DEPRECATED. See autofill_profiles.
+// autofill_profile_addresses
+//   guid               The guid string that identifies the profile to which
+//                      the name belongs.
+//                      This table stores the structured address information.
+//   street_address     Stores the street address. This field is also stored in
+//                      the profile table and is used to detect if a legacy
+//                      client that does not support writing to this table
+//                      changed the address. If this is true, the address stored
+//                      in the table is removed.
+//   street_name        The name of the street.
+//   dependent_street_name
+//                      The name of the crossing street.
+//   house_number       The house number.
+//   subpremise         The floor, apartment number and staircase.
+//                      apartment number.
+//   dependent_locality
+//                      A sub-classification beneath the city, e.g. an
+//                      inner-city district or suburb.
+//   city               The city information of the address.
+//   state              The state information of the address.
+//   zip_code           The zip code of the address.
+//   country_code       The code of the country of the address.
+//   sorting_code       Similar to the zipcode column, but used for businesses
+//                      or organizations that might not be geographically
+//                      contiguous.
+//   premise_name       The name of the premise.
+//   apartment_number   The number of the apartment.
+//   floor              The floor in which the apartment is located.
+//   street_address_status
+//   street_name_status
+//   dependent_street_name_status
+//   house_number_status
+//   subpremise_status
+//   premise_name_status
+//   dependent_locality_status
+//   city_status
+//   state_status
+//   zip_code_status
+//   country_code_status
+//   sorting_code_status
+//   apartment_number_status
+//   floor_status
+//                      Each token of the address has an additional validation
+//                      status that indicates if Autofill parsed the value out
+//                      of an unstructured (last) name, or if autofill formatted
+//                      the token from its structured subcomponents, or if the
+//                      value was observed in a form submission, or even
+//                      validated by the user in the settings.
+//
+// DEPRECATED. See autofill_profiles.
+// autofill_profile_names
+//                      This table contains the multi-valued name fields
+//                      associated with a profile.
+//
+//   guid               The guid string that identifies the profile to which
+//                      the name belongs.
+//   honorific_prefix   The honorific prefix of a person like Ms, Mr or Prof
+//   first_name         The first name of a person.
+//   middle_name        The middle name or even names of a person.
+//   last_name          The unstructured last name that is a combination of the
+//                      first and second last name.
+//   first_last_name    The first part of the last name. Mostly used for
+//                      Latinx/Hispanic last names.
+//   conjunction_last_name
+//                      An optional conjunction that is mostly used in
+//                      Hispanic/Latinx last names in between the first and
+//                      second last name in the unstructured representation.
+//   second_last_name   The second part of the last names. Last names only
+//                      consisting of a single part are stored in the second
+//                      part by default.
+//   full_name          The unstructured full name of a person.
+//   full_name_with_honorific_prefix
+//                      The combination of the full name and the honorific
+//                      prefix.
+//   honorific_prefix_status
+//   first_name_status
+//   middle_name_status
+//   last_name_status
+//   first_last_name_status
+//   conjunction_last_name_status
+//   second_last_name_status
+//   full_name_status
+//   full_name_with_honorific_prefix_status
+//                      Each token of the names has an additional validation
+//                      status that indicates if Autofill parsed the value out
+//                      of an unstructured (last) name, or if autofill formatted
+//                      the token from its structured subcomponents, or if the
+//                      value was observed in a form submission, or even
+//                      validated by the user in the settings.
+//
+// DEPRECATED. See autofill_profiles.
+// autofill_profile_emails
+//                      This table contains the multi-valued email fields
+//                      associated with a profile.
+//
+//   guid               The guid string that identifies the profile to which
+//                      the email belongs.
+//   email
+//
+// DEPRECATED. See autofill_profiles.
+// autofill_profile_phones
+//                      This table contains the multi-valued phone fields
+//                      associated with a profile.
+//
+//   guid               The guid string that identifies the profile to which the
+//                      phone number belongs.
+//   number
+//
+// DEPRECATED. See autofill_profiles.
+// autofill_profile_birthdates
+//                      This table contains the multi-valued birthdate fields
+//                      associated with a profile.
+//
+//   guid               The guid string that identifies the profile to which the
+//                      birthdate number belongs.
+//   day                As an integer between 1 and 31 inclusive, or 0 if unset.
+//   month              As an integer between 1 and 12 inclusive, or 0 if unset.
+//   year               As a 4 digit integer, or 0 if unset.
+//
+// contact_info         This table contains Autofill profile data synced from a
+//                      remote source.
+// local_addresses      This table contains kLocalOrSyncable Autofill profiles.
+//                      It has the same layout as the contact_info table.
+//
+//   guid               A guid string to uniquely identify the profile.
+//   use_count          The number of times this profile has been used to fill a
+//                      form.
+//   use_date           The date this profile was last used to fill a form, in
+//                      time_t.
+//   date_modified      The date on which this profile was last modified, in
+//                      time_t.
+//   language_code      The BCP 47 language code used to format the address for
+//                      display. For example, a JP address with "ja" language
+//                      code starts with the postal code, but a JP address with
+//                      "ja-latn" language code starts with the recipient name.
+//   label              A user-chosen and user-visible label for the profile to
+//                      help identifying the semantics of the profile. The user
+//                      can choose an arbitrary string in principle, but the
+//                      values '$HOME$' and '$WORK$' indicate a special meaning.
+//   initial_creator_id The application that initially created the profile.
+//                      Represented as an integer. See AutofillProfile.
+//   last_modifier_id   The application that performed the last non-metadata
+//                      modification of the profile.
+//                      Represented as an integer. See AutofillProfile.
+//
+// contact_info_type_tokens
+//                      Contains the values for all relevant FieldTyps of a
+//                      contact_info entry. At most one entry per (guid, type)
+//                      pair exists.
+// local_addresses_type_tokens
+//                      Like contact_info_type_tokens, but for local_addresses.
+//
+//  guid                The guid of the corresponding profile in contact_info.
+//  type                The FieldType, represented by its integer value in
+//                      the FieldType enum.
+//  value               The string value of the type.
+//  verification_status Each token has an additional validation status that
+//                      indicates if Autofill parsed the value out of an
+//                      unstructured token, or if Autofill formatted the token
+//                      from a structured subcomponent, or if the value was
+//                      observed in a form submission, or even validated by the
+//                      user in the settings.
+//  observations        An encoding of the observations stored for this `type`.
+//                      See `ProfileTokenConfidence::
+//                      SerializeObservationsForStoredType()`.
+class AddressAutofillTable : public WebDatabaseTable {
+ public:
+  AddressAutofillTable();
+
+  AddressAutofillTable(const AddressAutofillTable&) = delete;
+  AddressAutofillTable& operator=(const AddressAutofillTable&) = delete;
+
+  ~AddressAutofillTable() override;
+
+  // Retrieves the AddressAutofillTable* owned by |db|.
+  static AddressAutofillTable* FromWebDatabase(WebDatabase* db);
+
+  // WebDatabaseTable:
+  WebDatabaseTable::TypeKey GetTypeKey() const override;
+  bool CreateTablesIfNecessary() override;
+  bool MigrateToVersion(int version, bool* update_compatible_version) override;
+
+  // Records a single Autofill profile in the autofill_profiles table.
+  virtual bool AddAutofillProfile(const AutofillProfile& profile);
+
+  // Updates the database values for the specified profile.  Multi-value aware.
+  virtual bool UpdateAutofillProfile(const AutofillProfile& profile);
+
+  // Removes the Autofill profile with the given `guid`. `profile_source`
+  // indicates where the profile was synced from and thus whether it is stored
+  // in `kAutofillProfilesTable` or `kContactInfoTable`.
+  virtual bool RemoveAutofillProfile(const std::string& guid,
+                                     AutofillProfile::Source profile_source);
+
+  // Removes all profiles from the given `profile_source`.
+  bool RemoveAllAutofillProfiles(AutofillProfile::Source profile_source);
+
+  // Retrieves a profile with guid `guid` from `kAutofillProfilesTable` or
+  // `kContactInfoTable`.
+  std::unique_ptr<AutofillProfile> GetAutofillProfile(
+      const std::string& guid,
+      AutofillProfile::Source profile_source) const;
+
+  // Retrieves profiles in the database. They are returned in unspecified order.
+  // The `profile_source` specifies if profiles from the legacy or the remote
+  // backend should be retrieved.
+  virtual bool GetAutofillProfiles(
+      AutofillProfile::Source profile_source,
+      std::vector<std::unique_ptr<AutofillProfile>>* profiles) const;
+
+  // Deletes all data from the local_addresses tables. Returns true if any data
+  // was deleted, false if not (so false means "commit not needed" rather than
+  // "error").
+  bool ClearAllLocalData();
+
+  // Removes rows from local_addresses tables if they were created on or after
+  // `delete_begin` and strictly before `delete_end`. Returns the list of
+  // of deleted profiles in `profiles`. Return value is true if all rows were
+  // successfully removed. Returns false on database error. In that case, the
+  // output vector state is undefined, and may be partially filled.
+  // TODO(crbug.com/1135188): This function is solely used to remove browsing
+  // data. Once explicit save dialogs are fully launched, it can be removed. For
+  // this reason profiles in the `contact_info` table are not considered.
+  bool RemoveAutofillDataModifiedBetween(
+      const base::Time& delete_begin,
+      const base::Time& delete_end,
+      std::vector<std::unique_ptr<AutofillProfile>>* profiles);
+
+  // Table migration functions. NB: These do not and should not rely on other
+  // functions in this class. The implementation of a function such as
+  // GetCreditCard may change over time, but MigrateToVersionXX should never
+  // change.
+  bool MigrateToVersion88AddNewNameColumns();
+  bool MigrateToVersion90AddNewStructuredAddressColumns();
+  bool MigrateToVersion91AddMoreStructuredAddressColumns();
+  bool MigrateToVersion92AddNewPrefixedNameColumn();
+  bool MigrateToVersion93AddAutofillProfileLabelColumn();
+  bool MigrateToVersion96AddAutofillProfileDisallowConfirmableMergesColumn();
+  bool MigrateToVersion99RemoveAutofillProfilesTrashTable();
+  bool MigrateToVersion100RemoveProfileValidityBitfieldColumn();
+  bool MigrateToVersion102AddAutofillBirthdatesTable();
+  bool MigrateToVersion107AddContactInfoTables();
+  bool MigrateToVersion110AddInitialCreatorIdAndLastModifierId();
+  bool MigrateToVersion113MigrateLocalAddressProfilesToNewTable();
+  bool MigrateToVersion114DropLegacyAddressTables();
+  bool MigrateToVersion117AddProfileObservationColumn();
+  bool MigrateToVersion121DropServerAddressTables();
+
+  // Max data length saved in the table, AKA the maximum length allowed for
+  // form data.
+  // Copied to components/autofill/ios/browser/resources/autofill_controller.js.
+  static const size_t kMaxDataLength;
+
+ private:
+  // Reads profiles from the deprecated autofill_profiles table.
+  std::unique_ptr<AutofillProfile> GetAutofillProfileFromLegacyTable(
+      const std::string& guid) const;
+  bool GetAutofillProfilesFromLegacyTable(
+      std::vector<std::unique_ptr<AutofillProfile>>* profiles) const;
+
+  bool InitLegacyProfilesTable();
+  bool InitLegacyProfileAddressesTable();
+  bool InitLegacyProfileNamesTable();
+  bool InitLegacyProfileEmailsTable();
+  bool InitLegacyProfilePhonesTable();
+  bool InitLegacyProfileBirthdatesTable();
+  bool InitProfileMetadataTable(AutofillProfile::Source source);
+  bool InitProfileTypeTokensTable(AutofillProfile::Source source);
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_ADDRESSES_ADDRESS_AUTOFILL_TABLE_H_
diff --git a/components/autofill/core/browser/webdata/addresses/address_autofill_table_unittest.cc b/components/autofill/core/browser/webdata/addresses/address_autofill_table_unittest.cc
new file mode 100644
index 0000000..74517db
--- /dev/null
+++ b/components/autofill/core/browser/webdata/addresses/address_autofill_table_unittest.cc
@@ -0,0 +1,460 @@
+// 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.
+
+#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/time/time.h"
+#include "base/uuid.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/country_type.h"
+#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/profile_token_quality.h"
+#include "components/autofill/core/browser/profile_token_quality_test_api.h"
+#include "components/autofill/core/browser/test_autofill_clock.h"
+#include "components/autofill/core/common/autofill_clock.h"
+#include "components/autofill/core/common/autofill_features.h"
+#include "components/webdata/common/web_database.h"
+#include "sql/statement.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/origin.h"
+
+using base::Time;
+using testing::ElementsAre;
+using testing::UnorderedElementsAre;
+
+namespace autofill {
+
+class AddressAutofillTableTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    file_ = temp_dir_.GetPath().AppendASCII("TestWebDatabase");
+
+    table_ = std::make_unique<AddressAutofillTable>();
+    db_ = std::make_unique<WebDatabase>();
+    db_->AddTable(table_.get());
+    ASSERT_EQ(sql::INIT_OK, db_->Init(file_));
+  }
+
+  base::FilePath file_;
+  base::ScopedTempDir temp_dir_;
+  std::unique_ptr<AddressAutofillTable> table_;
+  std::unique_ptr<WebDatabase> db_;
+};
+
+// Tests for the AutofillProfil CRUD interface are tested with both profile
+// sources.
+class AddressAutofillTableProfileTest
+    : public AddressAutofillTableTest,
+      public testing::WithParamInterface<AutofillProfile::Source> {
+ public:
+  void SetUp() override {
+    AddressAutofillTableTest::SetUp();
+    features_.InitWithFeatures(
+        {features::kAutofillEnableSupportForLandmark,
+         features::kAutofillEnableSupportForBetweenStreets,
+         features::kAutofillEnableSupportForAdminLevel2,
+         features::kAutofillEnableSupportForAddressOverflow,
+         features::kAutofillEnableSupportForAddressOverflowAndLandmark,
+         features::kAutofillEnableSupportForBetweenStreetsOrLandmark},
+        {});
+  }
+  AutofillProfile::Source profile_source() const { return GetParam(); }
+
+  // Creates an `AutofillProfile` with `profile_source()` as its source.
+  AutofillProfile CreateAutofillProfile() const {
+    return AutofillProfile(profile_source(), AddressCountryCode("ES"));
+  }
+
+  // Depending on the `profile_source()`, the AutofillProfiles are stored in a
+  // different master table.
+  base::StringPiece GetProfileTable() const {
+    return profile_source() == AutofillProfile::Source::kLocalOrSyncable
+               ? "local_addresses"
+               : "contact_info";
+  }
+
+ private:
+  base::test::ScopedFeatureList features_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    AddressAutofillTableProfileTest,
+    testing::ValuesIn({AutofillProfile::Source::kLocalOrSyncable,
+                       AutofillProfile::Source::kAccount}));
+
+// Tests reading/writing name, email, company, address, phone number and
+// birthdate information.
+TEST_P(AddressAutofillTableProfileTest, AutofillProfile) {
+  AutofillProfile home_profile = CreateAutofillProfile();
+
+  // TODO(crbug.com/1113617): Honorifics are temporally disabled.
+  // home_profile.SetRawInfoWithVerificationStatus(
+  // NAME_HONORIFIC_PREFIX, u"Dr.",
+  // VerificationStatus::kObserved);
+
+  home_profile.SetRawInfoWithVerificationStatus(NAME_HONORIFIC_PREFIX, u"Dr.",
+                                                VerificationStatus::kObserved);
+
+  home_profile.SetRawInfoWithVerificationStatus(NAME_FIRST, u"John",
+                                                VerificationStatus::kObserved);
+
+  home_profile.SetRawInfoWithVerificationStatus(NAME_MIDDLE, u"Q.",
+                                                VerificationStatus::kObserved);
+
+  home_profile.SetRawInfoWithVerificationStatus(NAME_LAST_FIRST, u"Agent",
+                                                VerificationStatus::kParsed);
+
+  home_profile.SetRawInfoWithVerificationStatus(NAME_LAST_CONJUNCTION, u"007",
+                                                VerificationStatus::kParsed);
+
+  home_profile.SetRawInfoWithVerificationStatus(NAME_LAST_SECOND, u"Smith",
+                                                VerificationStatus::kParsed);
+
+  home_profile.SetRawInfoWithVerificationStatus(NAME_LAST, u"Agent 007 Smith",
+                                                VerificationStatus::kParsed);
+
+  home_profile.SetRawInfoWithVerificationStatus(
+      NAME_FULL, u"John Q. Agent 007 Smith", VerificationStatus::kObserved);
+
+  home_profile.SetRawInfoWithVerificationStatus(NAME_FULL_WITH_HONORIFIC_PREFIX,
+                                                u"Dr. John Q. Agent 007 Smith",
+                                                VerificationStatus::kObserved);
+
+  home_profile.SetRawInfo(EMAIL_ADDRESS, u"js@smith.xyz");
+  home_profile.SetRawInfo(COMPANY_NAME, u"Google");
+
+  home_profile.SetRawInfoWithVerificationStatus(
+      ADDRESS_HOME_STREET_ADDRESS,
+      u"Street Name between streets House Number Premise APT 10 Floor 2 "
+      u"Landmark",
+      VerificationStatus::kUserVerified);
+  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_STREET_LOCATION,
+                                                u"Street Name House Number",
+                                                VerificationStatus::kFormatted);
+  home_profile.SetRawInfoWithVerificationStatus(
+      ADDRESS_HOME_STREET_NAME, u"Street Name", VerificationStatus::kFormatted);
+  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_DEPENDENT_LOCALITY,
+                                                u"Dependent Locality",
+                                                VerificationStatus::kObserved);
+
+  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_CITY, u"City",
+                                                VerificationStatus::kObserved);
+
+  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_STATE, u"State",
+                                                VerificationStatus::kObserved);
+
+  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_SORTING_CODE,
+                                                u"Sorting Code",
+                                                VerificationStatus::kObserved);
+
+  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_ZIP, u"ZIP",
+                                                VerificationStatus::kObserved);
+
+  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_COUNTRY, u"DE",
+                                                VerificationStatus::kObserved);
+  home_profile.SetRawInfoWithVerificationStatus(
+      ADDRESS_HOME_HOUSE_NUMBER, u"House Number",
+      VerificationStatus::kUserVerified);
+  home_profile.SetRawInfoWithVerificationStatus(
+      ADDRESS_HOME_SUBPREMISE, u"APT 10 Floor 2",
+      VerificationStatus::kUserVerified);
+  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_APT_NUM, u"10",
+                                                VerificationStatus::kParsed);
+  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_FLOOR, u"2",
+                                                VerificationStatus::kParsed);
+  ASSERT_EQ(home_profile.GetRawInfo(ADDRESS_HOME_STREET_NAME), u"Street Name");
+  home_profile.SetRawInfoWithVerificationStatus(
+      ADDRESS_HOME_LANDMARK, u"Landmark", VerificationStatus::kObserved);
+  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_OVERFLOW,
+                                                u"Andar 1, Apto. 12",
+                                                VerificationStatus::kObserved);
+  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_BETWEEN_STREETS,
+                                                u"between streets",
+                                                VerificationStatus::kObserved);
+  home_profile.SetRawInfoWithVerificationStatus(
+      ADDRESS_HOME_ADMIN_LEVEL2, u"Oxaca", VerificationStatus::kObserved);
+
+  home_profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"18181234567");
+  home_profile.SetRawInfoAsInt(BIRTHDATE_DAY, 14);
+  home_profile.SetRawInfoAsInt(BIRTHDATE_MONTH, 3);
+  home_profile.SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, 1997);
+  home_profile.set_language_code("en");
+
+  // Add the profile to the table.
+  EXPECT_TRUE(table_->AddAutofillProfile(home_profile));
+
+  // Get the 'Home' profile from the table.
+  std::unique_ptr<AutofillProfile> db_profile =
+      table_->GetAutofillProfile(home_profile.guid(), home_profile.source());
+  ASSERT_TRUE(db_profile);
+
+  // Verify that it is correct.
+  EXPECT_EQ(home_profile, *db_profile);
+
+  // Remove the profile and expect that no profiles remain.
+  EXPECT_TRUE(
+      table_->RemoveAutofillProfile(home_profile.guid(), profile_source()));
+  std::vector<std::unique_ptr<AutofillProfile>> profiles;
+  EXPECT_TRUE(table_->GetAutofillProfiles(profile_source(), &profiles));
+  EXPECT_TRUE(profiles.empty());
+}
+
+// Tests that `GetAutofillProfiles(source, profiles)` clears `profiles` and
+// only returns profiles from the correct `source`.
+// Not part of the `AddressAutofillTableProfileTest` fixture, as it doesn't
+// benefit from parameterization on the `profile_source()`.
+TEST_F(AddressAutofillTableTest, GetAutofillProfiles) {
+  AutofillProfile local_profile(AutofillProfile::Source::kLocalOrSyncable,
+                                AddressCountryCode("ES"));
+  AutofillProfile account_profile(AutofillProfile::Source::kAccount,
+                                  AddressCountryCode("ES"));
+  EXPECT_TRUE(table_->AddAutofillProfile(local_profile));
+  EXPECT_TRUE(table_->AddAutofillProfile(account_profile));
+
+  std::vector<std::unique_ptr<AutofillProfile>> profiles;
+  EXPECT_TRUE(table_->GetAutofillProfiles(
+      AutofillProfile::Source::kLocalOrSyncable, &profiles));
+  EXPECT_THAT(profiles, ElementsAre(testing::Pointee(local_profile)));
+  EXPECT_TRUE(table_->GetAutofillProfiles(AutofillProfile::Source::kAccount,
+                                          &profiles));
+  EXPECT_THAT(profiles, ElementsAre(testing::Pointee(account_profile)));
+}
+
+// Tests that `RemoveAllAutofillProfiles()` clears all profiles of the given
+// source.
+TEST_P(AddressAutofillTableProfileTest, RemoveAllAutofillProfiles) {
+  ASSERT_TRUE(table_->AddAutofillProfile(
+      AutofillProfile(AutofillProfile::Source::kLocalOrSyncable,
+                      i18n_model_definition::kLegacyHierarchyCountryCode)));
+  ASSERT_TRUE(table_->AddAutofillProfile(
+      AutofillProfile(AutofillProfile::Source::kAccount,
+                      i18n_model_definition::kLegacyHierarchyCountryCode)));
+
+  EXPECT_TRUE(table_->RemoveAllAutofillProfiles(profile_source()));
+
+  // Expect that the profiles from `profile_source()` are gone.
+  std::vector<std::unique_ptr<AutofillProfile>> profiles;
+  ASSERT_TRUE(table_->GetAutofillProfiles(profile_source(), &profiles));
+  EXPECT_TRUE(profiles.empty());
+
+  // Expect that the profile from the opposite source remains.
+  const auto other_source =
+      profile_source() == AutofillProfile::Source::kAccount
+          ? AutofillProfile::Source::kLocalOrSyncable
+          : AutofillProfile::Source::kAccount;
+  ASSERT_TRUE(table_->GetAutofillProfiles(other_source, &profiles));
+  EXPECT_EQ(profiles.size(), 1u);
+}
+
+// Tests that `ProfileTokenQuality` observations are read and written.
+TEST_P(AddressAutofillTableProfileTest, ProfileTokenQuality) {
+  AutofillProfile profile = CreateAutofillProfile();
+  test_api(profile.token_quality())
+      .AddObservation(NAME_FIRST,
+                      ProfileTokenQuality::ObservationType::kAccepted,
+                      ProfileTokenQualityTestApi::FormSignatureHash(12));
+
+  // Add
+  table_->AddAutofillProfile(profile);
+  profile = *table_->GetAutofillProfile(profile.guid(), profile.source());
+  EXPECT_THAT(
+      profile.token_quality().GetObservationTypesForFieldType(NAME_FIRST),
+      UnorderedElementsAre(ProfileTokenQuality::ObservationType::kAccepted));
+  EXPECT_THAT(
+      test_api(profile.token_quality()).GetHashesForStoredType(NAME_FIRST),
+      UnorderedElementsAre(ProfileTokenQualityTestApi::FormSignatureHash(12)));
+
+  // Update
+  test_api(profile.token_quality())
+      .AddObservation(NAME_FIRST,
+                      ProfileTokenQuality::ObservationType::kEditedFallback,
+                      ProfileTokenQualityTestApi::FormSignatureHash(21));
+  table_->UpdateAutofillProfile(profile);
+  profile = *table_->GetAutofillProfile(profile.guid(), profile.source());
+  EXPECT_THAT(
+      profile.token_quality().GetObservationTypesForFieldType(NAME_FIRST),
+      UnorderedElementsAre(
+          ProfileTokenQuality::ObservationType::kAccepted,
+          ProfileTokenQuality::ObservationType::kEditedFallback));
+  EXPECT_THAT(
+      test_api(profile.token_quality()).GetHashesForStoredType(NAME_FIRST),
+      UnorderedElementsAre(ProfileTokenQualityTestApi::FormSignatureHash(12),
+                           ProfileTokenQualityTestApi::FormSignatureHash(21)));
+}
+
+TEST_P(AddressAutofillTableProfileTest, UpdateAutofillProfile) {
+  // Add a profile to the db.
+  AutofillProfile profile = CreateAutofillProfile();
+  profile.SetRawInfo(NAME_FIRST, u"John");
+  profile.SetRawInfo(NAME_MIDDLE, u"Q.");
+  profile.SetRawInfo(NAME_LAST, u"Smith");
+  profile.SetRawInfo(EMAIL_ADDRESS, u"js@example.com");
+  profile.SetRawInfo(COMPANY_NAME, u"Google");
+  profile.SetRawInfo(ADDRESS_HOME_LINE1, u"1234 Apple Way");
+  profile.SetRawInfo(ADDRESS_HOME_LINE2, u"unit 5");
+  profile.SetRawInfo(ADDRESS_HOME_CITY, u"Los Angeles");
+  profile.SetRawInfo(ADDRESS_HOME_STATE, u"CA");
+  profile.SetRawInfo(ADDRESS_HOME_ZIP, u"90025");
+  profile.SetRawInfo(ADDRESS_HOME_COUNTRY, u"ES");
+  profile.SetRawInfo(ADDRESS_HOME_OVERFLOW, u"Andar 1, Apto. 12");
+  profile.SetRawInfo(ADDRESS_HOME_LANDMARK, u"Landmark");
+  profile.SetRawInfo(ADDRESS_HOME_BETWEEN_STREETS, u"Marcos y Oliva");
+  profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"18181234567");
+  profile.SetRawInfoAsInt(BIRTHDATE_DAY, 14);
+  profile.SetRawInfoAsInt(BIRTHDATE_MONTH, 3);
+  profile.SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, 1997);
+  profile.set_language_code("en");
+  profile.FinalizeAfterImport();
+  table_->AddAutofillProfile(profile);
+
+  // Get the profile.
+  std::unique_ptr<AutofillProfile> db_profile =
+      table_->GetAutofillProfile(profile.guid(), profile.source());
+  ASSERT_TRUE(db_profile);
+  EXPECT_EQ(profile, *db_profile);
+
+  // Now, update the profile and save the update to the database.
+  // The modification date should change to reflect the update.
+  profile.SetRawInfo(EMAIL_ADDRESS, u"js@smith.xyz");
+  table_->UpdateAutofillProfile(profile);
+
+  // Get the profile.
+  db_profile = table_->GetAutofillProfile(profile.guid(), profile.source());
+  ASSERT_TRUE(db_profile);
+  EXPECT_EQ(profile, *db_profile);
+}
+
+TEST_F(AddressAutofillTableTest, RemoveAutofillDataModifiedBetween) {
+  // Populate the address tables.
+  ASSERT_TRUE(db_->GetSQLConnection()->Execute(
+      "INSERT INTO local_addresses (guid, date_modified) "
+      "VALUES('00000000-0000-0000-0000-000000000000', 11);"
+      "INSERT INTO local_addresses_type_tokens (guid, type, value) "
+      "VALUES('00000000-0000-0000-0000-000000000000', 3, 'first name0');"
+      "INSERT INTO local_addresses (guid, date_modified) "
+      "VALUES('00000000-0000-0000-0000-000000000001', 21);"
+      "INSERT INTO local_addresses_type_tokens (guid, type, value) "
+      "VALUES('00000000-0000-0000-0000-000000000001', 3, 'first name1');"
+      "INSERT INTO local_addresses (guid, date_modified) "
+      "VALUES('00000000-0000-0000-0000-000000000002', 31);"
+      "INSERT INTO local_addresses_type_tokens (guid, type, value) "
+      "VALUES('00000000-0000-0000-0000-000000000002', 3, 'first name2');"
+      "INSERT INTO local_addresses (guid, date_modified) "
+      "VALUES('00000000-0000-0000-0000-000000000003', 41);"
+      "INSERT INTO local_addresses_type_tokens (guid, type, value) "
+      "VALUES('00000000-0000-0000-0000-000000000003', 3, 'first name3');"
+      "INSERT INTO local_addresses (guid, date_modified) "
+      "VALUES('00000000-0000-0000-0000-000000000004', 51);"
+      "INSERT INTO local_addresses_type_tokens (guid, type, value) "
+      "VALUES('00000000-0000-0000-0000-000000000004', 3, 'first name4');"
+      "INSERT INTO local_addresses (guid, date_modified) "
+      "VALUES('00000000-0000-0000-0000-000000000005', 61);"
+      "INSERT INTO local_addresses_type_tokens (guid, type, value) "
+      "VALUES('00000000-0000-0000-0000-000000000005', 3, 'first name5');"));
+
+  // Remove all entries modified in the bounded time range [17,41).
+  std::vector<std::unique_ptr<AutofillProfile>> profiles;
+  table_->RemoveAutofillDataModifiedBetween(Time::FromTimeT(17),
+                                            Time::FromTimeT(41), &profiles);
+
+  // Two profiles should have been removed.
+  ASSERT_EQ(2UL, profiles.size());
+  EXPECT_EQ("00000000-0000-0000-0000-000000000001", profiles[0]->guid());
+  EXPECT_EQ("00000000-0000-0000-0000-000000000002", profiles[1]->guid());
+
+  // Make sure that only the expected profiles are still present.
+  sql::Statement s_autofill_profiles_bounded(
+      db_->GetSQLConnection()->GetUniqueStatement(
+          "SELECT date_modified FROM local_addresses ORDER BY guid"));
+  ASSERT_TRUE(s_autofill_profiles_bounded.is_valid());
+  ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+  EXPECT_EQ(11, s_autofill_profiles_bounded.ColumnInt64(0));
+  ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+  EXPECT_EQ(41, s_autofill_profiles_bounded.ColumnInt64(0));
+  ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+  EXPECT_EQ(51, s_autofill_profiles_bounded.ColumnInt64(0));
+  ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+  EXPECT_EQ(61, s_autofill_profiles_bounded.ColumnInt64(0));
+  EXPECT_FALSE(s_autofill_profiles_bounded.Step());
+
+  // Make sure that only the expected profile names are still present.
+  sql::Statement s_autofill_profile_names_bounded(
+      db_->GetSQLConnection()->GetUniqueStatement(
+          "SELECT value FROM local_addresses_type_tokens ORDER BY guid"));
+  ASSERT_TRUE(s_autofill_profile_names_bounded.is_valid());
+  ASSERT_TRUE(s_autofill_profile_names_bounded.Step());
+  EXPECT_EQ("first name0", s_autofill_profile_names_bounded.ColumnString(0));
+  ASSERT_TRUE(s_autofill_profile_names_bounded.Step());
+  EXPECT_EQ("first name3", s_autofill_profile_names_bounded.ColumnString(0));
+  ASSERT_TRUE(s_autofill_profile_names_bounded.Step());
+  EXPECT_EQ("first name4", s_autofill_profile_names_bounded.ColumnString(0));
+  ASSERT_TRUE(s_autofill_profile_names_bounded.Step());
+  EXPECT_EQ("first name5", s_autofill_profile_names_bounded.ColumnString(0));
+  EXPECT_FALSE(s_autofill_profile_names_bounded.Step());
+
+  // Remove all entries modified on or after time 51 (unbounded range).
+  table_->RemoveAutofillDataModifiedBetween(Time::FromTimeT(51), Time(),
+                                            &profiles);
+  ASSERT_EQ(2UL, profiles.size());
+  EXPECT_EQ("00000000-0000-0000-0000-000000000004", profiles[0]->guid());
+  EXPECT_EQ("00000000-0000-0000-0000-000000000005", profiles[1]->guid());
+
+  // Make sure that only the expected profiles are still present.
+  sql::Statement s_autofill_profiles_unbounded(
+      db_->GetSQLConnection()->GetUniqueStatement(
+          "SELECT date_modified FROM local_addresses ORDER BY guid"));
+  ASSERT_TRUE(s_autofill_profiles_unbounded.is_valid());
+  ASSERT_TRUE(s_autofill_profiles_unbounded.Step());
+  EXPECT_EQ(11, s_autofill_profiles_unbounded.ColumnInt64(0));
+  ASSERT_TRUE(s_autofill_profiles_unbounded.Step());
+  EXPECT_EQ(41, s_autofill_profiles_unbounded.ColumnInt64(0));
+  EXPECT_FALSE(s_autofill_profiles_unbounded.Step());
+
+  // Make sure that only the expected profile names are still present.
+  sql::Statement s_autofill_profile_names_unbounded(
+      db_->GetSQLConnection()->GetUniqueStatement(
+          "SELECT value FROM local_addresses_type_tokens ORDER BY guid"));
+  ASSERT_TRUE(s_autofill_profile_names_unbounded.is_valid());
+  ASSERT_TRUE(s_autofill_profile_names_unbounded.Step());
+  EXPECT_EQ("first name0", s_autofill_profile_names_unbounded.ColumnString(0));
+  ASSERT_TRUE(s_autofill_profile_names_unbounded.Step());
+  EXPECT_EQ("first name3", s_autofill_profile_names_unbounded.ColumnString(0));
+  EXPECT_FALSE(s_autofill_profile_names_unbounded.Step());
+
+  // Remove all remaining entries.
+  table_->RemoveAutofillDataModifiedBetween(Time(), Time(), &profiles);
+
+  // Two profiles should have been removed.
+  ASSERT_EQ(2UL, profiles.size());
+  EXPECT_EQ("00000000-0000-0000-0000-000000000000", profiles[0]->guid());
+  EXPECT_EQ("00000000-0000-0000-0000-000000000003", profiles[1]->guid());
+
+  // Make sure there are no profiles remaining.
+  sql::Statement s_autofill_profiles_empty(
+      db_->GetSQLConnection()->GetUniqueStatement(
+          "SELECT date_modified FROM local_addresses"));
+  ASSERT_TRUE(s_autofill_profiles_empty.is_valid());
+  EXPECT_FALSE(s_autofill_profiles_empty.Step());
+
+  // Make sure there are no profile names remaining.
+  sql::Statement s_autofill_profile_names_empty(
+      db_->GetSQLConnection()->GetUniqueStatement(
+          "SELECT value FROM local_addresses_type_tokens"));
+  ASSERT_TRUE(s_autofill_profile_names_empty.is_valid());
+  EXPECT_FALSE(s_autofill_profile_names_empty.Step());
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h b/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
index 7197764..2c74f0fe 100644
--- a/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
+++ b/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOCOMPLETE_SYNC_BRIDGE_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/memory/raw_ptr.h"
@@ -20,7 +21,6 @@
 #include "components/sync/model/model_error.h"
 #include "components/sync/model/model_type_change_processor.h"
 #include "components/sync/model/model_type_sync_bridge.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -52,10 +52,10 @@
   // syncer::ModelTypeSyncBridge implementation.
   std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
       override;
-  absl::optional<syncer::ModelError> MergeFullSyncData(
+  std::optional<syncer::ModelError> MergeFullSyncData(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_data) override;
-  absl::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
+  std::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_changes) override;
   void GetData(StorageKeyList storage_keys, DataCallback callback) override;
diff --git a/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
index 00091c8..ab29d61 100644
--- a/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
@@ -200,7 +200,7 @@
       initial_updates.push_back(SpecificsToUpdateResponse(specifics));
     }
     real_processor_->OnUpdateReceived(state, std::move(initial_updates),
-                                      /*gc_directive=*/absl::nullopt);
+                                      /*gc_directive=*/std::nullopt);
   }
 
   void SaveSpecificsToTable(
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
index db5528b..67c930a 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
@@ -15,6 +15,7 @@
 #include "components/autofill/core/browser/autofill_profile_sync_util.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/proto/autofill_sync.pb.h"
+#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
 #include "components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
@@ -26,8 +27,8 @@
 #include "components/sync/model/sync_metadata_store_change_list.h"
 #include "components/sync/protocol/entity_data.h"
 
-using absl::optional;
 using base::UTF16ToUTF8;
+using std::optional;
 using sync_pb::AutofillProfileSpecifics;
 using syncer::EntityData;
 using syncer::MetadataChangeList;
@@ -92,7 +93,7 @@
 AutofillProfileSyncBridge::CreateMetadataChangeList() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return std::make_unique<syncer::SyncMetadataStoreChangeList>(
-      GetAutofillTable(), syncer::AUTOFILL_PROFILE,
+      GetSyncMetadataStore(), syncer::AUTOFILL_PROFILE,
       base::BindRepeating(&syncer::ModelTypeChangeProcessor::ReportError,
                           change_processor()->GetWeakPtr()));
 }
@@ -127,7 +128,7 @@
       FlushSyncTracker(std::move(metadata_change_list), &initial_sync_tracker));
 
   web_data_backend_->CommitChanges();
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 optional<ModelError> AutofillProfileSyncBridge::ApplyIncrementalSyncChanges(
@@ -158,7 +159,7 @@
   RETURN_IF_ERROR(FlushSyncTracker(std::move(metadata_change_list), &tracker));
 
   web_data_backend_->CommitChanges();
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 void AutofillProfileSyncBridge::GetData(StorageKeyList storage_keys,
@@ -233,7 +234,7 @@
   // operation (that triggered this notification to the bridge) finishes.
 }
 
-absl::optional<syncer::ModelError> AutofillProfileSyncBridge::FlushSyncTracker(
+std::optional<syncer::ModelError> AutofillProfileSyncBridge::FlushSyncTracker(
     std::unique_ptr<MetadataChangeList> metadata_change_list,
     AutofillProfileSyncDifferenceTracker* tracker) {
   DCHECK(tracker);
@@ -261,15 +262,15 @@
 
 void AutofillProfileSyncBridge::LoadMetadata() {
   if (!web_data_backend_ || !web_data_backend_->GetDatabase() ||
-      !GetAutofillTable()) {
+      !GetAutofillTable() || !GetSyncMetadataStore()) {
     change_processor()->ReportError(
         {FROM_HERE, "Failed to load AutofillWebDatabase."});
     return;
   }
 
   auto batch = std::make_unique<syncer::MetadataBatch>();
-  if (!GetAutofillTable()->GetAllSyncMetadata(syncer::AUTOFILL_PROFILE,
-                                              batch.get())) {
+  if (!GetSyncMetadataStore()->GetAllSyncMetadata(syncer::AUTOFILL_PROFILE,
+                                                  batch.get())) {
     change_processor()->ReportError(
         {FROM_HERE, "Failed reading autofill metadata from WebDatabase."});
     return;
@@ -298,7 +299,12 @@
   ActOnLocalChange(change);
 }
 
-AutofillTable* AutofillProfileSyncBridge::GetAutofillTable() {
+AddressAutofillTable* AutofillProfileSyncBridge::GetAutofillTable() {
+  return AddressAutofillTable::FromWebDatabase(
+      web_data_backend_->GetDatabase());
+}
+
+AutofillTable* AutofillProfileSyncBridge::GetSyncMetadataStore() {
   return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase());
 }
 
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h
index aa1faa7..8204d74 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_SYNC_BRIDGE_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/memory/raw_ptr.h"
@@ -16,7 +17,6 @@
 #include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h"
 #include "components/sync/model/model_type_sync_bridge.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace syncer {
 class MetadataChangeList;
@@ -26,6 +26,7 @@
 
 namespace autofill {
 
+class AddressAutofillTable;
 class AutofillProfileSyncDifferenceTracker;
 class AutofillTable;
 class AutofillWebDataService;
@@ -71,10 +72,10 @@
   // syncer::ModelTypeSyncBridge implementation.
   std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
       override;
-  absl::optional<syncer::ModelError> MergeFullSyncData(
+  std::optional<syncer::ModelError> MergeFullSyncData(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_data) override;
-  absl::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
+  std::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_changes) override;
   void GetData(StorageKeyList storage_keys, DataCallback callback) override;
@@ -87,14 +88,18 @@
 
  private:
   // Returns the table associated with the |web_data_backend_|.
-  AutofillTable* GetAutofillTable();
+  AddressAutofillTable* GetAutofillTable();
+
+  // AutofillTable acts as the metadata storage for all components/autofill-
+  // related sync code.
+  AutofillTable* GetSyncMetadataStore();
 
   // Respond to local autofill profile entry changing by notifying sync of the
   // changes.
   void ActOnLocalChange(const AutofillProfileChange& change);
 
   // Flushes changes accumulated within |tracker| both to local and to sync.
-  absl::optional<syncer::ModelError> FlushSyncTracker(
+  std::optional<syncer::ModelError> FlushSyncTracker(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       AutofillProfileSyncDifferenceTracker* tracker);
 
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
index 79622a2..7e7d4790 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
@@ -25,6 +25,7 @@
 #include "components/autofill/core/browser/autofill_profile_sync_util.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
+#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
 #include "components/autofill/core/browser/webdata/autofill_change.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/autofill/core/browser/webdata/mock_autofill_webdata_backend.h"
@@ -263,6 +264,7 @@
     test_clock_.SetNow(kJune2017);
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     db_.AddTable(&table_);
+    db_.AddTable(&sync_metadata_table_);
     db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase"));
     ON_CALL(*backend(), GetDatabase()).WillByDefault(Return(&db_));
     ResetProcessor();
@@ -308,13 +310,13 @@
       initial_updates.push_back(SpecificsToUpdateResponse(specifics));
     }
     real_processor_->OnUpdateReceived(state, std::move(initial_updates),
-                                      /*gc_directive=*/absl::nullopt);
+                                      /*gc_directive=*/std::nullopt);
   }
 
   void ApplyIncrementalSyncChanges(EntityChangeList changes) {
     EXPECT_CALL(*backend(),
                 NotifyOnAutofillChangedBySync(syncer::AUTOFILL_PROFILE));
-    const absl::optional<syncer::ModelError> error =
+    const std::optional<syncer::ModelError> error =
         bridge()->ApplyIncrementalSyncChanges(
             bridge()->CreateMetadataChangeList(), std::move(changes));
     EXPECT_FALSE(error) << error->ToString();
@@ -361,7 +363,7 @@
     return mock_processor_;
   }
 
-  AutofillTable* table() { return &table_; }
+  AddressAutofillTable* table() { return &table_; }
 
   MockAutofillWebDataBackend* backend() { return &backend_; }
 
@@ -370,7 +372,8 @@
   ScopedTempDir temp_dir_;
   base::test::SingleThreadTaskEnvironment task_environment_;
   testing::NiceMock<MockAutofillWebDataBackend> backend_;
-  AutofillTable table_;
+  AddressAutofillTable table_;
+  AutofillTable sync_metadata_table_;
   WebDatabase db_;
   testing::NiceMock<MockModelTypeChangeProcessor> mock_processor_;
   std::unique_ptr<syncer::ClientTagBasedModelTypeProcessor> real_processor_;
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc
index f2eccd1..1b4fa4f 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc
@@ -10,13 +10,13 @@
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/autofill_profile_comparator.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
 #include "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h"
-#include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/sync/model/model_error.h"
 
 namespace autofill {
 
-using absl::optional;
+using std::optional;
 using syncer::ModelError;
 
 // Simplify checking for optional errors and returning only when present.
@@ -26,7 +26,7 @@
   }
 
 AutofillProfileSyncDifferenceTracker::AutofillProfileSyncDifferenceTracker(
-    AutofillTable* table)
+    AddressAutofillTable* table)
     : table_(table) {}
 
 AutofillProfileSyncDifferenceTracker::~AutofillProfileSyncDifferenceTracker() =
@@ -75,7 +75,7 @@
       update_to_local_.push_back(std::move(updated));
     }
     GetLocalOnlyEntries()->erase(remote_storage_key);
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Check if profile appears under a different storage key to be de-duplicated.
@@ -129,13 +129,13 @@
         // We keep the local entity and delete the remote one.
         delete_from_sync_.insert(remote_storage_key);
       }
-      return absl::nullopt;
+      return std::nullopt;
     }
   }
 
   // If no duplicate was found, just add the remote profile.
   add_to_local_.push_back(std::move(remote));
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 optional<ModelError>
@@ -167,7 +167,7 @@
       !update_to_local_.empty()) {
     std::move(autofill_changes_callback).Run();
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 optional<ModelError> AutofillProfileSyncDifferenceTracker::FlushToSync(
@@ -179,7 +179,7 @@
   for (const std::string& entry : delete_from_sync_) {
     profiles_to_delete_from_sync->push_back(std::move(entry));
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 optional<AutofillProfile> AutofillProfileSyncDifferenceTracker::ReadEntry(
@@ -189,7 +189,7 @@
   if (iter != GetLocalOnlyEntries()->end()) {
     return *iter->second;
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 optional<ModelError> AutofillProfileSyncDifferenceTracker::DeleteFromLocal(
@@ -199,7 +199,7 @@
   }
   delete_from_local_.insert(storage_key);
   GetLocalOnlyEntries()->erase(storage_key);
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 std::map<std::string, std::unique_ptr<AutofillProfile>>*
@@ -232,7 +232,7 @@
 }
 
 AutofillProfileInitialSyncDifferenceTracker::
-    AutofillProfileInitialSyncDifferenceTracker(AutofillTable* table)
+    AutofillProfileInitialSyncDifferenceTracker(AddressAutofillTable* table)
     : AutofillProfileSyncDifferenceTracker(table) {}
 
 AutofillProfileInitialSyncDifferenceTracker::
@@ -243,7 +243,7 @@
     const std::string& storage_key) {
   // Remote delete is not allowed in initial sync.
   NOTREACHED();
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 optional<ModelError> AutofillProfileInitialSyncDifferenceTracker::FlushToSync(
@@ -262,7 +262,7 @@
     DCHECK(delete_from_local_.count(storage_key) == 0);
     profiles_to_upload_to_sync->push_back(std::move(data));
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 optional<ModelError>
@@ -315,7 +315,7 @@
     RETURN_IF_ERROR(DeleteFromLocal(GetStorageKeyFromAutofillProfile(*local)));
   }
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 optional<AutofillProfile>
@@ -328,7 +328,7 @@
       return *local_candidate;
     }
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h
index f40fdf9..3bbca53 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h
@@ -7,13 +7,13 @@
 
 #include <map>
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <vector>
 
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace syncer {
 class ModelError;
@@ -23,7 +23,7 @@
 
 class AutofillProfile;
 class AutofillProfileComparator;
-class AutofillTable;
+class AddressAutofillTable;
 
 // This is used to respond to ApplyIncrementalSyncChanges() and
 // MergeFullSyncData(). Attempts to lazily load local data, and then react to
@@ -32,7 +32,7 @@
 // directions.
 class AutofillProfileSyncDifferenceTracker {
  public:
-  explicit AutofillProfileSyncDifferenceTracker(AutofillTable* table);
+  explicit AutofillProfileSyncDifferenceTracker(AddressAutofillTable* table);
 
   AutofillProfileSyncDifferenceTracker(
       const AutofillProfileSyncDifferenceTracker&) = delete;
@@ -43,42 +43,42 @@
 
   // Adds a new |remote| entry to the diff tracker, originating from the sync
   // server. The provided |remote| entry must be valid.
-  [[nodiscard]] absl::optional<syncer::ModelError> IncorporateRemoteProfile(
+  [[nodiscard]] std::optional<syncer::ModelError> IncorporateRemoteProfile(
       std::unique_ptr<AutofillProfile> remote);
 
   // Informs the diff tracker that the entry with |storage_key| has been deleted
   // from the sync server. |storage_key| must be non-empty.
-  [[nodiscard]] virtual absl::optional<syncer::ModelError>
+  [[nodiscard]] virtual std::optional<syncer::ModelError>
   IncorporateRemoteDelete(const std::string& storage_key);
 
   // Writes all local changes to the provided autofill |table_|. After flushing,
   // not further remote changes should get incorporated.
-  [[nodiscard]] absl::optional<syncer::ModelError> FlushToLocal(
+  [[nodiscard]] std::optional<syncer::ModelError> FlushToLocal(
       base::OnceClosure autofill_changes_callback);
 
   // Writes into |profiles_to_upload_to_sync| all autofill profiles to be sent
   // to the sync server, and into |profiles_to_delete_from_sync| the storage
   // keys of all profiles to be deleted from the server. After flushing, no
   // further remote changes should get incorporated.
-  [[nodiscard]] virtual absl::optional<syncer::ModelError> FlushToSync(
+  [[nodiscard]] virtual std::optional<syncer::ModelError> FlushToSync(
       std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync,
       std::vector<std::string>* profiles_to_delete_from_sync);
 
  protected:
-  // If the entry is found, |entry| will be return, otherwise absl::nullopt is
+  // If the entry is found, |entry| will be return, otherwise std::nullopt is
   // returned.
-  absl::optional<AutofillProfile> ReadEntry(const std::string& storage_key);
+  std::optional<AutofillProfile> ReadEntry(const std::string& storage_key);
 
   // Tries to find a local entry that is mergeable with |remote| (according to
   // |comparator|). If such an entry is found, it is returned. Otherwise,
-  // absl::nullopt is returned.
-  absl::optional<AutofillProfile> FindMergeableLocalEntry(
+  // std::nullopt is returned.
+  std::optional<AutofillProfile> FindMergeableLocalEntry(
       const AutofillProfile& remote,
       const AutofillProfileComparator& comparator);
 
   // Informs the tracker that a local entry with |storage_key| should get
   // deleted.
-  [[nodiscard]] absl::optional<syncer::ModelError> DeleteFromLocal(
+  [[nodiscard]] std::optional<syncer::ModelError> DeleteFromLocal(
       const std::string& storage_key);
 
   // Accessor for data that is only stored local. Initializes the data if
@@ -90,7 +90,7 @@
   bool InitializeLocalOnlyEntriesIfNeeded();
 
   // The table for reading local data.
-  const raw_ptr<AutofillTable> table_;
+  const raw_ptr<AddressAutofillTable> table_;
 
   // This class loads local data from |table_| lazily. This field tracks if that
   // has happened or not yet.
@@ -119,7 +119,8 @@
 class AutofillProfileInitialSyncDifferenceTracker
     : public AutofillProfileSyncDifferenceTracker {
  public:
-  explicit AutofillProfileInitialSyncDifferenceTracker(AutofillTable* table);
+  explicit AutofillProfileInitialSyncDifferenceTracker(
+      AddressAutofillTable* table);
 
   AutofillProfileInitialSyncDifferenceTracker(
       const AutofillProfileInitialSyncDifferenceTracker&) = delete;
@@ -128,23 +129,23 @@
 
   ~AutofillProfileInitialSyncDifferenceTracker() override;
 
-  [[nodiscard]] absl::optional<syncer::ModelError> IncorporateRemoteDelete(
+  [[nodiscard]] std::optional<syncer::ModelError> IncorporateRemoteDelete(
       const std::string& storage_key) override;
 
-  [[nodiscard]] absl::optional<syncer::ModelError> FlushToSync(
+  [[nodiscard]] std::optional<syncer::ModelError> FlushToSync(
       std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync,
       std::vector<std::string>* profiles_to_delete_from_sync) override;
 
   // Performs an additional pass through remote entries incorporated from sync
   // to find any similarities with local entries. Should be run after all
   // entries get incorporated but before flushing results to local/sync.
-  [[nodiscard]] absl::optional<syncer::ModelError>
+  [[nodiscard]] std::optional<syncer::ModelError>
   MergeSimilarEntriesForInitialSync(const std::string& app_locale);
 
  private:
   // Returns a local entry that is mergeable with |remote| if it exists.
-  // Otherwise, returns absl::nullopt.
-  absl::optional<AutofillProfile> FindMergeableLocalEntry(
+  // Otherwise, returns std::nullopt.
+  std::optional<AutofillProfile> FindMergeableLocalEntry(
       const AutofillProfile& remote,
       const AutofillProfileComparator& comparator);
 };
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc
index aae7642..82e28ceb 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc
@@ -14,7 +14,7 @@
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
-#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
 #include "components/autofill/core/common/autofill_constants.h"
 #include "components/sync/model/model_error.h"
 #include "components/webdata/common/web_database.h"
@@ -70,18 +70,18 @@
   }
 
   void IncorporateRemoteProfile(const AutofillProfile& profile) {
-    EXPECT_EQ(absl::nullopt, tracker()->IncorporateRemoteProfile(
-                                 std::make_unique<AutofillProfile>(profile)));
+    EXPECT_EQ(std::nullopt, tracker()->IncorporateRemoteProfile(
+                                std::make_unique<AutofillProfile>(profile)));
   }
 
   UpdatesToSync FlushToSync() {
-    EXPECT_EQ(absl::nullopt,
+    EXPECT_EQ(std::nullopt,
               tracker()->FlushToLocal(
                   /*autofill_changes_callback=*/base::DoNothing()));
 
     UpdatesToSync updates;
     std::vector<std::unique_ptr<AutofillProfile>> vector_of_unique_ptrs;
-    EXPECT_EQ(absl::nullopt,
+    EXPECT_EQ(std::nullopt,
               tracker()->FlushToSync(
                   /*profiles_to_upload_to_sync=*/&vector_of_unique_ptrs,
                   /*profiles_to_delete_from_sync=*/&updates
@@ -114,13 +114,13 @@
 
   virtual AutofillProfileSyncDifferenceTracker* tracker() = 0;
 
-  AutofillTable* table() { return &table_; }
+  AddressAutofillTable* table() { return &table_; }
 
  private:
   autofill::TestAutofillClock test_clock_;
   base::ScopedTempDir temp_dir_;
   base::test::TaskEnvironment task_environment_;
-  AutofillTable table_;
+  AddressAutofillTable table_;
   WebDatabase db_;
 };
 
@@ -320,7 +320,7 @@
   MockCallback<base::OnceClosure> autofill_changes_callback;
 
   EXPECT_CALL(autofill_changes_callback, Run()).Times(0);
-  EXPECT_EQ(absl::nullopt,
+  EXPECT_EQ(std::nullopt,
             tracker()->FlushToLocal(autofill_changes_callback.Get()));
 }
 
@@ -330,11 +330,11 @@
                         i18n_model_definition::kLegacyHierarchyCountryCode);
   AddAutofillProfilesToTable({local});
 
-  EXPECT_EQ(absl::nullopt, tracker()->IncorporateRemoteDelete(kSmallerGuid));
+  EXPECT_EQ(std::nullopt, tracker()->IncorporateRemoteDelete(kSmallerGuid));
 
   MockCallback<base::OnceClosure> autofill_changes_callback;
   EXPECT_CALL(autofill_changes_callback, Run()).Times(1);
-  EXPECT_EQ(absl::nullopt,
+  EXPECT_EQ(std::nullopt,
             tracker()->FlushToLocal(autofill_changes_callback.Get()));
 
   // On top of that, the profile should also get deleted.
@@ -350,7 +350,7 @@
 
   MockCallback<base::OnceClosure> autofill_changes_callback;
   EXPECT_CALL(autofill_changes_callback, Run()).Times(1);
-  EXPECT_EQ(absl::nullopt,
+  EXPECT_EQ(std::nullopt,
             tracker()->FlushToLocal(autofill_changes_callback.Get()));
 
   // On top of that, the profile should also get added.
@@ -372,7 +372,7 @@
 
   MockCallback<base::OnceClosure> autofill_changes_callback;
   EXPECT_CALL(autofill_changes_callback, Run()).Times(1);
-  EXPECT_EQ(absl::nullopt,
+  EXPECT_EQ(std::nullopt,
             tracker()->FlushToLocal(autofill_changes_callback.Get()));
 
   // On top of that, the profile with key kSmallerGuid should also get updated.
@@ -392,7 +392,7 @@
 
   ~AutofillProfileInitialSyncDifferenceTrackerTest() override {}
 
-  [[nodiscard]] absl::optional<syncer::ModelError>
+  [[nodiscard]] std::optional<syncer::ModelError>
   MergeSimilarEntriesForInitialSync() {
     return initial_tracker_.MergeSimilarEntriesForInitialSync(kLocaleString);
   }
@@ -427,7 +427,7 @@
   merged.set_use_count(27);
 
   IncorporateRemoteProfile(remote);
-  EXPECT_EQ(absl::nullopt, MergeSimilarEntriesForInitialSync());
+  EXPECT_EQ(std::nullopt, MergeSimilarEntriesForInitialSync());
 
   // The merged profile needs to get uploaded back to sync and stored locally.
   UpdatesToSync updates = FlushToSync();
@@ -454,7 +454,7 @@
   remote.set_use_count(27);
   remote.FinalizeAfterImport();
   IncorporateRemoteProfile(remote);
-  EXPECT_EQ(absl::nullopt, MergeSimilarEntriesForInitialSync());
+  EXPECT_EQ(std::nullopt, MergeSimilarEntriesForInitialSync());
 
   // Nothing gets uploaded to sync and the remote profile wins.
   UpdatesToSync updates = FlushToSync();
@@ -479,7 +479,7 @@
   remote.SetRawInfo(COMPANY_NAME, u"Frobbers, Inc.");
   remote.FinalizeAfterImport();
   IncorporateRemoteProfile(remote);
-  EXPECT_EQ(absl::nullopt, MergeSimilarEntriesForInitialSync());
+  EXPECT_EQ(std::nullopt, MergeSimilarEntriesForInitialSync());
 
   // The local profile gets uploaded (due to initial sync) and the remote
   // profile gets stored locally.
diff --git a/components/autofill/core/browser/webdata/autofill_table.cc b/components/autofill/core/browser/webdata/autofill_table.cc
index 4816d4f67..ef6f527 100644
--- a/components/autofill/core/browser/webdata/autofill_table.cc
+++ b/components/autofill/core/browser/webdata/autofill_table.cc
@@ -40,7 +40,6 @@
 #include "components/autofill/core/browser/data_model/iban.h"
 #include "components/autofill/core/browser/field_type_utils.h"
 #include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/geo/autofill_country.h"
 #include "components/autofill/core/browser/payments/payments_customer_data.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/webdata/autofill_change.h"
@@ -68,109 +67,16 @@
 
 namespace {
 
-constexpr std::string_view kAutofillProfilesTable = "autofill_profiles";
-constexpr std::string_view kGuid = "guid";
-constexpr std::string_view kLabel = "label";
-constexpr std::string_view kCompanyName = "company_name";
-constexpr std::string_view kStreetAddress = "street_address";
-constexpr std::string_view kDependentLocality = "dependent_locality";
-constexpr std::string_view kCity = "city";
-constexpr std::string_view kState = "state";
-constexpr std::string_view kZipcode = "zipcode";
-constexpr std::string_view kSortingCode = "sorting_code";
-constexpr std::string_view kCountryCode = "country_code";
-constexpr std::string_view kUseCount = "use_count";
-constexpr std::string_view kUseDate = "use_date";
-constexpr std::string_view kDateModified = "date_modified";
-constexpr std::string_view kOrigin = "origin";
-constexpr std::string_view kLanguageCode = "language_code";
-constexpr std::string_view kDisallowSettingsVisibleUpdates =
-    "disallow_settings_visible_updates";
-
-constexpr std::string_view kAutofillProfileAddressesTable =
-    "autofill_profile_addresses";
-// kGuid = "guid"
-// kStreetAddress = "street_address"
-constexpr std::string_view kStreetName = "street_name";
-constexpr std::string_view kDependentStreetName = "dependent_street_name";
-constexpr std::string_view kHouseNumber = "house_number";
-constexpr std::string_view kSubpremise = "subpremise";
-// kDependentLocality = "dependent_locality"
-// kCity = "city"
-// kState = "state"
-constexpr std::string_view kZipCode = "zip_code";
-// kCountryCode = "country_code"
-// kSortingCode = "sorting_code"
-constexpr std::string_view kApartmentNumber = "apartment_number";
-constexpr std::string_view kFloor = "floor";
-constexpr std::string_view kStreetAddressStatus = "street_address_status";
-constexpr std::string_view kStreetNameStatus = "street_name_status";
-constexpr std::string_view kDependentStreetNameStatus =
-    "dependent_street_name_status";
-constexpr std::string_view kHouseNumberStatus = "house_number_status";
-constexpr std::string_view kSubpremiseStatus = "subpremise_status";
-constexpr std::string_view kDependentLocalityStatus =
-    "dependent_locality_status";
-constexpr std::string_view kCityStatus = "city_status";
-constexpr std::string_view kStateStatus = "state_status";
-constexpr std::string_view kZipCodeStatus = "zip_code_status";
-constexpr std::string_view kCountryCodeStatus = "country_code_status";
-constexpr std::string_view kSortingCodeStatus = "sorting_code_status";
-constexpr std::string_view kApartmentNumberStatus = "apartment_number_status";
-constexpr std::string_view kFloorStatus = "floor_status";
-
-constexpr std::string_view kAutofillProfileNamesTable =
-    "autofill_profile_names";
-// kGuid = "guid"
-constexpr std::string_view kHonorificPrefix = "honorific_prefix";
-constexpr std::string_view kFirstName = "first_name";
-constexpr std::string_view kMiddleName = "middle_name";
-constexpr std::string_view kLastName = "last_name";
-constexpr std::string_view kFirstLastName = "first_last_name";
-constexpr std::string_view kConjunctionLastName = "conjunction_last_name";
-constexpr std::string_view kSecondLastName = "second_last_name";
-constexpr std::string_view kFullName = "full_name";
-constexpr std::string_view kFullNameWithHonorificPrefix =
-    "full_name_with_honorific_prefix";
-constexpr std::string_view kHonorificPrefixStatus = "honorific_prefix_status";
-constexpr std::string_view kFirstNameStatus = "first_name_status";
-constexpr std::string_view kMiddleNameStatus = "middle_name_status";
-constexpr std::string_view kLastNameStatus = "last_name_status";
-constexpr std::string_view kFirstLastNameStatus = "first_last_name_status";
-constexpr std::string_view kConjunctionLastNameStatus =
-    "conjunction_last_name_status";
-constexpr std::string_view kSecondLastNameStatus = "second_last_name_status";
-constexpr std::string_view kFullNameStatus = "full_name_status";
-constexpr std::string_view kFullNameWithHonorificPrefixStatus =
-    "full_name_with_honorific_prefix_status";
-
-constexpr std::string_view kAutofillProfileEmailsTable =
-    "autofill_profile_emails";
-// kGuid = "guid"
-constexpr std::string_view kEmail = "email";
-
-constexpr std::string_view kAutofillProfilePhonesTable =
-    "autofill_profile_phones";
-// kGuid = "guid"
-constexpr std::string_view kNumber = "number";
-
-constexpr std::string_view kAutofillProfileBirthdatesTable =
-    "autofill_profile_birthdates";
-// kGuid = "guid"
-constexpr std::string_view kDay = "day";
-constexpr std::string_view kMonth = "month";
-constexpr std::string_view kYear = "year";
-
 constexpr std::string_view kCreditCardsTable = "credit_cards";
-// kGuid = "guid"
+constexpr std::string_view kGuid = "guid";
 constexpr std::string_view kNameOnCard = "name_on_card";
 constexpr std::string_view kExpirationMonth = "expiration_month";
 constexpr std::string_view kExpirationYear = "expiration_year";
 constexpr std::string_view kCardNumberEncrypted = "card_number_encrypted";
-// kUseCount = "use_count"
-// kUseDate = "use_date"
-// kDateModified = "date_modified"
-// kOrigin = "origin"
+constexpr std::string_view kUseCount = "use_count";
+constexpr std::string_view kUseDate = "use_date";
+constexpr std::string_view kDateModified = "date_modified";
+constexpr std::string_view kOrigin = "origin";
 constexpr std::string_view kBillingAddressId = "billing_address_id";
 constexpr std::string_view kNickname = "nickname";
 
@@ -273,27 +179,6 @@
 // kOfferId = "offer_id"
 constexpr std::string_view kMerchantDomain = "merchant_domain";
 
-constexpr std::string_view kContactInfoTable = "contact_info";
-constexpr std::string_view kLocalAddressesTable = "local_addresses";
-// kGuid = "guid"
-// kUseCount = "use_count"
-// kUseDate = "use_date"
-// kDateModified = "date_modified"
-// kLanguageCode = "language_code"
-// kLabel = "label"
-constexpr std::string_view kInitialCreatorId = "initial_creator_id";
-constexpr std::string_view kLastModifierId = "last_modifier_id";
-
-constexpr std::string_view kContactInfoTypeTokensTable =
-    "contact_info_type_tokens";
-constexpr std::string_view kLocalAddressesTypeTokensTable =
-    "local_addresses_type_tokens";
-// kGuid = "guid"
-constexpr std::string_view kType = "type";
-// kValue = "value"
-constexpr std::string_view kVerificationStatus = "verification_status";
-constexpr std::string_view kObservations = "observations";
-
 constexpr std::string_view kVirtualCardUsageDataTable =
     "virtual_card_usage_data";
 // kId = "id"
@@ -395,38 +280,12 @@
         {kBenefitId, "VARCHAR NOT NULL"},
         {kMerchantDomain, "VARCHAR NOT NULL"}};
 
-// Helper struct for AutofillTable::RemoveFormElementsAddedBetween().
-// Contains all the necessary fields to update a row in the 'autofill' table.
-struct AutofillUpdate {
-  std::u16string name;
-  std::u16string value;
-  time_t date_created;
-  time_t date_last_used;
-  int count;
-};
-
 // Truncates `data` to the maximum length that can be stored in a column of the
 // Autofill database. Shorter strings are left as-is.
 std::u16string Truncate(const std::u16string& data) {
   return data.substr(0, AutofillTable::kMaxDataLength);
 }
 
-void AddAutofillProfileDetailsFromStatement(sql::Statement& s,
-                                            AutofillProfile* profile) {
-  int index = 0;
-  for (FieldType type :
-       {COMPANY_NAME, ADDRESS_HOME_STREET_ADDRESS,
-        ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE,
-        ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE, ADDRESS_HOME_COUNTRY}) {
-    profile->SetRawInfo(type, s.ColumnString16(index++));
-  }
-  profile->set_use_count(s.ColumnInt64(index++));
-  profile->set_use_date(base::Time::FromTimeT(s.ColumnInt64(index++)));
-  profile->set_modification_date(base::Time::FromTimeT(s.ColumnInt64(index++)));
-  profile->set_language_code(s.ColumnString(index++));
-  profile->set_profile_label(s.ColumnString(index++));
-}
-
 void BindEncryptedValueToColumn(sql::Statement* s,
                                 int column_index,
                                 const std::u16string& value,
@@ -564,7 +423,7 @@
 
 std::unique_ptr<CreditCard> CreditCardFromStatement(
     sql::Statement& card_statement,
-    absl::optional<std::reference_wrapper<sql::Statement>> cvc_statement,
+    std::optional<std::reference_wrapper<sql::Statement>> cvc_statement,
     const AutofillTableEncryptor& encryptor) {
   auto credit_card = std::make_unique<CreditCard>();
 
@@ -619,170 +478,6 @@
   return iban;
 }
 
-bool AddAutofillProfileNamesToProfile(sql::Database* db,
-                                      AutofillProfile* profile) {
-  if (!db->DoesTableExist(kAutofillProfileNamesTable)) {
-    return false;
-  }
-  sql::Statement s;
-  if (SelectByGuid(
-          db, s, kAutofillProfileNamesTable,
-          {kGuid, kHonorificPrefix, kHonorificPrefixStatus, kFirstName,
-           kFirstNameStatus, kMiddleName, kMiddleNameStatus, kFirstLastName,
-           kFirstLastNameStatus, kConjunctionLastName,
-           kConjunctionLastNameStatus, kSecondLastName, kSecondLastNameStatus,
-           kLastName, kLastNameStatus, kFullName, kFullNameStatus,
-           kFullNameWithHonorificPrefix, kFullNameWithHonorificPrefixStatus},
-          profile->guid())) {
-    DCHECK_EQ(profile->guid(), s.ColumnString(0));
-
-    int index = 1;
-    for (FieldType type :
-         {NAME_HONORIFIC_PREFIX, NAME_FIRST, NAME_MIDDLE, NAME_LAST_FIRST,
-          NAME_LAST_CONJUNCTION, NAME_LAST_SECOND, NAME_LAST, NAME_FULL,
-          NAME_FULL_WITH_HONORIFIC_PREFIX}) {
-      profile->SetRawInfoWithVerificationStatusInt(
-          type, s.ColumnString16(index), s.ColumnInt(index + 1));
-      index += 2;
-    }
-  }
-  return s.Succeeded();
-}
-
-bool AddAutofillProfileAddressesToProfile(sql::Database* db,
-                                          AutofillProfile* profile) {
-  if (!db->DoesTableExist(kAutofillProfileAddressesTable)) {
-    return false;
-  }
-  sql::Statement s;
-  if (SelectByGuid(db, s, kAutofillProfileAddressesTable,
-                   {kGuid,
-                    kStreetAddress,
-                    kStreetAddressStatus,
-                    kStreetName,
-                    kStreetNameStatus,
-                    kHouseNumber,
-                    kHouseNumberStatus,
-                    kSubpremise,
-                    kSubpremiseStatus,
-                    kDependentLocality,
-                    kDependentLocalityStatus,
-                    kCity,
-                    kCityStatus,
-                    kState,
-                    kStateStatus,
-                    kZipCode,
-                    kZipCodeStatus,
-                    kSortingCode,
-                    kSortingCodeStatus,
-                    kCountryCode,
-                    kCountryCodeStatus,
-                    kApartmentNumber,
-                    kApartmentNumberStatus,
-                    kFloor,
-                    kFloorStatus},
-                   profile->guid())) {
-    DCHECK_EQ(profile->guid(), s.ColumnString(0));
-    std::u16string street_address = s.ColumnString16(1);
-    std::u16string dependent_locality = s.ColumnString16(13);
-    std::u16string city = s.ColumnString16(15);
-    std::u16string state = s.ColumnString16(17);
-    std::u16string zip_code = s.ColumnString16(19);
-    std::u16string sorting_code = s.ColumnString16(21);
-    std::u16string country = s.ColumnString16(23);
-
-    std::u16string street_address_legacy =
-        profile->GetRawInfo(ADDRESS_HOME_STREET_ADDRESS);
-    std::u16string dependent_locality_legacy =
-        profile->GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY);
-    std::u16string city_legacy = profile->GetRawInfo(ADDRESS_HOME_CITY);
-    std::u16string state_legacy = profile->GetRawInfo(ADDRESS_HOME_STATE);
-    std::u16string zip_code_legacy = profile->GetRawInfo(ADDRESS_HOME_ZIP);
-    std::u16string sorting_code_legacy =
-        profile->GetRawInfo(ADDRESS_HOME_SORTING_CODE);
-    std::u16string country_legacy = profile->GetRawInfo(ADDRESS_HOME_COUNTRY);
-
-    // At this stage, the unstructured address was already written to
-    // the profile. If the address was changed by a legacy client, the
-    // information diverged from the one in this table that is only written by
-    // new clients. In this case remove the corresponding row from this table.
-    // Otherwise, read the new structured tokens and set the verification
-    // statuses for all tokens.
-    if (street_address == street_address_legacy &&
-        dependent_locality == dependent_locality_legacy &&
-        city == city_legacy && state == state_legacy &&
-        zip_code == zip_code_legacy && sorting_code == sorting_code_legacy &&
-        country == country_legacy) {
-      int index = 1;
-      for (FieldType type :
-           {ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_STREET_NAME,
-            ADDRESS_HOME_HOUSE_NUMBER, ADDRESS_HOME_SUBPREMISE,
-            ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_CITY,
-            ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE,
-            ADDRESS_HOME_COUNTRY, ADDRESS_HOME_APT_NUM, ADDRESS_HOME_FLOOR}) {
-        profile->SetRawInfoWithVerificationStatusInt(
-            type, s.ColumnString16(index), s.ColumnInt(index + 1));
-        index += 2;
-      }
-    } else {
-      // Remove the structured information from the table for
-      // eventual deletion consistency.
-      DeleteWhereColumnEq(db, kAutofillProfileAddressesTable, kGuid,
-                          profile->guid());
-    }
-  }
-  return s.Succeeded();
-}
-
-bool AddAutofillProfileEmailsToProfile(sql::Database* db,
-                                       AutofillProfile* profile) {
-  if (!db->DoesTableExist(kAutofillProfileEmailsTable)) {
-    return false;
-  }
-  // TODO(estade): update schema so that multiple emails are not associated
-  // per unique profile guid. Please refer https://crbug.com/497934.
-  sql::Statement s;
-  if (SelectByGuid(db, s, kAutofillProfileEmailsTable, {kGuid, kEmail},
-                   profile->guid())) {
-    DCHECK_EQ(profile->guid(), s.ColumnString(0));
-    profile->SetRawInfo(EMAIL_ADDRESS, s.ColumnString16(1));
-  }
-  return s.Succeeded();
-}
-
-bool AddAutofillProfilePhonesToProfile(sql::Database* db,
-                                       AutofillProfile* profile) {
-  if (!db->DoesTableExist(kAutofillProfilePhonesTable)) {
-    return false;
-  }
-  // TODO(estade): update schema so that multiple phone numbers are not
-  // associated per unique profile guid. Please refer
-  // https://crbug.com/497934.
-  sql::Statement s;
-  if (SelectByGuid(db, s, kAutofillProfilePhonesTable, {kGuid, kNumber},
-                   profile->guid())) {
-    DCHECK_EQ(profile->guid(), s.ColumnString(0));
-    profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, s.ColumnString16(1));
-  }
-  return s.Succeeded();
-}
-
-bool AddAutofillProfileBirthdateToProfile(sql::Database* db,
-                                          AutofillProfile* profile) {
-  if (!db->DoesTableExist(kAutofillProfileBirthdatesTable)) {
-    return false;
-  }
-  sql::Statement s;
-  if (SelectByGuid(db, s, kAutofillProfileBirthdatesTable,
-                   {kGuid, kDay, kMonth, kYear}, profile->guid())) {
-    DCHECK_EQ(profile->guid(), s.ColumnString(0));
-    profile->SetRawInfoAsInt(BIRTHDATE_DAY, s.ColumnInt(1));
-    profile->SetRawInfoAsInt(BIRTHDATE_MONTH, s.ColumnInt(2));
-    profile->SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, s.ColumnInt(3));
-  }
-  return s.Succeeded();
-}
-
 WebDatabaseTable::TypeKey GetKey() {
   // We just need a unique constant. Use the address of a static that
   // COMDAT folding won't touch in an optimizing linker.
@@ -797,138 +492,6 @@
   return end.ToTimeT();
 }
 
-// This helper function binds the `profile`s properties to the placeholders in
-// `s`, in the order the columns are defined in the header file.
-void BindAutofillProfileToStatement(const AutofillProfile& profile,
-                                    sql::Statement& s) {
-  int index = 0;
-  s.BindString(index++, profile.guid());
-  s.BindInt64(index++, profile.use_count());
-  s.BindInt64(index++, profile.use_date().ToTimeT());
-  s.BindInt64(index++, profile.modification_date().ToTimeT());
-  s.BindString(index++, profile.language_code());
-  s.BindString(index++, profile.profile_label());
-  s.BindInt(index++, profile.initial_creator_id());
-  s.BindInt(index++, profile.last_modifier_id());
-}
-
-// Local and account profiles are stored in different tables with the same
-// layout. One table contains profile-level metadata, while another table
-// contains the values for every relevant FieldType. The following two
-// functions are used to map from a profile's `source` to the correct table.
-std::string_view GetProfileMetadataTable(AutofillProfile::Source source) {
-  switch (source) {
-    case AutofillProfile::Source::kLocalOrSyncable:
-      return kLocalAddressesTable;
-    case AutofillProfile::Source::kAccount:
-      return kContactInfoTable;
-  }
-  NOTREACHED_NORETURN();
-}
-std::string_view GetProfileTypeTokensTable(AutofillProfile::Source source) {
-  switch (source) {
-    case AutofillProfile::Source::kLocalOrSyncable:
-      return kLocalAddressesTypeTokensTable;
-    case AutofillProfile::Source::kAccount:
-      return kContactInfoTypeTokensTable;
-  }
-  NOTREACHED_NORETURN();
-}
-
-// Inserts `profile` into `GetProfileMetadataTable()` and
-// `GetProfileTypeTokensTable()`, depending on the profile's source.
-bool AddAutofillProfileToTable(sql::Database* db,
-                               const AutofillProfile& profile) {
-  sql::Statement s;
-  InsertBuilder(db, s, GetProfileMetadataTable(profile.source()),
-                {kGuid, kUseCount, kUseDate, kDateModified, kLanguageCode,
-                 kLabel, kInitialCreatorId, kLastModifierId});
-  BindAutofillProfileToStatement(profile, s);
-  if (!s.Run())
-    return false;
-  for (FieldType type : GetDatabaseStoredTypesOfAutofillProfile()) {
-    if (!base::FeatureList::IsEnabled(
-            features::kAutofillEnableSupportForAddressOverflowAndLandmark) &&
-        type == ADDRESS_HOME_OVERFLOW_AND_LANDMARK) {
-      continue;
-    }
-    if (!base::FeatureList::IsEnabled(
-            features::kAutofillEnableSupportForBetweenStreetsOrLandmark) &&
-        type == ADDRESS_HOME_BETWEEN_STREETS_OR_LANDMARK) {
-      continue;
-    }
-
-    if (!base::FeatureList::IsEnabled(
-            features::kAutofillEnableSupportForAddressOverflow) &&
-        type == ADDRESS_HOME_OVERFLOW) {
-      continue;
-    }
-    if (!base::FeatureList::IsEnabled(
-            features::kAutofillEnableSupportForLandmark) &&
-        type == ADDRESS_HOME_LANDMARK) {
-      continue;
-    }
-    if (!base::FeatureList::IsEnabled(
-            features::kAutofillEnableSupportForBetweenStreets) &&
-        (type == ADDRESS_HOME_BETWEEN_STREETS ||
-         type == ADDRESS_HOME_BETWEEN_STREETS_1 ||
-         type == ADDRESS_HOME_BETWEEN_STREETS_2)) {
-      continue;
-    }
-    if (!base::FeatureList::IsEnabled(
-            features::kAutofillEnableSupportForAdminLevel2) &&
-        type == ADDRESS_HOME_ADMIN_LEVEL2) {
-      continue;
-    }
-    InsertBuilder(db, s, GetProfileTypeTokensTable(profile.source()),
-                  {kGuid, kType, kValue, kVerificationStatus, kObservations});
-    s.BindString(0, profile.guid());
-    s.BindInt(1, type);
-    s.BindString16(2, Truncate(profile.GetRawInfo(type)));
-    s.BindInt(3, profile.GetVerificationStatusInt(type));
-    s.BindBlob(
-        4, profile.token_quality().SerializeObservationsForStoredType(type));
-    if (!s.Run())
-      return false;
-  }
-  return true;
-}
-
-// `MigrateToVersion113MigrateLocalAddressProfilesToNewTable()` migrates
-// profiles from one table layout to another. This function inserts the given
-// `profile` into the `GetProfileMetadataTable()` of schema version 113.
-// `AddAutofillProfileToTable()` can't be reused, since the schema can change in
-// future database versions in ways incompatible with version 113 (e.g. adding
-// a column).
-// The code was copied from `AddAutofillProfileToTable()` in version 113. Like
-// the migration logic, it shouldn't be changed.
-bool AddAutofillProfileToTableVersion113(sql::Database* db,
-                                         const AutofillProfile& profile) {
-  sql::Statement s;
-  InsertBuilder(db, s, GetProfileMetadataTable(profile.source()),
-                {kGuid, kUseCount, kUseDate, kDateModified, kLanguageCode,
-                 kLabel, kInitialCreatorId, kLastModifierId});
-  BindAutofillProfileToStatement(profile, s);
-  if (!s.Run()) {
-    return false;
-  }
-  // Note that `GetDatabaseStoredTypesOfAutofillProfile()` might change in
-  // future versions. Due to the flexible layout of the type tokens table, this
-  // is not a problem.
-  for (FieldType type : GetDatabaseStoredTypesOfAutofillProfile()) {
-    InsertBuilder(db, s, GetProfileTypeTokensTable(profile.source()),
-                  {kGuid, kType, kValue, kVerificationStatus});
-    s.BindString(0, profile.guid());
-    s.BindInt(1, type);
-    s.BindString16(2, Truncate(profile.GetRawInfo(type)));
-    s.BindInt(3, profile.GetVerificationStatusInt(type));
-    if (!s.Run()) {
-      return false;
-    }
-  }
-  return true;
-}
-
 }  // namespace
 
 PaymentInstrumentFields::PaymentInstrumentFields() = default;
@@ -962,11 +525,6 @@
          InitModelTypeStateTable() && InitPaymentsCustomerDataTable() &&
          InitServerCreditCardCloudTokenDataTable() && InitOfferDataTable() &&
          InitOfferEligibleInstrumentTable() && InitOfferMerchantDomainTable() &&
-         InitProfileMetadataTable(AutofillProfile::Source::kAccount) &&
-         InitProfileTypeTokensTable(AutofillProfile::Source::kAccount) &&
-         InitProfileMetadataTable(AutofillProfile::Source::kLocalOrSyncable) &&
-         InitProfileTypeTokensTable(
-             AutofillProfile::Source::kLocalOrSyncable) &&
          InitVirtualCardUsageDataTable() && InitStoredCvcTable() &&
          InitMaskedIbansTable() && InitMaskedIbansMetadataTable() &&
          InitBankAccountsTable() && InitPaymentInstrumentsTable() &&
@@ -998,50 +556,23 @@
     case 87:
       *update_compatible_version = false;
       return MigrateToVersion87AddCreditCardNicknameColumn();
-    case 88:
-      *update_compatible_version = false;
-      return MigrateToVersion88AddNewNameColumns();
     case 89:
       *update_compatible_version = false;
       return MigrateToVersion89AddInstrumentIdColumnToMaskedCreditCard();
-    case 90:
-      *update_compatible_version = false;
-      return MigrateToVersion90AddNewStructuredAddressColumns();
-    case 91:
-      *update_compatible_version = false;
-      return MigrateToVersion91AddMoreStructuredAddressColumns();
-    case 92:
-      *update_compatible_version = false;
-      return MigrateToVersion92AddNewPrefixedNameColumn();
-    case 93:
-      *update_compatible_version = false;
-      return MigrateToVersion93AddAutofillProfileLabelColumn();
     case 94:
       *update_compatible_version = false;
       return MigrateToVersion94AddPromoCodeColumnsToOfferData();
     case 95:
       *update_compatible_version = false;
       return MigrateToVersion95AddVirtualCardMetadata();
-    case 96:
-      *update_compatible_version = false;
-      return MigrateToVersion96AddAutofillProfileDisallowConfirmableMergesColumn();
     case 98:
       *update_compatible_version = true;
       return MigrateToVersion98RemoveStatusColumnMaskedCreditCards();
-    case 99:
-      *update_compatible_version = true;
-      return MigrateToVersion99RemoveAutofillProfilesTrashTable();
-    case 100:
-      *update_compatible_version = true;
-      return MigrateToVersion100RemoveProfileValidityBitfieldColumn();
     case 101:
       // update_compatible_version is set to false because this table is not
       // used since M99.
       *update_compatible_version = false;
       return MigrateToVersion101RemoveCreditCardArtImageTable();
-    case 102:
-      *update_compatible_version = false;
-      return MigrateToVersion102AddAutofillBirthdatesTable();
     case 104:
       *update_compatible_version = false;
       return MigrateToVersion104AddProductDescriptionColumn();
@@ -1051,39 +582,21 @@
     case 106:
       *update_compatible_version = true;
       return MigrateToVersion106RecreateAutofillIbanTable();
-    case 107:
-      *update_compatible_version = false;
-      return MigrateToVersion107AddContactInfoTables();
     case 108:
       *update_compatible_version = false;
       return MigrateToVersion108AddCardIssuerIdColumn();
     case 109:
       *update_compatible_version = false;
       return MigrateToVersion109AddVirtualCardUsageDataTable();
-    case 110:
-      *update_compatible_version = false;
-      return MigrateToVersion110AddInitialCreatorIdAndLastModifierId();
     case 111:
       *update_compatible_version = false;
       return MigrateToVersion111AddVirtualCardEnrollmentTypeColumn();
-    case 112:  // AutofillTable didn't change in WebDatabase version 112.
-      *update_compatible_version = false;
-      return true;
-    case 113:
-      *update_compatible_version = false;
-      return MigrateToVersion113MigrateLocalAddressProfilesToNewTable();
-    case 114:
-      *update_compatible_version = true;
-      return MigrateToVersion114DropLegacyAddressTables();
     case 115:
       *update_compatible_version = true;
       return MigrateToVersion115EncryptIbanValue();
     case 116:
       *update_compatible_version = false;
       return MigrateToVersion116AddStoredCvcTable();
-    case 117:
-      *update_compatible_version = false;
-      return MigrateToVersion117AddProfileObservationColumn();
     case 118:
       *update_compatible_version = true;
       return MigrateToVersion118RemovePaymentsUpiVpaTable();
@@ -1093,12 +606,6 @@
     case 120:
       *update_compatible_version = false;
       return MigrateToVersion120AddPaymentInstrumentAndBankAccountTables();
-    case 121:
-      *update_compatible_version = true;
-      return MigrateToVersion121DropServerAddressTables();
-    case 122:  // AutofillTable didn't change in WebDatabase version 122.
-      *update_compatible_version = false;
-      return true;
     case 123:
       *update_compatible_version = false;
       return MigrateToVersion123AddProductTermsUrlColumnAndAddCardBenefitsTables();
@@ -1106,233 +613,6 @@
   return true;
 }
 
-bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) {
-  sql::Transaction transaction(db_);
-  return transaction.Begin() && AddAutofillProfileToTable(db_, profile) &&
-         transaction.Commit();
-}
-
-bool AutofillTable::UpdateAutofillProfile(const AutofillProfile& profile) {
-  DCHECK(base::Uuid::ParseCaseInsensitive(profile.guid()).is_valid());
-
-  std::unique_ptr<AutofillProfile> old_profile =
-      GetAutofillProfile(profile.guid(), profile.source());
-  if (!old_profile)
-    return false;
-
-  // Implementing an update as remove + add has multiple advantages:
-  // - Prevents outdated (FieldType, value) pairs from remaining in the
-  //   `GetProfileTypeTokensTable(profile)`, in case field types are removed.
-  // - Simpler code.
-  // The possible downside is performance. This is not an issue, as updates
-  // happen rarely and asynchronously.
-  sql::Transaction transaction(db_);
-  return transaction.Begin() &&
-         RemoveAutofillProfile(profile.guid(), profile.source()) &&
-         AddAutofillProfileToTable(db_, profile) && transaction.Commit();
-}
-
-bool AutofillTable::RemoveAutofillProfile(
-    const std::string& guid,
-    AutofillProfile::Source profile_source) {
-  DCHECK(base::Uuid::ParseCaseInsensitive(guid).is_valid());
-  sql::Transaction transaction(db_);
-  return transaction.Begin() &&
-         DeleteWhereColumnEq(db_, GetProfileMetadataTable(profile_source),
-                             kGuid, guid) &&
-         DeleteWhereColumnEq(db_, GetProfileTypeTokensTable(profile_source),
-                             kGuid, guid) &&
-         transaction.Commit();
-}
-
-bool AutofillTable::RemoveAllAutofillProfiles(
-    AutofillProfile::Source profile_source) {
-  sql::Transaction transaction(db_);
-  return transaction.Begin() &&
-         Delete(db_, GetProfileMetadataTable(profile_source)) &&
-         Delete(db_, GetProfileTypeTokensTable(profile_source)) &&
-         transaction.Commit();
-}
-
-std::unique_ptr<AutofillProfile> AutofillTable::GetAutofillProfile(
-    const std::string& guid,
-    AutofillProfile::Source profile_source) const {
-  DCHECK(base::Uuid::ParseCaseInsensitive(guid).is_valid());
-  sql::Statement s;
-  if (!SelectByGuid(db_, s, GetProfileMetadataTable(profile_source),
-                    {kUseCount, kUseDate, kDateModified, kLanguageCode, kLabel,
-                     kInitialCreatorId, kLastModifierId},
-                    guid)) {
-    return nullptr;
-  }
-
-  int index = 0;
-  const int64_t use_count = s.ColumnInt64(index++);
-  const base::Time use_date = base::Time::FromTimeT(s.ColumnInt64(index++));
-  const base::Time modification_date =
-      base::Time::FromTimeT(s.ColumnInt64(index++));
-  const std::string language_code = s.ColumnString(index++);
-  const std::string profile_label = s.ColumnString(index++);
-  const int creator_id = s.ColumnInt(index++);
-  const int modifier_id = s.ColumnInt(index++);
-
-  if (!SelectByGuid(db_, s, GetProfileTypeTokensTable(profile_source),
-                    {kType, kValue, kVerificationStatus, kObservations},
-                    guid)) {
-    return nullptr;
-  }
-
-  struct FieldTypeData {
-    // Type corresponding to the data entry.
-    FieldType type;
-    // Value corresponding to the entry type.
-    std::u16string value;
-    // VerificationStatus of the data entry's `value`.
-    int status;
-    // Serialized observations for the stored type.
-    std::vector<uint8_t> serialized_data;
-  };
-
-  std::vector<FieldTypeData> field_type_values;
-  std::string country_code;
-  // As `SelectByGuid()` already calls `s.Step()`, do-while is used here.
-  do {
-    FieldType type = ToSafeFieldType(s.ColumnInt(0), UNKNOWN_TYPE);
-    if (type == UNKNOWN_TYPE) {
-      // This is possible in two cases:
-      // - The database was tampered with by external means.
-      // - The type corresponding to `s.ColumnInt(0)` was deprecated. In this
-      //   case, due to the structure of
-      //   `GetProfileTypeTokensTable(profile_source)`, it is not necessary to
-      //   add database migration logic or drop a column. Instead, during the
-      //   next update, the data will be dropped.
-      continue;
-    }
-
-    base::span<const uint8_t> observations_data = s.ColumnBlob(3);
-    field_type_values.emplace_back(
-        type, s.ColumnString16(1), s.ColumnInt(2),
-        std::vector<uint8_t>(observations_data.begin(),
-                             observations_data.end()));
-
-    if (type == ADDRESS_HOME_COUNTRY) {
-      country_code = base::UTF16ToUTF8(s.ColumnString16(1));
-    }
-
-  } while (s.Step());
-
-  // TODO(crbug.com/1464568): Define a proper migration strategy from stored
-  // legacy profiles into i18n ones.
-  auto profile = std::make_unique<AutofillProfile>(
-      guid, profile_source, AddressCountryCode(country_code));
-  profile->set_use_count(use_count);
-  profile->set_use_date(use_date);
-  profile->set_modification_date(modification_date);
-  profile->set_language_code(language_code);
-  profile->set_profile_label(profile_label);
-  profile->set_initial_creator_id(creator_id);
-  profile->set_last_modifier_id(modifier_id);
-
-  for (const auto& data : field_type_values) {
-    profile->SetRawInfoWithVerificationStatusInt(data.type, data.value,
-                                                 data.status);
-    profile->token_quality().LoadSerializedObservationsForStoredType(
-        data.type, data.serialized_data);
-  }
-
-  profile->FinalizeAfterImport();
-  return profile;
-}
-
-bool AutofillTable::GetAutofillProfiles(
-    AutofillProfile::Source profile_source,
-    std::vector<std::unique_ptr<AutofillProfile>>* profiles) const {
-  CHECK(profiles);
-  profiles->clear();
-
-  sql::Statement s;
-  SelectBuilder(db_, s, GetProfileMetadataTable(profile_source), {kGuid});
-  while (s.Step()) {
-    std::string guid = s.ColumnString(0);
-    std::unique_ptr<AutofillProfile> profile =
-        GetAutofillProfile(guid, profile_source);
-    if (!profile) {
-      continue;
-    }
-    profiles->push_back(std::move(profile));
-  }
-
-  return s.Succeeded();
-}
-
-std::unique_ptr<AutofillProfile>
-AutofillTable::GetAutofillProfileFromLegacyTable(
-    const std::string& guid) const {
-  sql::Statement s;
-  if (!SelectByGuid(db_, s, kAutofillProfilesTable,
-                    {kCompanyName, kStreetAddress, kDependentLocality, kCity,
-                     kState, kZipcode, kSortingCode, kCountryCode, kUseCount,
-                     kUseDate, kDateModified, kLanguageCode, kLabel},
-                    guid)) {
-    return nullptr;
-  }
-
-  auto profile = std::make_unique<AutofillProfile>(
-      guid, AutofillProfile::Source::kLocalOrSyncable,
-      i18n_model_definition::kLegacyHierarchyCountryCode);
-
-  DCHECK(base::Uuid::ParseCaseInsensitive(profile->guid()).is_valid());
-
-  // Get associated name info using guid.
-  AddAutofillProfileNamesToProfile(db_, profile.get());
-
-  // Get associated email info using guid.
-  AddAutofillProfileEmailsToProfile(db_, profile.get());
-
-  // Get associated phone info using guid.
-  AddAutofillProfilePhonesToProfile(db_, profile.get());
-
-  // Get associated birthdate info using guid.
-  AddAutofillProfileBirthdateToProfile(db_, profile.get());
-
-  // The details should be added after the other info to make sure they don't
-  // change when we change the names/emails/phones.
-  AddAutofillProfileDetailsFromStatement(s, profile.get());
-
-  // The structured address information should be added after the street_address
-  // from the query above was  written because this information is used to
-  // detect changes by a legacy client.
-  AddAutofillProfileAddressesToProfile(db_, profile.get());
-
-  // For more-structured profiles, the profile must be finalized to fully
-  // populate the name fields.
-  profile->FinalizeAfterImport();
-
-  return profile;
-}
-
-// TODO(crbug.com/1443393): This function's implementation is very similar to
-// `GetAutofillProfiles()`. Simplify somehow.
-bool AutofillTable::GetAutofillProfilesFromLegacyTable(
-    std::vector<std::unique_ptr<AutofillProfile>>* profiles) const {
-  DCHECK(profiles);
-  profiles->clear();
-
-  sql::Statement s;
-  SelectBuilder(db_, s, kAutofillProfilesTable, {kGuid});
-
-  while (s.Step()) {
-    std::string guid = s.ColumnString(0);
-    std::unique_ptr<AutofillProfile> profile =
-        GetAutofillProfileFromLegacyTable(guid);
-    if (!profile)
-      continue;
-    profiles->push_back(std::move(profile));
-  }
-
-  return s.Succeeded();
-}
-
 std::unique_ptr<BankAccount> AutofillTable::GetBankAccount(
     const PaymentInstrumentFields& payment_instrument_fields) {
   sql::Statement s;
@@ -1821,9 +1101,9 @@
   bool has_cvc = cvc_statement.Step();
   return CreditCardFromStatement(
       card_statement,
-      has_cvc ? absl::optional<
-                    std::reference_wrapper<sql::Statement>>{cvc_statement}
-              : absl::nullopt,
+      has_cvc
+          ? std::optional<std::reference_wrapper<sql::Statement>>{cvc_statement}
+          : std::nullopt,
       *autofill_table_encryptor_);
 }
 
@@ -2590,10 +1870,8 @@
   if (!transaction.Begin())
     return false;  // Some error, nothing was changed.
 
-  RemoveAllAutofillProfiles(AutofillProfile::Source::kLocalOrSyncable);
-  bool changed = db_->GetLastChangeCount() > 0;
   ClearLocalPaymentMethodsData();
-  changed |= db_->GetLastChangeCount() > 0;
+  bool changed = db_->GetLastChangeCount() > 0;
 
   transaction.Commit();
   return changed;
@@ -2602,40 +1880,12 @@
 bool AutofillTable::RemoveAutofillDataModifiedBetween(
     const base::Time& delete_begin,
     const base::Time& delete_end,
-    std::vector<std::unique_ptr<AutofillProfile>>* profiles,
     std::vector<std::unique_ptr<CreditCard>>* credit_cards) {
   DCHECK(delete_end.is_null() || delete_begin < delete_end);
 
   time_t delete_begin_t = delete_begin.ToTimeT();
   time_t delete_end_t = GetEndTime(delete_end);
 
-  // Remember Autofill profiles in the time range.
-  sql::Statement s_profiles_get;
-  SelectBetween(
-      db_, s_profiles_get,
-      GetProfileMetadataTable(AutofillProfile::Source::kLocalOrSyncable),
-      {kGuid}, kDateModified, delete_begin_t, delete_end_t);
-
-  profiles->clear();
-  while (s_profiles_get.Step()) {
-    std::string guid = s_profiles_get.ColumnString(0);
-    std::unique_ptr<AutofillProfile> profile =
-        GetAutofillProfile(guid, AutofillProfile::Source::kLocalOrSyncable);
-    if (!profile)
-      return false;
-    profiles->push_back(std::move(profile));
-  }
-  if (!s_profiles_get.Succeeded())
-    return false;
-
-  // Remove Autofill profiles in the time range.
-  for (const std::unique_ptr<AutofillProfile>& profile : *profiles) {
-    if (!RemoveAutofillProfile(profile->guid(),
-                               AutofillProfile::Source::kLocalOrSyncable)) {
-      return false;
-    }
-  }
-
   // Remember Autofill credit cards in the time range.
   sql::Statement s_credit_cards_get;
   SelectBetween(db_, s_credit_cards_get, kCreditCardsTable, {kGuid},
@@ -2807,7 +2057,7 @@
 bool AutofillTable::MigrateToVersion83RemoveServerCardTypeColumn() {
   sql::Transaction transaction(db_);
   return transaction.Begin() &&
-         DropColumn(db_, kMaskedCreditCardsTable, kType) &&
+         DropColumn(db_, kMaskedCreditCardsTable, "type") &&
          transaction.Commit();
 }
 
@@ -2824,37 +2074,6 @@
                               "INTEGER DEFAULT 0");
 }
 
-bool AutofillTable::MigrateToVersion88AddNewNameColumns() {
-  for (std::string_view column : {kHonorificPrefix, kFirstLastName,
-                                  kConjunctionLastName, kSecondLastName}) {
-    if (!AddColumnIfNotExists(db_, kAutofillProfileNamesTable, column,
-                              "VARCHAR")) {
-      return false;
-    }
-  }
-
-  for (std::string_view column :
-       {kHonorificPrefixStatus, kFirstNameStatus, kMiddleNameStatus,
-        kLastNameStatus, kFirstLastNameStatus, kConjunctionLastNameStatus,
-        kSecondLastNameStatus, kFullNameStatus}) {
-    // The default value of 0 corresponds to the verification status
-    // |kNoStatus|.
-    if (!AddColumnIfNotExists(db_, kAutofillProfileNamesTable, column,
-                              "INTEGER DEFAULT 0")) {
-      return false;
-    }
-  }
-  return true;
-}
-
-bool AutofillTable::MigrateToVersion92AddNewPrefixedNameColumn() {
-  return AddColumnIfNotExists(db_, kAutofillProfileNamesTable,
-                              kFullNameWithHonorificPrefix, "VARCHAR") &&
-         AddColumnIfNotExists(db_, kAutofillProfileNamesTable,
-                              kFullNameWithHonorificPrefixStatus,
-                              "INTEGER DEFAULT 0");
-}
-
 bool AutofillTable::MigrateToVersion86RemoveUnmaskedCreditCardsUseColumns() {
   sql::Transaction transaction(db_);
   return transaction.Begin() &&
@@ -2868,70 +2087,6 @@
   return AddColumnIfNotExists(db_, kCreditCardsTable, kNickname, "VARCHAR");
 }
 
-bool AutofillTable::MigrateToVersion90AddNewStructuredAddressColumns() {
-  if (!db_->DoesTableExist("autofill_profile_addresses"))
-    InitLegacyProfileAddressesTable();
-
-  for (std::string_view column : {kDependentLocality, kCity, kState, kZipCode,
-                                  kSortingCode, kCountryCode}) {
-    if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column,
-                              "VARCHAR")) {
-      return false;
-    }
-  }
-
-  for (std::string_view column :
-       {kDependentLocalityStatus, kCityStatus, kStateStatus, kZipCodeStatus,
-        kSortingCodeStatus, kCountryCodeStatus}) {
-    // The default value of 0 corresponds to the verification status
-    // |kNoStatus|.
-    if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column,
-                              "INTEGER DEFAULT 0")) {
-      return false;
-    }
-  }
-  return true;
-}
-
-bool AutofillTable::MigrateToVersion91AddMoreStructuredAddressColumns() {
-  if (!db_->DoesTableExist(kAutofillProfileAddressesTable))
-    InitLegacyProfileAddressesTable();
-
-  for (std::string_view column : {kApartmentNumber, kFloor}) {
-    if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column,
-                              "VARCHAR")) {
-      return false;
-    }
-  }
-
-  for (std::string_view column : {kApartmentNumberStatus, kFloorStatus}) {
-    // The default value of 0 corresponds to the verification status
-    // |kNoStatus|.
-    if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column,
-                              "INTEGER DEFAULT 0")) {
-      return false;
-    }
-  }
-  return true;
-}
-
-bool AutofillTable::MigrateToVersion93AddAutofillProfileLabelColumn() {
-  if (!db_->DoesTableExist(kAutofillProfilesTable))
-    InitLegacyProfileAddressesTable();
-
-  return AddColumnIfNotExists(db_, kAutofillProfilesTable, kLabel, "VARCHAR");
-}
-
-bool AutofillTable::
-    MigrateToVersion96AddAutofillProfileDisallowConfirmableMergesColumn() {
-  if (!db_->DoesTableExist(kAutofillProfilesTable))
-    InitLegacyProfileAddressesTable();
-
-  return AddColumnIfNotExists(db_, kAutofillProfilesTable,
-                              kDisallowSettingsVisibleUpdates,
-                              "INTEGER NOT NULL DEFAULT 0");
-}
-
 bool AutofillTable::
     MigrateToVersion89AddInstrumentIdColumnToMaskedCreditCard() {
   // Add the new instrument_id column to the masked_credit_cards table and set
@@ -2989,31 +2144,10 @@
          transaction.Commit();
 }
 
-bool AutofillTable::MigrateToVersion99RemoveAutofillProfilesTrashTable() {
-  return DropTableIfExists(db_, "autofill_profiles_trash");
-}
-
-bool AutofillTable::MigrateToVersion100RemoveProfileValidityBitfieldColumn() {
-  sql::Transaction transaction(db_);
-  return transaction.Begin() &&
-         DropColumn(db_, kAutofillProfilesTable, "validity_bitfield") &&
-         DropColumn(db_, kAutofillProfilesTable,
-                    "is_client_validity_states_updated") &&
-         transaction.Commit();
-}
-
 bool AutofillTable::MigrateToVersion101RemoveCreditCardArtImageTable() {
   return DropTableIfExists(db_, "credit_card_art_images");
 }
 
-bool AutofillTable::MigrateToVersion102AddAutofillBirthdatesTable() {
-  return CreateTable(db_, kAutofillProfileBirthdatesTable,
-                     {{kGuid, "VARCHAR"},
-                      {kDay, "INTEGER DEFAULT 0"},
-                      {kMonth, "INTEGER DEFAULT 0"},
-                      {kYear, "INTEGER DEFAULT 0"}});
-}
-
 bool AutofillTable::MigrateToVersion104AddProductDescriptionColumn() {
   sql::Transaction transaction(db_);
   if (!transaction.Begin())
@@ -3052,25 +2186,6 @@
          transaction.Commit();
 }
 
-bool AutofillTable::MigrateToVersion107AddContactInfoTables() {
-  sql::Transaction transaction(db_);
-  return transaction.Begin() &&
-         CreateTable(db_, kContactInfoTable,
-                     {{kGuid, "VARCHAR PRIMARY KEY"},
-                      {kUseCount, "INTEGER NOT NULL DEFAULT 0"},
-                      {kUseDate, "INTEGER NOT NULL DEFAULT 0"},
-                      {kDateModified, "INTEGER NOT NULL DEFAULT 0"},
-                      {kLanguageCode, "VARCHAR"},
-                      {kLabel, "VARCHAR"}}) &&
-         CreateTable(db_, kContactInfoTypeTokensTable,
-                     {{kGuid, "VARCHAR"},
-                      {kType, "INTEGER"},
-                      {kValue, "VARCHAR"},
-                      {kVerificationStatus, "INTEGER DEFAULT 0"}},
-                     /*composite_primary_key=*/{kGuid, kType}) &&
-         transaction.Commit();
-}
-
 bool AutofillTable::MigrateToVersion108AddCardIssuerIdColumn() {
   // Add card_issuer_id to masked_credit_cards.
   return db_->DoesTableExist(kMaskedCreditCardsTable) &&
@@ -3086,78 +2201,12 @@
                       {kLastFour, "VARCHAR"}});
 }
 
-bool AutofillTable::MigrateToVersion110AddInitialCreatorIdAndLastModifierId() {
-  if (!db_->DoesTableExist(kContactInfoTable)) {
-    return false;
-  }
-  sql::Transaction transaction(db_);
-  return transaction.Begin() &&
-         AddColumnIfNotExists(db_, kContactInfoTable, kInitialCreatorId,
-                              "INTEGER DEFAULT 0") &&
-         AddColumnIfNotExists(db_, kContactInfoTable, kLastModifierId,
-                              "INTEGER DEFAULT 0") &&
-         transaction.Commit();
-}
-
 bool AutofillTable::MigrateToVersion111AddVirtualCardEnrollmentTypeColumn() {
   return db_->DoesTableExist(kMaskedCreditCardsTable) &&
          AddColumnIfNotExists(db_, kMaskedCreditCardsTable,
                               kVirtualCardEnrollmentType, "INTEGER DEFAULT 0");
 }
 
-bool AutofillTable::MigrateToVersion113MigrateLocalAddressProfilesToNewTable() {
-  sql::Transaction transaction(db_);
-  if (!transaction.Begin() ||
-      !CreateTableIfNotExists(db_, kLocalAddressesTable,
-                              {{kGuid, "VARCHAR PRIMARY KEY"},
-                               {kUseCount, "INTEGER NOT NULL DEFAULT 0"},
-                               {kUseDate, "INTEGER NOT NULL DEFAULT 0"},
-                               {kDateModified, "INTEGER NOT NULL DEFAULT 0"},
-                               {kLanguageCode, "VARCHAR"},
-                               {kLabel, "VARCHAR"},
-                               {kInitialCreatorId, "INTEGER DEFAULT 0"},
-                               {kLastModifierId, "INTEGER DEFAULT 0"}}) ||
-      !CreateTableIfNotExists(db_, kLocalAddressesTypeTokensTable,
-                              {{kGuid, "VARCHAR"},
-                               {kType, "INTEGER"},
-                               {kValue, "VARCHAR"},
-                               {kVerificationStatus, "INTEGER DEFAULT 0"}},
-                              /*composite_primary_key=*/{kGuid, kType})) {
-    return false;
-  }
-  bool success = true;
-  if (db_->DoesTableExist(kAutofillProfilesTable)) {
-    std::vector<std::unique_ptr<AutofillProfile>> profiles;
-    success = GetAutofillProfilesFromLegacyTable(&profiles);
-    // Migrate profiles to the new tables. Preserve the modification dates.
-    for (const std::unique_ptr<AutofillProfile>& profile : profiles) {
-      success = success && AddAutofillProfileToTableVersion113(db_, *profile);
-    }
-  }
-  // Delete all profiles from the legacy tables. The tables are dropped in
-  // version 114.
-  for (std::string_view deprecated_table :
-       {kAutofillProfilesTable, kAutofillProfileAddressesTable,
-        kAutofillProfileNamesTable, kAutofillProfileEmailsTable,
-        kAutofillProfilePhonesTable, kAutofillProfileBirthdatesTable}) {
-    success = success && (!db_->DoesTableExist(deprecated_table) ||
-                          Delete(db_, deprecated_table));
-  }
-  return success && transaction.Commit();
-}
-
-bool AutofillTable::MigrateToVersion114DropLegacyAddressTables() {
-  sql::Transaction transaction(db_);
-  bool success = transaction.Begin();
-  for (std::string_view deprecated_table :
-       {kAutofillProfilesTable, kAutofillProfileAddressesTable,
-        kAutofillProfileNamesTable, kAutofillProfileEmailsTable,
-        kAutofillProfilePhonesTable, kAutofillProfileBirthdatesTable}) {
-    success = success && DropTableIfExists(db_, deprecated_table);
-  }
-  return success && transaction.Commit();
-}
-
 bool AutofillTable::MigrateToVersion115EncryptIbanValue() {
   // Encrypt all existing IBAN values and rename the column name from `value` to
   // `value_encrypted` by the following steps:
@@ -3210,15 +2259,6 @@
          transaction.Commit();
 }
 
-bool AutofillTable::MigrateToVersion117AddProfileObservationColumn() {
-  sql::Transaction transaction(db_);
-  return transaction.Begin() &&
-         AddColumn(db_, kContactInfoTypeTokensTable, kObservations, "BLOB") &&
-         AddColumn(db_, kLocalAddressesTypeTokensTable, kObservations,
-                   "BLOB") &&
-         transaction.Commit();
-}
-
 bool AutofillTable::MigrateToVersion118RemovePaymentsUpiVpaTable() {
   sql::Transaction transaction(db_);
   return transaction.Begin() && DropTableIfExists(db_, kPaymentsUpiVpaTable) &&
@@ -3262,13 +2302,6 @@
          transaction.Commit();
 }
 
-bool AutofillTable::MigrateToVersion121DropServerAddressTables() {
-  sql::Transaction transaction(db_);
-  return transaction.Begin() && DropTableIfExists(db_, "server_addresses") &&
-         DropTableIfExists(db_, "server_address_metadata") &&
-         transaction.Commit();
-}
-
 bool AutofillTable::
     MigrateToVersion123AddProductTermsUrlColumnAndAddCardBenefitsTables() {
   sql::Transaction transaction(db_);
@@ -3433,107 +2466,6 @@
                                  {kNickname, "VARCHAR"}});
 }
 
-bool AutofillTable::InitLegacyProfilesTable() {
-  return CreateTableIfNotExists(
-      db_, kAutofillProfilesTable,
-      {{kGuid, "VARCHAR PRIMARY KEY"},
-       {kCompanyName, "VARCHAR"},
-       {kStreetAddress, "VARCHAR"},
-       {kDependentLocality, "VARCHAR"},
-       {kCity, "VARCHAR"},
-       {kState, "VARCHAR"},
-       {kZipcode, "VARCHAR"},
-       {kSortingCode, "VARCHAR"},
-       {kCountryCode, "VARCHAR"},
-       {kDateModified, "INTEGER NOT NULL DEFAULT 0"},
-       {kOrigin, "VARCHAR DEFAULT ''"},
-       {kLanguageCode, "VARCHAR"},
-       {kUseCount, "INTEGER NOT NULL DEFAULT 0"},
-       {kUseDate, "INTEGER NOT NULL DEFAULT 0"},
-       {kLabel, "VARCHAR"},
-       {kDisallowSettingsVisibleUpdates, "INTEGER NOT NULL DEFAULT 0"}});
-}
-
-bool AutofillTable::InitLegacyProfileNamesTable() {
-  // The default value of 0 corresponds to the verification status
-  // |kNoStatus|.
-  return CreateTableIfNotExists(
-      db_, kAutofillProfileNamesTable,
-      {{kGuid, "VARCHAR"},
-       {kFirstName, "VARCHAR"},
-       {kMiddleName, "VARCHAR"},
-       {kLastName, "VARCHAR"},
-       {kFullName, "VARCHAR"},
-       {kHonorificPrefix, "VARCHAR"},
-       {kFirstLastName, "VARCHAR"},
-       {kConjunctionLastName, "VARCHAR"},
-       {kSecondLastName, "VARCHAR"},
-       {kHonorificPrefixStatus, "INTEGER DEFAULT 0"},
-       {kFirstNameStatus, "INTEGER DEFAULT 0"},
-       {kMiddleNameStatus, "INTEGER DEFAULT 0"},
-       {kLastNameStatus, "INTEGER DEFAULT 0"},
-       {kFirstLastNameStatus, "INTEGER DEFAULT 0"},
-       {kConjunctionLastNameStatus, "INTEGER DEFAULT 0"},
-       {kSecondLastNameStatus, "INTEGER DEFAULT 0"},
-       {kFullNameStatus, "INTEGER DEFAULT 0"},
-       {kFullNameWithHonorificPrefix, "VARCHAR"},
-       {kFullNameWithHonorificPrefixStatus, "INTEGER DEFAULT 0"}});
-}
-
-bool AutofillTable::InitLegacyProfileAddressesTable() {
-  // The default value of 0 corresponds to the verification status
-  // |kNoStatus|.
-  return CreateTableIfNotExists(
-      db_, kAutofillProfileAddressesTable,
-      {{kGuid, "VARCHAR"},
-       {kStreetAddress, "VARCHAR"},
-       {kStreetName, "VARCHAR"},
-       {kDependentStreetName, "VARCHAR"},
-       {kHouseNumber, "VARCHAR"},
-       {kSubpremise, "VARCHAR"},
-       {"premise_name", "VARCHAR"},
-       {kStreetAddressStatus, "INTEGER DEFAULT 0"},
-       {kStreetNameStatus, "INTEGER DEFAULT 0"},
-       {kDependentStreetNameStatus, "INTEGER DEFAULT 0"},
-       {kHouseNumberStatus, "INTEGER DEFAULT 0"},
-       {kSubpremiseStatus, "INTEGER DEFAULT 0"},
-       {"premise_name_status", "INTEGER DEFAULT 0"},
-       {kDependentLocality, "VARCHAR"},
-       {kCity, "VARCHAR"},
-       {kState, "VARCHAR"},
-       {kZipCode, "VARCHAR"},
-       {kSortingCode, "VARCHAR"},
-       {kCountryCode, "VARCHAR"},
-       {kDependentLocalityStatus, "INTEGER DEFAULT 0"},
-       {kCityStatus, "INTEGER DEFAULT 0"},
-       {kStateStatus, "INTEGER DEFAULT 0"},
-       {kZipCodeStatus, "INTEGER DEFAULT 0"},
-       {kSortingCodeStatus, "INTEGER DEFAULT 0"},
-       {kCountryCodeStatus, "INTEGER DEFAULT 0"},
-       {kApartmentNumber, "VARCHAR"},
-       {kFloor, "VARCHAR"},
-       {kApartmentNumberStatus, "INTEGER DEFAULT 0"},
-       {kFloorStatus, "INTEGER DEFAULT 0"}});
-}
-
-bool AutofillTable::InitLegacyProfileEmailsTable() {
-  return CreateTableIfNotExists(db_, kAutofillProfileEmailsTable,
-                                {{kGuid, "VARCHAR"}, {kEmail, "VARCHAR"}});
-}
-
-bool AutofillTable::InitLegacyProfilePhonesTable() {
-  return CreateTableIfNotExists(db_, kAutofillProfilePhonesTable,
-                                {{kGuid, "VARCHAR"}, {kNumber, "VARCHAR"}});
-}
-
-bool AutofillTable::InitLegacyProfileBirthdatesTable() {
-  return CreateTableIfNotExists(db_, kAutofillProfileBirthdatesTable,
-                                {{kGuid, "VARCHAR"},
-                                 {kDay, "INTEGER DEFAULT 0"},
-                                 {kMonth, "INTEGER DEFAULT 0"},
-                                 {kYear, "INTEGER DEFAULT 0"}});
-}
-
 bool AutofillTable::InitMaskedCreditCardsTable() {
   return CreateTableIfNotExists(
       db_, kMaskedCreditCardsTable,
@@ -3655,28 +2587,6 @@
       {{kOfferId, "UNSIGNED LONG"}, {kMerchantDomain, "VARCHAR"}});
 }
 
-bool AutofillTable::InitProfileMetadataTable(AutofillProfile::Source source) {
-  return CreateTableIfNotExists(db_, GetProfileMetadataTable(source),
-                                {{kGuid, "VARCHAR PRIMARY KEY"},
-                                 {kUseCount, "INTEGER NOT NULL DEFAULT 0"},
-                                 {kUseDate, "INTEGER NOT NULL DEFAULT 0"},
-                                 {kDateModified, "INTEGER NOT NULL DEFAULT 0"},
-                                 {kLanguageCode, "VARCHAR"},
-                                 {kLabel, "VARCHAR"},
-                                 {kInitialCreatorId, "INTEGER DEFAULT 0"},
-                                 {kLastModifierId, "INTEGER DEFAULT 0"}});
-}
-
-bool AutofillTable::InitProfileTypeTokensTable(AutofillProfile::Source source) {
-  return CreateTableIfNotExists(db_, GetProfileTypeTokensTable(source),
-                                {{kGuid, "VARCHAR"},
-                                 {kType, "INTEGER"},
-                                 {kValue, "VARCHAR"},
-                                 {kVerificationStatus, "INTEGER DEFAULT 0"},
-                                 {kObservations, "BLOB"}},
-                                /*composite_primary_key=*/{kGuid, kType});
-}
-
 bool AutofillTable::InitVirtualCardUsageDataTable() {
   return CreateTableIfNotExists(db_, kVirtualCardUsageDataTable,
                                 {{kId, "VARCHAR PRIMARY KEY"},
diff --git a/components/autofill/core/browser/webdata/autofill_table.h b/components/autofill/core/browser/webdata/autofill_table.h
index b0e5aad..e5937f5 100644
--- a/components/autofill/core/browser/webdata/autofill_table.h
+++ b/components/autofill/core/browser/webdata/autofill_table.h
@@ -15,7 +15,6 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/time/time.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/payment_instrument.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/model/sync_metadata_store.h"
@@ -85,168 +84,6 @@
 //
 // Note: The database stores time in seconds, UTC.
 //
-// DEPRECATED. Use local_addresses instead.
-// autofill_profiles    This table contains Autofill profile data added by the
-//                      user with the Autofill dialog.  Most of the columns are
-//                      standard entries in a contact information form.
-//
-//   guid               A guid string to uniquely identify the profile.
-//                      Added in version 31.
-//   label              A user-chosen and user-visible label for the profile to
-//                      help identifying the semantics of the profile. The user
-//                      can choose an arbitrary string in principle, but the
-//                      values '$HOME$' and '$WORK$' indicate a special meaning.
-//   company_name
-//   street_address     The combined lines of the street address.
-//                      Added in version 54.
-//   dependent_locality
-//                      A sub-classification beneath the city, e.g. an
-//                      inner-city district or suburb.  Added in version 54.
-//   city
-//   state
-//   zipcode
-//   sorting_code       Similar to the zipcode column, but used for businesses
-//                      or organizations that might not be geographically
-//                      contiguous.  The canonical example is CEDEX in France.
-//                      Added in version 54.
-//   country_code
-//   use_count          The number of times this profile has been used to fill
-//                      a form. Added in version 61.
-//   use_date           The date this profile was last used to fill a form,
-//                      in time_t. Added in version 61.
-//   date_modified      The date on which this profile was last modified, in
-//                      time_t. Added in version 30.
-//   origin             The domain of origin for this profile.
-//                      Added in version 50.
-//   language_code      The BCP 47 language code used to format the address for
-//                      display. For example, a JP address with "ja" language
-//                      code starts with the postal code, but a JP address with
-//                      "ja-latn" language code starts with the recipient name.
-//                      Added in version 56.
-//   disallow_settings_visible_updates
-//                      If true, a profile does not qualify to get merged with
-//                      a profile observed in a form submission.
-//
-// DEPRECATED. See autofill_profiles.
-// autofill_profile_addresses
-//   guid               The guid string that identifies the profile to which
-//                      the name belongs.
-//                      This table stores the structured address information.
-//   street_address     Stores the street address. This field is also stored in
-//                      the profile table and is used to detect if a legacy
-//                      client that does not support writing to this table
-//                      changed the address. If this is true, the address stored
-//                      in the table is removed.
-//   street_name        The name of the street.
-//   dependent_street_name
-//                      The name of the crossing street.
-//   house_number       The house number.
-//   subpremise         The floor, apartment number and staircase.
-//                      apartment number.
-//   dependent_locality
-//                      A sub-classification beneath the city, e.g. an
-//                      inner-city district or suburb.
-//   city               The city information of the address.
-//   state              The state information of the address.
-//   zip_code           The zip code of the address.
-//   country_code       The code of the country of the address.
-//   sorting_code       Similar to the zipcode column, but used for businesses
-//                      or organizations that might not be geographically
-//                      contiguous.
-//   premise_name       The name of the premise.
-//   apartment_number   The number of the apartment.
-//   floor              The floor in which the apartment is located.
-//   street_address_status
-//   street_name_status
-//   dependent_street_name_status
-//   house_number_status
-//   subpremise_status
-//   premise_name_status
-//   dependent_locality_status
-//   city_status
-//   state_status
-//   zip_code_status
-//   country_code_status
-//   sorting_code_status
-//   apartment_number_status
-//   floor_status
-//                      Each token of the address has an additional validation
-//                      status that indicates if Autofill parsed the value out
-//                      of an unstructured (last) name, or if autofill formatted
-//                      the token from its structured subcomponents, or if the
-//                      value was observed in a form submission, or even
-//                      validated by the user in the settings.
-//
-// DEPRECATED. See autofill_profiles.
-// autofill_profile_names
-//                      This table contains the multi-valued name fields
-//                      associated with a profile.
-//
-//   guid               The guid string that identifies the profile to which
-//                      the name belongs.
-//   honorific_prefix   The honorific prefix of a person like Ms, Mr or Prof
-//   first_name         The first name of a person.
-//   middle_name        The middle name or even names of a person.
-//   last_name          The unstructured last name that is a combination of the
-//                      first and second last name.
-//   first_last_name    The first part of the last name. Mostly used for
-//                      Latinx/Hispanic last names.
-//   conjunction_last_name
-//                      An optional conjunction that is mostly used in
-//                      Hispanic/Latinx last names in between the first and
-//                      second last name in the unstructured representation.
-//   second_last_name   The second part of the last names. Last names only
-//                      consisting of a single part are stored in the second
-//                      part by default.
-//   full_name          The unstructured full name of a person.
-//   full_name_with_honorific_prefix
-//                      The combination of the full name and the honorific
-//                      prefix.
-//   honorific_prefix_status
-//   first_name_status
-//   middle_name_status
-//   last_name_status
-//   first_last_name_status
-//   conjunction_last_name_status
-//   second_last_name_status
-//   full_name_status
-//   full_name_with_honorific_prefix_status
-//                      Each token of the names has an additional validation
-//                      status that indicates if Autofill parsed the value out
-//                      of an unstructured (last) name, or if autofill formatted
-//                      the token from its structured subcomponents, or if the
-//                      value was observed in a form submission, or even
-//                      validated by the user in the settings.
-//
-// DEPRECATED. See autofill_profiles.
-// autofill_profile_emails
-//                      This table contains the multi-valued email fields
-//                      associated with a profile.
-//
-//   guid               The guid string that identifies the profile to which
-//                      the email belongs.
-//   email
-//
-// DEPRECATED. See autofill_profiles.
-// autofill_profile_phones
-//                      This table contains the multi-valued phone fields
-//                      associated with a profile.
-//
-//   guid               The guid string that identifies the profile to which the
-//                      phone number belongs.
-//   number
-//
-// DEPRECATED. See autofill_profiles.
-// autofill_profile_birthdates
-//                      This table contains the multi-valued birthdate fields
-//                      associated with a profile.
-//
-//   guid               The guid string that identifies the profile to which the
-//                      birthdate number belongs.
-//   day                As an integer between 1 and 31 inclusive, or 0 if unset.
-//   month              As an integer between 1 and 12 inclusive, or 0 if unset.
-//   year               As a 4 digit integer, or 0 if unset.
-//
 // credit_cards         This table contains credit card data added by the user
 //                      with the Autofill dialog.  Most of the columns are
 //                      standard entries in a credit card form.
@@ -464,53 +301,6 @@
 //   merchant_domain    List of full origins for merchant websites on which
 //                      this offer would apply.
 //
-// contact_info         This table contains Autofill profile data synced from a
-//                      remote source.
-// local_addresses      This table contains kLocalOrSyncable Autofill profiles.
-//                      It has the same layout as the contact_info table.
-//
-//   guid               A guid string to uniquely identify the profile.
-//   use_count          The number of times this profile has been used to fill a
-//                      form.
-//   use_date           The date this profile was last used to fill a form, in
-//                      time_t.
-//   date_modified      The date on which this profile was last modified, in
-//                      time_t.
-//   language_code      The BCP 47 language code used to format the address for
-//                      display. For example, a JP address with "ja" language
-//                      code starts with the postal code, but a JP address with
-//                      "ja-latn" language code starts with the recipient name.
-//   label              A user-chosen and user-visible label for the profile to
-//                      help identifying the semantics of the profile. The user
-//                      can choose an arbitrary string in principle, but the
-//                      values '$HOME$' and '$WORK$' indicate a special meaning.
-//   initial_creator_id The application that initially created the profile.
-//                      Represented as an integer. See AutofillProfile.
-//   last_modifier_id   The application that performed the last non-metadata
-//                      modification of the profile.
-//                      Represented as an integer. See AutofillProfile.
-//
-// contact_info_type_tokens
-//                      Contains the values for all relevant ServerFieldTypes of
-//                      a contact_info entry. At most one entry per (guid, type)
-//                      pair exists.
-// local_addresses_type_tokens
-//                      Like contact_info_type_tokens, but for local_addresses.
-//
-//  guid                The guid of the corresponding profile in contact_info.
-//  type                The FieldType, represented by its integer value in
-//                      the FieldType enum.
-//  value               The string value of the type.
-//  verification_status Each token has an additional validation status that
-//                      indicates if Autofill parsed the value out of an
-//                      unstructured token, or if Autofill formatted the token
-//                      from a structured subcomponent, or if the value was
-//                      observed in a form submission, or even validated by the
-//                      user in the settings.
-//  observations        An encoding of the observations stored for this `type`.
-//                      See `ProfileTokenConfidence::
-//                      SerializeObservationsForStoredType()`.
-//
 // virtual_card_usage_data
 //                      Contains data related to retrieval attempts of a virtual
 //                      card on a particular merchant domain
@@ -663,34 +453,6 @@
   bool CreateTablesIfNecessary() override;
   bool MigrateToVersion(int version, bool* update_compatible_version) override;
 
-  // Records a single Autofill profile in the autofill_profiles table.
-  virtual bool AddAutofillProfile(const AutofillProfile& profile);
-
-  // Updates the database values for the specified profile.  Multi-value aware.
-  virtual bool UpdateAutofillProfile(const AutofillProfile& profile);
-
-  // Removes the Autofill profile with the given `guid`. `profile_source`
-  // indicates where the profile was synced from and thus whether it is stored
-  // in `kAutofillProfilesTable` or `kContactInfoTable`.
-  virtual bool RemoveAutofillProfile(const std::string& guid,
-                                     AutofillProfile::Source profile_source);
-
-  // Removes all profiles from the given `profile_source`.
-  bool RemoveAllAutofillProfiles(AutofillProfile::Source profile_source);
-
-  // Retrieves a profile with guid `guid` from `kAutofillProfilesTable` or
-  // `kContactInfoTable`.
-  std::unique_ptr<AutofillProfile> GetAutofillProfile(
-      const std::string& guid,
-      AutofillProfile::Source profile_source) const;
-
-  // Retrieves profiles in the database. They are returned in unspecified order.
-  // The `profile_source` specifies if profiles from the legacy or the remote
-  // backend should be retrieved.
-  virtual bool GetAutofillProfiles(
-      AutofillProfile::Source profile_source,
-      std::vector<std::unique_ptr<AutofillProfile>>* profiles) const;
-
   // Fetches a PaymentInstrument from the autofill db. This will query the below
   // 3 tables to generate a PaymentInstrument object.
   //  `payment_instruments`
@@ -856,29 +618,26 @@
           virtual_card_usage_data);
   bool RemoveAllVirtualCardUsageData();
 
-  // Deletes all data from the server card and profile tables. Returns true if
-  // any data was deleted, false if not (so false means "commit not needed"
-  // rather than "error").
+  // Deletes all data from the server card tables. Returns true if any data was
+  // deleted, false if not (so false means "commit not needed" rather than
+  // "error").
   bool ClearAllServerData();
 
-  // Deletes all data from the local card and profiles table. Returns true if
-  // any data was deleted, false if not (so false means "commit not needed"
-  // rather than "error").
+  // Deletes all data from the local card table. Returns true if any data was
+  // deleted, false if not (so false means "commit not needed" rather than
+  // "error").
   bool ClearAllLocalData();
 
-  // Removes rows from autofill_profiles and credit_cards if they were created
-  // on or after `delete_begin` and strictly before `delete_end`. Returns the
-  // list of deleted profile guids in `profile_guids`. Return value is true if
-  // all rows were successfully removed. Returns false on database error. In
-  // that case, the output vector state is undefined, and may be partially
-  // filled.
+  // Removes rows from credit_cards if they were created on or after
+  // `delete_begin` and strictly before `delete_end`. Returns the list of
+  // deleted cards in `credit_cards`. Return value is true if all rows were
+  // successfully removed. Returns false on database error. In that case, the
+  // output vector state is undefined, and may be partially filled.
   // TODO(crbug.com/1135188): This function is solely used to remove browsing
-  // data. Once explicit save dialogs are fully launched, it can be removed. For
-  // this reason profiles in the `contact_info` table are not considered.
+  // data. Once explicit save dialogs are fully launched, it can be removed.
   bool RemoveAutofillDataModifiedBetween(
       const base::Time& delete_begin,
       const base::Time& delete_end,
-      std::vector<std::unique_ptr<AutofillProfile>>* profiles,
       std::vector<std::unique_ptr<CreditCard>>* credit_cards);
 
   // Removes origin URLs from the credit_cards tables if they were written on or
@@ -918,41 +677,22 @@
   bool MigrateToVersion85AddCardIssuerColumnToMaskedCreditCard();
   bool MigrateToVersion86RemoveUnmaskedCreditCardsUseColumns();
   bool MigrateToVersion87AddCreditCardNicknameColumn();
-  bool MigrateToVersion88AddNewNameColumns();
   bool MigrateToVersion89AddInstrumentIdColumnToMaskedCreditCard();
-  bool MigrateToVersion90AddNewStructuredAddressColumns();
-  bool MigrateToVersion91AddMoreStructuredAddressColumns();
-  bool MigrateToVersion92AddNewPrefixedNameColumn();
-  bool MigrateToVersion93AddAutofillProfileLabelColumn();
   bool MigrateToVersion94AddPromoCodeColumnsToOfferData();
   bool MigrateToVersion95AddVirtualCardMetadata();
-  bool MigrateToVersion96AddAutofillProfileDisallowConfirmableMergesColumn();
   bool MigrateToVersion98RemoveStatusColumnMaskedCreditCards();
-  bool MigrateToVersion99RemoveAutofillProfilesTrashTable();
-  bool MigrateToVersion100RemoveProfileValidityBitfieldColumn();
   bool MigrateToVersion101RemoveCreditCardArtImageTable();
-  bool MigrateToVersion102AddAutofillBirthdatesTable();
   bool MigrateToVersion104AddProductDescriptionColumn();
   bool MigrateToVersion105AddAutofillIbanTable();
   bool MigrateToVersion106RecreateAutofillIbanTable();
-  bool MigrateToVersion107AddContactInfoTables();
   bool MigrateToVersion108AddCardIssuerIdColumn();
   bool MigrateToVersion109AddVirtualCardUsageDataTable();
-  bool MigrateToVersion110AddInitialCreatorIdAndLastModifierId();
   bool MigrateToVersion111AddVirtualCardEnrollmentTypeColumn();
-  // No MigrateToVersion112. WebDatabase changed, but AutofillTable wasn't
-  // affected.
-  bool MigrateToVersion113MigrateLocalAddressProfilesToNewTable();
-  bool MigrateToVersion114DropLegacyAddressTables();
   bool MigrateToVersion115EncryptIbanValue();
   bool MigrateToVersion116AddStoredCvcTable();
-  bool MigrateToVersion117AddProfileObservationColumn();
   bool MigrateToVersion118RemovePaymentsUpiVpaTable();
   bool MigrateToVersion119AddMaskedIbanTablesAndRenameLocalIbanTable();
   bool MigrateToVersion120AddPaymentInstrumentAndBankAccountTables();
-  bool MigrateToVersion121DropServerAddressTables();
-  // No MigrateToVersion122. WebDatabase changed, but AutofillTable wasn't
-  // affected.
   bool MigrateToVersion123AddProductTermsUrlColumnAndAddCardBenefitsTables();
 
   // Max data length saved in the table, AKA the maximum length allowed for
@@ -982,12 +722,6 @@
   bool DeleteFromMaskedCreditCards(const std::string& id);
   bool DeleteFromUnmaskedCreditCards(const std::string& id);
 
-  // Reads profiles from the deprecated autofill_profiles table.
-  std::unique_ptr<AutofillProfile> GetAutofillProfileFromLegacyTable(
-      const std::string& guid) const;
-  bool GetAutofillProfilesFromLegacyTable(
-      std::vector<std::unique_ptr<AutofillProfile>>* profiles) const;
-
   // Retrieve the data from the `bank_accounts` table and return a BankAccount
   // object. The `payment_instrument_fields` contain the fields retrieved from
   // the `payment_instruments` and `payment_instrument_supported_rails` tables
@@ -1017,12 +751,6 @@
 
   bool InitCreditCardsTable();
   bool InitLocalIbansTable();
-  bool InitLegacyProfilesTable();
-  bool InitLegacyProfileAddressesTable();
-  bool InitLegacyProfileNamesTable();
-  bool InitLegacyProfileEmailsTable();
-  bool InitLegacyProfilePhonesTable();
-  bool InitLegacyProfileBirthdatesTable();
   bool InitMaskedCreditCardsTable();
   bool InitMaskedIbansTable();
   bool InitMaskedIbansMetadataTable();
@@ -1036,8 +764,6 @@
   bool InitOfferDataTable();
   bool InitOfferEligibleInstrumentTable();
   bool InitOfferMerchantDomainTable();
-  bool InitProfileMetadataTable(AutofillProfile::Source source);
-  bool InitProfileTypeTokensTable(AutofillProfile::Source source);
   bool InitVirtualCardUsageDataTable();
   bool InitBankAccountsTable();
   bool InitPaymentInstrumentsTable();
diff --git a/components/autofill/core/browser/webdata/autofill_table_unittest.cc b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
index 0c837ff..608cc667 100644
--- a/components/autofill/core/browser/webdata/autofill_table_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
@@ -24,18 +24,14 @@
 #include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/autofill_type.h"
-#include "components/autofill/core/browser/country_type.h"
 #include "components/autofill/core/browser/data_model/autofill_metadata.h"
 #include "components/autofill/core/browser/data_model/autofill_offer_data.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/autofill_wallet_usage_data.h"
 #include "components/autofill/core/browser/data_model/bank_account.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/data_model/credit_card_cloud_token_data.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/payments/payments_customer_data.h"
-#include "components/autofill/core/browser/profile_token_quality.h"
-#include "components/autofill/core/browser/profile_token_quality_test_api.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
 #include "components/autofill/core/browser/webdata/autofill_change.h"
 #include "components/autofill/core/common/autofill_clock.h"
@@ -112,248 +108,6 @@
   std::unique_ptr<WebDatabase> db_;
 };
 
-// Tests for the AutofillProfil CRUD interface are tested with both profile
-// sources.
-class AutofillTableProfileTest
-    : public AutofillTableTest,
-      public testing::WithParamInterface<AutofillProfile::Source> {
- public:
-  void SetUp() override {
-    AutofillTableTest::SetUp();
-    features_.InitWithFeatures(
-        {features::kAutofillEnableSupportForLandmark,
-         features::kAutofillEnableSupportForBetweenStreets,
-         features::kAutofillEnableSupportForAdminLevel2,
-         features::kAutofillEnableSupportForAddressOverflow,
-         features::kAutofillEnableSupportForAddressOverflowAndLandmark,
-         features::kAutofillEnableSupportForBetweenStreetsOrLandmark},
-        {});
-  }
-  AutofillProfile::Source profile_source() const { return GetParam(); }
-
-  // Creates an `AutofillProfile` with `profile_source()` as its source.
-  AutofillProfile CreateAutofillProfile() const {
-    return AutofillProfile(profile_source(), AddressCountryCode("ES"));
-  }
-
-  // Depending on the `profile_source()`, the AutofillProfiles are stored in a
-  // different master table.
-  std::string_view GetProfileTable() const {
-    return profile_source() == AutofillProfile::Source::kLocalOrSyncable
-               ? "local_addresses"
-               : "contact_info";
-  }
-
- private:
-  base::test::ScopedFeatureList features_;
-};
-
-INSTANTIATE_TEST_SUITE_P(
-    ,
-    AutofillTableProfileTest,
-    testing::ValuesIn({AutofillProfile::Source::kLocalOrSyncable,
-                       AutofillProfile::Source::kAccount}));
-
-// Tests reading/writing name, email, company, address, phone number and
-// birthdate information.
-TEST_P(AutofillTableProfileTest, AutofillProfile) {
-  AutofillProfile home_profile = CreateAutofillProfile();
-
-  // TODO(crbug.com/1113617): Honorifics are temporally disabled.
-  // home_profile.SetRawInfoWithVerificationStatus(
-  // NAME_HONORIFIC_PREFIX, u"Dr.",
-  // VerificationStatus::kObserved);
-
-  home_profile.SetRawInfoWithVerificationStatus(NAME_HONORIFIC_PREFIX, u"Dr.",
-                                                VerificationStatus::kObserved);
-
-  home_profile.SetRawInfoWithVerificationStatus(NAME_FIRST, u"John",
-                                                VerificationStatus::kObserved);
-
-  home_profile.SetRawInfoWithVerificationStatus(NAME_MIDDLE, u"Q.",
-                                                VerificationStatus::kObserved);
-
-  home_profile.SetRawInfoWithVerificationStatus(NAME_LAST_FIRST, u"Agent",
-                                                VerificationStatus::kParsed);
-
-  home_profile.SetRawInfoWithVerificationStatus(NAME_LAST_CONJUNCTION, u"007",
-                                                VerificationStatus::kParsed);
-
-  home_profile.SetRawInfoWithVerificationStatus(NAME_LAST_SECOND, u"Smith",
-                                                VerificationStatus::kParsed);
-
-  home_profile.SetRawInfoWithVerificationStatus(NAME_LAST, u"Agent 007 Smith",
-                                                VerificationStatus::kParsed);
-
-  home_profile.SetRawInfoWithVerificationStatus(
-      NAME_FULL, u"John Q. Agent 007 Smith", VerificationStatus::kObserved);
-
-  home_profile.SetRawInfoWithVerificationStatus(NAME_FULL_WITH_HONORIFIC_PREFIX,
-                                                u"Dr. John Q. Agent 007 Smith",
-                                                VerificationStatus::kObserved);
-
-  home_profile.SetRawInfo(EMAIL_ADDRESS, u"js@smith.xyz");
-  home_profile.SetRawInfo(COMPANY_NAME, u"Google");
-
-  home_profile.SetRawInfoWithVerificationStatus(
-      ADDRESS_HOME_STREET_ADDRESS,
-      u"Street Name between streets House Number Premise APT 10 Floor 2 "
-      u"Landmark",
-      VerificationStatus::kUserVerified);
-  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_STREET_LOCATION,
-                                                u"Street Name House Number",
-                                                VerificationStatus::kFormatted);
-  home_profile.SetRawInfoWithVerificationStatus(
-      ADDRESS_HOME_STREET_NAME, u"Street Name", VerificationStatus::kFormatted);
-  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_DEPENDENT_LOCALITY,
-                                                u"Dependent Locality",
-                                                VerificationStatus::kObserved);
-
-  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_CITY, u"City",
-                                                VerificationStatus::kObserved);
-
-  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_STATE, u"State",
-                                                VerificationStatus::kObserved);
-
-  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_SORTING_CODE,
-                                                u"Sorting Code",
-                                                VerificationStatus::kObserved);
-
-  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_ZIP, u"ZIP",
-                                                VerificationStatus::kObserved);
-
-  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_COUNTRY, u"DE",
-                                                VerificationStatus::kObserved);
-  home_profile.SetRawInfoWithVerificationStatus(
-      ADDRESS_HOME_HOUSE_NUMBER, u"House Number",
-      VerificationStatus::kUserVerified);
-  home_profile.SetRawInfoWithVerificationStatus(
-      ADDRESS_HOME_SUBPREMISE, u"APT 10 Floor 2",
-      VerificationStatus::kUserVerified);
-  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_APT_NUM, u"10",
-                                                VerificationStatus::kParsed);
-  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_FLOOR, u"2",
-                                                VerificationStatus::kParsed);
-  ASSERT_EQ(home_profile.GetRawInfo(ADDRESS_HOME_STREET_NAME), u"Street Name");
-  home_profile.SetRawInfoWithVerificationStatus(
-      ADDRESS_HOME_LANDMARK, u"Landmark", VerificationStatus::kObserved);
-  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_OVERFLOW,
-                                                u"Andar 1, Apto. 12",
-                                                VerificationStatus::kObserved);
-  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_BETWEEN_STREETS,
-                                                u"between streets",
-                                                VerificationStatus::kObserved);
-  home_profile.SetRawInfoWithVerificationStatus(
-      ADDRESS_HOME_ADMIN_LEVEL2, u"Oxaca", VerificationStatus::kObserved);
-
-  home_profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"18181234567");
-  home_profile.SetRawInfoAsInt(BIRTHDATE_DAY, 14);
-  home_profile.SetRawInfoAsInt(BIRTHDATE_MONTH, 3);
-  home_profile.SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, 1997);
-  home_profile.set_language_code("en");
-
-  // Add the profile to the table.
-  EXPECT_TRUE(table_->AddAutofillProfile(home_profile));
-
-  // Get the 'Home' profile from the table.
-  std::unique_ptr<AutofillProfile> db_profile =
-      table_->GetAutofillProfile(home_profile.guid(), home_profile.source());
-  ASSERT_TRUE(db_profile);
-
-  // Verify that it is correct.
-  EXPECT_EQ(home_profile, *db_profile);
-
-  // Remove the profile and expect that no profiles remain.
-  EXPECT_TRUE(
-      table_->RemoveAutofillProfile(home_profile.guid(), profile_source()));
-  std::vector<std::unique_ptr<AutofillProfile>> profiles;
-  EXPECT_TRUE(table_->GetAutofillProfiles(profile_source(), &profiles));
-  EXPECT_TRUE(profiles.empty());
-}
-
-// Tests that `GetAutofillProfiles(source, profiles)` clears `profiles` and
-// only returns profiles from the correct `source`.
-// Not part of the `AutofillTableProfileTest` fixture, as it doesn't benefit
-// from parameterization on the `profile_source()`.
-TEST_F(AutofillTableTest, GetAutofillProfiles) {
-  AutofillProfile local_profile(AutofillProfile::Source::kLocalOrSyncable,
-                                AddressCountryCode("ES"));
-  AutofillProfile account_profile(AutofillProfile::Source::kAccount,
-                                  AddressCountryCode("ES"));
-  EXPECT_TRUE(table_->AddAutofillProfile(local_profile));
-  EXPECT_TRUE(table_->AddAutofillProfile(account_profile));
-
-  std::vector<std::unique_ptr<AutofillProfile>> profiles;
-  EXPECT_TRUE(table_->GetAutofillProfiles(
-      AutofillProfile::Source::kLocalOrSyncable, &profiles));
-  EXPECT_THAT(profiles, ElementsAre(testing::Pointee(local_profile)));
-  EXPECT_TRUE(table_->GetAutofillProfiles(AutofillProfile::Source::kAccount,
-                                          &profiles));
-  EXPECT_THAT(profiles, ElementsAre(testing::Pointee(account_profile)));
-}
-
-// Tests that `RemoveAllAutofillProfiles()` clears all profiles of the given
-// source.
-TEST_P(AutofillTableProfileTest, RemoveAllAutofillProfiles) {
-  ASSERT_TRUE(table_->AddAutofillProfile(
-      AutofillProfile(AutofillProfile::Source::kLocalOrSyncable,
-                      i18n_model_definition::kLegacyHierarchyCountryCode)));
-  ASSERT_TRUE(table_->AddAutofillProfile(
-      AutofillProfile(AutofillProfile::Source::kAccount,
-                      i18n_model_definition::kLegacyHierarchyCountryCode)));
-
-  EXPECT_TRUE(table_->RemoveAllAutofillProfiles(profile_source()));
-
-  // Expect that the profiles from `profile_source()` are gone.
-  std::vector<std::unique_ptr<AutofillProfile>> profiles;
-  ASSERT_TRUE(table_->GetAutofillProfiles(profile_source(), &profiles));
-  EXPECT_TRUE(profiles.empty());
-
-  // Expect that the profile from the opposite source remains.
-  const auto other_source =
-      profile_source() == AutofillProfile::Source::kAccount
-          ? AutofillProfile::Source::kLocalOrSyncable
-          : AutofillProfile::Source::kAccount;
-  ASSERT_TRUE(table_->GetAutofillProfiles(other_source, &profiles));
-  EXPECT_EQ(profiles.size(), 1u);
-}
-
-// Tests that `ProfileTokenQuality` observations are read and written.
-TEST_P(AutofillTableProfileTest, ProfileTokenQuality) {
-  AutofillProfile profile = CreateAutofillProfile();
-  test_api(profile.token_quality())
-      .AddObservation(NAME_FIRST,
-                      ProfileTokenQuality::ObservationType::kAccepted,
-                      ProfileTokenQualityTestApi::FormSignatureHash(12));
-
-  // Add
-  table_->AddAutofillProfile(profile);
-  profile = *table_->GetAutofillProfile(profile.guid(), profile.source());
-  EXPECT_THAT(
-      profile.token_quality().GetObservationTypesForFieldType(NAME_FIRST),
-      UnorderedElementsAre(ProfileTokenQuality::ObservationType::kAccepted));
-  EXPECT_THAT(
-      test_api(profile.token_quality()).GetHashesForStoredType(NAME_FIRST),
-      UnorderedElementsAre(ProfileTokenQualityTestApi::FormSignatureHash(12)));
-
-  // Update
-  test_api(profile.token_quality())
-      .AddObservation(NAME_FIRST,
-                      ProfileTokenQuality::ObservationType::kEditedFallback,
-                      ProfileTokenQualityTestApi::FormSignatureHash(21));
-  table_->UpdateAutofillProfile(profile);
-  profile = *table_->GetAutofillProfile(profile.guid(), profile.source());
-  EXPECT_THAT(
-      profile.token_quality().GetObservationTypesForFieldType(NAME_FIRST),
-      UnorderedElementsAre(
-          ProfileTokenQuality::ObservationType::kAccepted,
-          ProfileTokenQuality::ObservationType::kEditedFallback));
-  EXPECT_THAT(
-      test_api(profile.token_quality()).GetHashesForStoredType(NAME_FIRST),
-      UnorderedElementsAre(ProfileTokenQualityTestApi::FormSignatureHash(12),
-                           ProfileTokenQualityTestApi::FormSignatureHash(21)));
-}
-
 TEST_F(AutofillTableTest, Iban) {
   // Add a valid IBAN.
   Iban iban;
@@ -841,48 +595,6 @@
   EXPECT_EQ(0, credit_card.Compare(*outputs[0]));
 }
 
-TEST_P(AutofillTableProfileTest, UpdateAutofillProfile) {
-  // Add a profile to the db.
-  AutofillProfile profile = CreateAutofillProfile();
-  profile.SetRawInfo(NAME_FIRST, u"John");
-  profile.SetRawInfo(NAME_MIDDLE, u"Q.");
-  profile.SetRawInfo(NAME_LAST, u"Smith");
-  profile.SetRawInfo(EMAIL_ADDRESS, u"js@example.com");
-  profile.SetRawInfo(COMPANY_NAME, u"Google");
-  profile.SetRawInfo(ADDRESS_HOME_LINE1, u"1234 Apple Way");
-  profile.SetRawInfo(ADDRESS_HOME_LINE2, u"unit 5");
-  profile.SetRawInfo(ADDRESS_HOME_CITY, u"Los Angeles");
-  profile.SetRawInfo(ADDRESS_HOME_STATE, u"CA");
-  profile.SetRawInfo(ADDRESS_HOME_ZIP, u"90025");
-  profile.SetRawInfo(ADDRESS_HOME_COUNTRY, u"ES");
-  profile.SetRawInfo(ADDRESS_HOME_OVERFLOW, u"Andar 1, Apto. 12");
-  profile.SetRawInfo(ADDRESS_HOME_LANDMARK, u"Landmark");
-  profile.SetRawInfo(ADDRESS_HOME_BETWEEN_STREETS, u"Marcos y Oliva");
-  profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"18181234567");
-  profile.SetRawInfoAsInt(BIRTHDATE_DAY, 14);
-  profile.SetRawInfoAsInt(BIRTHDATE_MONTH, 3);
-  profile.SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, 1997);
-  profile.set_language_code("en");
-  profile.FinalizeAfterImport();
-  table_->AddAutofillProfile(profile);
-
-  // Get the profile.
-  std::unique_ptr<AutofillProfile> db_profile =
-      table_->GetAutofillProfile(profile.guid(), profile.source());
-  ASSERT_TRUE(db_profile);
-  EXPECT_EQ(profile, *db_profile);
-
-  // Now, update the profile and save the update to the database.
-  // The modification date should change to reflect the update.
-  profile.SetRawInfo(EMAIL_ADDRESS, u"js@smith.xyz");
-  table_->UpdateAutofillProfile(profile);
-
-  // Get the profile.
-  db_profile = table_->GetAutofillProfile(profile.guid(), profile.source());
-  ASSERT_TRUE(db_profile);
-  EXPECT_EQ(profile, *db_profile);
-}
-
 TEST_F(AutofillTableTest, UpdateCreditCard) {
   base::test::ScopedFeatureList features(
       features::kAutofillEnableCvcStorageAndFilling);
@@ -1006,32 +718,8 @@
 }
 
 TEST_F(AutofillTableTest, RemoveAutofillDataModifiedBetween) {
-  // Populate the autofill_profiles and credit_cards tables.
+  // Populate the credit_cards tables.
   ASSERT_TRUE(db_->GetSQLConnection()->Execute(
-      "INSERT INTO local_addresses (guid, date_modified) "
-      "VALUES('00000000-0000-0000-0000-000000000000', 11);"
-      "INSERT INTO local_addresses_type_tokens (guid, type, value) "
-      "VALUES('00000000-0000-0000-0000-000000000000', 3, 'first name0');"
-      "INSERT INTO local_addresses (guid, date_modified) "
-      "VALUES('00000000-0000-0000-0000-000000000001', 21);"
-      "INSERT INTO local_addresses_type_tokens (guid, type, value) "
-      "VALUES('00000000-0000-0000-0000-000000000001', 3, 'first name1');"
-      "INSERT INTO local_addresses (guid, date_modified) "
-      "VALUES('00000000-0000-0000-0000-000000000002', 31);"
-      "INSERT INTO local_addresses_type_tokens (guid, type, value) "
-      "VALUES('00000000-0000-0000-0000-000000000002', 3, 'first name2');"
-      "INSERT INTO local_addresses (guid, date_modified) "
-      "VALUES('00000000-0000-0000-0000-000000000003', 41);"
-      "INSERT INTO local_addresses_type_tokens (guid, type, value) "
-      "VALUES('00000000-0000-0000-0000-000000000003', 3, 'first name3');"
-      "INSERT INTO local_addresses (guid, date_modified) "
-      "VALUES('00000000-0000-0000-0000-000000000004', 51);"
-      "INSERT INTO local_addresses_type_tokens (guid, type, value) "
-      "VALUES('00000000-0000-0000-0000-000000000004', 3, 'first name4');"
-      "INSERT INTO local_addresses (guid, date_modified) "
-      "VALUES('00000000-0000-0000-0000-000000000005', 61);"
-      "INSERT INTO local_addresses_type_tokens (guid, type, value) "
-      "VALUES('00000000-0000-0000-0000-000000000005', 3, 'first name5');"
       "INSERT INTO credit_cards (guid, date_modified) "
       "VALUES('00000000-0000-0000-0000-000000000006', 17);"
       "INSERT INTO local_stored_cvc (guid, value_encrypted, "
@@ -1064,45 +752,9 @@
       "VALUES('00000000-0000-0000-0000-000000000011', '', 67);"));
 
   // Remove all entries modified in the bounded time range [17,41).
-  std::vector<std::unique_ptr<AutofillProfile>> profiles;
   std::vector<std::unique_ptr<CreditCard>> credit_cards;
-  table_->RemoveAutofillDataModifiedBetween(
-      Time::FromTimeT(17), Time::FromTimeT(41), &profiles, &credit_cards);
-
-  // Two profiles should have been removed.
-  ASSERT_EQ(2UL, profiles.size());
-  EXPECT_EQ("00000000-0000-0000-0000-000000000001", profiles[0]->guid());
-  EXPECT_EQ("00000000-0000-0000-0000-000000000002", profiles[1]->guid());
-
-  // Make sure that only the expected profiles are still present.
-  sql::Statement s_autofill_profiles_bounded(
-      db_->GetSQLConnection()->GetUniqueStatement(
-          "SELECT date_modified FROM local_addresses ORDER BY guid"));
-  ASSERT_TRUE(s_autofill_profiles_bounded.is_valid());
-  ASSERT_TRUE(s_autofill_profiles_bounded.Step());
-  EXPECT_EQ(11, s_autofill_profiles_bounded.ColumnInt64(0));
-  ASSERT_TRUE(s_autofill_profiles_bounded.Step());
-  EXPECT_EQ(41, s_autofill_profiles_bounded.ColumnInt64(0));
-  ASSERT_TRUE(s_autofill_profiles_bounded.Step());
-  EXPECT_EQ(51, s_autofill_profiles_bounded.ColumnInt64(0));
-  ASSERT_TRUE(s_autofill_profiles_bounded.Step());
-  EXPECT_EQ(61, s_autofill_profiles_bounded.ColumnInt64(0));
-  EXPECT_FALSE(s_autofill_profiles_bounded.Step());
-
-  // Make sure that only the expected profile names are still present.
-  sql::Statement s_autofill_profile_names_bounded(
-      db_->GetSQLConnection()->GetUniqueStatement(
-          "SELECT value FROM local_addresses_type_tokens ORDER BY guid"));
-  ASSERT_TRUE(s_autofill_profile_names_bounded.is_valid());
-  ASSERT_TRUE(s_autofill_profile_names_bounded.Step());
-  EXPECT_EQ("first name0", s_autofill_profile_names_bounded.ColumnString(0));
-  ASSERT_TRUE(s_autofill_profile_names_bounded.Step());
-  EXPECT_EQ("first name3", s_autofill_profile_names_bounded.ColumnString(0));
-  ASSERT_TRUE(s_autofill_profile_names_bounded.Step());
-  EXPECT_EQ("first name4", s_autofill_profile_names_bounded.ColumnString(0));
-  ASSERT_TRUE(s_autofill_profile_names_bounded.Step());
-  EXPECT_EQ("first name5", s_autofill_profile_names_bounded.ColumnString(0));
-  EXPECT_FALSE(s_autofill_profile_names_bounded.Step());
+  table_->RemoveAutofillDataModifiedBetween(Time::FromTimeT(17),
+                                            Time::FromTimeT(41), &credit_cards);
 
   // Three cards should have been removed.
   ASSERT_EQ(3UL, credit_cards.size());
@@ -1137,32 +789,7 @@
 
   // Remove all entries modified on or after time 51 (unbounded range).
   table_->RemoveAutofillDataModifiedBetween(Time::FromTimeT(51), Time(),
-                                            &profiles, &credit_cards);
-  ASSERT_EQ(2UL, profiles.size());
-  EXPECT_EQ("00000000-0000-0000-0000-000000000004", profiles[0]->guid());
-  EXPECT_EQ("00000000-0000-0000-0000-000000000005", profiles[1]->guid());
-
-  // Make sure that only the expected profiles are still present.
-  sql::Statement s_autofill_profiles_unbounded(
-      db_->GetSQLConnection()->GetUniqueStatement(
-          "SELECT date_modified FROM local_addresses ORDER BY guid"));
-  ASSERT_TRUE(s_autofill_profiles_unbounded.is_valid());
-  ASSERT_TRUE(s_autofill_profiles_unbounded.Step());
-  EXPECT_EQ(11, s_autofill_profiles_unbounded.ColumnInt64(0));
-  ASSERT_TRUE(s_autofill_profiles_unbounded.Step());
-  EXPECT_EQ(41, s_autofill_profiles_unbounded.ColumnInt64(0));
-  EXPECT_FALSE(s_autofill_profiles_unbounded.Step());
-
-  // Make sure that only the expected profile names are still present.
-  sql::Statement s_autofill_profile_names_unbounded(
-      db_->GetSQLConnection()->GetUniqueStatement(
-          "SELECT value FROM local_addresses_type_tokens ORDER BY guid"));
-  ASSERT_TRUE(s_autofill_profile_names_unbounded.is_valid());
-  ASSERT_TRUE(s_autofill_profile_names_unbounded.Step());
-  EXPECT_EQ("first name0", s_autofill_profile_names_unbounded.ColumnString(0));
-  ASSERT_TRUE(s_autofill_profile_names_unbounded.Step());
-  EXPECT_EQ("first name3", s_autofill_profile_names_unbounded.ColumnString(0));
-  EXPECT_FALSE(s_autofill_profile_names_unbounded.Step());
+                                            &credit_cards);
 
   // Two cards should have been removed.
   ASSERT_EQ(2UL, credit_cards.size());
@@ -1187,27 +814,7 @@
   EXPECT_FALSE(s_cvc_unbounded.Step());
 
   // Remove all remaining entries.
-  table_->RemoveAutofillDataModifiedBetween(Time(), Time(), &profiles,
-                                            &credit_cards);
-
-  // Two profiles should have been removed.
-  ASSERT_EQ(2UL, profiles.size());
-  EXPECT_EQ("00000000-0000-0000-0000-000000000000", profiles[0]->guid());
-  EXPECT_EQ("00000000-0000-0000-0000-000000000003", profiles[1]->guid());
-
-  // Make sure there are no profiles remaining.
-  sql::Statement s_autofill_profiles_empty(
-      db_->GetSQLConnection()->GetUniqueStatement(
-          "SELECT date_modified FROM local_addresses"));
-  ASSERT_TRUE(s_autofill_profiles_empty.is_valid());
-  EXPECT_FALSE(s_autofill_profiles_empty.Step());
-
-  // Make sure there are no profile names remaining.
-  sql::Statement s_autofill_profile_names_empty(
-      db_->GetSQLConnection()->GetUniqueStatement(
-          "SELECT value FROM local_addresses_type_tokens"));
-  ASSERT_TRUE(s_autofill_profile_names_empty.is_valid());
-  EXPECT_FALSE(s_autofill_profile_names_empty.Step());
+  table_->RemoveAutofillDataModifiedBetween(Time(), Time(), &credit_cards);
 
   // One credit card should have been deleted.
   ASSERT_EQ(1UL, credit_cards.size());
@@ -1819,11 +1426,10 @@
   table_->UnmaskServerCreditCard(masked_card, full_number);
 
   // Delete data in a range a year in the future.
-  std::vector<std::unique_ptr<AutofillProfile>> profiles;
   std::vector<std::unique_ptr<CreditCard>> credit_cards;
   ASSERT_TRUE(table_->RemoveAutofillDataModifiedBetween(
       unmasked_time + base::Days(365), unmasked_time + base::Days(530),
-      &profiles, &credit_cards));
+      &credit_cards));
 
   // This should not affect the unmasked card (should be unmasked).
   std::vector<std::unique_ptr<CreditCard>> outputs;
@@ -1837,8 +1443,8 @@
   // Fudge |now| to make sure it's strictly greater than the |now| that
   // the database uses.
   base::Time now = AutofillClock::Now() + base::Seconds(1);
-  ASSERT_TRUE(table_->RemoveAutofillDataModifiedBetween(
-      now - base::Days(1), now, &profiles, &credit_cards));
+  ASSERT_TRUE(table_->RemoveAutofillDataModifiedBetween(now - base::Days(1),
+                                                        now, &credit_cards));
 
   // This should re-mask.
   ASSERT_TRUE(table_->GetServerCreditCards(outputs));
@@ -1858,7 +1464,7 @@
 
   // Delete all data.
   ASSERT_TRUE(table_->RemoveAutofillDataModifiedBetween(
-      base::Time(), base::Time::Max(), &profiles, &credit_cards));
+      base::Time(), base::Time::Max(), &credit_cards));
 
   // Should be masked again.
   ASSERT_TRUE(table_->GetServerCreditCards(outputs));
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_credential_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_wallet_credential_sync_bridge.cc
index b21f9a1..26d8c8c 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_credential_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_credential_sync_bridge.cc
@@ -77,7 +77,7 @@
                           change_processor()->GetWeakPtr()));
 }
 
-absl::optional<syncer::ModelError>
+std::optional<syncer::ModelError>
 AutofillWalletCredentialSyncBridge::MergeFullSyncData(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
@@ -87,7 +87,7 @@
                                      std::move(entity_data));
 }
 
-absl::optional<syncer::ModelError>
+std::optional<syncer::ModelError>
 AutofillWalletCredentialSyncBridge::ApplyIncrementalSyncChanges(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_credential_sync_bridge.h b/components/autofill/core/browser/webdata/autofill_wallet_credential_sync_bridge.h
index e57f7333..4f77360a 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_credential_sync_bridge.h
+++ b/components/autofill/core/browser/webdata/autofill_wallet_credential_sync_bridge.h
@@ -59,10 +59,10 @@
   // ModelTypeSyncBridge
   std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
       override;
-  absl::optional<syncer::ModelError> MergeFullSyncData(
+  std::optional<syncer::ModelError> MergeFullSyncData(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_data) override;
-  absl::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
+  std::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_changes) override;
   void GetData(StorageKeyList storage_keys, DataCallback callback) override;
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_credential_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_credential_sync_bridge_unittest.cc
index 9c45eb5..3ec0cde 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_credential_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_credential_sync_bridge_unittest.cc
@@ -128,7 +128,7 @@
       initial_updates.push_back(SpecificsToUpdateResponse(specifics));
     }
     real_processor_->OnUpdateReceived(state, std::move(initial_updates),
-                                      /*gc_directive=*/absl::nullopt);
+                                      /*gc_directive=*/std::nullopt);
   }
 
   syncer::UpdateResponseData SpecificsToUpdateResponse(
@@ -224,7 +224,7 @@
 
   EXPECT_EQ(bridge()->MergeFullSyncData(bridge()->CreateMetadataChangeList(),
                                         std::move(entity_change_list)),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_THAT(GetAllServerCvcDataFromTable(),
               testing::UnorderedElementsAre(server_cvc));
 }
@@ -261,7 +261,7 @@
   EXPECT_EQ(
       bridge()->ApplyIncrementalSyncChanges(
           bridge()->CreateMetadataChangeList(), std::move(entity_change_list)),
-      absl::nullopt);
+      std::nullopt);
   EXPECT_THAT(GetAllServerCvcDataFromTable(),
               testing::UnorderedElementsAre(server_cvc1, server_cvc2));
 }
@@ -294,7 +294,7 @@
   EXPECT_EQ(
       bridge()->ApplyIncrementalSyncChanges(
           bridge()->CreateMetadataChangeList(), std::move(entity_change_list)),
-      absl::nullopt);
+      std::nullopt);
   EXPECT_THAT(GetAllServerCvcDataFromTable(), testing::IsEmpty());
 }
 
@@ -330,7 +330,7 @@
   EXPECT_EQ(
       bridge()->ApplyIncrementalSyncChanges(
           bridge()->CreateMetadataChangeList(), std::move(entity_change_list)),
-      absl::nullopt);
+      std::nullopt);
   EXPECT_THAT(GetAllServerCvcDataFromTable(),
               testing::UnorderedElementsAre(server_cvc2));
 }
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
index ea7ac9c4..4b1089f 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
@@ -5,6 +5,7 @@
 #include "components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.h"
 
 #include <map>
+#include <optional>
 #include <unordered_set>
 #include <utility>
 #include <vector>
@@ -27,7 +28,6 @@
 #include "components/sync/model/mutable_data_batch.h"
 #include "components/sync/model/sync_metadata_store_change_list.h"
 #include "components/sync/protocol/entity_data.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -338,7 +338,7 @@
                           change_processor()->GetWeakPtr()));
 }
 
-absl::optional<syncer::ModelError>
+std::optional<syncer::ModelError>
 AutofillWalletMetadataSyncBridge::MergeFullSyncData(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
@@ -357,7 +357,7 @@
                             std::move(entity_data));
 }
 
-absl::optional<syncer::ModelError>
+std::optional<syncer::ModelError>
 AutofillWalletMetadataSyncBridge::ApplyIncrementalSyncChanges(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
@@ -377,7 +377,7 @@
 void AutofillWalletMetadataSyncBridge::GetAllDataForDebugging(
     DataCallback callback) {
   // Get all data by not providing any |storage_keys| filter.
-  GetDataImpl(/*storage_keys=*/absl::nullopt, std::move(callback));
+  GetDataImpl(/*storage_keys=*/std::nullopt, std::move(callback));
 }
 
 std::string AutofillWalletMetadataSyncBridge::GetClientTag(
@@ -523,7 +523,7 @@
 }
 
 void AutofillWalletMetadataSyncBridge::GetDataImpl(
-    absl::optional<std::unordered_set<std::string>> storage_keys_set,
+    std::optional<std::unordered_set<std::string>> storage_keys_set,
     DataCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -567,7 +567,7 @@
   }
 }
 
-absl::optional<syncer::ModelError>
+std::optional<syncer::ModelError>
 AutofillWalletMetadataSyncBridge::MergeRemoteChanges(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
@@ -592,7 +592,7 @@
         AutofillMetadata remote =
             CreateAutofillMetadataFromWalletMetadataSpecifics(specifics);
         auto it = cache_.find(change->storage_key());
-        absl::optional<AutofillMetadata> local = absl::nullopt;
+        std::optional<AutofillMetadata> local = std::nullopt;
         if (it != cache_.end()) {
           local = it->second;
         }
@@ -671,7 +671,7 @@
     case AutofillDataModelChange<DataType, KeyType>::UPDATE:
       AutofillMetadata new_entry = change.data_model().GetMetadata();
       auto it = cache_.find(storage_key);
-      absl::optional<AutofillMetadata> existing_entry = absl::nullopt;
+      std::optional<AutofillMetadata> existing_entry = std::nullopt;
       if (it != cache_.end()) {
         existing_entry = it->second;
       }
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.h b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.h
index b30945f..1d9fdd0 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.h
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.h
@@ -66,10 +66,10 @@
   // ModelTypeSyncBridge implementation.
   std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
       override;
-  absl::optional<syncer::ModelError> MergeFullSyncData(
+  std::optional<syncer::ModelError> MergeFullSyncData(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_data) override;
-  absl::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
+  std::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_changes) override;
   void GetData(StorageKeyList storage_keys, DataCallback callback) override;
@@ -105,7 +105,7 @@
   // |callback|. If |storage_keys_set| is not set, it returns all data entries.
   // Otherwise, it returns only entries with storage key in |storage_keys_set|.
   void GetDataImpl(
-      absl::optional<std::unordered_set<std::string>> storage_keys_set,
+      std::optional<std::unordered_set<std::string>> storage_keys_set,
       DataCallback callback);
 
   // Uploads local data that is not part of |entity_data| sent from the server
@@ -116,7 +116,7 @@
   // Merges remote changes, specified in |entity_data|, with the local DB and,
   // potentially, writes changes to the local DB and/or commits updates of
   // entities from |entity_data| up to sync.
-  absl::optional<syncer::ModelError> MergeRemoteChanges(
+  std::optional<syncer::ModelError> MergeRemoteChanges(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_data);
 
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
index b77fbb69..eb511ac7 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
@@ -301,7 +301,7 @@
       updates.push_back(SpecificsToUpdateResponse(specifics));
     }
     real_processor_->OnUpdateReceived(state, std::move(updates),
-                                      /*gc_directive=*/absl::nullopt);
+                                      /*gc_directive=*/std::nullopt);
   }
 
   void ReceiveTombstones(
@@ -320,7 +320,7 @@
           SpecificsToUpdateResponse(specifics, /*is_deleted=*/true));
     }
     real_processor_->OnUpdateReceived(state, std::move(updates),
-                                      /*gc_directive=*/absl::nullopt);
+                                      /*gc_directive=*/std::nullopt);
   }
 
   EntityData SpecificsToEntity(const WalletMetadataSpecifics& specifics,
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc
index 2fd16bf1..f14ddbb7 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc
@@ -86,23 +86,23 @@
                           change_processor()->GetWeakPtr()));
 }
 
-absl::optional<syncer::ModelError>
+std::optional<syncer::ModelError>
 AutofillWalletOfferSyncBridge::MergeFullSyncData(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   MergeRemoteData(std::move(entity_data));
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<syncer::ModelError>
+std::optional<syncer::ModelError>
 AutofillWalletOfferSyncBridge::ApplyIncrementalSyncChanges(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
   // This bridge does not support incremental updates, so whenever this is
   // called, the change list should be empty.
   DCHECK(entity_data.empty()) << "Received an unsupported incremental update.";
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 void AutofillWalletOfferSyncBridge::GetData(StorageKeyList storage_keys,
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.h b/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.h
index d9f945c2..9c6de42 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.h
+++ b/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.h
@@ -48,10 +48,10 @@
   // ModelTypeSyncBridge
   std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
       override;
-  absl::optional<syncer::ModelError> MergeFullSyncData(
+  std::optional<syncer::ModelError> MergeFullSyncData(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_data) override;
-  absl::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
+  std::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_changes) override;
   void GetData(StorageKeyList storage_keys, DataCallback callback) override;
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc
index e85af2b..38de37c 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc
@@ -175,7 +175,7 @@
                           change_processor()->GetWeakPtr()));
 }
 
-absl::optional<syncer::ModelError> AutofillWalletSyncBridge::MergeFullSyncData(
+std::optional<syncer::ModelError> AutofillWalletSyncBridge::MergeFullSyncData(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
   // We want to notify the metadata bridge about all changes so that the
@@ -184,17 +184,17 @@
 
   // TODO(crbug.com/853688): Update the AutofillTable API to know about write
   // errors and report them here.
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<syncer::ModelError>
+std::optional<syncer::ModelError>
 AutofillWalletSyncBridge::ApplyIncrementalSyncChanges(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
   // This bridge does not support incremental updates, so whenever this is
   // called, the change list should be empty.
   DCHECK(entity_data.empty()) << "Received an unsupported incremental update.";
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 void AutofillWalletSyncBridge::GetData(StorageKeyList storage_keys,
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.h b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.h
index 6e32e0ee..803d2b9 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.h
+++ b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.h
@@ -56,10 +56,10 @@
   // ModelTypeSyncBridge implementation.
   std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
       override;
-  absl::optional<syncer::ModelError> MergeFullSyncData(
+  std::optional<syncer::ModelError> MergeFullSyncData(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_data) override;
-  absl::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
+  std::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_changes) override;
   void GetData(StorageKeyList storage_keys, DataCallback callback) override;
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge.cc
index e0619810..6c387a2 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge.cc
@@ -73,7 +73,7 @@
                           change_processor()->GetWeakPtr()));
 }
 
-absl::optional<syncer::ModelError>
+std::optional<syncer::ModelError>
 AutofillWalletUsageDataSyncBridge::MergeFullSyncData(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
@@ -82,7 +82,7 @@
                                      std::move(entity_data));
 }
 
-absl::optional<syncer::ModelError>
+std::optional<syncer::ModelError>
 AutofillWalletUsageDataSyncBridge::ApplyIncrementalSyncChanges(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge.h b/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge.h
index 67b4bda..dcf0e89 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge.h
+++ b/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge.h
@@ -51,10 +51,10 @@
   // ModelTypeSyncBridge
   std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
       override;
-  absl::optional<syncer::ModelError> MergeFullSyncData(
+  std::optional<syncer::ModelError> MergeFullSyncData(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_data) override;
-  absl::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
+  std::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_changes) override;
   void GetData(StorageKeyList storage_keys, DataCallback callback) override;
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge_unittest.cc
index 65fe1f38..b0ba599 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge_unittest.cc
@@ -202,7 +202,7 @@
   // `MergeFullSyncData()` returns an error if it fails.
   EXPECT_EQ(bridge()->MergeFullSyncData(bridge()->CreateMetadataChangeList(),
                                         std::move(entity_change_list_merge)),
-            absl::nullopt);
+            std::nullopt);
   // Expect `MergeFullSyncData()` was successful.
   EXPECT_THAT(GetVirtualCardUsageDataFromTable(),
               testing::UnorderedElementsAre(virtual_card_usage_data1));
@@ -269,7 +269,7 @@
       *old_data.usage_data_id(), VirtualCardUsageDataToEntity(old_data)));
   EXPECT_EQ(bridge()->MergeFullSyncData(bridge()->CreateMetadataChangeList(),
                                         std::move(entity_change_list)),
-            absl::nullopt);
+            std::nullopt);
 
   EXPECT_CALL(backend(), CommitChanges());
   EXPECT_CALL(backend(),
diff --git a/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc b/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
index 3240fa5e..13606e3d 100644
--- a/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
+++ b/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
@@ -22,6 +22,7 @@
 #include "components/autofill/core/browser/data_model/iban.h"
 #include "components/autofill/core/browser/geo/autofill_country.h"
 #include "components/autofill/core/browser/payments/payments_customer_data.h"
+#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
 #include "components/autofill/core/browser/webdata/autocomplete_entry.h"
 #include "components/autofill/core/browser/webdata/autocomplete_table.h"
 #include "components/autofill/core/browser/webdata/autofill_change.h"
@@ -321,7 +322,7 @@
     const AutofillProfile& profile,
     WebDatabase* db) {
   DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
-  AutofillTable* table = AutofillTable::FromWebDatabase(db);
+  AddressAutofillTable* table = AddressAutofillTable::FromWebDatabase(db);
   if (!table->AddAutofillProfile(profile)) {
     ReportResult(Result::kAddAutofillProfile_Failure);
     return WebDatabase::COMMIT_NOT_NEEDED;
@@ -351,7 +352,7 @@
     const AutofillProfile& profile,
     WebDatabase* db) {
   DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
-  AutofillTable* table = AutofillTable::FromWebDatabase(db);
+  AddressAutofillTable* table = AddressAutofillTable::FromWebDatabase(db);
   // Only perform the update if the profile exists.  It is currently
   // valid to try to update a missing profile.  We simply drop the write and
   // the caller will detect this on the next refresh.
@@ -392,14 +393,14 @@
     WebDatabase* db) {
   DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
   std::unique_ptr<AutofillProfile> profile =
-      AutofillTable::FromWebDatabase(db)->GetAutofillProfile(guid,
-                                                             profile_source);
+      AddressAutofillTable::FromWebDatabase(db)->GetAutofillProfile(
+          guid, profile_source);
   if (!profile) {
     ReportResult(Result::kRemoveAutofillProfile_ReadFailure);
     return WebDatabase::COMMIT_NOT_NEEDED;
   }
 
-  if (!AutofillTable::FromWebDatabase(db)->RemoveAutofillProfile(
+  if (!AddressAutofillTable::FromWebDatabase(db)->RemoveAutofillProfile(
           guid, profile_source)) {
     ReportResult(Result::kRemoveAutofillProfile_WriteFailure);
     return WebDatabase::COMMIT_NOT_NEEDED;
@@ -426,8 +427,8 @@
     WebDatabase* db) {
   DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
   std::vector<std::unique_ptr<AutofillProfile>> profiles;
-  AutofillTable::FromWebDatabase(db)->GetAutofillProfiles(profile_source,
-                                                          &profiles);
+  AddressAutofillTable::FromWebDatabase(db)->GetAutofillProfiles(profile_source,
+                                                                 &profiles);
   return std::unique_ptr<WDTypedResult>(
       new WDResult<std::vector<std::unique_ptr<AutofillProfile>>>(
           AUTOFILL_PROFILES_RESULT, std::move(profiles)));
@@ -855,12 +856,13 @@
 WebDatabase::State AutofillWebDataBackendImpl::ClearAllLocalData(
     WebDatabase* db) {
   DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
-  if (AutofillTable::FromWebDatabase(db)->ClearAllLocalData()) {
-    ReportResult(Result::kClearAllLocalData_Success);
-    return WebDatabase::COMMIT_NEEDED;
-  }
-  ReportResult(Result::kClearAllLocalData_Failure);
-  return WebDatabase::COMMIT_NOT_NEEDED;
+  bool a_succeeded =
+      AddressAutofillTable::FromWebDatabase(db)->ClearAllLocalData();
+  bool b_succeeded = AutofillTable::FromWebDatabase(db)->ClearAllLocalData();
+  ReportResult(a_succeeded && b_succeeded ? Result::kClearAllLocalData_Success
+                                          : Result::kClearAllLocalData_Failure);
+  return a_succeeded || b_succeeded ? WebDatabase::COMMIT_NEEDED
+                                    : WebDatabase::COMMIT_NOT_NEEDED;
 }
 
 WebDatabase::State
@@ -870,28 +872,39 @@
     WebDatabase* db) {
   DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
   std::vector<std::unique_ptr<AutofillProfile>> profiles;
-  std::vector<std::unique_ptr<CreditCard>> credit_cards;
-  if (AutofillTable::FromWebDatabase(db)->RemoveAutofillDataModifiedBetween(
-          delete_begin, delete_end, &profiles, &credit_cards)) {
+  bool commit_needed = false;
+  bool failures_observed = false;
+  if (AddressAutofillTable::FromWebDatabase(db)
+          ->RemoveAutofillDataModifiedBetween(delete_begin, delete_end,
+                                              &profiles)) {
     for (const std::unique_ptr<AutofillProfile>& profile : profiles) {
       for (auto& db_observer : db_observer_list_) {
         db_observer.AutofillProfileChanged(AutofillProfileChange(
             AutofillProfileChange::REMOVE, profile->guid(), *profile));
       }
     }
+    commit_needed = true;
+  } else {
+    failures_observed = true;
+  }
+  std::vector<std::unique_ptr<CreditCard>> credit_cards;
+  if (AutofillTable::FromWebDatabase(db)->RemoveAutofillDataModifiedBetween(
+          delete_begin, delete_end, &credit_cards)) {
     for (const std::unique_ptr<CreditCard>& credit_card : credit_cards) {
       for (auto& db_observer : db_observer_list_) {
         db_observer.CreditCardChanged(CreditCardChange(
             CreditCardChange::REMOVE, credit_card->guid(), *credit_card));
       }
     }
-    // Note: It is the caller's responsibility to post notifications for any
-    // changes, e.g. by calling the Refresh() method of PersonalDataManager.
-    ReportResult(Result::kRemoveAutofillDataModifiedBetween_Success);
-    return WebDatabase::COMMIT_NEEDED;
+    commit_needed = true;
+  } else {
+    failures_observed = true;
   }
-  ReportResult(Result::kRemoveAutofillDataModifiedBetween_Failure);
-  return WebDatabase::COMMIT_NOT_NEEDED;
+  ReportResult(failures_observed
+                   ? Result::kRemoveAutofillDataModifiedBetween_Success
+                   : Result::kRemoveAutofillDataModifiedBetween_Failure);
+  return commit_needed ? WebDatabase::COMMIT_NEEDED
+                       : WebDatabase::COMMIT_NOT_NEEDED;
 }
 
 WebDatabase::State AutofillWebDataBackendImpl::RemoveOriginURLsModifiedBetween(
diff --git a/components/autofill/core/browser/webdata/contact_info_sync_bridge.cc b/components/autofill/core/browser/webdata/contact_info_sync_bridge.cc
index 6fb98fc..e6938cb 100644
--- a/components/autofill/core/browser/webdata/contact_info_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/contact_info_sync_bridge.cc
@@ -66,12 +66,12 @@
 ContactInfoSyncBridge::CreateMetadataChangeList() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return std::make_unique<syncer::SyncMetadataStoreChangeList>(
-      GetAutofillTable(), syncer::CONTACT_INFO,
+      GetSyncMetadataStore(), syncer::CONTACT_INFO,
       base::BindRepeating(&syncer::ModelTypeChangeProcessor::ReportError,
                           change_processor()->GetWeakPtr()));
 }
 
-absl::optional<syncer::ModelError> ContactInfoSyncBridge::MergeFullSyncData(
+std::optional<syncer::ModelError> ContactInfoSyncBridge::MergeFullSyncData(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
   // Since the local storage is cleared when the data type is disabled in
@@ -81,10 +81,10 @@
                                                std::move(entity_data))) {
     return error;
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<syncer::ModelError>
+std::optional<syncer::ModelError>
 ContactInfoSyncBridge::ApplyIncrementalSyncChanges(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_changes) {
@@ -134,7 +134,7 @@
   if (!entity_changes.empty())
     web_data_backend_->NotifyOnAutofillChangedBySync(syncer::CONTACT_INFO);
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 void ContactInfoSyncBridge::GetData(StorageKeyList storage_keys,
@@ -273,7 +273,12 @@
   return false;
 }
 
-AutofillTable* ContactInfoSyncBridge::GetAutofillTable() {
+AddressAutofillTable* ContactInfoSyncBridge::GetAutofillTable() {
+  return AddressAutofillTable::FromWebDatabase(
+      web_data_backend_->GetDatabase());
+}
+
+AutofillTable* ContactInfoSyncBridge::GetSyncMetadataStore() {
   return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase());
 }
 
@@ -303,8 +308,8 @@
 
 void ContactInfoSyncBridge::LoadMetadata() {
   auto batch = std::make_unique<syncer::MetadataBatch>();
-  if (!GetAutofillTable()->GetAllSyncMetadata(syncer::CONTACT_INFO,
-                                              batch.get())) {
+  if (!GetSyncMetadataStore()->GetAllSyncMetadata(syncer::CONTACT_INFO,
+                                                  batch.get())) {
     change_processor()->ReportError(
         {FROM_HERE, "Failed reading CONTACT_INFO metadata from WebDatabase."});
     return;
@@ -315,7 +320,8 @@
     // contains supported fields, this means that the browser was updated and
     // we should force the initial sync flow to propagate the cached data into
     // the local model.
-    GetAutofillTable()->DeleteAllSyncMetadata(syncer::ModelType::CONTACT_INFO);
+    GetSyncMetadataStore()->DeleteAllSyncMetadata(
+        syncer::ModelType::CONTACT_INFO);
 
     batch = std::make_unique<syncer::MetadataBatch>();
   }
diff --git a/components/autofill/core/browser/webdata/contact_info_sync_bridge.h b/components/autofill/core/browser/webdata/contact_info_sync_bridge.h
index 94d259fb3..174338b 100644
--- a/components/autofill/core/browser/webdata/contact_info_sync_bridge.h
+++ b/components/autofill/core/browser/webdata/contact_info_sync_bridge.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_CONTACT_INFO_SYNC_BRIDGE_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/functional/callback.h"
@@ -14,6 +15,7 @@
 #include "base/sequence_checker.h"
 #include "base/supports_user_data.h"
 #include "components/autofill/core/browser/contact_info_sync_util.h"
+#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h"
@@ -25,7 +27,6 @@
 #include "components/sync/model/model_type_sync_bridge.h"
 #include "components/sync/model/mutable_data_batch.h"
 #include "components/sync/protocol/entity_data.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -53,10 +54,10 @@
   // syncer::ModelTypeSyncBridge implementation.
   std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
       override;
-  absl::optional<syncer::ModelError> MergeFullSyncData(
+  std::optional<syncer::ModelError> MergeFullSyncData(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_data) override;
-  absl::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
+  std::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_changes) override;
   void GetData(StorageKeyList storage_keys, DataCallback callback) override;
@@ -81,7 +82,11 @@
       const syncer::EntityMetadataMap& metadata_map) const;
 
   // Returns the `AutofillTable` associated with the `web_data_backend_`.
-  AutofillTable* GetAutofillTable();
+  AddressAutofillTable* GetAutofillTable();
+
+  // AutofillTable acts as the metadata storage for all components/autofill-
+  // related sync code.
+  AutofillTable* GetSyncMetadataStore();
 
   // Queries all `Source::kAccount` profiles from `GetAutofillTable()` and
   // restricts the result to profiles where `filter(guid)` is true.
diff --git a/components/autofill/core/browser/webdata/contact_info_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/contact_info_sync_bridge_unittest.cc
index 16f194c..c3bc7d08 100644
--- a/components/autofill/core/browser/webdata/contact_info_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/contact_info_sync_bridge_unittest.cc
@@ -15,6 +15,7 @@
 #include "components/autofill/core/browser/contact_info_sync_util.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
+#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/autofill/core/browser/webdata/mock_autofill_webdata_backend.h"
 #include "components/sync/base/features.h"
@@ -77,6 +78,7 @@
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     db_.AddTable(&table_);
+    db_.AddTable(&sync_metadata_table_);
     db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase"));
     ON_CALL(backend_, GetDatabase()).WillByDefault(testing::Return(&db_));
 
@@ -144,7 +146,8 @@
   base::ScopedTempDir temp_dir_;
   base::test::SingleThreadTaskEnvironment task_environment_;
   testing::NiceMock<MockAutofillWebDataBackend> backend_;
-  AutofillTable table_;
+  AddressAutofillTable table_;
+  AutofillTable sync_metadata_table_;
   WebDatabase db_;
   testing::NiceMock<syncer::MockModelTypeChangeProcessor> mock_processor_;
   std::unique_ptr<ContactInfoSyncBridge> bridge_;
diff --git a/components/autofill/core/browser/webdata/web_data_service_unittest.cc b/components/autofill/core/browser/webdata/web_data_service_unittest.cc
index 198a06638..8bf8a2d 100644
--- a/components/autofill/core/browser/webdata/web_data_service_unittest.cc
+++ b/components/autofill/core/browser/webdata/web_data_service_unittest.cc
@@ -24,6 +24,7 @@
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/geo/autofill_country.h"
+#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
 #include "components/autofill/core/browser/webdata/autocomplete_entry.h"
 #include "components/autofill/core/browser/webdata/autocomplete_table.h"
 #include "components/autofill/core/browser/webdata/autofill_change.h"
@@ -118,6 +119,7 @@
 
     wdbs_ = new WebDatabaseService(
         path, base::SequencedTaskRunner::GetCurrentDefault(), db_task_runner_);
+    wdbs_->AddTable(std::make_unique<AddressAutofillTable>());
     wdbs_->AddTable(std::make_unique<AutocompleteTable>());
     wdbs_->AddTable(std::make_unique<AutofillTable>());
     wdbs_->LoadDatabase();
diff --git a/components/autofill/core/common/autocomplete_parsing_util.cc b/components/autofill/core/common/autocomplete_parsing_util.cc
index 77de0eb6..45cbd943 100644
--- a/components/autofill/core/common/autocomplete_parsing_util.cc
+++ b/components/autofill/core/common/autocomplete_parsing_util.cc
@@ -104,17 +104,17 @@
 // Chrome Autofill supports a subset of the field types listed at
 // http://is.gd/whatwg_autocomplete. Returns the corresponding HtmlFieldType, if
 // `value` matches any of them.
-absl::optional<HtmlFieldType> ParseStandardizedAutocompleteAttribute(
+std::optional<HtmlFieldType> ParseStandardizedAutocompleteAttribute(
     std::string_view value) {
   auto* it = kStandardizedAttributes.find(value);
   return it != kStandardizedAttributes.end()
-             ? absl::optional<HtmlFieldType>(it->second)
-             : absl::nullopt;
+             ? std::optional<HtmlFieldType>(it->second)
+             : std::nullopt;
 }
 
 // Maps `value`s that Autofill has proposed for the HTML autocomplete standard,
 // but which are not standardized, to their HtmlFieldType.
-absl::optional<HtmlFieldType> ParseProposedAutocompleteAttribute(
+std::optional<HtmlFieldType> ParseProposedAutocompleteAttribute(
     std::string_view value) {
   static constexpr auto proposed_attributes =
       base::MakeFixedFlatMap<std::string_view, HtmlFieldType>({
@@ -126,13 +126,13 @@
 
   auto* it = proposed_attributes.find(value);
   return it != proposed_attributes.end()
-             ? absl::optional<HtmlFieldType>(it->second)
-             : absl::nullopt;
+             ? std::optional<HtmlFieldType>(it->second)
+             : std::nullopt;
 }
 
 // Maps non-standardized `value`s for the HTML autocomplete attribute to an
 // HtmlFieldType. This is primarily a list of "reasonable guesses".
-absl::optional<HtmlFieldType> ParseNonStandarizedAutocompleteAttribute(
+std::optional<HtmlFieldType> ParseNonStandarizedAutocompleteAttribute(
     std::string_view value) {
   static constexpr auto non_standardized_attributes =
       base::MakeFixedFlatMap<std::string_view, HtmlFieldType>({
@@ -152,8 +152,8 @@
 
   auto* it = non_standardized_attributes.find(value);
   return it != non_standardized_attributes.end()
-             ? absl::optional<HtmlFieldType>(it->second)
-             : absl::nullopt;
+             ? std::optional<HtmlFieldType>(it->second)
+             : std::nullopt;
 }
 
 // If the autocomplete `value` doesn't match any of Autofill's supported values,
@@ -191,7 +191,7 @@
     base::ReplaceFirstSubstringAfterOffset(&value, 0, "phone", "tel");
   }
 
-  absl::optional<HtmlFieldType> type =
+  std::optional<HtmlFieldType> type =
       ParseStandardizedAutocompleteAttribute(value);
   if (!type.has_value()) {
     type = ParseProposedAutocompleteAttribute(value);
@@ -214,7 +214,7 @@
              : HtmlFieldType::kUnrecognized;
 }
 
-absl::optional<AutocompleteParsingResult> ParseAutocompleteAttribute(
+std::optional<AutocompleteParsingResult> ParseAutocompleteAttribute(
     std::string_view autocomplete_attribute) {
   std::vector<std::string> tokens =
       LowercaseAndTokenizeAttributeString(autocomplete_attribute);
@@ -224,7 +224,7 @@
   // latter type of attribute value.
   if (tokens.empty() ||
       (tokens.size() == 1 && ShouldIgnoreAutocompleteAttribute(tokens[0]))) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   AutocompleteParsingResult result;
@@ -249,7 +249,7 @@
     // Note that an invalid token invalidates the entire attribute value, even
     // if the other tokens are valid.
     if (!ContactTypeHintMatchesFieldType(tokens.back(), result.field_type))
-      return absl::nullopt;
+      return std::nullopt;
     // Chrome Autofill ignores these type hints.
     tokens.pop_back();
   }
@@ -276,7 +276,7 @@
 
   // (5) No other tokens are allowed. If there are any remaining, abort.
   if (!tokens.empty())
-    return absl::nullopt;
+    return std::nullopt;
 
   return result;
 }
diff --git a/components/autofill/core/common/autocomplete_parsing_util.h b/components/autofill/core/common/autocomplete_parsing_util.h
index e229cd9..3b86fc1d 100644
--- a/components/autofill/core/common/autocomplete_parsing_util.h
+++ b/components/autofill/core/common/autocomplete_parsing_util.h
@@ -5,11 +5,11 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_COMMON_AUTOCOMPLETE_PARSING_UTIL_H_
 #define COMPONENTS_AUTOFILL_CORE_COMMON_AUTOCOMPLETE_PARSING_UTIL_H_
 
+#include <optional>
 #include <string>
 #include <string_view>
 
 #include "components/autofill/core/common/html_field_types.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -18,7 +18,7 @@
 // and parses the following tokens:
 // [section-*] [shipping|billing] [type_hint] field_type [webauthn]
 // The parsing extracts these components from `field.autocomplete_attribute` or
-// returns absl::nullopt, if the parsing fails. The latter happens if:
+// returns std::nullopt, if the parsing fails. The latter happens if:
 // - The autocomplete value is empty or contains more than 5 tokens.
 // - The type_hint doesn't match the field_type.
 // - If ShouldIgnoreAutocompleteAttribute(autocomplete) is true.
@@ -38,7 +38,7 @@
   bool webauthn = false;
 };
 
-absl::optional<AutocompleteParsingResult> ParseAutocompleteAttribute(
+std::optional<AutocompleteParsingResult> ParseAutocompleteAttribute(
     std::string_view autocomplete_attribute);
 
 // Checks if `autocomplete_attribute` could not be recognized but was
diff --git a/components/autofill/core/common/autocomplete_parsing_util_unittest.cc b/components/autofill/core/common/autocomplete_parsing_util_unittest.cc
index 11aac50..89923a2 100644
--- a/components/autofill/core/common/autocomplete_parsing_util_unittest.cc
+++ b/components/autofill/core/common/autocomplete_parsing_util_unittest.cc
@@ -4,12 +4,12 @@
 
 #include "components/autofill/core/common/autocomplete_parsing_util.h"
 
+#include <optional>
 #include <string>
 
 #include "base/strings/string_piece.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -17,13 +17,13 @@
 // maxlength=`max_length` results in `expected_result`.
 struct AutocompleteAttributeTestcase {
   std::string_view autocomplete;
-  absl::optional<AutocompleteParsingResult> expected_result;
+  std::optional<AutocompleteParsingResult> expected_result;
 };
 
 class AutocompleteAttributeProcessingUtilTest
     : public testing::TestWithParam<AutocompleteAttributeTestcase> {};
 
-// In general, `ParseAutocompleteAttribute()` returns absl::nullopt if one of
+// In general, `ParseAutocompleteAttribute()` returns std::nullopt if one of
 // the tokens cannot be parsed. The exception is the field type, which defaults
 // to HtmlFieldType::kUnrecognized.
 const AutocompleteAttributeTestcase kAutocompleteTestcases[]{
@@ -31,15 +31,15 @@
     {"name", {{"", HtmlFieldMode::kNone, HtmlFieldType::kName}}},
     {"autofill", {{"", HtmlFieldMode::kNone, HtmlFieldType::kUnrecognized}}},
     // autocomplete=off is ignored completely.
-    {"off", absl::nullopt},
+    {"off", std::nullopt},
 
     // Type hints:
     // They are parsed and validated, but otherwise unused. Type hints are only
     // valid before tel* and email.
     {"home email", {{"", HtmlFieldMode::kNone, HtmlFieldType::kEmail}}},
     {"work email", {{"", HtmlFieldMode::kNone, HtmlFieldType::kEmail}}},
-    {"work cc-number", absl::nullopt},
-    {"unrecognized_type_hint email", absl::nullopt},
+    {"work cc-number", std::nullopt},
+    {"unrecognized_type_hint email", std::nullopt},
 
     // Billing and shipping modes:
     {"billing country",
@@ -50,8 +50,8 @@
      {{"", HtmlFieldMode::kBilling, HtmlFieldType::kUnrecognized}}},
     {"shipping work tel-local",
      {{"", HtmlFieldMode::kShipping, HtmlFieldType::kTelLocal}}},
-    {"unrecognized_mode country", absl::nullopt},
-    {"unrecognized_mode unrecognized", absl::nullopt},
+    {"unrecognized_mode country", std::nullopt},
+    {"unrecognized_mode unrecognized", std::nullopt},
 
     // Sections:
     {"section-one tel", {{"one", HtmlFieldMode::kNone, HtmlFieldType::kTel}}},
@@ -60,9 +60,9 @@
     {"section-one shipping home tel",
      {{"one", HtmlFieldMode::kShipping, HtmlFieldType::kTel}}},
     {"section- tel", {{"", HtmlFieldMode::kNone, HtmlFieldType::kTel}}},
-    {"section tel", absl::nullopt},
-    {"no_section tel", absl::nullopt},
-    {"no_section work tel", absl::nullopt},
+    {"section tel", std::nullopt},
+    {"no_section tel", std::nullopt},
+    {"no_section work tel", std::nullopt},
     {"section-random",
      {{"", HtmlFieldMode::kNone, HtmlFieldType::kUnrecognized}}},
 
@@ -77,7 +77,7 @@
        /*webauthn=*/true}}},
 
     // Too many tokens.
-    {"hello section-one shipping home tel webauthn", absl::nullopt}};
+    {"hello section-one shipping home tel webauthn", std::nullopt}};
 
 INSTANTIATE_TEST_SUITE_P(,
                          AutocompleteAttributeProcessingUtilTest,
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index bc4fc08..3b24146 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -167,7 +167,6 @@
 const base::FeatureParam<int> kAutofillRankingFormulaCreditCardsUsageHalfLife{
     &kAutofillEnableRankingFormulaCreditCards,
     "autofill_ranking_formula_credit_cards_usage_half_life", 20};
-
 // The boost factor applied to ranking virtual cards.
 const base::FeatureParam<int> kAutofillRankingFormulaVirtualCardBoost{
     &kAutofillEnableRankingFormulaCreditCards,
@@ -177,6 +176,12 @@
     &kAutofillEnableRankingFormulaCreditCards,
     "autofill_ranking_formula_virtual_card_boost_half_life", 15};
 
+// Relaxes the requirements for offering credit card import.
+// TODO(crbug.com/1381477): Clean up when launched.
+BASE_FEATURE(kAutofillRelaxCreditCardImport,
+             "AutofillRelaxCreditCardImport",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // When enabled, autofill will fill <selectlist> elements.
 // TODO(crbug.com/1427153) Remove once autofilling <selectlist> is launched.
 BASE_FEATURE(kAutofillEnableSelectList,
@@ -636,12 +641,6 @@
              "AutofillTrackProfileTokenQuality",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Controls whether suggestions' labels use the improved label disambiguation
-// format.
-BASE_FEATURE(kAutofillUseImprovedLabelDisambiguation,
-             "AutofillUseImprovedLabelDisambiguation",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 // Controls whether to use the combined heuristic and the autocomplete section
 // implementation for section splitting or not. See https://crbug.com/1076175.
 BASE_FEATURE(kAutofillUseNewSectioningMethod,
@@ -740,6 +739,16 @@
              "AutofillAndroidDisableSuggestionsOnJSFocus",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// When enabled, FormField::MatchesRegexWithCache tries to avoid re-computing
+// whether a regex matches an input string by caching the result. The result
+// size is controlled by kAutofillEnableCacheForRegexMatchingCacheSizeParam.
+BASE_FEATURE(kAutofillEnableCacheForRegexMatching,
+             "AutofillEnableCacheForRegexMatching",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+const base::FeatureParam<int>
+    kAutofillEnableCacheForRegexMatchingCacheSizeParam{
+        &kAutofillEnableCacheForRegexMatching, "cache_size", 300};
+
 #if BUILDFLAG(IS_ANDROID)
 // Controls the whether the Chrome may provide a virtual view structure for
 // Android Autofill.
@@ -750,13 +759,6 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-BASE_FEATURE(kAutofillUseMobileLabelDisambiguation,
-             "AutofillUseMobileLabelDisambiguation",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-const char kAutofillUseMobileLabelDisambiguationParameterName[] = "variant";
-const char kAutofillUseMobileLabelDisambiguationParameterShowAll[] = "show-all";
-const char kAutofillUseMobileLabelDisambiguationParameterShowOne[] = "show-one";
-
 // When enabled, the keyboard accessory is shown for autocomplete=unrecognized
 // fields. Selecting a keyboard accessory suggestion will fill the triggering
 // field (independently of the autocomplete attribute) and all
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index 01d152e..ea64a6cb 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -98,6 +98,8 @@
 extern const base::FeatureParam<int>
     kAutofillRankingFormulaVirtualCardBoostHalfLife;
 COMPONENT_EXPORT(AUTOFILL)
+BASE_DECLARE_FEATURE(kAutofillRelaxCreditCardImport);
+COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillEnableEmailHeuristicOnlyAddressForms);
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillEnableSupportForApartmentNumbers);
@@ -210,8 +212,6 @@
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillTrackProfileTokenQuality);
 COMPONENT_EXPORT(AUTOFILL)
-BASE_DECLARE_FEATURE(kAutofillUseImprovedLabelDisambiguation);
-COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillUseNewSectioningMethod);
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillUseParameterizedSectioning);
@@ -250,6 +250,11 @@
 BASE_DECLARE_FEATURE(kAutofillVirtualCardsOnTouchToFillAndroid);
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillAndroidDisableSuggestionsOnJSFocus);
+COMPONENT_EXPORT(AUTOFILL)
+BASE_DECLARE_FEATURE(kAutofillEnableCacheForRegexMatching);
+COMPONENT_EXPORT(AUTOFILL)
+extern const base::FeatureParam<int>
+    kAutofillEnableCacheForRegexMatchingCacheSizeParam;
 
 #if BUILDFLAG(IS_ANDROID)
 COMPONENT_EXPORT(AUTOFILL)
@@ -258,14 +263,6 @@
 
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
 COMPONENT_EXPORT(AUTOFILL)
-BASE_DECLARE_FEATURE(kAutofillUseMobileLabelDisambiguation);
-COMPONENT_EXPORT(AUTOFILL)
-extern const char kAutofillUseMobileLabelDisambiguationParameterName[];
-COMPONENT_EXPORT(AUTOFILL)
-extern const char kAutofillUseMobileLabelDisambiguationParameterShowOne[];
-COMPONENT_EXPORT(AUTOFILL)
-extern const char kAutofillUseMobileLabelDisambiguationParameterShowAll[];
-COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(
     kAutofillSuggestionsForAutocompleteUnrecognizedFieldsOnMobile);
 #endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
diff --git a/components/autofill/core/common/autofill_prefs_unittest.cc b/components/autofill/core/common/autofill_prefs_unittest.cc
index 3fbc376..4b7835d 100644
--- a/components/autofill/core/common/autofill_prefs_unittest.cc
+++ b/components/autofill/core/common/autofill_prefs_unittest.cc
@@ -110,7 +110,7 @@
   // Make sure that the dictionary keys don't contain the account id.
   const auto& dictionary =
       pref_service()->GetDict(prefs::kAutofillSyncTransportOptIn);
-  EXPECT_EQ(absl::nullopt, dictionary.FindInt(account1.ToString()));
+  EXPECT_EQ(std::nullopt, dictionary.FindInt(account1.ToString()));
 }
 
 // Tests that clearing the AutofillSyncTransportOptIn works as expected.
diff --git a/components/autofill/core/common/field_data_manager.cc b/components/autofill/core/common/field_data_manager.cc
index 587a6671..e2ff05a 100644
--- a/components/autofill/core/common/field_data_manager.cc
+++ b/components/autofill/core/common/field_data_manager.cc
@@ -70,7 +70,7 @@
   if (HasFieldData(id)) {
     field_value_and_properties_map_[id].second |= mask;
   } else {
-    field_value_and_properties_map_[id] = {absl::nullopt, mask};
+    field_value_and_properties_map_[id] = {std::nullopt, mask};
   }
 }
 
diff --git a/components/autofill/core/common/field_data_manager.h b/components/autofill/core/common/field_data_manager.h
index 339b4529..6410ccf 100644
--- a/components/autofill/core/common/field_data_manager.h
+++ b/components/autofill/core/common/field_data_manager.h
@@ -6,11 +6,11 @@
 #define COMPONENTS_AUTOFILL_CORE_COMMON_FIELD_DATA_MANAGER_H_
 
 #include <map>
+#include <optional>
 #include <string>
 
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/autofill/core/common/unique_ids.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace autofill {
 
@@ -20,7 +20,7 @@
  public:
   using FieldDataMap =
       std::map<FieldRendererId,
-               std::pair<absl::optional<std::u16string>, FieldPropertiesMask>>;
+               std::pair<std::optional<std::u16string>, FieldPropertiesMask>>;
 
   FieldDataManager();
 
diff --git a/components/autofill/core/common/form_field_data.h b/components/autofill/core/common/form_field_data.h
index f85030b6..9922442 100644
--- a/components/autofill/core/common/form_field_data.h
+++ b/components/autofill/core/common/form_field_data.h
@@ -287,7 +287,7 @@
 
   FormControlType form_control_type = FormControlType::kInputText;
   std::string autocomplete_attribute;
-  absl::optional<AutocompleteParsingResult> parsed_autocomplete;
+  std::optional<AutocompleteParsingResult> parsed_autocomplete;
   std::u16string placeholder;
   std::u16string css_classes;
   std::u16string aria_label;
diff --git a/components/autofill/core/common/logging/log_buffer.cc b/components/autofill/core/common/logging/log_buffer.cc
index f6fab5e..43cb93cf6 100644
--- a/components/autofill/core/common/logging/log_buffer.cc
+++ b/components/autofill/core/common/logging/log_buffer.cc
@@ -91,7 +91,7 @@
 LogBuffer& LogBuffer::operator=(LogBuffer&& other) = default;
 LogBuffer::~LogBuffer() = default;
 
-absl::optional<base::Value::Dict> LogBuffer::RetrieveResult() {
+std::optional<base::Value::Dict> LogBuffer::RetrieveResult() {
   // The buffer should always start with a fragment.
   DCHECK(buffer_.size() >= 1);
 
@@ -101,12 +101,12 @@
 
   auto* children = buffer_[0].FindList("children");
   if (!children || children->empty())
-    return absl::nullopt;
+    return std::nullopt;
 
   // If the fragment has a single child, remove it from |children| and return
   // that directly.
   if (children->size() == 1) {
-    return absl::optional<base::Value::Dict>(
+    return std::optional<base::Value::Dict>(
         std::move((*children).back().GetDict()));
   }
 
@@ -189,7 +189,7 @@
   if (!buf.active())
     return buf;
 
-  absl::optional<base::Value::Dict> node_to_add = buffer.RetrieveResult();
+  std::optional<base::Value::Dict> node_to_add = buffer.RetrieveResult();
   if (!node_to_add)
     return buf;
 
diff --git a/components/autofill/core/common/logging/log_buffer.h b/components/autofill/core/common/logging/log_buffer.h
index daf96558..7ae6a63 100644
--- a/components/autofill/core/common/logging/log_buffer.h
+++ b/components/autofill/core/common/logging/log_buffer.h
@@ -112,7 +112,7 @@
   LogBuffer& operator=(const LogBuffer& other) = delete;
 
   // Returns the contents of the buffer if any and empties it.
-  absl::optional<base::Value::Dict> RetrieveResult();
+  std::optional<base::Value::Dict> RetrieveResult();
 
   // Returns whether an active WebUI is listening. If false, the buffer may
   // not do any logging.
diff --git a/components/autofill/core/common/mojom/autofill_types_mojom_traits.h b/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
index a8298b3e..49e7030 100644
--- a/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
+++ b/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
@@ -223,7 +223,7 @@
     return r.autocomplete_attribute;
   }
 
-  static const absl::optional<autofill::AutocompleteParsingResult>
+  static const std::optional<autofill::AutocompleteParsingResult>
   parsed_autocomplete(const autofill::FormFieldData& r) {
     return r.parsed_autocomplete;
   }
diff --git a/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc b/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
index cd670b6..503d058 100644
--- a/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
+++ b/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
@@ -341,7 +341,7 @@
   input.id_attribute = u"id";
   input.name_attribute = u"name";
   input.autocomplete_attribute = "on";
-  input.parsed_autocomplete = absl::nullopt;
+  input.parsed_autocomplete = std::nullopt;
   input.placeholder = u"placeholder";
   input.css_classes = u"class1";
   input.aria_label = u"aria label";
diff --git a/components/autofill/ios/browser/autofill_driver_ios.h b/components/autofill/ios/browser/autofill_driver_ios.h
index e718f8d..04cea865 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.h
+++ b/components/autofill/ios/browser/autofill_driver_ios.h
@@ -45,7 +45,7 @@
 
   // AutofillDriver:
   LocalFrameToken GetFrameToken() const override;
-  absl::optional<LocalFrameToken> Resolve(FrameToken query) override;
+  std::optional<LocalFrameToken> Resolve(FrameToken query) override;
   AutofillDriverIOS* GetParent() override;
   BrowserAutofillManager& GetAutofillManager() override;
   bool IsInActiveFrame() const override;
diff --git a/components/autofill/ios/form_util/form_handlers_java_script_feature.h b/components/autofill/ios/form_util/form_handlers_java_script_feature.h
index 26bfbdae..962812b 100644
--- a/components/autofill/ios/form_util/form_handlers_java_script_feature.h
+++ b/components/autofill/ios/form_util/form_handlers_java_script_feature.h
@@ -35,7 +35,7 @@
   friend class base::NoDestructor<FormHandlersJavaScriptFeature>;
 
   // web::JavaScriptFeature
-  absl::optional<std::string> GetScriptMessageHandlerName() const override;
+  std::optional<std::string> GetScriptMessageHandlerName() const override;
   void ScriptMessageReceived(web::WebState* web_state,
                              const web::ScriptMessage& message) override;
 
diff --git a/components/browser_ui/media/android/BUILD.gn b/components/browser_ui/media/android/BUILD.gn
index 7446ad08..46997ab 100644
--- a/components/browser_ui/media/android/BUILD.gn
+++ b/components/browser_ui/media/android/BUILD.gn
@@ -13,7 +13,6 @@
     "java/src/org/chromium/components/browser_ui/media/MediaNotificationInfo.java",
     "java/src/org/chromium/components/browser_ui/media/MediaNotificationListener.java",
     "java/src/org/chromium/components/browser_ui/media/MediaNotificationManager.java",
-    "java/src/org/chromium/components/browser_ui/media/MediaNotificationUma.java",
     "java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java",
     "java/src/org/chromium/components/browser_ui/media/MediaSessionUma.java",
   ]
diff --git a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationUma.java b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationUma.java
deleted file mode 100644
index 1650944..0000000
--- a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationUma.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2016 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.browser_ui.media;
-
-import android.content.Intent;
-
-import androidx.annotation.IntDef;
-
-import org.chromium.base.metrics.RecordHistogram;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Helper class to record which kind of media notifications does the user click to go back to
- * Chrome.
- */
-public class MediaNotificationUma {
-    @IntDef({Source.INVALID, Source.MEDIA, Source.PRESENTATION, Source.MEDIA_FLING})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Source {
-        int INVALID = -1;
-        int MEDIA = 0;
-        int PRESENTATION = 1;
-        int MEDIA_FLING = 2;
-        int NUM_ENTRIES = 3;
-    }
-
-    public static final String INTENT_EXTRA_NAME =
-            "org.chromium.chrome.browser.metrics.MediaNotificationUma.EXTRA_CLICK_SOURCE";
-
-    /**
-     * Record the UMA as specified by {@link intent}. The {@link intent} should contain intent extra
-     * of name {@link INTENT_EXTRA_NAME} indicating the type.
-     * @param intent The intent starting the activity.
-     */
-    public static void recordClickSource(Intent intent) {
-        if (intent == null) return;
-        @Source int source = intent.getIntExtra(INTENT_EXTRA_NAME, Source.INVALID);
-        if (source == Source.INVALID || source >= Source.NUM_ENTRIES) return;
-        RecordHistogram.recordEnumeratedHistogram(
-                "Media.Notification.Click", source, Source.NUM_ENTRIES);
-    }
-}
diff --git a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java
index a62c349..11f09067 100644
--- a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java
+++ b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java
@@ -190,11 +190,6 @@
                 }
 
                 Intent contentIntent = mDelegate.createBringTabToFrontIntent();
-                if (contentIntent != null) {
-                    contentIntent.putExtra(
-                            MediaNotificationUma.INTENT_EXTRA_NAME,
-                            MediaNotificationUma.Source.MEDIA);
-                }
 
                 if (mFallbackTitle == null) mFallbackTitle = sanitizeMediaTitle(mOrigin);
 
diff --git a/components/client_hints/OWNERS b/components/client_hints/OWNERS
index 308a03815..1268c37 100644
--- a/components/client_hints/OWNERS
+++ b/components/client_hints/OWNERS
@@ -1,3 +1,4 @@
 arichiv@chromium.org
 ryansturm@chromium.org
 victortan@chromium.org
+yoavweiss@chromium.org
diff --git a/components/content_settings/core/browser/content_settings_default_provider.cc b/components/content_settings/core/browser/content_settings_default_provider.cc
index 1b0f509..f046137 100644
--- a/components/content_settings/core/browser/content_settings_default_provider.cc
+++ b/components/content_settings/core/browser/content_settings_default_provider.cc
@@ -11,6 +11,7 @@
 #include "base/functional/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
+#include "build/blink_buildflags.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "components/content_settings/core/browser/content_settings_info.h"
@@ -356,6 +357,15 @@
       IntToContentSetting(
           prefs_->GetInteger(GetPrefName(ContentSettingsType::POPUPS))),
       CONTENT_SETTING_NUM_SETTINGS);
+
+#if BUILDFLAG(USE_BLINK)
+  base::UmaHistogramEnumeration(
+      "ContentSettings.RegularProfile.DefaultSubresourceFilterSetting",
+      IntToContentSetting(
+          prefs_->GetInteger(GetPrefName(ContentSettingsType::ADS))),
+      CONTENT_SETTING_NUM_SETTINGS);
+#endif
+
 #if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
   base::UmaHistogramEnumeration(
       "ContentSettings.RegularProfile.DefaultImagesSetting",
@@ -413,11 +423,6 @@
           prefs_->GetInteger(GetPrefName(ContentSettingsType::AUTOPLAY))),
       CONTENT_SETTING_NUM_SETTINGS);
   base::UmaHistogramEnumeration(
-      "ContentSettings.RegularProfile.DefaultSubresourceFilterSetting",
-      IntToContentSetting(
-          prefs_->GetInteger(GetPrefName(ContentSettingsType::ADS))),
-      CONTENT_SETTING_NUM_SETTINGS);
-  base::UmaHistogramEnumeration(
       "ContentSettings.RegularProfile.DefaultSoundSetting",
       IntToContentSetting(
           prefs_->GetInteger(GetPrefName(ContentSettingsType::SOUND))),
diff --git a/components/content_settings/core/browser/content_settings_registry.cc b/components/content_settings/core/browser/content_settings_registry.cc
index efca3144..a5e61efa 100644
--- a/components/content_settings/core/browser/content_settings_registry.cc
+++ b/components/content_settings/core/browser/content_settings_registry.cc
@@ -110,7 +110,7 @@
            WebsiteSettingsInfo::TOP_ORIGIN_WITH_RESOURCE_EXCEPTIONS_SCOPE,
            WebsiteSettingsRegistry::DESKTOP |
                WebsiteSettingsRegistry::PLATFORM_ANDROID
-#if BUILDFLAG(IS_IOS) && BUILDFLAG(USE_BLINK)
+#if BUILDFLAG(USE_BLINK)
                | WebsiteSettingsRegistry::PLATFORM_IOS
 #endif
            ,
@@ -278,7 +278,11 @@
            /*valid_settings=*/{CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK},
            WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE,
            WebsiteSettingsRegistry::DESKTOP |
-               WebsiteSettingsRegistry::PLATFORM_ANDROID,
+               WebsiteSettingsRegistry::PLATFORM_ANDROID
+#if BUILDFLAG(USE_BLINK)
+               | WebsiteSettingsRegistry::PLATFORM_IOS
+#endif
+           ,
            ContentSettingsInfo::INHERIT_IN_INCOGNITO,
            ContentSettingsInfo::EXCEPTIONS_ON_SECURE_AND_INSECURE_ORIGINS);
 
@@ -599,7 +603,7 @@
            /*valid_settings=*/{CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK},
            WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE,
            WebsiteSettingsRegistry::PLATFORM_ANDROID
-#if BUILDFLAG(IS_IOS) && BUILDFLAG(USE_BLINK)
+#if BUILDFLAG(USE_BLINK)
                | WebsiteSettingsRegistry::PLATFORM_IOS
 #endif
            ,
diff --git a/components/content_settings/core/browser/website_settings_registry.cc b/components/content_settings/core/browser/website_settings_registry.cc
index a46ed8a9..c6e1d49e 100644
--- a/components/content_settings/core/browser/website_settings_registry.cc
+++ b/components/content_settings/core/browser/website_settings_registry.cc
@@ -136,7 +136,7 @@
            WebsiteSettingsInfo::UNSYNCABLE, WebsiteSettingsInfo::LOSSY,
            WebsiteSettingsInfo::GENERIC_SINGLE_ORIGIN_SCOPE,
            DESKTOP | PLATFORM_ANDROID
-#if BUILDFLAG(IS_IOS) && BUILDFLAG(USE_BLINK)
+#if BUILDFLAG(USE_BLINK)
                | PLATFORM_IOS
 #endif
            ,
@@ -146,7 +146,7 @@
            WebsiteSettingsInfo::LOSSY,
            WebsiteSettingsInfo::GENERIC_SINGLE_ORIGIN_SCOPE,
            DESKTOP | PLATFORM_ANDROID
-#if BUILDFLAG(IS_IOS) && BUILDFLAG(USE_BLINK)
+#if BUILDFLAG(USE_BLINK)
                | PLATFORM_IOS
 #endif
            ,
@@ -176,11 +176,16 @@
   // Set when an origin is activated for subresource filtering and the
   // associated UI is shown to the user. Cleared when a site is de-activated or
   // the first URL matching the origin is removed from history.
-  Register(
-      ContentSettingsType::ADS_DATA, "subresource-filter-data", base::Value(),
-      WebsiteSettingsInfo::UNSYNCABLE, WebsiteSettingsInfo::NOT_LOSSY,
-      WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE, DESKTOP | PLATFORM_ANDROID,
-      WebsiteSettingsInfo::INHERIT_IN_INCOGNITO);
+  Register(ContentSettingsType::ADS_DATA, "subresource-filter-data",
+           base::Value(), WebsiteSettingsInfo::UNSYNCABLE,
+           WebsiteSettingsInfo::NOT_LOSSY,
+           WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE,
+           DESKTOP | PLATFORM_ANDROID
+#if BUILDFLAG(USE_BLINK)
+               | PLATFORM_IOS
+#endif
+           ,
+           WebsiteSettingsInfo::INHERIT_IN_INCOGNITO);
   Register(
       ContentSettingsType::MEDIA_ENGAGEMENT, "media-engagement", base::Value(),
       WebsiteSettingsInfo::UNSYNCABLE, WebsiteSettingsInfo::LOSSY,
diff --git a/components/global_media_controls/public/media_session_item_producer.cc b/components/global_media_controls/public/media_session_item_producer.cc
index 6dbf357..50cd829 100644
--- a/components/global_media_controls/public/media_session_item_producer.cc
+++ b/components/global_media_controls/public/media_session_item_producer.cc
@@ -20,15 +20,6 @@
 
 constexpr const char kAutoDismissTimerInMinutesParamName[] = "timer_in_minutes";
 
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class MediaNotificationClickSource {
-  kMedia = 0,
-  kPresentation,
-  kMediaFling,
-  kMaxValue = kMediaFling
-};
-
 // Returns the time value to be used for the auto-dismissing of the
 // notifications after they are inactive.
 // If the feature (auto-dismiss) is disabled, the returned value will be
@@ -336,9 +327,6 @@
 
   it->second.OnSessionInteractedWith();
 
-  base::UmaHistogramEnumeration("Media.Notification.Click",
-                                MediaNotificationClickSource::kMedia);
-
   if (activate_original_media) {
     it->second.item()->Raise();
   }
diff --git a/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/CafNotificationController.java b/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/CafNotificationController.java
index 545b225f..fca92c8 100644
--- a/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/CafNotificationController.java
+++ b/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/CafNotificationController.java
@@ -6,7 +6,6 @@
 
 import android.content.Intent;
 
-import org.chromium.components.browser_ui.media.MediaNotificationUma;
 import org.chromium.components.media_router.MediaRouterClient;
 
 /** NotificationController implementation for presentation. */
@@ -18,13 +17,7 @@
 
     @Override
     public Intent createContentIntent() {
-        Intent contentIntent = createBringTabToFrontIntent();
-        if (contentIntent != null) {
-            contentIntent.putExtra(
-                    MediaNotificationUma.INTENT_EXTRA_NAME,
-                    MediaNotificationUma.Source.PRESENTATION);
-        }
-        return contentIntent;
+        return createBringTabToFrontIntent();
     }
 
     @Override
diff --git a/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/remoting/CafExpandedControllerActivity.java b/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/remoting/CafExpandedControllerActivity.java
index 03817d3..3685c97 100644
--- a/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/remoting/CafExpandedControllerActivity.java
+++ b/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/remoting/CafExpandedControllerActivity.java
@@ -17,7 +17,6 @@
 import androidx.fragment.app.FragmentActivity;
 import androidx.mediarouter.app.MediaRouteButton;
 
-import org.chromium.components.browser_ui.media.MediaNotificationUma;
 import org.chromium.components.media_router.R;
 import org.chromium.components.media_router.caf.BaseSessionController;
 import org.chromium.third_party.android.media.MediaController;
@@ -100,8 +99,6 @@
 
         mSessionController = RemotingSessionController.getInstance();
 
-        MediaNotificationUma.recordClickSource(getIntent());
-
         if (mSessionController == null || !mSessionController.isConnected()) {
             finish();
             return;
diff --git a/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/remoting/RemotingNotificationController.java b/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/remoting/RemotingNotificationController.java
index a98d6fc..be79de0 100644
--- a/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/remoting/RemotingNotificationController.java
+++ b/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/remoting/RemotingNotificationController.java
@@ -7,7 +7,6 @@
 import android.content.Intent;
 
 import org.chromium.base.ContextUtils;
-import org.chromium.components.browser_ui.media.MediaNotificationUma;
 import org.chromium.components.media_router.MediaRouterClient;
 import org.chromium.components.media_router.caf.BaseNotificationController;
 import org.chromium.components.media_router.caf.BaseSessionController;
@@ -21,12 +20,8 @@
 
     @Override
     public Intent createContentIntent() {
-        Intent contentIntent =
-                new Intent(
-                        ContextUtils.getApplicationContext(), CafExpandedControllerActivity.class);
-        contentIntent.putExtra(
-                MediaNotificationUma.INTENT_EXTRA_NAME, MediaNotificationUma.Source.MEDIA_FLING);
-        return contentIntent;
+        return new Intent(
+                ContextUtils.getApplicationContext(), CafExpandedControllerActivity.class);
     }
 
     @Override
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index 225fc6b5d..15ba6cf 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -4315,6 +4315,16 @@
     CA_FAILURE = 9;
     // The referenced certificate provisioning profile does not exist.
     PROFILE_NOT_FOUND = 10;
+    // A required attribute of the entity that is requesting a certificate was
+    // not present.
+    ESS_ATTRIBUTE_NOT_FOUND = 11;
+    // The certificate provisioning profile is referencing a non-existent CA
+    // connection.
+    CA_CONNECTION_NOT_FOUND = 12;
+    // The CA connection referenced by the certificate provisioning profile
+    // is referencing a non-existent PubSub topic, or the system does not have
+    // permission to publish to that PubSub topic.
+    PUBSUB_TOPIC_NOT_FOUND = 13;
   }
 
   // The specific error type.
diff --git a/components/safe_search_api/url_checker.cc b/components/safe_search_api/url_checker.cc
index 24fbe99c..93fd38f 100644
--- a/components/safe_search_api/url_checker.cc
+++ b/components/safe_search_api/url_checker.cc
@@ -4,12 +4,15 @@
 
 #include "components/safe_search_api/url_checker.h"
 
+#include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -19,6 +22,7 @@
 namespace {
 const size_t kDefaultCacheSize = 1000;
 const size_t kDefaultCacheTimeoutSeconds = 3600;
+constexpr std::string_view kCacheHitMetricKey{"Net.SafeSearch.CacheHit"};
 }  // namespace
 
 struct URLChecker::Check {
@@ -52,6 +56,25 @@
 
 URLChecker::~URLChecker() = default;
 
+void URLChecker::MaybeScheduleAsyncCheck(const GURL& url,
+                                         CheckCallback callback) {
+  // See if we already have a check in progress for this URL.
+  for (const auto& check : checks_in_progress_) {
+    if (check->url == url) {
+      DVLOG(1) << "Adding to pending check for " << url.spec();
+      check->callbacks.push_back(std::move(callback));
+      return;
+    }
+  }
+
+  auto it = checks_in_progress_.insert(
+      checks_in_progress_.begin(),
+      std::make_unique<Check>(url, std::move(callback)));
+  async_checker_->CheckURL(url,
+                           base::BindOnce(&URLChecker::OnAsyncCheckComplete,
+                                          weak_factory_.GetWeakPtr(), it));
+}
+
 bool URLChecker::CheckURL(const GURL& url, CheckCallback callback) {
   auto cache_it = cache_.Get(url);
   if (cache_it != cache_.end()) {
@@ -62,28 +85,22 @@
                << (result.classification == Classification::UNSAFE ? "NOT" : "")
                << " safe; certain: " << !result.uncertain;
       std::move(callback).Run(url, result.classification, result.uncertain);
+
+      base::UmaHistogramEnumeration(std::string(kCacheHitMetricKey),
+                                    CacheAccessStatus::kHit);
       return true;
     }
     DVLOG(1) << "Outdated cache entry for " << url.spec() << ", purging";
     cache_.Erase(cache_it);
+    base::UmaHistogramEnumeration(std::string(kCacheHitMetricKey),
+                                  CacheAccessStatus::kOutdated);
+    MaybeScheduleAsyncCheck(url, std::move(callback));
+    return false;
   }
 
-  // See if we already have a check in progress for this URL.
-  for (const auto& check : checks_in_progress_) {
-    if (check->url == url) {
-      DVLOG(1) << "Adding to pending check for " << url.spec();
-      check->callbacks.push_back(std::move(callback));
-      return false;
-    }
-  }
-
-  auto it = checks_in_progress_.insert(
-      checks_in_progress_.begin(),
-      std::make_unique<Check>(url, std::move(callback)));
-  async_checker_->CheckURL(url,
-                           base::BindOnce(&URLChecker::OnAsyncCheckComplete,
-                                          weak_factory_.GetWeakPtr(), it));
-
+  base::UmaHistogramEnumeration(std::string(kCacheHitMetricKey),
+                                CacheAccessStatus::kNotFound);
+  MaybeScheduleAsyncCheck(url, std::move(callback));
   return false;
 }
 
diff --git a/components/safe_search_api/url_checker.h b/components/safe_search_api/url_checker.h
index 3a6b3cd..168a4766d 100644
--- a/components/safe_search_api/url_checker.h
+++ b/components/safe_search_api/url_checker.h
@@ -20,6 +20,18 @@
 // The SafeSearch API classification of a URL.
 enum class Classification { SAFE, UNSAFE };
 
+// These values are sent to Uma to understand Cache utilization. Must not be
+// renumbered.
+enum class CacheAccessStatus {
+  // Entry was found.
+  kHit = 0,
+  // Entry was not found.
+  kNotFound = 1,
+  // Entry was found but was stale.
+  kOutdated = 2,
+  kMaxValue = kOutdated,
+};
+
 // This class uses one implementation of URLCheckerClient to check the
 // classification of the content on a given URL and returns the result
 // asynchronously via a callback. It is also responsible for the synchronous
@@ -63,6 +75,10 @@
                             const GURL& url,
                             ClientClassification classification);
 
+  // Either reuses pending check for given url if it already exists (see
+  // checks_in_progress_), or schedules a new one.
+  void MaybeScheduleAsyncCheck(const GURL& url, CheckCallback callback);
+
   std::unique_ptr<URLCheckerClient> async_checker_;
   CheckList checks_in_progress_;
 
diff --git a/components/safe_search_api/url_checker_unittest.cc b/components/safe_search_api/url_checker_unittest.cc
index 0550c7d..30847c5 100644
--- a/components/safe_search_api/url_checker_unittest.cc
+++ b/components/safe_search_api/url_checker_unittest.cc
@@ -6,13 +6,18 @@
 
 #include <stddef.h>
 
+#include <algorithm>
+#include <iterator>
+#include <map>
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "components/safe_search_api/fake_url_checker_client.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -48,6 +53,14 @@
   }
 }
 
+auto Recorded(const std::map<CacheAccessStatus, int>& expected) {
+  std::vector<base::Bucket> buckets_array;
+  std::transform(
+      expected.begin(), expected.end(), std::back_inserter(buckets_array),
+      [](auto& entry) { return base::Bucket(entry.first, entry.second); });
+  return base::BucketsInclude(buckets_array);
+}
+
 }  // namespace
 
 class SafeSearchURLCheckerTest : public testing::Test {
@@ -86,10 +99,17 @@
     return result;
   }
 
+  std::vector<base::Bucket> CacheHitMetric() {
+    return histogram_tester_.GetAllSamples("Net.SafeSearch.CacheHit");
+  }
+
   size_t next_url_{0};
   raw_ptr<FakeURLCheckerClient, DanglingUntriaged> fake_client_;
   std::unique_ptr<URLChecker> checker_;
   base::test::SingleThreadTaskEnvironment task_environment_;
+
+ private:
+  base::HistogramTester histogram_tester_;
 };
 
 TEST_F(SafeSearchURLCheckerTest, Simple) {
@@ -112,6 +132,9 @@
                 OnCheckDone(url, Classification::SAFE, /*uncertain=*/true));
     ASSERT_FALSE(SendResponse(url, Classification::SAFE, /*uncertain=*/true));
   }
+
+  EXPECT_THAT(CacheHitMetric(), Recorded({{CacheAccessStatus::kHit, 0},
+                                          {CacheAccessStatus::kNotFound, 3}}));
 }
 
 TEST_F(SafeSearchURLCheckerTest, Cache) {
@@ -145,6 +168,9 @@
   EXPECT_CALL(*this,
               OnCheckDone(url2, Classification::SAFE, /*uncertain=*/false));
   ASSERT_FALSE(SendResponse(url2, Classification::SAFE, /*uncertain=*/false));
+
+  EXPECT_THAT(CacheHitMetric(), Recorded({{CacheAccessStatus::kHit, 2},
+                                          {CacheAccessStatus::kNotFound, 4}}));
 }
 
 TEST_F(SafeSearchURLCheckerTest, CoalesceRequestsToSameURL) {
@@ -155,6 +181,9 @@
   // A single response should answer both of those checks
   EXPECT_CALL(*this, OnCheckDone(url, Classification::SAFE, false)).Times(2);
   fake_client_->RunCallback(ToAPIClassification(Classification::SAFE, false));
+
+  EXPECT_THAT(CacheHitMetric(), Recorded({{CacheAccessStatus::kHit, 0},
+                                          {CacheAccessStatus::kNotFound, 2}}));
 }
 
 TEST_F(SafeSearchURLCheckerTest, CacheTimeout) {
@@ -171,6 +200,10 @@
   EXPECT_CALL(*this,
               OnCheckDone(url, Classification::UNSAFE, /*uncertain=*/false));
   ASSERT_FALSE(SendResponse(url, Classification::UNSAFE, /*uncertain=*/false));
+
+  EXPECT_THAT(CacheHitMetric(), Recorded({{CacheAccessStatus::kHit, 0},
+                                          {CacheAccessStatus::kNotFound, 1},
+                                          {CacheAccessStatus::kOutdated, 1}}));
 }
 
 TEST_F(SafeSearchURLCheckerTest, DoNotCacheUncertainClassifications) {
@@ -180,6 +213,9 @@
       url, Classification::SAFE,
       /*uncertain=*/true));     // First check was asynchronous (uncached).
   EXPECT_FALSE(CheckURL(url));  // And so was the second one.
+
+  EXPECT_THAT(CacheHitMetric(), Recorded({{CacheAccessStatus::kHit, 0},
+                                          {CacheAccessStatus::kNotFound, 2}}));
 }
 
 TEST_F(SafeSearchURLCheckerTest, DestroyURLCheckerBeforeCallback) {
@@ -196,6 +232,9 @@
 
   // The callback should now be invalid.
   task_environment_.RunUntilIdle();
+
+  EXPECT_THAT(CacheHitMetric(), Recorded({{CacheAccessStatus::kHit, 0},
+                                          {CacheAccessStatus::kNotFound, 1}}));
 }
 
 }  // namespace safe_search_api
diff --git a/components/soda/constants.cc b/components/soda/constants.cc
index 95bb1ab..e5dd19add 100644
--- a/components/soda/constants.cc
+++ b/components/soda/constants.cc
@@ -159,20 +159,6 @@
   return absl::nullopt;
 }
 
-absl::optional<SodaLanguagePackComponentConfig>
-GetLanguageComponentConfigMatchingLanguageSubtag(
-    const std::string& language_name) {
-  for (const SodaLanguagePackComponentConfig& config :
-       kLanguageComponentConfigs) {
-    if (l10n_util::GetLanguage(base::ToLowerASCII(config.language_name)) ==
-        l10n_util::GetLanguage(base::ToLowerASCII(language_name))) {
-      return config;
-    }
-  }
-
-  return absl::nullopt;
-}
-
 LanguageCode GetLanguageCodeByComponentId(const std::string& component_id) {
   for (const SodaLanguagePackComponentConfig& config :
        kLanguageComponentConfigs) {
diff --git a/components/soda/constants.h b/components/soda/constants.h
index af5544eb..6d4f5be 100644
--- a/components/soda/constants.h
+++ b/components/soda/constants.h
@@ -230,13 +230,6 @@
 absl::optional<SodaLanguagePackComponentConfig> GetLanguageComponentConfig(
     const std::string& language_name);
 
-// Get the language component config matching a given language subtag. For
-// example, the "fr-CA" language name will return the language component config
-// for "fr-FR".
-absl::optional<SodaLanguagePackComponentConfig>
-GetLanguageComponentConfigMatchingLanguageSubtag(
-    const std::string& language_name);
-
 LanguageCode GetLanguageCodeByComponentId(const std::string& component_id);
 
 std::string GetLanguageName(LanguageCode language_code);
diff --git a/components/subresource_filter/content/browser/BUILD.gn b/components/subresource_filter/content/browser/BUILD.gn
index 8b6f78dc..27523cb 100644
--- a/components/subresource_filter/content/browser/BUILD.gn
+++ b/components/subresource_filter/content/browser/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/features.gni")
+
 static_library("browser") {
   sources = [
     "activation_state_computing_navigation_throttle.cc",
@@ -208,4 +210,7 @@
       "//components/strings:components_strings_grit",
     ]
   }
+  if (is_ios && use_blink) {
+    deps += [ "//components/test:subresource_filter_test_bundle_data" ]
+  }
 }
diff --git a/components/supervised_user/core/browser/proto_fetcher.cc b/components/supervised_user/core/browser/proto_fetcher.cc
index e9a7aa7..1eb312a 100644
--- a/components/supervised_user/core/browser/proto_fetcher.cc
+++ b/components/supervised_user/core/browser/proto_fetcher.cc
@@ -365,16 +365,6 @@
   FetcherImpl(const FetcherImpl&) = delete;
   FetcherImpl& operator=(const FetcherImpl&) = delete;
 
- protected:
-  std::string GetMetricKey(StringPiece metric_id) const {
-    return JoinString({config_.histogram_basename, metric_id}, ".");
-  }
-  std::string GetMetricKey(StringPiece metric_id,
-                           StringPiece metric_suffix) const {
-    return JoinString({config_.histogram_basename, metric_id, metric_suffix},
-                      ".");
-  }
-
  private:
   void RecordMetrics(const ProtoFetcherStatus& status) {
     metrics_.RecordStatus(status);
diff --git a/components/unexportable_keys/BUILD.gn b/components/unexportable_keys/BUILD.gn
index 1c7b3977..1aad660 100644
--- a/components/unexportable_keys/BUILD.gn
+++ b/components/unexportable_keys/BUILD.gn
@@ -12,6 +12,8 @@
     "background_task_priority.h",
     "background_task_type.cc",
     "background_task_type.h",
+    "features.cc",
+    "features.h",
     "ref_counted_unexportable_signing_key.cc",
     "ref_counted_unexportable_signing_key.h",
     "service_error.h",
diff --git a/components/unexportable_keys/features.cc b/components/unexportable_keys/features.cc
new file mode 100644
index 0000000..52e0c92
--- /dev/null
+++ b/components/unexportable_keys/features.cc
@@ -0,0 +1,13 @@
+// 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.
+
+#include "components/unexportable_keys/features.h"
+
+namespace unexportable_keys {
+
+BASE_FEATURE(kEnableBoundSessionCredentialsSoftwareKeysForManualTesting,
+             "EnableBoundSessionCredentialsSoftwareKeysForManualTesting",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+}  // namespace unexportable_keys
diff --git a/components/unexportable_keys/features.h b/components/unexportable_keys/features.h
new file mode 100644
index 0000000..743ff324
--- /dev/null
+++ b/components/unexportable_keys/features.h
@@ -0,0 +1,19 @@
+// 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 COMPONENTS_UNEXPORTABLE_KEYS_FEATURES_H_
+#define COMPONENTS_UNEXPORTABLE_KEYS_FEATURES_H_
+
+#include "base/feature_list.h"
+
+namespace unexportable_keys {
+
+// If enabled, DBSC-related code will switch `UnexportableKeyProvider` to a mock
+// software-backed implementation.
+// This feature flag is expected to never be shipped to end users.
+BASE_DECLARE_FEATURE(
+    kEnableBoundSessionCredentialsSoftwareKeysForManualTesting);
+
+}  // namespace unexportable_keys
+#endif  // COMPONENTS_UNEXPORTABLE_KEYS_FEATURES_H_
diff --git a/components/unexportable_keys/unexportable_key_task_manager.cc b/components/unexportable_keys/unexportable_key_task_manager.cc
index 3e9f967..b8bbb2e 100644
--- a/components/unexportable_keys/unexportable_key_task_manager.cc
+++ b/components/unexportable_keys/unexportable_key_task_manager.cc
@@ -19,6 +19,7 @@
 #include "components/unexportable_keys/background_long_task_scheduler.h"
 #include "components/unexportable_keys/background_task_priority.h"
 #include "components/unexportable_keys/background_task_type.h"
+#include "components/unexportable_keys/features.h"
 #include "components/unexportable_keys/ref_counted_unexportable_signing_key.h"
 #include "components/unexportable_keys/service_error.h"
 #include "components/unexportable_keys/unexportable_key_id.h"
@@ -93,10 +94,6 @@
 // static
 std::unique_ptr<crypto::UnexportableKeyProvider>
 UnexportableKeyTaskManager::GetUnexportableKeyProvider() {
-  static BASE_FEATURE(
-      kEnableBoundSessionCredentialsSoftwareKeysForManualTesting,
-      "EnableBoundSessionCredentialsSoftwareKeysForManualTesting",
-      base::FEATURE_DISABLED_BY_DEFAULT);
   if (base::FeatureList::IsEnabled(
           kEnableBoundSessionCredentialsSoftwareKeysForManualTesting)) {
     return crypto::GetSoftwareUnsecureUnexportableKeyProvider();
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc
index 9064745..dac8285a 100644
--- a/components/viz/service/display/renderer_pixeltest.cc
+++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -5979,9 +5979,9 @@
 
 INSTANTIATE_TEST_SUITE_P(,
                          DelegatedInkTest,
-                         testing::ValuesIn(GetRendererTypesSkiaOnly()),
+                         testing::ValuesIn(GetGpuRendererTypes()),
                          testing::PrintToStringParamName());
-// GetRendererTypesSkiaOnly() can return an empty list, e.g. on Fuchsia ARM64.
+// GetGpuRendererTypes() can return an empty list, e.g. on Fuchsia ARM64.
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DelegatedInkTest);
 
 // Test to confirm that predicted points are not drawn if prediction is not
@@ -6034,10 +6034,10 @@
 
 INSTANTIATE_TEST_SUITE_P(,
                          DelegatedInkWithPredictionTest,
-                         testing::ValuesIn(GetRendererTypesSkiaOnly()),
+                         testing::ValuesIn(GetGpuRendererTypes()),
                          testing::PrintToStringParamName());
 
-// GetRendererTypesSkiaOnly() can return an empty list, e.g. on Fuchsia ARM64.
+// GetGpuRendererTypes() can return an empty list, e.g. on Fuchsia ARM64.
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DelegatedInkWithPredictionTest);
 
 // Draw a single trail and erase it, making sure that no bits of trail are left
diff --git a/components/viz/service/display/skia_output_surface.h b/components/viz/service/display/skia_output_surface.h
index b96b656..71ce7727 100644
--- a/components/viz/service/display/skia_output_surface.h
+++ b/components/viz/service/display/skia_output_surface.h
@@ -221,6 +221,10 @@
   // Enqueue a GPU task to delete the specified shared image.
   virtual void DestroySharedImage(const gpu::Mailbox& mailbox) = 0;
 
+  // Enqueue a GPU task to set specified shared image as `purgeable`.
+  virtual void SetSharedImagePurgeable(const gpu::Mailbox& mailbox,
+                                       bool purgeable) = 0;
+
   virtual bool SupportsBGRA() const = 0;
 };
 
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index dcdd2308e..56b436e 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -1090,16 +1090,19 @@
           current_frame()->overlay_list.begin(), surface_candidate);
     }
   } else {
-#if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_APPLE)
-    // If there's no primary plane on these platforms it mean's we're delegating
-    // to the system compositor, and don't need the buffers anymore. If those
-    // buffers are managed by buffer_queue_, we can tell it to destroy them.
-    // They'll be recreated when we need them again when GetCurrentBuffer() is
-    // called.
     if (buffer_queue_) {
+      // If there's no primary plane on these platforms it mean's we're
+      // delegating to the system compositor, and don't need the buffers
+      // anymore. On LaCrOS the primary plane buffers are immediately destroyed.
+      // They'll be recreated when we need them again when GetCurrentBuffer() is
+      // called. On Mac the primary plane buffers are marked as purgeable so the
+      // OS can decide if they should be destroyed or not.
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
       buffer_queue_->DestroyBuffers();
+#elif BUILDFLAG(IS_APPLE)
+      buffer_queue_->SetBuffersPurgeable();
+#endif
     }
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_APPLE)
   }
 
   ScheduleOverlays();
diff --git a/components/viz/service/display_embedder/buffer_queue.cc b/components/viz/service/display_embedder/buffer_queue.cc
index bf06870..bd47be74 100644
--- a/components/viz/service/display_embedder/buffer_queue.cc
+++ b/components/viz/service/display_embedder/buffer_queue.cc
@@ -81,8 +81,16 @@
     available_buffers_.push_back(std::move(displayed_buffer_));
   }
   displayed_buffer_ = std::move(in_flight_buffers_.front());
-
   in_flight_buffers_.pop_front();
+
+  if (buffers_can_be_purged_) {
+    for (auto& buffer : available_buffers_) {
+      if (SetBufferPurgeable(*buffer, true)) {
+        // Set a single available buffer to purgeable each swap.
+        break;
+      }
+    }
+  }
 }
 
 void BufferQueue::SwapBuffersSkipped(const gfx::Rect& damage) {
@@ -104,6 +112,12 @@
     return true;
   }
 
+  if (buffers_can_be_purged_) {
+    // If buffers are purgeable wait to recreate until they will be used again.
+    DestroyBuffers();
+    return true;
+  }
+
   FreeAllBuffers();
   AllocateBuffers(number_of_buffers_);
 
@@ -114,6 +128,13 @@
   if (buffers_destroyed_) {
     return;
   }
+
+  if (buffers_can_be_purged_) {
+    // If buffers are purgeable wait to recreate until they will be used again.
+    DestroyBuffers();
+    return;
+  }
+
   FreeAllBuffers();
   AllocateBuffers(number_of_buffers_);
 }
@@ -142,6 +163,16 @@
   skia_output_surface_->DestroySharedImage(buffer->mailbox);
 }
 
+bool BufferQueue::SetBufferPurgeable(AllocatedBuffer& buffer, bool purgeable) {
+  if (buffer.purgeable == purgeable) {
+    return false;
+  }
+
+  skia_output_surface_->SetSharedImagePurgeable(buffer.mailbox, purgeable);
+  buffer.purgeable = true;
+  return true;
+}
+
 void BufferQueue::AllocateBuffers(size_t n) {
   DCHECK(format_);
   const SharedImageFormat format =
@@ -224,16 +255,31 @@
   FreeAllBuffers();
 }
 
-void BufferQueue::RecreateBuffersIfDestroyed() {
-  if (!buffers_destroyed_) {
+void BufferQueue::SetBuffersPurgeable() {
+  if (buffers_can_be_purged_) {
     return;
   }
-  AllocateBuffers(number_of_buffers_);
-  buffers_destroyed_ = false;
-  base::TimeDelta elapsed = destroyed_timer_->Elapsed();
-  UMA_HISTOGRAM_TIMES("Compositing.BufferQueue.TimeUntilBuffersRecreatedMs",
-                      elapsed);
-  destroyed_timer_.reset();
+  buffers_can_be_purged_ = true;
+}
+
+void BufferQueue::RecreateBuffersIfDestroyed() {
+  if (buffers_can_be_purged_) {
+    // Mark buffers as not purgeable. It's possible they were destroyed and
+    // `available_buffers_` is empty.
+    buffers_can_be_purged_ = false;
+    for (auto& buffer : available_buffers_) {
+      SetBufferPurgeable(*buffer, false);
+    }
+  }
+
+  if (buffers_destroyed_) {
+    buffers_destroyed_ = false;
+    AllocateBuffers(number_of_buffers_);
+    base::TimeDelta elapsed = destroyed_timer_->Elapsed();
+    UMA_HISTOGRAM_TIMES("Compositing.BufferQueue.TimeUntilBuffersRecreatedMs",
+                        elapsed);
+    destroyed_timer_.reset();
+  }
 }
 
 BufferQueue::AllocatedBuffer::AllocatedBuffer(const gpu::Mailbox& mailbox,
diff --git a/components/viz/service/display_embedder/buffer_queue.h b/components/viz/service/display_embedder/buffer_queue.h
index 711393c..c027062 100644
--- a/components/viz/service/display_embedder/buffer_queue.h
+++ b/components/viz/service/display_embedder/buffer_queue.h
@@ -110,6 +110,15 @@
   // buffers.
   void DestroyBuffers();
 
+  // Indicates buffer contents can be purged, aka their contents deleted if
+  // memory is needed. For each completed swap one buffer will be marked
+  // purgeable.
+  //
+  // NOTE: This should only be used when buffers are not currently needed, eg.
+  // when delegating to system compositor, and if the platform support purgeable
+  // shared images.
+  void SetBuffersPurgeable();
+
  private:
   friend class BufferQueueTest;
   friend class BufferQueueMockedSharedImageInterfaceTest;
@@ -121,6 +130,7 @@
     AllocatedBuffer(const gpu::Mailbox& mailbox, const gfx::Rect& rect);
     ~AllocatedBuffer();
 
+    bool purgeable = false;
     gpu::Mailbox mailbox;
     gfx::Rect damage;  // This is the damage for this frame from the previous.
   };
@@ -132,6 +142,10 @@
   // Free |buffer| and destroy its shared image.
   void FreeBuffer(std::unique_ptr<AllocatedBuffer> buffer);
 
+  // Sets `buffer`s shared image as `purgeable` and returns true if the value
+  // changed.
+  bool SetBufferPurgeable(AllocatedBuffer& buffer, bool purgeable);
+
   // Unions |damage| to all allocated buffers except |current_buffer_| which
   // hasn't been displayed yet.
   void UpdateBufferDamage(const gfx::Rect& damage);
@@ -173,6 +187,9 @@
   // frames where SwapBuffers() was called without calling GetCurrentBuffer().
   base::circular_deque<std::unique_ptr<AllocatedBuffer>> in_flight_buffers_;
 
+  // When the buffers are not being used due to delegated compositing.
+  bool buffers_can_be_purged_ = false;
+
   // Whether the buffers have been destroyed and are not yet recreated. If true,
   // don't allocate buffers when you normally would. They will be recreated on
   // demand the next time GetNextBuffer() is called.
diff --git a/components/viz/service/display_embedder/buffer_queue_unittest.cc b/components/viz/service/display_embedder/buffer_queue_unittest.cc
index a778102..006937d 100644
--- a/components/viz/service/display_embedder/buffer_queue_unittest.cc
+++ b/components/viz/service/display_embedder/buffer_queue_unittest.cc
@@ -12,6 +12,7 @@
 #include <string>
 #include <utility>
 
+#include "base/test/bind.h"
 #include "build/build_config.h"
 #include "components/viz/test/fake_skia_output_surface.h"
 #include "gpu/command_buffer/common/mailbox.h"
@@ -626,4 +627,92 @@
   EXPECT_FALSE(buffer_queue_->GetLastSwappedBuffer().IsZero());
 }
 
+TEST_F(BufferQueueTest, SetPurgeable) {
+  testing::MockFunction<void(const gpu::Mailbox&, bool)> mock;
+  skia_output_surface_->SetSharedImagePurgeableCallback(
+      base::BindLambdaForTesting(mock.AsStdFunction()));
+
+  EXPECT_TRUE(buffer_queue_->Reshape(screen_size, kBufferQueueColorSpace,
+                                     kBufferQueueFormat));
+  auto mb1 = SendDamagedFrame(small_damage);
+  auto mb2 = SendDamagedFrame(small_damage);
+  auto mb3 = SendDamagedFrame(small_damage);
+  EXPECT_EQ(buffer_queue_->GetLastSwappedBuffer(), mb3);
+
+  // Queue up `mb1` and `mb2` so they are in flight. `mb3` is still the last
+  // swapped buffer.
+  EXPECT_EQ(buffer_queue_->GetCurrentBuffer(), mb1);
+  buffer_queue_->SwapBuffers(small_damage);
+  EXPECT_EQ(buffer_queue_->GetCurrentBuffer(), mb2);
+  buffer_queue_->SwapBuffers(small_damage);
+  EXPECT_EQ(buffer_queue_->GetLastSwappedBuffer(), mb3);
+
+  // Set buffers as purgeable.
+  buffer_queue_->SetBuffersPurgeable();
+
+  // When the next swap finishes `mb3` is available and gets marked purgeable.
+  EXPECT_CALL(mock, Call(mb3, true));
+  buffer_queue_->SwapBuffersComplete();
+  EXPECT_EQ(buffer_queue_->GetLastSwappedBuffer(), mb1);
+
+  // When the next swap finishes `mb1` is available and gets marked purgeable.
+  EXPECT_CALL(mock, Call(mb1, true));
+  buffer_queue_->SwapBuffersComplete();
+  EXPECT_EQ(buffer_queue_->GetLastSwappedBuffer(), mb2);
+
+  // `mb2` is last swapped buffer now and there are no pending swaps. Push an
+  // empty swap and complete that so `mb2` is available.
+  EXPECT_CALL(mock, Call(mb2, true));
+  buffer_queue_->SwapBuffers(small_damage);
+  buffer_queue_->SwapBuffersComplete();
+
+  // The next non-delegated draw will get a primary plane buffer. This will
+  // cause all three buffers to be marked as not purgeable anymore.
+  EXPECT_CALL(mock, Call(mb3, false));
+  EXPECT_CALL(mock, Call(mb1, false));
+  EXPECT_CALL(mock, Call(mb2, false));
+  EXPECT_EQ(buffer_queue_->GetCurrentBuffer(), mb3);
+
+  // Reset callback since it points to stack allocated mock.
+  skia_output_surface_->SetSharedImagePurgeableCallback({});
+}
+
+TEST_F(BufferQueueTest, SetPurgeableThenReshape) {
+  testing::MockFunction<void(const gpu::Mailbox&, bool)> mock;
+  skia_output_surface_->SetSharedImagePurgeableCallback(
+      base::BindLambdaForTesting(mock.AsStdFunction()));
+
+  // This test will reshape before any buffers can be marked as purgeable.
+  EXPECT_CALL(mock, Call(testing::_, testing::_)).Times(0);
+
+  EXPECT_TRUE(buffer_queue_->Reshape(screen_size, kBufferQueueColorSpace,
+                                     kBufferQueueFormat));
+
+  // Swap three buffers. First buffer swap completes so there is one displayed
+  // buffer and two in flight buffers.
+  buffer_queue_->GetCurrentBuffer();
+  buffer_queue_->SwapBuffers(small_damage);
+  buffer_queue_->GetCurrentBuffer();
+  buffer_queue_->SwapBuffers(small_damage);
+  buffer_queue_->SwapBuffersComplete();
+  buffer_queue_->GetCurrentBuffer();
+  buffer_queue_->SwapBuffers(small_damage);
+  EXPECT_FALSE(buffer_queue_->GetLastSwappedBuffer().IsZero());
+
+  // Set the buffers as purgeable before the next swap buffers complete and then
+  // immediately reshape. The reshape will cause buffers to be deleted but not
+  // recreated at the new size until they will be used.
+  buffer_queue_->SetBuffersPurgeable();
+  EXPECT_TRUE(buffer_queue_->Reshape(gfx::Size(1, 1), kBufferQueueColorSpace,
+                                     kBufferQueueFormat));
+
+  // Complete the last two swaps. Since the reshape deleted all the buffers
+  // they will not be marked as purgeable.
+  buffer_queue_->SwapBuffersComplete();
+  buffer_queue_->SwapBuffersComplete();
+
+  // Reset callback since it points to stack allocated mock.
+  skia_output_surface_->SetSharedImagePurgeableCallback({});
+}
+
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 062fd58..2ab8521 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -1731,6 +1731,15 @@
                  /*need_framebuffer=*/false);
 }
 
+void SkiaOutputSurfaceImpl::SetSharedImagePurgeable(const gpu::Mailbox& mailbox,
+                                                    bool purgeable) {
+  auto task =
+      base::BindOnce(&SkiaOutputSurfaceImplOnGpu::SetSharedImagePurgeable,
+                     base::Unretained(impl_on_gpu_.get()), mailbox, purgeable);
+  EnqueueGpuTask(std::move(task), {}, /*make_current=*/false,
+                 /*need_framebuffer=*/false);
+}
+
 bool SkiaOutputSurfaceImpl::SupportsBGRA() const {
   if (graphite_recorder_) {
     // TODO(crbug.com/1451789): Implement properly for Graphite.
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h
index ecccf51..dfbc85c7 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -175,6 +175,8 @@
       const SkColor4f& color,
       const gfx::ColorSpace& color_space) override;
   void DestroySharedImage(const gpu::Mailbox& mailbox) override;
+  void SetSharedImagePurgeable(const gpu::Mailbox& mailbox,
+                               bool purgeable) override;
   bool SupportsBGRA() const override;
 
   // ExternalUseClient implementation:
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index 126d3f4..d9fe792 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -2735,6 +2735,12 @@
   solid_color_images_.erase(mailbox);
 }
 
+void SkiaOutputSurfaceImplOnGpu::SetSharedImagePurgeable(
+    const gpu::Mailbox& mailbox,
+    bool purgeable) {
+  shared_image_factory_->SetSharedImagePurgeable(mailbox, purgeable);
+}
+
 gpu::SkiaImageRepresentation* SkiaOutputSurfaceImplOnGpu::GetSkiaRepresentation(
     gpu::Mailbox mailbox) {
   auto it = skia_representations_.find(mailbox);
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index 92b11e18..d93b29e 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -281,6 +281,7 @@
                                    const SkColor4f& color,
                                    const gfx::ColorSpace& color_space);
   void DestroySharedImage(gpu::Mailbox mailbox);
+  void SetSharedImagePurgeable(const gpu::Mailbox& mailbox, bool purgeable);
 
   // Called on the viz thread!
   base::ScopedClosureRunner GetCacheBackBufferCb();
diff --git a/components/viz/test/fake_skia_output_surface.cc b/components/viz/test/fake_skia_output_surface.cc
index 04686f64..22beb781 100644
--- a/components/viz/test/fake_skia_output_surface.cc
+++ b/components/viz/test/fake_skia_output_surface.cc
@@ -436,6 +436,13 @@
   return gpu::Mailbox::GenerateForSharedImage();
 }
 
+void FakeSkiaOutputSurface::SetSharedImagePurgeable(const gpu::Mailbox& mailbox,
+                                                    bool purgeable) {
+  if (set_purgeable_callback_) {
+    set_purgeable_callback_.Run(mailbox, purgeable);
+  }
+}
+
 bool FakeSkiaOutputSurface::SupportsBGRA() const {
   return true;
 }
diff --git a/components/viz/test/fake_skia_output_surface.h b/components/viz/test/fake_skia_output_surface.h
index 566e8a3c..c7bc539 100644
--- a/components/viz/test/fake_skia_output_surface.h
+++ b/components/viz/test/fake_skia_output_surface.h
@@ -24,6 +24,9 @@
 namespace viz {
 
 class FakeSkiaOutputSurface : public SkiaOutputSurface {
+  using SharedImagePurgeableCallback =
+      base::RepeatingCallback<void(const gpu::Mailbox&, bool)>;
+
  public:
   static std::unique_ptr<FakeSkiaOutputSurface> Create3d() {
     auto provider = TestContextProvider::Create();
@@ -118,6 +121,8 @@
       const SkColor4f& color,
       const gfx::ColorSpace& color_space) override;
   void DestroySharedImage(const gpu::Mailbox& mailbox) override {}
+  void SetSharedImagePurgeable(const gpu::Mailbox& mailbox,
+                               bool purgeable) override;
   bool SupportsBGRA() const override;
 
   // ExternalUseClient implementation:
@@ -134,6 +139,10 @@
 
   gpu::SharedImageInterface* GetSharedImageInterface();
 
+  void SetSharedImagePurgeableCallback(SharedImagePurgeableCallback callback) {
+    set_purgeable_callback_ = std::move(callback);
+  }
+
   // If set true, callbacks triggering will be in a reverse order as SignalQuery
   // calls.
   void SetOutOfOrderCallbacks(bool out_of_order_callbacks);
@@ -201,6 +210,8 @@
   // correctly routed towards gpu main when the platform supports delegated ink.
   bool delegated_ink_renderer_receiver_arrived_ = false;
 
+  SharedImagePurgeableCallback set_purgeable_callback_;
+
   THREAD_CHECKER(thread_checker_);
 
   base::WeakPtrFactory<FakeSkiaOutputSurface> weak_ptr_factory_{this};
diff --git a/components/viz/test/test_context_provider.cc b/components/viz/test/test_context_provider.cc
index c9362f6..70ff478 100644
--- a/components/viz/test/test_context_provider.cc
+++ b/components/viz/test/test_context_provider.cc
@@ -198,12 +198,25 @@
                                             base::StringPiece debug_label,
                                             gpu::SurfaceHandle surface_handle,
                                             gfx::BufferUsage buffer_usage) {
+  if (fail_shared_image_creation_with_buffer_usage_) {
+    return nullptr;
+  }
+
   // Create a ClientSharedImage with a GMB.
   auto client_shared_image =
       CreateSharedImage(format, size, color_space, surface_origin, alpha_type,
                         usage, std::move(debug_label), surface_handle);
   CHECK(client_shared_image);
   auto mailbox = client_shared_image->mailbox();
+
+  if (test_gmb_manager_) {
+    auto gpu_memory_buffer = test_gmb_manager_->CreateGpuMemoryBuffer(
+        size, SinglePlaneSharedImageFormatToBufferFormat(format), buffer_usage,
+        surface_handle, nullptr);
+    return gpu::ClientSharedImage::CreateForTesting(
+        mailbox, std::move(gpu_memory_buffer));
+  }
+
   auto gmb_handle = CreateGMBHandle(format, size, buffer_usage);
 
   return base::MakeRefCounted<gpu::ClientSharedImage>(
diff --git a/components/viz/test/test_context_provider.h b/components/viz/test/test_context_provider.h
index 926d7c3..7056c5ae 100644
--- a/components/viz/test/test_context_provider.h
+++ b/components/viz/test/test_context_provider.h
@@ -22,6 +22,7 @@
 #include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/gpu/raster_context_provider.h"
 #include "components/viz/test/test_context_support.h"
+#include "components/viz/test/test_gpu_memory_buffer_manager.h"
 #include "gpu/command_buffer/client/gles2_interface_stub.h"
 #include "gpu/command_buffer/client/shared_image_interface.h"
 #include "gpu/command_buffer/common/shared_image_capabilities.h"
@@ -161,6 +162,14 @@
   const gpu::SharedImageCapabilities& GetCapabilities() override;
   void SetCapabilities(const gpu::SharedImageCapabilities& caps);
 
+  void SetFailSharedImageCreationWithBufferUsage(bool value) {
+    fail_shared_image_creation_with_buffer_usage_ = value;
+  }
+
+  void UseTestGMBInSharedImageCreationWithBufferUsage() {
+    test_gmb_manager_ = std::make_unique<TestGpuMemoryBufferManager>();
+  }
+
  private:
   mutable base::Lock lock_;
 
@@ -171,6 +180,11 @@
   base::flat_set<gpu::Mailbox> shared_images_;
 
   gpu::SharedImageCapabilities shared_image_capabilities_;
+  bool fail_shared_image_creation_with_buffer_usage_ = false;
+
+  // If non-null, this will be used to back mappable SharedImages with test
+  // GpuMemoryBuffers.
+  std::unique_ptr<TestGpuMemoryBufferManager> test_gmb_manager_;
 };
 
 class TestContextProvider
diff --git a/components/viz/test/test_types.cc b/components/viz/test/test_types.cc
index d2c38cf..521780d 100644
--- a/components/viz/test/test_types.cc
+++ b/components/viz/test/test_types.cc
@@ -27,11 +27,11 @@
   }
 }
 
-std::vector<RendererType> GetRendererTypes(bool include_software,
-                                           bool skia_only) {
+std::vector<RendererType> GetRendererTypes(bool include_software) {
   std::vector<RendererType> types;
-  if (include_software && !skia_only)
+  if (include_software) {
     types.push_back(RendererType::kSoftware);
+  }
 #if BUILDFLAG(ENABLE_GL_BACKEND_TESTS)
   types.push_back(RendererType::kSkiaGL);
 #endif  // BUILDFLAG(ENABLE_GL_BACKEND_TESTS)
@@ -54,15 +54,11 @@
 }
 
 std::vector<RendererType> GetRendererTypes() {
-  return GetRendererTypes(true, false);
+  return GetRendererTypes(true);
 }
 
 std::vector<RendererType> GetGpuRendererTypes() {
-  return GetRendererTypes(false, false);
-}
-
-std::vector<RendererType> GetRendererTypesSkiaOnly() {
-  return GetRendererTypes(false, true);
+  return GetRendererTypes(false);
 }
 
 }  // namespace viz
diff --git a/components/viz/test/test_types.h b/components/viz/test/test_types.h
index 1bf7f49..948dedc 100644
--- a/components/viz/test/test_types.h
+++ b/components/viz/test/test_types.h
@@ -27,9 +27,6 @@
 // applicable to the platform.
 std::vector<RendererType> GetGpuRendererTypes();
 
-// Returns a list containing all Skia RendererTypes applicable to the platform.
-std::vector<RendererType> GetRendererTypesSkiaOnly();
-
 }  // namespace viz
 
 #endif  // COMPONENTS_VIZ_TEST_TEST_TYPES_H_
diff --git a/components/webdata/common/web_database_migration_unittest.cc b/components/webdata/common/web_database_migration_unittest.cc
index d95244e..b2e77fa 100644
--- a/components/webdata/common/web_database_migration_unittest.cc
+++ b/components/webdata/common/web_database_migration_unittest.cc
@@ -17,6 +17,7 @@
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/geo/autofill_country.h"
+#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
 #include "components/autofill/core/browser/webdata/autocomplete_table.h"
 #include "components/autofill/core/browser/webdata/autofill_change.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
@@ -27,12 +28,6 @@
 #include "sql/statement.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using autofill::AutofillProfile;
-using autofill::AutofillTable;
-using autofill::CreditCard;
-using base::ASCIIToUTF16;
-using base::Time;
-
 namespace {
 
 // To make the comparison with golden files less whitespace sensitive:
@@ -77,12 +72,14 @@
   // Load the database via the WebDatabase class and migrate the database to
   // the current version.
   void DoMigration() {
+    autofill::AddressAutofillTable address_autofill_table;
     autofill::AutocompleteTable autocomplete_table;
-    AutofillTable autofill_table;
+    autofill::AutofillTable autofill_table;
     KeywordTable keyword_table;
     TokenServiceTable token_service_table;
 
     WebDatabase db;
+    db.AddTable(&address_autofill_table);
     db.AddTable(&autocomplete_table);
     db.AddTable(&autofill_table);
     db.AddTable(&keyword_table);
@@ -1058,21 +1055,22 @@
     EXPECT_TRUE(connection.DoesTableExist("local_addresses"));
     EXPECT_TRUE(connection.DoesTableExist("local_addresses_type_tokens"));
 
-    // Expect to find the profiles in the local_addresses tables. AutofillTable
-    // will read from them.
-    AutofillTable table;
+    // Expect to find the profiles in the local_addresses tables.
+    // AddressAutofillTable will read from them.
+    autofill::AddressAutofillTable table;
     table.Init(&connection, /*meta_table=*/nullptr);
-    std::unique_ptr<AutofillProfile> profile =
-        table.GetAutofillProfile("00000000-0000-0000-0000-000000000000",
-                                 AutofillProfile::Source::kLocalOrSyncable);
+    std::unique_ptr<autofill::AutofillProfile> profile =
+        table.GetAutofillProfile(
+            "00000000-0000-0000-0000-000000000000",
+            autofill::AutofillProfile::Source::kLocalOrSyncable);
     ASSERT_TRUE(profile);
-    EXPECT_EQ(profile->modification_date(), Time::FromTimeT(123));
+    EXPECT_EQ(profile->modification_date(), base::Time::FromTimeT(123));
     EXPECT_EQ(profile->GetRawInfo(autofill::NAME_FULL), u"full name");
     EXPECT_EQ(profile->GetRawInfo(autofill::ADDRESS_HOME_ZIP), u"4567");
 
-    EXPECT_TRUE(
-        table.GetAutofillProfile("00000000-0000-0000-0000-000000000001",
-                                 AutofillProfile::Source::kLocalOrSyncable));
+    EXPECT_TRUE(table.GetAutofillProfile(
+        "00000000-0000-0000-0000-000000000001",
+        autofill::AutofillProfile::Source::kLocalOrSyncable));
   }
 }
 
diff --git a/components/webdata_services/web_data_service_wrapper.cc b/components/webdata_services/web_data_service_wrapper.cc
index 073eb9a..e9dcf1bd 100644
--- a/components/webdata_services/web_data_service_wrapper.cc
+++ b/components/webdata_services/web_data_service_wrapper.cc
@@ -13,6 +13,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "build/build_config.h"
+#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
 #include "components/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
 #include "components/autofill/core/browser/webdata/autocomplete_table.h"
 #include "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h"
@@ -117,6 +118,8 @@
 
   // All tables objects that participate in managing the database must
   // be added here.
+  profile_database_->AddTable(
+      std::make_unique<autofill::AddressAutofillTable>());
   profile_database_->AddTable(std::make_unique<autofill::AutocompleteTable>());
   profile_database_->AddTable(std::make_unique<autofill::AutofillTable>());
   profile_database_->AddTable(std::make_unique<KeywordTable>());
diff --git a/content/browser/back_forward_cache_features_browsertest.cc b/content/browser/back_forward_cache_features_browsertest.cc
index 70e4b78..85fd7cc0 100644
--- a/content/browser/back_forward_cache_features_browsertest.cc
+++ b/content/browser/back_forward_cache_features_browsertest.cc
@@ -1130,39 +1130,28 @@
   ExpectRestored(FROM_HERE);
 }
 
-// The bool parameter is used for switching
-// `kEnableBackForwardCacheForPagesWithMediaDevicesDispatcherHost`.
-class BackForwardCacheForPagesWithMediaDevicesDispatcherHostTest
-    : public BackForwardCacheBrowserTest,
-      public testing::WithParamInterface<bool> {
+// The parameter is used for switching
+// `kAllowBFCacheForClosedMediaStreamTrack`.
+class BackForwardCacheMediaTest : public BackForwardCacheBrowserTest,
+                                  public testing::WithParamInterface<bool> {
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    if (IsBFCacheForPagesWithMediaDevicesDispatcherHostEnabled()) {
+    if (IsAllowBFCacheWhenClosedMediaStreamTrackEnabled()) {
       EnableFeatureAndSetParams(
-          features::
-              kEnableBackForwardCacheForPagesWithMediaDevicesDispatcherHost,
-          "", "");
+          blink::features::kAllowBFCacheWhenClosedMediaStreamTrack, "", "");
     } else {
-      DisableFeature(
-          features::
-              kEnableBackForwardCacheForPagesWithMediaDevicesDispatcherHost);
+      DisableFeature(blink::features::kAllowBFCacheWhenClosedMediaStreamTrack);
     }
     BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
   }
 
-  bool IsBFCacheForPagesWithMediaDevicesDispatcherHostEnabled() {
-    return GetParam();
-  }
+  bool IsAllowBFCacheWhenClosedMediaStreamTrackEnabled() { return GetParam(); }
 };
 
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    BackForwardCacheForPagesWithMediaDevicesDispatcherHostTest,
-    testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, BackForwardCacheMediaTest, testing::Bool());
 
-IN_PROC_BROWSER_TEST_P(
-    BackForwardCacheForPagesWithMediaDevicesDispatcherHostTest,
-    DoesNotCacheIfRecordingAudio) {
+IN_PROC_BROWSER_TEST_P(BackForwardCacheMediaTest,
+                       DoesNotCacheIfRecordingAudio) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   BackForwardCacheDisabledTester tester;
@@ -1183,7 +1172,8 @@
   RenderFrameDeletedObserver deleted(current_frame_host());
 
   // 2) Navigate away.
-  shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html"));
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
 
   // The page was still recording audio when we navigated away, so it shouldn't
   // have been cached.
@@ -1192,9 +1182,13 @@
   // 3) Go back.
   ASSERT_TRUE(HistoryGoBack(web_contents()));
 
-  if (IsBFCacheForPagesWithMediaDevicesDispatcherHostEnabled()) {
-    ExpectNotRestored({NotRestoredReason::kWasGrantedMediaAccess}, {}, {}, {},
-                      {}, FROM_HERE);
+  if (IsAllowBFCacheWhenClosedMediaStreamTrackEnabled()) {
+    // When the flag is enabled, a Media Stream Track that's in the live state
+    // will block BFCache.
+    ExpectNotRestored(
+        {NotRestoredReason::kBlocklistedFeatures},
+        {blink::scheduler::WebSchedulerTrackedFeature::kLiveMediaStreamTrack},
+        {}, {}, {}, FROM_HERE);
   } else {
     // Note that the reason for kWasGrantedMediaAccess occurs after
     // MediaDevicesDispatcherHost is called, hence, both are reasons for the
@@ -1207,9 +1201,8 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_P(
-    BackForwardCacheForPagesWithMediaDevicesDispatcherHostTest,
-    DoesNotCacheIfSubframeRecordingAudio) {
+IN_PROC_BROWSER_TEST_P(BackForwardCacheMediaTest,
+                       DoesNotCacheIfSubframeRecordingAudio) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   BackForwardCacheDisabledTester tester;
@@ -1231,7 +1224,8 @@
   RenderFrameDeletedObserver deleted(current_frame_host());
 
   // 2) Navigate away.
-  shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html"));
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
 
   // The page was still recording audio when we navigated away, so it shouldn't
   // have been cached.
@@ -1240,9 +1234,13 @@
   // 3) Go back.
   ASSERT_TRUE(HistoryGoBack(web_contents()));
 
-  if (IsBFCacheForPagesWithMediaDevicesDispatcherHostEnabled()) {
-    ExpectNotRestored({NotRestoredReason::kWasGrantedMediaAccess}, {}, {}, {},
-                      {}, FROM_HERE);
+  if (IsAllowBFCacheWhenClosedMediaStreamTrackEnabled()) {
+    // When the flag is enabled, a Media Stream Track that's in the live state
+    // blocks BFCache.
+    ExpectNotRestored(
+        {NotRestoredReason::kBlocklistedFeatures},
+        {blink::scheduler::WebSchedulerTrackedFeature::kLiveMediaStreamTrack},
+        {}, {}, {}, FROM_HERE);
   } else {
     // Note that the reason for kWasGrantedMediaAccess occurs after
     // MediaDevicesDispatcherHost is called, hence, both are reasons for the
@@ -1256,7 +1254,7 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    BackForwardCacheForPagesWithMediaDevicesDispatcherHostTest,
+    BackForwardCacheMediaTest,
     DoesNotCacheIfMediaDeviceSubscribedButDoesCacheIfFlagEnabled) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -1285,9 +1283,9 @@
   // 3) Go back.
   ASSERT_TRUE(HistoryGoBack(web_contents()));
 
-  if (IsBFCacheForPagesWithMediaDevicesDispatcherHostEnabled()) {
-    // The page should be BFCached when the flag is enabled because
-    // the only blocker when it's disabled is `kMediaDevicesDispatcherHost`.
+  if (IsAllowBFCacheWhenClosedMediaStreamTrackEnabled()) {
+    // When the flag is enabled, ended Media Stream Track does not block
+    // BFCache.
     ExpectRestored(FROM_HERE);
   } else {
     // The page was subscribed to media devices when we navigated away, so it
@@ -1304,7 +1302,7 @@
 // Checks that the page is not restored from BFCache when it calls
 // mediaDevice.enumerateDevices() unless the flag is enabled.
 IN_PROC_BROWSER_TEST_P(
-    BackForwardCacheForPagesWithMediaDevicesDispatcherHostTest,
+    BackForwardCacheMediaTest,
     DoesNotCacheIfDevicesEnumeratedButDoesCacheIfFlagEnabled) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -1323,8 +1321,9 @@
   EXPECT_TRUE(NavigateToURL(
       shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
 
-  if (IsBFCacheForPagesWithMediaDevicesDispatcherHostEnabled()) {
-    // 3) Go back. The page should be cached when the flag is enabled.
+  if (IsAllowBFCacheWhenClosedMediaStreamTrackEnabled()) {
+    // 3) Go back. When the flag is enabled, MediaDevicesDispatcherHost does not
+    // block BFCache.
     ASSERT_TRUE(HistoryGoBack(web_contents()));
     ExpectRestored(FROM_HERE);
   } else {
@@ -1341,13 +1340,12 @@
 }
 
 // Checks that the page is not restored from BFCache when it calls
-// mediaDevice.getDisplayMedia().
-// Since mediaDevice.getDisplayMedia() is not supported in Android, this test
+// mediaDevice.getDisplayMedia() and still has live MediaStreamTrack.
+// Since mediaDevice.getDisplayMedia() is not supported in Android, the tests
 // can't run on the OS.
 #if !BUILDFLAG(IS_ANDROID)
-IN_PROC_BROWSER_TEST_P(
-    BackForwardCacheForPagesWithMediaDevicesDispatcherHostTest,
-    DoesNotCacheIfDisplayMediaAccessGranted) {
+IN_PROC_BROWSER_TEST_P(BackForwardCacheMediaTest,
+                       DoesNotCacheIfDisplayMediaAccessGranted) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // 1) Navigate to an empty page.
@@ -1358,26 +1356,78 @@
 
   // Request for video and audio display permission.
   EXPECT_EQ("success", EvalJs(rfh.get(), R"(
-    navigator.mediaDevices.getDisplayMedia({audio: true, video: true})
-        .then(() => { return "success" })
+    new Promise((resolve) => {
+      navigator.mediaDevices.getDisplayMedia({audio: true, video: true})
+        .then(() => { resolve("success"); })
+    });
   )"));
 
   // 2) Navigate away.
   EXPECT_TRUE(NavigateToURL(
       shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
 
-  // The page where display media permission is requested shouldn't be cached.
   ASSERT_TRUE(rfh.WaitUntilRenderFrameDeleted());
 
-  if (IsBFCacheForPagesWithMediaDevicesDispatcherHostEnabled()) {
+  if (IsAllowBFCacheWhenClosedMediaStreamTrackEnabled()) {
+    // 3) Go back. When the flag is enabled, a Media Stream Track that's in the
+    // live state blocks BFCache.
+    ASSERT_TRUE(HistoryGoBack(web_contents()));
+    ExpectNotRestored(
+        {NotRestoredReason::kBlocklistedFeatures},
+        {blink::scheduler::WebSchedulerTrackedFeature::kLiveMediaStreamTrack},
+        {}, {}, {}, FROM_HERE);
+  } else {
     // 3) Go back.
     ASSERT_TRUE(HistoryGoBack(web_contents()));
-    ExpectNotRestored({NotRestoredReason::kWasGrantedMediaAccess}, {}, {}, {},
-                      {}, FROM_HERE);
+    auto reason = BackForwardCacheDisable::DisabledReason(
+        BackForwardCacheDisable::DisabledReasonId::kMediaDevicesDispatcherHost);
+    ExpectNotRestored({NotRestoredReason::kWasGrantedMediaAccess,
+                       NotRestoredReason::kDisableForRenderFrameHostCalled},
+                      {}, {}, {reason}, {}, FROM_HERE);
+  }
+}
+
+// Checks that the page is successfully restored from BFCache after stopping the
+// media stream track that was caused by getDisplayMedia(). However, the page
+// should not be stored in BFCache if the flag is enabled.
+IN_PROC_BROWSER_TEST_P(
+    BackForwardCacheMediaTest,
+    DoesCacheIfMediaStreamTrackUsingGetDisplayMediaEndedButDoesNotWithoutFlags) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // 1) Navigate to an empty page.
+  GURL url(embedded_test_server()->GetURL("/title1.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+
+  RenderFrameHostWrapper rfh(current_frame_host());
+
+  // Request for video and audio display permission, and stop it.
+  EXPECT_EQ("success", EvalJs(rfh.get(), R"(
+  new Promise((resolve) => {
+    navigator.mediaDevices.getDisplayMedia({ audio: true })
+      .then((mediaStream) => {
+        mediaStream.getTracks().forEach((track) => track.stop());
+        resolve("success");
+      })
+      .catch((error) => {
+        resolve("error");
+      });
+  });
+  )"));
+
+  // 2) Navigate away.
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
+
+  if (IsAllowBFCacheWhenClosedMediaStreamTrackEnabled()) {
+    // 3) Go back. When flag is enabled, an ended Media Stream Track doesn't
+    // block BFCache.
+    ASSERT_TRUE(HistoryGoBack(web_contents()));
+    ExpectRestored(FROM_HERE);
   } else {
-    // 3) Go back. kMediaDevicesDispatcherHost should be one of the blockers for
-    // BFCache when the flag is disabled.
-    // Also, kWasGrantedMediaAccess occurs after kMediaDevicesDispatcherHost.
+    ASSERT_TRUE(rfh.WaitUntilRenderFrameDeleted());
+
+    // 3) Go back.
     ASSERT_TRUE(HistoryGoBack(web_contents()));
     auto reason = BackForwardCacheDisable::DisabledReason(
         BackForwardCacheDisable::DisabledReasonId::kMediaDevicesDispatcherHost);
@@ -4387,6 +4437,8 @@
 
 // TODO(https://crbug.com/1213145): The test is consistently failing on some Mac
 // bots.
+// This test uses Media Stream Track, so the test class is
+// `BackForwardCacheMediaTest`.
 #if BUILDFLAG(IS_MAC)
 #define MAYBE_NonTrivialRTCPeerConnectionNotCached \
   DISABLED_NonTrivialRTCPeerConnectionNotCached
@@ -4394,7 +4446,7 @@
 #define MAYBE_NonTrivialRTCPeerConnectionNotCached \
   NonTrivialRTCPeerConnectionNotCached
 #endif
-IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+IN_PROC_BROWSER_TEST_P(BackForwardCacheMediaTest,
                        MAYBE_NonTrivialRTCPeerConnectionNotCached) {
   ASSERT_TRUE(CreateHttpsServer()->Start());
 
@@ -4459,9 +4511,19 @@
 
   // 3) Go back.
   ASSERT_TRUE(HistoryGoBack(web_contents()));
-  ExpectNotRestored({NotRestoredReason::kBlocklistedFeatures},
-                    {blink::scheduler::WebSchedulerTrackedFeature::kWebRTC}, {},
-                    {}, {}, FROM_HERE);
+
+  if (IsAllowBFCacheWhenClosedMediaStreamTrackEnabled()) {
+    // When the flag is enabled, a live Media Stream Track blocks BFCache.
+    ExpectNotRestored(
+        {NotRestoredReason::kBlocklistedFeatures},
+        {blink::scheduler::WebSchedulerTrackedFeature::kWebRTC,
+         blink::scheduler::WebSchedulerTrackedFeature::kLiveMediaStreamTrack},
+        {}, {}, {}, FROM_HERE);
+  } else {
+    ExpectNotRestored({NotRestoredReason::kBlocklistedFeatures},
+                      {blink::scheduler::WebSchedulerTrackedFeature::kWebRTC},
+                      {}, {}, {}, FROM_HERE);
+  }
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index 2de2f14..a1d569b 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -1626,6 +1626,8 @@
       NOTREACHED_NORETURN();
     case WebSchedulerTrackedFeature::kSmartCard:
       return Page::BackForwardCacheNotRestoredReasonEnum::SmartCard;
+    case WebSchedulerTrackedFeature::kLiveMediaStreamTrack:
+      return Page::BackForwardCacheNotRestoredReasonEnum::LiveMediaStreamTrack;
   }
 }
 
@@ -1826,6 +1828,7 @@
     case WebSchedulerTrackedFeature::kWebTransport:
     case WebSchedulerTrackedFeature::kIndexedDBEvent:
     case WebSchedulerTrackedFeature::kSmartCard:
+    case WebSchedulerTrackedFeature::kLiveMediaStreamTrack:
       return Page::BackForwardCacheNotRestoredReasonTypeEnum::PageSupportNeeded;
     case WebSchedulerTrackedFeature::kPortal:
     case WebSchedulerTrackedFeature::kWebNfc:
diff --git a/content/browser/network/quic_connection_migration_android_browsertest.cc b/content/browser/network/quic_connection_migration_android_browsertest.cc
index de3f0c82..e41f087 100644
--- a/content/browser/network/quic_connection_migration_android_browsertest.cc
+++ b/content/browser/network/quic_connection_migration_android_browsertest.cc
@@ -90,7 +90,8 @@
                                     "QUIC/Enabled");
     command_line->AppendSwitchASCII(
         switches::kForceFieldTrialParams,
-        "QUIC.Enabled:migrate_sessions_on_network_change_v2/true");
+        "QUIC.Enabled:migrate_sessions_on_network_change_v2/true/"
+        "retry_without_alt_svc_on_quic_errors/false");
     mock_cert_verifier_.SetUpCommandLine(command_line);
 
     ASSERT_TRUE(net::QuicSimpleTestServer::Start());
@@ -202,9 +203,11 @@
   net::android::SetWifiEnabledForTesting(false);
   WaitForNetworkChange();
 
+  // The subresource fetch should fail because the server closed the connection
+  // and retry is disabled.
   EvalJsResult result = ResolveDelayedResponse();
-  EXPECT_TRUE(result.error.empty())
-      << "Delayed response got error: '" << result.error << "'";
+  EXPECT_FALSE(result.error.empty());
+  EXPECT_EQ(histograms.GetTotalSum("Net.QuicProtocolError.RetryStatus"), 0);
 
   FetchHistogramsFromChildProcesses();
   ASSERT_EQ(histograms.GetBucketCount(
diff --git a/content/browser/renderer_host/back_forward_cache_impl.cc b/content/browser/renderer_host/back_forward_cache_impl.cc
index cdecc34..4e680d0 100644
--- a/content/browser/renderer_host/back_forward_cache_impl.cc
+++ b/content/browser/renderer_host/back_forward_cache_impl.cc
@@ -49,6 +49,7 @@
 #include "net/http/http_status_code.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
 #include "third_party/blink/public/mojom/frame/sudden_termination_disabler_type.mojom-shared.h"
 #if BUILDFLAG(IS_ANDROID)
@@ -184,6 +185,7 @@
           WebSchedulerTrackedFeature::kIndexedDBEvent,
           WebSchedulerTrackedFeature::kKeyboardLock,
           WebSchedulerTrackedFeature::kKeepaliveRequest,
+          WebSchedulerTrackedFeature::kLiveMediaStreamTrack,
           WebSchedulerTrackedFeature::kPaymentManager,
           WebSchedulerTrackedFeature::kPictureInPicture,
           WebSchedulerTrackedFeature::kPortal,
@@ -966,12 +968,14 @@
     PopulateStickyReasonsForDocument(
         BackForwardCacheCanStoreDocumentResult& result,
         RenderFrameHostImpl* rfh) {
-  // If the rfh has ever granted media access, prevent it from entering cache.
-  // TODO(crbug.com/989379): Consider only blocking when there's an active
-  //                         media stream.
-  if (rfh->was_granted_media_access()) {
-    result.No(
-        BackForwardCacheMetrics::NotRestoredReason::kWasGrantedMediaAccess);
+  if (!blink::features::IsAllowBFCacheWhenClosedMediaStreamTrackEnabled()) {
+    // `kWasGrantedMediaAccess` is no longer a BFCache blocker when the flag is
+    // enabled. With https://crbug.com/1502395, frames with only "live" Media
+    // Stream Track will be blocked from BFCache.
+    if (rfh->was_granted_media_access()) {
+      result.No(
+          BackForwardCacheMetrics::NotRestoredReason::kWasGrantedMediaAccess);
+    }
   }
 
   if (rfh->IsBackForwardCacheDisabled() && !ShouldIgnoreBlocklists()) {
diff --git a/content/browser/renderer_host/media/media_devices_dispatcher_host.cc b/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
index e1420156..edbb0e70 100644
--- a/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
+++ b/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
@@ -33,6 +33,7 @@
 #include "media/capture/mojom/video_capture_types.mojom.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/mediastream/media_devices.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 #include "url/origin.h"
@@ -43,13 +44,6 @@
 
 using blink::mojom::MediaDeviceType;
 
-namespace features {
-// When enabled, MediaDevicesDispatcherHost does not block back/forward cache.
-BASE_FEATURE(kEnableBackForwardCacheForPagesWithMediaDevicesDispatcherHost,
-             "EnableBackForwardCacheForPagesWithMediaDevicesDispatcherHost",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-}  // namespace features
-
 namespace content {
 
 namespace {
@@ -101,9 +95,8 @@
                        if (!render_frame_host)
                          return;
 
-                       if (!base::FeatureList::IsEnabled(
-                               features::
-                                   kEnableBackForwardCacheForPagesWithMediaDevicesDispatcherHost)) {
+                       if (!blink::features::
+                               IsAllowBFCacheWhenClosedMediaStreamTrackEnabled()) {
                          BackForwardCache::DisableForRenderFrameHost(
                              render_frame_host,
                              BackForwardCacheDisable::DisabledReason(
diff --git a/content/browser/renderer_host/media/media_devices_dispatcher_host.h b/content/browser/renderer_host/media/media_devices_dispatcher_host.h
index 32b70ed..aefaea82 100644
--- a/content/browser/renderer_host/media/media_devices_dispatcher_host.h
+++ b/content/browser/renderer_host/media/media_devices_dispatcher_host.h
@@ -24,11 +24,6 @@
 #include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
 #include "url/origin.h"
 
-namespace features {
-CONTENT_EXPORT BASE_DECLARE_FEATURE(
-    kEnableBackForwardCacheForPagesWithMediaDevicesDispatcherHost);
-}  // namespace features
-
 namespace content {
 
 class MediaStreamManager;
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index a32f344..a279e78a 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -4517,9 +4517,10 @@
 // frame and mouse up is on OOF iframe, the mouse up event is delivered to the
 // main frame as well to clear cached mouse states including autoscroll
 // selection state.
+// TODO(crbug.com/1512574): This test was detected flaky.
 IN_PROC_BROWSER_TEST_F(
     WebContentsImplBrowserTest,
-    MouseUpInOOPIframeShouldCancelMainFrameAutoscrollSelection) {
+    DISABLED_MouseUpInOOPIframeShouldCancelMainFrameAutoscrollSelection) {
   ASSERT_TRUE(embedded_test_server()->Start());
   WebContentsImpl* web_contents =
       static_cast<WebContentsImpl*>(shell()->web_contents());
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 67ef70a..e74c09e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -442,10 +442,10 @@
 # Flaky tests
 crbug.com/1489477 [ fuchsia fuchsia-board-astro ] conformance/more/conformance/quickCheck* [ Failure ]
 crbug.com/1489477 [ fuchsia web-engine-shell ] WebglExtension_EXT_float_blend [ Failure ]
-crbug.com/1489477 [ fuchsia ] conformance/buffers/buffer-data-array-buffer-delete.html [ Failure ]
-crbug.com/1489477 [ fuchsia ] conformance/buffers/element-array-buffer-delete-recreate.html [ Failure ]
+crbug.com/1489477 [ fuchsia fuchsia-board-astro ] conformance/buffers/element-array-buffer-delete-recreate.html [ Failure ]
 crbug.com/1489477 [ fuchsia ] conformance/state/gl-object-get-calls.html [ Failure ]
-crbug.com/1489477 [ fuchsia ] conformance/textures/canvas/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
+crbug.com/1489477 [ fuchsia fuchsia-board-nelson ] conformance/textures/canvas/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
+crbug.com/1489477 [ fuchsia fuchsia-board-sherlock ] conformance/textures/canvas/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
 crbug.com/1489477 [ fuchsia ] conformance/textures/canvas/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ]
 crbug.com/1489477 [ fuchsia ] conformance/textures/misc/texture-size-limit.html [ Failure ]
 crbug.com/1489477 [ fuchsia ] conformance/textures/misc/texture-sub-image-cube-maps.html [ Failure ]
diff --git a/gpu/command_buffer/client/client_shared_image.h b/gpu/command_buffer/client/client_shared_image.h
index 8116b2a..1da54e7b 100644
--- a/gpu/command_buffer/client/client_shared_image.h
+++ b/gpu/command_buffer/client/client_shared_image.h
@@ -106,6 +106,14 @@
         Mailbox::GenerateForSharedImage());
   }
 
+  static scoped_refptr<ClientSharedImage> CreateForTesting(
+      const Mailbox& mailbox,
+      std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer) {
+    auto client_si = base::MakeRefCounted<ClientSharedImage>(mailbox);
+    client_si->gpu_memory_buffer_ = std::move(gpu_memory_buffer);
+    return client_si;
+  }
+
  private:
   friend class base::RefCountedThreadSafe<ClientSharedImage>;
   ~ClientSharedImage();
diff --git a/gpu/command_buffer/service/shared_image/shared_image_factory.cc b/gpu/command_buffer/service/shared_image/shared_image_factory.cc
index 976e9f9..7be6010 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_factory.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_factory.cc
@@ -772,10 +772,23 @@
   return true;
 }
 
+bool SharedImageFactory::SetSharedImagePurgeable(const Mailbox& mailbox,
+                                                 bool purgeable) {
+  auto it = shared_images_.find(mailbox);
+  if (it == shared_images_.end()) {
+    LOG(ERROR)
+        << "SetSharedImagePurgeable: Could not find shared image mailbox";
+    return false;
+  }
+  (*it)->SetPurgeable(purgeable);
+  return true;
+}
+
 void SharedImageFactory::DestroyAllSharedImages(bool have_context) {
   if (!have_context) {
-    for (auto& shared_image : shared_images_)
+    for (auto& shared_image : shared_images_) {
       shared_image->OnContextLost();
+    }
   }
   shared_images_.clear();
 }
diff --git a/gpu/command_buffer/service/shared_image/shared_image_factory.h b/gpu/command_buffer/service/shared_image/shared_image_factory.h
index f0db175..db63eba 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_factory.h
+++ b/gpu/command_buffer/service/shared_image/shared_image_factory.h
@@ -103,6 +103,7 @@
   bool UpdateSharedImage(const Mailbox& mailbox,
                          std::unique_ptr<gfx::GpuFence> in_fence);
   bool DestroySharedImage(const Mailbox& mailbox);
+  bool SetSharedImagePurgeable(const Mailbox& mailbox, bool purgeable);
   bool HasImages() const { return !shared_images_.empty(); }
   void DestroyAllSharedImages(bool have_context);
 
diff --git a/gpu/command_buffer/service/shared_image/shared_image_representation.h b/gpu/command_buffer/service/shared_image/shared_image_representation.h
index d3a7d53..1dbf41bf 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_representation.h
+++ b/gpu/command_buffer/service/shared_image/shared_image_representation.h
@@ -192,6 +192,7 @@
     backing()->Update(std::move(in_fence));
     backing()->OnWriteSucceeded();
   }
+  void SetPurgeable(bool purgeable) { backing()->SetPurgeable(purgeable); }
   bool CopyToGpuMemoryBuffer() { return backing()->CopyToGpuMemoryBuffer(); }
   void GetGpuMemoryBufferHandleInfo(gfx::GpuMemoryBufferHandle& handle,
                                     viz::SharedImageFormat& format,
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 6f0acf2..60c9fca 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -4878,6 +4878,14 @@
       <message name="IDS_IOS_TAB_GRID_UNDO_CLOSE_ALL_BUTTON" desc="Title of the button in the tab grid UI that revert the close all action recently taken by the user. [iOS only]">
         Undo
       </message>
+      <message name="IDS_IOS_TAB_GROUP_CREATION_DATE" desc="Subtitle shown in tab group view to diplay the creation date of the group. [iOS only]">
+          Created <ph name="Date"><ex>2023/12/31</ex>$1</ph>
+      </message>
+      <message name="IDS_IOS_TAB_GROUP_TABS_NUMBER" desc="Subtitle shown in tab group view to diplay the current number of tab in the group. [iOS only]">
+        {count, plural,
+        =1 {1 Tab}
+        other {{count} Tabs}}
+      </message>
       <message name="IDS_IOS_TAB_PICKUP_BANNER_BUTTON" desc="Infobar button to open a previously opened tab from another device.">
         Open
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TAB_GROUP_CREATION_DATE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TAB_GROUP_CREATION_DATE.png.sha1
new file mode 100644
index 0000000..9c2001c
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TAB_GROUP_CREATION_DATE.png.sha1
@@ -0,0 +1 @@
+ddd5057fcf8773256caf4edc30212f15c3c11c80
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TAB_GROUP_TABS_NUMBER.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TAB_GROUP_TABS_NUMBER.png.sha1
new file mode 100644
index 0000000..9c2001c
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TAB_GROUP_TABS_NUMBER.png.sha1
@@ -0,0 +1 @@
+ddd5057fcf8773256caf4edc30212f15c3c11c80
\ No newline at end of file
diff --git a/ios/chrome/browser/autofill/model/bottom_sheet/bottom_sheet.ts b/ios/chrome/browser/autofill/model/bottom_sheet/bottom_sheet.ts
index cdad824..8c8f086 100644
--- a/ios/chrome/browser/autofill/model/bottom_sheet/bottom_sheet.ts
+++ b/ios/chrome/browser/autofill/model/bottom_sheet/bottom_sheet.ts
@@ -27,8 +27,12 @@
  * @private
  */
 function isObservable_(element: HTMLElement): boolean {
-  return (element instanceof HTMLInputElement) ||
-      (element instanceof HTMLFormElement);
+  // Ignore passkey fields, which contain the 'webauthn' autofill tag.
+  const autocomplete_attribute = element.getAttribute('autocomplete');
+  const isPasskeyField = autocomplete_attribute?.includes('webauthn');
+  return ((element instanceof HTMLInputElement) ||
+          (element instanceof HTMLFormElement)) &&
+      !isPasskeyField;
 }
 
 /*
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 49362b3..c6e2322 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -249,24 +249,6 @@
      std::size(kDefaultBrowserGenericConditionsHalfscreenPromo), nullptr},
 };
 
-const FeatureEntry::FeatureParam
-    kAutofillUseMobileLabelDisambiguationShowAll[] = {
-        {autofill::features::kAutofillUseMobileLabelDisambiguationParameterName,
-         autofill::features::
-             kAutofillUseMobileLabelDisambiguationParameterShowAll}};
-const FeatureEntry::FeatureParam
-    kAutofillUseMobileLabelDisambiguationShowOne[] = {
-        {autofill::features::kAutofillUseMobileLabelDisambiguationParameterName,
-         autofill::features::
-             kAutofillUseMobileLabelDisambiguationParameterShowOne}};
-
-const FeatureEntry::FeatureVariation
-    kAutofillUseMobileLabelDisambiguationVariations[] = {
-        {"(show all)", kAutofillUseMobileLabelDisambiguationShowAll,
-         std::size(kAutofillUseMobileLabelDisambiguationShowAll), nullptr},
-        {"(show one)", kAutofillUseMobileLabelDisambiguationShowOne,
-         std::size(kAutofillUseMobileLabelDisambiguationShowOne), nullptr}};
-
 // Uses int values from SigninPromoViewStyle enum.
 const FeatureEntry::FeatureParam kDiscoverFeedTopSyncPromoStandard[] = {
     {kDiscoverFeedTopSyncPromoStyle, "0"}};
@@ -871,14 +853,6 @@
      flag_descriptions::kOmniboxInspireMeSignedOutName,
      flag_descriptions::kOmniboxInspireMeSignedOutDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(omnibox::kZeroSuggestOnNTPForSignedOutUsers)},
-    {"autofill-use-mobile-label-disambiguation",
-     flag_descriptions::kAutofillUseMobileLabelDisambiguationName,
-     flag_descriptions::kAutofillUseMobileLabelDisambiguationDescription,
-     flags_ui::kOsIos,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(
-         autofill::features::kAutofillUseMobileLabelDisambiguation,
-         kAutofillUseMobileLabelDisambiguationVariations,
-         "AutofillUseMobileLabelDisambiguation")},
     {"force-startup-signin-promo",
      flag_descriptions::kForceStartupSigninPromoName,
      flag_descriptions::kForceStartupSigninPromoDescription, flags_ui::kOsIos,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 26091b04..3d59fb7 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -135,12 +135,6 @@
     "When enabled, Chrome Settings link directs to GPay Web rather than "
     "Payments Center for payment methods management.";
 
-const char kAutofillUseMobileLabelDisambiguationName[] =
-    "Autofill Uses Mobile Label Disambiguation";
-const char kAutofillUseMobileLabelDisambiguationDescription[] =
-    "When enabled, Autofill suggestions' labels are displayed using a "
-    "mobile-friendly format.";
-
 const char kAutofillUseRendererIDsName[] =
     "Autofill logic uses unqiue renderer IDs";
 const char kAutofillUseRendererIDsDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 14d33ef..8068b77 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -117,11 +117,6 @@
 extern const char kAutofillUpdateChromeSettingsLinkToGPayWebDescription[];
 
 // Title and description for the flag that controls whether Autofill's
-// suggestions' labels are formatting with a mobile-friendly approach.
-extern const char kAutofillUseMobileLabelDisambiguationName[];
-extern const char kAutofillUseMobileLabelDisambiguationDescription[];
-
-// Title and description for the flag that controls whether Autofill's
 // logic is using numeric unique renderer IDs instead of string IDs for
 // form and field elements.
 extern const char kAutofillUseRendererIDsName[];
diff --git a/ios/chrome/browser/metrics/model/ios_chrome_metrics_service_client.mm b/ios/chrome/browser/metrics/model/ios_chrome_metrics_service_client.mm
index 9a25f17..0cc8d8e 100644
--- a/ios/chrome/browser/metrics/model/ios_chrome_metrics_service_client.mm
+++ b/ios/chrome/browser/metrics/model/ios_chrome_metrics_service_client.mm
@@ -462,9 +462,6 @@
   }
   base::UmaHistogramCounts10000("Memory.Browser.MemoryFootprint.NumOpenTabs",
                                 open_tabs_count);
-  base::UmaHistogramCounts10000(
-      "Memory.Browser.MemoryFootprint.NumLiveOverscroll",
-      [OverscrollActionsController instanceCount]);
 
   std::move(collect_final_metrics_done_callback_).Run();
 }
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 59b76dc..493d5bb 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
@@ -54,6 +54,15 @@
                  @"Expected Autofill profile to be present");
 }
 
+void ClearRelevantData() {
+  [BookmarkEarlGrey clearBookmarks];
+
+  [ChromeEarlGrey clearFakeSyncServerData];
+  WaitForEntitiesOnFakeServer(0, syncer::AUTOFILL_PROFILE);
+  WaitForEntitiesOnFakeServer(0, syncer::BOOKMARKS);
+  WaitForEntitiesOnFakeServer(0, syncer::HISTORY);
+}
+
 }  // namespace
 
 // Hermetic sync tests, which use the fake sync server.
@@ -62,29 +71,27 @@
 
 @implementation SyncFakeServerTestCase
 
-- (void)tearDown {
++ (void)setUpForTestCase {
+  [super setUpForTestCase];
+
   [BookmarkEarlGrey waitForBookmarkModelsLoaded];
-  [BookmarkEarlGrey clearBookmarks];
 
-  [ChromeEarlGrey clearFakeSyncServerData];
-
-  WaitForEntitiesOnFakeServer(0, syncer::AUTOFILL_PROFILE);
-  WaitForEntitiesOnFakeServer(0, syncer::BOOKMARKS);
-  WaitForEntitiesOnFakeServer(0, syncer::HISTORY);
-
-  [super tearDown];
+  // Normally there shouldn't be any data (locally or on the fake server) at
+  // this point, but just in case some other test case didn't clean up after
+  // itself, clear everything here.
+  ClearRelevantData();
 }
 
 - (void)setUp {
   [super setUp];
 
   GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
+}
 
-  [ChromeEarlGrey clearFakeSyncServerData];
+- (void)tearDown {
+  ClearRelevantData();
 
-  WaitForEntitiesOnFakeServer(0, syncer::AUTOFILL_PROFILE);
-  WaitForEntitiesOnFakeServer(0, syncer::BOOKMARKS);
-  WaitForEntitiesOnFakeServer(0, syncer::HISTORY);
+  [super tearDown];
 }
 
 - (AppLaunchConfiguration)appConfigurationForTestCase {
diff --git a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
index 15d166d..bc8679ad 100644
--- a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
+++ b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
@@ -1673,12 +1673,30 @@
 // TODO(crbug.com/779791) : Do not pass `baseViewController` through dispatcher.
 - (void)showSignin:(ShowSigninCommand*)command
     baseViewController:(UIViewController*)baseViewController {
+  // Calling this method when there is a signinCoordinator alive is incorrect
+  // as there should not be 2 signinCoordinators alive at the same time (note
+  // that allocating the second one will dealloc the first and this crashes in
+  // various ways).
   if (command.skipIfUINotAvaible &&
       (baseViewController.presentedViewController ||
        ![self isTabAvailableToPresentViewController])) {
     // Make sure the UI is available to present the sign-in view.
     return;
   }
+  if (self.signinCoordinator) {
+    // As of M121, the CHECK above is known to fire in various cases. The goal
+    // of the histograms below is to detect the number of incorrect cases and
+    // for which of the access points they are triggered.
+    base::UmaHistogramEnumeration(
+        "Signin.ShowSigninCoordinatorWhenAlreadyPresent.NewAccessPoint",
+        command.accessPoint, signin_metrics::AccessPoint::ACCESS_POINT_MAX);
+    base::UmaHistogramEnumeration(
+        "Signin.ShowSigninCoordinatorWhenAlreadyPresent.OldAccessPoint",
+        self.signinCoordinator.accessPoint,
+        signin_metrics::AccessPoint::ACCESS_POINT_MAX);
+  }
+  // TODO(crbug.com/1479861): Change this to a CHECK once this invariant is
+  // correct.
   DCHECK(!self.signinCoordinator)
       << "self.signinCoordinator: "
       << base::SysNSStringToUTF8([self.signinCoordinator description]);
diff --git a/ios/chrome/browser/shared/ui/symbols/symbol_names.h b/ios/chrome/browser/shared/ui/symbols/symbol_names.h
index cbf6fab..7c662509 100644
--- a/ios/chrome/browser/shared/ui/symbols/symbol_names.h
+++ b/ios/chrome/browser/shared/ui/symbols/symbol_names.h
@@ -198,6 +198,7 @@
 extern NSString* const kLockSymbol;
 extern NSString* const kRulerSymbol;
 extern NSString* const kLaptopAndIphoneSymbol;
+extern NSString* const kNewTabGroupActionSymbol;
 
 // Names of the default symbol being non-monochrome by default. When using them,
 // you probably want to set their color to monochrome.
diff --git a/ios/chrome/browser/shared/ui/symbols/symbol_names.mm b/ios/chrome/browser/shared/ui/symbols/symbol_names.mm
index 46727c8..f4ea8f3 100644
--- a/ios/chrome/browser/shared/ui/symbols/symbol_names.mm
+++ b/ios/chrome/browser/shared/ui/symbols/symbol_names.mm
@@ -192,6 +192,7 @@
 NSString* const kLockSymbol = @"lock";
 NSString* const kRulerSymbol = @"ruler";
 NSString* const kLaptopAndIphoneSymbol = @"laptopcomputer.and.iphone";
+NSString* const kNewTabGroupActionSymbol = @"plus.square.on.square";
 
 // Names of the default symbol being non-monochrome by default. When using them,
 // you probably want to set their color to monochrome.
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 e0b18c1..849dc65 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
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#import "base/test/ios/wait_util.h"
 #import "components/policy/policy_constants.h"
 #import "components/supervised_user/core/browser/supervised_user_url_filter.h"
 #import "components/supervised_user/core/common/features.h"
@@ -208,11 +209,17 @@
       1, [ChromeEarlGrey realizedWebStatesCount],
       @"A single realized web state must exist. The tab reloading filtering"
       @"behaviour should not force web states to become realized.");
-  GREYAssertEqual(1,
-                  [SupervisedUserSettingsAppInterface
-                      countSupervisedUserIntersitialsForExistingWebStates],
-                  @"A single interstitial must exist.");
-  [ChromeEarlGrey waitForMainTabCount:3];
+
+  // Wait for one interstitial to appear (on the realized tab).
+  GREYAssert(
+      base::test::ios::WaitUntilConditionOrTimeout(
+          base::test::ios::kWaitForPageLoadTimeout,
+          ^bool {
+            return
+                [SupervisedUserSettingsAppInterface
+                    countSupervisedUserIntersitialsForExistingWebStates] == 1;
+          }),
+      @"Interstitial did not appear.");
 
   // Out of the 3 tabs, only the active one should have recorded metrics for
   // filtering.
diff --git a/ios/chrome/browser/ui/browser_view/BUILD.gn b/ios/chrome/browser/ui/browser_view/BUILD.gn
index e6bb03f7..c27311d 100644
--- a/ios/chrome/browser/ui/browser_view/BUILD.gn
+++ b/ios/chrome/browser/ui/browser_view/BUILD.gn
@@ -193,6 +193,7 @@
     "//ios/chrome/browser/ui/sad_tab",
     "//ios/chrome/browser/ui/sad_tab:coordinator",
     "//ios/chrome/browser/ui/safe_browsing",
+    "//ios/chrome/browser/ui/save_to_drive",
     "//ios/chrome/browser/ui/save_to_photos",
     "//ios/chrome/browser/ui/send_tab_to_self",
     "//ios/chrome/browser/ui/settings/autofill",
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
index d428672..05436287 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -191,6 +191,7 @@
 #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/sad_tab/sad_tab_coordinator.h"
 #import "ios/chrome/browser/ui/safe_browsing/safe_browsing_coordinator.h"
+#import "ios/chrome/browser/ui/save_to_drive/save_to_drive_coordinator.h"
 #import "ios/chrome/browser/ui/save_to_photos/save_to_photos_coordinator.h"
 #import "ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_coordinator.h"
 #import "ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_coordinator.h"
@@ -472,6 +473,9 @@
 // Coordinator for Safe Browsing.
 @property(nonatomic, strong) SafeBrowsingCoordinator* safeBrowsingCoordinator;
 
+// Coordinator for displaying the Save to Drive UI.
+@property(nonatomic, strong) SaveToDriveCoordinator* saveToDriveCoordinator;
+
 // Coordinator for displaying the Save to Photos UI.
 @property(nonatomic, strong) SaveToPhotosCoordinator* saveToPhotosCoordinator;
 
@@ -658,6 +662,7 @@
 - (void)clearPresentedStateWithCompletion:(ProceduralBlock)completion
                            dismissOmnibox:(BOOL)dismissOmnibox {
   [self stopSaveToPhotos];
+  [self hideSaveToDrive];
 
   [self.passKitCoordinator stop];
 
@@ -2624,11 +2629,20 @@
 #pragma mark - SaveToDriveCommands
 
 - (void)showSaveToDrive:(ShowSaveToDriveCommand*)command {
-  // TODO(crbug.com/1495352): Start SaveToDriveCoordinator.
+  // If the Save to Drive coordinator is not nil, stop it.
+  [self hideSaveToDrive];
+
+  _saveToDriveCoordinator = [[SaveToDriveCoordinator alloc]
+      initWithBaseViewController:self.viewController
+                         browser:self.browser
+                        fileName:command.fileName
+                        fileSize:command.fileSize];
+  [_saveToDriveCoordinator start];
 }
 
 - (void)hideSaveToDrive {
-  // TODO(crbug.com/1495352): Stop SaveToDriveCoordinator.
+  [_saveToDriveCoordinator stop];
+  _saveToDriveCoordinator = nil;
 }
 
 #pragma mark - SaveToPhotosCommands
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_text_field_experimental.mm b/ios/chrome/browser/ui/omnibox/omnibox_text_field_experimental.mm
index 793b7e1..14f03786 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_text_field_experimental.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_text_field_experimental.mm
@@ -448,6 +448,18 @@
                                       : [super caretRectForPosition:position];
 }
 
+- (NSArray<UITextSelectionRect*>*)selectionRectsForRange:(UITextRange*)range {
+  // Hide the selection UI in pre-edit. UITextField is expected to hide the
+  // selection UI when `clearsOnInsertion` is YES, but this behavior is not
+  // working on iOS 17.
+  if (@available(iOS 17, *)) {
+    if (self.isPreEditing) {
+      return nil;
+    }
+  }
+  return [super selectionRectsForRange:range];
+}
+
 #pragma mark - UITextInput
 
 - (void)beginFloatingCursorAtPoint:(CGPoint)point {
diff --git a/ios/chrome/browser/ui/passwords/bottom_sheet/password_suggestion_bottom_sheet_egtest.mm b/ios/chrome/browser/ui/passwords/bottom_sheet/password_suggestion_bottom_sheet_egtest.mm
index f59267d1..343d4e0 100644
--- a/ios/chrome/browser/ui/passwords/bottom_sheet/password_suggestion_bottom_sheet_egtest.mm
+++ b/ios/chrome/browser/ui/passwords/bottom_sheet/password_suggestion_bottom_sheet_egtest.mm
@@ -196,6 +196,13 @@
   [ChromeEarlGrey waitForWebStateContainingText:"Login form."];
 }
 
+- (void)loadLoginPasskeyPage {
+  // Loads simple page. It is on localhost so it is considered a secure context.
+  [ChromeEarlGrey
+      loadURL:self.testServer->GetURL("/simple_login_form_empty_passkey.html")];
+  [ChromeEarlGrey waitForWebStateContainingText:"Login form."];
+}
+
 // Returns the matcher for the edit button from the navigation bar.
 id<GREYMatcher> NavigationBarEditButton() {
   return grey_allOf(chrome_test_util::ButtonWithAccessibilityLabelId(
@@ -302,6 +309,26 @@
   GREYAssert(WaitForKeyboardToAppear(), @"Keyboard didn't appear.");
 }
 
+// This test verifies that the password bottom sheet does not open when the
+// webpage has enabled passkey login.
+- (void)testOpenKeyboardOnPasskey {
+  [PasswordManagerAppInterface
+      storeCredentialWithUsername:@"user"
+                         password:@"password"
+                              URL:net::NSURLWithGURL(self.testServer->GetURL(
+                                      "/simple_login_form_empty_passkey."
+                                      "html"))];
+  [SigninEarlGreyUI signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]
+                                enableSync:NO];
+
+  [self loadLoginPasskeyPage];
+
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
+      performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
+
+  GREYAssert(WaitForKeyboardToAppear(), @"Keyboard didn't appear.");
+}
+
 // This test will allow us to know if we're using a coherent browser state to
 // open the bottom sheet in incognito mode.
 - (void)testOpenPasswordBottomSheetUsePasswordIncognito {
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
index 95cd602c..2e4dbc9 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
@@ -176,6 +176,7 @@
 @property(nonatomic, assign) sessions::TabRestoreService* tabRestoreService;
 // The sync state.
 @property(nonatomic, assign) SessionsSyncUserState sessionState;
+// Mediator in charge of inviting the user to sign-in with a Google account.
 @property(nonatomic, strong) SigninPromoViewMediator* signinPromoViewMediator;
 // The browser state used for many operations, derived from the one provided by
 // `self.browser`.
@@ -282,7 +283,9 @@
   }
 
   if (self.syncService->GetUserSettings()->IsTypeManagedByPolicy(
-          syncer::UserSelectableType::kTabs)) {
+          syncer::UserSelectableType::kTabs) ||
+      self.syncService->GetUserSettings()->IsTypeManagedByPolicy(
+          syncer::UserSelectableType::kHistory)) {
     // Return YES if the data type is disabled by the SyncTypesListDisabled
     // policy.
     return YES;
diff --git a/ios/chrome/browser/ui/save_to_drive/BUILD.gn b/ios/chrome/browser/ui/save_to_drive/BUILD.gn
new file mode 100644
index 0000000..7c2d8e4
--- /dev/null
+++ b/ios/chrome/browser/ui/save_to_drive/BUILD.gn
@@ -0,0 +1,15 @@
+# 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.
+
+source_set("save_to_drive") {
+  sources = [
+    "save_to_drive_coordinator.h",
+    "save_to_drive_coordinator.mm",
+  ]
+  deps = [
+    "//base",
+    "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
+  ]
+  frameworks = [ "UIKit.framework" ]
+}
diff --git a/ios/chrome/browser/ui/save_to_drive/save_to_drive_coordinator.h b/ios/chrome/browser/ui/save_to_drive/save_to_drive_coordinator.h
new file mode 100644
index 0000000..acc32b7
--- /dev/null
+++ b/ios/chrome/browser/ui/save_to_drive/save_to_drive_coordinator.h
@@ -0,0 +1,23 @@
+// 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_SAVE_TO_DRIVE_SAVE_TO_DRIVE_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_SAVE_TO_DRIVE_SAVE_TO_DRIVE_COORDINATOR_H_
+
+#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h"
+
+@interface SaveToDriveCoordinator : ChromeCoordinator
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
+                                  fileName:(NSString*)fileName
+                                  fileSize:(int64_t)fileSize
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SAVE_TO_DRIVE_SAVE_TO_DRIVE_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/save_to_drive/save_to_drive_coordinator.mm b/ios/chrome/browser/ui/save_to_drive/save_to_drive_coordinator.mm
new file mode 100644
index 0000000..dc4836c8
--- /dev/null
+++ b/ios/chrome/browser/ui/save_to_drive/save_to_drive_coordinator.mm
@@ -0,0 +1,36 @@
+// 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/save_to_drive/save_to_drive_coordinator.h"
+
+@implementation SaveToDriveCoordinator {
+  NSString* _fileName;
+  int64_t _fileSize;
+  // TODO(crbug.com/1495352): Add an account picker coordinator to let the user
+  // select the identity with which they wish to save the file to Drive.
+}
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
+                                  fileName:(NSString*)fileName
+                                  fileSize:(int64_t)fileSize {
+  self = [super initWithBaseViewController:viewController browser:browser];
+  if (self) {
+    _fileName = fileName;
+    _fileSize = fileSize;
+  }
+  return self;
+}
+
+#pragma mark - ChromeCoordinator
+
+- (void)start {
+  // TODO(crbug.com/1495352): Start the account picker coordinator.
+}
+
+- (void)stop {
+  // TODO(crbug.com/1495352): Stop the account picker coordinator.
+}
+
+@end
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/BUILD.gn
index bb9ac7ad..b074eeb 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/BUILD.gn
@@ -33,10 +33,14 @@
     ":tab_group_consumer",
     ":tab_group_mutator",
     "//base",
+    "//base:i18n",
+    "//ios/chrome/app/strings",
     "//ios/chrome/browser/shared/public/features",
+    "//ios/chrome/browser/shared/ui/symbols",
     "//ios/chrome/browser/ui/tab_switcher/tab_grid/grid:grid_ui_constants",
     "//ios/chrome/common/ui/colors",
     "//ios/chrome/common/ui/util",
+    "//ui/base",
   ]
 }
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_view_controller.mm
index 863acd6..12625a4 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_view_controller.mm
@@ -5,21 +5,30 @@
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_view_controller.h"
 
 #import "base/check.h"
+#import "base/i18n/time_formatting.h"
+#import "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
+#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_mutator.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_groups_commands.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
+#import "ios/chrome/grit/ios_strings.h"
+#import "ui/base/l10n/l10n_util_mac.h"
 
 namespace {
 constexpr CGFloat kColoredDotSize = 20;
+constexpr CGFloat kDotSeparationSize = 4;
 constexpr CGFloat kTitleHorizontalMargin = 16;
 constexpr CGFloat kTitleVerticalMargin = 10;
-constexpr CGFloat kLeftMargin = 9;
-constexpr CGFloat kFullTitleTopMargin = 24;
+constexpr CGFloat kHorizontalMargin = 9;
+constexpr CGFloat kPrimaryTitleMargin = 24;
 constexpr CGFloat kDotTitleSeparationMargin = 8;
 constexpr CGFloat kBackgroundAlpha = 0.6;
+constexpr CGFloat kSubTitleHorizontalPadding = 7;
+constexpr CGFloat kThreeDotButtonSize = 19;
+constexpr CGFloat kTitleBackgroundCornerRadius = 17;
 }  // namespace
 
 @interface TabGroupViewController () <UINavigationBarDelegate>
@@ -67,7 +76,26 @@
   }
 
   [self configureNavigationBar];
-  [self configurePrimaryTitle];
+  UIView* primaryTitle = [self configuredPrimaryTitle];
+  UIView* secondaryTitle = [self configuredSubTitle];
+
+  [self.view addSubview:primaryTitle];
+  [self.view addSubview:secondaryTitle];
+
+  [NSLayoutConstraint activateConstraints:@[
+    [primaryTitle.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor
+                                               constant:kHorizontalMargin],
+    [primaryTitle.topAnchor constraintEqualToAnchor:_navigationBar.bottomAnchor
+                                           constant:kPrimaryTitleMargin],
+    [secondaryTitle.leadingAnchor
+        constraintEqualToAnchor:self.view.leadingAnchor
+                       constant:kHorizontalMargin],
+    [secondaryTitle.trailingAnchor
+        constraintEqualToAnchor:self.view.trailingAnchor
+                       constant:-kHorizontalMargin],
+    [secondaryTitle.topAnchor constraintEqualToAnchor:primaryTitle.bottomAnchor
+                                             constant:kPrimaryTitleMargin],
+  ]];
 }
 
 - (void)didTapPlusButton {
@@ -192,13 +220,13 @@
   return titleLabel;
 }
 
-// Configures the full primary title (colored dot and text title).
-- (void)configurePrimaryTitle {
+// Returns the configured full primary title (colored dot and text title).
+- (UIView*)configuredPrimaryTitle {
   UIView* fullTitleView = [[UIView alloc] initWithFrame:CGRectZero];
   fullTitleView.translatesAutoresizingMaskIntoConstraints = NO;
   fullTitleView.backgroundColor =
       [[UIColor colorNamed:kSolidWhiteColor] colorWithAlphaComponent:0.1];
-  fullTitleView.layer.cornerRadius = 17;
+  fullTitleView.layer.cornerRadius = kTitleBackgroundCornerRadius;
   fullTitleView.opaque = NO;
 
   UIView* coloredDotView = [self groupColorDotView];
@@ -206,18 +234,12 @@
   [fullTitleView addSubview:coloredDotView];
   [fullTitleView addSubview:titleView];
 
-  [self.view addSubview:fullTitleView];
-
   [NSLayoutConstraint activateConstraints:@[
     [titleView.leadingAnchor
         constraintEqualToAnchor:coloredDotView.trailingAnchor
                        constant:kDotTitleSeparationMargin],
     [coloredDotView.centerYAnchor
         constraintEqualToAnchor:titleView.centerYAnchor],
-    [fullTitleView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor
-                                                constant:kLeftMargin],
-    [fullTitleView.topAnchor constraintEqualToAnchor:_navigationBar.bottomAnchor
-                                            constant:kFullTitleTopMargin],
     [coloredDotView.leadingAnchor
         constraintEqualToAnchor:fullTitleView.leadingAnchor
                        constant:kTitleHorizontalMargin],
@@ -229,6 +251,96 @@
     [fullTitleView.bottomAnchor constraintEqualToAnchor:titleView.bottomAnchor
                                                constant:kTitleVerticalMargin],
   ]];
+  return fullTitleView;
+}
+
+// Returns the string with give the current number of tabs in the group.
+- (NSString*)numberOfTabsString {
+  // TODO(crbug.com/1501837): Configure the string with the real number of
+  // items.
+  return l10n_util::GetPluralNSStringF(IDS_IOS_TAB_GROUP_TABS_NUMBER, 1);
+}
+
+// Returns the string which give information about the creation date.
+- (NSString*)creationDateString {
+  NSString* dateString = base::SysUTF16ToNSString(
+      base::LocalizedTimeFormatWithPattern(_groupCreationDate, "YYYY/MM/dd"));
+  return l10n_util::GetNSStringF(IDS_IOS_TAB_GROUP_CREATION_DATE,
+                                 base::SysNSStringToUTF16(dateString));
+}
+
+// Returns the configured sub titles view.
+- (UIView*)configuredSubTitle {
+  UIView* subTitleView = [[UIView alloc] initWithFrame:CGRectZero];
+  subTitleView.translatesAutoresizingMaskIntoConstraints = NO;
+
+  UITraitCollection* interfaceStyleDarkTraitCollection = [UITraitCollection
+      traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
+  UIColor* textColor = [[UIColor colorNamed:kTextSecondaryColor]
+      resolvedColorWithTraitCollection:interfaceStyleDarkTraitCollection];
+
+  UILabel* numberOfTabsLabel = [[UILabel alloc] init];
+  numberOfTabsLabel.translatesAutoresizingMaskIntoConstraints = NO;
+  numberOfTabsLabel.textColor = textColor;
+  numberOfTabsLabel.font =
+      [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
+  numberOfTabsLabel.text = [self numberOfTabsString];
+
+  UILabel* creationDateLabel = [[UILabel alloc] init];
+  creationDateLabel.translatesAutoresizingMaskIntoConstraints = NO;
+  creationDateLabel.textColor = textColor;
+  creationDateLabel.font =
+      [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
+  creationDateLabel.text = [self creationDateString];
+
+  UIView* dotSeparation = [[UIView alloc] initWithFrame:CGRectZero];
+  dotSeparation.translatesAutoresizingMaskIntoConstraints = NO;
+  dotSeparation.layer.backgroundColor = textColor.CGColor;
+  dotSeparation.layer.cornerRadius = kDotSeparationSize / 2;
+
+  // TODO(crbug.com/1501837): Add action to the button.
+  UIButton* menuButton = [[UIButton alloc] init];
+  menuButton.translatesAutoresizingMaskIntoConstraints = NO;
+  [menuButton
+      setImage:DefaultSymbolWithPointSize(kMenuSymbol, kThreeDotButtonSize)
+      forState:UIControlStateNormal];
+  menuButton.tintColor = [UIColor colorNamed:kSolidWhiteColor];
+
+  [subTitleView addSubview:numberOfTabsLabel];
+  [subTitleView addSubview:dotSeparation];
+  [subTitleView addSubview:creationDateLabel];
+  [subTitleView addSubview:menuButton];
+
+  [NSLayoutConstraint activateConstraints:@[
+    [numberOfTabsLabel.leadingAnchor
+        constraintEqualToAnchor:subTitleView.leadingAnchor
+                       constant:kSubTitleHorizontalPadding],
+    [numberOfTabsLabel.topAnchor
+        constraintEqualToAnchor:subTitleView.topAnchor],
+    [subTitleView.heightAnchor
+        constraintEqualToAnchor:numberOfTabsLabel.heightAnchor],
+    [dotSeparation.leadingAnchor
+        constraintEqualToAnchor:numberOfTabsLabel.trailingAnchor
+                       constant:kDotTitleSeparationMargin],
+    [dotSeparation.centerYAnchor
+        constraintEqualToAnchor:numberOfTabsLabel.centerYAnchor],
+    [dotSeparation.heightAnchor constraintEqualToConstant:kDotSeparationSize],
+    [dotSeparation.widthAnchor constraintEqualToConstant:kDotSeparationSize],
+    [creationDateLabel.leadingAnchor
+        constraintEqualToAnchor:dotSeparation.trailingAnchor
+                       constant:kDotTitleSeparationMargin],
+    [creationDateLabel.centerYAnchor
+        constraintEqualToAnchor:numberOfTabsLabel.centerYAnchor],
+    [menuButton.trailingAnchor
+        constraintEqualToAnchor:subTitleView.trailingAnchor
+                       constant:-kSubTitleHorizontalPadding],
+    [menuButton.centerYAnchor
+        constraintEqualToAnchor:numberOfTabsLabel.centerYAnchor],
+    [creationDateLabel.trailingAnchor
+        constraintLessThanOrEqualToAnchor:menuButton.leadingAnchor],
+  ]];
+
+  return subTitleView;
 }
 
 @end
diff --git a/ios/chrome/common/ui/promo_style/resources/BUILD.gn b/ios/chrome/common/ui/promo_style/resources/BUILD.gn
index 2929c8c..def18a9 100644
--- a/ios/chrome/common/ui/promo_style/resources/BUILD.gn
+++ b/ios/chrome/common/ui/promo_style/resources/BUILD.gn
@@ -18,6 +18,7 @@
   sources = [
     "promo_background_left.imageset/Contents.json",
     "promo_background_left.imageset/promo_background_left.pdf",
+    "promo_background_left.imageset/promo_background_left_dark.pdf",
   ]
 }
 
@@ -25,5 +26,6 @@
   sources = [
     "promo_background_right.imageset/Contents.json",
     "promo_background_right.imageset/promo_background_right.pdf",
+    "promo_background_right.imageset/promo_background_right_dark.pdf",
   ]
 }
diff --git a/ios/chrome/common/ui/promo_style/resources/promo_background_left.imageset/Contents.json b/ios/chrome/common/ui/promo_style/resources/promo_background_left.imageset/Contents.json
index 7c412807..f2b0c1b 100644
--- a/ios/chrome/common/ui/promo_style/resources/promo_background_left.imageset/Contents.json
+++ b/ios/chrome/common/ui/promo_style/resources/promo_background_left.imageset/Contents.json
@@ -11,7 +11,7 @@
           "value" : "dark"
         }
       ],
-      "filename" : "promo_background_left.pdf",
+      "filename" : "promo_background_left_dark.pdf",
       "idiom" : "universal"
     }
   ],
diff --git a/ios/chrome/common/ui/promo_style/resources/promo_background_left.imageset/promo_background_left.pdf b/ios/chrome/common/ui/promo_style/resources/promo_background_left.imageset/promo_background_left.pdf
index a54512f..dd07493d 100644
--- a/ios/chrome/common/ui/promo_style/resources/promo_background_left.imageset/promo_background_left.pdf
+++ b/ios/chrome/common/ui/promo_style/resources/promo_background_left.imageset/promo_background_left.pdf
Binary files differ
diff --git a/ios/chrome/common/ui/promo_style/resources/promo_background_left.imageset/promo_background_left_dark.pdf b/ios/chrome/common/ui/promo_style/resources/promo_background_left.imageset/promo_background_left_dark.pdf
new file mode 100644
index 0000000..45fc6b8
--- /dev/null
+++ b/ios/chrome/common/ui/promo_style/resources/promo_background_left.imageset/promo_background_left_dark.pdf
Binary files differ
diff --git a/ios/chrome/common/ui/promo_style/resources/promo_background_right.imageset/Contents.json b/ios/chrome/common/ui/promo_style/resources/promo_background_right.imageset/Contents.json
index 69dc7127..c051e06 100644
--- a/ios/chrome/common/ui/promo_style/resources/promo_background_right.imageset/Contents.json
+++ b/ios/chrome/common/ui/promo_style/resources/promo_background_right.imageset/Contents.json
@@ -11,7 +11,7 @@
           "value" : "dark"
         }
       ],
-      "filename" : "promo_background_right.pdf",
+      "filename" : "promo_background_right_dark.pdf",
       "idiom" : "universal"
     }
   ],
diff --git a/ios/chrome/common/ui/promo_style/resources/promo_background_right.imageset/promo_background_right_dark.pdf b/ios/chrome/common/ui/promo_style/resources/promo_background_right.imageset/promo_background_right_dark.pdf
new file mode 100644
index 0000000..3360bc9
--- /dev/null
+++ b/ios/chrome/common/ui/promo_style/resources/promo_background_right.imageset/promo_background_right_dark.pdf
Binary files differ
diff --git a/ios/testing/data/http_server_files/simple_login_form_empty_passkey.html b/ios/testing/data/http_server_files/simple_login_form_empty_passkey.html
new file mode 100644
index 0000000..d9514cfe
--- /dev/null
+++ b/ios/testing/data/http_server_files/simple_login_form_empty_passkey.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+
+<html><body>
+Login form.
+<form name='login_form' id='login_form' action='https://google.com/'>
+  <input autocomplete='webauthn' type='text' name='username' id='un'><br/>
+  <input autocomplete='current-password webauthn' type='password' name='password' id='pw'><br/>
+  <button id='submit_button' value='Submit'>SubForm</button>
+</form>
+</body></html>
diff --git a/ios/testing/http_server_bundle_data.filelist b/ios/testing/http_server_bundle_data.filelist
index f427f09..d7e04b0d 100644
--- a/ios/testing/http_server_bundle_data.filelist
+++ b/ios/testing/http_server_bundle_data.filelist
@@ -47,6 +47,7 @@
 data/http_server_files/simple_login_form.html
 data/http_server_files/simple_login_form_empty.html
 data/http_server_files/simple_login_form_empty_autofocus.html
+data/http_server_files/simple_login_form_empty_passkey.html
 data/http_server_files/simple_signup_form.html
 data/http_server_files/single_page_wide.pdf
 data/http_server_files/state_operations.html
diff --git a/ios_internal b/ios_internal
index e0a71e9..f94bc05 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit e0a71e95e946c2884c2980332683abf210fd3ffc
+Subproject commit f94bc05bf5ee979b36eb2d58b336e469404524c1
diff --git a/media/audio/apple/audio_manager_apple.h b/media/audio/apple/audio_manager_apple.h
index 94f251d..9a0c463 100644
--- a/media/audio/apple/audio_manager_apple.h
+++ b/media/audio/apple/audio_manager_apple.h
@@ -41,6 +41,10 @@
   // Refer main:media/audio/mac/audio_manager_mac.h for more details.
   virtual bool ShouldDeferStreamStart() const = 0;
 
+  // Retrieves the current hardware sample rate associated with a specified
+  // device.
+  virtual int HardwareSampleRateForDevice(AudioDeviceID device_id) = 0;
+
  protected:
   AudioManagerApple(std::unique_ptr<AudioThread> audio_thread,
                     AudioLogFactory* audio_log_factory);
diff --git a/media/audio/ios/audio_manager_ios.cc b/media/audio/ios/audio_manager_ios.cc
index 2af0b78d..9d609390 100644
--- a/media/audio/ios/audio_manager_ios.cc
+++ b/media/audio/ios/audio_manager_ios.cc
@@ -172,10 +172,6 @@
 }
 
 // static
-double AudioManagerIOS::HardwareSampleRate() {
-  return AudioSessionManagerIOS::GetInstance().HardwareSampleRate();
-}
-
 double AudioManagerIOS::HardwareIOBufferDuration() {
   return AudioSessionManagerIOS::GetInstance().HardwareIOBufferDuration();
 }
@@ -202,6 +198,11 @@
   return AudioSessionManagerIOS::GetInstance().IsInputMuted();
 }
 
+int AudioManagerIOS::HardwareSampleRateForDevice(AudioDeviceID device_id) {
+  return static_cast<int>(
+      AudioSessionManagerIOS::GetInstance().HardwareSampleRate());
+}
+
 bool AudioManagerIOS::IsInputGainSettable() {
   return AudioSessionManagerIOS::GetInstance().IsInputGainSettable();
 }
diff --git a/media/audio/ios/audio_manager_ios.h b/media/audio/ios/audio_manager_ios.h
index ae71e7c..01dce9d 100644
--- a/media/audio/ios/audio_manager_ios.h
+++ b/media/audio/ios/audio_manager_ios.h
@@ -98,11 +98,14 @@
   // Returns the current muting state for the microphone.
   bool IsInputMuted(AudioDeviceID device_id) override;
 
+  // Retrieves the current hardware sample rate associated with a specified
+  // device.
+  int HardwareSampleRateForDevice(AudioDeviceID device_id) override;
+
   // Check if delayed start for stream is needed.
   bool ShouldDeferStreamStart() const override;
 
   // Hardware information
-  double HardwareSampleRate();
   double HardwareIOBufferDuration();
   double HardwareLatency(bool is_input);
   long GetDeviceChannels(bool is_input);
diff --git a/media/audio/mac/audio_low_latency_input_mac.cc b/media/audio/mac/audio_low_latency_input_mac.cc
index 5b5af3dd..cb1fda2 100644
--- a/media/audio/mac/audio_low_latency_input_mac.cc
+++ b/media/audio/mac/audio_low_latency_input_mac.cc
@@ -213,7 +213,7 @@
 
   // The requested sample-rate must match the hardware sample-rate.
   const int sample_rate =
-      AudioManagerMac::HardwareSampleRateForDevice(input_device_id_);
+      manager_->HardwareSampleRateForDevice(input_device_id_);
   DCHECK_EQ(sample_rate, format_.mSampleRate);
 
   log_callback_.Run(base::StrCat(
@@ -985,14 +985,6 @@
   return noErr;
 }
 
-int AUAudioInputStream::HardwareSampleRate() {
-  // Determine the default input device's sample-rate.
-  AudioDeviceID input_device_id = kAudioObjectUnknown;
-  AudioManagerMac::GetDefaultInputDevice(&input_device_id);
-  return static_cast<int>(
-      AudioManagerMac::HardwareSampleRateForDevice(input_device_id));
-}
-
 base::TimeTicks AUAudioInputStream::GetCaptureTime(
     const AudioTimeStamp* input_time_stamp) {
   // Total latency is composed by the dynamic latency and the fixed
diff --git a/media/audio/mac/audio_low_latency_input_mac.h b/media/audio/mac/audio_low_latency_input_mac.h
index bb3cb68..5fb98629 100644
--- a/media/audio/mac/audio_low_latency_input_mac.h
+++ b/media/audio/mac/audio_low_latency_input_mac.h
@@ -25,7 +25,6 @@
 //
 // - It is recommended to first acquire the native sample rate of the default
 //   input device and then use the same rate when creating this object.
-//   Use AUAudioInputStream::HardwareSampleRate() to retrieve the sample rate.
 // - Calling Close() also leads to self destruction.
 // - The latency consists of two parts:
 //   1) Hardware latency, which includes Audio Unit latency, audio device
@@ -89,9 +88,6 @@
   bool IsMuted() override;
   void SetOutputDeviceForAec(const std::string& output_device_id) override;
 
-  // Returns the current hardware sample rate for the default input device.
-  static int HardwareSampleRate();
-
   // Returns true if the audio unit is active/running.
   // The result is based on the kAudioOutputUnitProperty_IsRunning property
   // which exists for output units.
diff --git a/media/audio/mac/audio_low_latency_input_mac_unittest.cc b/media/audio/mac/audio_low_latency_input_mac_unittest.cc
index b4ceb0b..f8a5a70 100644
--- a/media/audio/mac/audio_low_latency_input_mac_unittest.cc
+++ b/media/audio/mac/audio_low_latency_input_mac_unittest.cc
@@ -139,11 +139,19 @@
 #endif
   }
 
+  int HardwareSampleRateForDefaultInputDevice() {
+    // Determine the default input device's sample-rate.
+    AudioDeviceID input_device_id = kAudioObjectUnknown;
+    AudioManagerMac::GetDefaultInputDevice(&input_device_id);
+    auto* manager = static_cast<AudioManagerApple*>(audio_manager_.get());
+    return manager->HardwareSampleRateForDevice(input_device_id);
+  }
+
   // Convenience method which creates a default AudioInputStream object using
   // a 10ms frame size and a sample rate which is set to the hardware sample
   // rate.
   AudioInputStream* CreateDefaultAudioInputStream() {
-    int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
+    int fs = HardwareSampleRateForDefaultInputDevice();
     int samples_per_packet = fs / 100;
     AudioInputStream* ais = audio_manager_->MakeAudioInputStream(
         AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
@@ -159,7 +167,7 @@
   // specified channel layout.
   AudioInputStream* CreateAudioInputStream(
       ChannelLayoutConfig channel_layout_config) {
-    int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
+    int fs = HardwareSampleRateForDefaultInputDevice();
     int samples_per_packet = fs / 100;
     AudioInputStream* ais = audio_manager_->MakeAudioInputStream(
         AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
@@ -289,7 +297,7 @@
   ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
   const char* file_name = "out_stereo_10sec.pcm";
 
-  int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
+  int fs = HardwareSampleRateForDefaultInputDevice();
   AudioInputStream* ais = CreateDefaultAudioInputStream();
   EXPECT_EQ(ais->Open(), AudioInputStream::OpenOutcome::kSuccess);
 
diff --git a/media/audio/mac/audio_manager_mac.cc b/media/audio/mac/audio_manager_mac.cc
index cfbd2f1..7f07999 100644
--- a/media/audio/mac/audio_manager_mac.cc
+++ b/media/audio/mac/audio_manager_mac.cc
@@ -652,27 +652,6 @@
 }
 
 // static
-int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
-  DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
-  Float64 nominal_sample_rate;
-  UInt32 info_size = sizeof(nominal_sample_rate);
-
-  static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
-      kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal,
-      kAudioObjectPropertyElementMain};
-  OSStatus result =
-      AudioObjectGetPropertyData(device_id, &kNominalSampleRateAddress, 0, 0,
-                                 &info_size, &nominal_sample_rate);
-  if (result != noErr) {
-    OSSTATUS_DLOG(WARNING, result)
-        << "Could not get default sample rate for device: " << device_id
-        << ", returing fallback sample rate " << kFallbackSampleRate;
-    return kFallbackSampleRate;
-  }
-
-  return static_cast<int>(nominal_sample_rate);
-}
-
 void AudioManagerMac::GetAudioInputDeviceNames(
     media::AudioDeviceNames* device_names) {
   DCHECK(device_names->empty());
@@ -1464,6 +1443,27 @@
   return result == noErr && muted != 0;
 }
 
+int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
+  DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
+  Float64 nominal_sample_rate;
+  UInt32 info_size = sizeof(nominal_sample_rate);
+
+  static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
+      kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal,
+      kAudioObjectPropertyElementMain};
+  OSStatus result =
+      AudioObjectGetPropertyData(device_id, &kNominalSampleRateAddress, 0, 0,
+                                 &info_size, &nominal_sample_rate);
+  if (result != noErr) {
+    OSSTATUS_DLOG(WARNING, result)
+        << "Could not get default sample rate for device: " << device_id
+        << ", returing fallback sample rate " << kFallbackSampleRate;
+    return kFallbackSampleRate;
+  }
+
+  return static_cast<int>(nominal_sample_rate);
+}
+
 // static
 AudioDeviceID AudioManagerMac::FindFirstOutputSubdevice(
     AudioDeviceID aggregate_device_id) {
diff --git a/media/audio/mac/audio_manager_mac.h b/media/audio/mac/audio_manager_mac.h
index 8761484..1183932 100644
--- a/media/audio/mac/audio_manager_mac.h
+++ b/media/audio/mac/audio_manager_mac.h
@@ -118,7 +118,10 @@
   // Returns the current muting state for the microphone.
   bool IsInputMuted(AudioDeviceID device_id) override;
 
-  static int HardwareSampleRateForDevice(AudioDeviceID device_id);
+  // Retrieves the current hardware sample rate associated with a specified
+  // device.
+  int HardwareSampleRateForDevice(AudioDeviceID device_id) override;
+
   static bool GetDefaultInputDevice(AudioDeviceID* input_device);
   static bool GetDefaultOutputDevice(AudioDeviceID* output_device);
   static AudioDeviceID GetAudioDeviceIdByUId(bool is_input,
diff --git a/testing/libfuzzer/fuzzing_browsertests.md b/testing/libfuzzer/fuzzing_browsertests.md
index 3bdce35..c898d972 100644
--- a/testing/libfuzzer/fuzzing_browsertests.md
+++ b/testing/libfuzzer/fuzzing_browsertests.md
@@ -22,7 +22,7 @@
 # Writing an in process fuzz case
 
 * Use the template `chrome/test/fuzzing/in_process_fuzzer.gni`
-* Provide a source code file which inherits from `InProcessFuzzTest`. This
+* Provide a source code file which inherits from `InProcessFuzzer`. This
   must override the `Fuzz` method. You'll find that your base class inherits
   from the full browser test infrastructure, so you can do anything you'd
   do in a normal Chrome browser test.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index eff7cc9b..f90261559 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1276,6 +1276,28 @@
             ]
         }
     ],
+    "AutofillEnableCacheForRegexMatching": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "chromeos_lacros",
+                "ios",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AutofillEnableCacheForRegexMatching"
+                    ]
+                }
+            ]
+        }
+    ],
     "AutofillEnableCardArtAndCardProductName": [
         {
             "platforms": [
@@ -2633,29 +2655,6 @@
             ]
         }
     ],
-    "BlinkSchedulerPrioritizeNavigationIPCs": [
-        {
-            "platforms": [
-                "android",
-                "android_weblayer",
-                "android_webview",
-                "chromeos",
-                "chromeos_lacros",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "BlinkSchedulerPrioritizeNavigationIPCs"
-                    ]
-                }
-            ]
-        }
-    ],
     "BlockMidiByDefault": [
         {
             "platforms": [
@@ -10127,21 +10126,6 @@
             ]
         }
     ],
-    "LanguagePacksBasePack": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "HandwritingLibraryDlc"
-                    ]
-                }
-            ]
-        }
-    ],
     "LauncherGameSearchStudy": [
         {
             "platforms": [
diff --git a/third_party/angle b/third_party/angle
index 40f4de8..d704273 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 40f4de8fa1ff252fd57552a6a1d5cf4067f83883
+Subproject commit d704273d78967f286c300d445bdc0eb9f43b2389
diff --git a/third_party/beto-core/BUILD.gn b/third_party/beto-core/BUILD.gn
index 960942f2..fe8724a7 100644
--- a/third_party/beto-core/BUILD.gn
+++ b/third_party/beto-core/BUILD.gn
@@ -4,21 +4,124 @@
 
 assert(is_chromeos, "beto-core is used by Nearby Presence which is CrOS only")
 
-import("//build/rust/cargo_crate.gni")
 import("//build/rust/rust_static_library.gni")
 import("//build/rust/rust_unit_test.gni")
 import("//build/rust/rust_unit_tests_group.gni")
 import("//testing/test.gni")
 
-executable("ldt_c_sample") {
-  sources = [ "src/nearby/presence/ldt_np_c_sample/main.c" ]
-  include_dirs = [ "src/nearby/presence/ldt_np_adv_ffi/include" ]
-  deps = [ ":ldt_np_adv_ffi" ]
+test("np_cpp_tests") {
+  sources = [
+    "src/nearby/presence/np_cpp_ffi/shared/shared_test_util.cc",
+    "src/nearby/presence/np_cpp_ffi/tests/byte_buffer_tests.cc",
+    "src/nearby/presence/np_cpp_ffi/tests/credential_book_tests.cc",
+    "src/nearby/presence/np_cpp_ffi/tests/credential_slab_tests.cc",
+    "src/nearby/presence/np_cpp_ffi/tests/deserialize_result_tests.cc",
+    "src/nearby/presence/np_cpp_ffi/tests/deserialize_v0_tests.cc",
+    "src/nearby/presence/np_cpp_ffi/tests/deserialize_v1_tests.cc",
+    "src/nearby/presence/np_cpp_ffi/tests/np_cpp_test.cc",
+    "src/nearby/presence/np_cpp_ffi/tests/np_cpp_test.h",
+  ]
+  include_dirs = [
+    "src/nearby/presence/np_cpp_ffi/include",
+    "src/nearby/presence/np_c_ffi/include/cpp",
+    "src/nearby/presence/np_cpp_ffi/shared",
+  ]
+  deps = [
+    ":nearby_protocol",
+    "//base/test:run_all_unittests",
+    "//testing/gtest",
+  ]
 }
 
-cargo_crate("np_adv") {
-  build_native_rust_unit_tests = false
-  crate_type = "rlib"
+executable("np_rust_sample") {
+  sources = [ "src/nearby/presence/np_cpp_ffi/sample/main.cc" ]
+  include_dirs = [
+    "src/nearby/presence/np_cpp_ffi/include",
+    "src/nearby/presence/np_c_ffi/include/cpp",
+  ]
+  deps = [ ":nearby_protocol" ]
+}
+
+source_set("nearby_protocol") {
+  sources = [ "src/nearby/presence/np_cpp_ffi/nearby_protocol.cc" ]
+  public = [
+    "src/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_types.h",
+    "src/nearby/presence/np_cpp_ffi/include/nearby_protocol.h",
+  ]
+  include_dirs = [ "src/nearby/presence/np_c_ffi/include/cpp" ]
+  public_deps = [ "//third_party/abseil-cpp:absl" ]
+  deps = [ ":np_c_ffi" ]
+}
+
+rust_static_library("np_c_ffi") {
+  crate_root = "src/nearby/presence/np_c_ffi/src/lib.rs"
+  sources = [
+    "src/nearby/presence/np_c_ffi/src/credentials.rs",
+    "src/nearby/presence/np_c_ffi/src/deserialize/mod.rs",
+    "src/nearby/presence/np_c_ffi/src/deserialize/v0.rs",
+    "src/nearby/presence/np_c_ffi/src/deserialize/v1.rs",
+    "src/nearby/presence/np_c_ffi/src/lib.rs",
+  ]
+  deps = [
+    ":lock_adapter",
+    ":np_ffi_core",
+  ]
+  features = [ "boringssl" ]
+  allow_unsafe = true
+}
+
+rust_static_library("np_ffi_core") {
+  crate_root = "src/nearby/presence/np_ffi_core/src/lib.rs"
+  sources = [
+    "src/nearby/presence/np_ffi_core/src/common.rs",
+    "src/nearby/presence/np_ffi_core/src/credentials.rs",
+    "src/nearby/presence/np_ffi_core/src/deserialize/mod.rs",
+    "src/nearby/presence/np_ffi_core/src/deserialize/v0.rs",
+    "src/nearby/presence/np_ffi_core/src/deserialize/v1.rs",
+    "src/nearby/presence/np_ffi_core/src/lib.rs",
+    "src/nearby/presence/np_ffi_core/src/utils.rs",
+  ]
+  deps = [
+    ":array_view",
+    ":crypto_provider",
+    ":crypto_provider_default",
+    ":handle_map",
+    ":ldt_np_adv",
+    ":lock_adapter",
+    ":np_adv",
+    ":np_hkdf",
+    "//third_party/rust/lazy_static/v1:lib",
+  ]
+  features = [ "boringssl" ]
+}
+
+rust_static_library("handle_map") {
+  build_native_rust_unit_tests = true
+  crate_root = "src/nearby/util/handle_map/src/lib.rs"
+  sources = [
+    "src/nearby/util/handle_map/src/declare_handle_map.rs",
+    "src/nearby/util/handle_map/src/guard.rs",
+    "src/nearby/util/handle_map/src/lib.rs",
+    "src/nearby/util/handle_map/src/shard.rs",
+    "src/nearby/util/handle_map/src/tests.rs",
+  ]
+  deps = [ ":lock_adapter" ]
+}
+
+rust_static_library("lock_adapter") {
+  build_native_rust_unit_tests = true
+  crate_root = "src/nearby/util/lock_adapter/src/lib.rs"
+  sources = [
+    "src/nearby/util/lock_adapter/src/lib.rs",
+    "src/nearby/util/lock_adapter/src/spin.rs",
+    "src/nearby/util/lock_adapter/src/std.rs",
+  ]
+  features = [ "std" ]
+}
+
+rust_static_library("np_adv") {
+  build_native_rust_unit_tests = true
+
   crate_root = "src/nearby/presence/np_adv/src/lib.rs"
   sources = [
     "src/nearby/presence/np_adv/src/array_vec.rs",
@@ -85,11 +188,48 @@
     "//third_party/rust/strum_macros/v0_25:lib",
     "//third_party/rust/tinyvec/v1:lib",
   ]
+  test_deps = [
+    ":crypto_provider_default",
+    ":rand_ext",
+    ":test_helper",
+    "//third_party/rust/anyhow/v1:lib",
+    "//third_party/rust/hex/v0_4:lib",
+    "//third_party/rust/rand/v0_8:lib",
+    "//third_party/rust/serde_json/v1:lib",
+  ]
+  features = [ "alloc" ]
 }
 
-cargo_crate("np_ed25519") {
-  crate_type = "rlib"
-  build_native_rust_unit_tests = false
+rust_unit_test("np_adv_examples_v0") {
+  crate_root = "src/nearby/presence/np_adv/tests/examples_v0.rs"
+  sources = [ "src/nearby/presence/np_adv/tests/examples_v0.rs" ]
+  deps = [
+    ":crypto_provider",
+    ":crypto_provider_default",
+    ":ldt_np_adv",
+    ":np_adv",
+    ":np_hkdf",
+    "//third_party/rust/serde/v1:lib",
+    "//third_party/rust/serde_json/v1:lib",
+  ]
+}
+
+rust_unit_test("np_adv_examples_v1") {
+  crate_root = "src/nearby/presence/np_adv/tests/examples_v1.rs"
+  sources = [ "src/nearby/presence/np_adv/tests/examples_v1.rs" ]
+  deps = [
+    ":crypto_provider",
+    ":crypto_provider_default",
+    ":ldt_np_adv",
+    ":np_adv",
+    ":np_ed25519",
+    ":np_hkdf",
+    "//third_party/rust/serde/v1:lib",
+    "//third_party/rust/serde_json/v1:lib",
+  ]
+}
+
+rust_static_library("np_ed25519") {
   crate_root = "src/nearby/presence/np_ed25519/src/lib.rs"
   sources = [ "src/nearby/presence/np_ed25519/src/lib.rs" ]
   deps = [
@@ -98,14 +238,20 @@
     ":sink",
     "//third_party/rust/tinyvec/v1:lib",
   ]
+  features = [ "std" ]
 }
 
-cargo_crate("sink") {
-  crate_type = "rlib"
-  build_native_rust_unit_tests = false
+rust_static_library("sink") {
   crate_root = "src/nearby/presence/sink/src/lib.rs"
   sources = [ "src/nearby/presence/sink/src/lib.rs" ]
   deps = [ "//third_party/rust/tinyvec/v1:lib" ]
+  features = [ "std" ]
+}
+
+executable("ldt_c_sample") {
+  sources = [ "src/nearby/presence/ldt_np_c_sample/main.c" ]
+  include_dirs = [ "src/nearby/presence/ldt_np_adv_ffi/include" ]
+  deps = [ ":ldt_np_adv_ffi" ]
 }
 
 test("ldt_ffi_tests") {
@@ -140,9 +286,9 @@
   ]
 }
 
-cargo_crate("crypto_provider_boringssl") {
+rust_static_library("crypto_provider_boringssl") {
   build_native_rust_unit_tests = true
-  crate_type = "rlib"
+
   crate_root = "src/nearby/crypto/crypto_provider_boringssl/src/lib.rs"
   sources = [
     "src/nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm.rs",
@@ -164,12 +310,10 @@
     ":crypto_provider_stubs",
     "//third_party/boringssl:bssl_crypto",
   ]
-  dev_deps = [ ":crypto_provider_test" ]
+  test_deps = [ ":crypto_provider_test" ]
 }
 
-cargo_crate("crypto_provider") {
-  build_native_rust_unit_tests = false
-  crate_type = "rlib"
+rust_static_library("crypto_provider") {
   crate_root = "src/nearby/crypto/crypto_provider/src/lib.rs"
   sources = [
     "src/nearby/crypto/crypto_provider/src/aead.rs",
@@ -193,10 +337,8 @@
   deps = [ "//third_party/rust/tinyvec/v1:lib" ]
 }
 
-cargo_crate("crypto_provider_test") {
-  build_native_rust_unit_tests = false
+rust_static_library("crypto_provider_test") {
   testonly = true
-  crate_type = "rlib"
   crate_root = "src/nearby/crypto/crypto_provider_test/src/lib.rs"
   sources = [ "src/nearby/crypto/crypto_provider_test/src/lib.rs" ]
   deps = [
@@ -212,18 +354,13 @@
   ]
 }
 
-cargo_crate("crypto_provider_stubs") {
-  build_native_rust_unit_tests = false
-  crate_type = "rlib"
+rust_static_library("crypto_provider_stubs") {
   crate_root = "src/nearby/crypto/crypto_provider_stubs/src/lib.rs"
   sources = [ "src/nearby/crypto/crypto_provider_stubs/src/lib.rs" ]
   deps = [ ":crypto_provider" ]
 }
 
-cargo_crate("crypto_provider_default") {
-  testonly = true
-  build_native_rust_unit_tests = false
-  crate_type = "rlib"
+rust_static_library("crypto_provider_default") {
   crate_root = "src/nearby/crypto/crypto_provider_default/src/lib.rs"
   sources = [ "src/nearby/crypto/crypto_provider_default/src/lib.rs" ]
   edition = "2021"
@@ -235,8 +372,7 @@
   features = [ "boringssl" ]
 }
 
-cargo_crate("ldt_np_adv") {
-  crate_type = "rlib"
+rust_static_library("ldt_np_adv") {
   build_native_rust_unit_tests = true
   crate_root = "src/nearby/presence/ldt_np_adv/src/lib.rs"
   sources = [ "src/nearby/presence/ldt_np_adv/src/lib.rs" ]
@@ -248,7 +384,7 @@
     ":np_hkdf",
     ":xts_aes",
   ]
-  dev_deps = [
+  test_deps = [
     ":crypto_provider_default",
     ":rand_ext",
     ":test_helper",
@@ -259,9 +395,7 @@
   ]
 }
 
-cargo_crate("ldt") {
-  crate_type = "rlib"
-  build_native_rust_unit_tests = false
+rust_static_library("ldt") {
   crate_root = "src/nearby/presence/ldt/src/lib.rs"
   sources = [ "src/nearby/presence/ldt/src/lib.rs" ]
   deps = [
@@ -322,31 +456,24 @@
   ]
 }
 
-cargo_crate("ldt_tbc") {
-  crate_type = "rlib"
-  build_native_rust_unit_tests = false
+rust_static_library("ldt_tbc") {
   crate_root = "src/nearby/presence/ldt_tbc/src/lib.rs"
   sources = [ "src/nearby/presence/ldt_tbc/src/lib.rs" ]
   deps = [ ":crypto_provider" ]
 }
 
-cargo_crate("array_view") {
-  crate_type = "rlib"
+rust_static_library("array_view") {
   build_native_rust_unit_tests = true
   crate_root = "src/nearby/presence/array_view/src/lib.rs"
   sources = [ "src/nearby/presence/array_view/src/lib.rs" ]
 }
 
-cargo_crate("array_ref") {
-  crate_type = "rlib"
-  build_native_rust_unit_tests = false
+rust_static_library("array_ref") {
   crate_root = "src/nearby/presence/array_ref/src/lib.rs"
   sources = [ "src/nearby/presence/array_ref/src/lib.rs" ]
 }
 
-cargo_crate("np_hkdf") {
-  crate_type = "rlib"
-  build_native_rust_unit_tests = false
+rust_static_library("np_hkdf") {
   crate_root = "src/nearby/presence/np_hkdf/src/lib.rs"
   sources = [
     "src/nearby/presence/np_hkdf/src/lib.rs",
@@ -375,8 +502,7 @@
   ]
 }
 
-cargo_crate("xts_aes") {
-  crate_type = "rlib"
+rust_static_library("xts_aes") {
   build_native_rust_unit_tests = true
   crate_root = "src/nearby/presence/xts_aes/src/lib.rs"
   sources = [ "src/nearby/presence/xts_aes/src/lib.rs" ]
@@ -385,7 +511,7 @@
     ":crypto_provider",
     ":ldt_tbc",
   ]
-  dev_deps = [
+  test_deps = [
     ":test_helper",
     "//third_party/rust/hex/v0_4:lib",
   ]
@@ -419,11 +545,8 @@
   ]
 }
 
-# rust test targets
-cargo_crate("test_helper") {
+rust_static_library("test_helper") {
   testonly = true
-  build_native_rust_unit_tests = false
-  crate_type = "rlib"
   crate_root = "src/nearby/presence/test_helper/src/lib.rs"
   sources = [ "src/nearby/presence/test_helper/src/lib.rs" ]
   edition = "2021"
@@ -434,11 +557,8 @@
   ]
 }
 
-# rust test targets
-cargo_crate("rand_ext") {
+rust_static_library("rand_ext") {
   testonly = true
-  build_native_rust_unit_tests = false
-  crate_type = "rlib"
   crate_root = "src/nearby/presence/rand_ext/src/lib.rs"
   sources = [ "src/nearby/presence/rand_ext/src/lib.rs" ]
   edition = "2021"
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index fcb7e4f..11cfff4 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -54,6 +54,12 @@
              "AdInterestGroupAPIRestrictedPolicyByDefault",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Make MediaDevicesDispatcherHost and ended MediaStreamTrack not block BFCache.
+// See https://chrbug.com/1502395 for more details.
+BASE_FEATURE(kAllowBFCacheWhenClosedMediaStreamTrack,
+             "AllowBFCacheWhenClosedMediaStreamTrack",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kComputePressureRateObfuscationMitigation,
              "ComputePressureRateObfuscationMitigation",
              base::FEATURE_ENABLED_BY_DEFAULT);
@@ -2271,6 +2277,11 @@
   return base::FeatureList::IsEnabled(blink::features::kAllowURNsInIframes);
 }
 
+bool IsAllowBFCacheWhenClosedMediaStreamTrackEnabled() {
+  return base::FeatureList::IsEnabled(
+      blink::features::kAllowBFCacheWhenClosedMediaStreamTrack);
+}
+
 bool IsFencedFramesEnabled() {
   return base::FeatureList::IsEnabled(blink::features::kFencedFrames);
 }
diff --git a/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc b/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
index 8fb17f0..d80a45af 100644
--- a/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
+++ b/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
@@ -134,6 +134,8 @@
       return {"WebSerial", "Serial port open"};
     case WebSchedulerTrackedFeature::kSmartCard:
       return {"SmartCard", "SmartCardContext used"};
+    case WebSchedulerTrackedFeature::kLiveMediaStreamTrack:
+      return {"LiveMediaStreamTrack", "page has live MediaStreamTrack"};
   }
   return {};
 }
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 4df0611..1a6905f 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -51,6 +51,9 @@
     kAdInterestGroupAPIRestrictedPolicyByDefault);
 
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(
+    kAllowBFCacheWhenClosedMediaStreamTrack);
+
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(
     kAlignFontDisplayAutoTimeoutWithLCPGoal);
 BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
     kAlignFontDisplayAutoTimeoutWithLCPGoalTimeoutParam;
@@ -147,7 +150,6 @@
 
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(
     kBackForwardCacheDWCOnJavaScriptExecution);
-
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kBackForwardCacheWithKeepaliveRequest);
 
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kBackgroundResourceFetch);
@@ -398,7 +400,6 @@
 BLINK_COMMON_EXPORT extern const base::FeatureParam<int> kDeprecateUnloadBucket;
 BLINK_COMMON_EXPORT extern const base::FeatureParam<std::string>
     kDeprecateUnloadAllowlist;
-
 // This feature (EventTimingReportAllEarlyEntriesOnPaintedPresentation) is
 // having an effect only when EventTimingMatchPresentationIndex is turned on.
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(
@@ -1468,6 +1469,8 @@
 // Helper functions for querying feature status. Please declare any features or
 // constants for features in the section above.
 
+BLINK_COMMON_EXPORT bool IsAllowBFCacheWhenClosedMediaStreamTrackEnabled();
+
 BLINK_COMMON_EXPORT int GetMaxUnthrottledTimeoutNestingLevel();
 
 // Checks both of kAllowPageWithIDBConnectionInBFCache and
diff --git a/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h b/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
index 8936826..1d3a82a 100644
--- a/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
+++ b/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
@@ -147,13 +147,14 @@
   // See comments for `kWebTransportSticky`.
   kWebSocketSticky = 63,
   kWebRTCSticky = 64,
-
   kSmartCard = 65,
+  // There is a "live" MediaStreamTrack.
+  kLiveMediaStreamTrack = 66,
 
   // Please keep in sync with WebSchedulerTrackedFeature in
   // tools/metrics/histograms/enums.xml. These values should not be renumbered.
 
-  kMaxValue = kSmartCard,
+  kMaxValue = kLiveMediaStreamTrack,
 };
 
 using WebSchedulerTrackedFeatures =
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index ff76494..447c534 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -8903,6 +8903,7 @@
       WebTransportSticky
       WebSocketSticky
       SmartCard
+      LiveMediaStreamTrack
       # Disabled for RenderFrameHost reasons
       # See content/browser/renderer_host/back_forward_cache_disable.h for explanations.
       ContentSecurityHandler
diff --git a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
index e497771..fd81b966 100644
--- a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
+++ b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
@@ -296,8 +296,6 @@
   // Use the global proxy as window wrapper object.
   V8DOMWrapper::SetNativeInfo(GetIsolate(), global_proxy, wrapper_type_info,
                               window);
-  // Mark the handle to be traced by Oilpan, since the global proxy has a
-  // reference to the DOMWindow.
   CHECK(global_proxy_ == window->AssociateWithWrapper(GetIsolate(), world_,
                                                       wrapper_type_info,
                                                       global_proxy));
diff --git a/third_party/blink/renderer/bindings/core/v8/remote_window_proxy.cc b/third_party/blink/renderer/bindings/core/v8/remote_window_proxy.cc
index 59439de..3caa238f 100644
--- a/third_party/blink/renderer/bindings/core/v8/remote_window_proxy.cc
+++ b/third_party/blink/renderer/bindings/core/v8/remote_window_proxy.cc
@@ -133,6 +133,8 @@
 
   // The global proxy object.  Note this is not the global object.
   v8::Local<v8::Object> global_proxy = global_proxy_.Get(GetIsolate());
+  V8DOMWrapper::SetNativeInfo(GetIsolate(), global_proxy, wrapper_type_info,
+                              window);
   CHECK(global_proxy == window->AssociateWithWrapper(GetIsolate(), world_,
                                                      wrapper_type_info,
                                                      global_proxy));
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc
index 38942bc..a308d63 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc
@@ -27,8 +27,10 @@
 
 #include <memory>
 
+#include "base/check_op.h"
 #include "base/functional/callback_helpers.h"
 #include "build/build_config.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/modules/mediastream/web_media_stream_track.h"
 #include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h"
@@ -416,6 +418,7 @@
 
   setReadyState(MediaStreamSource::kReadyStateEnded);
   feature_handle_for_scheduler_.reset();
+  feature_handle_for_scheduler_on_live_media_stream_track_.reset();
   UserMediaClient* user_media_client =
       UserMediaClient::From(To<LocalDOMWindow>(execution_context));
   if (user_media_client) {
@@ -857,6 +860,8 @@
       }
       PropagateTrackEnded();
       feature_handle_for_scheduler_.reset();
+      feature_handle_for_scheduler_on_live_media_stream_track_.reset();
+
       break;
   }
   SendLogMessage(String::Format("%s()", __func__));
@@ -1062,9 +1067,18 @@
 }
 
 void MediaStreamTrackImpl::EnsureFeatureHandleForScheduler() {
+  // The two handlers must be in sync.
+  if (features::IsAllowBFCacheWhenClosedMediaStreamTrackEnabled()) {
+    CHECK_EQ(!!feature_handle_for_scheduler_,
+             !!feature_handle_for_scheduler_on_live_media_stream_track_);
+  } else {
+    CHECK(!feature_handle_for_scheduler_on_live_media_stream_track_);
+  }
+
   if (feature_handle_for_scheduler_) {
     return;
   }
+
   LocalDOMWindow* window = DynamicTo<LocalDOMWindow>(GetExecutionContext());
   // Ideally we'd use To<LocalDOMWindow>, but in unittests the ExecutionContext
   // may not be a LocalDOMWindow.
@@ -1080,6 +1094,12 @@
           SchedulingPolicy::Feature::kWebRTC,
           {SchedulingPolicy::DisableAggressiveThrottling(),
            SchedulingPolicy::DisableAlignWakeUps()});
+  if (features::IsAllowBFCacheWhenClosedMediaStreamTrackEnabled()) {
+    feature_handle_for_scheduler_on_live_media_stream_track_ =
+        GetExecutionContext()->GetScheduler()->RegisterFeature(
+            SchedulingPolicy::Feature::kLiveMediaStreamTrack,
+            {SchedulingPolicy::DisableBackForwardCache()});
+  }
 }
 
 void MediaStreamTrackImpl::AddObserver(MediaStreamTrack::Observer* observer) {
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.h b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.h
index b6ebe5c..fa8c3c2 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.h
@@ -201,6 +201,17 @@
   FrameScheduler::SchedulingAffectingFeatureHandle
       feature_handle_for_scheduler_;
 
+  // This handle notifies the scheduler about a live media stream track
+  // for the purpose of disabling/enabling BFCache. When there is a live stream
+  // track, the page should not be BFCached.
+  // TODO(crbug.com/1502395): Currently we intentionally use this handler for
+  // BFCache although its behavior is almost the same as the one above. The one
+  // above uses the WebRTC feature even though it's not necessarily related to
+  // Web RTC. Discuss with those who own the handler and merge the two handlers
+  // into one.
+  FrameScheduler::SchedulingAffectingFeatureHandle
+      feature_handle_for_scheduler_on_live_media_stream_track_;
+
   MediaStreamSource::ReadyState ready_state_;
   HeapHashSet<Member<MediaStream>> registered_media_streams_;
   bool is_iterating_registered_media_streams_ = false;
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.h b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.h
index 4dabeb70..be9e4211 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.h
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.h
@@ -45,6 +45,7 @@
 MLOperand* BuildArgMinMax(
     V8TestingScope& scope,
     MLGraphBuilder* builder,
+    ArgMinMaxKind kind,
     const MLOperand* input,
     const MLArgMinMaxOptions* options = MLArgMinMaxOptions::Create());
 
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_test_mojo.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_test_mojo.cc
index 6f8d480..ecc370a5 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_test_mojo.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_test_mojo.cc
@@ -5059,6 +5059,192 @@
   }
 }
 
+struct ArgMinMaxTester {
+  OperandInfoBlink input;
+  absl::optional<Vector<uint32_t>> axes;
+  absl::optional<bool> keep_dimensions;
+  absl::optional<bool> select_last_index;
+  OperandInfoMojo expected_input;
+  OperandInfoMojo expected_output;
+  Vector<uint32_t> expected_axes;
+  bool expected_keep_dimensions;
+  bool expected_select_last_index;
+
+  void Test(MLGraphTestMojo& helper,
+            V8TestingScope& scope,
+            MLGraphBuilder* builder) {
+    Test(helper, scope, builder, ArgMinMaxKind::kArgMin);
+    Test(helper, scope, builder, ArgMinMaxKind::kArgMax);
+  }
+
+  void Test(MLGraphTestMojo& helper,
+            V8TestingScope& scope,
+            MLGraphBuilder* builder,
+            ArgMinMaxKind kind) {
+    // Build the graph.
+    auto* input_operand =
+        BuildInput(builder, "input", input.dimensions, input.data_type,
+                   scope.GetExceptionState());
+    auto* options = MLArgMinMaxOptions::Create();
+    if (axes.has_value()) {
+      options->setAxes(axes.value());
+    }
+    if (keep_dimensions.has_value()) {
+      options->setKeepDimensions(keep_dimensions.value());
+    }
+    if (select_last_index.has_value()) {
+      options->setSelectLastIndex(select_last_index.value());
+    }
+    auto* output_operand =
+        BuildArgMinMax(scope, builder, kind, input_operand, options);
+    auto [graph, build_exception] =
+        helper.BuildGraph(scope, builder, {{"output", output_operand}});
+    ASSERT_NE(graph, nullptr);
+
+    auto graph_info = helper.GetGraphInfo();
+    // Verify the graph information of mojo are as expected.
+    ASSERT_EQ(graph_info->operations.size(), 1u);
+    auto& operation = graph_info->operations[0];
+    ASSERT_TRUE(operation->is_arg_min_max());
+    auto& argminmax = operation->get_arg_min_max();
+
+    blink_mojom::ArgMinMax::Kind mojom_kind;
+    switch (kind) {
+      case ArgMinMaxKind::kArgMin:
+        mojom_kind = blink_mojom::ArgMinMax::Kind::kMin;
+        break;
+      case ArgMinMaxKind::kArgMax:
+        mojom_kind = blink_mojom::ArgMinMax::Kind::kMax;
+        break;
+    }
+    EXPECT_EQ(argminmax->kind, mojom_kind);
+    // Validate the axes of ArgMinMax operation.
+    EXPECT_EQ(argminmax->axes, expected_axes);
+    // Validate the keep_dimensions of ArgMinMax operation.
+    EXPECT_EQ(argminmax->keep_dimensions, expected_keep_dimensions);
+    // Validate the select_last_index of ArgMinMax operation.
+    EXPECT_EQ(argminmax->select_last_index, expected_select_last_index);
+
+    // Validate the input operand.
+    EXPECT_EQ(graph_info->input_operands.size(), 1u);
+    auto input_operand_id = graph_info->input_operands[0];
+    EXPECT_EQ(argminmax->input_operand_id, input_operand_id);
+    auto input_operand_iter =
+        graph_info->id_to_operand_map.find(input_operand_id);
+    ASSERT_TRUE(input_operand_iter != graph_info->id_to_operand_map.end());
+    EXPECT_EQ(input_operand_iter->value->data_type, expected_input.data_type);
+    EXPECT_EQ(input_operand_iter->value->dimensions, expected_input.dimensions);
+
+    // Validate the output operand.
+    EXPECT_EQ(graph_info->output_operands.size(), 1u);
+    auto output_operand_id = graph_info->output_operands[0];
+    EXPECT_EQ(argminmax->output_operand_id, output_operand_id);
+    auto output_operand_iter =
+        graph_info->id_to_operand_map.find(output_operand_id);
+    ASSERT_TRUE(output_operand_iter != graph_info->id_to_operand_map.end());
+    EXPECT_EQ(output_operand_iter->value->data_type, expected_output.data_type);
+    EXPECT_EQ(output_operand_iter->value->dimensions,
+              expected_output.dimensions);
+  }
+};
+
+TEST_P(MLGraphTestMojo, ArgMinMaxTest) {
+  V8TestingScope scope;
+  // Bind fake WebNN Context in the service for testing.
+  ScopedWebNNServiceBinder scoped_setup_binder(*this, scope);
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      webnn::features::kWebMachineLearningNeuralNetwork);
+  auto* options = MLContextOptions::Create();
+  // Create WebNN Context with GPU device type.
+  options->setDeviceType(V8MLDeviceType::Enum::kGpu);
+  auto* builder = CreateGraphBuilder(scope, options);
+  ASSERT_NE(builder, nullptr);
+  {
+    // Test argMinMax with default options.
+    ArgMinMaxTester{
+        .input = {.data_type = V8MLOperandDataType::Enum::kFloat32,
+                  .dimensions = {1, 2, 3, 4}},
+        .expected_input = {.data_type =
+                               blink_mojom::Operand::DataType::kFloat32,
+                           .dimensions = {1, 2, 3, 4}},
+        .expected_output = {.data_type = blink_mojom::Operand::DataType::kInt64,
+                            .dimensions = {}},
+        .expected_axes = {0, 1, 2, 3},
+        .expected_keep_dimensions = false,
+        .expected_select_last_index = false}
+        .Test(*this, scope, builder);
+  }
+  {
+    // Test argMinMax with axes = {}.
+    ArgMinMaxTester{
+        .input = {.data_type = V8MLOperandDataType::Enum::kFloat32,
+                  .dimensions = {1, 2, 3, 4}},
+        .axes = Vector<uint32_t>{},
+        .expected_input = {.data_type =
+                               blink_mojom::Operand::DataType::kFloat32,
+                           .dimensions = {1, 2, 3, 4}},
+        .expected_output = {.data_type = blink_mojom::Operand::DataType::kInt64,
+                            .dimensions = {1, 2, 3, 4}},
+        .expected_axes = {},
+        .expected_keep_dimensions = false,
+        .expected_select_last_index = false}
+        .Test(*this, scope, builder);
+  }
+  {
+    // Test argMinMax with axes = {1}.
+    ArgMinMaxTester{
+        .input = {.data_type = V8MLOperandDataType::Enum::kFloat32,
+                  .dimensions = {1, 2, 3, 4}},
+        .axes = Vector<uint32_t>{1},
+        .expected_input = {.data_type =
+                               blink_mojom::Operand::DataType::kFloat32,
+                           .dimensions = {1, 2, 3, 4}},
+        .expected_output = {.data_type = blink_mojom::Operand::DataType::kInt64,
+                            .dimensions = {1, 3, 4}},
+        .expected_axes = {1},
+        .expected_keep_dimensions = false,
+        .expected_select_last_index = false}
+        .Test(*this, scope, builder);
+  }
+  {
+    // Test argMinMax with axes = {1, 3} and keepDimensions = true.
+    ArgMinMaxTester{
+        .input = {.data_type = V8MLOperandDataType::Enum::kFloat32,
+                  .dimensions = {1, 2, 3, 4}},
+        .axes = Vector<uint32_t>{1, 3},
+        .keep_dimensions = true,
+        .expected_input = {.data_type =
+                               blink_mojom::Operand::DataType::kFloat32,
+                           .dimensions = {1, 2, 3, 4}},
+        .expected_output = {.data_type = blink_mojom::Operand::DataType::kInt64,
+                            .dimensions = {1, 1, 3, 1}},
+        .expected_axes = {1, 3},
+        .expected_keep_dimensions = true,
+        .expected_select_last_index = false}
+        .Test(*this, scope, builder);
+  }
+  {
+    // Test argMinMax with axes = {1, 3}, keepDimensions = true and and
+    // selectLastIndex = true.
+    ArgMinMaxTester{
+        .input = {.data_type = V8MLOperandDataType::Enum::kFloat32,
+                  .dimensions = {1, 2, 3, 4}},
+        .axes = Vector<uint32_t>{1, 3},
+        .keep_dimensions = true,
+        .select_last_index = true,
+        .expected_input = {.data_type =
+                               blink_mojom::Operand::DataType::kFloat32,
+                           .dimensions = {1, 2, 3, 4}},
+        .expected_output = {.data_type = blink_mojom::Operand::DataType::kInt64,
+                            .dimensions = {1, 1, 3, 1}},
+        .expected_axes = {1, 3},
+        .expected_keep_dimensions = true,
+        .expected_select_last_index = true}
+        .Test(*this, scope, builder);
+  }
+}
+
 TEST_P(MLGraphTestMojo, WebNNGraphComputeTest) {
   V8TestingScope scope;
   // Bind fake WebNN Context in the service for testing.
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_type_converter.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_type_converter.cc
index e0707905..2bb226e 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_type_converter.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_type_converter.cc
@@ -256,7 +256,7 @@
       static_cast<const blink::MLArgMinMaxOptions*>(arg_min_max->Options());
   CHECK(options);
   const auto input_rank = arg_min_max->Inputs()[0]->Dimensions().size();
-  const auto axes = options->axes();
+  const auto axes = options->getAxesOr(CreateAllAxes(input_rank));
   CHECK_LE(axes.size(), input_rank);
   arg_min_max_mojo->axes = axes;
   arg_min_max_mojo->keep_dimensions = options->keepDimensions();
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index cbc474a..17f9801 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -49,10 +49,8 @@
 #include "components/viz/common/resources/transferable_resource.h"
 #include "gpu/command_buffer/client/client_shared_image.h"
 #include "gpu/command_buffer/client/context_support.h"
-#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
 #include "gpu/command_buffer/client/shared_image_interface.h"
 #include "gpu/command_buffer/common/capabilities.h"
-#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/config/gpu_driver_bug_workaround_type.h"
 #include "gpu/config/gpu_feature_info.h"
@@ -827,7 +825,6 @@
     SkAlphaType alpha_type,
     GLenum texture_target,
     GLuint texture_id,
-    std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer,
     bool is_overlay_candidate,
     gpu::Mailbox mailbox)
     : owning_thread_ref(base::PlatformThread::CurrentRef()),
@@ -838,7 +835,6 @@
       alpha_type(alpha_type),
       texture_target(texture_target),
       texture_id(texture_id),
-      gpu_memory_buffer(std::move(gpu_memory_buffer)),
       is_overlay_candidate(is_overlay_candidate),
       mailbox(mailbox) {}
 
@@ -873,7 +869,6 @@
   }
 
   sii->DestroySharedImage(receive_sync_token, mailbox);
-  gpu_memory_buffer.reset();
   gl->DeleteTextures(1u, &texture_id);
 }
 
@@ -1893,15 +1888,13 @@
   state_restorer_->SetTextureBindingDirty();
 
   gpu::SharedImageInterface* sii = ContextProvider()->SharedImageInterface();
-  gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager =
-      Platform::Current()->GetGpuMemoryBufferManager();
 
   gpu::Mailbox back_buffer_mailbox;
   // Set only when using swap chains.
   gpu::Mailbox front_buffer_mailbox;
   GLenum texture_target = GL_TEXTURE_2D;
   GLuint texture_id = 0;
-  std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
+  bool created_mappable_si = false;
   uint32_t usage = gpu::SHARED_IMAGE_USAGE_GLES2_READ |
                    gpu::SHARED_IMAGE_USAGE_GLES2_WRITE |
                    gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT |
@@ -1973,28 +1966,23 @@
               viz::SinglePlaneSharedImageFormatToBufferFormat(
                   color_buffer_format_),
               ContextProvider()->GetCapabilities())) {
-        gpu_memory_buffer = gpu_memory_buffer_manager->CreateGpuMemoryBuffer(
-            size,
-            viz::SinglePlaneSharedImageFormatToBufferFormat(
-                color_buffer_format_),
-            buffer_usage, gpu::kNullSurfaceHandle, nullptr);
-        if (gpu_memory_buffer) {
-          auto client_shared_image = sii->CreateSharedImage(
-              color_buffer_format_, size, color_space_, origin,
-              back_buffer_alpha_type, usage | additional_usage_flags,
-              "WebGLDrawingBuffer", gpu_memory_buffer->CloneHandle());
+        auto client_shared_image = sii->CreateSharedImage(
+            color_buffer_format_, size, color_space_, origin,
+            back_buffer_alpha_type, usage | additional_usage_flags,
+            "WebGLDrawingBuffer", gpu::kNullSurfaceHandle, buffer_usage);
+        if (client_shared_image) {
+          created_mappable_si = true;
 #if BUILDFLAG(IS_MAC)
           // Ensure that the backing IOSurface has its color space set to be the
           // same as that of the just-created SharedImage (the former is used by
           // CoreAnimation, while the latter is used by viz).
           // TODO(crbug.com/924198): Explore moving to a CreateSharedImage()
           // codepath that sets the color space of the IOSurface on the service
-          // side and eliminating the usage of GMB here altogether. Will require
-          // resolving issues with low-latency canvas tests that caused prior
-          // attempts to be reverted (crbug.com/1346737).
-          gpu_memory_buffer->SetColorSpace(color_space_);
+          // side and eliminating the usage of MappableSI here altogether. Will
+          // require resolving issues with low-latency canvas tests that caused
+          // prior attempts to be reverted (crbug.com/1346737).
+          client_shared_image->SetColorSpaceOnNativeBuffer(color_space_);
 #endif
-          CHECK(client_shared_image);
           back_buffer_mailbox = client_shared_image->mailbox();
 #if BUILDFLAG(IS_MAC)
           // A CHROMIUM_image backed texture requires a specialized set of
@@ -2005,9 +1993,9 @@
       }
     }
 
-    // Create a normal SharedImage if GpuMemoryBuffer is not needed or the
+    // Create a normal SharedImage if Mappable SharedImage is not needed or the
     // allocation above failed.
-    if (!gpu_memory_buffer) {
+    if (!created_mappable_si) {
       // We want to set the correct SkAlphaType on the new shared image but in
       // the case of ShouldUseChromiumImage() we instead keep this buffer
       // premultiplied, draw to |premultiplied_alpha_false_mailbox_|, and
@@ -2049,7 +2037,7 @@
         front_buffer_mailbox.name);
     front_color_buffer_ = base::MakeRefCounted<ColorBuffer>(
         weak_factory_.GetWeakPtr(), size, color_space_, color_buffer_format_,
-        back_buffer_alpha_type, texture_target, texture_id, nullptr,
+        back_buffer_alpha_type, texture_target, texture_id,
         /*is_overlay_candidate=*/true, front_buffer_mailbox);
   }
 
@@ -2077,12 +2065,12 @@
                               texture_target, 0, 0);
     gl_->DeleteFramebuffers(1, &fbo);
   }
-  const bool is_overlay_candidate = !!gpu_memory_buffer || using_swap_chain_;
+  const bool is_overlay_candidate = created_mappable_si || using_swap_chain_;
 
   return base::MakeRefCounted<ColorBuffer>(
       weak_factory_.GetWeakPtr(), size, color_space_, color_buffer_format_,
-      back_buffer_alpha_type, texture_target, texture_id,
-      std::move(gpu_memory_buffer), is_overlay_candidate, back_buffer_mailbox);
+      back_buffer_alpha_type, texture_target, texture_id, is_overlay_candidate,
+      back_buffer_mailbox);
 }
 
 void DrawingBuffer::AttachColorBufferToReadFramebuffer() {
@@ -2165,8 +2153,7 @@
 
 bool DrawingBuffer::ShouldUseChromiumImage() {
   return RuntimeEnabledFeatures::WebGLImageChromiumEnabled() &&
-         chromium_image_usage_ == kAllowChromiumImage &&
-         Platform::Current()->GetGpuMemoryBufferManager();
+         chromium_image_usage_ == kAllowChromiumImage;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
index 93ac96a..f504d97 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
@@ -67,10 +67,6 @@
 class Layer;
 }
 
-namespace gfx {
-class GpuMemoryBuffer;
-}
-
 namespace gpu {
 namespace gles2 {
 class GLES2Interface;
@@ -428,7 +424,6 @@
                 SkAlphaType alpha_type,
                 GLenum texture_target,
                 GLuint texture_id,
-                std::unique_ptr<gfx::GpuMemoryBuffer>,
                 bool is_overlay_candidate,
                 gpu::Mailbox mailbox);
     ColorBuffer(const ColorBuffer&) = delete;
@@ -449,7 +444,6 @@
     const SkAlphaType alpha_type;
     const GLenum texture_target;
     const GLuint texture_id;
-    std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
     const bool is_overlay_candidate;
 
     // The mailbox used to send this buffer to the compositor.
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
index def4774..d343f44 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
@@ -43,7 +43,6 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h"
-#include "third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "ui/gl/gpu_preference.h"
 #include "v8/include/v8.h"
@@ -352,13 +351,18 @@
 
  protected:
   void SetUp() override {
-    platform_ = std::make_unique<
-        ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform>>();
-
     gfx::Size initial_size(kInitialWidth, kInitialHeight);
     auto gl = std::make_unique<GLES2InterfaceForTests>();
     auto provider =
         std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
+
+    // DrawingBuffer requests MappableSharedImages with usage SCANOUT, whereas
+    // TestSII by default creates backing SharedMemory GMBs that don't support
+    // this usage. Configure the TestSII to instead use test GMBs that have
+    // relaxed usage validation.
+    auto* sii = static_cast<viz::TestSharedImageInterface*>(
+        provider->SharedImageInterface());
+    sii->UseTestGMBInSharedImageCreationWithBufferUsage();
     GLES2InterfaceForTests* gl_ =
         static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
     EXPECT_CALL(*gl_, CreateAndTexStorage2DSharedImageCHROMIUMMock(_)).Times(1);
@@ -372,13 +376,7 @@
     testing::Mock::VerifyAndClearExpectations(gl_);
   }
 
-  void TearDown() override {
-    platform_.reset();
-  }
-
   GLuint image_id0_;
-  std::unique_ptr<ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform>>
-      platform_;
 };
 
 TEST_F(DrawingBufferImageChromiumTest, VerifyResizingReallocatesImages) {
@@ -512,9 +510,6 @@
 
 TEST_F(DrawingBufferImageChromiumTest, AllocationFailure) {
   GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
-  viz::TestGpuMemoryBufferManager* gmb_manager =
-      static_cast<viz::TestGpuMemoryBufferManager*>(
-          Platform::Current()->GetGpuMemoryBufferManager());
   viz::TestSharedImageInterface* sii =
       drawing_buffer_->SharedImageInterfaceForTests();
 
@@ -538,10 +533,10 @@
   testing::Mock::VerifyAndClearExpectations(gl_);
   VerifyStateWasRestored();
 
-  // Force GpuMemoryBuffer creation failure. Request another resource. It should
+  // Force MappableSI creation failure. Request another resource. It should
   // still be provided, but this time with allowOverlay = false.
   EXPECT_CALL(*gl_, CreateAndTexStorage2DSharedImageCHROMIUMMock(_)).Times(1);
-  gmb_manager->SetFailOnCreate(true);
+  sii->SetFailSharedImageCreationWithBufferUsage(true);
   EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
                                                            &release_callback2));
@@ -551,10 +546,10 @@
   EXPECT_TRUE(sii->CheckSharedImageExists(mailbox2));
   VerifyStateWasRestored();
 
-  // Check that if GpuMemoryBuffer allocation starts working again, resources
+  // Check that if MappableSI creation starts working again, resources
   // are correctly created with allowOverlay = true.
   EXPECT_CALL(*gl_, CreateAndTexStorage2DSharedImageCHROMIUMMock(_)).Times(1);
-  gmb_manager->SetFailOnCreate(false);
+  sii->SetFailSharedImageCreationWithBufferUsage(false);
   EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
   EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
                                                            &release_callback3));
diff --git a/third_party/blink/renderer/platform/graphics/image_to_buffer_copier.cc b/third_party/blink/renderer/platform/graphics/image_to_buffer_copier.cc
index 3414f9d..99b9a75 100644
--- a/third_party/blink/renderer/platform/graphics/image_to_buffer_copier.cc
+++ b/third_party/blink/renderer/platform/graphics/image_to_buffer_copier.cc
@@ -35,13 +35,14 @@
 
     dest_image_size_ = size;
 
+    // We copy the contents of the source image into the destination SharedImage
+    // via GL, followed by giving out the destination SharedImage's native
+    // buffer handle to eventually be read by the display compositor.
     dest_shared_image_ = sii_->CreateSharedImage(
         viz::SinglePlaneFormat::kRGBA_8888, size, gfx::ColorSpace(),
         kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
-        gpu::SHARED_IMAGE_USAGE_GLES2_READ |
-            gpu::SHARED_IMAGE_USAGE_GLES2_WRITE,
-        "ImageToBufferCopier", gpu::kNullSurfaceHandle,
-        gfx::BufferUsage::SCANOUT);
+        gpu::SHARED_IMAGE_USAGE_GLES2_WRITE, "ImageToBufferCopier",
+        gpu::kNullSurfaceHandle, gfx::BufferUsage::SCANOUT);
     CHECK(dest_shared_image_);
     gl_->WaitSyncTokenCHROMIUM(sii_->GenUnverifiedSyncToken().GetConstData());
   }
diff --git a/third_party/blink/renderer/platform/text/text_break_iterator.cc b/third_party/blink/renderer/platform/text/text_break_iterator.cc
index 6e5766cf..55bcf81f 100644
--- a/third_party/blink/renderer/platform/text/text_break_iterator.cc
+++ b/third_party/blink/renderer/platform/text/text_break_iterator.cc
@@ -293,53 +293,6 @@
   return ch > kAsciiLineBreakTableLastChar && ch != kNoBreakSpaceCharacter;
 }
 
-template <typename CharacterType>
-struct LazyLineBreakIterator::Context {
-  STACK_ALLOCATED();
-
- public:
-  struct ContextChar {
-    STACK_ALLOCATED();
-
-   public:
-    ContextChar() = default;
-    explicit ContextChar(UChar ch) : ch(ch), is_space(IsBreakableSpace(ch)) {}
-
-    UChar ch = 0;
-    bool is_space = false;
-  };
-
-  Context(const CharacterType* str, int len, unsigned start_offset, int index) {
-    CHECK_GE(index, 0);
-    DCHECK_GE(static_cast<unsigned>(index), start_offset);
-    CHECK_LE(index, len);
-    if (index > 0) {
-      last = ContextChar(str[index - 1]);
-      if (index > 1) {
-        last_last_ch = str[index - 2];
-      }
-    }
-  }
-
-  bool Fetch(const CharacterType* str, int len, int index) {
-    if (UNLIKELY(index >= len)) {
-      return false;
-    }
-    current = ContextChar(str[index]);
-    return true;
-  }
-
-  void Advance(int& index) {
-    ++index;
-    last_last_ch = last.ch;
-    last = current;
-  }
-
-  ContextChar current;
-  ContextChar last;
-  CharacterType last_last_ch;
-};
-
 template <typename CharacterType,
           LineBreakType lineBreakType,
           BreakSpaceType break_space>
@@ -347,60 +300,57 @@
     int pos,
     const CharacterType* str,
     int len) const {
-  Context<CharacterType> context(str, len, start_offset_, pos);
+  CHECK_GE(pos, 0);
+  DCHECK_GE(static_cast<unsigned>(pos), start_offset_);
+  CHECK_LE(pos, len);
   int next_break = -1;
+  UChar last_last_ch = pos > 1 ? str[pos - 2] : 0;
+  UChar last_ch = pos > 0 ? str[pos - 1] : 0;
+  bool is_last_space = IsBreakableSpace(last_ch);
   ULineBreak last_line_break;
-  if (lineBreakType == LineBreakType::kBreakAll) {
-    last_line_break =
-        LineBreakPropertyValue(context.last_last_ch, context.last.ch);
-  }
-  for (int i = pos; context.Fetch(str, len, i); context.Advance(i)) {
+  if (lineBreakType == LineBreakType::kBreakAll)
+    last_line_break = LineBreakPropertyValue(last_last_ch, last_ch);
+  CharacterType ch;
+  bool is_space;
+  for (int i = pos; i < len;
+       i++, last_last_ch = last_ch, last_ch = ch, is_last_space = is_space) {
+    ch = str[i];
+
+    is_space = IsBreakableSpace(ch);
     switch (break_space) {
       case BreakSpaceType::kAfterSpaceRun:
-        if (context.current.is_space) {
+        if (is_space)
           continue;
-        }
-        if (context.last.is_space) {
+        if (is_last_space)
           return i;
-        }
         break;
       case BreakSpaceType::kAfterEverySpace:
-        if (context.last.is_space ||
-            IsOtherSpaceSeparator<CharacterType>(context.last.ch)) {
+        if (is_last_space || IsOtherSpaceSeparator<CharacterType>(last_ch))
           return i;
-        }
-        if ((context.current.is_space ||
-             IsOtherSpaceSeparator<CharacterType>(context.current.ch)) &&
-            i + 1 < len) {
+        if ((is_space || IsOtherSpaceSeparator<CharacterType>(ch)) &&
+            i + 1 < len)
           return i + 1;
-        }
         break;
     }
 
-    if (ShouldBreakAfter(context.last_last_ch, context.last.ch,
-                         context.current.ch)) {
+    if (ShouldBreakAfter(last_last_ch, last_ch, ch))
       return i;
-    }
 
-    if (lineBreakType == LineBreakType::kBreakAll &&
-        !U16_IS_LEAD(context.current.ch)) {
-      ULineBreak line_break =
-          LineBreakPropertyValue(context.last.ch, context.current.ch);
+    if (lineBreakType == LineBreakType::kBreakAll && !U16_IS_LEAD(ch)) {
+      ULineBreak line_break = LineBreakPropertyValue(last_ch, ch);
       if (ShouldBreakAfterBreakAll(last_line_break, line_break))
-        return i > pos && U16_IS_TRAIL(context.current.ch) ? i - 1 : i;
+        return i > pos && U16_IS_TRAIL(ch) ? i - 1 : i;
       if (line_break != U_LB_COMBINING_MARK)
         last_line_break = line_break;
     }
 
     if (lineBreakType == LineBreakType::kKeepAll &&
-        ShouldKeepAfterKeepAll(context.last_last_ch, context.last.ch,
-                               context.current.ch)) {
+        ShouldKeepAfterKeepAll(last_last_ch, last_ch, ch)) {
       // word-break:keep-all prevents breaks between East Asian ideographic.
       continue;
     }
 
-    if (NeedsLineBreakIterator(context.current.ch) ||
-        NeedsLineBreakIterator(context.last.ch)) {
+    if (NeedsLineBreakIterator(ch) || NeedsLineBreakIterator(last_ch)) {
       if (next_break < i) {
         // Don't break if positioned at start of primary context.
         if (i) {
@@ -426,9 +376,8 @@
           }
         }
       }
-      if (i == next_break && !context.last.is_space) {
+      if (i == next_break && !is_last_space)
         return i;
-      }
     }
   }
 
diff --git a/third_party/blink/renderer/platform/text/text_break_iterator.h b/third_party/blink/renderer/platform/text/text_break_iterator.h
index d10dfa4..bb00bdb 100644
--- a/third_party/blink/renderer/platform/text/text_break_iterator.h
+++ b/third_party/blink/renderer/platform/text/text_break_iterator.h
@@ -227,9 +227,6 @@
  private:
   FRIEND_TEST_ALL_PREFIXES(TextBreakIteratorTest, Strictness);
 
-  template <typename CharacterType>
-  struct Context;
-
   const AtomicString& LocaleWithKeyword() const;
   void InvalidateLocaleWithKeyword();
 
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
index 5f14a2a..69967dc 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
+++ b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
@@ -109,7 +109,6 @@
 crbug.com/626703 external/wpt/dom/nodes/NodeList-static-length-getter-tampered-indexOf-3.html [ Timeout ]
 crbug.com/626703 external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-zero-intersection-area.html [ Timeout ]
 crbug.com/626703 external/wpt/html/browsers/windows/clear-window-name.https.html [ Timeout ]
-crbug.com/626703 external/wpt/custom-elements/form-associated/ElementInternals-target-element-is-held-strongly.html [ Timeout ]
 crbug.com/626703 external/wpt/custom-elements/throw-on-dynamic-markup-insertion-counter-construct-xml-parser.xhtml [ Crash ]
 crbug.com/626703 external/wpt/service-workers/cache-storage/crashtests/cache-response-clone.https.html [ Timeout ]
 crbug.com/626703 external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index 1890226..2a64eb5 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -736,7 +736,10 @@
 crbug.com/874695 [ Mac12 ] virtual/threaded/http/tests/devtools/tracing/timeline-layout/timeline-layout-with-invalidations.js [ Slow ]
 crbug.com/874695 [ Release Win ] virtual/threaded/http/tests/devtools/tracing/timeline-layout/timeline-layout-with-invalidations.js [ Slow ]
 crbug.com/1494075 virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow-desktop.html [ Slow ]
-crbug.com/1494075 virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow.html [ Slow ]
+crbug.com/1494075 [ Debug Mac13 ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow.html [ Slow ]
+crbug.com/1494075 [ Linux ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow.html [ Slow ]
+crbug.com/1494075 [ Mac Release ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow.html [ Slow ]
+crbug.com/1494075 [ Release Win ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow.html [ Slow ]
 crbug.com/1494075 virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-out-slow.html [ Slow ]
 crbug.com/871139 virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-in-slow-desktop.html [ Slow ]
 crbug.com/871139 [ Linux ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-in-slow.html [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 825dfca..bdff7d0 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -418,7 +418,6 @@
 crbug.com/1370704 external/wpt/editing/other/join-different-white-space-style-left-paragraph-and-right-line.html?method=select-boundary&left-white-space=nowrap&right-white-space=pre-wrap [ Failure ]
 crbug.com/1370704 external/wpt/editing/other/join-different-white-space-style-left-paragraph-and-right-line.html?method=select-boundary&left-white-space=pre&right-white-space=normal [ Failure ]
 crbug.com/1370704 external/wpt/editing/other/join-different-white-space-style-left-paragraph-and-right-line.html?method=select-boundary&left-white-space=pre&right-white-space=nowrap [ Failure ]
-crbug.com/1370704 external/wpt/editing/other/join-different-white-space-style-left-paragraph-and-right-line.html?method=select-boundary&left-white-space=pre&right-white-space=pre-line [ Failure ]
 crbug.com/1370704 external/wpt/editing/other/join-different-white-space-style-left-paragraph-and-right-line.html?method=select-boundary&left-white-space=pre&right-white-space=pre-wrap [ Failure ]
 crbug.com/1370704 external/wpt/editing/other/join-different-white-space-style-left-paragraph-and-right-line.html?method=select-boundary&left-white-space=pre-line&right-white-space=normal [ Failure ]
 crbug.com/1370704 external/wpt/editing/other/join-different-white-space-style-left-paragraph-and-right-line.html?method=select-boundary&left-white-space=pre-line&right-white-space=pre-wrap [ Failure ]
@@ -550,6 +549,11 @@
 crbug.com/1370460 [ Mac ] external/wpt/svg/text/reftests/lang-attribute.svg [ Failure ]
 crbug.com/1370460 [ Mac ] external/wpt/svg/text/reftests/xml-lang-attribute.svg [ Failure ]
 
+# Temporarily disabled to unblock https://crrev.com/c/5131015
+crbug.com/1512108 http/tests/devtools/console/console-functions.js [ Failure Pass ]
+crbug.com/1512108 http/tests/devtools/console/console-tainted-globals.js [ Failure Pass ]
+crbug.com/1512108 http/tests/devtools/sources/debugger/properties-special.js [ Failure Pass ]
+
 # WPT backgrounds and borders tests. Note that there are many more in NeverFixTests
 # that should be investigated (see crbug.com/780700)
 crbug.com/492187 external/wpt/css/CSS2/backgrounds/background-intrinsic-004.xht [ Failure ]
@@ -574,9 +578,6 @@
 crbug.com/882975 [ Win10.20h2 ] virtual/threaded/fast/events/pinch/gesture-pinch-zoom-prevent-in-handler.html [ Failure Pass ]
 crbug.com/882975 [ Win10.20h2 ] virtual/threaded/fast/events/pinch/scroll-visual-viewport-send-boundary-events.html [ Failure Pass ]
 
-# Temporarily disabled to unblock https://crrev.com/c/5126051
-crbug.com/798498 http/tests/devtools/copy-network-request.js [ Failure Pass ]
-
 crbug.com/898394 virtual/android/url-bar/bottom-and-top-fixed-sticks-to-top.html [ Failure Timeout ]
 
 crbug.com/1046784 http/tests/devtools/elements/styles-3/styles-disable-then-delete.js [ Crash Failure Pass Timeout ]
@@ -2610,7 +2611,6 @@
 crbug.com/626703 external/wpt/css/css-text/text-align/text-align-justify-tabs-002.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/text-align/text-align-justify-tabs-003.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/text-align/text-align-justify-tabs-004.html [ Failure ]
-crbug.com/626703 external/wpt/xhr/send-authentication-basic.htm [ Failure ]
 crbug.com/626703 external/wpt/service-workers/service-worker/controlled-dedicatedworker-postMessage.https.html [ Failure Timeout ]
 crbug.com/626703 external/wpt/service-workers/service-worker/controlled-iframe-postMessage.https.html [ Failure Timeout ]
 crbug.com/626703 external/wpt/fetch/metadata/generated/worker-dedicated-constructor.sub.html [ Failure ]
@@ -2629,6 +2629,8 @@
 crbug.com/626703 external/wpt/editing/other/insertparagraph-in-editing-host-cannot-have-div.tentative.html* [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 virtual/webnn-service-enabled/external/wpt/webnn/gpu/arg_min_max.https.any.html [ Skip Timeout ]
+crbug.com/626703 virtual/webnn-service-enabled/external/wpt/webnn/gpu/arg_min_max.https.any.worker.html [ Crash ]
 crbug.com/626703 [ Mac13 ] virtual/threaded/external/wpt/long-animation-frame/tentative/loaf-source-location-redirect.html [ Skip Timeout ]
 crbug.com/626703 external/wpt/css/css-page/page-orientation-on-landscape-001-print.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-page/page-orientation-on-portrait-001-print.html [ Failure ]
@@ -2697,7 +2699,6 @@
 crbug.com/626703 [ Mac12 ] external/wpt/css/css-masking/animations/clip-path-interpolation-002.html [ Timeout ]
 crbug.com/626703 [ Mac12 ] external/wpt/editing/other/join-pre-and-other-block.html?method=backspace&block=div [ Timeout ]
 crbug.com/626703 [ Mac12 ] external/wpt/performance-timeline/back-forward-cache-restoration.tentative.html [ Timeout ]
-crbug.com/626703 [ Mac12 ] virtual/keepalive-in-browser-migration/external/wpt/fetch/api/response/response-clone.any.sharedworker.html [ Timeout ]
 crbug.com/626703 [ Mac12 ] virtual/pna-navigations-warning/external/wpt/fetch/private-network-access/shared-worker-fetch.tentative.https.window.html [ Timeout ]
 crbug.com/626703 [ Mac12 ] virtual/prefetch-no-vary-search/external/wpt/speculation-rules/prefetch/no-vary-search/prefetch-single-with-hint.https.html?3-3 [ Timeout ]
 crbug.com/626703 [ Mac12 ] virtual/prefetch-reusable/external/wpt/speculation-rules/prefetch/no-vary-search/prefetch-single-with-hint.https.html?27-27 [ Timeout ]
@@ -4849,7 +4850,6 @@
 
 crbug.com/1454939 fast/events/touch/gesture/focus-selectionchange-on-tap.html [ Failure Pass ]
 crbug.com/1455239 http/tests/security/drag-drop-different-origin.html [ Failure Pass ]
-crbug.com/1455579 virtual/disable-device-id-pointer-event/fast/events/pointerevents/device-id/get-device-id-from-pointer-event.html [ Failure Pass ]
 crbug.com/1456442 virtual/compositor-threaded-percent-based-scrolling/fast/events/wheel/wheel-latched-scroll-node-removed.html [ Failure Pass Timeout ]
 
 # Green Mac11 Test
@@ -6515,7 +6515,6 @@
 # Flaky test
 crbug.com/1454689 http/tests/inspector-protocol/tracing/cpu-profiling.js [ Failure Pass ]
 
-crbug.com/1455245 virtual/fedcm-multi-idp/external/wpt/credential-management/fedcm-multi-idp/abort-multiple-gets-through-first-idp.https.html [ Pass Timeout ]
 crbug.com/1455245 virtual/fedcm-multi-idp/external/wpt/credential-management/fedcm-multi-idp/get-before-and-after-onload.https.html [ Failure Pass Timeout ]
 crbug.com/1455245 virtual/fedcm-multi-idp/external/wpt/credential-management/fedcm-multi-idp/get-before-and-during-onload.https.html [ Pass Timeout ]
 crbug.com/1455245 virtual/fedcm-multi-idp/external/wpt/credential-management/fedcm-multi-idp/get-before-onload-and-during-dom-content-loaded.https.html [ Pass Timeout ]
@@ -6869,7 +6868,9 @@
 crbug.com/1510005 [ Linux ] virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-svg.html [ Crash Failure Pass Timeout ]
 
 # TODO(crbug.com/1511492): Re-enable these tests.
-crbug.com/1511492 [ Linux ] external/wpt/event-timing/first-input-interactionid-tap.html [ Crash Failure Timeout ]
+crbug.com/1511492 [ Linux ] external/wpt/event-timing/first-input-interactionid-tap.html [ Crash Failure Pass Timeout ]
+crbug.com/1511492 [ Linux ] external/wpt/event-timing/interactionid-tap.html [ Crash Failure Pass Timeout ]
+crbug.com/1511492 [ Linux ] external/wpt/pointerevents/pointerevent_contextmenu_is_a_pointerevent.html?touch [ Crash Failure Pass Timeout ]
 crbug.com/1511492 [ Linux ] external/wpt/event-timing/external/wpt/event-timing/interactionid-tap.html [ Crash Failure Timeout ]
 
 # TODO(crbug.com/1496375): Re-enable these tests.
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 58965ae39..8c2d209e 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
@@ -395334,6 +395334,14 @@
      "3f87fc8042d2b3de1bf13d64333ba08cdfdabb3b",
      []
     ],
+    "arg_min_max.https.any-expected.txt": [
+     "6fcce59d82c31dc4b67915add69a3c5d2a2ccfbd",
+     []
+    ],
+    "arg_min_max.https.any.worker-expected.txt": [
+     "7f086a50092bb70e348758d75df9ee8668b111d7",
+     []
+    ],
     "batch_normalization.https.any-expected.txt": [
      "7369d8f44cb7cbe82d7242ea94726a2dc080ae15",
      []
@@ -395500,6 +395508,14 @@
        "0a12871cebab50b0e826674107ff503e6ede5347",
        []
       ],
+      "arg_max.json": [
+       "9af47a95894223eb39d4f73f81f5b9c9a4112bff",
+       []
+      ],
+      "arg_min.json": [
+       "dc0b5cdfd1a6bc822bce05f3ba014bb119f3e679",
+       []
+      ],
       "average_pool2d.json": [
        "802e0d764621707c0b00d6a05627d0c1eeba9233",
        []
@@ -395746,7 +395762,7 @@
       ]
      },
      "utils.js": [
-      "c3b10a7020182f1765cdd7890410766334bc6055",
+      "0e2687ec22bac4a4dea3dbe938f9fc386a0a6a4e",
       []
      ]
     },
@@ -643821,6 +643837,13 @@
       {}
      ]
     ],
+    "viewport-scrollbars-cause-resize-in-iframe.html": [
+     "ce9ec3276886b5786ffe1309251469e92f29e1a9",
+     [
+      null,
+      {}
+     ]
+    ],
     "viewport-scrollbars-cause-resize.html": [
      "086e8d92b087afe8aea2fbc91c1f96dd7d328e72",
      [
@@ -659697,6 +659720,57 @@
     ]
    },
    "webnn": {
+    "arg_min_max.https.any.js": [
+     "cff1d6a955ce31fbecde084f8407a8f53bf6b889",
+     [
+      "webnn/arg_min_max.https.any.html",
+      {
+       "script_metadata": [
+        [
+         "title",
+         "test WebNN API argMin/Max operations"
+        ],
+        [
+         "global",
+         "window,dedicatedworker"
+        ],
+        [
+         "script",
+         "./resources/utils.js"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ],
+     [
+      "webnn/arg_min_max.https.any.worker.html",
+      {
+       "script_metadata": [
+        [
+         "title",
+         "test WebNN API argMin/Max operations"
+        ],
+        [
+         "global",
+         "window,dedicatedworker"
+        ],
+        [
+         "script",
+         "./resources/utils.js"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ]
+    ],
     "batch_normalization.https.any.js": [
      "15e66a8bc06b2e73c19f910c765428b89b3f3b3d",
      [
@@ -660259,6 +660333,57 @@
      ]
     ],
     "gpu": {
+     "arg_min_max.https.any.js": [
+      "76092ea92e6931f64f24f068341fe7335fd58259",
+      [
+       "webnn/gpu/arg_min_max.https.any.html",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "test WebNN API argMin/Max operations"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "script",
+          "../resources/utils.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ],
+      [
+       "webnn/gpu/arg_min_max.https.any.worker.html",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "test WebNN API argMin/Max operations"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "script",
+          "../resources/utils.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ]
+     ],
      "batch_normalization.https.any.js": [
       "90b6def636d1090dc0fe19368fbb5ac38d0133cd",
       [
diff --git a/third_party/blink/web_tests/external/wpt/webnn/arg_min_max.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/webnn/arg_min_max.https.any-expected.txt
new file mode 100644
index 0000000..6fcce59d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webnn/arg_min_max.https.any-expected.txt
@@ -0,0 +1,83 @@
+This is a testharness.js-based test.
+[FAIL] argMin float32 1D tensor default options / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 2D tensor default options / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 3D tensor default options / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 4D tensor default options / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 5D tensor default options / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 4D tensor options.axes=[2] / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 4D tensor options.axes=[] / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 4D tensor options.keepDimensions=true / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 4D tensor options.keepDimensions=false / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 4D tensor options.selectLastIndex=true / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 4D tensor options.selectLastIndex=false / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 4D tensor options.axes=[0, 2] options.keepDimensions=false / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 4D tensor options.axes=[3, 0, 1] options.keepDimensions=true / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 4D tensor options.axes=[0, 2] options.selectLastIndex=false / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 4D tensor options.axes=[0, 2] options.selectLastIndex=true / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=false / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=true / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 4D tensor all options / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 0D scalar options.axes=[] / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMin float32 0D scalar options.axes=[] no effect by both keepDimensions and selectLastIndex being true / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 1D tensor default options / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 2D tensor default options / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 3D tensor default options / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 4D tensor default options / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 5D tensor default options / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 4D tensor options.axes=[2] / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 4D tensor options.axes=[] / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 4D tensor options.keepDimensions=true / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 4D tensor options.keepDimensions=false / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 4D tensor options.selectLastIndex=true / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 4D tensor options.selectLastIndex=false / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 4D tensor options.axes=[0, 2] options.keepDimensions=false / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 4D tensor options.axes=[3, 0, 1] options.keepDimensions=true / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 4D tensor options.axes=[0, 2] options.selectLastIndex=false / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 4D tensor options.axes=[0, 2] options.selectLastIndex=true / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=false / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=true / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 4D tensor all options / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 0D scalar options.axes=[] / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+[FAIL] argMax float32 0D scalar options.axes=[] no effect by both keepDimensions and selectLastIndex being true / async
+  promise_test: Unhandled rejection with value: object "NotSupportedError: The operand data type (int64) is not supported."
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webnn/arg_min_max.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/arg_min_max.https.any.js
new file mode 100644
index 0000000..cff1d6a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webnn/arg_min_max.https.any.js
@@ -0,0 +1,10 @@
+// META: title=test WebNN API argMin/Max operations
+// META: global=window,dedicatedworker
+// META: script=./resources/utils.js
+// META: timeout=long
+
+'use strict';
+
+// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-argminmax
+
+testWebNNOperation(['argMin', 'argMax'], buildOperationWithSingleInput);
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webnn/arg_min_max.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/webnn/arg_min_max.https.any.worker-expected.txt
new file mode 100644
index 0000000..7f086a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webnn/arg_min_max.https.any.worker-expected.txt
@@ -0,0 +1,164 @@
+This is a testharness.js-based test.
+Found 80 FAIL, 0 TIMEOUT, 0 NOTRUN.
+[FAIL] argMin float32 1D tensor default options / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 2D tensor default options / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 3D tensor default options / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 4D tensor default options / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 5D tensor default options / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 4D tensor options.axes=[2] / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 4D tensor options.axes=[] / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 4D tensor options.keepDimensions=true / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 4D tensor options.keepDimensions=false / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 4D tensor options.selectLastIndex=true / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 4D tensor options.selectLastIndex=false / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 4D tensor options.axes=[0, 2] options.keepDimensions=false / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 4D tensor options.axes=[3, 0, 1] options.keepDimensions=true / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 4D tensor options.axes=[0, 2] options.selectLastIndex=false / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 4D tensor options.axes=[0, 2] options.selectLastIndex=true / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=false / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=true / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 4D tensor all options / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 0D scalar options.axes=[] / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 0D scalar options.axes=[] no effect by both keepDimensions and selectLastIndex being true / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 1D tensor default options / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 2D tensor default options / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 3D tensor default options / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 4D tensor default options / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 5D tensor default options / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 4D tensor options.axes=[2] / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 4D tensor options.axes=[] / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 4D tensor options.keepDimensions=true / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 4D tensor options.keepDimensions=false / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 4D tensor options.selectLastIndex=true / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 4D tensor options.selectLastIndex=false / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 4D tensor options.axes=[0, 2] options.keepDimensions=false / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 4D tensor options.axes=[3, 0, 1] options.keepDimensions=true / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 4D tensor options.axes=[0, 2] options.selectLastIndex=false / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 4D tensor options.axes=[0, 2] options.selectLastIndex=true / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=false / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=true / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 4D tensor all options / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 0D scalar options.axes=[] / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMax float32 0D scalar options.axes=[] no effect by both keepDimensions and selectLastIndex being true / sync
+  Failed to execute 'buildSync' on 'MLGraphBuilder': The operand data type (int64) is not supported.
+[FAIL] argMin float32 1D tensor default options / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 2D tensor default options / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 3D tensor default options / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 4D tensor default options / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 5D tensor default options / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 4D tensor options.axes=[2] / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 4D tensor options.axes=[] / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 4D tensor options.keepDimensions=true / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 4D tensor options.keepDimensions=false / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 4D tensor options.selectLastIndex=true / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 4D tensor options.selectLastIndex=false / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 4D tensor options.axes=[0, 2] options.keepDimensions=false / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 4D tensor options.axes=[3, 0, 1] options.keepDimensions=true / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 4D tensor options.axes=[0, 2] options.selectLastIndex=false / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 4D tensor options.axes=[0, 2] options.selectLastIndex=true / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=false / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=true / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 4D tensor all options / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 0D scalar options.axes=[] / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMin float32 0D scalar options.axes=[] no effect by both keepDimensions and selectLastIndex being true / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 1D tensor default options / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 2D tensor default options / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 3D tensor default options / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 4D tensor default options / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 5D tensor default options / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 4D tensor options.axes=[2] / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 4D tensor options.axes=[] / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 4D tensor options.keepDimensions=true / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 4D tensor options.keepDimensions=false / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 4D tensor options.selectLastIndex=true / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 4D tensor options.selectLastIndex=false / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 4D tensor options.axes=[0, 2] options.keepDimensions=false / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 4D tensor options.axes=[3, 0, 1] options.keepDimensions=true / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 4D tensor options.axes=[0, 2] options.selectLastIndex=false / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 4D tensor options.axes=[0, 2] options.selectLastIndex=true / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=false / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=true / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 4D tensor all options / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 0D scalar options.axes=[] / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+[FAIL] argMax float32 0D scalar options.axes=[] no effect by both keepDimensions and selectLastIndex being true / async
+  promise_test: Unhandled rejection with value: object "TypeError: builder.build is not a function"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webnn/gpu/arg_min_max.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/gpu/arg_min_max.https.any.js
new file mode 100644
index 0000000..76092ea9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webnn/gpu/arg_min_max.https.any.js
@@ -0,0 +1,10 @@
+// META: title=test WebNN API argMin/Max operations
+// META: global=window,dedicatedworker
+// META: script=../resources/utils.js
+// META: timeout=long
+
+'use strict';
+
+// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-argminmax
+
+testWebNNOperation(['argMin', 'argMax'], buildOperationWithSingleInput, 'gpu');
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webnn/resources/test_data/arg_max.json b/third_party/blink/web_tests/external/wpt/webnn/resources/test_data/arg_max.json
new file mode 100644
index 0000000..9af47a9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webnn/resources/test_data/arg_max.json
@@ -0,0 +1,919 @@
+{
+  "tests": [
+    {
+      "name": "argMax float32 1D tensor default options",
+      "inputs": {
+        "input": {
+          "shape": [24],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          3
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 2D tensor default options",
+      "inputs": {
+        "input": {
+          "shape": [4, 6],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          3
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 3D tensor default options",
+      "inputs": {
+        "input": {
+          "shape": [2, 3, 4],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          3
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 4D tensor default options",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          3
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 5D tensor default options",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 1, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          3
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 4D tensor options.axes=[2]",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [2]
+      },
+      "expected": {
+        "name": "output",
+        "shape": [2, 1, 3],
+        "data": [
+          1,
+          2,
+          2,
+          1,
+          2,
+          2
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 4D tensor options.axes=[]",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": []
+      },
+      "expected": {
+        "name": "output",
+        "shape": [2, 1, 4, 3],
+        "data": [
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 4D tensor options.keepDimensions=true",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "keepDimensions": true
+      },
+      "expected": {
+        "name": "output",
+        "shape": [1, 1, 1, 1],
+        "data": [
+          3
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 4D tensor options.keepDimensions=false",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "keepDimensions": true
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          3
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 4D tensor options.selectLastIndex=true",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "selectLastIndex": true
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          20
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 4D tensor options.selectLastIndex=false",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "selectLastIndex": false
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          3
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 4D tensor options.axes=[0, 2] options.keepDimensions=false",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [0, 2],
+        "keepDimensions": false
+      },
+      "expected": {
+        "name": "output",
+        "shape": [1, 3],
+        "data": [
+          1,
+          2,
+          2
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 4D tensor options.axes=[3, 0, 1] options.keepDimensions=true",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [3, 0, 1],
+        "keepDimensions": true
+      },
+      "expected": {
+        "name": "output",
+        "shape": [1, 1, 4, 1],
+        "data": [
+          2,
+          0,
+          2,
+          1
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 4D tensor options.axes=[0, 2] options.selectLastIndex=false",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [0, 2],
+        "selectLastIndex": false
+      },
+      "expected": {
+        "name": "output",
+        "shape": [1, 3],
+        "data": [
+          1,
+          2,
+          2
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 4D tensor options.axes=[0, 2] options.selectLastIndex=true",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [0, 2],
+        "selectLastIndex": true
+      },
+      "expected": {
+        "name": "output",
+        "shape": [1, 3],
+        "data": [
+          5,
+          6,
+          6
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=false",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [3, 0, 1],
+        "selectLastIndex": false
+      },
+      "expected": {
+        "name": "output",
+        "shape": [4],
+        "data": [
+          2,
+          0,
+          2,
+          1
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=true",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [3, 0, 1],
+        "selectLastIndex": true
+      },
+      "expected": {
+        "name": "output",
+        "shape": [4],
+        "data": [
+          5,
+          3,
+          5,
+          4
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 4D tensor all options",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955,
+            -51.0936194154457,
+            -6.5397018645619625,
+            73.81338015899149,
+            88.46114630531724,
+            -5.294266751122791,
+            -79.20668057325759,
+            -41.70176598864654,
+            73.81338015899149,
+            88.46114630531724,
+            -84.939998758247,
+            -61.488942502520906,
+            -98.33874402761955
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [3, 0],
+        "keepDimensions": true,
+        "selectLastIndex": false
+      },
+      "expected": {
+        "name": "output",
+        "shape": [1, 1, 4, 1],
+        "data": [
+          2,
+          0,
+          2,
+          1
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 0D scalar options.axes=[]",
+      "inputs": {
+        "input": {
+          "shape": [],
+          "data": [
+            -51.0936194154457
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": []
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          0
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMax float32 0D scalar options.axes=[] no effect by both keepDimensions and selectLastIndex being true",
+      "inputs": {
+        "input": {
+          "shape": [],
+          "data": [
+            -51.0936194154457
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [],
+        "keepDimensions": true,
+        "selectLastIndex": true
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          0
+        ],
+        "type": "int64"
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webnn/resources/test_data/arg_min.json b/third_party/blink/web_tests/external/wpt/webnn/resources/test_data/arg_min.json
new file mode 100644
index 0000000..dc0b5cd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webnn/resources/test_data/arg_min.json
@@ -0,0 +1,919 @@
+{
+  "tests": [
+    {
+      "name": "argMin float32 1D tensor default options",
+      "inputs": {
+        "input": {
+          "shape": [24],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          7
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 2D tensor default options",
+      "inputs": {
+        "input": {
+          "shape": [4, 6],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          7
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 3D tensor default options",
+      "inputs": {
+        "input": {
+          "shape": [2, 3, 4],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          7
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 4D tensor default options",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          7
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 5D tensor default options",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 1, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          7
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 4D tensor options.axes=[2]",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [2]
+      },
+      "expected": {
+        "name": "output",
+        "shape": [2, 1, 3],
+        "data": [
+          2,
+          2,
+          0,
+          0,
+          0,
+          0
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 4D tensor options.axes=[]",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": []
+      },
+      "expected": {
+        "name": "output",
+        "shape": [2, 1, 4, 3],
+        "data": [
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 4D tensor options.keepDimensions=true",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "keepDimensions": true
+      },
+      "expected": {
+        "name": "output",
+        "shape": [1, 1, 1, 1],
+        "data": [
+          7
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 4D tensor options.keepDimensions=false",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "keepDimensions": true
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          7
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 4D tensor options.selectLastIndex=true",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "selectLastIndex": true
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          19
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 4D tensor options.selectLastIndex=false",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "selectLastIndex": false
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          7
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 4D tensor options.axes=[0, 2] options.keepDimensions=false",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [0, 2],
+        "keepDimensions": false
+      },
+      "expected": {
+        "name": "output",
+        "shape": [1, 3],
+        "data": [
+          2,
+          2,
+          4
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 4D tensor options.axes=[3, 0, 1] options.keepDimensions=true",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [3, 0, 1],
+        "keepDimensions": true
+      },
+      "expected": {
+        "name": "output",
+        "shape": [1, 1, 4, 1],
+        "data": [
+          4,
+          0,
+          1,
+          1
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 4D tensor options.axes=[0, 2] options.selectLastIndex=false",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [0, 2],
+        "selectLastIndex": false
+      },
+      "expected": {
+        "name": "output",
+        "shape": [1, 3],
+        "data": [
+          2,
+          2,
+          4
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 4D tensor options.axes=[0, 2] options.selectLastIndex=true",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [0, 2],
+        "selectLastIndex": true
+      },
+      "expected": {
+        "name": "output",
+        "shape": [1, 3],
+        "data": [
+          6,
+          6,
+          4
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=false",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [3, 0, 1],
+        "selectLastIndex": false
+      },
+      "expected": {
+        "name": "output",
+        "shape": [4],
+        "data": [
+          4,
+          0,
+          1,
+          1
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 4D tensor options.axes=[3, 0, 1] options.selectLastIndex=true",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [3, 0, 1],
+        "selectLastIndex": true
+      },
+      "expected": {
+        "name": "output",
+        "shape": [4],
+        "data": [
+          4,
+          0,
+          4,
+          1
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 4D tensor all options",
+      "inputs": {
+        "input": {
+          "shape": [2, 1, 4, 3],
+          "data": [
+            3.830124090690262,
+            -24.986487937638074,
+            5.299982630691289,
+            -48.5486590218902,
+            40.30886781808215,
+            60.184293919409726,
+            -82.78385618759043,
+            -96.50904103637833,
+            71.87028201591897,
+            38.866394268784035,
+            -39.143725517854435,
+            31.444366685561903,
+            -82.78385618759043,
+            -96.50904103637833,
+            -25.53388886326502,
+            -16.142265850469343,
+            66.63677406472371,
+            82.5119815304117,
+            -82.78385618759043,
+            -96.50904103637833,
+            39.7687246127592,
+            42.15040238450999,
+            82.66863662444459,
+            85.4526923278379
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [3, 0],
+        "keepDimensions": true,
+        "selectLastIndex": false
+      },
+      "expected": {
+        "name": "output",
+        "shape": [1, 1, 4, 1],
+        "data": [
+          4,
+          0,
+          1,
+          1
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 0D scalar options.axes=[]",
+      "inputs": {
+        "input": {
+          "shape": [],
+          "data": [
+            3.830124090690262
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": []
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          0
+        ],
+        "type": "int64"
+      }
+    },
+    {
+      "name": "argMin float32 0D scalar options.axes=[] no effect by both keepDimensions and selectLastIndex being true",
+      "inputs": {
+        "input": {
+          "shape": [],
+          "data": [
+            3.830124090690262
+          ],
+          "type": "float32"
+        }
+      },
+      "options": {
+        "axes": [],
+        "keepDimensions": true,
+        "selectLastIndex": true
+      },
+      "expected": {
+        "name": "output",
+        "shape": [],
+        "data": [
+          0
+        ],
+        "type": "int64"
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webnn/resources/utils.js b/third_party/blink/web_tests/external/wpt/webnn/resources/utils.js
index c3b10a7..0e2687ec 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/resources/utils.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/resources/utils.js
@@ -12,6 +12,7 @@
   uint32: Uint32Array,
   int8: Int8Array,
   uint8: Uint8Array,
+  int64: BigInt64Array,
 };
 
 const getTypedArrayData = (type, data) => {
@@ -22,6 +23,11 @@
     for (let i = 0; i < data.length; i++) {
       outData[i] = toHalf(data[i]);
     }
+  } else if (type === 'int64') {
+    outData = new TypedArrayDict[type](data.length);
+    for (let i = 0; i < data.length; i++) {
+      outData[i] = BigInt(data[i]);
+    }
   } else {
     outData = new TypedArrayDict[type](data);
   }
@@ -280,6 +286,8 @@
 
 // Refer to precision metrics on https://github.com/webmachinelearning/webnn/issues/265#issuecomment-1256242643
 const PrecisionMetrics = {
+  argMax: {ULP: {int64: 0}},
+  argMin: {ULP: {int64: 0}},
   batchNormalization: {ULP: {float32: 6, float16: 6}},
   clamp: {ULP: {float32: 0, float16: 0}},
   concat: {ULP: {float32: 0, float16: 0}},
@@ -428,6 +436,9 @@
         actualBitwise = actual[i];
         // convert expected data of Float16 to Uint16
         expectedBitwise = toHalf(expected[i]);
+      } else if (dataType === 'int64') {
+        actualBitwise = actual[i];
+        expectedBitwise = BigInt(expected[i]);
       }
       distance = actualBitwise - expectedBitwise;
       distance = distance >= 0 ? distance : -distance;
diff --git a/third_party/blink/web_tests/http/tests/devtools/copy-network-request-expected.txt b/third_party/blink/web_tests/http/tests/devtools/copy-network-request-expected.txt
index 0bac7b0..e92460e45 100644
--- a/third_party/blink/web_tests/http/tests/devtools/copy-network-request-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/copy-network-request-expected.txt
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/cssom/caretPositionFromPoint-with-transformation-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/cssom/caretPositionFromPoint-with-transformation-expected.txt
deleted file mode 100644
index ed6d508..0000000
--- a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/cssom/caretPositionFromPoint-with-transformation-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught TypeError: frameDoc.caretPositionFromPoint is not a function
-[TIMEOUT] iframe's with equal content should report the same caret offset
-  Test timed out
-Harness: the test ran to completion.
diff --git a/third_party/chromium-variations b/third_party/chromium-variations
index 42bf9b8..6a94fae 160000
--- a/third_party/chromium-variations
+++ b/third_party/chromium-variations
@@ -1 +1 @@
-Subproject commit 42bf9b85713027338720203624f1ee98d0982a89
+Subproject commit 6a94fae344833b2e4c170955c9b10fa3a40c5550
diff --git a/third_party/cros-components/src b/third_party/cros-components/src
index 075d293..f448b44 160000
--- a/third_party/cros-components/src
+++ b/third_party/cros-components/src
@@ -1 +1 @@
-Subproject commit 075d293d1a98cce55cdb88185239126eb64f6d55
+Subproject commit f448b44c03202bdb5fb1305216d4723724d5288b
diff --git a/third_party/dawn b/third_party/dawn
index 673e8b4..0b6ca39 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit 673e8b4639e3e19b97380992f2a82bc4442d9643
+Subproject commit 0b6ca39d38735c9177dc8f7291dad5314fed3b11
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal
index 2854d42..457fec8b 160000
--- a/third_party/devtools-frontend-internal
+++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@
-Subproject commit 2854d4287a81c0a177ea4d6fa64737f1b9640614
+Subproject commit 457fec8b5b9ff390f9e7a8e64f8a886d97cbe1f9
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 0a48a3b..c67fdad 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 0a48a3bd9f916f605f225b4e7e2760380d0a9cc9
+Subproject commit c67fdadcd104ba82740a25feed4fec2931b5f6f3
diff --git a/third_party/pdfium b/third_party/pdfium
index b2d4f11..57e284b 160000
--- a/third_party/pdfium
+++ b/third_party/pdfium
@@ -1 +1 @@
-Subproject commit b2d4f1104c43ccd42df2e48902ae857f0d5a188a
+Subproject commit 57e284ba20215dfa5cdbf732b4e158c544323d86
diff --git a/third_party/skia b/third_party/skia
index e40b61c..276075b 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit e40b61c3dfefe6a5ac8f200b0041148d5108cfa0
+Subproject commit 276075b975675ba92d907dd64247937b7b73245e
diff --git a/third_party/webrtc b/third_party/webrtc
index 57b0664..acdc89d6 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 57b06646bee860edb996c9c9f4ea6372a12a9536
+Subproject commit acdc89d65328911b4e98afe0757028eca162cbb3
diff --git a/third_party/zlib/trees.c b/third_party/zlib/trees.c
index 5ca23e9..3813527 100644
--- a/third_party/zlib/trees.c
+++ b/third_party/zlib/trees.c
@@ -938,7 +938,8 @@
 
         /* Check for no overlay of pending_buf on needed symbols */
 #ifdef LIT_MEM
-        Assert(s->pending < (s->lit_bufsize << 1) + sx, "pendingBuf overflow");
+        Assert(s->pending < (s->lit_bufsize << 1) + (sx << 1),
+               "pendingBuf overflow");
 #else
         Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow");
 #endif
diff --git a/tools/android/forwarder2/host_forwarder_main.cc b/tools/android/forwarder2/host_forwarder_main.cc
index 57ee6fd..7790665 100644
--- a/tools/android/forwarder2/host_forwarder_main.cc
+++ b/tools/android/forwarder2/host_forwarder_main.cc
@@ -35,7 +35,7 @@
 namespace {
 
 const char kLogFilePath[] = "/tmp/host_forwarder_log";
-const char kDaemonIdentifier[] = "chrome_host_forwarder_daemon";
+const char kDefaultDaemonIdentifier[] = "chrome_host_forwarder_daemon";
 
 const int kBufSize = 256;
 
@@ -188,11 +188,11 @@
   const base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess();
   std::string adb_path = "adb";
   bool kill_server = false;
+  std::string serial_id = cmd_line.HasSwitch("serial-id") ?
+          cmd_line.GetSwitchValueASCII("serial-id") : std::string();
 
   base::Pickle pickle;
-  pickle.WriteString(
-      cmd_line.HasSwitch("serial-id") ?
-          cmd_line.GetSwitchValueASCII("serial-id") : std::string());
+  pickle.WriteString(serial_id);
 
   const std::vector<std::string> args = cmd_line.GetArgs();
   if (cmd_line.HasSwitch("kill-server")) {
@@ -221,10 +221,15 @@
   if (kill_server && args.size() > 0)
     ExitWithUsage();
 
+  std::string daemon_identifier = kDefaultDaemonIdentifier;
+  if (!serial_id.empty()) {
+    daemon_identifier += "_" + serial_id;
+  }
+
   ClientDelegate client_delegate(pickle);
   ServerDelegate daemon_delegate(adb_path);
   Daemon daemon(
-      kLogFilePath, kDaemonIdentifier, &client_delegate, &daemon_delegate,
+      kLogFilePath, daemon_identifier, &client_delegate, &daemon_delegate,
       &GetExitNotifierFD);
 
   if (kill_server)
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index b14d5b56..401cb8b 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -6093,6 +6093,40 @@
   </description>
 </action>
 
+<action name="ChromeOS.AppInstallDialog.AppLaunched">
+  <owner>dominicschulz@google.com</owner>
+  <owner>cros-web-apps-infra-team@google.com</owner>
+  <description>
+    Recorded when the user requested a previously installed app to be launched
+    in the CrOS App Install dialog.
+  </description>
+</action>
+
+<action name="ChromeOS.AppInstallDialog.Cancelled">
+  <owner>dominicschulz@google.com</owner>
+  <owner>cros-web-apps-infra-team@google.com</owner>
+  <description>
+    Recorded when the user closed an installation dialog without installing.
+  </description>
+</action>
+
+<action name="ChromeOS.AppInstallDialog.Installed">
+  <owner>dominicschulz@google.com</owner>
+  <owner>cros-web-apps-infra-team@google.com</owner>
+  <description>
+    Recorded when the user requested an app to be installed in the CrOS App
+    Install dialog.
+  </description>
+</action>
+
+<action name="ChromeOS.AppInstallDialog.Shown">
+  <owner>dominicschulz@google.com</owner>
+  <owner>cros-web-apps-infra-team@google.com</owner>
+  <description>
+    Recorded when the CrOS App Install dialog was opened by the user.
+  </description>
+</action>
+
 <action name="ChromeOS.Settings.InputMethod.Autocorrect.Open">
   <owner>zacpartridge@chromium.org</owner>
   <owner>essential-inputs-team@google.com</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a934214..39d884a4 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -28552,8 +28552,6 @@
   <int value="-2133276662" label="EduCoexistenceConsentLog:disabled"/>
   <int value="-2132591642" label="enable-input-view"/>
   <int value="-2132161378" label="SendTabToSelfHistory:disabled"/>
-  <int value="-2131746498"
-      label="AutofillUseImprovedLabelDisambiguation:enabled"/>
   <int value="-2130982324" label="DesktopPWAsWebBundles:disabled"/>
   <int value="-2129940395" label="WebAssemblySimd:disabled"/>
   <int value="-2129781123" label="RequestDesktopSiteZoom:enabled"/>
@@ -30825,6 +30823,8 @@
   <int value="-1093135462" label="DesktopPWAsAttentionBadgingCrOS:disabled"/>
   <int value="-1092514142" label="JourneysKeywordFiltering:disabled"/>
   <int value="-1092211161" label="BluetoothWbsDogfood:disabled"/>
+  <int value="-1092165602"
+      label="EnableBoundSessionCredentialsSoftwareKeysForManualTesting:enabled"/>
   <int value="-1090001589" label="WebViewRestrictSensitiveContent:enabled"/>
   <int value="-1089665395" label="HTMLParamElementUrlSupport:enabled"/>
   <int value="-1088804127" label="DuetTabStripIntegrationAndroid:disabled"/>
@@ -32276,6 +32276,8 @@
   <int value="-400098787" label="QuietNotificationPrompts:enabled"/>
   <int value="-399614193" label="EnableSuggestedDriveFiles:enabled"/>
   <int value="-399333540" label="FiltersInRecents:disabled"/>
+  <int value="-399166855"
+      label="enable-bound-session-credentials-software-keys-for-manual-testing:enabled"/>
   <int value="-398922143" label="IframeOneGoogleBar:enabled"/>
   <int value="-398623652" label="CCTTargetTranslateLanguage:enabled"/>
   <int value="-398325847" label="HelpAppCrosComponents:enabled"/>
@@ -33465,7 +33467,6 @@
   <int value="160524775" label="PDFTwoUpView:disabled"/>
   <int value="160838658" label="SmartDimModelV3:enabled"/>
   <int value="161008398" label="NearbyKeepAliveFix:disabled"/>
-  <int value="161409456" label="AutofillUseMobileLabelDisambiguation:disabled"/>
   <int value="161694478" label="OmniboxNewAnswerLayout:enabled"/>
   <int value="161706692" label="ContextualPageActionPriceTracking:disabled"/>
   <int value="165429419" label="DrawNativeEdgeToEdge:disabled"/>
@@ -34343,6 +34344,8 @@
   <int value="578894559" label="IPH_Snooze:enabled"/>
   <int value="580288865" label="ClipboardMaximumAge:disabled"/>
   <int value="580546202" label="WindowSplitting:disabled"/>
+  <int value="580915599"
+      label="EnableBoundSessionCredentialsSoftwareKeysForManualTesting:disabled"/>
   <int value="581118445" label="enable-eol-notification"/>
   <int value="581355159" label="ContentSuggestionsCategoryRanker:disabled"/>
   <int value="582187448" label="DontPrefetchLibraries:enabled"/>
@@ -35236,8 +35239,6 @@
   <int value="1006280999" label="CrOSLateBootArcVmmSwap:enabled"/>
   <int value="1006608931" label="ArcEnableUnifiedAudioFocus:disabled"/>
   <int value="1007444341" label="enable-prefixed-encrypted-media"/>
-  <int value="1008677979"
-      label="AutofillUseImprovedLabelDisambiguation:disabled"/>
   <int value="1009252543" label="BlinkExtension:disabled"/>
   <int value="1009437086" label="AutofillEnableOfferNotification:disabled"/>
   <int value="1009596554" label="PWAsDefaultOfflinePage:enabled"/>
@@ -35569,7 +35570,6 @@
   <int value="1169418814" label="ManualFallbacksFilling:enabled"/>
   <int value="1170030686" label="BookmarkBottomSheet:enabled"/>
   <int value="1170632919" label="DrawNativeEdgeToEdge:enabled"/>
-  <int value="1173244409" label="AutofillUseMobileLabelDisambiguation:enabled"/>
   <int value="1174088940" label="enable-wasm"/>
   <int value="1175660959" label="AutofillEnableCardArtImage:enabled"/>
   <int value="1176183341" label="SearchHistoryLink:enabled"/>
@@ -36125,6 +36125,8 @@
   <int value="1429923065" label="enable-media-internals:enabled"/>
   <int value="1430924529" label="TranslateIntent:enabled"/>
   <int value="1431050645" label="PayWithGoogleV1:disabled"/>
+  <int value="1431148950"
+      label="enable-bound-session-credentials-software-keys-for-manual-testing:disabled"/>
   <int value="1431934725" label="OmniboxAutocompleteTitles:disabled"/>
   <int value="1432263468" label="SpeculationRulesPrefetchProxy:disabled"/>
   <int value="1433706192" label="FileSystemObserver:disabled"/>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 2132e9a4..16f6d81e 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -3791,19 +3791,6 @@
   </summary>
 </histogram>
 
-<histogram name="Autofill.ProfileSuggestionsMadeWithFormatter"
-    enum="BooleanCreated" expires_after="2024-12-12">
-  <owner>battre@chromium.org</owner>
-  <owner>src/components/autofill/OWNERS</owner>
-  <summary>
-    This metric is recorded when the autofill-use-improved-label-disambiguation
-    experiment is enabled and profile suggestions are available. This metric
-    measures how often suggestions were created with and without a formatter.
-    Warning: this histogram was expired from 2020-09-30 to 2022-04-07; data may
-    be missing.
-  </summary>
-</histogram>
-
 <histogram
     name="Autofill.ProfileTokenQuality.ObservationCountBeforeSubmission.PerProfile"
     units="Observations" expires_after="M125">
diff --git a/tools/metrics/histograms/metadata/chromeos/enums.xml b/tools/metrics/histograms/metadata/chromeos/enums.xml
index 4e90f891..9bc92f8 100644
--- a/tools/metrics/histograms/metadata/chromeos/enums.xml
+++ b/tools/metrics/histograms/metadata/chromeos/enums.xml
@@ -855,6 +855,12 @@
   <int value="5" label="FailedToDownloadToFile"/>
 </enum>
 
+<enum name="GeolocationAccessLevel">
+  <int value="0" label="Blocked for all"/>
+  <int value="1" label="Allowed"/>
+  <int value="2" label="Only allowed for system services"/>
+</enum>
+
 <enum name="HardwareVerifierQualificationStatus">
   <summary>
     List of the possible qualification status of a component. This is defined in
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index 97faee1..68a6302c 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -2031,6 +2031,24 @@
   </summary>
 </histogram>
 
+<histogram name="ChromeOS.PrivacyHub.Geolocation.AccessLevelChanged.{Source}"
+    enum="GeolocationAccessLevel" expires_after="2024-06-01">
+  <owner>zauri@google.com</owner>
+  <owner>chromeos-privacyhub@google.com</owner>
+  <summary>
+    Records system geolocation permission change originated from the {Source}.
+  </summary>
+  <token key="Source">
+    <variant name="GeolocationDialog"
+        summary="geolocation dialogs. These dialogs are surfaced in the
+                 affected system services, like Automatic Time Zone"/>
+    <variant name="LocationPermissionNotification"
+        summary="ChromeOS system notification"/>
+    <variant name="SystemSettings"
+        summary="ChromeOS system settings &gt; Location access subpage"/>
+  </token>
+</histogram>
+
 <histogram name="ChromeOS.PrivacyHub.LearnMorePage.Opened"
     enum="PrivacyHubLearnMoreSensor" expires_after="2024-05-19">
   <owner>cschlosser@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/media/enums.xml b/tools/metrics/histograms/metadata/media/enums.xml
index c3b1a67..831749a2 100644
--- a/tools/metrics/histograms/metadata/media/enums.xml
+++ b/tools/metrics/histograms/metadata/media/enums.xml
@@ -1239,12 +1239,6 @@
   <int value="2" label="MediaStream"/>
 </enum>
 
-<enum name="MediaNotificationClickSource">
-  <int value="0" label="Media"/>
-  <int value="1" label="Presentation"/>
-  <int value="2" label="MediaFling"/>
-</enum>
-
 <enum name="MediaNotificationDisplayPage">
   <int value="0" label="Unknown"/>
   <int value="1" label="Quick settings media view"/>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index 0cc9af9..4cd87c0 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -4242,15 +4242,6 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Notification.Click" enum="MediaNotificationClickSource"
-    expires_after="2020-06-07">
-  <owner>mlamouri@chromium.org</owner>
-  <owner>media-dev-uma@chromium.org</owner>
-  <summary>
-    The type of media notification the user clicked to go back to Chrome.
-  </summary>
-</histogram>
-
 <histogram name="Media.Notification.Count" units="count"
     expires_after="2024-06-02">
   <owner>yrw@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index caccd1b..1d659c7 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -290,21 +290,6 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.Browser.MemoryFootprint.NumLiveOverscroll" units="tabs"
-    expires_after="2021-09-12">
-  <owner>ajuma@chromium.org</owner>
-  <owner>rkgibson@google.com</owner>
-  <summary>
-    Investigation into crbug.com/1102494 shows that OverscrollActionsController
-    is allocating thousands of objects in it's -initWithScrollView:. This is
-    likely because thousands of OverscrollActionsControllers are being
-    initialized. The theory is that there are users that have many many tabs,
-    causing many OverscrollActionsControllers to be allocated. This histogram
-    tests that theory by logging how many live OverscrollActionsControllers
-    there are. This is recorded every time histograms are uploaded.
-  </summary>
-</histogram>
-
 <histogram name="Memory.Browser.MemoryFootprint.NumOpenTabs" units="tabs"
     expires_after="2024-04-28">
   <owner>ajuma@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/net/enums.xml b/tools/metrics/histograms/metadata/net/enums.xml
index f4c89be..f58bf02 100644
--- a/tools/metrics/histograms/metadata/net/enums.xml
+++ b/tools/metrics/histograms/metadata/net/enums.xml
@@ -124,6 +124,19 @@
   <int value="5" label="HTTP_NETWORK_TRANSACTION"/>
 </enum>
 
+<enum name="CacheAccessStatus">
+  <int value="0" label="Hit">
+    Recorded when cache entry was found and produced a classification.
+  </int>
+  <int value="1" label="Not found">
+    Recorded when cache entry was not found.
+  </int>
+  <int value="2" label="Outdated">
+    Recorded when cache entry was found but did not produce a classification,
+    because it was outdated.
+  </int>
+</enum>
+
 <enum name="CertificateChangeNotificationType">
   <int value="0" label="Trust"/>
   <int value="1" label="Client Cert"/>
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index 91c8a3b..96cee27 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -1723,16 +1723,6 @@
   </summary>
 </histogram>
 
-<histogram name="Net.ErrorPageCounts.SuggestionClicked"
-    enum="NetErrorOfflineSuggestionType" expires_after="2021-12-19">
-  <owner>sclittle@chromium.org</owner>
-  <owner>offline-dev@chromium.org</owner>
-  <summary>
-    Counts of the type of offline content suggestions clicked on the network
-    error page. Reported upon click.
-  </summary>
-</histogram>
-
 <histogram name="Net.ErrorPageCounts.SuggestionPresented"
     enum="NetErrorOfflineSuggestionType" expires_after="2021-12-05">
   <owner>sclittle@chromium.org</owner>
@@ -4699,6 +4689,17 @@
   </summary>
 </histogram>
 
+<histogram name="Net.SafeSearch.CacheHit" enum="CacheAccessStatus"
+    expires_after="2024-04-28">
+  <owner>tju@google.com</owner>
+  <owner>chrome-kids-eng@google.com</owner>
+  <summary>
+    Records if a URL classification in safe_search_api::URLChecker was provided
+    from the cache or not. In the latter case, it distinguishes between true
+    cache misses and cache hits which were unfit for classification.
+  </summary>
+</histogram>
+
 <histogram name="Net.SharedDictionaryManagerOnDisk.DictionarySizeKB" units="KB"
     expires_after="2024-03-03">
   <owner>horo@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/settings/histograms.xml b/tools/metrics/histograms/metadata/settings/histograms.xml
index 9ab33d3..347b924 100644
--- a/tools/metrics/histograms/metadata/settings/histograms.xml
+++ b/tools/metrics/histograms/metadata/settings/histograms.xml
@@ -497,7 +497,7 @@
 </histogram>
 
 <histogram name="Settings.SafetyCheck.NotificationsModuleEntryPointShown"
-    enum="BooleanVisible" expires_after="2023-12-21">
+    enum="BooleanVisible" expires_after="2024-06-01">
   <owner>sideyilmaz@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
   <owner>hkamila@chromium.org</owner>
@@ -593,7 +593,7 @@
 
 <histogram
     name="Settings.SafetyCheck.UnusedSitePermissionsRegrantDays{SourceUI}.All"
-    units="days" expires_after="2023-12-21">
+    units="days" expires_after="2024-06-01">
   <owner>sideyilmaz@chromium.org</owner>
   <owner>tov@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
@@ -612,7 +612,7 @@
 
 <histogram
     name="Settings.SafetyCheck.UnusedSitePermissionsRegrantDays{SourceUI}.{PermissionType}"
-    units="days" expires_after="2023-12-21">
+    units="days" expires_after="2024-06-01">
   <owner>sideyilmaz@chromium.org</owner>
   <owner>tov@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/signin/histograms.xml b/tools/metrics/histograms/metadata/signin/histograms.xml
index dd110110..70b366f 100644
--- a/tools/metrics/histograms/metadata/signin/histograms.xml
+++ b/tools/metrics/histograms/metadata/signin/histograms.xml
@@ -1498,6 +1498,24 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Signin.ShowSigninCoordinatorWhenAlreadyPresent.{View}AccessPoint"
+    enum="SigninAccessPoint" expires_after="2024-12-01">
+  <owner>arthurmilchior@chromium.org</owner>
+  <owner>chrome-signin-team@google.com</owner>
+  <owner>msarda@chromium.org</owner>
+  <owner>jlebel@chromium.org</owner>
+  <summary>
+    This histogram is recorded when two sign-in views are being opened at the
+    same time. This indicates the access point which triggered the {View} to be
+    opened.
+  </summary>
+  <token key="View">
+    <variant name="New" summary="second"/>
+    <variant name="Old" summary="first"/>
+  </token>
+</histogram>
+
 <histogram name="Signin.SignIn.Completed" enum="SigninAccessPoint"
     expires_after="2024-04-28">
   <owner>bsazonov@chromium.org</owner>
diff --git a/tools/rust/build_rust.py b/tools/rust/build_rust.py
index c727853..77f562d 100755
--- a/tools/rust/build_rust.py
+++ b/tools/rust/build_rust.py
@@ -763,6 +763,11 @@
         GitCherryPick(RUST_SRC_DIR, 'https://github.com/rust-lang/rust.git',
                       '46a801559127441675f2341bd1d684809a47def1')
 
+        # TODO: Remove once
+        # https://github.com/rust-lang/rust/pull/118941 has been merged.
+        GitCherryPick(RUST_SRC_DIR, 'https://github.com/rust-lang/rust.git',
+                      '0a285e8de7eec36e1de68c83764d23f2522a4274')
+
         path = FetchBetaPackage('cargo', checkout_revision)
         if sys.platform == 'win32':
             cargo_bin = os.path.join(path, 'cargo', 'bin', 'cargo.exe')
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html
index b6cb4fe..fbf8b09 100644
--- a/ui/file_manager/file_manager/main.html
+++ b/ui/file_manager/file_manager/main.html
@@ -355,7 +355,7 @@
                       slot="jelly"
                       id="pinned-toggle-jelly"
                       command="#toggle-pinned"
-                      aria-labelledby="pinned-toggle-label">
+                      aria-label="$i18n{OFFLINE_COLUMN_LABEL}">
                   </cros-switch>
                   <cr-toggle
                       slot="old"
diff --git a/ui/ozone/platform/headless/headless_window.cc b/ui/ozone/platform/headless/headless_window.cc
index ba5852b..df7fdfd 100644
--- a/ui/ozone/platform/headless/headless_window.cc
+++ b/ui/ozone/platform/headless/headless_window.cc
@@ -92,8 +92,15 @@
     UpdateWindowState(PlatformWindowState::kNormal);
   }
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  delegate_->OnFullscreenModeChanged();
-#endif
+  // Setting kImmersive when it's fullscreen on headless window since the
+  // immersive fullscreen is default for fullscreen on Lacros.
+  delegate_->OnFullscreenTypeChanged(
+      window_state_ == PlatformWindowState::kFullScreen
+          ? PlatformFullscreenType::kImmersive
+          : PlatformFullscreenType::kNone,
+      fullscreen ? PlatformFullscreenType::kImmersive
+                 : PlatformFullscreenType::kNone);
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 }
 
 void HeadlessWindow::Maximize() {
diff --git a/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h b/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h
index 63f185d..735a218 100644
--- a/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h
+++ b/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h
@@ -84,10 +84,6 @@
   // Sets a native window's immersive mode.
   virtual void SetUseImmersiveMode(bool immersive) = 0;
 
-  // Whether the shell supports top level immersive status. The deprecated
-  // immersive status used to be set on the surface level.
-  virtual bool SupportsTopLevelImmersiveStatus() const = 0;
-
   // Sets the top inset (header) height which is reserved or occupied by the top
   // window frame.
   virtual void SetTopInset(int height) = 0;
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
index 33b5727..ea8d63ab 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -558,17 +558,17 @@
   fullscreen_display_id_ = display::kInvalidDisplayId;
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  if (shell_toplevel_ && shell_toplevel()->SupportsTopLevelImmersiveStatus() &&
-      is_immersive_fullscreen_ != window_states.is_immersive_fullscreen) {
-    is_immersive_fullscreen_ = window_states.is_immersive_fullscreen;
-    delegate()->OnImmersiveModeChanged(is_immersive_fullscreen_);
-  }
-
-  if (is_fullscreen_ != window_states.is_fullscreen) {
-    is_fullscreen_ = window_states.is_fullscreen;
+  CHECK(!window_states.is_immersive_fullscreen || window_states.is_fullscreen);
+  PlatformFullscreenType fullscreen_type =
+      window_states.is_immersive_fullscreen
+          ? PlatformFullscreenType::kImmersive
+          : (window_states.is_fullscreen ? PlatformFullscreenType::kPlain
+                                         : PlatformFullscreenType::kNone);
+  if (fullscreen_type_ != fullscreen_type) {
     // The fullscreen state change has finished and we we need to inform the
     // browser/app that the transition is done.
-    delegate()->OnFullscreenModeChanged();
+    delegate()->OnFullscreenTypeChanged(fullscreen_type_, fullscreen_type);
+    fullscreen_type_ = fullscreen_type;
   }
 #endif
 
@@ -822,21 +822,13 @@
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 void WaylandToplevelWindow::SetImmersiveFullscreenStatus(bool status) {
-  auto* zaura_surface = GetZAuraSurface();
-  if (shell_toplevel_ && shell_toplevel_->SupportsTopLevelImmersiveStatus()) {
+  if (shell_toplevel_) {
     shell_toplevel_->SetUseImmersiveMode(status);
-  } else if (zaura_surface &&
-             zaura_surface->SetFullscreenMode(
-                 status ? ZAURA_SURFACE_FULLSCREEN_MODE_IMMERSIVE
-                        : ZAURA_SURFACE_FULLSCREEN_MODE_PLAIN)) {
-    // TODO(ffred): the deprecated immersive mode flow used to transition
-    // immediately after sending the request to exo. This is needed to
-    // maintain backwards compatibility. Remove once we have rolled past the
-    // supported skew.
-    delegate()->OnImmersiveModeChanged(status);
   } else {
-    // TODO(https://crbug.com/1113900): Implement AuraShell support for
-    // non-browser windows and replace this if-else clause by a DCHECK.
+    // TODO(elkurin): Investigate whether we can deprecate this clause. This
+    // pass is used by some tests which do not set shell properly and ideally we
+    // would like to fix those tests. After those fixes, remove this clause or
+    // replace it by CHECK.
     NOTIMPLEMENTED_LOG_ONCE();
     // TODO(https://crbug.com/1113900): With Lacros, the state change gets
     // completed asynchronously (see removal of notification call in
@@ -844,7 +836,10 @@
     // application now. This needs also be properly addressed with the
     // immersive mode change inside the immersive mode handling by calling
     // this delegate - or `BrowserView::FullscreenStateChanged()` directly.
-    delegate()->OnFullscreenModeChanged();
+    auto new_type = status ? PlatformFullscreenType::kImmersive
+                           : PlatformFullscreenType::kPlain;
+    delegate()->OnFullscreenTypeChanged(fullscreen_type_, new_type);
+    fullscreen_type_ = new_type;
   }
 }
 
@@ -1016,7 +1011,18 @@
 void WaylandToplevelWindow::DumpState(std::ostream& out) const {
   WaylandWindow::DumpState(out);
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  out << ", is_immersive_fullscreen=" << ToBoolString(is_immersive_fullscreen_);
+  out << ", fullscreen_type=";
+  switch (fullscreen_type_) {
+    case PlatformFullscreenType::kNone:
+      out << "not fullscreen";
+      break;
+    case PlatformFullscreenType::kPlain:
+      out << "plain fullscreen";
+      break;
+    case PlatformFullscreenType::kImmersive:
+      out << "immersive fullscreen";
+      break;
+  }
 #endif
   out << ", title=" << window_title_
       << ", is_active=" << ToBoolString(is_active_)
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
index 2455bca7..2e0a395 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
@@ -267,11 +267,9 @@
   bool is_active_ = false;
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  bool is_immersive_fullscreen_ = false;
-
-  // This is used to detect fullscreen state changes from the Aura side
+  // This is used to detect fullscreen type changes from the Aura side
   // to inform Lacros clients from the asynchronous task completion.
-  bool is_fullscreen_ = false;
+  PlatformFullscreenType fullscreen_type_ = PlatformFullscreenType::kNone;
 
   // Unique ID for this window. May be shared over non-Wayland IPC transports
   // (e.g. mojo) to identify the window.
diff --git a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
index dfae20e..92d79ed 100644
--- a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
@@ -1186,7 +1186,10 @@
 
   // Now, set to fullscreen.
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  EXPECT_CALL(delegate_, OnFullscreenModeChanged()).Times(1);
+  EXPECT_CALL(delegate_,
+              OnFullscreenTypeChanged(PlatformFullscreenType::kNone,
+                                      PlatformFullscreenType::kPlain))
+      .Times(1);
 #endif
   EXPECT_CALL(delegate_,
               OnWindowStateChanged(_, Eq(PlatformWindowState::kFullScreen)))
@@ -1203,7 +1206,9 @@
 
   // Unfullscreen
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  EXPECT_CALL(delegate_, OnFullscreenModeChanged()).Times(1);
+  EXPECT_CALL(delegate_, OnFullscreenTypeChanged(PlatformFullscreenType::kPlain,
+                                                 PlatformFullscreenType::kNone))
+      .Times(1);
 #endif
   EXPECT_CALL(delegate_,
               OnWindowStateChanged(_, Eq(PlatformWindowState::kNormal)))
@@ -1240,7 +1245,10 @@
   AdvanceFrameToCurrent(window_.get(), delegate_);
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  EXPECT_CALL(delegate_, OnFullscreenModeChanged()).Times(1);
+  EXPECT_CALL(delegate_,
+              OnFullscreenTypeChanged(PlatformFullscreenType::kNone,
+                                      PlatformFullscreenType::kPlain))
+      .Times(1);
 #endif
   EXPECT_CALL(delegate_,
               OnWindowStateChanged(_, Eq(PlatformWindowState::kFullScreen)))
@@ -1257,7 +1265,9 @@
 
   // Restore
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  EXPECT_CALL(delegate_, OnFullscreenModeChanged()).Times(1);
+  EXPECT_CALL(delegate_, OnFullscreenTypeChanged(PlatformFullscreenType::kPlain,
+                                                 PlatformFullscreenType::kNone))
+      .Times(1);
 #endif
   EXPECT_CALL(delegate_,
               OnWindowStateChanged(_, Eq(PlatformWindowState::kNormal)))
@@ -4219,7 +4229,10 @@
   auto toplevel = CreateWaylandWindowWithParams(
       PlatformWindowType::kWindow, gfx::Rect(10, 10, 200, 200), &delegate_2);
 
-  EXPECT_CALL(delegate_2, OnImmersiveModeChanged(true)).Times(1);
+  EXPECT_CALL(delegate_2,
+              OnFullscreenTypeChanged(PlatformFullscreenType::kNone,
+                                      PlatformFullscreenType::kImmersive))
+      .Times(1);
   toplevel->HandleAuraToplevelConfigure(0, 0, 0, 0,
                                         {.is_maximized = false,
                                          .is_fullscreen = true,
@@ -4248,7 +4261,10 @@
                                          .is_activated = true});
   toplevel->HandleSurfaceConfigure(++serial);
 
-  EXPECT_CALL(delegate_2, OnImmersiveModeChanged(false)).Times(1);
+  EXPECT_CALL(delegate_2,
+              OnFullscreenTypeChanged(PlatformFullscreenType::kImmersive,
+                                      PlatformFullscreenType::kNone))
+      .Times(1);
   toplevel->HandleAuraToplevelConfigure(0, 0, 0, 0,
                                         {.is_maximized = false,
                                          .is_fullscreen = false,
diff --git a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc
index 814a86c..642abc2 100644
--- a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc
+++ b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc
@@ -195,16 +195,9 @@
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 void XDGToplevelWrapperImpl::SetUseImmersiveMode(bool immersive) {
-  if (SupportsTopLevelImmersiveStatus()) {
-    auto mode = immersive ? ZAURA_TOPLEVEL_FULLSCREEN_MODE_IMMERSIVE
-                          : ZAURA_TOPLEVEL_FULLSCREEN_MODE_PLAIN;
-    zaura_toplevel_set_fullscreen_mode(aura_toplevel_.get(), mode);
-  }
-}
-
-bool XDGToplevelWrapperImpl::SupportsTopLevelImmersiveStatus() const {
-  return aura_toplevel_ && zaura_toplevel_get_version(aura_toplevel_.get()) >=
-                               ZAURA_TOPLEVEL_SET_FULLSCREEN_MODE_SINCE_VERSION;
+  auto mode = immersive ? ZAURA_TOPLEVEL_FULLSCREEN_MODE_IMMERSIVE
+                        : ZAURA_TOPLEVEL_FULLSCREEN_MODE_PLAIN;
+  zaura_toplevel_set_fullscreen_mode(aura_toplevel_.get(), mode);
 }
 
 void XDGToplevelWrapperImpl::SetTopInset(int height) {
diff --git a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h
index 854ebc9..47604d47 100644
--- a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h
+++ b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h
@@ -41,7 +41,6 @@
   void UnSetFullscreen() override;
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
   void SetUseImmersiveMode(bool immersive) override;
-  bool SupportsTopLevelImmersiveStatus() const override;
   void SetTopInset(int height) override;
   void SetShadowCornersRadii(const gfx::RoundedCornersF& radii) override;
 #endif
diff --git a/ui/ozone/test/mock_platform_window_delegate.h b/ui/ozone/test/mock_platform_window_delegate.h
index e63156d..18db812 100644
--- a/ui/ozone/test/mock_platform_window_delegate.h
+++ b/ui/ozone/test/mock_platform_window_delegate.h
@@ -35,8 +35,12 @@
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
   MOCK_METHOD1(OnWindowTiledStateChanged,
                void(WindowTiledEdges new_tiled_edges));
-  MOCK_METHOD0(OnFullscreenModeChanged, void());
 #endif
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  MOCK_METHOD2(OnFullscreenTypeChanged,
+               void(PlatformFullscreenType old_type,
+                    PlatformFullscreenType new_type));
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
   MOCK_METHOD0(OnLostCapture, void());
   MOCK_METHOD1(OnAcceleratedWidgetAvailable,
                void(gfx::AcceleratedWidget widget));
@@ -49,7 +53,6 @@
   MOCK_METHOD0(GetOwnedWindowAnchorAndRectInDIP,
                absl::optional<OwnedWindowAnchor>());
   MOCK_METHOD0(OnMouseEnter, void());
-  MOCK_METHOD1(OnImmersiveModeChanged, void(bool immersive));
   MOCK_METHOD1(OnOverviewModeChanged, void(bool overview));
   MOCK_METHOD2(OnRotateFocus,
                bool(PlatformWindowDelegate::RotateDirection, bool));
diff --git a/ui/platform_window/platform_window_delegate.cc b/ui/platform_window/platform_window_delegate.cc
index 6ce982e8..b94be90e 100644
--- a/ui/platform_window/platform_window_delegate.cc
+++ b/ui/platform_window/platform_window_delegate.cc
@@ -42,7 +42,9 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-void PlatformWindowDelegate::OnFullscreenModeChanged() {}
+void PlatformWindowDelegate::OnFullscreenTypeChanged(
+    PlatformFullscreenType old_type,
+    PlatformFullscreenType new_type) {}
 #endif
 
 absl::optional<gfx::Size> PlatformWindowDelegate::GetMinimumSizeForWindow() {
diff --git a/ui/platform_window/platform_window_delegate.h b/ui/platform_window/platform_window_delegate.h
index fa43b7c..42f0e2a 100644
--- a/ui/platform_window/platform_window_delegate.h
+++ b/ui/platform_window/platform_window_delegate.h
@@ -45,6 +45,24 @@
   kFloated,
 };
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+enum class PlatformFullscreenType {
+  // kNone represents a non-fullscreen state. This should be set for most cases
+  // except for the window state is `kFullscreen`.
+  kNone,
+
+  // kPlain represents a fullscreen mode without immersive feature. This
+  // corresponds to fullscreen + non-immersive mode. The window state must be
+  // 'kFullscreen`. This state is also used by the locked fullscreen or pinned
+  // mode in other words.
+  kPlain,
+
+  // kImmersive represents a immersive fullscreen mode. This corresponds to
+  // fullscreen + immersive mode. The window state must be `kFullscreen`.
+  kImmersive,
+};
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
 enum class PlatformWindowOcclusionState {
   kUnknown,
   kVisible,
@@ -140,17 +158,13 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  // Notifies delegate that the pending fullscreen operation has been completed.
-  virtual void OnFullscreenModeChanged();
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
   // TODO(ffred): We should just add kImmersiveFullscreen as a state. However,
   // that will require more refactoring in other places to understand that
   // kImmersiveFullscreen is a fullscreen status.
-  // Sets the immersive mode for the window. This will only have an effect on
-  // ChromeOS platforms.
-  virtual void OnImmersiveModeChanged(bool immersive) {}
+  //
+  // Notifies that fullscreen type has changed.
+  virtual void OnFullscreenTypeChanged(PlatformFullscreenType old_type,
+                                       PlatformFullscreenType new_type);
 
   // Lets the window know that ChromeOS overview mode has changed.
   virtual void OnOverviewModeChanged(bool in_overview) {}
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_lacros.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_lacros.cc
index e31ee7c..5028a156 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_lacros.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_lacros.cc
@@ -74,6 +74,10 @@
   void OnTouchEvent(ui::TouchEvent* event) override { event->SetHandled(); }
 };
 
+bool IsImmersive(ui::PlatformFullscreenType type) {
+  return type == ui::PlatformFullscreenType::kImmersive;
+}
+
 }  // namespace
 namespace views {
 
@@ -128,6 +132,7 @@
   DestroyNonClientEventFilter();
   DesktopWindowTreeHostPlatform::OnClosed();
 }
+
 void DesktopWindowTreeHostLacros::OnWindowStateChanged(
     ui::PlatformWindowState old_window_show_state,
     ui::PlatformWindowState new_window_show_state) {
@@ -138,10 +143,15 @@
       ToChromeosWindowStateType(new_window_show_state));
 }
 
-void DesktopWindowTreeHostLacros::OnImmersiveModeChanged(bool enabled) {
+void DesktopWindowTreeHostLacros::OnFullscreenTypeChanged(
+    ui::PlatformFullscreenType old_type,
+    ui::PlatformFullscreenType new_type) {
   // Keep in sync with ImmersiveFullscreenController::Enable for widget. See
   // comment there for details.
-  GetContentWindow()->SetProperty(chromeos::kImmersiveIsActive, enabled);
+  if (IsImmersive(old_type) != IsImmersive(new_type)) {
+    GetContentWindow()->SetProperty(chromeos::kImmersiveIsActive,
+                                    IsImmersive(new_type));
+  }
 }
 
 void DesktopWindowTreeHostLacros::OnOverviewModeChanged(bool in_overview) {
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_lacros.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_lacros.h
index 88ed9243..be6d51d 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_lacros.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_lacros.h
@@ -64,7 +64,8 @@
   void OnWindowStateChanged(
       ui::PlatformWindowState old_window_show_state,
       ui::PlatformWindowState new_window_show_state) override;
-  void OnImmersiveModeChanged(bool enabled) override;
+  void OnFullscreenTypeChanged(ui::PlatformFullscreenType old_type,
+                               ui::PlatformFullscreenType new_type) override;
   void OnOverviewModeChanged(bool in_overview) override;
   void OnTooltipShownOnServer(const std::u16string& text,
                               const gfx::Rect& bounds) override;