diff --git a/AUTHORS b/AUTHORS
index 7f036859..39e9b86 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -729,6 +729,7 @@
 Kalyan Kondapally <kalyan.kondapally@intel.com>
 Kamil Jiwa <kamil.jiwa@gmail.com>
 Kamil Rytarowski <krytarowski@gmail.com>
+Kanaru Sato <i.am.kanaru.sato@gmail.com>
 Kangil Han <kangil.han@samsung.com>
 Kangyuan Shu <kangyuan.shu@intel.com>
 Karan Thakkar <karanjthakkar@gmail.com>
diff --git a/BUILD.gn b/BUILD.gn
index ff7aeab..cc7a8dd 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -851,7 +851,7 @@
 
   if (enable_rust_mojo) {
     deps += [
-      "//mojo/public/rust",
+      "//mojo/public/rust:mojo_rust",
       "//mojo/public/rust:mojo_rust_integration_unittests",
       "//mojo/public/rust:mojo_rust_unittests",
     ]
diff --git a/DEPS b/DEPS
index a8ebe5e..bd2e2d2b7 100644
--- a/DEPS
+++ b/DEPS
@@ -303,19 +303,19 @@
   # 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': 'cf84bab13da3201dc3164a5c938fb70cc9458d5a',
+  'skia_revision': '01aa69c38af545e361bcf8d9b0f8688f19d4244a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '3469157c02160bf1e6035c2a15087e7702fa75a0',
+  'v8_revision': 'e6049fb98502acb2afb1ec9f5181c3db5e08c9b7',
   # 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': 'dea00fff8ae1f6fadba47474826c9465568a551e',
+  'angle_revision': 'a950f0057dbfc4a4924a0dcfa3970ec1e807c3fa',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '77be35e337bb80df52c5ea4dd4cf2d2072abad25',
+  'swiftshader_revision': '2fa7e9b99ae4e70ea5ae2cc9c8d3afb43391384f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # 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': '68cb7ee131434b825dca5e465653e3292abaf116',
+  'chromium_variations_revision': '78c4cb498a6ce49194e7e1c140e292eb76a9c630',
   # 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': '699358f39b61599f314f72580e9f8ff096785bed',
+  'devtools_frontend_revision': '87acf6b1ff2cb02f2bd838a821790727a83f0414',
   # 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': '762e826397ab42b8379d9bf3063117b6530be6a3',
+  'dawn_revision': '9c54476ce64f21d6878ede05af9397dd55973a14',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -490,11 +490,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling beto-core
   # and whatever else without interference from each other.
-  'betocore_revision': '1aeda0e62218efbaf48e7b8d839dc2a2d8a2bbf1',
+  'betocore_revision': '4d202dab960a0b6a6e4757ab4393945aca5a09db',
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
-  'libcxx_revision':       '99f5e4d09e3014e1b178dfd7ebf451e652461ad6',
+  'libcxx_revision':       '6226f31a8c995612018d53c7accad729a028e9c0',
 
   # GN CIPD package version.
   'gn_version': 'git_revision:85944ebc24a90ec1e489e85a46fdc68542c3146f',
@@ -818,7 +818,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '4131ebd0819f60c9621c3e17362fc1e6e0bd14ed',
+    '68277d9bc7d17ef27563d83fcc443e33fbeec22f',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -827,7 +827,7 @@
   },
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '3b476eb5360c12898c019df2829f2b42d2682738',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '40fc9251e010da0a2a0d6351617a6959e7fe90a8',
       'condition': 'checkout_ios',
   },
 
@@ -980,7 +980,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'whg3i36GbZEjncDbs6fRE8BMOKoUfsWJnBb8ovLn_JAC',
+          'version': 'lk2zuSttomSkFcwnwmWmHtuIYwRjPxUm7XJgsNFsdGEC',
       },
     ],
     'condition': 'checkout_android',
@@ -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' + '@' + '1e99db8ac5adeec92e59c8b9f602808af61444a9',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '7978586bb4d2abcb15c816ec7c9e0d7d4b299a9c',
     'condition': 'checkout_src_internal',
   },
 
@@ -1751,7 +1751,7 @@
     Var('chromium_git') + '/external/github.com/google/snappy.git' + '@' + 'c9f9edf6d75bb065fa47468bf035e051a57bec7c',
 
   'src/third_party/sqlite/src':
-    Var('chromium_git') + '/chromium/deps/sqlite.git' + '@' + 'a7a54e1dd9b6124c725a35552d48d6ce0e3c1ef9',
+    Var('chromium_git') + '/chromium/deps/sqlite.git' + '@' + 'cd9486849ba3c3ec753f556fd29c0aabee122a28',
 
   'src/third_party/sqlite4java': {
       'packages': [
@@ -1843,10 +1843,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'f4bf599a8b575df685c31d9c4729a70a04e377ed',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'dac947fdd70cf6ec088e1c621f58eaf99430175f',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'eb1892c3e4ab734a135e8752f5442e270a4738d1',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'ac60ad7acd95149fa7bbd61a2f71e27b019ff96a',
+    Var('webrtc_git') + '/src.git' + '@' + 'c9d44b3fb94511e92879f3b6061aee117474cb27',
 
   # 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.
@@ -3949,7 +3949,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        '82e3e31f26bc1c980b5616c7845c7fff4df69fa2',
+        '3e64fe978f46937931741c6842958594b6ed7e9a',
       'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index f1c0955..c0a32235 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -19,7 +19,6 @@
 import("//chrome/android/trichrome.gni")
 import("//components/safe_browsing/buildflags.gni")
 import("//components/services/screen_ai/buildflags/features.gni")
-import("//components/signin/features.gni")
 import("//components/spellcheck/spellcheck_build_features.gni")
 import("//components/supervised_user/buildflags.gni")
 import("//device/vr/buildflags/buildflags.gni")
@@ -1174,7 +1173,6 @@
     "enable_pdf=$enable_pdf",
     "enable_print_preview=$enable_print_preview",
     "enable_screen_ai_service=$enable_screen_ai_service",
-    "enable_search_engine_choice=$enable_search_engine_choice",
     "enable_vr=$enable_vr",
     "use_blink=$use_blink",
   ]
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index f5845dd..72608f5 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1603,6 +1603,8 @@
     "system/input_device_settings/input_device_settings_dispatcher.h",
     "system/input_device_settings/input_device_settings_logging.cc",
     "system/input_device_settings/input_device_settings_logging.h",
+    "system/input_device_settings/input_device_settings_metadata.cc",
+    "system/input_device_settings/input_device_settings_metadata.h",
     "system/input_device_settings/input_device_settings_metrics_manager.cc",
     "system/input_device_settings/input_device_settings_metrics_manager.h",
     "system/input_device_settings/input_device_settings_notification_controller.cc",
@@ -3548,6 +3550,7 @@
     "system/input_device_settings/input_device_settings_controller_unittest.cc",
     "system/input_device_settings/input_device_settings_dispatcher_unittest.cc",
     "system/input_device_settings/input_device_settings_logging_unittest.cc",
+    "system/input_device_settings/input_device_settings_metadata_unittest.cc",
     "system/input_device_settings/input_device_settings_metrics_manager_unittest.cc",
     "system/input_device_settings/input_device_settings_notification_controller_unittest.cc",
     "system/input_device_settings/input_device_settings_policy_handler_unittest.cc",
@@ -3993,6 +3996,7 @@
     "//components/app_restore",
     "//components/desks_storage",
     "//components/desks_storage:test_support",
+    "//components/feature_engagement/public",
     "//components/global_media_controls",
     "//components/language/core/browser:browser",
     "//components/live_caption:constants",
@@ -4185,6 +4189,7 @@
     "//chromeos/dbus/power:power",
     "//chromeos/dbus/power:power_manager_proto",
     "//chromeos/services/network_config/public/cpp:test_support",
+    "//components/feature_engagement/public",
     "//components/user_education/common",
     "//components/user_education/views",
     "//components/user_manager",
diff --git a/ash/app_list/BUILD.gn b/ash/app_list/BUILD.gn
index 1bb7d7d..593449dd 100644
--- a/ash/app_list/BUILD.gn
+++ b/ash/app_list/BUILD.gn
@@ -184,6 +184,7 @@
     "//chromeos/ui/vector_icons",
     "//chromeos/ui/wm",
     "//chromeos/utils",
+    "//components/feature_engagement/public",
     "//components/keyed_service/core",
     "//components/pref_registry",
     "//components/prefs",
@@ -329,6 +330,7 @@
     "//base/test:test_support",
     "//chromeos/ash/components/feature_usage",
     "//chromeos/ash/services/assistant/public/cpp",
+    "//components/feature_engagement/public",
     "//mojo/core/embedder",
     "//mojo/public/cpp/bindings",
     "//skia",
diff --git a/ash/app_list/DEPS b/ash/app_list/DEPS
index f66bdb7..ef5e9546 100644
--- a/ash/app_list/DEPS
+++ b/ash/app_list/DEPS
@@ -5,6 +5,7 @@
   "+ash/resources/vector_icons",
   "+ash/strings",
   "+chromeos/utils",
+  "+components/feature_engagement/public/feature_constants.h",
   "+components/keyed_service/core",
   "+components/sync",
   "+mojo/public/cpp",
diff --git a/ash/app_list/views/app_list_view_pixeltest.cc b/ash/app_list/views/app_list_view_pixeltest.cc
index 8ccf9d28..22e660e 100644
--- a/ash/app_list/views/app_list_view_pixeltest.cc
+++ b/ash/app_list/views/app_list_view_pixeltest.cc
@@ -24,8 +24,7 @@
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "chromeos/constants/chromeos_features.h"
+#include "components/feature_engagement/public/feature_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/events/types/event_type.h"
@@ -461,8 +460,8 @@
   }
 
   void SetUp() override {
-    scoped_features_.InitWithFeatureStates(
-        {{assistant::features::kEnableAssistantLearnMore, true}});
+    scoped_feature_list_.InitAndEnableFeature(
+        feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
     AssistantAshTestBase::SetUp();
     SetNumberOfSessionsWhereOnboardingShown(
@@ -475,7 +474,7 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_features_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 INSTANTIATE_TEST_SUITE_P(RTL,
diff --git a/ash/app_list/views/assistant/assistant_main_stage.cc b/ash/app_list/views/assistant/assistant_main_stage.cc
index add1322..b8edaf2 100644
--- a/ash/app_list/views/assistant/assistant_main_stage.cc
+++ b/ash/app_list/views/assistant/assistant_main_stage.cc
@@ -18,13 +18,13 @@
 #include "ash/assistant/ui/main_stage/ui_element_container_view.h"
 #include "ash/assistant/util/animation_util.h"
 #include "ash/assistant/util/assistant_util.h"
-#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/assistant/controller/assistant_interaction_controller.h"
 #include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
 #include "ash/public/cpp/style/color_provider.h"
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/time/time.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
+#include "components/feature_engagement/public/feature_constants.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/color/color_id.h"
@@ -120,7 +120,8 @@
     AssistantViewDelegate* delegate)
     : delegate_(delegate) {
   SetID(AssistantViewID::kMainStage);
-  if (assistant::features::IsAssistantLearnMoreEnabled()) {
+  if (base::FeatureList::IsEnabled(
+          feature_engagement::kIPHLauncherSearchHelpUiFeature)) {
     InitLayoutWithIph();
   } else {
     InitLayout();
@@ -480,7 +481,8 @@
   assistant::util::FadeOutAndHide(zero_state_view_,
                                   kZeroStateAnimationFadeOutDuration);
 
-  if (assistant::features::IsAssistantLearnMoreEnabled()) {
+  if (base::FeatureList::IsEnabled(
+          feature_engagement::kIPHLauncherSearchHelpUiFeature)) {
     AnimateInFooter();
   }
 }
@@ -499,7 +501,8 @@
   } else {
     AnimateInZeroState();
 
-    if (assistant::features::IsAssistantLearnMoreEnabled()) {
+    if (base::FeatureList::IsEnabled(
+            feature_engagement::kIPHLauncherSearchHelpUiFeature)) {
       footer_->SetVisible(false);
     } else {
       AnimateInFooter();
diff --git a/ash/app_list/views/assistant/assistant_main_stage_unittest.cc b/ash/app_list/views/assistant/assistant_main_stage_unittest.cc
index 181e835..ba1798a 100644
--- a/ash/app_list/views/assistant/assistant_main_stage_unittest.cc
+++ b/ash/app_list/views/assistant/assistant_main_stage_unittest.cc
@@ -12,7 +12,7 @@
 #include "ash/style/ash_color_provider.h"
 #include "ash/style/dark_light_mode_controller_impl.h"
 #include "base/test/scoped_feature_list.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
+#include "components/feature_engagement/public/feature_constants.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/color/color_id.h"
 #include "ui/gfx/canvas.h"
@@ -72,7 +72,7 @@
 TEST_F(AssistantMainStageTest, FooterIsVisible) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndDisableFeature(
-      assistant::features::kEnableAssistantLearnMore);
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   ShowAssistantUi();
 
@@ -82,7 +82,7 @@
 
 TEST_F(AssistantMainStageTest, FooterIsNotVisible) {
   base::test::ScopedFeatureList scoped_feature_list(
-      assistant::features::kEnableAssistantLearnMore);
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   ShowAssistantUi();
 
@@ -92,7 +92,7 @@
 
 TEST_F(AssistantMainStageTest, FooterIsVisibleAfterQuery) {
   base::test::ScopedFeatureList scoped_feature_list(
-      assistant::features::kEnableAssistantLearnMore);
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   ShowAssistantUi();
 
@@ -105,7 +105,7 @@
 
 TEST_F(AssistantMainStageTest, FooterIsVisibleAfterResponse) {
   base::test::ScopedFeatureList scoped_feature_list(
-      assistant::features::kEnableAssistantLearnMore);
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   ShowAssistantUi();
 
@@ -119,7 +119,7 @@
 TEST_F(AssistantMainStageTest, FooterIsVisible_Tablet) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndDisableFeature(
-      assistant::features::kEnableAssistantLearnMore);
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   SetTabletMode(true);
   ShowAssistantUi();
@@ -130,7 +130,7 @@
 
 TEST_F(AssistantMainStageTest, FooterIsNotVisible_Tablet) {
   base::test::ScopedFeatureList scoped_feature_list(
-      assistant::features::kEnableAssistantLearnMore);
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   SetTabletMode(true);
   ShowAssistantUi();
@@ -141,7 +141,7 @@
 
 TEST_F(AssistantMainStageTest, FooterIsVisibleAfterQuery_Tablet) {
   base::test::ScopedFeatureList scoped_feature_list(
-      assistant::features::kEnableAssistantLearnMore);
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   SetTabletMode(true);
   ShowAssistantUi();
@@ -157,7 +157,7 @@
 
 TEST_F(AssistantMainStageTest, FooterIsVisibleAfterResponse_Tablet) {
   base::test::ScopedFeatureList scoped_feature_list(
-      assistant::features::kEnableAssistantLearnMore);
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   SetTabletMode(true);
   ShowAssistantUi();
diff --git a/ash/assistant/ui/BUILD.gn b/ash/assistant/ui/BUILD.gn
index a6b07d1..089fbec 100644
--- a/ash/assistant/ui/BUILD.gn
+++ b/ash/assistant/ui/BUILD.gn
@@ -103,6 +103,7 @@
     "//chromeos/ash/services/assistant/public/mojom",
     "//chromeos/ash/services/libassistant/public/cpp:structs",
     "//chromeos/ui/vector_icons",
+    "//components/feature_engagement/public",
     "//components/vector_icons",
     "//ui/aura",
     "//ui/chromeos/styles:cros_styles_views",
diff --git a/ash/assistant/ui/DEPS b/ash/assistant/ui/DEPS
index b28d5de..484c287 100644
--- a/ash/assistant/ui/DEPS
+++ b/ash/assistant/ui/DEPS
@@ -19,6 +19,7 @@
   "+chromeos/ash/services/assistant/public/cpp",
   "+chromeos/ash/services/libassistant/public/cpp",
   "+chromeos/constants",
+  "+components/feature_engagement/public/feature_constants.h",
   "+mojo/public/cpp",
   "+net/base",
   "+third_party/skia/include/core",
diff --git a/ash/assistant/ui/main_stage/assistant_zero_state_view.cc b/ash/assistant/ui/main_stage/assistant_zero_state_view.cc
index 034dcb2f..a80b9d669 100644
--- a/ash/assistant/ui/main_stage/assistant_zero_state_view.cc
+++ b/ash/assistant/ui/main_stage/assistant_zero_state_view.cc
@@ -12,15 +12,15 @@
 #include "ash/assistant/ui/assistant_view_ids.h"
 #include "ash/assistant/ui/main_stage/assistant_onboarding_view.h"
 #include "ash/assistant/ui/main_stage/launcher_search_iph_view.h"
-#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/assistant/assistant_state.h"
 #include "ash/public/cpp/assistant/controller/assistant_controller.h"
 #include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_id.h"
+#include "base/feature_list.h"
 #include "base/notreached.h"
 #include "base/strings/string_piece.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
+#include "components/feature_engagement/public/feature_constants.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -44,14 +44,16 @@
 constexpr int kOnboardingViewTopMarginDip = 48;
 
 bool ShouldShowGreetingOrOnboarding(bool in_tablet_mode) {
-  if (assistant::features::IsAssistantLearnMoreEnabled()) {
+  if (base::FeatureList::IsEnabled(
+          feature_engagement::kIPHLauncherSearchHelpUiFeature)) {
     return !in_tablet_mode;
   }
   return true;
 }
 
 bool ShouldShowIph() {
-  return assistant::features::IsAssistantLearnMoreEnabled();
+  return base::FeatureList::IsEnabled(
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 }
 
 }  // namespace
diff --git a/ash/assistant/ui/main_stage/assistant_zero_state_view_unittest.cc b/ash/assistant/ui/main_stage/assistant_zero_state_view_unittest.cc
index 793d6e6d..7651ce78 100644
--- a/ash/assistant/ui/main_stage/assistant_zero_state_view_unittest.cc
+++ b/ash/assistant/ui/main_stage/assistant_zero_state_view_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chromeos/ash/services/assistant/public/cpp/features.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "components/feature_engagement/public/feature_constants.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/chromeos/styles/cros_styles.h"
 #include "ui/chromeos/styles/cros_tokens_color_mappings.h"
@@ -168,9 +169,9 @@
 }
 
 TEST_F(AssistantZeroStateViewUnittest, IphViewIsNotVisible) {
-  base::test::ScopedFeatureList feature_list_;
-  feature_list_.InitAndDisableFeature(
-      assistant::features::kEnableAssistantLearnMore);
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   ShowAssistantUi();
 
@@ -183,8 +184,8 @@
 }
 
 TEST_F(AssistantZeroStateViewUnittest, IphViewIsVisible) {
-  base::test::ScopedFeatureList scoped_feature_list(
-      assistant::features::kEnableAssistantLearnMore);
+  base::test::ScopedFeatureList feature_list(
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   ShowAssistantUi();
 
@@ -197,8 +198,8 @@
 }
 
 TEST_F(AssistantZeroStateViewUnittest, IphViewIsNotVisibleAfterResponse) {
-  base::test::ScopedFeatureList scoped_feature_list(
-      assistant::features::kEnableAssistantLearnMore);
+  base::test::ScopedFeatureList feature_list(
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   ShowAssistantUi();
 
@@ -217,7 +218,7 @@
 TEST_F(AssistantZeroStateViewUnittest, IphViewIsNotVisible_TabletMode) {
   base::test::ScopedFeatureList feature_list_;
   feature_list_.InitAndDisableFeature(
-      assistant::features::kEnableAssistantLearnMore);
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   SetNumberOfSessionsWhereOnboardingShown(
       assistant::ui::kOnboardingMaxSessionsShown);
@@ -239,8 +240,8 @@
 }
 
 TEST_F(AssistantZeroStateViewUnittest, IphViewIsVisible_TabletMode) {
-  base::test::ScopedFeatureList scoped_feature_list(
-      assistant::features::kEnableAssistantLearnMore);
+  base::test::ScopedFeatureList feature_list(
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   SetNumberOfSessionsWhereOnboardingShown(
       assistant::ui::kOnboardingMaxSessionsShown);
@@ -263,8 +264,8 @@
 
 TEST_F(AssistantZeroStateViewUnittest,
        IphViewIsNotVisibleAfterResponse_TabletMode) {
-  base::test::ScopedFeatureList scoped_feature_list(
-      assistant::features::kEnableAssistantLearnMore);
+  base::test::ScopedFeatureList feature_list(
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   SetNumberOfSessionsWhereOnboardingShown(
       assistant::ui::kOnboardingMaxSessionsShown);
diff --git a/ash/assistant/ui/main_stage/launcher_search_iph_view_unittest.cc b/ash/assistant/ui/main_stage/launcher_search_iph_view_unittest.cc
index 9146a59..39d18e1d 100644
--- a/ash/assistant/ui/main_stage/launcher_search_iph_view_unittest.cc
+++ b/ash/assistant/ui/main_stage/launcher_search_iph_view_unittest.cc
@@ -9,7 +9,7 @@
 #include "ash/assistant/ui/main_stage/chip_view.h"
 #include "base/memory/raw_ptr.h"
 #include "base/test/scoped_feature_list.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
+#include "components/feature_engagement/public/feature_constants.h"
 
 namespace ash {
 
@@ -22,7 +22,7 @@
 TEST_F(LauncherSearchIphViewTest,
        ShouldShuffleQueriesWhenShowingAssistantPage) {
   base::test::ScopedFeatureList scoped_feature_list(
-      assistant::features::kEnableAssistantLearnMore);
+      feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
   ShowAssistantUi();
   LauncherSearchIphView* iph_view = static_cast<LauncherSearchIphView*>(
diff --git a/ash/assistant/ui/main_stage/suggestion_container_view.cc b/ash/assistant/ui/main_stage/suggestion_container_view.cc
index 2f8ae18..09f3c92 100644
--- a/ash/assistant/ui/main_stage/suggestion_container_view.cc
+++ b/ash/assistant/ui/main_stage/suggestion_container_view.cc
@@ -18,14 +18,14 @@
 #include "ash/assistant/ui/main_stage/element_animator.h"
 #include "ash/assistant/util/animation_util.h"
 #include "ash/assistant/util/assistant_util.h"
-#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/assistant/assistant_state.h"
 #include "ash/public/cpp/assistant/controller/assistant_suggestions_controller.h"
 #include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/histogram_functions.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
+#include "components/feature_engagement/public/feature_constants.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/compositor/callback_layer_animation_observer.h"
 #include "ui/compositor/layer.h"
@@ -178,7 +178,8 @@
     return;
   }
 
-  if (assistant::features::IsAssistantLearnMoreEnabled()) {
+  if (base::FeatureList::IsEnabled(
+          feature_engagement::kIPHLauncherSearchHelpUiFeature)) {
     return;
   }
 
diff --git a/ash/capture_mode/gif_recording_unittests.cc b/ash/capture_mode/gif_recording_unittests.cc
index e0c45489..36306e78 100644
--- a/ash/capture_mode/gif_recording_unittests.cc
+++ b/ash/capture_mode/gif_recording_unittests.cc
@@ -17,6 +17,7 @@
 #include "ash/style/icon_button.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_util.h"
+#include "base/test/gtest_tags.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "ui/events/keycodes/keyboard_codes_posix.h"
@@ -261,6 +262,9 @@
 }
 
 TEST_F(GifRecordingTest, TabNavigation) {
+  base::AddFeatureIdTagToTestResult(
+      "screenplay-759f3130-3839-408a-8342-a373654e8927");
+
   auto* controller = StartRegionVideoCapture();
 
   // Tab 15 times until we reach the capture button.
diff --git a/ash/components/arc/arc_util.cc b/ash/components/arc/arc_util.cc
index 1c81702..3ce8cdf 100644
--- a/ash/components/arc/arc_util.cc
+++ b/ash/components/arc/arc_util.cc
@@ -55,7 +55,7 @@
 
 constexpr const char kCrosSystemPath[] = "/usr/bin/crossystem";
 
-// ArcVmUreadaheadMode param value strings.
+// ArcUreadaheadMode param value strings.
 constexpr char kReadahead[] = "readahead";
 constexpr char kGenerate[] = "generate";
 constexpr char kDisabled[] = "disabled";
@@ -218,11 +218,13 @@
       ash::switches::kIgnoreArcVmDevConf);
 }
 
+// TODO(b/315507371): Remove after deprecated switches are not in use
 bool IsUreadaheadDisabled() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
       ash::switches::kArcDisableUreadahead);
 }
 
+// TODO(b/315507371): Remove after deprecated switches are not in use
 bool IsHostUreadaheadGeneration() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
       ash::switches::kArcHostUreadaheadGeneration);
@@ -233,25 +235,25 @@
       ash::switches::kArcUseDevCaches);
 }
 
-ArcVmUreadaheadMode GetArcVmUreadaheadMode() {
-  ArcVmUreadaheadMode mode = IsUreadaheadDisabled()
-                                 ? ArcVmUreadaheadMode::DISABLED
-                                 : ArcVmUreadaheadMode::READAHEAD;
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          ash::switches::kArcVmUreadaheadMode)) {
-    const std::string value =
-        base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-            ash::switches::kArcVmUreadaheadMode);
-    if (value == kReadahead) {
-      mode = ArcVmUreadaheadMode::READAHEAD;
-    } else if (value == kGenerate) {
-      mode = ArcVmUreadaheadMode::GENERATE;
-    } else if (value == kDisabled) {
-      mode = ArcVmUreadaheadMode::DISABLED;
-    } else {
-      LOG(ERROR) << "Invalid parameter " << value << " for "
-                 << ash::switches::kArcVmUreadaheadMode;
-    }
+ArcUreadaheadMode GetArcUreadaheadMode(
+    std::string_view ureadahead_mode_switch) {
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          ureadahead_mode_switch)) {
+    return ArcUreadaheadMode::READAHEAD;
+  }
+  ArcUreadaheadMode mode = ArcUreadaheadMode::READAHEAD;
+  const std::string value =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          ureadahead_mode_switch);
+  if (value == kReadahead) {
+    mode = ArcUreadaheadMode::READAHEAD;
+  } else if (value == kGenerate) {
+    mode = ArcUreadaheadMode::GENERATE;
+  } else if (value == kDisabled) {
+    mode = ArcUreadaheadMode::DISABLED;
+  } else {
+    LOG(FATAL) << "Invalid parameter " << value << " for "
+               << ureadahead_mode_switch;
   }
   return mode;
 }
diff --git a/ash/components/arc/arc_util.h b/ash/components/arc/arc_util.h
index a5646ee..2efbfe34 100644
--- a/ash/components/arc/arc_util.h
+++ b/ash/components/arc/arc_util.h
@@ -50,8 +50,8 @@
   JOB_STOP_AND_START,
 };
 
-// Enum for configuring ureadahead mode of operation during ARCVM boot process.
-enum class ArcVmUreadaheadMode {
+// Enum for configuring ureadahead mode of operation during ARC boot process.
+enum class ArcUreadaheadMode {
   // ARCVM ureadahead is in readahead mode for normal user boot flow.
   READAHEAD = 0,
   // ARCVM ureadahead is turned on for generate mode in data collector flow.
@@ -161,9 +161,9 @@
 // Returns true if ARC is using dev caches for arccachesetup service.
 bool IsArcUseDevCaches();
 
-// Returns mode of operation for ureadahead during the ARCVM boot flow.
+// Returns mode of operation for ureadahead during the ARC boot flow.
 // Valid modes are readahead, generate, or disabled.
-ArcVmUreadaheadMode GetArcVmUreadaheadMode();
+ArcUreadaheadMode GetArcUreadaheadMode(std::string_view ureadahead_mode_switch);
 
 // Returns true if ARC should always start within the primary user session
 // (opted in user or not), and other supported mode such as guest and Kiosk
diff --git a/ash/components/arc/arc_util_unittest.cc b/ash/components/arc/arc_util_unittest.cc
index 030a71a..17269f70 100644
--- a/ash/components/arc/arc_util_unittest.cc
+++ b/ash/components/arc/arc_util_unittest.cc
@@ -14,6 +14,7 @@
 #include "ash/components/arc/session/arc_vm_data_migration_status.h"
 #include "ash/components/arc/test/arc_util_test_support.h"
 #include "ash/constants/app_types.h"
+#include "ash/constants/ash_switches.h"
 #include "ash/test/ash_test_base.h"
 #include "base/base_switches.h"
 #include "base/command_line.h"
@@ -294,27 +295,38 @@
   EXPECT_TRUE(IsArcVmDevConfIgnored());
 }
 
-TEST_F(ArcUtilTest, GetArcVmUreadaheadMode) {
+TEST_F(ArcUtilTest, GetArcUreadaheadModeVmSwitch) {
   auto* command_line = base::CommandLine::ForCurrentProcess();
+  const char* mode = ash::switches::kArcVmUreadaheadMode;
 
   command_line->InitFromArgv({""});
-  EXPECT_EQ(ArcVmUreadaheadMode::READAHEAD, GetArcVmUreadaheadMode());
-
-  command_line->InitFromArgv({"", "--arc-disable-ureadahead"});
-  EXPECT_EQ(ArcVmUreadaheadMode::DISABLED, GetArcVmUreadaheadMode());
-
-  command_line->InitFromArgv(
-      {"", "--arc-disable-ureadahead", "--arcvm-ureadahead-mode=readahead"});
-  EXPECT_EQ(ArcVmUreadaheadMode::READAHEAD, GetArcVmUreadaheadMode());
+  EXPECT_EQ(ArcUreadaheadMode::READAHEAD, GetArcUreadaheadMode(mode));
 
   command_line->InitFromArgv({"", "--arcvm-ureadahead-mode=readahead"});
-  EXPECT_EQ(ArcVmUreadaheadMode::READAHEAD, GetArcVmUreadaheadMode());
+  EXPECT_EQ(ArcUreadaheadMode::READAHEAD, GetArcUreadaheadMode(mode));
 
   command_line->InitFromArgv({"", "--arcvm-ureadahead-mode=generate"});
-  EXPECT_EQ(ArcVmUreadaheadMode::GENERATE, GetArcVmUreadaheadMode());
+  EXPECT_EQ(ArcUreadaheadMode::GENERATE, GetArcUreadaheadMode(mode));
 
   command_line->InitFromArgv({"", "--arcvm-ureadahead-mode=disabled"});
-  EXPECT_EQ(ArcVmUreadaheadMode::DISABLED, GetArcVmUreadaheadMode());
+  EXPECT_EQ(ArcUreadaheadMode::DISABLED, GetArcUreadaheadMode(mode));
+}
+
+TEST_F(ArcUtilTest, GetArcUreadaheadModeContainerSwitch) {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  const char* mode = ash::switches::kArcHostUreadaheadMode;
+
+  command_line->InitFromArgv({""});
+  EXPECT_EQ(ArcUreadaheadMode::READAHEAD, GetArcUreadaheadMode(mode));
+
+  command_line->InitFromArgv({"", "--arc-host-ureadahead-mode=readahead"});
+  EXPECT_EQ(ArcUreadaheadMode::READAHEAD, GetArcUreadaheadMode(mode));
+
+  command_line->InitFromArgv({"", "--arc-host-ureadahead-mode=generate"});
+  EXPECT_EQ(ArcUreadaheadMode::GENERATE, GetArcUreadaheadMode(mode));
+
+  command_line->InitFromArgv({"", "--arc-host-ureadahead-mode=disabled"});
+  EXPECT_EQ(ArcUreadaheadMode::DISABLED, GetArcUreadaheadMode(mode));
 }
 
 TEST_F(ArcUtilTest, UreadaheadDefault) {
diff --git a/ash/components/arc/session/arc_client_adapter.cc b/ash/components/arc/session/arc_client_adapter.cc
index 3d4eda71..0c4491d 100644
--- a/ash/components/arc/session/arc_client_adapter.cc
+++ b/ash/components/arc/session/arc_client_adapter.cc
@@ -44,6 +44,19 @@
   }
 }
 
+StartArcMiniInstanceRequest_HostUreadaheadMode
+ToArcMiniInstanceRequestHostUreadaheadMode(
+    StartParams::HostUreadaheadMode host_ureadahead_mode) {
+  switch (host_ureadahead_mode) {
+    case StartParams::HostUreadaheadMode::MODE_READAHEAD:
+      return StartArcMiniInstanceRequest_HostUreadaheadMode_MODE_DEFAULT;
+    case StartParams::HostUreadaheadMode::MODE_DISABLED:
+      return StartArcMiniInstanceRequest_HostUreadaheadMode_MODE_DISABLED;
+    case StartParams::HostUreadaheadMode::MODE_GENERATE:
+      return StartArcMiniInstanceRequest_HostUreadaheadMode_MODE_GENERATE;
+  }
+}
+
 }  // namespace
 
 ArcClientAdapter::ArcClientAdapter() = default;
@@ -81,6 +94,8 @@
   request.set_disable_download_provider(params.disable_download_provider);
   request.set_disable_ureadahead(params.disable_ureadahead);
   request.set_host_ureadahead_generation(params.host_ureadahead_generation);
+  request.set_host_ureadahead_mode(
+      ToArcMiniInstanceRequestHostUreadaheadMode(params.host_ureadahead_mode));
   request.set_use_dev_caches(params.use_dev_caches);
   request.set_arc_signed_in(params.arc_signed_in);
   request.set_arc_generate_pai(params.arc_generate_play_auto_install);
diff --git a/ash/components/arc/session/arc_container_client_adapter_unittest.cc b/ash/components/arc/session/arc_container_client_adapter_unittest.cc
index 6c38f04..ad0b9a9 100644
--- a/ash/components/arc/session/arc_container_client_adapter_unittest.cc
+++ b/ash/components/arc/session/arc_container_client_adapter_unittest.cc
@@ -319,6 +319,40 @@
   EXPECT_EQ(test_param.expectation, request.dalvik_memory_profile());
 }
 
+struct HostUreadaheadModeTestParam {
+  // Requested profile.
+  StartParams::HostUreadaheadMode mode;
+  // Expected value passed to DBus.
+  StartArcMiniInstanceRequest_HostUreadaheadMode expectation;
+};
+
+constexpr HostUreadaheadModeTestParam kHostUreadaheadModeTestCases[] = {
+    {StartParams::HostUreadaheadMode::MODE_READAHEAD,
+     StartArcMiniInstanceRequest_HostUreadaheadMode_MODE_DEFAULT},
+    {StartParams::HostUreadaheadMode::MODE_GENERATE,
+     StartArcMiniInstanceRequest_HostUreadaheadMode_MODE_GENERATE},
+    {StartParams::HostUreadaheadMode::MODE_DISABLED,
+     StartArcMiniInstanceRequest_HostUreadaheadMode_MODE_DISABLED}};
+
+class ArcContainerClientAdapterHostUreadaheadModeTest
+    : public ArcContainerClientAdapterTest,
+      public testing::WithParamInterface<HostUreadaheadModeTestParam> {};
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         ArcContainerClientAdapterHostUreadaheadModeTest,
+                         ::testing::ValuesIn(kHostUreadaheadModeTestCases));
+
+TEST_P(ArcContainerClientAdapterHostUreadaheadModeTest, Mode) {
+  const auto& test_param = GetParam();
+  StartParams start_params;
+  start_params.host_ureadahead_mode = test_param.mode;
+  client_adapter()->StartMiniArc(std::move(start_params),
+                                 base::BindOnce(&OnMiniInstanceStarted));
+  const auto& request = ash::FakeSessionManagerClient::Get()
+                            ->last_start_arc_mini_container_request();
+  EXPECT_TRUE(request.has_host_ureadahead_mode());
+  EXPECT_EQ(test_param.expectation, request.host_ureadahead_mode());
+}
 }  // namespace
 
 }  // namespace arc
diff --git a/ash/components/arc/session/arc_session_impl.cc b/ash/components/arc/session/arc_session_impl.cc
index 137dac84..61ec55c 100644
--- a/ash/components/arc/session/arc_session_impl.cc
+++ b/ash/components/arc/session/arc_session_impl.cc
@@ -124,6 +124,32 @@
           << (mem_info.total / 1024) << "Mb device.";
 }
 
+void ApplyHostUreadaheadMode(StartParams* params) {
+  // Check if deprecated flags are in use, override later if necessary
+  const arc::ArcUreadaheadMode mode =
+      arc::GetArcUreadaheadMode(ash::switches::kArcHostUreadaheadMode);
+  switch (mode) {
+    case arc::ArcUreadaheadMode::READAHEAD: {
+      params->host_ureadahead_mode =
+          StartParams::HostUreadaheadMode::MODE_READAHEAD;
+      break;
+    }
+    case arc::ArcUreadaheadMode::GENERATE: {
+      params->host_ureadahead_mode =
+          StartParams::HostUreadaheadMode::MODE_GENERATE;
+      break;
+    }
+    case arc::ArcUreadaheadMode::DISABLED: {
+      params->host_ureadahead_mode =
+          StartParams::HostUreadaheadMode::MODE_DISABLED;
+      break;
+    }
+    default: {
+      NOTREACHED_NORETURN();
+    }
+  }
+}
+
 void ApplyDisableDownloadProvider(StartParams* params) {
   params->disable_download_provider =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -492,6 +518,7 @@
   ApplyDisableUreadahed(&params);
   ApplyHostUreadahedGeneration(&params);
   ApplyUseDevCaches(&params);
+  ApplyHostUreadaheadMode(&params);
 
   client_->StartMiniArc(std::move(params),
                         base::BindOnce(&ArcSessionImpl::OnMiniInstanceStarted,
diff --git a/ash/components/arc/session/arc_session_impl_unittest.cc b/ash/components/arc/session/arc_session_impl_unittest.cc
index bc04f73..1d873b9 100644
--- a/ash/components/arc/session/arc_session_impl_unittest.cc
+++ b/ash/components/arc/session/arc_session_impl_unittest.cc
@@ -1024,5 +1024,41 @@
                          ArcSessionImplDalvikMemoryProfileTest,
                          ::testing::ValuesIn(kDalvikMemoryProfileVariant));
 
+struct HostUreadaheadModeState {
+  const char* mode_switch;
+  const StartParams::HostUreadaheadMode expected_mode;
+};
+
+constexpr HostUreadaheadModeState kHostUreadaheadMode[] = {
+    {"readahead", StartParams::HostUreadaheadMode::MODE_READAHEAD},
+    {"generate", StartParams::HostUreadaheadMode::MODE_GENERATE},
+    {"disabled", StartParams::HostUreadaheadMode::MODE_DISABLED},
+};
+
+class ArcSessionImplHostUreadaheadModeTest
+    : public ArcSessionImplTest,
+      public ::testing::WithParamInterface<HostUreadaheadModeState> {};
+
+TEST_P(ArcSessionImplHostUreadaheadModeTest, HostUreadaheadModes) {
+  auto arc_session = CreateArcSession();
+  const HostUreadaheadModeState state = GetParam();
+
+  if (state.mode_switch) {
+    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+    command_line->AppendSwitchASCII(ash::switches::kArcHostUreadaheadMode,
+                                    state.mode_switch);
+  }
+
+  arc_session->StartMiniInstance();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(
+      state.expected_mode,
+      GetClient(arc_session.get())->last_start_params().host_ureadahead_mode);
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         ArcSessionImplHostUreadaheadModeTest,
+                         ::testing::ValuesIn(kHostUreadaheadMode));
+
 }  // namespace
 }  // namespace arc
diff --git a/ash/components/arc/session/arc_start_params.h b/ash/components/arc/session/arc_start_params.h
index 36bf266b..20e420e 100644
--- a/ash/components/arc/session/arc_start_params.h
+++ b/ash/components/arc/session/arc_start_params.h
@@ -31,6 +31,15 @@
     M16G,
   };
 
+  enum HostUreadaheadMode {
+    // By default, ureadahead is in readahead mode.
+    MODE_READAHEAD = 0,
+    // Ureadahead is in generate mode.
+    MODE_GENERATE = 1,
+    // Ureadahead is in disabled mode.
+    MODE_DISABLED = 2,
+  };
+
   StartParams();
 
   StartParams(const StartParams&) = delete;
@@ -53,6 +62,8 @@
 
   DalvikMemoryProfile dalvik_memory_profile = DalvikMemoryProfile::DEFAULT;
 
+  HostUreadaheadMode host_ureadahead_mode = HostUreadaheadMode::MODE_READAHEAD;
+
   // Experiment flag for ARC Custom Tabs.
   bool arc_custom_tabs_experiment = false;
 
diff --git a/ash/components/arc/session/arc_vm_client_adapter.cc b/ash/components/arc/session/arc_vm_client_adapter.cc
index 4b528b1..c27b536 100644
--- a/ash/components/arc/session/arc_vm_client_adapter.cc
+++ b/ash/components/arc/session/arc_vm_client_adapter.cc
@@ -490,20 +490,22 @@
       break;
   }
 
-  const ArcVmUreadaheadMode mode = GetArcVmUreadaheadMode();
+  const ArcUreadaheadMode mode =
+      GetArcUreadaheadMode(ash::switches::kArcVmUreadaheadMode);
   switch (mode) {
     using StartArcVmRequest = vm_tools::concierge::StartArcVmRequest;
-    case ArcVmUreadaheadMode::READAHEAD:
+    case ArcUreadaheadMode::READAHEAD:
       request.set_ureadahead_mode(StartArcVmRequest::UREADAHEAD_MODE_READAHEAD);
       break;
-    case ArcVmUreadaheadMode::GENERATE:
+    case ArcUreadaheadMode::GENERATE:
       request.set_ureadahead_mode(StartArcVmRequest::UREADAHEAD_MODE_GENERATE);
       break;
-    case ArcVmUreadaheadMode::DISABLED:
+    case ArcUreadaheadMode::DISABLED:
       request.set_ureadahead_mode(StartArcVmRequest::UREADAHEAD_MODE_DISABLED);
       break;
+    default:
+      NOTREACHED_NORETURN();
   }
-
   return request;
 }
 
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index fdf12b0..400f894 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -1425,6 +1425,11 @@
              "HoldingSpaceCameraAppIntegration",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Enables Photoshop Web integration with holding space.
+BASE_FEATURE(kHoldingSpacePhotoshopWebIntegration,
+             "HoldingSpacePhotoshopWeb",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Enables holding space icon to be permanently displayed with extended file
 // expiration to increase predictability of the feature.
 BASE_FEATURE(kHoldingSpacePredictability,
@@ -3517,6 +3522,10 @@
   return base::FeatureList::IsEnabled(kHoldingSpaceCameraAppIntegration);
 }
 
+bool IsHoldingSpacePhotoshopWebIntegrationEnabled() {
+  return base::FeatureList::IsEnabled(kHoldingSpacePhotoshopWebIntegration);
+}
+
 bool IsHoldingSpacePredictabilityEnabled() {
   return base::FeatureList::IsEnabled(kHoldingSpacePredictability);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index b3ddbaf..de7c105 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -463,6 +463,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kHoldingSpaceCameraAppIntegration);
 COMPONENT_EXPORT(ASH_CONSTANTS)
+BASE_DECLARE_FEATURE(kHoldingSpacePhotoshopWebIntegration);
+COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kHoldingSpacePredictability);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kHoldingSpaceRefresh);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kHoldingSpaceSuggestions);
@@ -1037,6 +1039,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHideShelfControlsInTabletModeEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsHoldingSpaceCameraAppIntegrationEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS)
+bool IsHoldingSpacePhotoshopWebIntegrationEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHoldingSpacePredictabilityEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHoldingSpaceRefreshEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHoldingSpaceSuggestionsEnabled();
diff --git a/ash/constants/ash_switches.cc b/ash/constants/ash_switches.cc
index 97026bb..b05972b 100644
--- a/ash/constants/ash_switches.cc
+++ b/ash/constants/ash_switches.cc
@@ -95,10 +95,10 @@
 // Used in autotest to disable GMS-core caches which is on by default.
 const char kArcDisableGmsCoreCache[] = "arc-disable-gms-core-cache";
 
-// Flag that disables ARC locale sync with Android container. Used in autotest
+// Flag that disables ARC locale sync with Android Container. Used in autotest
 // to prevent conditions when certain apps, including Play Store may get
 // restarted. Restarting Play Store may cause random test failures. Enabling
-// this flag would also forces ARC container to use 'en-US' as a locale and
+// this flag would also forces ARC Container to use 'en-US' as a locale and
 // 'en-US,en' as preferred languages.
 const char kArcDisableLocaleSync[] = "arc-disable-locale-sync";
 
@@ -185,13 +185,22 @@
 // readahead (default) - used during production and is equivalent to no switch
 //                       being set. This is used in tast test to explicitly turn
 //                       on guest ureadahead (see |kArcDisableUreadahead|).
-// generate - used during Android PFQ data collector to pre-generate pack file
+// generate - used during Android Uprev data collector to pre-generate pack file
 //            and upload to Google Cloud as build artifact for CrOS build image.
 // disabled - used for test purpose to disable ureadahead during ARCVM boot.
 //            note, |kArcDisableUreadahead| also disables both, guest and host
 //            parts of ureadahead.
 const char kArcVmUreadaheadMode[] = "arcvm-ureadahead-mode";
 
+// Sets the mode of operation for ureadahead during ARC Container boot.
+// readahead (default) - used during production and is equivalent to no switch
+//                       being set.
+// generate - used during Android Uprev data collector to pre-generate pack file
+//            and upload to Google Cloud as build artifact for CrOS build image.
+// disabled - used for test purpose to disable ureadahead during ARC Container
+// boot.
+const char kArcHostUreadaheadMode[] = "arc-host-ureadahead-mode";
+
 // Madvises the kernel to use Huge Pages for guest memory.
 const char kArcVmUseHugePages[] = "arcvm-use-hugepages";
 
diff --git a/ash/constants/ash_switches.h b/ash/constants/ash_switches.h
index 7c006bd..2369f85a 100644
--- a/ash/constants/ash_switches.h
+++ b/ash/constants/ash_switches.h
@@ -62,6 +62,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kPrivacyPolicyHostForTests[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcVmMountDebugFs[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcVmUreadaheadMode[];
+COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcHostUreadaheadMode[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcVmUseHugePages[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kAshClearFastInkBuffer[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kAshBypassGlanceablesPref[];
diff --git a/ash/display/display_move_window_util_unittest.cc b/ash/display/display_move_window_util_unittest.cc
index ec3fb1c..0d346ad 100644
--- a/ash/display/display_move_window_util_unittest.cc
+++ b/ash/display/display_move_window_util_unittest.cc
@@ -271,7 +271,7 @@
   // Move window to display [p] and set that its bounds is changed by user.
   WindowState* window_state = WindowState::Get(window);
   PerformMoveWindowAccel();
-  window_state->set_bounds_changed_by_user(true);
+  window_state->SetBoundsChangedByUser(true);
   // Move window back to display [1], but its bounds has been changed by user.
   // Then window bounds should be kept the same as that in display [p].
   PerformMoveWindowAccel();
diff --git a/ash/events/peripheral_customization_event_rewriter.cc b/ash/events/peripheral_customization_event_rewriter.cc
index abc0a70..20474a3 100644
--- a/ash/events/peripheral_customization_event_rewriter.cc
+++ b/ash/events/peripheral_customization_event_rewriter.cc
@@ -388,6 +388,16 @@
   return left.button->is_vkey();
 }
 
+// Verify if the keyboard code is an alphabet letter.
+bool IsAlphaKeyboardCode(ui::KeyboardCode key_code) {
+  return key_code >= ui::VKEY_A && key_code <= ui::VKEY_Z;
+}
+
+// Verify if the keyboard code is a number.
+bool IsNumber(ui::KeyboardCode key_code) {
+  return key_code >= ui::VKEY_0 && key_code <= ui::VKEY_9;
+}
+
 PeripheralCustomizationEventRewriter::DeviceIdButton::DeviceIdButton(
     int device_id,
     mojom::ButtonPtr button)
@@ -498,17 +508,41 @@
   return true;
 }
 
+bool PeripheralCustomizationEventRewriter::IsButtonCustomizable(
+    const ui::KeyEvent& key_event) {
+  const auto iter = mice_to_observe_.find(key_event.source_device_id());
+  if (iter == mice_to_observe().end()) {
+    return false;
+  }
+  const auto customization_restriction = iter->second;
+  // There are several cases for the customization restriction:
+  // 1. If restriction is kAllowCustomizations, mice are allowed to observe
+  // key events.
+  // 2. If restriction is kAllowAlphabetKeyEventRewrites, mice are allowed to
+  // observe only alphabet letters key event.
+  // 3. If restriction is kAllowAlphabetOrNumberKeyEventRewrites, mice are
+  // allowed to observe alphabet letters or number key event.
+  // 4. Mice are not allowed to observe key event in other cases.
+  switch (customization_restriction) {
+    case mojom::CustomizationRestriction::kAllowCustomizations:
+      return true;
+    case mojom::CustomizationRestriction::kAllowAlphabetKeyEventRewrites:
+      return IsAlphaKeyboardCode(key_event.key_code());
+    case mojom::CustomizationRestriction::
+        kAllowAlphabetOrNumberKeyEventRewrites:
+      return IsAlphaKeyboardCode(key_event.key_code()) ||
+             IsNumber(key_event.key_code());
+    case mojom::CustomizationRestriction::kDisallowCustomizations:
+    case mojom::CustomizationRestriction::kDisableKeyEventRewrites:
+      return false;
+  }
+}
+
 bool PeripheralCustomizationEventRewriter::NotifyKeyEventObserving(
     const ui::KeyEvent& key_event,
     DeviceType device_type) {
-  // Only mice that have kAllowCustomizations restriction should be allowed
-  // to observe key events.
-  if (device_type == DeviceType::kMouse) {
-    const auto iter = mice_to_observe_.find(key_event.source_device_id());
-    if (iter == mice_to_observe().end() ||
-        iter->second != mojom::CustomizationRestriction::kAllowCustomizations) {
-      return false;
-    }
+  if (device_type == DeviceType::kMouse && !IsButtonCustomizable(key_event)) {
+    return false;
   }
 
   // Observers should only be notified on key presses.
diff --git a/ash/events/peripheral_customization_event_rewriter.h b/ash/events/peripheral_customization_event_rewriter.h
index 92b4c8b3..814528b 100644
--- a/ash/events/peripheral_customization_event_rewriter.h
+++ b/ash/events/peripheral_customization_event_rewriter.h
@@ -110,6 +110,9 @@
   bool NotifyKeyEventObserving(const ui::KeyEvent& key_event,
                                DeviceType device_type);
 
+  // Returns if the button is customizable.
+  bool IsButtonCustomizable(const ui::KeyEvent& key_event);
+
   // Rewrites the given event that came from `button` within the
   // `rewritten_event` param. Returns true if the original event should be
   // discarded.
diff --git a/ash/events/peripheral_customization_event_rewriter_unittest.cc b/ash/events/peripheral_customization_event_rewriter_unittest.cc
index a23e6fc5..42c22965 100644
--- a/ash/events/peripheral_customization_event_rewriter_unittest.cc
+++ b/ash/events/peripheral_customization_event_rewriter_unittest.cc
@@ -833,6 +833,76 @@
       /*expected_count=*/1u);
 }
 
+TEST_F(MouseButtonObserverTest, RewriteAlphabetKeyEvent) {
+  TestEventRewriterContinuation continuation;
+
+  rewriter_->StartObservingMouse(
+      kMouseDeviceId,
+      /*customization_restriction=*/mojom::CustomizationRestriction::
+          kAllowAlphabetKeyEventRewrites);
+
+  ui::KeyEvent key_event = CreateKeyButtonEvent(
+      ui::ET_KEY_PRESSED, ui::VKEY_LEFT, ui::EF_COMMAND_DOWN);
+  rewriter_->RewriteEvent(key_event,
+                          continuation.weak_ptr_factory_.GetWeakPtr());
+  // Key event shouldn't be discarded if the key
+  // code is not alphabet letter.
+  ASSERT_TRUE(continuation.passthrough_event);
+  ASSERT_TRUE(continuation.passthrough_event->IsKeyEvent());
+  EXPECT_EQ(ConvertToString(key_event),
+            ConvertToString(*continuation.passthrough_event));
+
+  ui::KeyEvent new_key_event =
+      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_COMMAND_DOWN);
+  continuation.reset();
+  rewriter_->RewriteEvent(new_key_event,
+                          continuation.weak_ptr_factory_.GetWeakPtr());
+  // New key event should be discarded if the key
+  // code is alphabet letter.
+  ASSERT_TRUE(continuation.discarded());
+  EXPECT_EQ(nullptr, continuation.passthrough_event);
+}
+
+TEST_F(MouseButtonObserverTest, RewriteAlphabetOrNumberKeyEvent) {
+  TestEventRewriterContinuation continuation;
+
+  rewriter_->StartObservingMouse(
+      kMouseDeviceId,
+      /*customization_restriction=*/mojom::CustomizationRestriction::
+          kAllowAlphabetOrNumberKeyEventRewrites);
+
+  ui::KeyEvent key_event = CreateKeyButtonEvent(
+      ui::ET_KEY_PRESSED, ui::VKEY_LEFT, ui::EF_COMMAND_DOWN);
+  rewriter_->RewriteEvent(key_event,
+                          continuation.weak_ptr_factory_.GetWeakPtr());
+  // Key event shouldn't be discarded if the key
+  // code is not alphabet letter or number.
+  ASSERT_TRUE(continuation.passthrough_event);
+  ASSERT_TRUE(continuation.passthrough_event->IsKeyEvent());
+  EXPECT_EQ(ConvertToString(key_event),
+            ConvertToString(*continuation.passthrough_event));
+
+  ui::KeyEvent new_alphabet_key_event =
+      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_COMMAND_DOWN);
+  continuation.reset();
+  rewriter_->RewriteEvent(new_alphabet_key_event,
+                          continuation.weak_ptr_factory_.GetWeakPtr());
+  // New key event should be discarded if the key
+  // code is alphabet letter.
+  ASSERT_TRUE(continuation.discarded());
+  EXPECT_EQ(nullptr, continuation.passthrough_event);
+
+  ui::KeyEvent new_number_key_event =
+      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_0, ui::EF_COMMAND_DOWN);
+  continuation.reset();
+  rewriter_->RewriteEvent(new_number_key_event,
+                          continuation.weak_ptr_factory_.GetWeakPtr());
+  // New key event should be discarded if the key
+  // code is number.
+  ASSERT_TRUE(continuation.discarded());
+  EXPECT_EQ(nullptr, continuation.passthrough_event);
+}
+
 class GraphicsTabletButtonObserverTest
     : public PeripheralCustomizationEventRewriterTest,
       public testing::WithParamInterface<EventRewriterTestData> {};
diff --git a/ash/public/cpp/holding_space/holding_space_item.cc b/ash/public/cpp/holding_space/holding_space_item.cc
index 6b26491..e7936d0 100644
--- a/ash/public/cpp/holding_space/holding_space_item.cc
+++ b/ash/public/cpp/holding_space/holding_space_item.cc
@@ -120,6 +120,7 @@
     case Type::kLocalSuggestion:
     case Type::kNearbyShare:
     case Type::kPhoneHubCameraRoll:
+    case Type::kPhotoshopWeb:
     case Type::kPinnedFile:
     case Type::kPrintedPdf:
     case Type::kScan:
@@ -147,6 +148,7 @@
     case Type::kLocalSuggestion:
     case Type::kNearbyShare:
     case Type::kPhoneHubCameraRoll:
+    case Type::kPhotoshopWeb:
     case Type::kPinnedFile:
     case Type::kPrintedPdf:
     case Type::kScan:
@@ -177,6 +179,7 @@
     case Type::kLocalSuggestion:
     case Type::kNearbyShare:
     case Type::kPhoneHubCameraRoll:
+    case Type::kPhotoshopWeb:
     case Type::kPinnedFile:
     case Type::kPrintedPdf:
     case Type::kScan:
@@ -201,6 +204,7 @@
     case Type::kLacrosDownload:
     case Type::kNearbyShare:
     case Type::kPhoneHubCameraRoll:
+    case Type::kPhotoshopWeb:
     case Type::kPinnedFile:
     case Type::kPrintedPdf:
     case Type::kScan:
diff --git a/ash/public/cpp/holding_space/holding_space_item.h b/ash/public/cpp/holding_space/holding_space_item.h
index 4afd1a4..97ee9533 100644
--- a/ash/public/cpp/holding_space/holding_space_item.h
+++ b/ash/public/cpp/holding_space/holding_space_item.h
@@ -92,7 +92,8 @@
     kCameraAppScanPdf = 16,
     kCameraAppVideoGif = 17,
     kCameraAppVideoMp4 = 18,
-    kMaxValue = kCameraAppVideoMp4,
+    kPhotoshopWeb = 19,
+    kMaxValue = kPhotoshopWeb,
   };
 
   HoldingSpaceItem(const HoldingSpaceItem&) = delete;
diff --git a/ash/public/cpp/holding_space/holding_space_item_unittest.cc b/ash/public/cpp/holding_space/holding_space_item_unittest.cc
index 012be9b..78888c1 100644
--- a/ash/public/cpp/holding_space/holding_space_item_unittest.cc
+++ b/ash/public/cpp/holding_space/holding_space_item_unittest.cc
@@ -185,6 +185,7 @@
     case HoldingSpaceItem::Type::kLocalSuggestion:
     case HoldingSpaceItem::Type::kNearbyShare:
     case HoldingSpaceItem::Type::kPhoneHubCameraRoll:
+    case HoldingSpaceItem::Type::kPhotoshopWeb:
     case HoldingSpaceItem::Type::kPinnedFile:
     case HoldingSpaceItem::Type::kPrintedPdf:
     case HoldingSpaceItem::Type::kScan:
@@ -218,6 +219,7 @@
     case HoldingSpaceItem::Type::kLocalSuggestion:
     case HoldingSpaceItem::Type::kNearbyShare:
     case HoldingSpaceItem::Type::kPhoneHubCameraRoll:
+    case HoldingSpaceItem::Type::kPhotoshopWeb:
     case HoldingSpaceItem::Type::kPinnedFile:
     case HoldingSpaceItem::Type::kPrintedPdf:
     case HoldingSpaceItem::Type::kScan:
diff --git a/ash/public/cpp/holding_space/holding_space_section.cc b/ash/public/cpp/holding_space/holding_space_section.cc
index 47e7fdf6..4dd7608 100644
--- a/ash/public/cpp/holding_space/holding_space_section.cc
+++ b/ash/public/cpp/holding_space/holding_space_section.cc
@@ -50,6 +50,7 @@
               HoldingSpaceItem::Type::kDownload,
               HoldingSpaceItem::Type::kLacrosDownload,
               HoldingSpaceItem::Type::kNearbyShare,
+              HoldingSpaceItem::Type::kPhotoshopWeb,
               HoldingSpaceItem::Type::kPrintedPdf,
               HoldingSpaceItem::Type::kScan,
               HoldingSpaceItem::Type::kPhoneHubCameraRoll,
diff --git a/ash/public/cpp/holding_space/holding_space_section_unittest.cc b/ash/public/cpp/holding_space/holding_space_section_unittest.cc
index e996d3e7..b52bb82e 100644
--- a/ash/public/cpp/holding_space/holding_space_section_unittest.cc
+++ b/ash/public/cpp/holding_space/holding_space_section_unittest.cc
@@ -51,6 +51,7 @@
                       HoldingSpaceItem::Type::kLacrosDownload,
                       HoldingSpaceItem::Type::kNearbyShare,
                       HoldingSpaceItem::Type::kPhoneHubCameraRoll,
+                      HoldingSpaceItem::Type::kPhotoshopWeb,
                       HoldingSpaceItem::Type::kPrintedPdf,
                       HoldingSpaceItem::Type::kScan));
       EXPECT_EQ(section->max_item_count, 50u);
@@ -117,6 +118,7 @@
       case HoldingSpaceItem::Type::kLacrosDownload:
       case HoldingSpaceItem::Type::kNearbyShare:
       case HoldingSpaceItem::Type::kPhoneHubCameraRoll:
+      case HoldingSpaceItem::Type::kPhotoshopWeb:
       case HoldingSpaceItem::Type::kPrintedPdf:
       case HoldingSpaceItem::Type::kScan:
         id = HoldingSpaceSectionId::kDownloads;
diff --git a/ash/public/cpp/holding_space/holding_space_util.cc b/ash/public/cpp/holding_space/holding_space_util.cc
index ce0c5ef..c8e9bfdd 100644
--- a/ash/public/cpp/holding_space/holding_space_util.cc
+++ b/ash/public/cpp/holding_space/holding_space_util.cc
@@ -107,6 +107,7 @@
     case HoldingSpaceItem::Type::kLocalSuggestion:
     case HoldingSpaceItem::Type::kNearbyShare:
     case HoldingSpaceItem::Type::kPhoneHubCameraRoll:
+    case HoldingSpaceItem::Type::kPhotoshopWeb:
     case HoldingSpaceItem::Type::kPinnedFile:
     case HoldingSpaceItem::Type::kPrintedPdf:
     case HoldingSpaceItem::Type::kScan:
@@ -186,6 +187,8 @@
       return "NearbyShare";
     case HoldingSpaceItem::Type::kPhoneHubCameraRoll:
       return "PhoneHubCameraRoll";
+    case HoldingSpaceItem::Type::kPhotoshopWeb:
+      return "PhotoshopWeb";
     case HoldingSpaceItem::Type::kPinnedFile:
       return "PinnedFile";
     case HoldingSpaceItem::Type::kPrintedPdf:
diff --git a/ash/public/cpp/holding_space/holding_space_util_unittest.cc b/ash/public/cpp/holding_space/holding_space_util_unittest.cc
index 6c5b38e2..263f3f9 100644
--- a/ash/public/cpp/holding_space/holding_space_util_unittest.cc
+++ b/ash/public/cpp/holding_space/holding_space_util_unittest.cc
@@ -182,6 +182,9 @@
       case HoldingSpaceItem::Type::kPhoneHubCameraRoll:
         expected_string = "PhoneHubCameraRoll";
         break;
+      case HoldingSpaceItem::Type::kPhotoshopWeb:
+        expected_string = "PhotoshopWeb";
+        break;
       case HoldingSpaceItem::Type::kPinnedFile:
         expected_string = "PinnedFile";
         break;
diff --git a/ash/public/mojom/input_device_settings.mojom b/ash/public/mojom/input_device_settings.mojom
index 4f9f7c7..5d2a351 100644
--- a/ash/public/mojom/input_device_settings.mojom
+++ b/ash/public/mojom/input_device_settings.mojom
@@ -363,4 +363,8 @@
   kDisallowCustomizations = 1,
   // Disable key event rewrites.
   kDisableKeyEventRewrites = 2,
+  // Allow Alphabet key event rewrites.
+  kAllowAlphabetKeyEventRewrites = 3,
+  // Allow Alphabet or Number key event rewrites.
+  kAllowAlphabetOrNumberKeyEventRewrites = 4,
 };
diff --git a/ash/quick_pair/common/account_key_failure.h b/ash/quick_pair/common/account_key_failure.h
index d477470..23371b43 100644
--- a/ash/quick_pair/common/account_key_failure.h
+++ b/ash/quick_pair/common/account_key_failure.h
@@ -13,7 +13,8 @@
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused. This enum should be kept in sync with
-// the FastPairAccountKeyFailure enum in src/tools/metrics/histograms/enums.xml.
+// the FastPairAccountKeyFailure enum in
+// //tools/metrics/histograms/metadata/bluetooth/enums.xml.
 enum class AccountKeyFailure {
   // Failed to find the Account Key GATT characteristic.
   kAccountKeyCharacteristicDiscovery = 0,
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
index b75e586..97d961f 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
+++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
@@ -40,10 +40,11 @@
 
 // Top Popular peripherals and first party devices. These device
 // model names should be kept in sync with the FastPairTrackedModelID
-// enum in src/tools/metrics/histograms/enums.xml. Devices may have multiple
-// Model IDs associated with the same device (for example, each Pixel Bud Pros
-// have different Model IDs for each different color) so we append '_*' to the
-// naming for subsequent Model IDs after the first one.
+// token in //tools/metrics/histograms/metadata/bluetooth/histograms.xml.
+// Devices may have multiple Model IDs associated with the same device
+// (for example, each Pixel Bud Pros have different Model IDs for each different
+// color) so we append '_*' to the naming for subsequent Model IDs after the
+// first one.
 const char kPopularPeripheral_BoatRockerz255Pro_ModelId[] = "CFF121";
 const char kPopularPeripheral_BoatRockerz255Pro_Name[] = "BoatRockerz255Pro";
 
@@ -553,7 +554,7 @@
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused. This enum should be kept in sync
 // with the BluetoothConnectToServiceError enum in
-// src/tools/metrics/histograms/enums.xml.
+// //tools/metrics/histograms/metadata/bluetooth/enums.xml.
 enum class ConnectToServiceError {
   kUnknownError = 0,
   kAcceptFailed = 1,
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
index f564b9d..b8d06ad 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
+++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
@@ -23,7 +23,8 @@
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused. The numbers here correspond to the
 // ordering of the flow. This enum should be kept in sync with the
-// FastPairEngagementFlowEvent enum in src/tools/metrics/histograms/enums.xml.
+// FastPairEngagementFlowEvent enum in
+// //tools/metrics/histograms/metadata/bluetooth/enums.xml.
 enum COMPONENT_EXPORT(QUICK_PAIR_COMMON) FastPairEngagementFlowEvent {
   kDiscoveryUiShown = 1,
   kDiscoveryUiDismissed = 11,
@@ -47,7 +48,7 @@
 // numeric values should never be reused. The numbers here correspond to the
 // ordering of the flow. This enum should be kept in sync with the
 // FastPairRetroactiveEngagementFlowEvent enum in
-// src/tools/metrics/histograms/enums.xml.
+// //tools/metrics/histograms/metadata/bluetooth/enums.xml.
 enum COMPONENT_EXPORT(QUICK_PAIR_COMMON)
     FastPairRetroactiveEngagementFlowEvent {
       kAssociateAccountUiShown = 1,
@@ -66,7 +67,7 @@
 // numeric values should never be reused. The numbers here correspond to the
 // ordering of the flow. This enum should be kept in sync with the
 // FastPairInitialSuccessFunnelEvent enum in
-// src/tools/metrics/histograms/enums.xml.
+// //tools/metrics/histograms/metadata/fastpair/enums.xml.
 enum class COMPONENT_EXPORT(QUICK_PAIR_COMMON)
     FastPairInitialSuccessFunnelEvent {
       kNotificationsClicked = 0,
@@ -86,7 +87,7 @@
 // numeric values should never be reused. The numbers here correspond to the
 // ordering of the flow. This enum should be kept in sync with the
 // FastPairSubsequentSuccessFunnelEvent enum in
-// src/tools/metrics/histograms/enums.xml.
+// //tools/metrics/histograms/metadata/fastpair/enums.xml.
 enum class COMPONENT_EXPORT(QUICK_PAIR_COMMON)
     FastPairSubsequentSuccessFunnelEvent {
       kNotificationsClicked = 0,
@@ -100,7 +101,7 @@
 // numeric values should never be reused. The numbers here correspond to the
 // ordering of the flow. This enum should be kept in sync with the
 // FastPairRetroactiveSuccessFunnelEvent enum in
-// src/tools/metrics/histograms/enums.xml.
+// //tools/metrics/histograms/metadata/fastpair/enums.xml.
 enum class COMPONENT_EXPORT(QUICK_PAIR_COMMON)
     FastPairRetroactiveSuccessFunnelEvent {
       kDeviceDetected = 0,
@@ -118,7 +119,7 @@
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused. This enum should be kept in sync
 // with the FastPairInitializePairingProcessEvent enum in
-// src/tools/metrics/histograms/enums.xml.
+// //tools/metrics/histograms/metadata/fastpair/enums.xml.
 enum class COMPONENT_EXPORT(QUICK_PAIR_COMMON)
     FastPairInitializePairingProcessEvent {
       kInitializationStarted = 0,
@@ -133,7 +134,7 @@
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused. This enum should be kept in sync
 // with the FastPairPairingMethod enum in
-// src/tools/metrics/histograms/enums.xml.
+// //tools/metrics/histograms/metadata/bluetooth/enums.xml.
 enum class COMPONENT_EXPORT(QUICK_PAIR_COMMON) PairingMethod {
   kFastPair = 0,
   kSystemPairingUi = 1,
@@ -143,7 +144,7 @@
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused. This enum should be kept in sync
 // with the FastPairHandshakeFailureReason enum in
-// src/tools/metrics/histograms/enums.xml.
+// //tools/metrics/histograms/metadata/bluetooth/enums.xml.
 enum class COMPONENT_EXPORT(QUICK_PAIR_COMMON) HandshakeFailureReason {
   kFailedGattInit = 0,
   kFailedCreateEncryptor = 1,
@@ -155,7 +156,8 @@
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused. This enum should be kept in sync
-// with the FastPairVersion enum in src/tools/metrics/histograms/enums.xml.
+// with the FastPairVersion enum in
+// //tools/metrics/histograms/metadata/bluetooth/enums.xml.
 enum class COMPONENT_EXPORT(QUICK_PAIR_COMMON) FastPairVersion {
   kVersion1 = 0,
   kVersion2 = 1,
@@ -165,7 +167,7 @@
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused. This enum should be kept in sync
 // with the FastPairHandshakeSteps enum in
-// src/tools/metrics/histograms/enums.xml.
+// //tools/metrics/histograms/metadata/bluetooth/enums.xml.
 enum class COMPONENT_EXPORT(QUICK_PAIR_COMMON) FastPairHandshakeSteps {
   kHandshakeStarted = 0,
   kGattInitalized = 1,
@@ -177,7 +179,7 @@
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused. This enum should be kept in sync
 // with the FastPairGattConnectionSteps enum in
-// src/tools/metrics/histograms/enums.xml.
+// //tools/metrics/histograms/metadata/fastpair/enums.xml.
 enum class COMPONENT_EXPORT(QUICK_PAIR_COMMON) FastPairGattConnectionSteps {
   kConnectionStarted = 0,
   kConnectionReady = 1,
@@ -189,8 +191,8 @@
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused. This enum should be kept in sync
-// with the FastPairHandshakeSteps enum in
-// src/tools/metrics/histograms/enums.xml.
+// with the FastPairProtocolPairingSteps enum in
+// //tools/metrics/histograms/metadata/bluetooth/enums.xml.
 enum class COMPONENT_EXPORT(QUICK_PAIR_COMMON) FastPairProtocolPairingSteps {
   kPairingStarted = 0,
   kExhaustedRetries = 1,
diff --git a/ash/quick_pair/common/pair_failure.h b/ash/quick_pair/common/pair_failure.h
index e24daa93..a94780f 100644
--- a/ash/quick_pair/common/pair_failure.h
+++ b/ash/quick_pair/common/pair_failure.h
@@ -13,7 +13,7 @@
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused. This enum should be kept in sync with
-// the FastPairPairFailure enum in src/tools/metrics/histograms/enums.xml.
+// the FastPairPairFailure enum in //tools/metrics/histograms/enums.xml.
 enum class PairFailure {
   // Failed to create a GATT connection to the device.
   kCreateGattConnection = 0,
diff --git a/ash/system/holding_space/holding_space_item_screen_capture_view.cc b/ash/system/holding_space/holding_space_item_screen_capture_view.cc
index 3b2b1ebf..c4e3576c 100644
--- a/ash/system/holding_space/holding_space_item_screen_capture_view.cc
+++ b/ash/system/holding_space/holding_space_item_screen_capture_view.cc
@@ -59,6 +59,7 @@
     case HoldingSpaceItem::Type::kLocalSuggestion:
     case HoldingSpaceItem::Type::kNearbyShare:
     case HoldingSpaceItem::Type::kPhoneHubCameraRoll:
+    case HoldingSpaceItem::Type::kPhotoshopWeb:
     case HoldingSpaceItem::Type::kPinnedFile:
     case HoldingSpaceItem::Type::kPrintedPdf:
     case HoldingSpaceItem::Type::kScan:
diff --git a/ash/system/input_device_settings/input_device_settings_metadata.cc b/ash/system/input_device_settings/input_device_settings_metadata.cc
new file mode 100644
index 0000000..2d3f875
--- /dev/null
+++ b/ash/system/input_device_settings/input_device_settings_metadata.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 "ash/system/input_device_settings/input_device_settings_metadata.h"
+
+#include "ash/public/mojom/input_device_settings.mojom-shared.h"
+#include "ash/public/mojom/input_device_settings.mojom.h"
+#include "ash/system/input_device_settings/input_device_settings_utils.h"
+#include "base/containers/fixed_flat_map.h"
+
+namespace ash {
+
+namespace {
+static constexpr auto kMouseMetadata =
+    base::MakeFixedFlatMap<VendorProductId, MouseMetadata>({
+        {{0xffff, 0xffff},
+         {mojom::CustomizationRestriction::kAllowCustomizations}},  // Fake data
+                                                                    // for
+                                                                    // testing.
+    });
+}
+
+bool MouseMetadata::operator==(const MouseMetadata& other) const {
+  return customization_restriction == other.customization_restriction;
+}
+
+const MouseMetadata* GetMouseMetadata(const ui::InputDevice& device) {
+  const auto* iter = kMouseMetadata.find({device.vendor_id, device.product_id});
+  if (iter != kMouseMetadata.end()) {
+    return &(iter->second);
+  }
+  return nullptr;
+}
+
+}  // namespace ash
diff --git a/ash/system/input_device_settings/input_device_settings_metadata.h b/ash/system/input_device_settings/input_device_settings_metadata.h
new file mode 100644
index 0000000..8a39577
--- /dev/null
+++ b/ash/system/input_device_settings/input_device_settings_metadata.h
@@ -0,0 +1,25 @@
+// 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 ASH_SYSTEM_INPUT_DEVICE_SETTINGS_INPUT_DEVICE_SETTINGS_METADATA_H_
+#define ASH_SYSTEM_INPUT_DEVICE_SETTINGS_INPUT_DEVICE_SETTINGS_METADATA_H_
+
+#include "ash/ash_export.h"
+#include "ash/public/mojom/input_device_settings.mojom.h"
+#include "ui/events/devices/input_device.h"
+
+namespace ash {
+
+struct ASH_EXPORT MouseMetadata {
+  mojom::CustomizationRestriction customization_restriction;
+  bool operator==(const MouseMetadata& other) const;
+};
+
+// This function returns mouse metadata. Returns nullptr if there is no metadata
+// on the mouse.
+ASH_EXPORT const MouseMetadata* GetMouseMetadata(const ui::InputDevice& device);
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_INPUT_DEVICE_SETTINGS_INPUT_DEVICE_SETTINGS_METADATA_H_
diff --git a/ash/system/input_device_settings/input_device_settings_metadata_unittest.cc b/ash/system/input_device_settings/input_device_settings_metadata_unittest.cc
new file mode 100644
index 0000000..847af86
--- /dev/null
+++ b/ash/system/input_device_settings/input_device_settings_metadata_unittest.cc
@@ -0,0 +1,41 @@
+// 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 "ash/system/input_device_settings/input_device_settings_metadata.h"
+
+#include "ash/public/mojom/input_device_settings.mojom.h"
+#include "ash/test/ash_test_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/devices/input_device.h"
+
+namespace ash {
+
+class MetadataTest : public AshTestBase {};
+
+TEST_F(MetadataTest, MouseMetadata) {
+  const ui::InputDevice kSampleMouse1(0, ui::INPUT_DEVICE_USB, "kSampleMouse1",
+                                      /*phys=*/"",
+                                      /*sys_path=*/base::FilePath(),
+                                      /*vendor=*/0x0000,
+                                      /*product=*/0x0000,
+                                      /*version=*/0x0001);
+  const ui::InputDevice kSampleMouse2(1, ui::INPUT_DEVICE_USB, "kSampleMouse2",
+                                      /*phys=*/"",
+                                      /*sys_path=*/base::FilePath(),
+                                      /*vendor=*/0xffff,
+                                      /*product=*/0xffff,
+                                      /*version=*/0x0002);
+
+  const auto* metadata1 = GetMouseMetadata(kSampleMouse1);
+  EXPECT_EQ(metadata1, nullptr);
+
+  const auto* metadata2 = GetMouseMetadata(kSampleMouse2);
+  MouseMetadata expected_metadata;
+  expected_metadata.customization_restriction =
+      mojom::CustomizationRestriction::kAllowCustomizations;
+  ASSERT_TRUE(metadata2);
+  EXPECT_EQ(*metadata2, expected_metadata);
+}
+
+}  // namespace ash
diff --git a/ash/system/input_device_settings/input_device_settings_utils.cc b/ash/system/input_device_settings/input_device_settings_utils.cc
index 6288f00..4e3a6f0c 100644
--- a/ash/system/input_device_settings/input_device_settings_utils.cc
+++ b/ash/system/input_device_settings/input_device_settings_utils.cc
@@ -48,11 +48,6 @@
 
 }  // namespace
 
-constexpr bool VendorProductId::operator<(const VendorProductId& other) const {
-  return vendor_id == other.vendor_id ? product_id < other.product_id
-                                      : vendor_id < other.vendor_id;
-}
-
 bool VendorProductId::operator==(const VendorProductId& other) const {
   return vendor_id == other.vendor_id && product_id == other.product_id;
 }
diff --git a/ash/system/input_device_settings/input_device_settings_utils.h b/ash/system/input_device_settings/input_device_settings_utils.h
index fa9ea54c..493ed12 100644
--- a/ash/system/input_device_settings/input_device_settings_utils.h
+++ b/ash/system/input_device_settings/input_device_settings_utils.h
@@ -20,7 +20,10 @@
 struct VendorProductId {
   uint16_t vendor_id;
   uint16_t product_id;
-  constexpr bool operator<(const VendorProductId& other) const;
+  constexpr bool operator<(const VendorProductId& other) const {
+    return vendor_id == other.vendor_id ? product_id < other.product_id
+                                        : vendor_id < other.vendor_id;
+  }
   bool operator==(const VendorProductId& other) const;
 };
 
diff --git a/ash/system/time/calendar_event_list_item_view.cc b/ash/system/time/calendar_event_list_item_view.cc
index ca25e1f..3623bde 100644
--- a/ash/system/time/calendar_event_list_item_view.cc
+++ b/ash/system/time/calendar_event_list_item_view.cc
@@ -57,10 +57,16 @@
 // Default Calendar API color ID to use when no event color is specifified.
 constexpr char kDefaultColorId[] = "7";
 
-// Map of Calendar API color ids and their respective hex color code.
+// The color ID for past event items.
+constexpr char kPastEventsColorId[] = "0";
+
+// Map of Calendar API color ids and their respective hex color code. For color
+// id "0", it's not part of the Calendar API color, but it's used for past
+// events for a gray out effect.
 constexpr auto kEventHexColorCodes =
     base::MakeFixedFlatMap<base::StringPiece, base::StringPiece>(
-        {{"1", "6994FF"},
+        {{"0", "1B1B1F"},
+         {"1", "6994FF"},
          {"2", "3CBD8E"},
          {"3", "BB74F2"},
          {"4", "FA827A"},
@@ -72,12 +78,17 @@
          {"10", "5B9157"},
          {"11", "D45D5D"}});
 
+constexpr SkAlpha SK_Alpha38Opacity = 0x61;
+constexpr SkAlpha SK_Alpha50Opacity = 0x80;
+
 // Renders an Event color dot.
 class CalendarEventListItemDot : public views::View {
   METADATA_HEADER(CalendarEventListItemDot, views::View)
 
  public:
-  explicit CalendarEventListItemDot(std::string color_id) {
+  explicit CalendarEventListItemDot(std::string color_id)
+      : alpha_(color_id == kPastEventsColorId ? SK_Alpha38Opacity
+                                              : SK_AlphaOPAQUE) {
     DCHECK(color_id.empty() || kEventHexColorCodes.count(color_id));
 
     base::BasicStringPiece<char> hex_code = LookupColorId(color_id);
@@ -94,7 +105,7 @@
   // Draws the circle for the event color dot.
   void OnPaint(gfx::Canvas* canvas) override {
     cc::PaintFlags color_dot;
-    color_dot.setColor(SkColorSetA(color_, SK_AlphaOPAQUE));
+    color_dot.setColor(SkColorSetA(color_, alpha_));
     color_dot.setStyle(cc::PaintFlags::kFill_Style);
     color_dot.setAntiAlias(true);
     canvas->DrawCircle(GetContentsBounds().CenterPoint(), kColorDotRadius,
@@ -110,8 +121,9 @@
     return iter->second;
   }
 
-  // The color value of the dot.
+  // The color value and the opacity of the dot.
   int color_;
+  const SkAlpha alpha_;
 };
 
 BEGIN_METADATA(CalendarEventListItemDot)
@@ -121,14 +133,16 @@
 views::Builder<views::Label> CreateSummaryLabel(
     const std::string& event_summary,
     const std::u16string& tooltip_text,
-    const int& fixed_width) {
+    const int& fixed_width,
+    const bool is_past_event) {
   return views::Builder<views::Label>(
              bubble_utils::CreateLabel(
                  TypographyToken::kCrosButton2,
                  event_summary.empty()
                      ? l10n_util::GetStringUTF16(IDS_ASH_CALENDAR_NO_TITLE)
                      : base::UTF8ToUTF16(event_summary),
-                 cros_tokens::kCrosSysOnSurface))
+                 is_past_event ? cros_tokens::kCrosSysDisabled
+                               : cros_tokens::kCrosSysOnSurface))
       .SetID(kSummaryLabelID)
       .SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT)
       .SetAutoColorReadabilityEnabled(false)
@@ -141,12 +155,14 @@
 }
 
 // Creates and returns a label containing the event time.
-views::Builder<views::Label> CreateTimeLabel(
-    const std::u16string& title,
-    const std::u16string& tooltip_text) {
+views::Builder<views::Label> CreateTimeLabel(const std::u16string& title,
+                                             const std::u16string& tooltip_text,
+                                             const bool is_past_event) {
   return views::Builder<views::Label>(
-             bubble_utils::CreateLabel(TypographyToken::kCrosAnnotation1, title,
-                                       cros_tokens::kCrosSysOnSurfaceVariant))
+             bubble_utils::CreateLabel(
+                 TypographyToken::kCrosAnnotation1, title,
+                 is_past_event ? cros_tokens::kCrosSysDisabled
+                               : cros_tokens::kCrosSysOnSurfaceVariant))
       .SetID(kTimeLabelID)
       .SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT)
       .SetAutoColorReadabilityEnabled(false)
@@ -234,6 +250,8 @@
   SetUpFocusHighlight(item_corner_radius);
 
   std::u16string formatted_time_text;
+
+  is_past_event_ = end_time < base::Time::NowFromSystemTime();
   if (calendar_utils::IsMultiDayEvent(&event) || event.all_day_event()) {
     formatted_time_text = event_date_formatter_util::GetMultiDayText(
         &event, selected_date_params_.selected_date_midnight,
@@ -241,8 +259,9 @@
   } else {
     formatted_time_text =
         event_date_formatter_util::GetFormattedInterval(start_time, end_time);
-    is_current_or_next_event_ = end_time >= base::Time::NowFromSystemTime();
+    is_current_or_next_single_day_event_ = !is_past_event_;
   }
+
   const auto tooltip_text = l10n_util::GetStringFUTF16(
       IDS_ASH_CALENDAR_EVENT_ENTRY_TOOL_TIP, base::UTF8ToUTF16(event.summary()),
       formatted_time_text);
@@ -266,8 +285,8 @@
     layout_vertical_start->set_main_axis_alignment(
         views::BoxLayout::MainAxisAlignment::kStart);
     event_list_dot_container
-        ->AddChildView(
-            std::make_unique<CalendarEventListItemDot>(event.color_id()))
+        ->AddChildView(std::make_unique<CalendarEventListItemDot>(
+            is_past_event_ ? kPastEventsColorId : event.color_id()))
         ->SetID(kEventListItemDotID);
   }
 
@@ -277,14 +296,16 @@
   vertical_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical));
   vertical_container->AddChildView(
-      CreateSummaryLabel(event.summary(), tooltip_text, ui_params.fixed_width)
+      CreateSummaryLabel(event.summary(), tooltip_text, ui_params.fixed_width,
+                         is_past_event_)
           .Build());
   vertical_container->AddChildView(
-      CreateTimeLabel(formatted_time_text, tooltip_text).Build());
+      CreateTimeLabel(formatted_time_text, tooltip_text, is_past_event_)
+          .Build());
   horizontal_container_layout_manager->SetFlexForView(vertical_container, 1);
 
-  // Join button.
-  if (!video_conference_url_.is_empty()) {
+  // Join button. Only shows it if the event is not the past event.
+  if (!video_conference_url_.is_empty() && !is_past_event_) {
     views::View* join_button_container =
         horizontal_container->AddChildView(std::make_unique<views::View>());
     auto* layout_vertical_center = join_button_container->SetLayoutManager(
@@ -311,7 +332,8 @@
 void CalendarEventListItemView::OnThemeChanged() {
   views::View::OnThemeChanged();
   SetBackground(views::CreateSolidBackground(
-      GetColorProvider()->GetColor(cros_tokens::kCrosSysSurface5)));
+      SkColorSetA(GetColorProvider()->GetColor(cros_tokens::kCrosSysSurface5),
+                  is_past_event_ ? SK_Alpha50Opacity : SK_AlphaOPAQUE)));
 }
 
 void CalendarEventListItemView::PerformAction(const ui::Event& event) {
diff --git a/ash/system/time/calendar_event_list_item_view.h b/ash/system/time/calendar_event_list_item_view.h
index e824c87..da14403 100644
--- a/ash/system/time/calendar_event_list_item_view.h
+++ b/ash/system/time/calendar_event_list_item_view.h
@@ -86,7 +86,9 @@
 
   void OnJoinMeetingButtonPressed(const ui::Event& event);
 
-  bool is_current_or_next_event() const { return is_current_or_next_event_; }
+  bool is_current_or_next_single_day_event() const {
+    return is_current_or_next_single_day_event_;
+  }
 
  private:
   friend class CalendarViewEventListViewTest;
@@ -104,7 +106,10 @@
 
   // Whether this item which is not an all-day or multi-day event is the current
   // or next event. Used for auto scroll in the `CalendarEventListView`.
-  bool is_current_or_next_event_ = false;
+  bool is_current_or_next_single_day_event_ = false;
+
+  // Whether this event has ended by now.
+  bool is_past_event_ = false;
 
   base::WeakPtrFactory<CalendarEventListItemView> weak_ptr_factory_{this};
 };
diff --git a/ash/system/time/calendar_event_list_item_view_unittest.cc b/ash/system/time/calendar_event_list_item_view_unittest.cc
index 2b6fcbe..b64c4fda 100644
--- a/ash/system/time/calendar_event_list_item_view_unittest.cc
+++ b/ash/system/time/calendar_event_list_item_view_unittest.cc
@@ -33,8 +33,8 @@
 class CalendarViewEventListItemViewTest : public AshTestBase {
  public:
   CalendarViewEventListItemViewTest() = default;
-  CalendarViewEventListItemViewTest(
-      const CalendarViewEventListItemViewTest&) = delete;
+  CalendarViewEventListItemViewTest(const CalendarViewEventListItemViewTest&) =
+      delete;
   CalendarViewEventListItemViewTest& operator=(
       const CalendarViewEventListItemViewTest&) = delete;
   ~CalendarViewEventListItemViewTest() override = default;
@@ -56,13 +56,12 @@
     event_list_item_view_.reset();
     controller_->UpdateMonth(date);
     controller_->selected_date_ = date;
-    event_list_item_view_ =
-        std::make_unique<CalendarEventListItemView>(
-            controller_.get(),
-            SelectedDateParams{controller_->selected_date().value(),
-                               controller_->selected_date_midnight(),
-                               controller_->selected_date_midnight_utc()},
-            *event, ui_params, EventListItemIndex{1, 1});
+    event_list_item_view_ = std::make_unique<CalendarEventListItemView>(
+        controller_.get(),
+        SelectedDateParams{controller_->selected_date().value(),
+                           controller_->selected_date_midnight(),
+                           controller_->selected_date_midnight_utc()},
+        *event, ui_params, EventListItemIndex{1, 1});
   }
 
   void SetSelectedDateInController(base::Time date) {
@@ -92,6 +91,9 @@
         event_list_item_view_->GetViewByID(kJoinButtonID));
   }
 
+  static base::Time FakeTimeNow() { return fake_time_; }
+  static void SetFakeNow(base::Time fake_now) { fake_time_ = fake_now; }
+
   CalendarViewController* controller() { return controller_.get(); }
 
   CalendarEventListItemView* event_list_item_view() {
@@ -101,8 +103,11 @@
  private:
   std::unique_ptr<CalendarEventListItemView> event_list_item_view_;
   std::unique_ptr<CalendarViewController> controller_;
+  static base::Time fake_time_;
 };
 
+base::Time CalendarViewEventListItemViewTest::fake_time_;
+
 TEST_F(CalendarViewEventListItemViewTest,
        ShouldShowCorrectLabels_GivenAOneHourEvent) {
   ash::system::ScopedTimezoneSettings timezone_settings(u"GMT+2");
@@ -132,8 +137,7 @@
       event_list_item_view()->GetAccessibleName());
 }
 
-TEST_F(CalendarViewEventListItemViewTest,
-       EventListViewItemTopRoundedCorners) {
+TEST_F(CalendarViewEventListItemViewTest, EventListViewItemTopRoundedCorners) {
   base::Time date;
   ASSERT_TRUE(base::Time::FromString("22 Nov 2021 00:00 UTC", &date));
   SetSelectedDateInController(date);
@@ -169,8 +173,7 @@
             background_layer->rounded_corner_radii());
 }
 
-TEST_F(CalendarViewEventListItemViewTest,
-       EventListViewItemAllRoundedCorners) {
+TEST_F(CalendarViewEventListItemViewTest, EventListViewItemAllRoundedCorners) {
   base::Time date;
   ASSERT_TRUE(base::Time::FromString("22 Nov 2021 00:00 UTC", &date));
   SetSelectedDateInController(date);
@@ -234,8 +237,7 @@
   EXPECT_EQ(fixed_width, GetSummaryLabel()->width());
 }
 
-TEST_F(CalendarViewEventListItemViewTest,
-       ShouldShowAndHideEventListItemDot) {
+TEST_F(CalendarViewEventListItemViewTest, ShouldShowAndHideEventListItemDot) {
   base::Time date;
   ASSERT_TRUE(base::Time::FromString("22 Nov 2021 00:00 UTC", &date));
   SetSelectedDateInController(date);
@@ -266,20 +268,58 @@
 
 TEST_F(CalendarViewEventListItemViewTest,
        ShouldShowJoinMeetingButton_WhenConferenceDataUrlExists) {
+  // Sets the timezone to "GMT".
+  ash::system::ScopedTimezoneSettings timezone_settings(u"GMT");
+
   base::Time date;
-  ASSERT_TRUE(base::Time::FromString("22 Nov 2021 00:00 UTC", &date));
+  ASSERT_TRUE(base::Time::FromString("22 Nov 2021 00:00 GMT", &date));
   SetSelectedDateInController(date);
   const char* start_time_string = "22 Nov 2021 09:00 GMT";
   const char* end_time_string = "22 Nov 2021 10:00 GMT";
   const auto event = CreateEvent(start_time_string, end_time_string, false,
                                  GURL("https://meet.google.com/my-meeting"));
 
+  // Sets the current time to be a time that the event has started but hasn't
+  // ended.
+  base::Time current_time;
+  ASSERT_TRUE(base::Time::FromString("22 Nov 2021 09:30 GMT", &current_time));
+  SetFakeNow(current_time);
+  base::subtle::ScopedTimeClockOverrides time_override(
+      &CalendarViewEventListItemViewTest::FakeTimeNow,
+      /*time_ticks_override=*/nullptr, /*thread_ticks_override=*/nullptr);
+
   CreateEventListItemView(date, event.get());
 
   EXPECT_TRUE(GetJoinButton());
 }
 
 TEST_F(CalendarViewEventListItemViewTest,
+       ShouldNotShowJoinMeetingButton_WhenTheEventHasEnded) {
+  // Sets the timezone to "GMT".
+  ash::system::ScopedTimezoneSettings timezone_settings(u"GMT");
+
+  base::Time date;
+  ASSERT_TRUE(base::Time::FromString("22 Nov 2021 00:00 GMT", &date));
+  SetSelectedDateInController(date);
+  const char* start_time_string = "22 Nov 2021 09:00 GMT";
+  const char* end_time_string = "22 Nov 2021 10:00 GMT";
+  const auto event = CreateEvent(start_time_string, end_time_string, false,
+                                 GURL("https://meet.google.com/my-meeting"));
+
+  // Sets the current time to be a time that the event has ended.
+  base::Time current_time;
+  ASSERT_TRUE(base::Time::FromString("22 Nov 2021 10:30 GMT", &current_time));
+  SetFakeNow(current_time);
+  base::subtle::ScopedTimeClockOverrides time_override(
+      &CalendarViewEventListItemViewTest::FakeTimeNow,
+      /*time_ticks_override=*/nullptr, /*thread_ticks_override=*/nullptr);
+
+  CreateEventListItemView(date, event.get());
+
+  EXPECT_FALSE(GetJoinButton());
+}
+
+TEST_F(CalendarViewEventListItemViewTest,
        ShouldHideJoinMeetingButton_WhenConferenceDataUrlDoesNotExist) {
   base::Time date;
   ASSERT_TRUE(base::Time::FromString("22 Nov 2021 00:00 UTC", &date));
diff --git a/ash/system/time/calendar_event_list_view.cc b/ash/system/time/calendar_event_list_view.cc
index 4ac7031..8c87a6b 100644
--- a/ash/system/time/calendar_event_list_view.cc
+++ b/ash/system/time/calendar_event_list_view.cc
@@ -283,7 +283,7 @@
     // The `current_or_next_event_view_` is the first event that is not an
     // all-day or multi-day event, and is the ongoing or the following event.
     if (!current_or_next_event_view_ &&
-        event_list_item_view->is_current_or_next_event()) {
+        event_list_item_view->is_current_or_next_single_day_event()) {
       current_or_next_event_view_ = event_list_item_view;
       current_or_next_event_index_ = event_index - 1;
     }
diff --git a/ash/system/time/calendar_view.cc b/ash/system/time/calendar_view.cc
index 4c225f9..8d91ca4 100644
--- a/ash/system/time/calendar_view.cc
+++ b/ash/system/time/calendar_view.cc
@@ -509,7 +509,7 @@
   if (calendar_utils::IsForGlanceablesV2()) {
     calendar_header_view = CreateCalendarHeaderRow();
   } else {
-    CreateCalendarTitleRow(IDS_ASH_CALENDAR_TITLE);
+    CreateCalendarTitleRow();
   }
 
   // Adds an empty view as a placeholder so that the views below won't move up
@@ -663,7 +663,7 @@
   return AddChildView(calendar_header_view);
 }
 
-void CalendarView::CreateCalendarTitleRow(int string_id) {
+void CalendarView::CreateCalendarTitleRow() {
   DCHECK(!tri_view_);
 
   tri_view_ =
@@ -674,7 +674,7 @@
   ConfigureTitleTriView(tri_view_.get(), TriView::Container::END);
 
   auto* title_label = TrayPopupUtils::CreateDefaultLabel();
-  title_label->SetText(l10n_util::GetStringUTF16(string_id));
+  title_label->SetText(l10n_util::GetStringUTF16(IDS_ASH_CALENDAR_TITLE));
   title_label->SetEnabledColorId(cros_tokens::kCrosSysOnSurface);
   ash::TypographyProvider::Get()->StyleLabel(ash::TypographyToken::kCrosTitle1,
                                              *title_label);
diff --git a/ash/system/time/calendar_view.h b/ash/system/time/calendar_view.h
index 87e8ace..f28e0697 100644
--- a/ash/system/time/calendar_view.h
+++ b/ash/system/time/calendar_view.h
@@ -199,7 +199,7 @@
 
   // Creates the calendar view title that includes a label,
   // `reset_to_today_button_`, and a `settings_button_`.
-  void CreateCalendarTitleRow(int string_id);
+  void CreateCalendarTitleRow();
 
   // Creates the `CalendarHeaderView`s container that contains `header_` and
   // `temp_header_`.
diff --git a/ash/system/time/calendar_view_pixeltest.cc b/ash/system/time/calendar_view_pixeltest.cc
index c15951ac..5b922e03 100644
--- a/ash/system/time/calendar_view_pixeltest.cc
+++ b/ash/system/time/calendar_view_pixeltest.cc
@@ -138,7 +138,7 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "event_list_view",
-      /*revision_number=*/9, GetEventListView()));
+      /*revision_number=*/10, GetEventListView()));
 }
 
 }  // namespace ash
diff --git a/ash/webui/personalization_app/resources/js/personalization_breadcrumb_element.html b/ash/webui/personalization_app/resources/js/personalization_breadcrumb_element.html
index de86945..f2f88335 100644
--- a/ash/webui/personalization_app/resources/js/personalization_breadcrumb_element.html
+++ b/ash/webui/personalization_app/resources/js/personalization_breadcrumb_element.html
@@ -70,6 +70,22 @@
     --text-color: var(--cros-sys-on_surface);
     background-color: transparent;
   }
+
+  #seaPenDropdown {
+    margin-inline-start: 6px;
+  }
+
+  .dropdown-check {
+    margin-inline-end: 16px;
+  }
+
+  button:not([aria-selected='true']) iron-icon.dropdown-check {
+    visibility: hidden;
+  }
+
+  button {
+    padding: 8px 32px 8px 16px;
+  }
 </style>
 <nav id="container">
     <iron-a11y-keys id="keys" keys="left right" on-keys-pressed="onKeysPress_">
@@ -95,7 +111,27 @@
             id="breadcrumb[[index]]"
             on-click="onBreadcrumbClick_">
           <div class="ellipsis" title$="[[breadcrumb]]">[[breadcrumb]]</div>
+          <template is="dom-if" if="[[shouldShowSeaPenDropdown_(path, breadcrumb)]]" restamp>
+            <cr-icon-button
+                id="seaPenDropdown"
+                iron-icon="cr:arrow-drop-down"
+                role="button"
+                on-click="onClickMenuIcon_">
+            </cr-icon-button>
+          </template>
         </cr-button>
       </template>
+      <cr-action-menu>
+        <template is="dom-repeat" items="[[seaPenTemplates_]]" as="template">
+          <button
+              aria-selected$="[[getAriaSelected_(template.id, seaPenTemplateId)]]"
+              class="dropdown-item"
+              data-id$="[[template.id]]"
+              on-click="onClickMenuItem_">
+            <iron-icon class="dropdown-check" icon="cr:check"></iron-icon>
+            [[template.title]]
+          </button>
+        </template>
+      </cr-action-menu>
     </iron-selector>
 </nav>
diff --git a/ash/webui/personalization_app/resources/js/personalization_breadcrumb_element.ts b/ash/webui/personalization_app/resources/js/personalization_breadcrumb_element.ts
index c0d094e..1aeba75 100644
--- a/ash/webui/personalization_app/resources/js/personalization_breadcrumb_element.ts
+++ b/ash/webui/personalization_app/resources/js/personalization_breadcrumb_element.ts
@@ -18,6 +18,8 @@
 import '../css/common.css.js';
 import '../css/cros_button_style.css.js';
 
+import {assert} from 'chrome://resources/ash/common/assert.js';
+import {AnchorAlignment} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
 import {IronA11yKeysElement} from 'chrome://resources/polymer/v3_0/iron-a11y-keys/iron-a11y-keys.js';
 import {IronSelectorElement} from 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js';
 
@@ -27,7 +29,8 @@
 import {isPathValid, Paths, PersonalizationRouterElement} from './personalization_router_element.js';
 import {WithPersonalizationStore} from './personalization_store.js';
 import {inBetween, isNonEmptyArray} from './utils.js';
-import {getSeaPenTemplates, QUERY, SeaPenTemplate} from './wallpaper/sea_pen/constants.js';
+import {getSeaPenTemplates, SeaPenTemplate} from './wallpaper/sea_pen/constants.js';
+import {isSeaPenEnabled} from './wallpaper/sea_pen/load_time_booleans.js';
 import {findAlbumById} from './wallpaper/utils.js';
 
 /** Event interface for dom-repeat. */
@@ -226,10 +229,7 @@
         breadcrumbs.push(this.i18n('wallpaperLabel'));
         // TODO(b/308200616): Add real text
         breadcrumbs.push('Sea Pen');
-        if (this.seaPenTemplateId === QUERY) {
-          breadcrumbs.push(QUERY);
-        } else if (
-            this.seaPenTemplateId && isNonEmptyArray(this.seaPenTemplates_)) {
+        if (this.seaPenTemplateId && isNonEmptyArray(this.seaPenTemplates_)) {
           const template = this.seaPenTemplates_.find(
               template => template.id === this.seaPenTemplateId);
           if (template) {
@@ -289,6 +289,45 @@
     }
   }
 
+  private onClickMenuIcon_(e: Event) {
+    const targetElement = e.currentTarget as HTMLElement;
+    const menuIconContainerRect = targetElement.getBoundingClientRect();
+    const config = {
+      // 8px is the padding of .menu-icon-container.
+      top: menuIconContainerRect.top - 8,
+      left: menuIconContainerRect.left - menuIconContainerRect.width / 2,
+      height: menuIconContainerRect.height,
+      width: menuIconContainerRect.width,
+      anchorAlignmentX: AnchorAlignment.CENTER,
+      anchorAlignmentY: AnchorAlignment.AFTER_END,
+    };
+    const menuElement = this.shadowRoot!.querySelector('cr-action-menu');
+    menuElement!.showAtPosition(config);
+  }
+
+  private onClickMenuItem_(e: Event) {
+    const targetElement = e.currentTarget as HTMLElement;
+    const templateId = targetElement.dataset['id'];
+    assert(!!templateId, 'templateId is required');
+    PersonalizationRouterElement.instance().goToRoute(
+        Paths.SEA_PEN_RESULTS, {seaPenTemplateId: templateId});
+  }
+
+  private shouldShowSeaPenDropdown_(path: string, breadcrumb: string): boolean {
+    if (!isSeaPenEnabled()) {
+      return false;
+    }
+    const template =
+        this.seaPenTemplates_?.find(template => template.title === breadcrumb);
+
+    return path === Paths.SEA_PEN_RESULTS && !!template;
+  }
+
+  private getAriaSelected_(templateId: string, seaPenTemplateId: string):
+      'true'|'false' {
+    return templateId === seaPenTemplateId ? 'true' : 'false';
+  }
+
   private onHomeIconClick_() {
     PersonalizationRouterElement.instance().goToRoute(Paths.ROOT);
   }
diff --git a/ash/webui/personalization_app/resources/js/personalization_router_element.ts b/ash/webui/personalization_app/resources/js/personalization_router_element.ts
index c87537b..d4ea9d95 100644
--- a/ash/webui/personalization_app/resources/js/personalization_router_element.ts
+++ b/ash/webui/personalization_app/resources/js/personalization_router_element.ts
@@ -19,6 +19,7 @@
 import {isAmbientModeAllowed} from './load_time_booleans.js';
 import {logPersonalizationPathUMA} from './personalization_metrics_logger.js';
 import {getTemplate} from './personalization_router_element.html.js';
+import {WallpaperObserver} from './wallpaper/wallpaper_observer.js';
 
 export enum Paths {
   AMBIENT = '/ambient',
@@ -117,6 +118,7 @@
 
   override connectedCallback() {
     super.connectedCallback();
+    WallpaperObserver.initWallpaperObserverIfNeeded();
   }
 
   get collectionId() {
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_actions.ts b/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_actions.ts
index 489acc72..cc186b0 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_actions.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_actions.ts
@@ -17,15 +17,22 @@
   BEGIN_SEARCH_SEA_PEN_THUMBNAILS = 'begin_search_sea_pen_thumbnails',
   BEGIN_LOAD_RECENT_SEA_PEN_IMAGES = 'begin_load_recent_sea_pen_images',
   BEGIN_LOAD_RECENT_SEA_PEN_IMAGE_DATA = 'begin_load_recent_sea_pen_image_data',
+  BEGIN_LOAD_SELECTED_RECENT_SEA_PEN_IMAGE =
+      'begin_load_selected_recent_sea_pen_image',
+  BEGIN_SELECT_RECENT_SEA_PEN_IMAGE = 'begin_select_recent_sea_pen_image',
+  END_SELECT_RECENT_SEA_PEN_IMAGE = 'end_select_recent_sea_pen_image',
   SET_SEA_PEN_THUMBNAILS = 'set_sea_pen_thumbnails',
   SET_RECENT_SEA_PEN_IMAGES = 'set_recent_sea_pen_images',
   SET_RECENT_SEA_PEN_IMAGE_DATA = 'set_recent_sea_pen_image_data',
+  SET_SELECTED_RECENT_SEA_PEN_IMAGE = 'set_selected_recent_sea_pen_image',
 }
 
-export type SeaPenActions =
-    BeginSearchSeaPenThumbnailsAction|BeginLoadRecentSeaPenImagesAction|
-    BeginLoadRecentSeaPenImageDataAction|SetSeaPenThumbnailsAction|
-    SetRecentSeaPenImagesAction|SetRecentSeaPenImageDataAction;
+export type SeaPenActions = BeginSearchSeaPenThumbnailsAction|
+    BeginLoadRecentSeaPenImagesAction|BeginLoadRecentSeaPenImageDataAction|
+    BeginLoadSelectedRecentSeaPenImageAction|BeginSelectRecentSeaPenImageAction|
+    EndSelectRecentSeaPenImageAction|SetSeaPenThumbnailsAction|
+    SetRecentSeaPenImagesAction|SetRecentSeaPenImageDataAction|
+    SetSelectedRecentSeaPenImageAction;
 
 export interface BeginSearchSeaPenThumbnailsAction extends Action {
   name: SeaPenActionName.BEGIN_SEARCH_SEA_PEN_THUMBNAILS;
@@ -119,3 +126,65 @@
     data,
   };
 }
+
+export interface BeginSelectRecentSeaPenImageAction extends Action {
+  name: SeaPenActionName.BEGIN_SELECT_RECENT_SEA_PEN_IMAGE;
+  image: FilePath;
+}
+
+/**
+ * Begins selecting a recent Sea Pen image.
+ */
+export function beginSelectRecentSeaPenImageAction(image: FilePath):
+    BeginSelectRecentSeaPenImageAction {
+  return {
+    name: SeaPenActionName.BEGIN_SELECT_RECENT_SEA_PEN_IMAGE,
+    image: image,
+  };
+}
+
+export interface EndSelectRecentSeaPenImageAction extends Action {
+  name: SeaPenActionName.END_SELECT_RECENT_SEA_PEN_IMAGE;
+  image: FilePath;
+  success: boolean;
+}
+
+/**
+ * Ends selecting a recent Sea Pen image.
+ */
+export function endSelectRecentSeaPenImageAction(
+    image: FilePath, success: boolean): EndSelectRecentSeaPenImageAction {
+  return {
+    name: SeaPenActionName.END_SELECT_RECENT_SEA_PEN_IMAGE,
+    image,
+    success,
+  };
+}
+
+export interface BeginLoadSelectedRecentSeaPenImageAction extends Action {
+  name: SeaPenActionName.BEGIN_LOAD_SELECTED_RECENT_SEA_PEN_IMAGE;
+}
+
+/**
+ * Begins loading the selected recent Sea Pen image.
+ */
+export function beginLoadSelectedRecentSeaPenImageAction():
+    BeginLoadSelectedRecentSeaPenImageAction {
+  return {name: SeaPenActionName.BEGIN_LOAD_SELECTED_RECENT_SEA_PEN_IMAGE};
+}
+
+export interface SetSelectedRecentSeaPenImageAction extends Action {
+  name: SeaPenActionName.SET_SELECTED_RECENT_SEA_PEN_IMAGE;
+  key: string|null;
+}
+
+/**
+ * Sets the selected recent Sea Pen image.
+ */
+export function setSelectedRecentSeaPenImageAction(key: string|null):
+    SetSelectedRecentSeaPenImageAction {
+  return {
+    name: SeaPenActionName.SET_SELECTED_RECENT_SEA_PEN_IMAGE,
+    key: key,
+  };
+}
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_controller.ts b/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_controller.ts
index 57f8d3f..d9e3733 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_controller.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_controller.ts
@@ -6,16 +6,33 @@
 
 import {SeaPenProviderInterface, SeaPenQuery, SeaPenThumbnail} from '../../../sea_pen.mojom-webui.js';
 import {isNonEmptyArray} from '../../utils.js';
+import {isFilePath} from '../utils.js';
 
 import * as seaPenAction from './sea_pen_actions.js';
 import {SeaPenStoreInterface} from './sea_pen_store.js';
 
 export async function selectRecentSeaPenImage(
-    image: FilePath, provider: SeaPenProviderInterface): Promise<void> {
-  const {success} = await provider.selectRecentSeaPenImage(image);
-  if (!success) {
-    console.warn('failed to set recent sea pen image');
+    image: FilePath, provider: SeaPenProviderInterface,
+    store: SeaPenStoreInterface): Promise<void> {
+  // Returns if the selected image is the current wallpaper.
+  if (isFilePath(image) && image.path === store.data.currentSelected) {
+    return;
   }
+  // Batch these changes together to reduce polymer churn as multiple state
+  // fields change quickly.
+  store.beginBatchUpdate();
+  store.dispatch(seaPenAction.beginSelectRecentSeaPenImageAction(image));
+  store.dispatch(seaPenAction.beginLoadSelectedRecentSeaPenImageAction());
+  store.endBatchUpdate();
+
+  const {success} = await provider.selectRecentSeaPenImage(image);
+
+  store.beginBatchUpdate();
+  store.dispatch(seaPenAction.endSelectRecentSeaPenImageAction(image, success));
+  if (!success) {
+    console.warn('Error setting wallpaper');
+  }
+  store.endBatchUpdate();
 }
 
 export async function searchSeaPenThumbnails(
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_recent_wallpapers_element.html b/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_recent_wallpapers_element.html
index 6ff72a4..1ab8dad8 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_recent_wallpapers_element.html
+++ b/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_recent_wallpapers_element.html
@@ -81,7 +81,7 @@
             aria-posinset$="[[getAriaIndex_(index)]]"
             on-wallpaper-grid-item-selected="onRecentImageSelected_"
             role="option"
-            selected="[[isRecentImageSelected_(image)]]"
+            selected="[[isRecentImageSelected_(image, currentSelected_, pendingSelected_)]]"
             src="[[getRecentImageUrl_(image, recentImageData_, recentImageDataLoading_)]]"
             tabindex$="[[tabIndex]]">
         </wallpaper-grid-item>
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_recent_wallpapers_element.ts b/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_recent_wallpapers_element.ts
index 391b811..4039085 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_recent_wallpapers_element.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_recent_wallpapers_element.ts
@@ -60,6 +60,10 @@
         type: Number,
         value: null,
       },
+
+      currentSelected_: Object,
+
+      pendingSelected_: Object,
     };
   }
 
@@ -68,6 +72,8 @@
   private recentImageDataLoading_: Record<FilePath['path'], boolean>;
   private recentImagesToDisplay_: FilePath[];
   private currentShowWallpaperInfoDialog_: number|null;
+  private currentSelected_: string|null;
+  private pendingSelected_: FilePath|null;
 
   static get observers() {
     return ['onRecentImageLoaded_(recentImageData_, recentImageDataLoading_)'];
@@ -81,6 +87,10 @@
         'recentImageData_', state => state.recentImageData);
     this.watch<SeaPenRecentWallpapersElement['recentImageDataLoading_']>(
         'recentImageDataLoading_', state => state.loading.recentImageData);
+    this.watch<SeaPenRecentWallpapersElement['currentSelected_']>(
+        'currentSelected_', state => state.currentSelected);
+    this.watch<SeaPenRecentWallpapersElement['pendingSelected_']>(
+        'pendingSelected_', state => state.pendingSelected);
     this.updateFromStore();
     // TODO(b/304576846): also refetch sea pen data when adding and deleting
     // image.
@@ -171,16 +181,24 @@
     return isNonEmptyArray(recentImages);
   }
 
-  private isRecentImageSelected_(_: FilePath) {
-    // TODO(b/314342868) show real checked state.
-    return false;
+  private isRecentImageSelected_(
+      image: FilePath, currentSelected: string|null,
+      pendingSelected: FilePath|null) {
+    if (!isFilePath(image)) {
+      return false;
+    }
+
+    return (isFilePath(pendingSelected) &&
+            image.path === pendingSelected.path) ||
+        (!pendingSelected && image.path === currentSelected);
   }
 
   private onRecentImageSelected_(event: WallpaperGridItemSelectedEvent&
                                  {model: {image: FilePath}}) {
     assert(
         isFilePath(event.model.image), 'recent Sea Pen image is a file path');
-    selectRecentSeaPenImage(event.model.image, getSeaPenProvider());
+    selectRecentSeaPenImage(
+        event.model.image, getSeaPenProvider(), this.getStore());
   }
 
   private onClickMenuIcon_(e: Event) {
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_reducer.ts b/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_reducer.ts
index b3f27a6..8de887f 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_reducer.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_reducer.ts
@@ -63,6 +63,74 @@
           [action.id]: false,
         },
       };
+    case SeaPenActionName.BEGIN_SELECT_RECENT_SEA_PEN_IMAGE:
+      return {...state, setImage: state.setImage + 1};
+    case SeaPenActionName.END_SELECT_RECENT_SEA_PEN_IMAGE:
+      if (state.setImage <= 0) {
+        console.error('Impossible state for loading.setImage');
+        // Reset to 0.
+        return {...state, setImage: 0};
+      }
+      return {...state, setImage: state.setImage - 1};
+    case SeaPenActionName.BEGIN_LOAD_SELECTED_RECENT_SEA_PEN_IMAGE:
+      return {
+        ...state,
+        currentSelected: true,
+      };
+    case SeaPenActionName.SET_SELECTED_RECENT_SEA_PEN_IMAGE:
+      return {
+        ...state,
+        currentSelected: false,
+      };
+    default:
+      return state;
+  }
+}
+
+function currentSelectedReducer(
+    state: string|null, action: SeaPenActions): string|null {
+  switch (action.name) {
+    case SeaPenActionName.SET_SELECTED_RECENT_SEA_PEN_IMAGE:
+      return action.key;
+    default:
+      return state;
+  }
+}
+
+/**
+ * Reducer for the pending selected image. The pendingSelected state is set when
+ * a user clicks on an image and before the client code is reached.
+ *
+ * Note: We allow multiple concurrent requests of selecting images while only
+ * keeping the latest pending image and failing others occurred in between.
+ * The pendingSelected state should not be cleared in this scenario (of multiple
+ * concurrent requests). Otherwise, it results in a unwanted jumpy motion of
+ * selected state.
+ */
+function pendingSelectedReducer(
+    state: FilePath|null, action: SeaPenActions,
+    globalState: SeaPenState): FilePath|null {
+  switch (action.name) {
+    case SeaPenActionName.BEGIN_SELECT_RECENT_SEA_PEN_IMAGE:
+      return action.image;
+    case SeaPenActionName.SET_SELECTED_RECENT_SEA_PEN_IMAGE:
+      const {key} = action;
+      if (!key) {
+        console.warn('pendingSelectedReducer: Failed to get selected image.');
+        return null;
+      } else if (globalState.loading.setImage == 0) {
+        // Clear the pending state when there are no more requests.
+        return null;
+      }
+      return state;
+    case SeaPenActionName.END_SELECT_RECENT_SEA_PEN_IMAGE:
+      const {success} = action;
+      if (!success && globalState.loading.setImage <= 1) {
+        // Clear the pending selected state if an error occurs and
+        // there are no multiple concurrent requests of selecting images.
+        return null;
+      }
+      return state;
     default:
       return state;
   }
@@ -118,6 +186,9 @@
     recentImageData: recentImageDataReducer(state.recentImageData, action),
     recentImages: recentImagesReducer(state.recentImages, action),
     thumbnails: thumbnailsReducer(state.thumbnails, action),
+    currentSelected: currentSelectedReducer(state.currentSelected, action),
+    pendingSelected:
+        pendingSelectedReducer(state.pendingSelected, action, state),
   };
   return newState;
 }
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_state.ts b/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_state.ts
index 3b6a05c..f9fb96a 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_state.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/sea_pen/sea_pen_state.ts
@@ -12,6 +12,8 @@
   recentImageData: Record<FilePath['path'], boolean>;
   recentImages: boolean;
   thumbnails: boolean;
+  currentSelected: boolean;
+  setImage: number;
 }
 
 export interface SeaPenState {
@@ -19,6 +21,8 @@
   recentImageData: Record<FilePath['path'], RecentSeaPenData>;
   recentImages: FilePath[]|null;
   thumbnails: SeaPenThumbnail[]|null;
+  currentSelected: string|null;
+  pendingSelected: FilePath|null;
 }
 
 export function emptyState(): SeaPenState {
@@ -27,9 +31,13 @@
       recentImages: false,
       recentImageData: {},
       thumbnails: false,
+      currentSelected: false,
+      setImage: 0,
     },
     recentImageData: {},
     recentImages: null,
     thumbnails: null,
+    currentSelected: null,
+    pendingSelected: null,
   };
 }
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_observer.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_observer.ts
index 2c37bf80..a0edbc1 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_observer.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_observer.ts
@@ -5,6 +5,7 @@
 import {CurrentAttribution, CurrentWallpaper, WallpaperObserverInterface, WallpaperObserverReceiver, WallpaperProviderInterface, WallpaperType} from '../../personalization_app.mojom-webui.js';
 import {PersonalizationStore} from '../personalization_store.js';
 
+import {setSelectedRecentSeaPenImageAction} from './sea_pen/sea_pen_actions.js';
 import {setAttributionAction, setFullscreenEnabledAction, setSelectedImageAction, setUpdatedDailyRefreshImageAction} from './wallpaper_actions.js';
 import {getDailyRefreshState} from './wallpaper_controller.js';
 import {getWallpaperProvider} from './wallpaper_interface_provider.js';
@@ -75,6 +76,13 @@
       initialLoadTimeout = null;
     }
     store.dispatch(setSelectedImageAction(currentWallpaper));
+
+    if (currentWallpaper && currentWallpaper.type == WallpaperType.kSeaPen) {
+      store.dispatch(setSelectedRecentSeaPenImageAction(currentWallpaper.key));
+    } else {
+      store.dispatch(setSelectedRecentSeaPenImageAction(null));
+    }
+
     if (currentWallpaper &&
         (currentWallpaper.type == WallpaperType.kDailyGooglePhotos ||
          currentWallpaper.type == WallpaperType.kDaily ||
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_preview_element.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_preview_element.ts
index d930eb6..18581f9 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_preview_element.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_preview_element.ts
@@ -22,7 +22,6 @@
 import {WithPersonalizationStore} from '../personalization_store.js';
 
 import {getWallpaperAriaLabel, getWallpaperSrc} from './utils.js';
-import {WallpaperObserver} from './wallpaper_observer.js';
 import {getTemplate} from './wallpaper_preview_element.html.js';
 
 export class WallpaperPreviewElement extends WithPersonalizationStore {
@@ -71,7 +70,6 @@
 
   override connectedCallback() {
     super.connectedCallback();
-    WallpaperObserver.initWallpaperObserverIfNeeded();
     this.watch('attribution_', state => state.wallpaper.attribution);
     this.watch('image_', state => state.wallpaper.currentSelected);
     this.watch(
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_selected_element.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_selected_element.ts
index 45ec505..01eba9c6 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_selected_element.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_selected_element.ts
@@ -28,7 +28,6 @@
 import {getLocalStorageAttribution, getWallpaperAriaLabel, getWallpaperLayoutEnum, getWallpaperSrc} from './utils.js';
 import {getDailyRefreshState, selectGooglePhotosAlbum, setCurrentWallpaperLayout, setDailyRefreshCollectionId, updateDailyRefreshWallpaper} from './wallpaper_controller.js';
 import {getWallpaperProvider} from './wallpaper_interface_provider.js';
-import {WallpaperObserver} from './wallpaper_observer.js';
 import {getTemplate} from './wallpaper_selected_element.html.js';
 import {DailyRefreshState} from './wallpaper_state.js';
 
@@ -200,7 +199,6 @@
 
   override connectedCallback() {
     super.connectedCallback();
-    WallpaperObserver.initWallpaperObserverIfNeeded();
     this.watch('error_', state => state.error);
     this.watch('attribution_', state => state.wallpaper.attribution);
     this.watch('image_', state => state.wallpaper.currentSelected);
diff --git a/ash/wm/base_state.cc b/ash/wm/base_state.cc
index b05acdc..7fa169f5 100644
--- a/ash/wm/base_state.cc
+++ b/ash/wm/base_state.cc
@@ -215,7 +215,7 @@
          event_type == WM_EVENT_SNAP_SECONDARY);
   DCHECK(window_state->CanSnap());
 
-  window_state->set_bounds_changed_by_user(true);
+  window_state->SetBoundsChangedByUser(true);
   aura::Window* window = window_state->window();
   // `SplitViewController` will decide if the window needs to be snapped in
   // split view.
diff --git a/ash/wm/multi_display/persistent_window_controller_unittest.cc b/ash/wm/multi_display/persistent_window_controller_unittest.cc
index 606b1c1..29696eba 100644
--- a/ash/wm/multi_display/persistent_window_controller_unittest.cc
+++ b/ash/wm/multi_display/persistent_window_controller_unittest.cc
@@ -102,7 +102,7 @@
 
   // Sets |w2|'s bounds changed by user and then reconnects secondary display.
   WindowState* w2_state = WindowState::Get(w2);
-  w2_state->set_bounds_changed_by_user(true);
+  w2_state->SetBoundsChangedByUser(true);
   display_info_list.push_back(secondary_info);
   display_manager()->OnNativeDisplaysChanged(display_info_list);
   EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
@@ -764,7 +764,7 @@
   w1->SetBounds(gfx::Rect(
       gfx::Point(bounds_in_portrait.x() - 100, bounds_in_portrait.y() - 100),
       bounds_in_portrait.size()));
-  window_state->set_bounds_changed_by_user(true);
+  window_state->SetBoundsChangedByUser(true);
   bounds_in_portrait = w1->GetBoundsInScreen();
   EXPECT_FALSE(window_state->persistent_window_info_of_screen_rotation());
 
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index 3adfecd..8f82d3d 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -66,6 +66,7 @@
 #include "ash/wm/overview/scoped_overview_transform_window.h"
 #include "ash/wm/resize_shadow.h"
 #include "ash/wm/resize_shadow_controller.h"
+#include "ash/wm/splitview/split_view_constants.h"
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/splitview/split_view_divider.h"
 #include "ash/wm/splitview/split_view_drag_indicators.h"
@@ -6768,21 +6769,61 @@
     return window;
   }
 
-  gfx::Rect GetSplitViewLeftWindowBounds() {
-    return split_view_controller()->GetSnappedWindowBoundsInScreen(
-        SnapPosition::kPrimary, split_view_controller()->primary_window(),
-        chromeos::kDefaultSnapRatio);
-  }
+  // Returns the expected overview bounds including the hotseat inset. See
+  // `ShrinkBoundsByHotseatInset()`.
+  // TODO(sophiewen): Refactor this for both `SplitViewOverviewSessionTest`
+  // and `FasterSplitScreenSetupTest` and make this work for multi-display.
+  gfx::Rect GetExpectedOverviewBounds() {
+    aura::Window* root_window = Shell::GetPrimaryRootWindow();
+    gfx::Rect overview_bounds(GetWorkAreaInScreen(root_window));
 
-  gfx::Rect GetSplitViewRightWindowBounds() {
-    return split_view_controller()->GetSnappedWindowBoundsInScreen(
-        SnapPosition::kSecondary, split_view_controller()->secondary_window(),
-        chromeos::kDefaultSnapRatio);
+    if (auto* split_view_drag_indicators =
+            GetOverviewGridForRoot(root_window)->split_view_drag_indicators();
+        split_view_drag_indicators) {
+      // If we are dragging to snap, `SplitViewOverviewSession` is not active
+      // yet, but the overview grid bounds are split.
+      gfx::Rect left_bounds, right_bounds;
+      overview_bounds.SplitVertically(&left_bounds, &right_bounds);
+      // If we are dragging to snap in tablet mode, `split_view_divider` hasn't
+      // been created yet, but we still need to subtract the divider width.
+      const int divider_width = display::Screen::GetScreen()->InTabletMode()
+                                    ? kSplitviewDividerShortSideLength / 2
+                                    : 0;
+      switch (split_view_drag_indicators->current_window_dragging_state()) {
+        case SplitViewDragIndicators::WindowDraggingState::kToSnapPrimary:
+          // If we are dragging to snap left, the grid bounds are on the right.
+          right_bounds.set_x(right_bounds.x() + divider_width);
+          right_bounds.set_width(right_bounds.width() - divider_width);
+          return right_bounds;
+        case SplitViewDragIndicators::WindowDraggingState::kToSnapSecondary:
+          // If we are dragging to snap right, the grid bounds are on the left.
+          left_bounds.set_width(left_bounds.width() - divider_width);
+          return left_bounds;
+        case SplitViewDragIndicators::WindowDraggingState::kNoDrag:
+          break;
+        case SplitViewDragIndicators::WindowDraggingState::kOtherDisplay:
+        case SplitViewDragIndicators::WindowDraggingState::kFromOverview:
+        case SplitViewDragIndicators::WindowDraggingState::kFromTop:
+        case SplitViewDragIndicators::WindowDraggingState::kFromShelf:
+        case SplitViewDragIndicators::WindowDraggingState::kFromFloat:
+          NOTREACHED_NORETURN();
+      }
+    }
+
+    auto* split_view_overview_session =
+        RootWindowController::ForWindow(root_window)
+            ->split_view_overview_session();
+    CHECK(split_view_overview_session);
+    aura::Window* window = split_view_overview_session->window();
+    overview_bounds.Subtract(window->GetBoundsInScreen());
+    overview_bounds.Subtract(GetSplitViewDividerBounds(/*is_dragging=*/false));
+    return overview_bounds;
   }
 
   gfx::Rect GetSplitViewDividerBounds(bool is_dragging) {
-    if (!split_view_controller()->InSplitViewMode())
+    if (!split_view_controller()->InTabletSplitViewMode()) {
       return gfx::Rect();
+    }
     return split_view_controller()
         ->split_view_divider_->GetDividerBoundsInScreen(is_dragging);
   }
@@ -6986,7 +7027,7 @@
   EXPECT_EQ(SplitViewController::State::kNoSnap,
             split_view_controller()->state());
   EXPECT_TRUE(split_view_controller()->primary_window() == nullptr);
-  EXPECT_EQ(ShrinkBoundsByHotseatInset(GetSplitViewRightWindowBounds()),
+  EXPECT_EQ(ShrinkBoundsByHotseatInset(GetExpectedOverviewBounds()),
             GetGridBounds());
 
   // Verify that when dragged to the right, the window grid is located where the
@@ -6996,7 +7037,7 @@
   EXPECT_EQ(SplitViewController::State::kNoSnap,
             split_view_controller()->state());
   EXPECT_TRUE(split_view_controller()->secondary_window() == nullptr);
-  EXPECT_EQ(ShrinkBoundsByHotseatInset(GetSplitViewLeftWindowBounds()),
+  EXPECT_EQ(ShrinkBoundsByHotseatInset(GetExpectedOverviewBounds()),
             GetGridBounds());
 
   // Verify that when dragged to the center, the window grid is has the
@@ -7024,14 +7065,13 @@
   // Verify that when there is a snapped window, the window grid bounds remain
   // constant despite overview items being dragged left and right.
   GetOverviewSession()->Drag(overview_item, left);
-  EXPECT_EQ(ShrinkBoundsByHotseatInset(GetSplitViewRightWindowBounds()),
-            GetGridBounds());
+  const gfx::Rect expected_grid_bounds(
+      ShrinkBoundsByHotseatInset(GetExpectedOverviewBounds()));
+  EXPECT_EQ(expected_grid_bounds, GetGridBounds());
   GetOverviewSession()->Drag(overview_item, right);
-  EXPECT_EQ(ShrinkBoundsByHotseatInset(GetSplitViewRightWindowBounds()),
-            GetGridBounds());
+  EXPECT_EQ(expected_grid_bounds, GetGridBounds());
   GetOverviewSession()->Drag(overview_item, center);
-  EXPECT_EQ(ShrinkBoundsByHotseatInset(GetSplitViewRightWindowBounds()),
-            GetGridBounds());
+  EXPECT_EQ(expected_grid_bounds, GetGridBounds());
 }
 
 // Tests dragging a unsnappable window.
@@ -8937,7 +8977,7 @@
   EXPECT_TRUE(GetOverviewController()->InOverviewSession());
   EXPECT_TRUE(split_view_controller()->InSplitViewMode());
   EXPECT_NE(GetGridBounds(), overview_bounds);
-  EXPECT_EQ(GetGridBounds(), GetSplitViewRightWindowBounds());
+  EXPECT_EQ(GetGridBounds(), GetExpectedOverviewBounds());
   window1.reset();
   EXPECT_TRUE(GetOverviewController()->InOverviewSession());
   EXPECT_FALSE(split_view_controller()->InSplitViewMode());
@@ -9016,7 +9056,7 @@
   auto* overview_item1 = GetOverviewItemForWindow(window1.get());
   DragWindowTo(overview_item1, gfx::PointF(0, 0));
   EXPECT_NE(GetGridBounds(), overview_full_bounds);
-  EXPECT_EQ(GetGridBounds(), GetSplitViewRightWindowBounds());
+  EXPECT_EQ(GetGridBounds(), GetExpectedOverviewBounds());
   gfx::Rect overview_snapped_bounds = GetGridBounds();
 
   // Resize that happens on the right edge of the left snapped window will
@@ -9037,7 +9077,7 @@
   EXPECT_TRUE(split_view_controller()->InSplitViewMode());
   EXPECT_NE(GetGridBounds(), overview_full_bounds);
   EXPECT_NE(GetGridBounds(), overview_snapped_bounds);
-  EXPECT_EQ(GetGridBounds(), GetSplitViewRightWindowBounds());
+  EXPECT_EQ(GetGridBounds(), GetExpectedOverviewBounds());
   EXPECT_TRUE(RootWindowController::ForWindow(Shell::GetPrimaryRootWindow())
                   ->split_view_overview_session());
 
@@ -9092,7 +9132,7 @@
   overview_item2 = GetOverviewItemForWindow(window2.get());
   DragWindowTo(overview_item2, gfx::PointF(599, 0));
   EXPECT_NE(GetGridBounds(), overview_full_bounds);
-  EXPECT_EQ(GetGridBounds(), GetSplitViewLeftWindowBounds());
+  EXPECT_EQ(GetGridBounds(), GetExpectedOverviewBounds());
   overview_snapped_bounds = GetGridBounds();
 
   ui::test::EventGenerator generator5(Shell::GetPrimaryRootWindow(),
@@ -9110,7 +9150,7 @@
   EXPECT_TRUE(split_view_controller()->InSplitViewMode());
   EXPECT_NE(GetGridBounds(), overview_full_bounds);
   EXPECT_NE(GetGridBounds(), overview_snapped_bounds);
-  EXPECT_EQ(GetGridBounds(), GetSplitViewLeftWindowBounds());
+  EXPECT_EQ(GetGridBounds(), GetExpectedOverviewBounds());
   EXPECT_EQ(overview_snapped_bounds.width() + 50, GetGridBounds().width());
   EXPECT_EQ(work_area.width(),
             GetGridBounds().width() + window2->GetBoundsInScreen().width());
diff --git a/ash/wm/pip/pip_double_tap_handler.cc b/ash/wm/pip/pip_double_tap_handler.cc
index 99f9894..3abc006 100644
--- a/ash/wm/pip/pip_double_tap_handler.cc
+++ b/ash/wm/pip/pip_double_tap_handler.cc
@@ -104,7 +104,7 @@
 
   gfx::Rect bounds = window_state->window()->bounds();
 
-  window_state->set_bounds_changed_by_user(true);
+  window_state->SetBoundsChangedByUser(true);
 
   gfx::Size calculated_max_size = GetMaxSize(window_state);
   // If the window is not at max size, we will expand it.
diff --git a/ash/wm/pip/pip_window_resizer.cc b/ash/wm/pip/pip_window_resizer.cc
index 4393f1a..994bc1d 100644
--- a/ash/wm/pip/pip_window_resizer.cc
+++ b/ash/wm/pip/pip_window_resizer.cc
@@ -324,7 +324,7 @@
       last_location_in_screen_.value_or(gfx::PointF()));
 
   window_state()->ClearRestoreBounds();
-  window_state()->set_bounds_changed_by_user(moved_or_resized_);
+  window_state()->SetBoundsChangedByUser(moved_or_resized_);
 
   int fling_amount = fling_velocity_x_ * fling_velocity_x_ +
                      fling_velocity_y_ * fling_velocity_y_;
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index 070a12a..871c131 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -140,6 +140,10 @@
 // for the purpose of metric collection.
 base::Time g_multi_display_split_view_start_time;
 
+bool InTabletMode() {
+  return display::Screen::GetScreen()->InTabletMode();
+}
+
 bool IsExactlyOneRootInSplitView() {
   const aura::Window::Windows all_root_windows = Shell::GetAllRootWindows();
   return 1 ==
@@ -493,9 +497,6 @@
   if (SnapGroupController* snap_group_controller = SnapGroupController::Get()) {
     snap_group_controller->AddObserver(this);
   }
-  split_view_type_ = display::Screen::GetScreen()->InTabletMode()
-                         ? SplitViewType::kTabletType
-                         : SplitViewType::kClamshellType;
 }
 
 SplitViewController::~SplitViewController() {
@@ -525,11 +526,11 @@
 }
 
 bool SplitViewController::InClamshellSplitViewMode() const {
-  return InSplitViewMode() && split_view_type_ == SplitViewType::kClamshellType;
+  return InSplitViewMode() && !InTabletMode();
 }
 
 bool SplitViewController::InTabletSplitViewMode() const {
-  return InSplitViewMode() && split_view_type_ == SplitViewType::kTabletType;
+  return InSplitViewMode() && InTabletMode();
 }
 
 bool SplitViewController::CanSnapWindow(aura::Window* window) const {
@@ -614,8 +615,7 @@
   // Note that at this point `state_` may not have been updated yet, so check if
   // only one of `primary_window_` or `secondary_window_` are snapped.
   return !IsInOverviewSession() && !DesksController::Get()->animation() &&
-         split_view_type_ == SplitViewType::kTabletType &&
-         !!primary_window_ != !!secondary_window_;
+         InTabletMode() && !!primary_window_ != !!secondary_window_;
 }
 
 void SplitViewController::SnapWindow(aura::Window* window,
@@ -674,7 +674,7 @@
   // `IsSnapGroupEnabledInClamshellMode`, the window should be managed by
   // `SplitViewController`. Otherwise, the window should be snapped normally and
   // should be managed by `WindowState`.
-  if (split_view_type_ == SplitViewType::kClamshellType &&
+  if (!InTabletMode() &&
       !(in_overview || IsSnapGroupEnabledInClamshellMode())) {
     return;
   }
@@ -749,8 +749,7 @@
           std::make_unique<AutoSnapController>(root_window_);
     }
 
-    if (!display::Screen::GetScreen()->InTabletMode() &&
-        IsInOverviewSession()) {
+    if (!InTabletMode() && IsInOverviewSession()) {
       if (auto* root_window_controller =
               RootWindowController::ForWindow(window)) {
         // Start the clamshell split overview session. It is too late to create
@@ -823,7 +822,7 @@
         ->OnOverviewItemDragEnded(/*snap=*/true);
   }
 
-  if (split_view_type_ == SplitViewType::kTabletType && !split_view_divider_) {
+  if (InTabletMode() && !split_view_divider_) {
     // `split_view_divider_` must be created after we start observing windows.
     split_view_divider_ =
         std::make_unique<SplitViewDivider>(this, divider_position_);
@@ -950,24 +949,16 @@
     return bounds;
   }
 
-  if (!window_for_minimum_size) {
-    // Some clients may request for a null `window_for_minimum_size`, e.g.
-    // overview or backdrop controller. Use `root_window` for the work area.
-    // TODO(sophiewen): Consider moving this to a separate function.
-    window_for_minimum_size = root_window_;
-  }
-
   const int divider_position =
       divider_position_ < 0
           ? CalculateDividerPosition(snap_position, snap_ratio)
           : divider_position_;
-  const int divider_width =
-      display::Screen::GetScreen()->InTabletMode() || split_view_divider_
-          ? kSplitviewDividerShortSideLength
-          : 0;
+  const int divider_width = InTabletMode() || split_view_divider_
+                                ? kSplitviewDividerShortSideLength
+                                : 0;
   return CalculateSnappedWindowBoundsInScreen(
-      snap_position, window_for_minimum_size, divider_position, divider_width,
-      IsResizingWithDivider());
+      snap_position, root_window_, window_for_minimum_size, divider_position,
+      divider_width, IsResizingWithDivider());
 }
 
 int SplitViewController::CalculateDividerPosition(SnapPosition snap_position,
@@ -980,7 +971,7 @@
   int next_divider_position = snap_position == SnapPosition::kPrimary
                                   ? snap_width
                                   : divider_upper_limit - snap_width;
-  if (split_view_divider_ || split_view_type_ == SplitViewType::kTabletType) {
+  if (split_view_divider_ || InTabletMode()) {
     // The divider may be visible in tablet mode, or between two windows in a
     // snap group in clamshell mode.
     // In tablet mode, we always consider the divider width even if
@@ -1276,8 +1267,7 @@
     // If window|is currently minimized then it will undergo the unminimizing
     // animation instead, therefore skip the divider spawn animation if
     // the window is minimized.
-    if (state_ == State::kNoSnap &&
-        split_view_type_ == SplitViewType::kTabletType &&
+    if (state_ == State::kNoSnap && InTabletMode() &&
         old_type != WindowStateType::kMinimized &&
         !window->transform().IsIdentity()) {
       // For the divider spawn animation, at the end of the delay, the divider
@@ -1312,7 +1302,7 @@
       // the window is not supposed to be minmized in tablet mode. And in
       // clamshell splitview mode, we respect the minimization of the window
       // and end overview instead.
-      if (split_view_type_ == SplitViewType::kTabletType) {
+      if (InTabletMode()) {
         InsertWindowToOverview(window);
       } else {
         Shell::Get()->overview_controller()->EndOverview(
@@ -1363,8 +1353,9 @@
   // If clamshell split view mode is active, bail out. `OnOverviewModeEnded`
   // will end split view. We do not end split view here, because that would mess
   // up histograms of overview exit animation smoothness.
-  if (split_view_type_ == SplitViewType::kClamshellType)
+  if (!InTabletMode()) {
     return;
+  }
 
   // Tablet split view mode is active. If it still only has one snapped window,
   // snap the first snappable window in the overview grid on the other side.
@@ -1431,8 +1422,7 @@
 
 void SplitViewController::OnOverviewModeEnded() {
   DCHECK(InSplitViewMode());
-  if (split_view_type_ == SplitViewType::kClamshellType &&
-      !IsSnapGroupEnabledInClamshellMode()) {
+  if (!InTabletMode() && !IsSnapGroupEnabledInClamshellMode()) {
     EndSplitView();
   }
 }
@@ -1494,8 +1484,9 @@
 
   // In clamshell split view mode, the divider position will be adjusted in
   // |OnWindowBoundsChanged|.
-  if (split_view_type_ == SplitViewType::kClamshellType)
+  if (!InTabletMode()) {
     return;
+  }
 
   // Before adjusting the divider position for the new display metrics, if the
   // divider is animating to a snap position, then stop it and shove it there.
@@ -1540,7 +1531,6 @@
       OnTabletModeEnded();
       break;
     case display::TabletState::kEnteringTabletMode:
-      OnTabletModeStarting();
       break;
     case display::TabletState::kInTabletMode:
       OnTabletModeStarted();
@@ -1954,7 +1944,7 @@
 void SplitViewController::UpdateSnappedBounds(aura::Window* window) {
   DCHECK(IsWindowInSplitView(window));
   WindowState* window_state = WindowState::Get(window);
-  if (display::Screen::GetScreen()->InTabletMode()) {
+  if (InTabletMode()) {
     if (window->GetProperty(aura::client::kAppType) ==
         static_cast<int>(AppType::ARC_APP)) {
       // TODO(b/264962634): Remove this workaround. Probably, we can rewrite
@@ -2152,8 +2142,7 @@
   // but instead snap that window to the opposite side.
   if (previous_state &&
       *previous_state == chromeos::WindowStateType::kFloated &&
-      display::Screen::GetScreen()->InTabletMode() &&
-      state_ != State::kBothSnapped) {
+      InTabletMode() && state_ != State::kBothSnapped) {
     for (aura::Window* mru_window :
          Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(
              kActiveDesk)) {
@@ -2248,8 +2237,7 @@
                              ? SnapPosition::kSecondary
                              : SnapPosition::kPrimary);
 
-    if (reason == WindowDetachedReason::kWindowFloated &&
-        display::Screen::GetScreen()->InTabletMode()) {
+    if (reason == WindowDetachedReason::kWindowFloated && InTabletMode()) {
       // Maximize the other window, which will end split view.
       WMEvent event(WM_EVENT_MAXIMIZE);
       WindowState::Get(other_window)->OnWMEvent(&event);
@@ -2611,10 +2599,6 @@
   }
 }
 
-void SplitViewController::OnTabletModeStarting() {
-  split_view_type_ = SplitViewType::kTabletType;
-}
-
 void SplitViewController::OnTabletModeStarted() {
   is_previous_layout_right_side_up_ = IsCurrentScreenOrientationPrimary();
   // If splitview is active when tablet mode is starting, create the split view
@@ -2633,8 +2617,6 @@
 }
 
 void SplitViewController::OnTabletModeEnding() {
-  split_view_type_ = SplitViewType::kClamshellType;
-
   // `OnTabletModeEnding()` can also be called during test teardown.
   const bool is_divider_animating = IsDividerAnimating();
   if (IsResizingWithDivider() || is_divider_animating) {
diff --git a/ash/wm/splitview/split_view_controller.h b/ash/wm/splitview/split_view_controller.h
index 357ad69..595dd32 100644
--- a/ash/wm/splitview/split_view_controller.h
+++ b/ash/wm/splitview/split_view_controller.h
@@ -581,7 +581,6 @@
                               const gfx::Point& event_location);
 
   // Called when the display tablet state is changed.
-  void OnTabletModeStarting();
   void OnTabletModeStarted();
   void OnTabletModeEnding();
   void OnTabletModeEnded();
@@ -676,10 +675,6 @@
   std::optional<OverviewStartAction> overview_start_action_;
   std::optional<OverviewEnterExitType> enter_exit_overview_type_;
 
-  // The split view type. See SplitViewType for the differences between tablet
-  // split view and clamshell split view.
-  SplitViewType split_view_type_ = SplitViewType::kTabletType;
-
   // The time when splitview starts. Used for metric collection purpose.
   base::Time splitview_start_time_;
 
diff --git a/ash/wm/splitview/split_view_drag_indicators.cc b/ash/wm/splitview/split_view_drag_indicators.cc
index 83083cb..138e40b 100644
--- a/ash/wm/splitview/split_view_drag_indicators.cc
+++ b/ash/wm/splitview/split_view_drag_indicators.cc
@@ -260,7 +260,12 @@
       dragged_window_->RemoveObserver(this);
   }
 
-  SplitViewHighlightView* left_highlight_view() { return left_highlight_view_; }
+  const SplitViewHighlightView* left_highlight_view() const {
+    return left_highlight_view_;
+  }
+  const SplitViewHighlightView* right_highlight_view() const {
+    return right_highlight_view_;
+  }
 
   // Called by parent widget when the state machine changes. Handles setting the
   // opacity and bounds of the highlights and labels.
@@ -676,14 +681,19 @@
   widget_->SetBounds(GetWorkAreaBoundsNoOverlapWithShelf(root_window));
 }
 
+gfx::Rect SplitViewDragIndicators::GetLeftHighlightViewBounds() const {
+  return indicators_view_->left_highlight_view()->bounds();
+}
+
+gfx::Rect SplitViewDragIndicators::GetRightHighlightViewBoundsForTesting()
+    const {
+  return indicators_view_->right_highlight_view()->bounds();
+}
+
 bool SplitViewDragIndicators::GetIndicatorTypeVisibilityForTesting(
     IndicatorType type) const {
   return indicators_view_->GetViewForIndicatorType(type)->layer()->opacity() >
          0.f;
 }
 
-gfx::Rect SplitViewDragIndicators::GetLeftHighlightViewBounds() const {
-  return indicators_view_->left_highlight_view()->bounds();
-}
-
 }  // namespace ash
diff --git a/ash/wm/splitview/split_view_drag_indicators.h b/ash/wm/splitview/split_view_drag_indicators.h
index ce69e21d1..04fba2e 100644
--- a/ash/wm/splitview/split_view_drag_indicators.h
+++ b/ash/wm/splitview/split_view_drag_indicators.h
@@ -120,9 +120,11 @@
   void SetDraggedWindow(aura::Window* dragged_window);
   void SetWindowDraggingState(WindowDraggingState window_dragging_state);
   void OnDisplayBoundsChanged();
-  bool GetIndicatorTypeVisibilityForTesting(IndicatorType type) const;
   gfx::Rect GetLeftHighlightViewBounds() const;
 
+  gfx::Rect GetRightHighlightViewBoundsForTesting() const;
+  bool GetIndicatorTypeVisibilityForTesting(IndicatorType type) const;
+
  private:
   FRIEND_TEST_ALL_PREFIXES(SplitViewDragIndicatorsTest,
                            SplitViewDragIndicatorsWidgetReparenting);
diff --git a/ash/wm/splitview/split_view_drag_indicators_unittest.cc b/ash/wm/splitview/split_view_drag_indicators_unittest.cc
index 5d24523a..94e67ddc 100644
--- a/ash/wm/splitview/split_view_drag_indicators_unittest.cc
+++ b/ash/wm/splitview/split_view_drag_indicators_unittest.cc
@@ -16,9 +16,11 @@
 #include "ash/wm/splitview/split_view_constants.h"
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "ash/wm/work_area_insets.h"
 #include "base/command_line.h"
 #include "base/memory/raw_ptr.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "split_view_constants.h"
 #include "split_view_drag_indicators.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/env.h"
@@ -557,4 +559,46 @@
       portrait_display.work_area().width() - 2 * kHighlightScreenEdgePaddingDp);
 }
 
+// Test that when we drag in overview clamshell to another display, the
+// splitview indicators are the expected size. Regression test for
+// http://b/316043785.
+TEST_F(ClamshellMultiDisplaySplitViewDragIndicatorsTest, IndicatorSize) {
+  // The displays need to be different widths for the bug to repro.
+  UpdateDisplay("800x600,800+0-1000x600");
+
+  std::unique_ptr<aura::Window> window = CreateAppWindow(gfx::Rect(300, 300));
+
+  ToggleOverview();
+
+  // Start dragging the overview item on the primary display.
+  auto* item = GetOverviewItemForWindow(window.get());
+  gfx::PointF start_location(item->target_bounds().CenterPoint());
+  overview_session_->InitiateDrag(item, start_location,
+                                  /*is_touch_dragging=*/false,
+                                  /*event_source_item=*/item);
+
+  // Drag the overview item to the secondary display right edge, so the right
+  // side preview indicator shows up.
+  // TODO(crbug.com/990589): Unit tests should be able to simulate mouse input
+  // without having to call `CursorManager::SetDisplay()`.
+  aura::Window* secondary_root = Shell::GetAllRootWindows()[1];
+  Shell::Get()->cursor_manager()->SetDisplay(
+      display::Screen::GetScreen()->GetDisplayNearestWindow(secondary_root));
+  overview_session_->Drag(item, gfx::PointF(1780.f, 500.f));
+
+  // Verify the size of the right side indicator. It should be roughly half of
+  // the secondary display.
+  auto* secondary_display_indicators =
+      overview_session_->GetGridWithRootWindow(secondary_root)
+          ->split_view_drag_indicators();
+  gfx::Rect expected_bounds_in_parent(500, 0, 500,
+                                      WorkAreaInsets::ForWindow(secondary_root)
+                                          ->user_work_area_bounds()
+                                          .height());
+  expected_bounds_in_parent.Inset(kHighlightScreenEdgePaddingDp);
+  EXPECT_EQ(
+      expected_bounds_in_parent,
+      secondary_display_indicators->GetRightHighlightViewBoundsForTesting());
+}
+
 }  // namespace ash
diff --git a/ash/wm/splitview/split_view_overview_session.h b/ash/wm/splitview/split_view_overview_session.h
index 4cf89cb..2d72a7b 100644
--- a/ash/wm/splitview/split_view_overview_session.h
+++ b/ash/wm/splitview/split_view_overview_session.h
@@ -75,7 +75,7 @@
   void RecordSplitViewOverviewSessionExitPointMetrics(
       SplitViewOverviewSessionExitPoint user_action);
 
-  const aura::Window* window() const { return window_; }
+  aura::Window* window() { return window_; }
   SplitViewOverviewSetupType setup_type() const { return setup_type_; }
   chromeos::WindowStateType GetWindowStateType() const;
   AutoSnapController* auto_snap_controller() {
diff --git a/ash/wm/splitview/split_view_utils.cc b/ash/wm/splitview/split_view_utils.cc
index 56d140b8..bcc997bb 100644
--- a/ash/wm/splitview/split_view_utils.cc
+++ b/ash/wm/splitview/split_view_utils.cc
@@ -629,11 +629,11 @@
 
 gfx::Rect CalculateSnappedWindowBoundsInScreen(
     SnapPosition snap_position,
+    aura::Window* root_window,
     aura::Window* window_for_minimum_size,
     int divider_position,
     int divider_width,
     bool is_resizing_with_divider) {
-  aura::Window* root_window = window_for_minimum_size->GetRootWindow();
   const gfx::Rect work_area_bounds_in_screen =
       screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           root_window);
diff --git a/ash/wm/splitview/split_view_utils.h b/ash/wm/splitview/split_view_utils.h
index e68f09c..20f4131 100644
--- a/ash/wm/splitview/split_view_utils.h
+++ b/ash/wm/splitview/split_view_utils.h
@@ -174,8 +174,6 @@
                 int horizontal_edge_inset,
                 int vertical_edge_inset);
 
-bool IsInTabletMode();
-
 // The return values of these two functions together indicate what actual
 // positions correspond to |PRIMARY| and |SECONDARY|:
 // |IsLayoutHorizontal|  |IsLayoutPrimary|    |PRIMARY|           |SECONDARY|
@@ -216,6 +214,7 @@
 // bounds based on the window's minimum size.
 gfx::Rect CalculateSnappedWindowBoundsInScreen(
     SnapPosition snap_position,
+    aura::Window* root_window,
     aura::Window* window_for_minimum_size,
     int divider_position,
     int divider_width,
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.cc b/ash/wm/tablet_mode/tablet_mode_window_state.cc
index 67e7f3b..a704f74 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_state.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_state.cc
@@ -632,7 +632,7 @@
     return;
   }
 
-  window_state->set_bounds_changed_by_user(true);
+  window_state->SetBoundsChangedByUser(true);
   chromeos::WindowStateType new_state_type =
       snap_event_type == WM_EVENT_SNAP_PRIMARY
           ? WindowStateType::kPrimarySnapped
diff --git a/ash/wm/window_positioner.cc b/ash/wm/window_positioner.cc
index 16b51d23..c8b233c 100644
--- a/ash/wm/window_positioner.cc
+++ b/ash/wm/window_positioner.cc
@@ -324,9 +324,9 @@
   if (single_window) {
     // When going from one to two windows both windows loose their
     // "positioned by user" flags.
-    added_window_state->set_bounds_changed_by_user(false);
+    added_window_state->SetBoundsChangedByUser(false);
     WindowState* other_window_state = WindowState::Get(other_shown_window);
-    other_window_state->set_bounds_changed_by_user(false);
+    other_window_state->SetBoundsChangedByUser(false);
 
     if (WindowPositionCanBeManaged(other_shown_window)) {
       // Don't override pre auto managed bounds as the current bounds
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index 03f9ebc..f61df77 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -706,7 +706,7 @@
   return window_->GetProperty(kImmersiveIsActive);
 }
 
-void WindowState::set_bounds_changed_by_user(bool bounds_changed_by_user) {
+void WindowState::SetBoundsChangedByUser(bool bounds_changed_by_user) {
   bounds_changed_by_user_ = bounds_changed_by_user;
   if (bounds_changed_by_user) {
     pre_auto_manage_window_bounds_.reset();
diff --git a/ash/wm/window_state.h b/ash/wm/window_state.h
index f9e87680..9784f84 100644
--- a/ash/wm/window_state.h
+++ b/ash/wm/window_state.h
@@ -199,7 +199,6 @@
 
   // Whether or not the window's position or size was changed by a user.
   bool bounds_changed_by_user() const { return bounds_changed_by_user_; }
-  void set_bounds_changed_by_user(bool bounds_changed_by_user);
 
   // True if the window should not adjust the window's bounds when
   // virtual keyboard bounds changes.
@@ -401,6 +400,10 @@
   // also allow the shelf to be shown in some situations.
   bool IsInImmersiveFullscreen() const;
 
+  // Sets `bounds_changed_by_user_` to the given value and resets the
+  // corresponding variables.
+  void SetBoundsChangedByUser(bool bounds_changed_by_user);
+
   // Creates and takes ownership of a pointer to DragDetails when resizing is
   // active. This should be done before a resizer gets created.
   void CreateDragDetails(const gfx::PointF& point_in_parent,
diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc
index 7f79a5e..c42a2d4 100644
--- a/ash/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc
@@ -490,7 +490,7 @@
   parent->RemoveChild(out_window.get());
   out_window->SetBounds(gfx::Rect(-200, -200, 200, 200));
   // UserHasChangedWindowPositionOrSize flag shouldn't turn off this behavior.
-  WindowState::Get(window.get())->set_bounds_changed_by_user(true);
+  WindowState::Get(window.get())->SetBoundsChangedByUser(true);
   parent->AddChild(out_window.get());
   EXPECT_GT(bounds.width(), out_window->bounds().width() * 0.29);
   EXPECT_GT(bounds.height(), out_window->bounds().height() * 0.29);
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc
index c1aed6e8..cd6f1f6 100644
--- a/ash/wm/workspace/workspace_window_resizer.cc
+++ b/ash/wm/workspace/workspace_window_resizer.cc
@@ -836,7 +836,7 @@
   }
 
   ResetFrameRestoreLookKey(window_state());
-  window_state()->set_bounds_changed_by_user(true);
+  window_state()->SetBoundsChangedByUser(true);
   snap_phantom_window_controller_.reset();
 
   // If the window's state type changed over the course of the drag do not snap
@@ -965,7 +965,7 @@
 
   window_state()->OnRevertDrag(last_location_in_screen_);
   EndDragForAttachedWindows(/*revert_drag=*/true);
-  window_state()->set_bounds_changed_by_user(initial_bounds_changed_by_user_);
+  window_state()->SetBoundsChangedByUser(initial_bounds_changed_by_user_);
   snap_phantom_window_controller_.reset();
 
   if (!did_move_or_resize_) {
diff --git a/ash/wm/workspace_controller_unittest.cc b/ash/wm/workspace_controller_unittest.cc
index 5661d9e..3d234ee 100644
--- a/ash/wm/workspace_controller_unittest.cc
+++ b/ash/wm/workspace_controller_unittest.cc
@@ -841,7 +841,7 @@
 
   // Test4: A single manageable window should get centered.
   window4.reset();
-  window1_state->set_bounds_changed_by_user(false);
+  window1_state->SetBoundsChangedByUser(false);
   // Trigger the auto window placement function by showing (and hiding) it.
   window1->Hide();
   window1->Show();
@@ -871,7 +871,7 @@
 
   // Check that the current location gets preserved if the user has
   // positioned it previously.
-  window1_state->set_bounds_changed_by_user(true);
+  window1_state->SetBoundsChangedByUser(true);
   window1->Show();
   EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
   // Flag should be still set.
@@ -893,7 +893,7 @@
   EXPECT_FALSE(window2_state->bounds_changed_by_user());
 
   // Going back to one shown window should keep the state.
-  window1_state->set_bounds_changed_by_user(true);
+  window1_state->SetBoundsChangedByUser(true);
   window2->Hide();
   EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
   EXPECT_TRUE(window1_state->bounds_changed_by_user());
@@ -1023,7 +1023,7 @@
   resizer->Drag(location, 0);
   resizer->CompleteDrag();
 
-  window1_state->set_bounds_changed_by_user(true);
+  window1_state->SetBoundsChangedByUser(true);
   window1->SetBounds(gfx::Rect(100, 0, 100, 100));
 
   window2->Show();
diff --git a/base/functional/bind_internal.h b/base/functional/bind_internal.h
index fa7a98c..5c54708 100644
--- a/base/functional/bind_internal.h
+++ b/base/functional/bind_internal.h
@@ -984,37 +984,6 @@
   return false;
 }
 
-// Used by QueryCancellationTraits below.
-template <typename Functor, typename BoundArgsTuple, size_t... indices>
-bool QueryCancellationTraitsImpl(BindStateBase::CancellationQueryMode mode,
-                                 const Functor& functor,
-                                 const BoundArgsTuple& bound_args,
-                                 std::index_sequence<indices...>) {
-  switch (mode) {
-    case BindStateBase::IS_CANCELLED:
-      return CallbackCancellationTraits<Functor, BoundArgsTuple>::IsCancelled(
-          functor, std::get<indices>(bound_args)...);
-    case BindStateBase::MAYBE_VALID:
-      return CallbackCancellationTraits<Functor, BoundArgsTuple>::MaybeValid(
-          functor, std::get<indices>(bound_args)...);
-  }
-  NOTREACHED();
-  return false;
-}
-
-// Relays |base| to corresponding CallbackCancellationTraits<>::Run(). Returns
-// true if the callback |base| represents is canceled.
-template <typename BindStateType>
-bool QueryCancellationTraits(const BindStateBase* base,
-                             BindStateBase::CancellationQueryMode mode) {
-  const BindStateType* storage = static_cast<const BindStateType*>(base);
-  static constexpr size_t num_bound_args =
-      std::tuple_size_v<decltype(storage->bound_args_)>;
-  return QueryCancellationTraitsImpl(
-      mode, storage->functor_, storage->bound_args_,
-      std::make_index_sequence<num_bound_args>());
-}
-
 // The base case of BanUnconstructedRefCountedReceiver that checks nothing.
 template <typename Functor, typename... Unused>
 void BanUnconstructedRefCountedReceiver(Unused&&...) {}
@@ -1063,9 +1032,11 @@
 // This stores all the state passed into Bind().
 template <typename Functor, typename... BoundArgs>
 struct BindState final : BindStateBase {
-  using IsCancellable = std::bool_constant<
-      CallbackCancellationTraits<Functor,
-                                 std::tuple<BoundArgs...>>::is_cancellable>;
+ private:
+  using BoundArgsTuple = std::tuple<BoundArgs...>;
+  using Traits = CallbackCancellationTraits<Functor, BoundArgsTuple>;
+
+ public:
   template <typename ForwardFunctor, typename... ForwardBoundArgs>
   static BindState* Create(BindStateBase::InvokeFuncStorage invoke_func,
                            ForwardFunctor&& functor,
@@ -1074,16 +1045,13 @@
     // a common pattern of racy situation.
     BanUnconstructedRefCountedReceiver<ForwardFunctor>(bound_args...);
 
-    // IsCancellable is std::false_type if
-    // CallbackCancellationTraits<>::IsCancelled returns always false.
-    // Otherwise, it's std::true_type.
-    return new BindState(IsCancellable{}, invoke_func,
-                         std::forward<ForwardFunctor>(functor),
+    return new BindState(std::bool_constant<Traits::is_cancellable>(),
+                         invoke_func, std::forward<ForwardFunctor>(functor),
                          std::forward<ForwardBoundArgs>(bound_args)...);
   }
 
   Functor functor_;
-  std::tuple<BoundArgs...> bound_args_;
+  BoundArgsTuple bound_args_;
 
  private:
   static constexpr bool kIsNestedCallback =
@@ -1094,9 +1062,7 @@
                      BindStateBase::InvokeFuncStorage invoke_func,
                      ForwardFunctor&& functor,
                      ForwardBoundArgs&&... bound_args)
-      : BindStateBase(invoke_func,
-                      &Destroy,
-                      &QueryCancellationTraits<BindState>),
+      : BindStateBase(invoke_func, &Destroy, &QueryCancellationTraits),
         functor_(std::forward<ForwardFunctor>(functor)),
         bound_args_(std::forward<ForwardBoundArgs>(bound_args)...) {
     // We check the validity of nested callbacks (e.g., Bind(callback, ...)) in
@@ -1128,9 +1094,30 @@
 
   ~BindState() = default;
 
+  static bool QueryCancellationTraits(
+      const BindStateBase* base,
+      BindStateBase::CancellationQueryMode mode) {
+    auto* const storage = static_cast<const BindState*>(base);
+    static constexpr std::make_index_sequence<sizeof...(BoundArgs)> kIndices;
+    return (mode == BindStateBase::CancellationQueryMode::kIsCancelled)
+               ? storage->IsCancelled(kIndices)
+               : storage->MaybeValid(kIndices);
+  }
+
   static void Destroy(const BindStateBase* self) {
     delete static_cast<const BindState*>(self);
   }
+
+  // Helpers to do arg tuple expansion.
+  template <size_t... indices>
+  bool IsCancelled(std::index_sequence<indices...>) const {
+    return Traits::IsCancelled(functor_, std::get<indices>(bound_args_)...);
+  }
+
+  template <size_t... indices>
+  bool MaybeValid(std::index_sequence<indices...>) const {
+    return Traits::MaybeValid(functor_, std::get<indices>(bound_args_)...);
+  }
 };
 
 // Used to implement MakeBindStateType. The specializations below cover all
@@ -1404,27 +1391,26 @@
     template <typename FunctorParamType>
     struct ToParamWithType {
       static constexpr bool kRawPtr = IsRawPtrV<FunctorParamType>;
-
+      static constexpr bool kRawPtrMayBeDangling =
+          IsRawPtrMayDangleV<FunctorParamType>;
       static constexpr bool kCanBeForwardedToBoundFunctor =
           std::is_convertible_v<ForwardingType, FunctorParamType>;
 
       // If the bound type can't be forwarded then test if `FunctorParamType` is
       // a non-const lvalue reference and a reference to the unwrapped type
       // *could* have been successfully forwarded.
-      static constexpr bool kNonConstRefParamMustBeWrapped =
-          kCanBeForwardedToBoundFunctor ||
-          !(std::is_lvalue_reference_v<FunctorParamType> &&
-            !std::is_const_v<std::remove_reference_t<FunctorParamType>> &&
-            std::is_convertible_v<std::decay_t<ForwardingType>&,
-                                  FunctorParamType>);
+      static constexpr bool kIsUnwrappedForwardableNonConstReference =
+          std::is_lvalue_reference_v<FunctorParamType> &&
+          !std::is_const_v<std::remove_reference_t<FunctorParamType>> &&
+          std::is_convertible_v<std::decay_t<ForwardingType>&,
+                                FunctorParamType>;
 
       // Note that this intentionally drops the const qualifier from
       // `ForwardingType`, to test if it *could* have been successfully
       // forwarded if `Passed()` had been used.
-      static constexpr bool kMoveOnlyTypeMustUseBasePassed =
-          kCanBeForwardedToBoundFunctor ||
-          !std::is_convertible_v<std::decay_t<ForwardingType>&&,
-                                 FunctorParamType>;
+      static constexpr bool kWouldBeForwardableWithPassed =
+          std::is_convertible_v<std::decay_t<ForwardingType>&&,
+                                FunctorParamType>;
     };
   };
 
@@ -1434,12 +1420,12 @@
     struct StoredAs {
       static constexpr bool kBindArgumentCanBeCaptured =
           std::is_constructible_v<StorageType, BoundAsType>;
+
       // Note that this intentionally drops the const qualifier from
       // `BoundAsType`, to test if it *could* have been successfully bound if
       // `std::move()` had been used.
-      static constexpr bool kMoveOnlyTypeMustUseStdMove =
-          kBindArgumentCanBeCaptured ||
-          !std::is_constructible_v<StorageType, std::decay_t<BoundAsType>&&>;
+      static constexpr bool kWouldBeCapturableWithStdMove =
+          std::constructible_from<StorageType, std::decay_t<BoundAsType>&&>;
     };
   };
 
@@ -1447,42 +1433,12 @@
   struct ToParamWithType {
     template <typename StorageType>
     struct StoredAs {
-      template <bool is_method>
-      // true if we are handling `this` parameter.
-      static constexpr bool kParamIsThisPointer = is_method && i == 0;
-      // true if the current parameter is of type `raw_ptr<T>` with
-      // `RawPtrTraits::kMayDangle` trait (e.g. `MayBeDangling<T>`).
-      static constexpr bool kParamIsDanglingRawPtr =
-          IsRawPtrMayDangleV<FunctionParamType>;
-      // true if the bound parameter is of type
-      // `UnretainedWrapper<T, unretained_traits::MayDangle, PtrTraits>`.
       static constexpr bool kBoundPtrMayDangle =
           IsUnretainedMayDangle<StorageType>;
-      // true if bound parameter of type `UnretainedWrapper` and parameter of
-      // type `raw_ptr` have compatible `RawPtrTraits`.
-      static constexpr bool kMayBeDanglingTraitsCorrectness =
+
+      static constexpr bool kMayDangleAndMayBeDanglingHaveMatchingTraits =
           UnretainedAndRawPtrHaveCompatibleTraits<StorageType,
                                                   FunctionParamType>;
-      // true if the receiver argument **must** be of type `MayBeDangling<T>`.
-      static constexpr bool kMayBeDanglingMustBeUsed =
-          kBoundPtrMayDangle && kParamIsDanglingRawPtr;
-
-      // true iff:
-      // - bound parameter is of type
-      //   `UnretainedWrapper<T, unretained_traits::MayDangle, PtrTraits>`
-      // - the receiving argument is of type `MayBeDangling<T>`
-      template <bool is_method>
-      static constexpr bool kMayBeDanglingPtrPassedCorrectly =
-          kParamIsThisPointer<is_method> ||
-          kBoundPtrMayDangle == kParamIsDanglingRawPtr;
-
-      // true if:
-      // - MayBeDangling<T> must not be used as receiver parameter.
-      // OR
-      // - MayBeDangling<T> must be used as receiver parameter and its traits
-      // are matching Unretained traits.
-      static constexpr bool kUnsafeDanglingAndMayBeDanglingHaveMatchingTraits =
-          !kMayBeDanglingMustBeUsed || kMayBeDanglingTraitsCorrectness;
     };
   };
 };
@@ -1505,11 +1461,8 @@
   using BoundStorage =
       BindArgument<i>::template BoundAs<Arg>::template StoredAs<Storage>;
 
-  // We forbid callbacks from using raw_ptr as a parameter. However, we allow
-  // `MayBeDangling<T>` iff the callback argument was created using
-  // `base::UnsafeDangling`.
   template <bool v = !UnwrappedParam::kRawPtr ||
-                     ParamStorage::kMayBeDanglingMustBeUsed>
+                     UnwrappedParam::kRawPtrMayBeDangling>
   struct NotRawPtr {
     static constexpr bool value = [] {
       static_assert(
@@ -1519,13 +1472,11 @@
     }();
   };
 
-  // A bound functor must take a dangling pointer argument (e.g. bound using the
-  // `UnsafeDangling` helper) as a `MayBeDangling<T>`, to make it clear that the
-  // pointee's lifetime must be externally validated before using it. For
-  // methods, exempt a bound receiver (i.e. the this pointer) as it is not
-  // passed as a regular function argument.
-  template <bool v = ParamStorage::template kMayBeDanglingPtrPassedCorrectly<
-                is_method>>
+  template <bool v = !ParamStorage::kBoundPtrMayDangle ||
+                     UnwrappedParam::kRawPtrMayBeDangling ||
+                     // Exempt `this` pointer as it is not passed as a regular
+                     // function argument.
+                     (is_method && i == 0)>
   struct MayBeDanglingPtrPassedCorrectly {
     static constexpr bool value = [] {
       static_assert(v, "base::UnsafeDangling() pointers should only be passed "
@@ -1535,8 +1486,10 @@
   };
 
   template <bool v =
-                ParamStorage::kUnsafeDanglingAndMayBeDanglingHaveMatchingTraits>
-  struct UnsafeDanglingAndMayBeDanglingHaveMatchingTraits {
+                !UnwrappedParam::kRawPtrMayBeDangling ||
+                (ParamStorage::kBoundPtrMayDangle &&
+                 ParamStorage::kMayDangleAndMayBeDanglingHaveMatchingTraits)>
+  struct MayDangleAndMayBeDanglingHaveMatchingTraits {
     static constexpr bool value = [] {
       static_assert(
           v, "Pointers passed to MayBeDangling<T> parameters must be created "
@@ -1571,7 +1524,8 @@
   // callbacks were the only callback type. A `RepeatingCallback` with a
   // `Passed()` argument is really a `OnceCallback` and should eventually be
   // migrated.
-  template <bool v = UnwrappedParam::kMoveOnlyTypeMustUseBasePassed>
+  template <bool v = UnwrappedParam::kCanBeForwardedToBoundFunctor ||
+                     !UnwrappedParam::kWouldBeForwardableWithPassed>
   struct MoveOnlyTypeMustUseBasePassed {
     static constexpr bool value = [] {
       static_assert(v,
@@ -1582,7 +1536,8 @@
     }();
   };
 
-  template <bool v = UnwrappedParam::kNonConstRefParamMustBeWrapped>
+  template <bool v = UnwrappedParam::kCanBeForwardedToBoundFunctor ||
+                     !UnwrappedParam::kIsUnwrappedForwardableNonConstReference>
   struct NonConstRefParamMustBeWrapped {
     static constexpr bool value = [] {
       static_assert(v,
@@ -1602,7 +1557,8 @@
     }();
   };
 
-  template <bool v = BoundStorage::kMoveOnlyTypeMustUseStdMove>
+  template <bool v = BoundStorage::kBindArgumentCanBeCaptured ||
+                     !BoundStorage::kWouldBeCapturableWithStdMove>
   struct MoveOnlyTypeMustUseStdMove {
     static constexpr bool value = [] {
       static_assert(v,
@@ -1628,7 +1584,7 @@
   static constexpr bool value =
       std::conjunction_v<NotRawPtr<>,
                          MayBeDanglingPtrPassedCorrectly<>,
-                         UnsafeDanglingAndMayBeDanglingHaveMatchingTraits<>,
+                         MayDangleAndMayBeDanglingHaveMatchingTraits<>,
                          MoveOnlyTypeMustUseBasePassed<>,
                          NonConstRefParamMustBeWrapped<>,
                          CanBeForwardedToBoundFunctor<>,
diff --git a/base/functional/callback_internal.cc b/base/functional/callback_internal.cc
index be05f6bd..30be53ca 100644
--- a/base/functional/callback_internal.cc
+++ b/base/functional/callback_internal.cc
@@ -6,6 +6,7 @@
 
 #include "base/check.h"
 #include "base/notreached.h"
+#include "base/types/cxx23_to_underlying.h"
 
 namespace base {
 namespace internal {
@@ -15,13 +16,9 @@
 bool QueryCancellationTraitsForNonCancellables(
     const BindStateBase*,
     BindStateBase::CancellationQueryMode mode) {
-  switch (mode) {
-    case BindStateBase::IS_CANCELLED:
-      return false;
-    case BindStateBase::MAYBE_VALID:
-      return true;
-  }
-  NOTREACHED();
+  // Non-cancellables are never cancelled and always valid, which means the
+  // response for each mode is the same as its underlying value.
+  return to_underlying(mode);
 }
 
 }  // namespace
@@ -31,16 +28,15 @@
 }
 
 BindStateBase::BindStateBase(InvokeFuncStorage polymorphic_invoke,
-                             void (*destructor)(const BindStateBase*))
+                             DestructorPtr destructor)
     : BindStateBase(polymorphic_invoke,
                     destructor,
                     &QueryCancellationTraitsForNonCancellables) {}
 
 BindStateBase::BindStateBase(
     InvokeFuncStorage polymorphic_invoke,
-    void (*destructor)(const BindStateBase*),
-    bool (*query_cancellation_traits)(const BindStateBase*,
-                                      CancellationQueryMode))
+    DestructorPtr destructor,
+    QueryCancellationTraitsPtr query_cancellation_traits)
     : polymorphic_invoke_(polymorphic_invoke),
       destructor_(destructor),
       query_cancellation_traits_(query_cancellation_traits) {}
diff --git a/base/functional/callback_internal.h b/base/functional/callback_internal.h
index 295526e4..5d71f4ef 100644
--- a/base/functional/callback_internal.h
+++ b/base/functional/callback_internal.h
@@ -49,9 +49,12 @@
  public:
   REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
 
-  enum CancellationQueryMode {
-    IS_CANCELLED,
-    MAYBE_VALID,
+  // What kind of cancellation query the call to the cancellation traits is
+  // making. This enum could be removed, at the cost of storing an extra
+  // function pointer.
+  enum class CancellationQueryMode : bool {
+    kIsCancelled = false,
+    kMaybeValid = true,
   };
 
   using InvokeFuncStorage = void (*)();
@@ -60,13 +63,14 @@
   BindStateBase& operator=(const BindStateBase&) = delete;
 
  private:
-  BindStateBase(InvokeFuncStorage polymorphic_invoke,
-                void (*destructor)(const BindStateBase*));
-  BindStateBase(InvokeFuncStorage polymorphic_invoke,
-                void (*destructor)(const BindStateBase*),
-                bool (*query_cancellation_traits)(const BindStateBase*,
-                                                  CancellationQueryMode mode));
+  using DestructorPtr = void (*)(const BindStateBase*);
+  using QueryCancellationTraitsPtr = bool (*)(const BindStateBase*,
+                                              CancellationQueryMode mode);
 
+  BindStateBase(InvokeFuncStorage polymorphic_invoke, DestructorPtr destructor);
+  BindStateBase(InvokeFuncStorage polymorphic_invoke,
+                DestructorPtr destructor,
+                QueryCancellationTraitsPtr query_cancellation_traits);
   ~BindStateBase() = default;
 
   friend struct BindStateBaseRefCountTraits;
@@ -80,11 +84,12 @@
   friend struct ::base::FakeBindState;
 
   bool IsCancelled() const {
-    return query_cancellation_traits_(this, IS_CANCELLED);
+    return query_cancellation_traits_(this,
+                                      CancellationQueryMode::kIsCancelled);
   }
 
   bool MaybeValid() const {
-    return query_cancellation_traits_(this, MAYBE_VALID);
+    return query_cancellation_traits_(this, CancellationQueryMode::kMaybeValid);
   }
 
   // In C++, it is safe to cast function pointers to function pointers of
@@ -94,9 +99,8 @@
   InvokeFuncStorage polymorphic_invoke_;
 
   // Pointer to a function that will properly destroy |this|.
-  void (*destructor_)(const BindStateBase*);
-  bool (*query_cancellation_traits_)(const BindStateBase*,
-                                     CancellationQueryMode mode);
+  DestructorPtr destructor_;
+  QueryCancellationTraitsPtr query_cancellation_traits_;
 };
 
 // Minimal wrapper around a `scoped_refptr<BindStateBase>`. It allows more
diff --git a/base/functional/callback_unittest.cc b/base/functional/callback_unittest.cc
index f56b2aa..c2506ce 100644
--- a/base/functional/callback_unittest.cc
+++ b/base/functional/callback_unittest.cc
@@ -28,23 +28,13 @@
 // a type we declared in the anonymous namespace above to remove any chance of
 // colliding with another instantiation and breaking the one-definition-rule.
 struct FakeBindState : internal::BindStateBase {
-  FakeBindState() : BindStateBase(&NopInvokeFunc, &Destroy, &IsCancelled) {}
+  FakeBindState() : BindStateBase(&NopInvokeFunc, &Destroy) {}
 
  private:
   ~FakeBindState() = default;
   static void Destroy(const internal::BindStateBase* self) {
     delete static_cast<const FakeBindState*>(self);
   }
-  static bool IsCancelled(const internal::BindStateBase*,
-                          internal::BindStateBase::CancellationQueryMode mode) {
-    switch (mode) {
-      case internal::BindStateBase::IS_CANCELLED:
-        return false;
-      case internal::BindStateBase::MAYBE_VALID:
-        return true;
-    }
-    NOTREACHED();
-  }
 };
 
 namespace {
diff --git a/build/rust/cargo_crate.gni b/build/rust/cargo_crate.gni
index 76b68f5f..c0f45847 100644
--- a/build/rust/cargo_crate.gni
+++ b/build/rust/cargo_crate.gni
@@ -195,6 +195,19 @@
     }
   }
 
+  if (defined(invoker.output_name)) {
+    _output_name = invoker.output_name
+  } else if (_crate_type != "bin") {
+    # Note that file names of libraries must start with the crate name in
+    # order for the compiler to find transitive dependencies in the
+    # directory search paths (since they are not all explicitly specified).
+    #
+    # For bin targets, we expect the target name to be unique, and the name
+    # of the exe should not add magic stuff to it. And bin crates can not be
+    # transitive dependencies.
+    _output_name = "${_crate_name}_${_orig_target_name}"
+  }
+
   _testonly = false
   if (defined(invoker.testonly)) {
     _testonly = invoker.testonly
@@ -228,6 +241,11 @@
       # See comments above about cdylib.
       crate_type = "rlib"
     }
+    crate_name = _crate_name
+
+    if (defined(_output_name)) {
+      output_name = _output_name
+    }
 
     # Don't import the `chromium` crate into third-party code.
     no_chromium_prelude = true
@@ -405,6 +423,7 @@
       # the rust_macro_toolchain for it to unblock building them while the
       # Chromium stdlib is still being compiled.
       rust_executable(_build_script_name) {
+        crate_name = _build_script_name
         sources = invoker.build_sources
         crate_root = invoker.build_root
         testonly = _testonly
diff --git a/build/rust/rust_executable.gni b/build/rust/rust_executable.gni
index 478f529..a1f38fd 100644
--- a/build/rust/rust_executable.gni
+++ b/build/rust/rust_executable.gni
@@ -60,6 +60,14 @@
     executable_configs = invoker.configs
     target_type = "executable"
     assert(!defined(cxx_bindings))
+
+    # Executable targets should be unique names as they all get placed in the
+    # root output dir. We want their exe file name to be the same as the GN
+    # target, not a mangled name including the full GN path, and the exe file
+    # name comes from the crate name.
+    if (!defined(invoker.crate_name)) {
+      crate_name = target_name
+    }
   }
 }
 
diff --git a/build/rust/rust_static_library.gni b/build/rust/rust_static_library.gni
index 52a1b9d..6eb36dd8 100644
--- a/build/rust/rust_static_library.gni
+++ b/build/rust/rust_static_library.gni
@@ -169,9 +169,13 @@
       # files to the linker, but Rust does not have a parallel to this. Instead,
       # force the linker to always include the whole archive.
       #
-      # The library name is hardcoded here, since `get_target_outputs()` can't
-      # be used with a target that doesn't exist yet!
-      _rlib_path = "${_output_dir}/lib${_target_name}.rlib"
+      # The crate name mangling is copied from the rust_target template, and
+      # the library name is hardcoded here since `get_target_outputs()` can't be
+      # used with a target that doesn't exist yet!
+      _dir = rebase_path(target_out_dir, root_out_dir + "/obj")
+      _crate_name =
+          string_join("_", string_split(_dir, "/")) + "_" + target_name
+      _rlib_path = "${_output_dir}/lib${_crate_name}.rlib"
       if (current_os == "aix") {
         # The AIX linker does not implement an option for this.
       } else if (is_win) {
diff --git a/build/rust/rust_target.gni b/build/rust/rust_target.gni
index 14156e9..a11b15d 100644
--- a/build/rust/rust_target.gni
+++ b/build/rust/rust_target.gni
@@ -33,9 +33,14 @@
 
 template("rust_target") {
   _target_name = target_name
-  _crate_name = target_name
+
   if (defined(invoker.crate_name)) {
     _crate_name = invoker.crate_name
+  } else {
+    # Build a unique mangled crate name from the full GN path that the
+    # chromium::import! macro can find.
+    _dir = rebase_path(target_out_dir, root_out_dir + "/obj")
+    _crate_name = string_join("_", string_split(_dir, "/")) + "_" + target_name
   }
   _generate_crate_root =
       defined(invoker.generate_crate_root) && invoker.generate_crate_root
@@ -44,11 +49,6 @@
   # neither.
   assert(!defined(invoker.crate_root) || !_generate_crate_root)
 
-  if (defined(invoker.output_dir) && invoker.output_dir != "") {
-    # This is where the build target (.exe, .rlib, etc) goes.
-    _output_dir = invoker.output_dir
-  }
-
   # This is where the OUT_DIR environment variable points to when running a
   # build script and when compiling the build target, for consuming generated
   # files.
@@ -223,12 +223,6 @@
       _rustc_metadata = invoker.rustc_metadata
     }
 
-    # Add a metadata-influenced suffix to the output name for libraries only.
-    _output_suffix = ""
-    if (invoker.target_type == "rust_library" && _rustc_metadata != "") {
-      _output_suffix = "-${_rustc_metadata}"
-    }
-
     group(_target_name) {
       testonly = _testonly
       if (defined(_visibility)) {
@@ -301,6 +295,7 @@
 
       rust_unit_test(_unit_test_target) {
         testonly = true
+        crate_name = _unit_test_target
         crate_root = _crate_root
         sources = invoker.sources + [ crate_root ]
         rustflags = _rustflags
@@ -378,14 +373,24 @@
         sources += [ _crate_root ]
       }
 
-      # The Rust tool() declarations, like C++ ones, use the output_name and
-      # output_dir, so that GN targets can override these if needed. Here we
-      # give them their default values, or allow them to be overridden.
-      if (defined(_output_dir)) {
-        output_dir = _output_dir
-      }
-      if (!defined(output_name) || output_name == "") {
-        output_name = "${crate_name}${_output_suffix}"
+      if (!defined(output_name)) {
+        # Note that file names of libraries must start with the crate name in
+        # order for the compiler to find transitive dependencies in the
+        # directory search paths (since they are not all explicitly specified).
+        #
+        # For bin targets, we expect the target name to be unique, and the name
+        # of the exe should not add magic stuff to it. And bin crates can not be
+        # transitive dependencies.
+        if (invoker.target_type == "executable") {
+          output_name = _target_name
+        } else {
+          # TODO(danakj): Since the crate name includes the whole path for 1p
+          # libraries, we could move the output_dir to `root_out_dir` here for
+          # them, which would make for shorter file paths. But we need to not
+          # do the same for 3p crates or those with a `crate_name` set
+          # explicitly.
+          output_name = _crate_name
+        }
       }
 
       if (!_allow_unsafe) {
diff --git a/build/rust/tests/bindgen_test/main.rs b/build/rust/tests/bindgen_test/main.rs
index 499d93da..0e30fa6 100644
--- a/build/rust/tests/bindgen_test/main.rs
+++ b/build/rust/tests/bindgen_test/main.rs
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+chromium::import! {
+    "//build/rust/tests/bindgen_test:bindgen_test_lib";
+}
+
 use bindgen_test_lib::add_two_numbers_in_c;
 
 fn main() {
diff --git a/build/rust/tests/test_aliased_deps/BUILD.gn b/build/rust/tests/test_aliased_deps/BUILD.gn
index 45ad73f..35effeff 100644
--- a/build/rust/tests/test_aliased_deps/BUILD.gn
+++ b/build/rust/tests/test_aliased_deps/BUILD.gn
@@ -19,12 +19,15 @@
   aliased_deps = {
     # Unfortunately we have to know the `__rlib` suffix which is attached to the
     # actual rlib in `rust_static_library()`.
-    other_name = ":real_name__rlib"
+    build_rust_tests_test_aliased_deps_other_name = ":real_name__rlib"
   }
   build_native_rust_unit_tests = true
 }
 
 rust_static_library("real_name") {
+  # Using a fixed crate_name here which does not work with `import!` because
+  # we're testing `aliased_deps` which also does not work with `import!`.
+  crate_name = "real_name"
   crate_root = "real_name.rs"
   sources = [ crate_root ]
 }
diff --git a/build/rust/tests/test_aliased_deps/lib.rs b/build/rust/tests/test_aliased_deps/lib.rs
index dcaa343..ff5c2b80 100644
--- a/build/rust/tests/test_aliased_deps/lib.rs
+++ b/build/rust/tests/test_aliased_deps/lib.rs
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-pub use other_name;
+chromium::import! {
+    pub "//build/rust/tests/test_aliased_deps:other_name";
+}
 
 #[cfg(test)]
 #[test]
diff --git a/build/rust/tests/test_aliased_deps/main.rs b/build/rust/tests/test_aliased_deps/main.rs
index 8f33abe..2d66598 100644
--- a/build/rust/tests/test_aliased_deps/main.rs
+++ b/build/rust/tests/test_aliased_deps/main.rs
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+chromium::import! {
+    "//build/rust/tests/test_aliased_deps";
+}
+
 fn main() {
     test_aliased_deps::other_name::hello_world();
 }
diff --git a/build/rust/tests/test_rust_exe/BUILD.gn b/build/rust/tests/test_rust_exe/BUILD.gn
index 493854a4..cd8f4a5 100644
--- a/build/rust/tests/test_rust_exe/BUILD.gn
+++ b/build/rust/tests/test_rust_exe/BUILD.gn
@@ -11,7 +11,7 @@
     "//build/rust/tests/test_proc_macro_crate",
     "//build/rust/tests/test_rlib_crate:target1",
     "//build/rust/tests/test_rust_static_library",
-    "//build/rust/tests/test_rust_static_library_non_standard_arrangement",
+    "//build/rust/tests/test_rust_static_library_non_standard_arrangement:lib",
   ]
   build_native_rust_unit_tests = true
 }
diff --git a/build/rust/tests/test_rust_exe/main.rs b/build/rust/tests/test_rust_exe/main.rs
index 0409901..ed6cd07 100644
--- a/build/rust/tests/test_rust_exe/main.rs
+++ b/build/rust/tests/test_rust_exe/main.rs
@@ -2,6 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+chromium::import! {
+    "//build/rust/tests/test_rust_static_library";
+    "//build/rust/tests/test_rust_static_library_non_standard_arrangement:lib" as
+        test_rust_static_library_non_standard_arrangement;
+}
+
+// To mimic third-party, test_rlib_crate has a short crate_name which we do not
+// need to import!.
 use test_rlib_crate::say_hello_from_crate;
 
 fn main() {
diff --git a/build/rust/tests/test_rust_metadata/BUILD.gn b/build/rust/tests/test_rust_metadata/BUILD.gn
index 95171aa2f..2491879 100644
--- a/build/rust/tests/test_rust_metadata/BUILD.gn
+++ b/build/rust/tests/test_rust_metadata/BUILD.gn
@@ -3,14 +3,14 @@
 # found in the LICENSE file.
 
 import("//build/config/rust.gni")
+import("//build/rust/cargo_crate.gni")
 import("//build/rust/rust_executable.gni")
 import("//build/rust/rust_static_library.gni")
 import("//build/rust/rust_unit_test.gni")
 
 # This target depends on two variants of the same crate: one directly, and one
 # transitively. With correct metadata handling, this will work.
-rust_static_library("test_rust_metadata_lib") {
-  crate_name = "lib"
+rust_static_library("lib") {
   crate_root = "lib.rs"
   sources = [ "lib.rs" ]
   deps = [
@@ -36,24 +36,24 @@
   rust_unit_test("test_rust_metadata_unittests") {
     crate_root = "tests.rs"
     sources = [ "tests.rs" ]
-    deps = [ ":test_rust_metadata_lib" ]
+    deps = [ ":lib" ]
   }
 }
 
 rust_executable("test_rust_metadata_exe") {
   crate_root = "main.rs"
   sources = [ "main.rs" ]
-  deps = [ ":test_rust_metadata_lib" ]
+  deps = [ ":lib" ]
 }
 
 # Check that the metadata handling works when linking into a C++ binary too.
 executable("test_rust_metadata_cc_exe") {
   sources = [ "main.cc" ]
-  deps = [ ":test_rust_metadata_lib" ]
+  deps = [ ":lib" ]
 }
 
 # A source file whose behavior depends on cfg options.
-rust_static_library("transitive_dep_1") {
+cargo_crate("transitive_dep_1") {
   crate_name = "transitive_dep"
   crate_root = "transitive_dep.rs"
   sources = [ "transitive_dep.rs" ]
@@ -63,7 +63,7 @@
 
 # Build the same source again, but with a feature enabled. The metadata should
 # disambiguate the symbols when linking.
-rust_static_library("transitive_dep_2") {
+cargo_crate("transitive_dep_2") {
   crate_name = "transitive_dep"
   crate_root = "transitive_dep.rs"
   sources = [ "transitive_dep.rs" ]
diff --git a/build/rust/tests/test_rust_metadata/lib.rs b/build/rust/tests/test_rust_metadata/lib.rs
index 4081c4df..bfe2e1b2 100644
--- a/build/rust/tests/test_rust_metadata/lib.rs
+++ b/build/rust/tests/test_rust_metadata/lib.rs
@@ -11,6 +11,10 @@
 // what, rustc will see the conflict.
 extern crate transitive_dep;
 
+chromium::import! {
+    "//build/rust/tests/test_rust_metadata:foo_dependency";
+}
+
 pub use foo_dependency::say_foo;
 pub use foo_dependency::say_foo_directly;
 pub use transitive_dep::say_something;
diff --git a/build/rust/tests/test_rust_metadata/main.rs b/build/rust/tests/test_rust_metadata/main.rs
index a0d3866f..451910f0 100644
--- a/build/rust/tests/test_rust_metadata/main.rs
+++ b/build/rust/tests/test_rust_metadata/main.rs
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+chromium::import! {
+    "//build/rust/tests/test_rust_metadata:lib";
+}
+
 fn main() {
     lib::print_foo_bar();
     println!("{} from re-exported function", lib::say_foo_directly());
diff --git a/build/rust/tests/test_rust_metadata/tests.rs b/build/rust/tests/test_rust_metadata/tests.rs
index 2652ce0..4dd9199 100644
--- a/build/rust/tests/test_rust_metadata/tests.rs
+++ b/build/rust/tests/test_rust_metadata/tests.rs
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+chromium::import! {
+    "//build/rust/tests/test_rust_metadata:lib";
+}
+
 #[test]
 fn test_expected_outputs() {
     assert_eq!(lib::say_foo(), "foo");
diff --git a/build/rust/tests/test_rust_multiple_dep_versions_exe/BUILD.gn b/build/rust/tests/test_rust_multiple_dep_versions_exe/BUILD.gn
index c4d4785..4ebccae 100644
--- a/build/rust/tests/test_rust_multiple_dep_versions_exe/BUILD.gn
+++ b/build/rust/tests/test_rust_multiple_dep_versions_exe/BUILD.gn
@@ -5,7 +5,7 @@
 import("//build/rust/rust_executable.gni")
 import("//build/rust/rust_static_library.gni")
 
-# The exe depends on lib v1.But it also transitively depends on lib v2.
+# The exe depends on lib v1. But it also transitively depends on lib v2.
 # The code in the exe should use v1, and the code in the transitive lib should
 # use v2.
 rust_executable("test_rust_multiple_dep_versions_exe") {
diff --git a/build/rust/tests/test_rust_multiple_dep_versions_exe/main.rs b/build/rust/tests/test_rust_multiple_dep_versions_exe/main.rs
index e5471db..fdf730d 100644
--- a/build/rust/tests/test_rust_multiple_dep_versions_exe/main.rs
+++ b/build/rust/tests/test_rust_multiple_dep_versions_exe/main.rs
@@ -2,6 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+chromium::import! {
+    "//build/rust/tests/test_rust_multiple_dep_versions_exe:transitive_v2";
+}
+
+// To mimic third-party, the `test_lib` crate has a short name which does not
+// need to be import!ed.
+
 fn main() {
     test_lib::say_hello_from_v1();
     transitive_v2::transitively_say_hello();
diff --git a/build/rust/tests/test_rust_multiple_dep_versions_exe/transitive_lib.rs b/build/rust/tests/test_rust_multiple_dep_versions_exe/transitive_lib.rs
index 51806d7..30d9c89 100644
--- a/build/rust/tests/test_rust_multiple_dep_versions_exe/transitive_lib.rs
+++ b/build/rust/tests/test_rust_multiple_dep_versions_exe/transitive_lib.rs
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// To mimic third-party, the `test_lib` crate has a short name which does not
+// need to be import!ed.
+
 pub fn transitively_say_hello() {
     test_lib::say_hello_from_v2();
 }
diff --git a/build/rust/tests/test_rust_multiple_dep_versions_exe/v1/BUILD.gn b/build/rust/tests/test_rust_multiple_dep_versions_exe/v1/BUILD.gn
index 0704a16..5532b77c 100644
--- a/build/rust/tests/test_rust_multiple_dep_versions_exe/v1/BUILD.gn
+++ b/build/rust/tests/test_rust_multiple_dep_versions_exe/v1/BUILD.gn
@@ -5,6 +5,8 @@
 import("//build/rust/cargo_crate.gni")
 
 cargo_crate("test_lib") {
+  crate_name = "test_lib"
+
   # This crate has the same name as v2/test_lib, but a different epoch. The GN
   # target for the unit tests should not collide.
   epoch = "1"
diff --git a/build/rust/tests/test_rust_multiple_dep_versions_exe/v2/BUILD.gn b/build/rust/tests/test_rust_multiple_dep_versions_exe/v2/BUILD.gn
index 3fada7b..bda02c5 100644
--- a/build/rust/tests/test_rust_multiple_dep_versions_exe/v2/BUILD.gn
+++ b/build/rust/tests/test_rust_multiple_dep_versions_exe/v2/BUILD.gn
@@ -5,6 +5,8 @@
 import("//build/rust/cargo_crate.gni")
 
 cargo_crate("test_lib") {
+  crate_name = "test_lib"
+
   # This crate has the same name as v1/test_lib, but a different epoch. The GN
   # target for the unit tests should not collide.
   epoch = "2"
diff --git a/build/rust/tests/test_rust_static_library_non_standard_arrangement/BUILD.gn b/build/rust/tests/test_rust_static_library_non_standard_arrangement/BUILD.gn
index 6a85557..8f1a6c0e 100644
--- a/build/rust/tests/test_rust_static_library_non_standard_arrangement/BUILD.gn
+++ b/build/rust/tests/test_rust_static_library_non_standard_arrangement/BUILD.gn
@@ -4,7 +4,7 @@
 
 import("//build/rust/rust_static_library.gni")
 
-rust_static_library("test_rust_static_library_non_standard_arrangement") {
+rust_static_library("lib") {
   sources = [ "foo.rs" ]
   crate_root = "foo.rs"
   unit_test_target = "foo_tests"
diff --git a/build/rust/tests/test_rust_unittests/main.rs b/build/rust/tests/test_rust_unittests/main.rs
index a10b006..43ca4f2 100644
--- a/build/rust/tests/test_rust_unittests/main.rs
+++ b/build/rust/tests/test_rust_unittests/main.rs
@@ -5,6 +5,10 @@
 #![feature(test)]
 extern crate test;
 
+chromium::import! {
+    "//build/rust/tests/test_rust_static_library";
+}
+
 use test::Bencher;
 use test_rust_static_library::add_two_ints_via_rust;
 
diff --git a/buildtools/deps_revisions.gni b/buildtools/deps_revisions.gni
index fc5495e..2a52056 100644
--- a/buildtools/deps_revisions.gni
+++ b/buildtools/deps_revisions.gni
@@ -5,5 +5,5 @@
 declare_args() {
   # Used to cause full rebuilds on libc++ rolls. This should be kept in sync
   # with the libcxx_revision vars in //DEPS.
-  libcxx_revision = "99f5e4d09e3014e1b178dfd7ebf451e652461ad6"
+  libcxx_revision = "6226f31a8c995612018d53c7accad729a028e9c0"
 }
diff --git a/chrome/VERSION b/chrome/VERSION
index 95fe74bd..dc89e6d 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=122
 MINOR=0
-BUILD=6188
+BUILD=6189
 PATCH=0
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IphMessageService.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IphMessageService.java
index 73fa178..2fd2c79 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IphMessageService.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IphMessageService.java
@@ -16,7 +16,7 @@
 
 /** One of the concrete {@link MessageService} that only serves {@link MessageType.IPH}. */
 public class IphMessageService extends MessageService {
-    private final TabSwitcherCoordinator.IphController mIphController;
+    private final TabSwitcherIphController mIphController;
     private Tracker mTracker;
     private Callback<Boolean> mInitializedCallback =
             (result) -> {
@@ -55,7 +55,7 @@
         }
     }
 
-    IphMessageService(TabSwitcherCoordinator.IphController controller) {
+    IphMessageService(TabSwitcherIphController controller) {
         super(MessageType.IPH);
         mIphController = controller;
         Profile profile = Profile.getLastUsedRegularProfile().getOriginalProfile();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageController.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageController.java
new file mode 100644
index 0000000..2e65be97
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageController.java
@@ -0,0 +1,26 @@
+// 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.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+/** An interface to control price welcome message in grid tab switcher. */
+interface PriceWelcomeMessageController {
+    /**
+     * Remove the price welcome message item in the model list. Right now this is used when its
+     * binding tab is closed in the grid tab switcher.
+     */
+    void removePriceWelcomeMessage();
+
+    /**
+     * Restore the price welcome message item that should show. Right now this is only used when the
+     * closure of the binding tab in tab switcher is undone.
+     */
+    void restorePriceWelcomeMessage();
+
+    /**
+     * Show the price welcome message in tab switcher. This is used when any open tab in tab
+     * switcher has a price drop.
+     */
+    void showPriceWelcomeMessage(PriceMessageService.PriceTabData priceTabData);
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
index 70feb82..86f92bc 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -20,6 +20,8 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.Supplier;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabCreationState;
 import org.chromium.chrome.browser.tab.TabLaunchType;
@@ -176,7 +178,7 @@
                     public void tabClosureUndone(Tab tab) {
                         updateDialog();
                         updateGridTabSwitcher();
-                        snackbarManager.dismissSnackbars(TabGridDialogMediator.this, tab.getId());
+                        dismissSingleTabSnackbar(tab.getId());
                     }
 
                     @Override
@@ -240,21 +242,21 @@
 
                     @Override
                     public void tabClosureCommitted(Tab tab) {
-                        dismissSingleTabClosureSnackbar(tab.getId());
+                        dismissSingleTabSnackbar(tab.getId());
                     }
 
                     @Override
                     public void onFinishingMultipleTabClosure(List<Tab> tabs) {
                         if (tabs.size() == 1) {
-                            dismissSingleTabClosureSnackbar(tabs.get(0).getId());
+                            dismissSingleTabSnackbar(tabs.get(0).getId());
                             return;
                         }
-                        snackbarManager.dismissSnackbars(TabGridDialogMediator.this, tabs);
+                        dismissMultipleTabSnackbar(tabs);
                     }
 
                     @Override
                     public void allTabsClosureCommitted(boolean isIncognito) {
-                        snackbarManager.dismissSnackbars(TabGridDialogMediator.this);
+                        dismissAllSnackbars();
                     }
 
                     private void showSingleTabClosureSnackbar(Tab tab) {
@@ -269,8 +271,30 @@
                                         .setAction(mContext.getString(R.string.undo), tab.getId()));
                     }
 
-                    private void dismissSingleTabClosureSnackbar(int tabId) {
-                        snackbarManager.dismissSnackbars(TabGridDialogMediator.this, tabId);
+                    private void dismissMultipleTabSnackbar(List<Tab> tabs) {
+                        PostTask.postTask(
+                                TaskTraits.UI_DEFAULT,
+                                () -> {
+                                    snackbarManager.dismissSnackbars(
+                                            TabGridDialogMediator.this, tabs);
+                                });
+                    }
+
+                    private void dismissSingleTabSnackbar(int tabId) {
+                        PostTask.postTask(
+                                TaskTraits.UI_DEFAULT,
+                                () -> {
+                                    snackbarManager.dismissSnackbars(
+                                            TabGridDialogMediator.this, tabId);
+                                });
+                    }
+
+                    private void dismissAllSnackbars() {
+                        PostTask.postTask(
+                                TaskTraits.UI_DEFAULT,
+                                () -> {
+                                    snackbarManager.dismissSnackbars(TabGridDialogMediator.this);
+                                });
                     }
                 };
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphDialogCoordinator.java
index e1776a5d..a17bb14c 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphDialogCoordinator.java
@@ -18,7 +18,7 @@
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** Coordinator for the IPH dialog in grid tab switcher. */
-class TabGridIphDialogCoordinator implements TabSwitcherCoordinator.IphController {
+class TabGridIphDialogCoordinator implements TabSwitcherIphController {
     private final View mParentView;
     private final TabGridIphDialogView mIphDialogView;
     private final PropertyModel mModel;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
index 2618c33..5afeabf 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -124,7 +124,8 @@
      * @param itemType The item type to put in the list of tabs.
      * @param selectionDelegateProvider Provider to provide selected Tabs for a selectable tab list.
      *     It's NULL when selection is not possible.
-     * @param priceWelcomeMessageController A controller to show PriceWelcomeMessage.
+     * @param priceWelcomeMessageControllerSupplier A supplier for a controller to show
+     *     PriceWelcomeMessage.
      * @param parentView {@link ViewGroup} The root view of the UI.
      * @param attachToParent Whether the UI should attach to root view.
      * @param componentName A unique string uses to identify different components for UMA recording.
@@ -150,8 +151,7 @@
             @Nullable TabListMediator.TabGridDialogHandler dialogHandler,
             @UiType int itemType,
             @Nullable TabListMediator.SelectionDelegateProvider selectionDelegateProvider,
-            @Nullable
-                    TabSwitcherMediator.PriceWelcomeMessageController priceWelcomeMessageController,
+            @NonNull Supplier<PriceWelcomeMessageController> priceWelcomeMessageControllerSupplier,
             @NonNull ViewGroup parentView,
             boolean attachToParent,
             String componentName,
@@ -170,7 +170,7 @@
                 dialogHandler,
                 itemType,
                 selectionDelegateProvider,
-                priceWelcomeMessageController,
+                priceWelcomeMessageControllerSupplier,
                 parentView,
                 attachToParent,
                 componentName,
@@ -196,8 +196,7 @@
             @Nullable TabListMediator.TabGridDialogHandler dialogHandler,
             @UiType int itemType,
             @Nullable TabListMediator.SelectionDelegateProvider selectionDelegateProvider,
-            @Nullable
-                    TabSwitcherMediator.PriceWelcomeMessageController priceWelcomeMessageController,
+            @NonNull Supplier<PriceWelcomeMessageController> priceWelcomeMessageControllerSupplier,
             @NonNull ViewGroup parentView,
             boolean attachToParent,
             String componentName,
@@ -352,7 +351,7 @@
                         selectionDelegateProvider,
                         gridCardOnClickListenerProvider,
                         dialogHandler,
-                        priceWelcomeMessageController,
+                        priceWelcomeMessageControllerSupplier,
                         componentName,
                         itemType);
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index 105be86..057e066 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -74,7 +74,6 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator.TabListMode;
 import org.chromium.chrome.browser.tasks.tab_management.TabListFaviconProvider.TabFaviconFetcher;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.UiType;
-import org.chromium.chrome.browser.tasks.tab_management.TabSwitcherMediator.PriceWelcomeMessageController;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabListEditorActionMetricGroups;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
@@ -164,17 +163,21 @@
 
     /** Provides capability to asynchronously acquire {@link ShoppingPersistedTabData} */
     static class ShoppingPersistedTabDataFetcher {
-        protected Tab mTab;
-        protected PriceWelcomeMessageController mPriceWelcomeMessageController;
+        protected final Tab mTab;
+        protected final Supplier<PriceWelcomeMessageController>
+                mPriceWelcomeMessageControllerSupplier;
 
         /**
          * @param tab {@link Tab} {@link ShoppingPersistedTabData} will be acquired for.
-         * @param priceWelcomeMessageController to show the price welcome message.
+         * @param priceWelcomeMessageControllerSupplier to show the price welcome message.
          */
         ShoppingPersistedTabDataFetcher(
-                Tab tab, @Nullable PriceWelcomeMessageController priceWelcomeMessageController) {
+                Tab tab,
+                @NonNull
+                        Supplier<PriceWelcomeMessageController>
+                                priceWelcomeMessageControllerSupplier) {
             mTab = tab;
-            mPriceWelcomeMessageController = priceWelcomeMessageController;
+            mPriceWelcomeMessageControllerSupplier = priceWelcomeMessageControllerSupplier;
         }
 
         /**
@@ -199,15 +202,18 @@
                             () -> {
                                 if (!PriceTrackingUtilities.isPriceWelcomeMessageCardEnabled(
                                                 Profile.getLastUsedRegularProfile())
-                                        || (mPriceWelcomeMessageController == null)
+                                        || (mPriceWelcomeMessageControllerSupplier == null)
+                                        || (mPriceWelcomeMessageControllerSupplier.get() == null)
                                         || (shoppingPersistedTabData == null)
                                         || (shoppingPersistedTabData.getPriceDrop() == null)) {
                                     return;
                                 }
-                                mPriceWelcomeMessageController.showPriceWelcomeMessage(
-                                        new PriceTabData(
-                                                mTab.getId(),
-                                                shoppingPersistedTabData.getPriceDrop()));
+                                mPriceWelcomeMessageControllerSupplier
+                                        .get()
+                                        .showPriceWelcomeMessage(
+                                                new PriceTabData(
+                                                        mTab.getId(),
+                                                        shoppingPersistedTabData.getPriceDrop()));
                             });
         }
     }
@@ -329,7 +335,7 @@
     private final GridCardOnClickListenerProvider mGridCardOnClickListenerProvider;
     private final TabGridDialogHandler mTabGridDialogHandler;
     private final TabListFaviconProvider mTabListFaviconProvider;
-    private final PriceWelcomeMessageController mPriceWelcomeMessageController;
+    private final Supplier<PriceWelcomeMessageController> mPriceWelcomeMessageControllerSupplier;
 
     private Size mDefaultGridCardSize;
     private String mComponentName;
@@ -756,7 +762,8 @@
      * @param gridCardOnClickListenerProvider Provides the onClickListener for opening dialog when
      *     click on a grid card.
      * @param dialogHandler A handler to handle requests about updating TabGridDialog.
-     * @param priceWelcomeMessageController A controller to show PriceWelcomeMessage.
+     * @param priceWelcomeMessageControllerSupplier A supplier of a controller to show
+     *     PriceWelcomeMessage.
      * @param componentName This is a unique string to identify different components.
      * @param uiType The type of UI this mediator should be building.
      */
@@ -773,7 +780,7 @@
             @Nullable SelectionDelegateProvider selectionDelegateProvider,
             @Nullable GridCardOnClickListenerProvider gridCardOnClickListenerProvider,
             @Nullable TabGridDialogHandler dialogHandler,
-            @Nullable PriceWelcomeMessageController priceWelcomeMessageController,
+            @NonNull Supplier<PriceWelcomeMessageController> priceWelcomeMessageControllerSupplier,
             String componentName,
             @UiType int uiType) {
         mContext = context;
@@ -790,7 +797,7 @@
         mTabGridDialogHandler = dialogHandler;
         mActionsOnAllRelatedTabs = actionOnRelatedTabs;
         mUiType = uiType;
-        mPriceWelcomeMessageController = priceWelcomeMessageController;
+        mPriceWelcomeMessageControllerSupplier = priceWelcomeMessageControllerSupplier;
 
         mTabModelObserver =
                 new TabModelObserver() {
@@ -1905,7 +1912,8 @@
                         .set(
                                 TabProperties.SHOPPING_PERSISTED_TAB_DATA_FETCHER,
                                 new ShoppingPersistedTabDataFetcher(
-                                        pseudoTab.getTab(), mPriceWelcomeMessageController));
+                                        pseudoTab.getTab(),
+                                        mPriceWelcomeMessageControllerSupplier));
             } else {
                 mModel.get(index)
                         .model
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
index 3a2e3fb..617ee97 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.tasks.tab_management;
 
 import android.app.Activity;
-import android.content.Context;
 import android.content.SharedPreferences;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
@@ -38,9 +37,6 @@
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher;
-import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
-import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManager;
-import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManagerFactory;
 import org.chromium.chrome.browser.price_tracking.PriceTrackingFeatures;
 import org.chromium.chrome.browser.price_tracking.PriceTrackingUtilities;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -51,7 +47,6 @@
 import org.chromium.chrome.browser.tasks.ReturnToChromeUtil;
 import org.chromium.chrome.browser.tasks.pseudotab.PseudoTab;
 import org.chromium.chrome.browser.tasks.pseudotab.TabAttributeCache;
-import org.chromium.chrome.browser.tasks.tab_management.PriceMessageService.PriceMessageType;
 import org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator.TabListMode;
 import org.chromium.chrome.browser.tasks.tab_management.suggestions.TabSuggestionsOrchestrator;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
@@ -61,7 +56,6 @@
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator.SystemUiScrimDelegate;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.modaldialog.ModalDialogManager;
-import org.chromium.ui.modelutil.LayoutViewBuilder;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
@@ -77,19 +71,10 @@
                 TabSwitcher,
                 TabSwitcher.TabListDelegate,
                 TabSwitcherMediator.ResetHandler,
-                TabSwitcherMediator.MessageItemsController,
-                TabSwitcherMediator.PriceWelcomeMessageController,
                 TabGridItemTouchHelperCallback.OnLongPressTabItemEventListener {
-    /** Interface to control the IPH dialog. */
-    interface IphController {
-        /** Show the dialog with IPH. */
-        void showIph();
-    }
-
     // TODO(crbug.com/982018): Rename 'COMPONENT_NAME' so as to add different metrics for carousel
     // tab switcher.
     static final String COMPONENT_NAME = "GridTabSwitcher";
-    private static boolean sAppendedMessagesForTesting;
     private final Activity mActivity;
     private final PropertyModelChangeProcessor mContainerViewChangeProcessor;
     private final ActivityLifecycleDispatcher mLifecycleDispatcher;
@@ -102,24 +87,20 @@
     private final BrowserControlsStateProvider mBrowserControlsStateProvider;
     private final TabModelSelector mTabModelSelector;
     private final @TabListCoordinator.TabListMode int mMode;
-    private final MessageCardProviderCoordinator mMessageCardProviderCoordinator;
-    private final MultiWindowModeStateDispatcher mMultiWindowModeStateDispatcher;
     private final Supplier<DynamicResourceLoader> mDynamicResourceLoaderSupplier;
     private final SnackbarManager mSnackbarManager;
     private final ModalDialogManager mModalDialogManager;
+    private final TabSwitcherMessageManager mMessageManager;
     private final TabListEditorManager mTabListEditorManager;
     private TabSuggestionsOrchestrator mTabSuggestionsOrchestrator;
-    private TabSuggestionMessageService mTabSuggestionMessageService;
     private TabAttributeCache mTabAttributeCache;
     private ViewGroup mContainer;
     private TabCreatorManager mTabCreatorManager;
     private boolean mIsInitialized;
-    private PriceMessageService mPriceMessageService;
     private SharedPreferences.OnSharedPreferenceChangeListener mPriceAnnotationsPrefListener;
     private final ViewGroup mCoordinatorView;
     private final ViewGroup mRootView;
     private TabContentManager mTabContentManager;
-    private IncognitoReauthPromoMessageService mIncognitoReauthPromoMessageService;
 
     /**
      * TODO(crbug.com/1227656): Refactor this to pass a supplier instead to ensure we re-use the
@@ -164,7 +145,6 @@
             mContainer = container;
             mCoordinatorView = activity.findViewById(R.id.coordinator);
             mTabCreatorManager = tabCreatorManager;
-            mMultiWindowModeStateDispatcher = multiWindowModeStateDispatcher;
             mRootView = rootView;
             mTabContentManager = tabContentManager;
             mDynamicResourceLoaderSupplier = dynamicResourceLoaderSupplier;
@@ -197,9 +177,6 @@
                             browserControls,
                             container,
                             tabContentManager,
-                            this,
-                            this,
-                            multiWindowModeStateDispatcher,
                             new Handler(),
                             mode,
                             incognitoReauthControllerSupplier,
@@ -237,6 +214,10 @@
             int emptyHeadingStringResId = R.string.tabswitcher_no_tabs_empty_state;
             int emptySubheadingStringResId =
                     R.string.tabswitcher_no_tabs_open_to_visit_different_pages;
+            // Note: getMessageManager is used because () -> mMessageManager warns that the object
+            // might not be initialized. This is safe because downstream has null checks and only
+            // uses the object once tabs are added to the model which happens after this constructor
+            // finishes.
             mTabListCoordinator =
                     new TabListCoordinator(
                             mode,
@@ -251,7 +232,7 @@
                             null,
                             TabProperties.UiType.CLOSABLE,
                             null,
-                            this,
+                            this::getMessageManager,
                             container,
                             true,
                             COMPONENT_NAME,
@@ -288,55 +269,29 @@
             mMediator.setTabListEditorControllerSupplier(
                     mTabListEditorManager.getControllerSupplier());
 
-            mMessageCardProviderCoordinator =
-                    new MessageCardProviderCoordinator(
-                            activity,
-                            tabModelSelector::isIncognitoSelected,
-                            (identifier) -> {
-                                if (identifier == MessageService.MessageType.PRICE_MESSAGE
-                                        || identifier
-                                                == MessageService.MessageType
-                                                        .INCOGNITO_REAUTH_PROMO_MESSAGE
-                                        || identifier
-                                                == MessageService.MessageType.TAB_SUGGESTION) {
-                                    mTabListCoordinator.removeSpecialListItem(
-                                            TabProperties.UiType.LARGE_MESSAGE, identifier);
-                                } else {
-                                    mTabListCoordinator.removeSpecialListItem(
-                                            TabProperties.UiType.MESSAGE, identifier);
-                                    appendNextMessage(identifier);
-                                }
+            var tabListEditorControllerSupplier =
+                    LazyOneshotSupplier.fromSupplier(
+                            () -> {
+                                mTabListEditorManager.initTabListEditor();
+                                return mTabListEditorManager.getControllerSupplier().get();
                             });
+            mMessageManager =
+                    new TabSwitcherMessageManager(
+                            activity,
+                            lifecycleDispatcher,
+                            currentTabModelFilterSupplier,
+                            container,
+                            multiWindowModeStateDispatcher,
+                            snackbarManager,
+                            modalDialogManager,
+                            mTabListCoordinator,
+                            tabListEditorControllerSupplier,
+                            mMediator,
+                            mode);
 
             mMenuOrKeyboardActionController = menuOrKeyboardActionController;
 
             if (mode == TabListCoordinator.TabListMode.GRID) {
-                mTabListCoordinator.registerItemType(
-                        TabProperties.UiType.MESSAGE,
-                        new LayoutViewBuilder(R.layout.tab_grid_message_card_item),
-                        MessageCardViewBinder::bind);
-
-                if (shouldRegisterLargeMessageItemType()) {
-                    mTabListCoordinator.registerItemType(
-                            TabProperties.UiType.LARGE_MESSAGE,
-                            new LayoutViewBuilder(R.layout.large_message_card_item),
-                            LargeMessageCardViewBinder::bind);
-                }
-
-                if (shouldRegisterLargeMessageItemType()) {
-                    mTabListCoordinator.registerItemType(
-                            TabProperties.UiType.CUSTOM_MESSAGE,
-                            new LayoutViewBuilder(R.layout.custom_message_card_item),
-                            (model, view, key) -> {
-                                CustomMessageCardViewBinder.bind(
-                                        model,
-                                        new CustomMessageCardViewBinder.ViewHolder(
-                                                (CustomMessageCardView) view,
-                                                mTabSuggestionMessageService),
-                                        key);
-                            });
-                }
-
                 if (ProfileManager.isInitialized()
                         && PriceTrackingFeatures.isPriceTrackingEnabled(
                                 Profile.getLastUsedRegularProfile())) {
@@ -424,14 +379,6 @@
                 coordinator.getContext().getColor(R.color.omnibox_focused_fading_background_color));
     }
 
-    public static void resetHasAppendedMessagesForTesting() {
-        sAppendedMessagesForTesting = false;
-    }
-
-    public static boolean hasAppendedMessagesForTesting() {
-        return sAppendedMessagesForTesting;
-    }
-
     @Override
     public void initWithNative() {
         if (mIsInitialized) return;
@@ -444,80 +391,16 @@
             mTabListCoordinator.initWithNative(
                     shouldUseDynamicResource ? mDynamicResourceLoaderSupplier.get() : null);
 
-            if (mMode == TabListCoordinator.TabListMode.GRID) {
-                if (ChromeFeatureList.sArchiveTabService.isEnabled()) {
-                    var currentTabModelFilterSupplier =
-                            mTabModelSelector
-                                    .getTabModelFilterProvider()
-                                    .getCurrentTabModelFilterSupplier();
-                    mTabSuggestionsOrchestrator =
-                            new TabSuggestionsOrchestrator(
-                                    mActivity, currentTabModelFilterSupplier);
-                    mTabSuggestionMessageService =
-                            new TabSuggestionMessageService(
-                                    mActivity,
-                                    currentTabModelFilterSupplier,
-                                    () -> {
-                                        // TODO(crbug/1504606): Migrate to a separate manager
-                                        // instance that uses closable tabs and make this method
-                                        // private.
-                                        mTabListEditorManager.initTabListEditor();
-                                        return mTabListEditorManager.getControllerSupplier().get();
-                                    });
-                    mTabSuggestionsOrchestrator.addObserver(mTabSuggestionMessageService);
-                    mMessageCardProviderCoordinator.subscribeMessageService(
-                            mTabSuggestionMessageService);
-                }
-
-                mTabGridIphDialogCoordinator =
-                        new TabGridIphDialogCoordinator(mActivity, mContainer, mModalDialogManager);
-                IphMessageService iphMessageService =
-                        new IphMessageService(mTabGridIphDialogCoordinator);
-                mMessageCardProviderCoordinator.subscribeMessageService(iphMessageService);
-
-                if (IncognitoReauthManager.isIncognitoReauthFeatureAvailable()
-                        && mIncognitoReauthPromoMessageService == null) {
-                    mIncognitoReauthManager = new IncognitoReauthManager();
-                    mIncognitoReauthPromoMessageService =
-                            new IncognitoReauthPromoMessageService(
-                                    MessageService.MessageType.INCOGNITO_REAUTH_PROMO_MESSAGE,
-                                    Profile.getLastUsedRegularProfile(),
-                                    mActivity,
-                                    ChromeSharedPreferences.getInstance(),
-                                    mIncognitoReauthManager,
-                                    mSnackbarManager,
-                                    () ->
-                                            TabUiFeatureUtilities.isTabToGtsAnimationEnabled(
-                                                    mActivity),
-                                    mLifecycleDispatcher);
-                    mMessageCardProviderCoordinator.subscribeMessageService(
-                            mIncognitoReauthPromoMessageService);
-                }
-            }
+            mMessageManager.initWithNative();
 
             mMultiThumbnailCardProvider.initWithNative(
                     mTabModelSelector.getModel(false).getProfile());
             mMediator.initWithNative(mSnackbarManager);
-            // TODO(crbug.com/1222762): Only call setUpPriceTracking in GRID TabSwitcher.
-            setUpPriceTracking(mActivity, mModalDialogManager);
 
             mIsInitialized = true;
         }
     }
 
-    private void setUpPriceTracking(Context context, ModalDialogManager modalDialogManager) {
-        if (PriceTrackingFeatures.isPriceTrackingEnabled(Profile.getLastUsedRegularProfile())) {
-            PriceDropNotificationManager notificationManager =
-                    PriceDropNotificationManagerFactory.create();
-            if (mMode == TabListCoordinator.TabListMode.GRID) {
-                mPriceMessageService =
-                        new PriceMessageService(
-                                mTabListCoordinator, mMediator, notificationManager);
-                mMessageCardProviderCoordinator.subscribeMessageService(mPriceMessageService);
-                mMediator.setPriceMessageService(mPriceMessageService);
-            }
-        }
-    }
 
     // TabSwitcher implementation.
     @Override
@@ -653,98 +536,13 @@
 
     @Override
     public boolean resetWithTabs(@Nullable List<PseudoTab> tabs, boolean quickMode) {
-        // Invalidate price welcome message for every reset so that the stale message won't be
-        // restored by mistake (e.g. from tabClosureUndone in TabSwitcherMediator).
-        if (mPriceMessageService != null) {
-            mPriceMessageService.invalidateMessage();
-        }
+        mMessageManager.beforeReset();
         boolean showQuickly = mTabListCoordinator.resetWithListOfTabs(tabs, quickMode);
-        removeAllAppendedMessage();
-        if (tabs != null && tabs.size() > 0) {
-            if (mPriceMessageService != null
-                    && PriceTrackingUtilities.isPriceAlertsMessageCardEnabled(
-                            Profile.getLastUsedRegularProfile())) {
-                mPriceMessageService.preparePriceMessage(PriceMessageType.PRICE_ALERTS, null);
-            }
-            appendMessagesTo(tabs.size());
-        }
+        mMessageManager.afterReset(tabs == null ? 0 : tabs.size());
 
         return showQuickly;
     }
 
-    // MessageItemsController implementation.
-    @Override
-    public void removeAllAppendedMessage() {
-        mTabListCoordinator.removeSpecialListItem(
-                TabProperties.UiType.MESSAGE, MessageService.MessageType.ALL);
-        mTabListCoordinator.removeSpecialListItem(
-                TabProperties.UiType.LARGE_MESSAGE,
-                MessageService.MessageType.INCOGNITO_REAUTH_PROMO_MESSAGE);
-        mTabListCoordinator.removeSpecialListItem(
-                TabProperties.UiType.LARGE_MESSAGE, MessageService.MessageType.TAB_SUGGESTION);
-        sAppendedMessagesForTesting = false;
-    }
-
-    @Override
-    public void restoreAllAppendedMessage() {
-        sAppendedMessagesForTesting = false;
-        List<MessageCardProviderMediator.Message> messages =
-                mMessageCardProviderCoordinator.getMessageItems();
-        for (int i = 0; i < messages.size(); i++) {
-            if (!shouldAppendMessage(messages.get(i).model)) continue;
-            // The restore of PRICE_MESSAGE is handled in the restorePriceWelcomeMessage() below.
-            if (messages.get(i).type == MessageService.MessageType.PRICE_MESSAGE) {
-                continue;
-            } else if (messages.get(i).type
-                    == MessageService.MessageType.INCOGNITO_REAUTH_PROMO_MESSAGE) {
-                mTabListCoordinator.addSpecialListItemToEnd(
-                        TabProperties.UiType.LARGE_MESSAGE, messages.get(i).model);
-            } else if (messages.get(i).type == MessageService.MessageType.TAB_SUGGESTION) {
-                mTabListCoordinator.addSpecialListItemToEnd(
-                        TabProperties.UiType.LARGE_MESSAGE, messages.get(i).model);
-            } else {
-                mTabListCoordinator.addSpecialListItemToEnd(
-                        TabProperties.UiType.MESSAGE, messages.get(i).model);
-            }
-        }
-        sAppendedMessagesForTesting = messages.size() > 0;
-    }
-
-    // PriceWelcomeMessageController implementation.
-    @Override
-    public void removePriceWelcomeMessage() {
-        mTabListCoordinator.removeSpecialListItem(
-                TabProperties.UiType.LARGE_MESSAGE, MessageService.MessageType.PRICE_MESSAGE);
-    }
-
-    @Override
-    public void restorePriceWelcomeMessage() {
-        appendNextMessage(MessageService.MessageType.PRICE_MESSAGE);
-    }
-
-    @Override
-    public void showPriceWelcomeMessage(PriceMessageService.PriceTabData priceTabData) {
-        if (mPriceMessageService == null
-                || !PriceTrackingUtilities.isPriceWelcomeMessageCardEnabled(
-                        Profile.getLastUsedRegularProfile())
-                || mMessageCardProviderCoordinator.isMessageShown(
-                        MessageService.MessageType.PRICE_MESSAGE, PriceMessageType.PRICE_WELCOME)) {
-            return;
-        }
-        if (mPriceMessageService.preparePriceMessage(
-                PriceMessageType.PRICE_WELCOME, priceTabData)) {
-            appendNextMessage(MessageService.MessageType.PRICE_MESSAGE);
-            // To make the message card in view when user enters tab switcher, we should scroll to
-            // current tab with 0 offset. See {@link
-            // TabSwitcherMediator#setInitialScrollIndexOffset} for more details.
-            mMediator.scrollToTab(
-                    mTabModelSelector
-                            .getTabModelFilterProvider()
-                            .getCurrentTabModelFilter()
-                            .index());
-        }
-    }
-
     // OnLongPressTabItemEventListener implementation
     @Override
     public void onLongPressEvent(int tabId) {
@@ -752,80 +550,6 @@
         RecordUserAction.record("TabMultiSelectV2.OpenLongPressInGrid");
     }
 
-    private void appendMessagesTo(int index) {
-        if (mMultiWindowModeStateDispatcher.isInMultiWindowMode()) return;
-        sAppendedMessagesForTesting = false;
-        List<MessageCardProviderMediator.Message> messages =
-                mMessageCardProviderCoordinator.getMessageItems();
-        for (int i = 0; i < messages.size(); i++) {
-            if (!shouldAppendMessage(messages.get(i).model)) continue;
-            if (messages.get(i).type == MessageService.MessageType.PRICE_MESSAGE) {
-                mTabListCoordinator.addSpecialListItem(
-                        index, TabProperties.UiType.LARGE_MESSAGE, messages.get(i).model);
-            } else if (messages.get(i).type
-                    == MessageService.MessageType.INCOGNITO_REAUTH_PROMO_MESSAGE) {
-                mayAddIncognitoReauthPromoCard(messages.get(i).model);
-            } else if (messages.get(i).type == MessageService.MessageType.TAB_SUGGESTION) {
-                // TODO(crbug.com/1487664): Update to a mayAdd call checking show criteria
-                mTabListCoordinator.addSpecialListItem(
-                        mTabModelSelector
-                                        .getTabModelFilterProvider()
-                                        .getCurrentTabModelFilter()
-                                        .index()
-                                + 1,
-                        TabProperties.UiType.LARGE_MESSAGE,
-                        messages.get(i).model);
-            } else {
-                mTabListCoordinator.addSpecialListItem(
-                        index, TabProperties.UiType.MESSAGE, messages.get(i).model);
-            }
-            index++;
-        }
-        if (messages.size() > 0) sAppendedMessagesForTesting = true;
-    }
-
-    private void appendNextMessage(@MessageService.MessageType int messageType) {
-        assert mMessageCardProviderCoordinator != null;
-
-        MessageCardProviderMediator.Message nextMessage =
-                mMessageCardProviderCoordinator.getNextMessageItemForType(messageType);
-        if (nextMessage == null || !shouldAppendMessage(nextMessage.model)) return;
-        if (messageType == MessageService.MessageType.PRICE_MESSAGE) {
-            mTabListCoordinator.addSpecialListItem(
-                    mTabListCoordinator.getPriceWelcomeMessageInsertionIndex(),
-                    TabProperties.UiType.LARGE_MESSAGE,
-                    nextMessage.model);
-        } else {
-            mTabListCoordinator.addSpecialListItemToEnd(
-                    TabProperties.UiType.MESSAGE, nextMessage.model);
-        }
-    }
-
-    private void mayAddIncognitoReauthPromoCard(PropertyModel model) {
-        if (mIncognitoReauthPromoMessageService.isIncognitoReauthPromoMessageEnabled(
-                Profile.getLastUsedRegularProfile())) {
-            mTabListCoordinator.addSpecialListItemToEnd(TabProperties.UiType.LARGE_MESSAGE, model);
-            mIncognitoReauthPromoMessageService.increasePromoShowCountAndMayDisableIfCountExceeds();
-        }
-    }
-
-    private boolean shouldAppendMessage(PropertyModel messageModel) {
-        Integer messageCardVisibilityControlValue =
-                messageModel.get(
-                        MessageCardViewProperties
-                                .MESSAGE_CARD_VISIBILITY_CONTROL_IN_REGULAR_AND_INCOGNITO_MODE);
-
-        @MessageCardViewProperties.MessageCardScope
-        int scope =
-                (messageCardVisibilityControlValue != null)
-                        ? messageCardVisibilityControlValue
-                        : MessageCardViewProperties.MessageCardScope.REGULAR;
-
-        if (scope == MessageCardViewProperties.MessageCardScope.BOTH) return true;
-        return mTabModelSelector.isIncognitoSelected()
-                ? scope == MessageCardViewProperties.MessageCardScope.INCOGNITO
-                : scope == MessageCardViewProperties.MessageCardScope.REGULAR;
-    }
 
     private View getTabGridDialogAnimationSourceView(int tabId) {
         int index = mTabListCoordinator.indexOfTab(tabId);
@@ -839,15 +563,6 @@
         return sourceViewHolder.itemView;
     }
 
-    private boolean shouldRegisterLargeMessageItemType() {
-        if (ProfileManager.isInitialized()
-                && PriceTrackingFeatures.isPriceTrackingEnabled(
-                        Profile.getLastUsedRegularProfile())) {
-            return true;
-        }
-        return IncognitoReauthManager.isIncognitoReauthFeatureAvailable();
-    }
-
     @Override
     public void softCleanup() {
         mTabListCoordinator.softCleanup();
@@ -870,7 +585,6 @@
                     mTabSwitcherMenuActionHandler);
         }
         mTabListCoordinator.onDestroy();
-        mMessageCardProviderCoordinator.destroy();
         mContainerViewChangeProcessor.destroy();
         if (mTabGridDialogCoordinator != null) {
             mTabGridDialogCoordinator.destroy();
@@ -895,4 +609,8 @@
     public void runAnimationOnNextLayout(Runnable runnable) {
         mTabListCoordinator.runAnimationOnNextLayout(runnable);
     }
+
+    private TabSwitcherMessageManager getMessageManager() {
+        return mMessageManager;
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherIphController.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherIphController.java
new file mode 100644
index 0000000..541fcce9
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherIphController.java
@@ -0,0 +1,11 @@
+// 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.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+/** Interface to control the IPH dialog. */
+interface TabSwitcherIphController {
+    /** Show the dialog with IPH. */
+    void showIph();
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
index 80497aa3..5eb2038 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
@@ -48,7 +48,6 @@
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver;
 import org.chromium.chrome.browser.layouts.LayoutType;
-import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher;
 import org.chromium.chrome.browser.price_tracking.PriceTrackingUtilities;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
@@ -114,8 +113,6 @@
     private final boolean mIsStartSurfaceEnabled;
     private final boolean mIsStartSurfaceRefactorEnabled;
     private final boolean mIsTablet;
-    private final MultiWindowModeStateDispatcher mMultiWindowModeStateDispatcher;
-    private final MultiWindowModeStateDispatcher.MultiWindowModeObserver mMultiWindowModeObserver;
     private final ObservableSupplierImpl<Boolean> mBackPressChangedSupplier =
             new ObservableSupplierImpl<>();
     private final ObservableSupplierImpl<Boolean> mIsDialogVisibleSupplier =
@@ -159,7 +156,6 @@
     private @Nullable TransitiveObservableSupplier<TabListEditorController, Boolean>
             mCurrentTabListEditorControllerBackSupplier;
     private TabSwitcher.OnTabSelectingListener mOnTabSelectingListener;
-    private PriceMessageService mPriceMessageService;
 
     /**
      * This allows to check if re-auth is pending when tab switcher is shown in Incognito mode.
@@ -231,42 +227,6 @@
         void hardCleanup();
     }
 
-    /** Interface to control message items in grid tab switcher. */
-    interface MessageItemsController {
-        /**
-         * Remove all the message items in the model list. Right now this is used when all tabs are
-         * closed in the grid tab switcher.
-         */
-        void removeAllAppendedMessage();
-
-        /**
-         * Restore all the message items that should show. Right now this is only used to restore
-         * message items when the closure of the last tab in tab switcher is undone.
-         */
-        void restoreAllAppendedMessage();
-    }
-
-    /** An interface to control price welcome message in grid tab switcher. */
-    interface PriceWelcomeMessageController {
-        /**
-         * Remove the price welcome message item in the model list. Right now this is used when
-         * its binding tab is closed in the grid tab switcher.
-         */
-        void removePriceWelcomeMessage();
-
-        /**
-         * Restore the price welcome message item that should show. Right now this is only used
-         * when the closure of the binding tab in tab switcher is undone.
-         */
-        void restorePriceWelcomeMessage();
-
-        /**
-         * Show the price welcome message in tab switcher. This is used when any open tab in tab
-         * switcher has a price drop.
-         */
-        void showPriceWelcomeMessage(PriceMessageService.PriceTabData priceTabData);
-    }
-
     /**
      * Basic constructor for the Mediator.
      *
@@ -278,8 +238,6 @@
      * @param browserControlsStateProvider {@link BrowserControlsStateProvider} to use.
      * @param containerView The container {@link ViewGroup} to use.
      * @param tabContentManager The {@link TabContentManager} for first meaningful paint event.
-     * @param multiWindowModeStateDispatcher The {@link MultiWindowModeStateDispatcher} to observe
-     *     for multi-window related changes.
      * @param handler The {@link Handler} for running cleanup callbacks.
      * @param mode One of the {@link TabListMode}.
      * @param incognitoReauthControllerSupplier {@link OneshotSupplier<IncognitoReauthController>}
@@ -300,9 +258,6 @@
             BrowserControlsStateProvider browserControlsStateProvider,
             ViewGroup containerView,
             TabContentManager tabContentManager,
-            MessageItemsController messageItemsController,
-            PriceWelcomeMessageController priceWelcomeMessageController,
-            MultiWindowModeStateDispatcher multiWindowModeStateDispatcher,
             @NonNull Handler handler,
             @TabListMode int mode,
             @Nullable OneshotSupplier<IncognitoReauthController> incognitoReauthControllerSupplier,
@@ -316,7 +271,6 @@
         mContainerViewModel = containerViewModel;
         mTabModelSelector = tabModelSelector;
         mBrowserControlsStateProvider = browserControlsStateProvider;
-        mMultiWindowModeStateDispatcher = multiWindowModeStateDispatcher;
         mMode = mode;
         mHandler = handler;
         mContainerViewModel.set(MODE, mode);
@@ -435,25 +389,9 @@
                         setInitialScrollIndexOffset();
                     }
 
-                    @Override
-                    public void willCloseTab(Tab tab, boolean animate, boolean didCloseAlone) {
-                        if (mTabModelSelector.getCurrentModel().getCount() == 1) {
-                            messageItemsController.removeAllAppendedMessage();
-                        } else if (mPriceMessageService != null
-                                && mPriceMessageService.getBindingTabId() == tab.getId()) {
-                            priceWelcomeMessageController.removePriceWelcomeMessage();
-                        }
-                    }
 
                     @Override
                     public void tabClosureUndone(Tab tab) {
-                        if (mTabModelSelector.getCurrentModel().getCount() == 1) {
-                            messageItemsController.restoreAllAppendedMessage();
-                        }
-                        if (mPriceMessageService != null
-                                && mPriceMessageService.getBindingTabId() == tab.getId()) {
-                            priceWelcomeMessageController.restorePriceWelcomeMessage();
-                        }
                         notifyBackPressStateChangedInternal();
                     }
 
@@ -479,15 +417,6 @@
                         notifyBackPressStateChangedInternal();
                     }
 
-                    @Override
-                    public void tabClosureCommitted(Tab tab) {
-                        // TODO(crbug.com/1157578): Auto update the PriceMessageService instead of
-                        // updating it based on the client caller.
-                        if (mPriceMessageService != null
-                                && mPriceMessageService.getBindingTabId() == tab.getId()) {
-                            mPriceMessageService.invalidateMessage();
-                        }
-                    }
                 };
 
         mBrowserControlsObserver =
@@ -576,15 +505,6 @@
                 };
         mTabContentManager = tabContentManager;
 
-        mMultiWindowModeObserver =
-                isInMultiWindowMode -> {
-                    if (isInMultiWindowMode) {
-                        messageItemsController.removeAllAppendedMessage();
-                    } else {
-                        messageItemsController.restoreAllAppendedMessage();
-                    }
-                };
-        mMultiWindowModeStateDispatcher.addObserver(mMultiWindowModeObserver);
         notifyBackPressStateChangedInternal();
     }
 
@@ -1105,16 +1025,12 @@
         mTabModelSelector
                 .getTabModelFilterProvider()
                 .removeTabModelFilterObserver(mTabModelObserver);
-        mMultiWindowModeStateDispatcher.removeObserver(mMultiWindowModeObserver);
     }
 
     void setOnTabSelectingListener(TabSwitcher.OnTabSelectingListener listener) {
         mOnTabSelectingListener = listener;
     }
 
-    void setPriceMessageService(PriceMessageService priceMessageService) {
-        mPriceMessageService = priceMessageService;
-    }
 
     void requestAccessibilityFocusOnCurrentTab() {
         if (!mIsTabSwitcherShowing || !mTabModelSelector.isTabStateInitialized()) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java
new file mode 100644
index 0000000..46de45ed
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java
@@ -0,0 +1,534 @@
+// 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.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+import android.content.Context;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.chromium.base.ObserverList;
+import org.chromium.base.ValueChangedCallback;
+import org.chromium.base.supplier.LazyOneshotSupplier;
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthManager;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher;
+import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
+import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManager;
+import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManagerFactory;
+import org.chromium.chrome.browser.price_tracking.PriceTrackingFeatures;
+import org.chromium.chrome.browser.price_tracking.PriceTrackingUtilities;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.profiles.ProfileManager;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabModelFilter;
+import org.chromium.chrome.browser.tabmodel.TabModelObserver;
+import org.chromium.chrome.browser.tasks.tab_management.MessageService.MessageType;
+import org.chromium.chrome.browser.tasks.tab_management.PriceMessageService.PriceMessageType;
+import org.chromium.chrome.browser.tasks.tab_management.PriceMessageService.PriceWelcomeMessageReviewActionProvider;
+import org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator.TabListMode;
+import org.chromium.chrome.browser.tasks.tab_management.TabListEditorCoordinator.TabListEditorController;
+import org.chromium.chrome.browser.tasks.tab_management.suggestions.TabSuggestionsOrchestrator;
+import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
+import org.chromium.chrome.tab_ui.R;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modelutil.LayoutViewBuilder;
+import org.chromium.ui.modelutil.PropertyModel;
+
+import java.util.List;
+
+/** Manages message related glue for the {@link TabSwitcher}. */
+public class TabSwitcherMessageManager implements PriceWelcomeMessageController {
+    /** Used to observe updates to message cards. */
+    public interface MessageUpdateObserver {
+        /** Invoked when messages are added. */
+        default void onAppendedMessage() {}
+
+        /** Invoked when messages are removed. */
+        default void onRemoveAllAppendedMessage() {}
+
+        /** Invoked when messages are restored. */
+        default void onRestoreAllAppendedMessage() {}
+
+        /** Invoked when price message is shown. */
+        default void onShowPriceWelcomeMessage() {}
+
+        /** Invoked when price message is removed. */
+        default void onRemovePriceWelcomeMessage() {}
+
+        /** Invoked when price message is restored. */
+        default void onRestorePriceWelcomeMessage() {}
+    }
+
+    private final MultiWindowModeStateDispatcher.MultiWindowModeObserver mMultiWindowModeObserver =
+            isInMultiWindowMode -> {
+                if (isInMultiWindowMode) {
+                    removeAllAppendedMessage();
+                } else {
+                    restoreAllAppendedMessage();
+                }
+            };
+
+    private final TabModelObserver mTabModelObserver =
+            new TabModelObserver() {
+                @Override
+                public void willCloseTab(Tab tab, boolean animate, boolean didCloseAlone) {
+                    if (mCurrentTabModelFilterSupplier.get().getTabModel().getCount() == 1) {
+                        removeAllAppendedMessage();
+                    } else if (mPriceMessageService != null
+                            && mPriceMessageService.getBindingTabId() == tab.getId()) {
+                        removePriceWelcomeMessage();
+                    }
+                }
+
+                @Override
+                public void tabClosureUndone(Tab tab) {
+                    if (mCurrentTabModelFilterSupplier.get().getTabModel().getCount() == 1) {
+                        restoreAllAppendedMessage();
+                    }
+                    if (mPriceMessageService != null
+                            && mPriceMessageService.getBindingTabId() == tab.getId()) {
+                        restorePriceWelcomeMessage();
+                    }
+                }
+
+                @Override
+                public void tabClosureCommitted(Tab tab) {
+                    // TODO(crbug.com/1157578): Auto update the PriceMessageService instead of
+                    // updating it based on the client caller.
+                    if (mPriceMessageService != null
+                            && mPriceMessageService.getBindingTabId() == tab.getId()) {
+                        mPriceMessageService.invalidateMessage();
+                    }
+                }
+            };
+
+    private static boolean sAppendedMessagesForTesting;
+
+    private final @NonNull ObserverList<MessageUpdateObserver> mObservers = new ObserverList<>();
+    private final @NonNull Context mContext;
+    private final @NonNull ActivityLifecycleDispatcher mLifecylceDispatcher;
+    private final @NonNull ObservableSupplier<TabModelFilter> mCurrentTabModelFilterSupplier;
+    private final @NonNull ViewGroup mContainer;
+    private final @NonNull MultiWindowModeStateDispatcher mMultiWindowModeStateDispatcher;
+    private final @NonNull SnackbarManager mSnackbarManager;
+    private final @NonNull ModalDialogManager mModalDialogManager;
+    private final @NonNull TabListCoordinator mTabListCoordinator;
+    private final @NonNull LazyOneshotSupplier<TabListEditorController>
+            mTabListEditorControllerSupplier;
+    private final @NonNull PriceWelcomeMessageReviewActionProvider
+            mPriceWelcomeMessageReviewActionProvider;
+    private final @TabListMode int mMode;
+    private final @NonNull MessageCardProviderCoordinator mMessageCardProviderCoordinator;
+    private final @NonNull ValueChangedCallback<TabModelFilter> mOnTabModelFilterChanged =
+            new ValueChangedCallback<>(this::onTabModelFilterChanged);
+
+    private @Nullable TabGridIphDialogCoordinator mTabGridIphDialogCoordinator;
+    private @Nullable IncognitoReauthManager mIncognitoReauthManager;
+    private @Nullable TabSuggestionsOrchestrator mTabSuggestionsOrchestrator;
+    private @Nullable TabSuggestionMessageService mTabSuggestionMessageService;
+    private @Nullable PriceMessageService mPriceMessageService;
+    private @Nullable IncognitoReauthPromoMessageService mIncognitoReauthPromoMessageService;
+
+    /**
+     * @param context The Android activity context.
+     * @param lifecycleDispatcher The {@link ActivityLifecycleDispatcher} for the activity.
+     * @param currentTabModelFilterSupplier The supplier of the current {@link TabModelFilter}.
+     * @param container The {@link ViewGroup} of the container view.
+     * @param multiWindowModeStateDispatcher The {@link MultiWindowModeStateDispatcher} to observe
+     *     for multi-window related changes.
+     * @param snackbarManager The {@link SnackbarManager} for the activity.
+     * @param modalDialogManager The {@link ModalDialogManager} for the activity.
+     * @param tabListCoordinator The {@link TabListCoordinator} to show messages on.
+     * @param tabListEditorControllerSupplier The supplier of the {@link TabListEditorController}.
+     * @param priceWelcomeMessageReviewActionProvider The review action provider for price welcome.
+     * @param mode The {@link TabListMode} the {@link TabListCoordinator} is in.
+     */
+    public TabSwitcherMessageManager(
+            @NonNull Context context,
+            @NonNull ActivityLifecycleDispatcher lifecylceDispatcher,
+            @NonNull ObservableSupplier<TabModelFilter> currentTabModelFilterSupplier,
+            @NonNull ViewGroup container,
+            @NonNull MultiWindowModeStateDispatcher multiWindowModeStateDispatcher,
+            @NonNull SnackbarManager snackbarManager,
+            @NonNull ModalDialogManager modalDialogManager,
+            @NonNull TabListCoordinator tabListCoordinator,
+            @NonNull LazyOneshotSupplier<TabListEditorController> tabListEditorControllerSupplier,
+            @NonNull
+                    PriceWelcomeMessageReviewActionProvider priceWelcomeMessageReviewActionProvider,
+            @TabListMode int mode) {
+        mContext = context;
+        mLifecylceDispatcher = lifecylceDispatcher;
+        mCurrentTabModelFilterSupplier = currentTabModelFilterSupplier;
+        mContainer = container;
+        mMultiWindowModeStateDispatcher = multiWindowModeStateDispatcher;
+        mSnackbarManager = snackbarManager;
+        mModalDialogManager = modalDialogManager;
+        mTabListCoordinator = tabListCoordinator;
+        mTabListEditorControllerSupplier = tabListEditorControllerSupplier;
+        mPriceWelcomeMessageReviewActionProvider = priceWelcomeMessageReviewActionProvider;
+        mMode = mode;
+
+        mMessageCardProviderCoordinator =
+                new MessageCardProviderCoordinator(
+                        context,
+                        () -> currentTabModelFilterSupplier.get().isIncognito(),
+                        this::dismissHandler);
+
+        registerMessages(tabListCoordinator, mode);
+
+        mMultiWindowModeStateDispatcher.addObserver(mMultiWindowModeObserver);
+        mOnTabModelFilterChanged.onResult(
+                currentTabModelFilterSupplier.addObserver(mOnTabModelFilterChanged));
+    }
+
+    /**
+     * @param observer The {@link MessageUpdateObserver} to notify.
+     */
+    public void addObserver(MessageUpdateObserver observer) {
+        mObservers.addObserver(observer);
+    }
+
+    /**
+     * @param observer The {@link MessageUpdateObserver} to remove.
+     */
+    public void removeObserver(MessageUpdateObserver observer) {
+        mObservers.removeObserver(observer);
+    }
+
+    /** Post-native initialization. */
+    public void initWithNative() {
+        if (mMode != TabListCoordinator.TabListMode.GRID) return;
+
+        if (ChromeFeatureList.sArchiveTabService.isEnabled()) {
+            mTabSuggestionsOrchestrator =
+                    new TabSuggestionsOrchestrator(mContext, mCurrentTabModelFilterSupplier);
+            mTabSuggestionMessageService =
+                    new TabSuggestionMessageService(
+                            mContext,
+                            mCurrentTabModelFilterSupplier,
+                            mTabListEditorControllerSupplier::get);
+            mTabSuggestionsOrchestrator.addObserver(mTabSuggestionMessageService);
+            mMessageCardProviderCoordinator.subscribeMessageService(mTabSuggestionMessageService);
+        }
+
+        mTabGridIphDialogCoordinator =
+                new TabGridIphDialogCoordinator(mContext, mContainer, mModalDialogManager);
+        IphMessageService iphMessageService = new IphMessageService(mTabGridIphDialogCoordinator);
+        mMessageCardProviderCoordinator.subscribeMessageService(iphMessageService);
+
+        if (IncognitoReauthManager.isIncognitoReauthFeatureAvailable()
+                && mIncognitoReauthPromoMessageService == null) {
+            mIncognitoReauthManager = new IncognitoReauthManager();
+            mIncognitoReauthPromoMessageService =
+                    new IncognitoReauthPromoMessageService(
+                            MessageService.MessageType.INCOGNITO_REAUTH_PROMO_MESSAGE,
+                            Profile.getLastUsedRegularProfile(),
+                            mContext,
+                            ChromeSharedPreferences.getInstance(),
+                            mIncognitoReauthManager,
+                            mSnackbarManager,
+                            () -> TabUiFeatureUtilities.isTabToGtsAnimationEnabled(mContext),
+                            mLifecylceDispatcher);
+            mMessageCardProviderCoordinator.subscribeMessageService(
+                    mIncognitoReauthPromoMessageService);
+        }
+        setUpPriceTracking();
+    }
+
+    /** Called before resetting the list of tabs. */
+    public void beforeReset() {
+        // Invalidate price welcome message for every reset so that the stale message won't be
+        // restored by mistake (e.g. from tabClosureUndone in TabSwitcherMediator).
+        if (mPriceMessageService != null) {
+            mPriceMessageService.invalidateMessage();
+        }
+    }
+
+    /** Called after resetting the list of tabs. */
+    public void afterReset(int tabCount) {
+        removeAllAppendedMessage();
+        if (tabCount > 0) {
+            if (mPriceMessageService != null
+                    && PriceTrackingUtilities.isPriceAlertsMessageCardEnabled(
+                            Profile.getLastUsedRegularProfile())) {
+                mPriceMessageService.preparePriceMessage(PriceMessageType.PRICE_ALERTS, null);
+            }
+            appendMessagesTo(tabCount);
+        }
+    }
+
+    /** Destroys the module and unregisters observers. */
+    public void destroy() {
+        mMultiWindowModeStateDispatcher.removeObserver(mMultiWindowModeObserver);
+        removeTabModelFilterObservers(mCurrentTabModelFilterSupplier.get());
+        mCurrentTabModelFilterSupplier.removeObserver(mOnTabModelFilterChanged);
+
+        mMessageCardProviderCoordinator.destroy();
+    }
+
+    @Override
+    public void showPriceWelcomeMessage(PriceMessageService.PriceTabData priceTabData) {
+        if (mPriceMessageService == null
+                || !PriceTrackingUtilities.isPriceWelcomeMessageCardEnabled(
+                        Profile.getLastUsedRegularProfile())
+                || mMessageCardProviderCoordinator.isMessageShown(
+                        MessageService.MessageType.PRICE_MESSAGE, PriceMessageType.PRICE_WELCOME)) {
+            return;
+        }
+        if (mPriceMessageService.preparePriceMessage(
+                PriceMessageType.PRICE_WELCOME, priceTabData)) {
+            appendNextMessage(MessageService.MessageType.PRICE_MESSAGE);
+            // To make the message card in view when user enters tab switcher, we should scroll to
+            // current tab with 0 offset. See {@link
+            // TabSwitcherMediator#setInitialScrollIndexOffset} for more details.
+            mPriceWelcomeMessageReviewActionProvider.scrollToTab(
+                    mCurrentTabModelFilterSupplier.get().index());
+        }
+        for (MessageUpdateObserver observer : mObservers) {
+            observer.onShowPriceWelcomeMessage();
+        }
+    }
+
+    @Override
+    public void removePriceWelcomeMessage() {
+        mTabListCoordinator.removeSpecialListItem(
+                TabProperties.UiType.LARGE_MESSAGE, MessageService.MessageType.PRICE_MESSAGE);
+        for (MessageUpdateObserver observer : mObservers) {
+            observer.onRemovePriceWelcomeMessage();
+        }
+    }
+
+    @Override
+    public void restorePriceWelcomeMessage() {
+        appendNextMessage(MessageService.MessageType.PRICE_MESSAGE);
+        for (MessageUpdateObserver observer : mObservers) {
+            observer.onRestorePriceWelcomeMessage();
+        }
+    }
+
+    private void appendNextMessage(@MessageService.MessageType int messageType) {
+        assert mMessageCardProviderCoordinator != null;
+
+        MessageCardProviderMediator.Message nextMessage =
+                mMessageCardProviderCoordinator.getNextMessageItemForType(messageType);
+        if (nextMessage == null || !shouldAppendMessage(nextMessage.model)) return;
+        if (messageType == MessageService.MessageType.PRICE_MESSAGE) {
+            mTabListCoordinator.addSpecialListItem(
+                    mTabListCoordinator.getPriceWelcomeMessageInsertionIndex(),
+                    TabProperties.UiType.LARGE_MESSAGE,
+                    nextMessage.model);
+        } else {
+            mTabListCoordinator.addSpecialListItemToEnd(
+                    TabProperties.UiType.MESSAGE, nextMessage.model);
+        }
+    }
+
+    private void appendMessagesTo(int index) {
+        if (mMultiWindowModeStateDispatcher.isInMultiWindowMode()) return;
+        sAppendedMessagesForTesting = false;
+        List<MessageCardProviderMediator.Message> messages =
+                mMessageCardProviderCoordinator.getMessageItems();
+        for (int i = 0; i < messages.size(); i++) {
+            if (!shouldAppendMessage(messages.get(i).model)) continue;
+            if (messages.get(i).type == MessageService.MessageType.PRICE_MESSAGE) {
+                mTabListCoordinator.addSpecialListItem(
+                        index, TabProperties.UiType.LARGE_MESSAGE, messages.get(i).model);
+            } else if (messages.get(i).type
+                    == MessageService.MessageType.INCOGNITO_REAUTH_PROMO_MESSAGE) {
+                mayAddIncognitoReauthPromoCard(messages.get(i).model);
+            } else if (messages.get(i).type == MessageService.MessageType.TAB_SUGGESTION) {
+                // TODO(crbug.com/1487664): Update to a mayAdd call checking show criteria
+                mTabListCoordinator.addSpecialListItem(
+                        mCurrentTabModelFilterSupplier.get().index() + 1,
+                        TabProperties.UiType.LARGE_MESSAGE,
+                        messages.get(i).model);
+            } else {
+                mTabListCoordinator.addSpecialListItem(
+                        index, TabProperties.UiType.MESSAGE, messages.get(i).model);
+            }
+            index++;
+        }
+        if (messages.size() > 0) sAppendedMessagesForTesting = true;
+        for (MessageUpdateObserver observer : mObservers) {
+            observer.onAppendedMessage();
+        }
+    }
+
+    private void mayAddIncognitoReauthPromoCard(PropertyModel model) {
+        if (mIncognitoReauthPromoMessageService.isIncognitoReauthPromoMessageEnabled(
+                Profile.getLastUsedRegularProfile())) {
+            mTabListCoordinator.addSpecialListItemToEnd(TabProperties.UiType.LARGE_MESSAGE, model);
+            mIncognitoReauthPromoMessageService.increasePromoShowCountAndMayDisableIfCountExceeds();
+        }
+    }
+
+    private boolean shouldAppendMessage(PropertyModel messageModel) {
+        Integer messageCardVisibilityControlValue =
+                messageModel.get(
+                        MessageCardViewProperties
+                                .MESSAGE_CARD_VISIBILITY_CONTROL_IN_REGULAR_AND_INCOGNITO_MODE);
+
+        @MessageCardViewProperties.MessageCardScope
+        int scope =
+                (messageCardVisibilityControlValue != null)
+                        ? messageCardVisibilityControlValue
+                        : MessageCardViewProperties.MessageCardScope.REGULAR;
+
+        if (scope == MessageCardViewProperties.MessageCardScope.BOTH) return true;
+        return mCurrentTabModelFilterSupplier.get().isIncognito()
+                ? scope == MessageCardViewProperties.MessageCardScope.INCOGNITO
+                : scope == MessageCardViewProperties.MessageCardScope.REGULAR;
+    }
+
+    /**
+     * Remove all the message items in the model list. Right now this is used when all tabs are
+     * closed in the grid tab switcher.
+     */
+    private void removeAllAppendedMessage() {
+        mTabListCoordinator.removeSpecialListItem(
+                TabProperties.UiType.MESSAGE, MessageService.MessageType.ALL);
+        mTabListCoordinator.removeSpecialListItem(
+                TabProperties.UiType.LARGE_MESSAGE,
+                MessageService.MessageType.INCOGNITO_REAUTH_PROMO_MESSAGE);
+        mTabListCoordinator.removeSpecialListItem(
+                TabProperties.UiType.LARGE_MESSAGE, MessageService.MessageType.TAB_SUGGESTION);
+        sAppendedMessagesForTesting = false;
+        for (MessageUpdateObserver observer : mObservers) {
+            observer.onRemoveAllAppendedMessage();
+        }
+    }
+
+    /**
+     * Restore all the message items that should show. Right now this is only used to restore
+     * message items when the closure of the last tab in tab switcher is undone.
+     */
+    private void restoreAllAppendedMessage() {
+        sAppendedMessagesForTesting = false;
+        List<MessageCardProviderMediator.Message> messages =
+                mMessageCardProviderCoordinator.getMessageItems();
+        for (int i = 0; i < messages.size(); i++) {
+            if (!shouldAppendMessage(messages.get(i).model)) continue;
+            // The restore of PRICE_MESSAGE is handled in the restorePriceWelcomeMessage() below.
+            if (messages.get(i).type == MessageService.MessageType.PRICE_MESSAGE) {
+                continue;
+            } else if (messages.get(i).type
+                    == MessageService.MessageType.INCOGNITO_REAUTH_PROMO_MESSAGE) {
+                mTabListCoordinator.addSpecialListItemToEnd(
+                        TabProperties.UiType.LARGE_MESSAGE, messages.get(i).model);
+            } else if (messages.get(i).type == MessageService.MessageType.TAB_SUGGESTION) {
+                mTabListCoordinator.addSpecialListItemToEnd(
+                        TabProperties.UiType.LARGE_MESSAGE, messages.get(i).model);
+            } else {
+                mTabListCoordinator.addSpecialListItemToEnd(
+                        TabProperties.UiType.MESSAGE, messages.get(i).model);
+            }
+        }
+        sAppendedMessagesForTesting = messages.size() > 0;
+        for (MessageUpdateObserver observer : mObservers) {
+            observer.onRestoreAllAppendedMessage();
+        }
+    }
+
+    private void registerMessages(
+            @NonNull TabListCoordinator tabListCoordinator, @TabListMode int mode) {
+        if (mode != TabListCoordinator.TabListMode.GRID) return;
+
+        tabListCoordinator.registerItemType(
+                TabProperties.UiType.MESSAGE,
+                new LayoutViewBuilder(R.layout.tab_grid_message_card_item),
+                MessageCardViewBinder::bind);
+
+        if (shouldRegisterLargeMessageItemType()) {
+            tabListCoordinator.registerItemType(
+                    TabProperties.UiType.LARGE_MESSAGE,
+                    new LayoutViewBuilder(R.layout.large_message_card_item),
+                    LargeMessageCardViewBinder::bind);
+        }
+
+        if (ChromeFeatureList.sArchiveTabService.isEnabled()) {
+            tabListCoordinator.registerItemType(
+                    TabProperties.UiType.CUSTOM_MESSAGE,
+                    new LayoutViewBuilder(R.layout.custom_message_card_item),
+                    (model, view, key) -> {
+                        CustomMessageCardViewBinder.bind(
+                                model,
+                                new CustomMessageCardViewBinder.ViewHolder(
+                                        (CustomMessageCardView) view, mTabSuggestionMessageService),
+                                key);
+                    });
+        }
+    }
+
+    private boolean shouldRegisterLargeMessageItemType() {
+        if (ProfileManager.isInitialized()
+                && PriceTrackingFeatures.isPriceTrackingEnabled(
+                        Profile.getLastUsedRegularProfile())) {
+            return true;
+        }
+        return IncognitoReauthManager.isIncognitoReauthFeatureAvailable();
+    }
+
+    private void setUpPriceTracking() {
+        if (PriceTrackingFeatures.isPriceTrackingEnabled(Profile.getLastUsedRegularProfile())) {
+            PriceDropNotificationManager notificationManager =
+                    PriceDropNotificationManagerFactory.create();
+            if (mPriceMessageService == null) {
+                mPriceMessageService =
+                        new PriceMessageService(
+                                mTabListCoordinator,
+                                mPriceWelcomeMessageReviewActionProvider,
+                                notificationManager);
+            }
+            mMessageCardProviderCoordinator.subscribeMessageService(mPriceMessageService);
+        }
+    }
+
+    private void dismissHandler(@MessageType int messageType) {
+        if (messageType == MessageService.MessageType.PRICE_MESSAGE
+                || messageType == MessageService.MessageType.INCOGNITO_REAUTH_PROMO_MESSAGE
+                || messageType == MessageService.MessageType.TAB_SUGGESTION) {
+            mTabListCoordinator.removeSpecialListItem(
+                    TabProperties.UiType.LARGE_MESSAGE, messageType);
+        } else {
+            mTabListCoordinator.removeSpecialListItem(TabProperties.UiType.MESSAGE, messageType);
+            appendNextMessage(messageType);
+        }
+    }
+
+    private void onTabModelFilterChanged(
+            @Nullable TabModelFilter newFilter, @Nullable TabModelFilter oldFilter) {
+        removeTabModelFilterObservers(oldFilter);
+
+        if (newFilter != null) {
+            newFilter.addObserver(mTabModelObserver);
+        }
+    }
+
+    private void removeTabModelFilterObservers(@Nullable TabModelFilter filter) {
+        if (filter != null) {
+            filter.removeObserver(mTabModelObserver);
+        }
+    }
+
+    /** Returns whether this manager has appended any messages. */
+    public static boolean hasAppendedMessagesForTesting() {
+        return sAppendedMessagesForTesting;
+    }
+
+    /** Resets whether this manager has appended any messages. */
+    public static void resetHasAppendedMessagesForTesting() {
+        sAppendedMessagesForTesting = false;
+    }
+
+    void setPriceMessageServiceForTesting(@NonNull PriceMessageService priceMessageService) {
+        assert mPriceMessageService == null
+                : "setPriceMessageServiceForTesting() must be before initWithNative().";
+        mPriceMessageService = priceMessageService;
+    }
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManagerUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManagerUnitTest.java
new file mode 100644
index 0000000..b25f6a1f
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManagerUnitTest.java
@@ -0,0 +1,276 @@
+// 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.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.widget.FrameLayout;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import org.chromium.base.supplier.LazyOneshotSupplier;
+import org.chromium.base.supplier.ObservableSupplierImpl;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher;
+import org.chromium.chrome.browser.price_tracking.PriceTrackingFeatures;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.tab.MockTab;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelFilter;
+import org.chromium.chrome.browser.tabmodel.TabModelObserver;
+import org.chromium.chrome.browser.tasks.tab_management.PriceMessageService.PriceWelcomeMessageReviewActionProvider;
+import org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator.TabListMode;
+import org.chromium.chrome.browser.tasks.tab_management.TabListEditorCoordinator.TabListEditorController;
+import org.chromium.chrome.browser.tasks.tab_management.TabSwitcherMessageManager.MessageUpdateObserver;
+import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
+import org.chromium.components.feature_engagement.Tracker;
+import org.chromium.ui.base.TestActivity;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+
+/** Unit tests for the TabSwitcherMessageManager. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class TabSwitcherMessageManagerUnitTest {
+    private static final int TAB1_ID = 456;
+    private static final int TAB2_ID = 789;
+
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Rule
+    public ActivityScenarioRule<TestActivity> mActivityScenarioRule =
+            new ActivityScenarioRule<>(TestActivity.class);
+
+    @Mock private Tracker mTracker;
+    @Mock private Profile mProfile;
+    @Mock private TabModel mTabModel;
+    @Mock private TabModelFilter mTabModelFilter;
+    @Mock private ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
+    @Mock private MultiWindowModeStateDispatcher mMultiWindowModeStateDispatcher;
+    @Mock private SnackbarManager mSnackbarManager;
+    @Mock private ModalDialogManager mModalDialogManager;
+    @Mock private TabListCoordinator mTabListCoordinator;
+    @Mock private TabListEditorController mTabListEditorController;
+    @Mock private PriceWelcomeMessageReviewActionProvider mPriceWelcomeMessageReviewActionProvider;
+    @Mock private PriceMessageService mPriceMessageService;
+    @Mock private MessageUpdateObserver mMessageUpdateObserver;
+
+    @Captor private ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor;
+
+    @Captor
+    private ArgumentCaptor<MultiWindowModeStateDispatcher.MultiWindowModeObserver>
+            mMultiWindowModeObserverCaptor;
+
+    private final ObservableSupplierImpl<TabModelFilter> mCurrentTabModelFilterSupplier =
+            new ObservableSupplierImpl<>();
+
+    private TabSwitcherMessageManager mMessageManager;
+    private MockTab mTab1;
+    private MockTab mTab2;
+
+    @Before
+    public void setUp() {
+        PriceTrackingFeatures.setPriceTrackingEnabledForTesting(true);
+        PriceTrackingFeatures.setIsSignedInAndSyncEnabledForTesting(true);
+
+        TrackerFactory.setTrackerForTests(mTracker);
+
+        mTab1 = MockTab.createAndInitialize(TAB1_ID, mProfile);
+        mTab2 = MockTab.createAndInitialize(TAB2_ID, mProfile);
+
+        doReturn(true)
+                .when(mMultiWindowModeStateDispatcher)
+                .addObserver(mMultiWindowModeObserverCaptor.capture());
+        doNothing().when(mTabModelFilter).addObserver(mTabModelObserverCaptor.capture());
+        doReturn(mTabModel).when(mTabModelFilter).getTabModel();
+        mCurrentTabModelFilterSupplier.set(mTabModelFilter);
+        Profile.setLastUsedProfileForTesting(mProfile);
+
+        mActivityScenarioRule.getScenario().onActivity(this::onActivityReady);
+    }
+
+    private void onActivityReady(Activity activity) {
+        FrameLayout container = new FrameLayout(activity);
+        activity.setContentView(container);
+        mMessageManager =
+                new TabSwitcherMessageManager(
+                        activity,
+                        mActivityLifecycleDispatcher,
+                        mCurrentTabModelFilterSupplier,
+                        container,
+                        mMultiWindowModeStateDispatcher,
+                        mSnackbarManager,
+                        mModalDialogManager,
+                        mTabListCoordinator,
+                        LazyOneshotSupplier.fromValue(mTabListEditorController),
+                        mPriceWelcomeMessageReviewActionProvider,
+                        TabListMode.GRID);
+        mMessageManager.addObserver(mMessageUpdateObserver);
+
+        mMessageManager.setPriceMessageServiceForTesting(mPriceMessageService);
+        mMessageManager.initWithNative();
+
+        assertTrue(mCurrentTabModelFilterSupplier.hasObservers());
+    }
+
+    @After
+    public void tearDown() {
+        mMessageManager.removeObserver(mMessageUpdateObserver);
+        mMessageManager.destroy();
+        assertFalse(mCurrentTabModelFilterSupplier.hasObservers());
+    }
+
+    @Test
+    @SmallTest
+    public void testBeforeReset() {
+        mMessageManager.beforeReset();
+        verify(mPriceMessageService).invalidateMessage();
+    }
+
+    @Test
+    @SmallTest
+    public void testAfterReset() {
+        mMessageManager.afterReset(0);
+        verify(mMessageUpdateObserver).onRemoveAllAppendedMessage();
+        verify(mMessageUpdateObserver, never()).onAppendedMessage();
+
+        mMessageManager.afterReset(1);
+        verify(mMessageUpdateObserver, times(2)).onRemoveAllAppendedMessage();
+        verify(mMessageUpdateObserver).onAppendedMessage();
+    }
+
+    @Test
+    @SmallTest
+    public void removeMessageItemsWhenCloseLastTab() {
+        // Mock that mTab1 is not the only tab in the current tab model and it will be closed.
+        doReturn(2).when(mTabModel).getCount();
+        mTabModelObserverCaptor.getValue().willCloseTab(mTab1, false, true);
+        verify(mTabListCoordinator, never()).removeSpecialListItem(anyInt(), anyInt());
+
+        // Mock that mTab1 is the only tab in the current tab model and it will be closed.
+        doReturn(1).when(mTabModel).getCount();
+        mTabModelObserverCaptor.getValue().willCloseTab(mTab1, false, true);
+        verify(mTabListCoordinator)
+                .removeSpecialListItem(
+                        TabProperties.UiType.MESSAGE, MessageService.MessageType.ALL);
+        verify(mTabListCoordinator)
+                .removeSpecialListItem(
+                        TabProperties.UiType.LARGE_MESSAGE,
+                        MessageService.MessageType.INCOGNITO_REAUTH_PROMO_MESSAGE);
+        verify(mTabListCoordinator)
+                .removeSpecialListItem(
+                        TabProperties.UiType.LARGE_MESSAGE,
+                        MessageService.MessageType.TAB_SUGGESTION);
+        verify(mMessageUpdateObserver).onRemoveAllAppendedMessage();
+    }
+
+    @Test
+    @SmallTest
+    public void restoreMessageItemsWhenUndoLastTabClosure() {
+        // Mock that mTab1 was not the only tab in the current tab model and its closure will be
+        // undone.
+        doReturn(2).when(mTabModel).getCount();
+        mTabModelObserverCaptor.getValue().tabClosureUndone(mTab1);
+        verify(mMessageUpdateObserver, never()).onRestoreAllAppendedMessage();
+
+        // Mock that mTab1 was the only tab in the current tab model and its closure will be undone.
+        doReturn(1).when(mTabModel).getCount();
+        mTabModelObserverCaptor.getValue().tabClosureUndone(mTab1);
+        verify(mMessageUpdateObserver).onRestoreAllAppendedMessage();
+    }
+
+    @Test
+    @SmallTest
+    public void enterMultiWindowMode() {
+        mMultiWindowModeObserverCaptor.getValue().onMultiWindowModeChanged(true);
+
+        verify(mTabListCoordinator)
+                .removeSpecialListItem(
+                        TabProperties.UiType.MESSAGE, MessageService.MessageType.ALL);
+        verify(mTabListCoordinator)
+                .removeSpecialListItem(
+                        TabProperties.UiType.LARGE_MESSAGE,
+                        MessageService.MessageType.INCOGNITO_REAUTH_PROMO_MESSAGE);
+        verify(mTabListCoordinator)
+                .removeSpecialListItem(
+                        TabProperties.UiType.LARGE_MESSAGE,
+                        MessageService.MessageType.TAB_SUGGESTION);
+        verify(mMessageUpdateObserver).onRemoveAllAppendedMessage();
+    }
+
+    @Test
+    @SmallTest
+    public void exitMultiWindowMode() {
+        mMultiWindowModeObserverCaptor.getValue().onMultiWindowModeChanged(false);
+
+        verify(mMessageUpdateObserver).onRestoreAllAppendedMessage();
+    }
+
+    @Test
+    @SmallTest
+    public void removePriceWelcomeMessageWhenCloseBindingTab() {
+        doReturn(1).when(mTabModel).getCount();
+        doReturn(TAB1_ID).when(mPriceMessageService).getBindingTabId();
+        mTabModelObserverCaptor.getValue().willCloseTab(mTab1, false, true);
+        verify(mMessageUpdateObserver, never()).onRemovePriceWelcomeMessage();
+
+        doReturn(2).when(mTabModel).getCount();
+        doReturn(TAB2_ID).when(mPriceMessageService).getBindingTabId();
+        mTabModelObserverCaptor.getValue().willCloseTab(mTab1, false, true);
+        verify(mMessageUpdateObserver, never()).onRemovePriceWelcomeMessage();
+
+        doReturn(2).when(mTabModel).getCount();
+        doReturn(TAB1_ID).when(mPriceMessageService).getBindingTabId();
+        mTabModelObserverCaptor.getValue().willCloseTab(mTab1, false, true);
+        verify(mMessageUpdateObserver).onRemovePriceWelcomeMessage();
+    }
+
+    @Test
+    @SmallTest
+    public void restorePriceWelcomeMessageWhenUndoBindingTabClosure() {
+        doReturn(1).when(mTabModel).getCount();
+        doReturn(TAB1_ID).when(mPriceMessageService).getBindingTabId();
+        mTabModelObserverCaptor.getValue().tabClosureUndone(mTab1);
+        verify(mMessageUpdateObserver).onRestorePriceWelcomeMessage();
+
+        doReturn(2).when(mTabModel).getCount();
+        doReturn(TAB2_ID).when(mPriceMessageService).getBindingTabId();
+        mTabModelObserverCaptor.getValue().tabClosureUndone(mTab1);
+        // Not called a second time.
+        verify(mMessageUpdateObserver).onRestorePriceWelcomeMessage();
+    }
+
+    @Test
+    @SmallTest
+    public void invalidatePriceWelcomeMessageWhenBindingTabClosureCommitted() {
+        doReturn(TAB2_ID).when(mPriceMessageService).getBindingTabId();
+        mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1);
+        verify(mPriceMessageService, never()).invalidateMessage();
+
+        doReturn(TAB1_ID).when(mPriceMessageService).getBindingTabId();
+        mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1);
+        verify(mPriceMessageService).invalidateMessage();
+    }
+}
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoReauthPromoCardRenderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoReauthPromoCardRenderTest.java
index 7f7987f..ad3546b 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoReauthPromoCardRenderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoReauthPromoCardRenderTest.java
@@ -81,7 +81,7 @@
 
         createTabs(cta, true, 1);
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
         mRenderTestRule.render(
                 cta.findViewById(R.id.large_message_card_item), "incognito_reauth_promo_portrait");
@@ -98,7 +98,7 @@
         ActivityTestUtils.rotateActivityToOrientation(cta, Configuration.ORIENTATION_LANDSCAPE);
 
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
         mRenderTestRule.render(
                 cta.findViewById(R.id.large_message_card_item), "incognito_reauth_promo_landscape");
@@ -113,7 +113,7 @@
 
         createTabs(cta, true, 1);
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
         onView(withText(R.string.incognito_reauth_lock_action_text)).perform(click());
         onView(withId(R.id.snackbar)).check(matches(isDisplayed()));
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceAlertsMessageCardTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceAlertsMessageCardTest.java
index 07914dc..241b78d 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceAlertsMessageCardTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceAlertsMessageCardTest.java
@@ -146,7 +146,7 @@
         assertTrue(isPriceAlertsMessageCardEnabled());
 
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
     }
 
@@ -199,7 +199,7 @@
         assertNull(mPriceDropNotificationManager.getNotificationChannel());
 
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
 
         onView(
@@ -228,7 +228,7 @@
         mMockNotificationManager.setNotificationsEnabled(false);
 
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
 
         onView(
@@ -256,7 +256,7 @@
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
 
         onView(allOf(withId(R.id.close_button), withParent(withId(R.id.large_message_card_view))))
@@ -277,7 +277,7 @@
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
 
         RecyclerView.ViewHolder viewHolder =
@@ -307,11 +307,12 @@
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
 
         closeFirstTabInTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(() -> !TabSwitcherCoordinator.hasAppendedMessagesForTesting());
+        CriteriaHelper.pollUiThread(
+                () -> !TabSwitcherMessageManager.hasAppendedMessagesForTesting());
         verifyTabSwitcherCardCount(cta, 0);
         onView(withId(R.id.large_message_card_item)).check(doesNotExist());
 
@@ -320,7 +321,7 @@
                 InstrumentationRegistry.getInstrumentation(), cta, false, true);
         enterTabSwitcher(cta);
         verifyTabSwitcherCardCount(cta, 1);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
     }
 
@@ -333,7 +334,7 @@
 
         for (int i = 0; i < 10; i++) {
             enterTabSwitcher(cta);
-            CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+            CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
             onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
             assertTrue(isPriceAlertsMessageCardEnabled());
             clickFirstCardFromTabSwitcher(cta);
@@ -358,7 +359,7 @@
         mMockNotificationManager.setNotificationsEnabled(true);
 
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
 
         mRenderTestRule.render(
@@ -376,7 +377,7 @@
         mMockNotificationManager.setNotificationsEnabled(false);
 
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
 
         mRenderTestRule.render(
@@ -396,7 +397,7 @@
 
         ActivityTestUtils.rotateActivityToOrientation(cta, Configuration.ORIENTATION_LANDSCAPE);
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
 
         mRenderTestRule.render(
@@ -415,7 +416,7 @@
 
         ActivityTestUtils.rotateActivityToOrientation(cta, Configuration.ORIENTATION_LANDSCAPE);
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
 
         mRenderTestRule.render(
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
index 9a8d73e2..062c610d 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
@@ -1647,13 +1647,14 @@
                 || isTablet(cta)) {
             return;
         }
-        final @ColorInt int scrimDefaultColor = cta.getColor(R.color.default_scrim_color);
-        final @ColorInt int navigationBarColor = SemanticColorUtils.getBottomSystemNavColor(cta);
+        @ColorInt int scrimDefaultColor = cta.getColor(R.color.default_scrim_color);
+        @ColorInt int navigationBarColor = SemanticColorUtils.getBottomSystemNavColor(cta);
         float scrimColorAlpha = (scrimDefaultColor >>> 24) / 255f;
-        int scrimColorOpaque = scrimDefaultColor & 0xFF000000;
+        @ColorInt int scrimColorOpaque = scrimDefaultColor | 0xFF000000;
+        @ColorInt
         int navigationBarColorWithScrimOverlay =
                 ColorUtils.getColorWithOverlay(
-                        navigationBarColor, scrimColorOpaque, scrimColorAlpha, true);
+                        navigationBarColor, scrimColorOpaque, scrimColorAlpha);
 
         assertEquals(cta.getWindow().getNavigationBarColor(), navigationBarColorWithScrimOverlay);
         assertNotEquals(navigationBarColor, navigationBarColorWithScrimOverlay);
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIncognitoReauthPromoTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIncognitoReauthPromoTest.java
index 08a4ade..bd31f9e 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIncognitoReauthPromoTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIncognitoReauthPromoTest.java
@@ -77,7 +77,7 @@
     @After
     public void tearDown() {
         TestThreadUtils.runOnUiThreadBlocking(
-                TabSwitcherCoordinator::resetHasAppendedMessagesForTesting);
+                TabSwitcherMessageManager::resetHasAppendedMessagesForTesting);
     }
 
     @Test
@@ -90,7 +90,7 @@
         enterTabSwitcher(cta);
 
         assertTrue(cta.getTabModelSelector().getCurrentModel().isIncognito());
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onViewWaiting(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
     }
 
@@ -104,7 +104,7 @@
         enterTabSwitcher(cta);
 
         assertTrue(cta.getTabModelSelector().getCurrentModel().isIncognito());
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onViewWaiting(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
 
         onView(withText(R.string.incognito_reauth_lock_action_text)).perform(click());
@@ -136,7 +136,7 @@
         enterTabSwitcher(cta);
 
         assertTrue(cta.getTabModelSelector().getCurrentModel().isIncognito());
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onViewWaiting(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
 
         switchTabModel(cta, false);
@@ -154,7 +154,7 @@
         enterTabSwitcher(cta);
 
         assertTrue(cta.getTabModelSelector().getCurrentModel().isIncognito());
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onViewWaiting(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
         onView(withId(R.id.secondary_action_button)).perform(click());
 
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
index 0999172..f22aef9 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
@@ -137,7 +137,7 @@
     public void tearDown() {
         ActivityTestUtils.clearActivityOrientation(mActivityTestRule.getActivity());
         TestThreadUtils.runOnUiThreadBlocking(
-                TabSwitcherCoordinator::resetHasAppendedMessagesForTesting);
+                TabSwitcherMessageManager::resetHasAppendedMessagesForTesting);
     }
 
     @Test
@@ -147,7 +147,7 @@
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         // Check the IPH message card is showing and open the IPH dialog.
         onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
         onView(allOf(withId(R.id.action_button), withParent(withId(R.id.tab_grid_message_item))))
@@ -197,7 +197,7 @@
         createTabs(cta, true, 1);
         enterTabSwitcher(cta);
         assertTrue(cta.getTabModelSelector().getCurrentModel().isIncognito());
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
     }
 
@@ -208,7 +208,7 @@
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
 
         // Restart chrome to verify that IPH message card is still there.
@@ -216,7 +216,7 @@
         mActivityTestRule.startMainActivityFromLauncher();
         cta = mActivityTestRule.getActivity();
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
 
         // Remove the message card and dismiss the feature by clicking close button.
@@ -240,7 +240,7 @@
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
 
         ChromeRenderTestRule.sanitize(cta.findViewById(R.id.tab_grid_message_item));
@@ -257,7 +257,7 @@
 
         enterTabSwitcher(cta);
         ActivityTestUtils.rotateActivityToOrientation(cta, Configuration.ORIENTATION_LANDSCAPE);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onViewWaiting(
                         allOf(
                                 withParent(withId(TabUiTestHelper.getTabSwitcherParentId(cta))),
@@ -278,7 +278,7 @@
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onViewWaiting(
                         allOf(
                                 withId(R.id.action_button),
@@ -309,7 +309,7 @@
 
         enterTabSwitcher(cta);
         ActivityTestUtils.rotateActivityToOrientation(cta, Configuration.ORIENTATION_LANDSCAPE);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         // Scroll to the position of the IPH entrance so that it is completely showing for Espresso
         // click.
         onViewWaiting(
@@ -342,12 +342,13 @@
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
 
         enterTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
 
         // Close the last tab in tab switcher and the IPH item should not be showing.
         closeFirstTabInTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(() -> !TabSwitcherCoordinator.hasAppendedMessagesForTesting());
+        CriteriaHelper.pollUiThread(
+                () -> !TabSwitcherMessageManager.hasAppendedMessagesForTesting());
         verifyTabSwitcherCardCount(cta, 0);
         onView(withId(R.id.tab_grid_message_item)).check(doesNotExist());
 
@@ -357,7 +358,8 @@
 
         // Close the last tab in the tab switcher.
         closeFirstTabInTabSwitcher(cta);
-        CriteriaHelper.pollUiThread(() -> !TabSwitcherCoordinator.hasAppendedMessagesForTesting());
+        CriteriaHelper.pollUiThread(
+                () -> !TabSwitcherMessageManager.hasAppendedMessagesForTesting());
         verifyTabSwitcherCardCount(cta, 0);
         onView(withId(R.id.tab_grid_message_item)).check(doesNotExist());
 
@@ -366,7 +368,7 @@
                 InstrumentationRegistry.getInstrumentation(), cta, false, true);
         enterTabSwitcher(cta);
         verifyTabSwitcherCardCount(cta, 1);
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
     }
 
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardTest.java
index 18a4e01..c84a843 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardTest.java
@@ -141,7 +141,7 @@
 
     private void enteringTabSwitcherAndVerifySuggestionIsShown(String suggestionText) {
         TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(allOf(withParent(withId(R.id.tab_grid_message_item)), withText(suggestionText)))
                 .check(matches(isDisplayed()));
     }
@@ -260,7 +260,7 @@
         CriteriaHelper.pollUiThread(TabSuggestionMessageService::isSuggestionAvailableForTesting);
 
         TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
 
         dismissSuggestion(false);
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherAndStartSurfaceLayoutTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherAndStartSurfaceLayoutTest.java
index 5b7dba6..253da4e 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherAndStartSurfaceLayoutTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherAndStartSurfaceLayoutTest.java
@@ -1025,9 +1025,10 @@
         enterGTSWithThumbnailChecking();
 
         // TODO(meiliang): Avoid using static variable for tracking state,
-        // TabSwitcherCoordinator::hasAppendedMessagesForTesting. Instead, we can query the number
+        // TabSwitcherMessageManager::hasAppendedMessagesForTesting. Instead, we can query the
+        // number
         // of items that the inner model of the TabSwitcher has.
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         ViewUtils.onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
         onView(allOf(withId(R.id.close_button), withParent(withId(R.id.tab_grid_message_item))))
                 .perform(click());
@@ -1059,7 +1060,7 @@
         mayEnterGTSAndLeave(mActivityTestRule.getActivity());
         enterGTSWithThumbnailChecking();
 
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         ViewUtils.onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
         ViewUtils.onViewWaiting(
                         allOf(
@@ -1135,7 +1136,7 @@
 
         mayEnterGTSAndLeave(mActivityTestRule.getActivity());
         enterGTSWithThumbnailChecking();
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         ViewUtils.onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
 
         closeFirstTabInTabSwitcher(mActivityTestRule.getActivity());
@@ -1178,7 +1179,7 @@
 
         mayEnterGTSAndLeave(mActivityTestRule.getActivity());
         enterGTSWithThumbnailChecking();
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
 
         // Force portrait mode since the device can be wrongly in landscape. See crbug/1063639.
         ActivityTestUtils.rotateActivityToOrientation(cta, Configuration.ORIENTATION_PORTRAIT);
@@ -1600,7 +1601,7 @@
         // TabObserver#didFirstVisuallyNonEmptyPaint and invalidates the suggestion. Do the
         // thumbnail checking here is to ensure the suggestion is valid when entering tab switcher.
         enterGTSWithThumbnailChecking();
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
         onView(allOf(withId(R.id.action_button), withParent(withId(R.id.tab_grid_message_item))))
                 .perform(click());
@@ -2369,14 +2370,14 @@
 
         mayEnterGTSAndLeave(mActivityTestRule.getActivity());
         enterGTSWithThumbnailChecking();
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         onView(allOf(withText(suggestionMessage), withParent(withId(R.id.tab_grid_message_item))))
                 .check(matches(isDisplayed()));
         leaveGTSAndVerifyThumbnailsAreReleased();
 
         // With soft or hard clean up depends on the soft-cleanup-delay and cleanup-delay params.
         enterGTSWithThumbnailChecking();
-        CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
+        CriteriaHelper.pollUiThread(TabSwitcherMessageManager::hasAppendedMessagesForTesting);
         // This will fail with error "matched multiple views" when there is more than one suggestion
         // message card.
         onView(allOf(withText(suggestionMessage), withParent(withId(R.id.tab_grid_message_item))))
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/IphMessageServiceUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/IphMessageServiceUnitTest.java
index 595f0ad3..75f6d6c 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/IphMessageServiceUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/IphMessageServiceUnitTest.java
@@ -30,7 +30,7 @@
 @RunWith(BaseRobolectricTestRunner.class)
 @Batch(Batch.UNIT_TESTS)
 public class IphMessageServiceUnitTest {
-    @Mock private TabSwitcherCoordinator.IphController mIphController;
+    @Mock private TabSwitcherIphController mIphController;
 
     @Mock private Profile mProfile;
 
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
index 614d86b5..9b1eddb 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
@@ -42,6 +42,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
 import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.base.Callback;
 import org.chromium.base.supplier.ObservableSupplierImpl;
@@ -603,6 +604,7 @@
 
         assertThat(mModel.get(TabGridPanelProperties.HEADER_TITLE), equalTo(DIALOG_TITLE1));
         verify(mTabSwitcherResetHandler).resetWithTabList(mTabGroupModelFilter, false);
+        ShadowLooper.runUiThreadTasks();
         verify(mSnackbarManager).dismissSnackbars(eq(mMediator), eq(TAB1_ID));
     }
 
@@ -626,6 +628,7 @@
         assertThat(
                 mModel.get(TabGridPanelProperties.HEADER_TITLE), equalTo(CUSTOMIZED_DIALOG_TITLE));
         verify(mTabSwitcherResetHandler).resetWithTabList(mTabGroupModelFilter, false);
+        ShadowLooper.runUiThreadTasks();
         verify(mSnackbarManager).dismissSnackbars(eq(mMediator), eq(TAB2_ID));
     }
 
@@ -642,6 +645,7 @@
         // Dialog should still be hidden.
         assertThat(mModel.get(TabGridPanelProperties.IS_DIALOG_VISIBLE), equalTo(false));
         verify(mTabSwitcherResetHandler, never()).resetWithTabList(mTabGroupModelFilter, false);
+        ShadowLooper.runUiThreadTasks();
         verify(mSnackbarManager).dismissSnackbars(eq(mMediator), eq(TAB1_ID));
     }
 
@@ -649,6 +653,7 @@
     public void tabClosureCommitted() {
         mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1);
 
+        ShadowLooper.runUiThreadTasks();
         verify(mSnackbarManager).dismissSnackbars(eq(mMediator), eq(TAB1_ID));
     }
 
@@ -657,6 +662,7 @@
         List<Tab> tabs = Arrays.asList(mTab1, mTab2);
         mTabModelObserverCaptor.getValue().onFinishingMultipleTabClosure(tabs);
 
+        ShadowLooper.runUiThreadTasks();
         verify(mSnackbarManager).dismissSnackbars(eq(mMediator), eq(tabs));
     }
 
@@ -665,6 +671,7 @@
         List<Tab> tabs = Arrays.asList(mTab1);
         mTabModelObserverCaptor.getValue().onFinishingMultipleTabClosure(tabs);
 
+        ShadowLooper.runUiThreadTasks();
         verify(mSnackbarManager).dismissSnackbars(eq(mMediator), eq(TAB1_ID));
     }
 
@@ -672,6 +679,7 @@
     public void allTabsClosureCommitted() {
         mTabModelObserverCaptor.getValue().allTabsClosureCommitted(false);
 
+        ShadowLooper.runUiThreadTasks();
         verify(mSnackbarManager).dismissSnackbars(eq(mMediator));
     }
 
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
index f63f2bca5..9b2a65fb 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -87,6 +87,7 @@
 import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.shared_preferences.SharedPreferencesManager;
 import org.chromium.base.supplier.ObservableSupplierImpl;
+import org.chromium.base.supplier.Supplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.build.BuildConfig;
@@ -253,7 +254,7 @@
     @Mock OptimizationGuideBridge.Natives mOptimizationGuideBridgeJniMock;
     @Mock TabListMediator.TabGridAccessibilityHelper mTabGridAccessibilityHelper;
     @Mock TemplateUrlService mTemplateUrlService;
-    @Mock TabSwitcherMediator.PriceWelcomeMessageController mPriceWelcomeMessageController;
+    @Mock PriceWelcomeMessageController mPriceWelcomeMessageController;
     @Mock ShoppingPersistedTabData mShoppingPersistedTabData;
     @Mock SelectionDelegate<Integer> mSelectionDelegate;
 
@@ -2596,7 +2597,7 @@
     public void testMaybeShowPriceWelcomeMessage() {
         prepareTestMaybeShowPriceWelcomeMessage();
         ShoppingPersistedTabDataFetcher fetcher =
-                new ShoppingPersistedTabDataFetcher(mTab1, mPriceWelcomeMessageController);
+                new ShoppingPersistedTabDataFetcher(mTab1, () -> mPriceWelcomeMessageController);
         fetcher.maybeShowPriceWelcomeMessage(mShoppingPersistedTabData);
         verify(mPriceWelcomeMessageController, times(1)).showPriceWelcomeMessage(mPriceTabData);
     }
@@ -2605,7 +2606,7 @@
     public void testMaybeShowPriceWelcomeMessage_MessageDisabled() {
         prepareTestMaybeShowPriceWelcomeMessage();
         ShoppingPersistedTabDataFetcher fetcher =
-                new ShoppingPersistedTabDataFetcher(mTab1, mPriceWelcomeMessageController);
+                new ShoppingPersistedTabDataFetcher(mTab1, () -> mPriceWelcomeMessageController);
 
         PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeBoolean(
                 PriceTrackingUtilities.PRICE_WELCOME_MESSAGE_CARD, false);
@@ -2618,7 +2619,7 @@
     }
 
     @Test
-    public void testMaybeShowPriceWelcomeMessage_NullParameter() {
+    public void testMaybeShowPriceWelcomeMessage_SupplierIsNull() {
         prepareTestMaybeShowPriceWelcomeMessage();
 
         new ShoppingPersistedTabDataFetcher(mTab1, null)
@@ -2627,10 +2628,20 @@
     }
 
     @Test
+    public void testMaybeShowPriceWelcomeMessage_SupplierContainsNull() {
+        prepareTestMaybeShowPriceWelcomeMessage();
+
+        Supplier<PriceWelcomeMessageController> supplier = () -> null;
+        new ShoppingPersistedTabDataFetcher(mTab1, supplier)
+                .maybeShowPriceWelcomeMessage(mShoppingPersistedTabData);
+        verify(mPriceWelcomeMessageController, times(0)).showPriceWelcomeMessage(mPriceTabData);
+    }
+
+    @Test
     public void testMaybeShowPriceWelcomeMessage_NoPriceDrop() {
         prepareTestMaybeShowPriceWelcomeMessage();
         ShoppingPersistedTabDataFetcher fetcher =
-                new ShoppingPersistedTabDataFetcher(mTab1, mPriceWelcomeMessageController);
+                new ShoppingPersistedTabDataFetcher(mTab1, () -> mPriceWelcomeMessageController);
 
         fetcher.maybeShowPriceWelcomeMessage(null);
         verify(mPriceWelcomeMessageController, times(0)).showPriceWelcomeMessage(mPriceTabData);
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
index 4f1dc01..ecf3e76 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
@@ -64,7 +64,6 @@
 import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthController;
 import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthManager;
 import org.chromium.chrome.browser.layouts.LayoutType;
-import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.chrome.browser.tabmodel.TabModel;
@@ -132,9 +131,6 @@
     @Mock private CompositorViewHolder mCompositorViewHolder;
     @Mock private TabSwitcher.OnTabSelectingListener mOnTabSelectingListener;
     @Mock private TabGridDialogMediator.DialogController mTabGridDialogController;
-    @Mock private TabSwitcherMediator.MessageItemsController mMessageItemsController;
-    @Mock private TabSwitcherMediator.PriceWelcomeMessageController mPriceWelcomeMessageController;
-    @Mock private MultiWindowModeStateDispatcher mMultiWindowModeStateDispatcher;
     @Mock private PriceMessageService mPriceMessageService;
     @Mock private IncognitoReauthController mIncognitoReauthController;
     @Mock private View mCustomViewMock;
@@ -147,9 +143,6 @@
     private ArgumentCaptor<BrowserControlsStateProvider.Observer>
             mBrowserControlsStateProviderObserverCaptor;
 
-    @Captor
-    private ArgumentCaptor<MultiWindowModeStateDispatcher.MultiWindowModeObserver>
-            mMultiWindowModeObserverCaptor;
 
     @Captor
     private ArgumentCaptor<IncognitoReauthManager.IncognitoReauthCallback>
@@ -217,9 +210,6 @@
         doNothing()
                 .when(mBrowserControlsStateProvider)
                 .addObserver(mBrowserControlsStateProviderObserverCaptor.capture());
-        doReturn(true)
-                .when(mMultiWindowModeStateDispatcher)
-                .addObserver(mMultiWindowModeObserverCaptor.capture());
         doReturn(mEditorControllerBackPressChangedSupplier)
                 .when(mEditorController)
                 .getHandleBackPressChangedSupplier();
@@ -242,9 +232,6 @@
                         mBrowserControlsStateProvider,
                         mCompositorViewHolder,
                         null,
-                        mMessageItemsController,
-                        mPriceWelcomeMessageController,
-                        mMultiWindowModeStateDispatcher,
                         mHandler,
                         TabListCoordinator.TabListMode.GRID,
                         mIncognitoReauthControllerSupplier,
@@ -568,83 +555,6 @@
     }
 
     @Test
-    public void removeMessageItemsWhenCloseLastTab() {
-        initAndAssertAllProperties();
-        // Mock that mTab1 is not the only tab in the current tab model and it will be closed.
-        doReturn(2).when(mTabModel).getCount();
-        mTabModelObserverCaptor.getValue().willCloseTab(mTab1, false, true);
-        verify(mMessageItemsController, never()).removeAllAppendedMessage();
-
-        // Mock that mTab1 is the only tab in the current tab model and it will be closed.
-        doReturn(1).when(mTabModel).getCount();
-        mTabModelObserverCaptor.getValue().willCloseTab(mTab1, false, true);
-        verify(mMessageItemsController).removeAllAppendedMessage();
-    }
-
-    @Test
-    public void restoreMessageItemsWhenUndoLastTabClosure() {
-        initAndAssertAllProperties();
-        // Mock that mTab1 was not the only tab in the current tab model and its closure will be
-        // undone.
-        doReturn(2).when(mTabModel).getCount();
-        mTabModelObserverCaptor.getValue().tabClosureUndone(mTab1);
-        verify(mMessageItemsController, never()).restoreAllAppendedMessage();
-
-        // Mock that mTab1 was the only tab in the current tab model and its closure will be undone.
-        doReturn(1).when(mTabModel).getCount();
-        mTabModelObserverCaptor.getValue().tabClosureUndone(mTab1);
-        verify(mMessageItemsController).restoreAllAppendedMessage();
-    }
-
-    @Test
-    public void removePriceWelcomeMessageWhenCloseBindingTab() {
-        mMediator.setPriceMessageService(mPriceMessageService);
-
-        doReturn(1).when(mTabModel).getCount();
-        doReturn(TAB1_ID).when(mPriceMessageService).getBindingTabId();
-        mTabModelObserverCaptor.getValue().willCloseTab(mTab1, false, true);
-        verify(mPriceWelcomeMessageController, times(0)).removePriceWelcomeMessage();
-
-        doReturn(2).when(mTabModel).getCount();
-        doReturn(TAB2_ID).when(mPriceMessageService).getBindingTabId();
-        mTabModelObserverCaptor.getValue().willCloseTab(mTab1, false, true);
-        verify(mPriceWelcomeMessageController, times(0)).removePriceWelcomeMessage();
-
-        doReturn(2).when(mTabModel).getCount();
-        doReturn(TAB1_ID).when(mPriceMessageService).getBindingTabId();
-        mTabModelObserverCaptor.getValue().willCloseTab(mTab1, false, true);
-        verify(mPriceWelcomeMessageController, times(1)).removePriceWelcomeMessage();
-    }
-
-    @Test
-    public void restorePriceWelcomeMessageWhenUndoBindingTabClosure() {
-        mMediator.setPriceMessageService(mPriceMessageService);
-
-        doReturn(1).when(mTabModel).getCount();
-        doReturn(TAB1_ID).when(mPriceMessageService).getBindingTabId();
-        mTabModelObserverCaptor.getValue().tabClosureUndone(mTab1);
-        verify(mPriceWelcomeMessageController, times(1)).restorePriceWelcomeMessage();
-
-        doReturn(2).when(mTabModel).getCount();
-        doReturn(TAB2_ID).when(mPriceMessageService).getBindingTabId();
-        mTabModelObserverCaptor.getValue().tabClosureUndone(mTab1);
-        verify(mPriceWelcomeMessageController, times(1)).restorePriceWelcomeMessage();
-    }
-
-    @Test
-    public void invalidatePriceWelcomeMessageWhenBindingTabClosureCommitted() {
-        mMediator.setPriceMessageService(mPriceMessageService);
-
-        doReturn(TAB2_ID).when(mPriceMessageService).getBindingTabId();
-        mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1);
-        verify(mPriceMessageService, times(0)).invalidateMessage();
-
-        doReturn(TAB1_ID).when(mPriceMessageService).getBindingTabId();
-        mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1);
-        verify(mPriceMessageService, times(1)).invalidateMessage();
-    }
-
-    @Test
     public void testScrollToTab() {
         initAndAssertAllProperties();
         mMediator.scrollToTab(0);
@@ -958,9 +868,6 @@
                 mBrowserControlsStateProvider,
                 mCompositorViewHolder,
                 null,
-                mMessageItemsController,
-                mPriceWelcomeMessageController,
-                mMultiWindowModeStateDispatcher,
                 mHandler,
                 TabListCoordinator.TabListMode.GRID,
                 mIncognitoReauthControllerSupplier,
@@ -979,9 +886,6 @@
                 mBrowserControlsStateProvider,
                 mCompositorViewHolder,
                 null,
-                mMessageItemsController,
-                mPriceWelcomeMessageController,
-                mMultiWindowModeStateDispatcher,
                 mHandler,
                 TabListCoordinator.TabListMode.STRIP,
                 mIncognitoReauthControllerSupplier,
@@ -993,24 +897,6 @@
     }
 
     @Test
-    public void enterMultiWindowMode() {
-        initAndAssertAllProperties();
-
-        mMultiWindowModeObserverCaptor.getValue().onMultiWindowModeChanged(true);
-
-        verify(mMessageItemsController).removeAllAppendedMessage();
-    }
-
-    @Test
-    public void exitMultiWindowMode() {
-        initAndAssertAllProperties();
-
-        mMultiWindowModeObserverCaptor.getValue().onMultiWindowModeChanged(false);
-
-        verify(mMessageItemsController).restoreAllAppendedMessage();
-    }
-
-    @Test
     @EnableFeatures(ChromeFeatureList.BACK_GESTURE_REFACTOR)
     public void testBackPress() {
         initAndAssertAllProperties();
diff --git a/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chrome/android/features/tab_ui/tab_management_java_sources.gni
index c5361551..f01bcea 100644
--- a/chrome/android/features/tab_ui/tab_management_java_sources.gni
+++ b/chrome/android/features/tab_ui/tab_management_java_sources.gni
@@ -52,6 +52,7 @@
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceCardView.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceMessageCardViewModel.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceMessageService.java",
+  "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageController.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SelectableTabGridView.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java",
@@ -104,7 +105,9 @@
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardViewModel.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageService.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java",
+  "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherIphController.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java",
+  "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPane.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneDrawableCoordinator.java",
@@ -146,6 +149,7 @@
 ]
 
 tab_management_junit_java_sources = [
+  "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManagerUnitTest.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneDrawableCoordinatorUnitTest.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneDrawableMediatorUnitTest.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneDrawableViewBinderUnitTest.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkFolderSelectActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkFolderSelectActivity.java
index 3f7e075..86a7dcd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkFolderSelectActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkFolderSelectActivity.java
@@ -297,7 +297,7 @@
 
     private void moveBookmarksAndFinish(List<BookmarkId> bookmarkIds, BookmarkId parentId) {
         BookmarkUtils.moveBookmarksToParent(mModel, bookmarkIds, parentId);
-        BookmarkUtils.setLastUsedParent(this, parentId);
+        BookmarkUtils.setLastUsedParent(parentId);
         finishActivity(bookmarkIds);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
index 8ed6626..4cfbf3f9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
@@ -19,7 +19,6 @@
 import org.chromium.chrome.browser.back_press.MinimizeAppAndCloseTabBackPressHandler;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
-import org.chromium.chrome.browser.customtabs.features.branding.BrandingController;
 import org.chromium.chrome.browser.feed.FeedPlaceholderLayout;
 import org.chromium.chrome.browser.firstrun.FirstRunUtils;
 import org.chromium.chrome.browser.flags.CachedFieldTrialParameter;
@@ -82,10 +81,6 @@
 
         List<CachedFieldTrialParameter> fieldTrialsToCache =
                 List.of(
-                        BrandingController.BRANDING_CADENCE_MS,
-                        BrandingController.MAX_BLANK_TOOLBAR_TIMEOUT_MS,
-                        BrandingController.USE_TEMPORARY_STORAGE,
-                        BrandingController.ANIMATE_TOOLBAR_ICON_TRANSITION,
                         ChimeFeatures.ALWAYS_REGISTER,
                         FeedPlaceholderLayout.ENABLE_INSTANT_START_ANIMATION,
                         HubFieldTrial.FLOATING_ACTION_BUTTON,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderPickerMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderPickerMediator.java
index d90084f..9039fe8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderPickerMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderPickerMediator.java
@@ -248,7 +248,7 @@
     private void onMoveClicked() {
         BookmarkUtils.moveBookmarksToParent(
                 mBookmarkModel, mBookmarkIds, mCurrentParentItem.getId());
-        BookmarkUtils.setLastUsedParent(mContext, mCurrentParentItem.getId());
+        BookmarkUtils.setLastUsedParent(mCurrentParentItem.getId());
         mFinishRunnable.run();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java
index f08f01e..c6dcb47 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java
@@ -883,7 +883,7 @@
         if (state.mUiMode == BookmarkUiMode.FOLDER) {
             // Loading and searching states may be pushed to the stack but should never be stored in
             // preferences.
-            BookmarkUtils.setLastUsedUrl(mContext, state.mUrl);
+            BookmarkUtils.setLastUsedUrl(state.mUrl);
             // If a loading state is replaced by another loading state, do not notify this change.
             if (mNativePage != null) {
                 mNativePage.onStateChange(state.mUrl, false);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
index 60f6b5ee..d0aea53 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
@@ -89,10 +89,8 @@
      * @param existingBookmarkItem The {@link BookmarkItem} if the tab has already been bookmarked.
      * @param bookmarkModel The bookmark model.
      * @param tab The tab to add or edit a bookmark.
-     * @param snackbarManager The {@link SnackbarManager} used to show the snackbar.
      * @param bottomSheetController The {@link BottomSheetController} used to show the bottom sheet.
      * @param activity Current activity.
-     * @param fromCustomTab boolean indicates whether it is called by Custom Tab.
      * @param bookmarkType Type of the added bookmark.
      * @param callback Invoked with the resulting bookmark ID, which could be null if unsuccessful.
      * @param fromExplicitTrackUi Whether the bookmark was added directly from a tracking ui (e.g.
@@ -102,10 +100,8 @@
             @Nullable BookmarkItem existingBookmarkItem,
             BookmarkModel bookmarkModel,
             Tab tab,
-            SnackbarManager snackbarManager,
             BottomSheetController bottomSheetController,
             Activity activity,
-            boolean fromCustomTab,
             @BookmarkType int bookmarkType,
             Callback<BookmarkId> callback,
             boolean fromExplicitTrackUi) {
@@ -139,10 +135,10 @@
      * Shows the bookmark save flow.
      *
      * @param activity The current Activity.
-     * @param bottomSheetController The BottomsheetController, used to show the save flow.
+     * @param bottomSheetController The BottomSheetController, used to show the save flow.
      * @param fromExplicitTrackUi Whether the bookmark was added from the explicit UI.
      * @param bookmarkId The BookmarkId to show the save flow for. Can be null in some cases.
-     * @param wasBookmarkMoved Whether the save flow is shown as a reslult of a moved bookmark.
+     * @param wasBookmarkMoved Whether the save flow is shown as a result of a moved bookmark.
      * @param isNewBookmark Whether the bookmark is newly created.
      * @param profile The profile currently used.
      */
@@ -205,7 +201,7 @@
                     "Bookmarks.AddedPerProfileType", type, BrowserProfileType.MAX_VALUE + 1);
         }
 
-        Snackbar snackbar = null;
+        Snackbar snackbar;
         if (bookmarkId == null) {
             snackbar =
                     Snackbar.make(
@@ -227,7 +223,7 @@
                             bookmarkModel.getBookmarkById(bookmarkId).getParentId());
             SnackbarController snackbarController =
                     createSnackbarControllerForEditButton(activity, bookmarkId);
-            if (getLastUsedParent(activity, bookmarkModel) == null) {
+            if (getLastUsedParent() == null) {
                 if (fromCustomTab) {
                     String packageLabel = BuildInfo.getInstance().hostPackageLabel;
                     snackbar =
@@ -387,7 +383,7 @@
             GURL url,
             @Nullable BookmarkId parent,
             @BookmarkType int bookmarkType) {
-        parent = parent == null ? getLastUsedParent(context, bookmarkModel) : parent;
+        parent = parent == null ? getLastUsedParent() : parent;
         BookmarkItem parentItem = null;
         if (parent != null) {
             parentItem = bookmarkModel.getBookmarkById(parent);
@@ -416,7 +412,7 @@
         bookmarkId =
                 bookmarkModel.addBookmark(parent, bookmarkModel.getChildCount(parent), title, url);
         if (bookmarkId == null) {
-            setLastUsedParent(context, bookmarkModel.getDefaultFolder());
+            setLastUsedParent(bookmarkModel.getDefaultFolder());
         }
         return bookmarkId;
     }
@@ -484,7 +480,7 @@
             @Nullable Activity activity, @Nullable BookmarkId folderId, boolean isIncognito) {
         ThreadUtils.assertOnUiThread();
         Context context = activity == null ? ContextUtils.getApplicationContext() : activity;
-        String url = getFirstUrlToLoad(context, folderId);
+        String url = getFirstUrlToLoad(folderId);
 
         if (ChromeSharedPreferences.getInstance()
                 .contains(ChromePreferenceKeys.BOOKMARKS_LAST_USED_URL)) {
@@ -547,11 +543,11 @@
     /**
      * @return the bookmark folder URL to open.
      */
-    private static String getFirstUrlToLoad(Context context, @Nullable BookmarkId folderId) {
+    private static String getFirstUrlToLoad(@Nullable BookmarkId folderId) {
         String url;
         if (folderId == null) {
             // Load most recently visited bookmark folder.
-            url = getLastUsedUrl(context);
+            url = getLastUsedUrl();
         } else {
             // Load a specific folder.
             url = BookmarkUiState.createFolderUrl(folderId).toString();
@@ -562,46 +558,39 @@
 
     /**
      * Saves the last used url to preference. The saved url will be later queried by {@link
-     * #getLastUsedUrl(Context)}
+     * #getLastUsedUrl()}.
      */
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
-    public static void setLastUsedUrl(Context context, String url) {
+    public static void setLastUsedUrl(String url) {
         ChromeSharedPreferences.getInstance()
                 .writeString(ChromePreferenceKeys.BOOKMARKS_LAST_USED_URL, url);
     }
 
     /** Fetches url representing the user's state last time they close the bookmark manager. */
     @VisibleForTesting
-    public static String getLastUsedUrl(Context context) {
+    public static String getLastUsedUrl() {
         return ChromeSharedPreferences.getInstance()
                 .readString(
                         ChromePreferenceKeys.BOOKMARKS_LAST_USED_URL, UrlConstants.BOOKMARKS_URL);
     }
 
     /** Save the last used {@link BookmarkId} as a folder to put new bookmarks to. */
-    public static void setLastUsedParent(Context context, BookmarkId bookmarkId) {
+    public static void setLastUsedParent(BookmarkId bookmarkId) {
         ChromeSharedPreferences.getInstance()
                 .writeString(
                         ChromePreferenceKeys.BOOKMARKS_LAST_USED_PARENT, bookmarkId.toString());
     }
 
     /**
-     * @param context The current android {@link Context}.
-     * @param bookmarkModel The bookmark model used to reset the last used parent for type swapping
-     *     edge cases.
      * @return The parent {@link BookmarkId} that the user used the last time or null if the user
      *     has never selected a parent folder to use.
      */
-    static BookmarkId getLastUsedParent(Context context, BookmarkModel bookmarkModel) {
+    static BookmarkId getLastUsedParent() {
         SharedPreferencesManager preferences = ChromeSharedPreferences.getInstance();
         if (!preferences.contains(ChromePreferenceKeys.BOOKMARKS_LAST_USED_PARENT)) return null;
 
-        BookmarkId parent =
-                BookmarkId.getBookmarkIdFromString(
-                        preferences.readString(
-                                ChromePreferenceKeys.BOOKMARKS_LAST_USED_PARENT, null));
-
-        return parent;
+        return BookmarkId.getBookmarkIdFromString(
+                preferences.readString(ChromePreferenceKeys.BOOKMARKS_LAST_USED_PARENT, null));
     }
 
     /** Starts an {@link BookmarkEditActivity} for the given {@link BookmarkId}. */
@@ -639,7 +628,7 @@
 
     /**
      * Given the {@link BookmarkId}s serialized {@link String}s, return a list of the {@link
-     * BookmarkIds}.
+     * BookmarkId}s.
      */
     public static List<BookmarkId> stringListToBookmarkIds(
             BookmarkModel bookmarkModel, List<String> bookmarkIdStrings) {
@@ -845,7 +834,7 @@
      * Moves the given {@link BookmarkId}s to the new parent if the parent is valid. Type swapping
      * between regular bookmarks and Reading List items as necessary. This method assumes that the
      * bookmark ids that are passed in are valid bookmarks that are moveable. If the newParent
-     * argument doesn't point to a valid location for all of the {@link bookmarksToMove}, then the
+     * argument doesn't point to a valid location for all of the {@param bookmarksToMove}, then the
      * operation is abandoned and nothing is moved.
      *
      * @param bookmarkModel The underlying BookmarkModel, used to move the bookmarks.
@@ -878,7 +867,7 @@
      * All bookmarks will skip over mobile bookmarks and other bookmarks.
      *
      * @param bookmarkModel The {@link BookmarkModel}.
-     * @param bookmarkId The {@link BookmarkId} to get the bparent for.
+     * @param bookmarkId The {@link BookmarkId} to get the parent for.
      */
     public static BookmarkId getParentFolderForViewing(
             BookmarkModel bookmarkModel, BookmarkId bookmarkId) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/TabBookmarker.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/TabBookmarker.java
index 93e7fd5..4f356e2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/TabBookmarker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/TabBookmarker.java
@@ -123,7 +123,6 @@
                     }
 
                     BookmarkId bookmarkId = bookmarkModel.getUserBookmarkIdForTab(tabToBookmark);
-                    boolean isNewBookmark = bookmarkId == null;
                     BookmarkItem currentBookmarkItem =
                             bookmarkId == null ? null : bookmarkModel.getBookmarkById(bookmarkId);
                     onBookmarkModelLoaded(
@@ -131,8 +130,7 @@
                             currentBookmarkItem,
                             bookmarkModel,
                             bookmarkType,
-                            fromExplicitTrackUi,
-                            isNewBookmark);
+                            fromExplicitTrackUi);
                 });
     }
 
@@ -141,16 +139,13 @@
             @Nullable final BookmarkItem currentBookmarkItem,
             final BookmarkModel bookmarkModel,
             @BookmarkType int bookmarkType,
-            boolean fromExplicitTrackUi,
-            boolean isNewBookmark) {
+            boolean fromExplicitTrackUi) {
         BookmarkUtils.addOrEditBookmark(
                 currentBookmarkItem,
                 bookmarkModel,
                 tabToBookmark,
-                mSnackbarManagerSupplier.get(),
                 mBottomSheetControllerSupplier.get(),
                 mActivity,
-                mIsCustomTab,
                 bookmarkType,
                 (newBookmarkId) -> {
                     BookmarkId currentBookmarkId =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
index bdbb1bc..aaecb3c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
@@ -910,7 +910,7 @@
         float scrimColorAlpha = (scrimColor >>> 24) / 255f;
         int scrimColorOpaque = scrimColor & 0xFF000000;
         return ColorUtils.getColorWithOverlay(
-                baseColor, scrimColorOpaque, scrimFraction * scrimColorAlpha, false);
+                baseColor, scrimColorOpaque, scrimFraction * scrimColorAlpha);
     }
 
     // ============================================================================================
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index f7f5cfa..748f590 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -1144,11 +1144,6 @@
             } else {
                 // Could be just prefetching, check if that failed.
                 onContextualSearchRequestNavigation(isFailure);
-
-                // Record metrics for when the prefetched results became viewable.
-                if (mSearchRequest != null && mSearchRequest.wasPrefetch()) {
-                    boolean didResolve = mPolicy.shouldPreviousGestureResolve();
-                }
             }
         }
 
@@ -1828,7 +1823,6 @@
                 assert !TextUtils.isEmpty(selection);
 
                 WebContents baseWebContents = getBaseWebContents();
-                boolean isExactResolve = mSelectionController.isAdjustedSelection();
                 if (baseWebContents != null && mContext != null && mContext.canResolve()) {
                     issueResolveRequest();
                 } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
index a763b26..fc60abec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
@@ -98,8 +98,6 @@
 
     private CustomTabHeightStrategy mCustomTabHeightStrategy;
 
-    // Created only when ChromeFeatureList.CctBrandTransparency is enabled.
-    // TODO(https://crbug.com/1343056): Make it part of the ctor.
     private @Nullable BrandingController mBrandingController;
 
     private @Nullable DesktopSiteSettingsIPHController mDesktopSiteSettingsIPHController;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategy.java
index b33678f..04dc64d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategy.java
@@ -437,7 +437,7 @@
         int scrimColorOpaque = scrimColor & 0xFF000000;
         int color =
                 ColorUtils.getColorWithOverlay(
-                        mToolbarColor, scrimColorOpaque, scrimFraction * scrimColorAlpha, false);
+                        mToolbarColor, scrimColorOpaque, scrimFraction * scrimColorAlpha);
 
         // Drag handle view is not part of CoordinatorLayout. As the root UI scrim changes,
         // the handle view color needs updating to match it. This is a better way than running
@@ -450,7 +450,7 @@
         if (scrimFraction > 0.f) {
             handle.setColorFilter(
                     ColorUtils.getColorWithOverlay(
-                            handleColor, scrimColorOpaque, scrimFraction * scrimColorAlpha, false));
+                            handleColor, scrimColorOpaque, scrimFraction * scrimColorAlpha));
         } else {
             handle.clearColorFilter();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
index 52e84bb..eac4cff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
@@ -1064,7 +1064,6 @@
                 (v, l, t, r, b, ol, ot, or, ob) -> setButtonsVisibility();
         private boolean mCurrentlyShowingBranding;
         private boolean mBrandingStarted;
-        private boolean mAnimateIconTransition = true;
         private CallbackController mCallbackController = new CallbackController();
         // Cached the state before branding start so we can reset to the state when its done.
         private @Nullable Integer mPreBandingState;
@@ -1133,11 +1132,6 @@
                     MIN_URL_BAR_VISIBLE_TIME_POST_BRANDING_MS);
         }
 
-        @Override
-        public void setIconTransitionEnabled(boolean enabled) {
-            mAnimateIconTransition = enabled;
-        }
-
         // CookieControlsObserver interface
         @Override
         public void onBreakageConfidenceLevelChanged(int level) {
@@ -1413,7 +1407,7 @@
                     AppCompatResources.getColorStateList(
                             getContext(), mLocationBarDataProvider.getSecurityIconColorStateList());
             ImageViewCompat.setImageTintList(mSecurityButton, colorStateList);
-            mAnimDelegate.updateSecurityButton(R.drawable.chromelogo16, mAnimateIconTransition);
+            mAnimDelegate.updateSecurityButton(R.drawable.chromelogo16);
 
             mUrlCoordinator.setUrlBarData(
                     UrlBarData.forNonUrlText(
@@ -1451,7 +1445,7 @@
                                 mLocationBarDataProvider.getSecurityIconColorStateList());
                 ImageViewCompat.setImageTintList(mSecurityButton, colorStateList);
             }
-            mAnimDelegate.updateSecurityButton(securityIconResource, mAnimateIconTransition);
+            mAnimDelegate.updateSecurityButton(securityIconResource);
 
             int contentDescriptionId =
                     mLocationBarDataProvider.getSecurityIconContentDescriptionResourceId();
@@ -1462,7 +1456,7 @@
         private void animateCookieControlsIcon() {
             mTaskHandler.removeCallbacksAndMessages(null);
             mAnimDelegate.setUseRotationSecurityButtonTransition(true);
-            mAnimDelegate.updateSecurityButton(R.drawable.ic_eye_crossed, true);
+            mAnimDelegate.updateSecurityButton(R.drawable.ic_eye_crossed);
 
             Runnable finishIconAnimation =
                     () -> {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarAnimationDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarAnimationDelegate.java
index a34be9e..2910be8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarAnimationDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarAnimationDelegate.java
@@ -167,11 +167,12 @@
 
     /**
      * Starts the animation to show/hide the security button,
-     * @param securityIconResource  The updated resource to be assigned to the security status icon.
-     * When this is null, the icon is animated to the left and faded out.
+     *
+     * @param securityIconResource The updated resource to be assigned to the security status icon.
+     *     When this is null, the icon is animated to the left and faded out.
      */
-    void updateSecurityButton(@DrawableRes int securityIconResource, boolean animate) {
-        if (mUseRotationTransition && animate) {
+    void updateSecurityButton(@DrawableRes int securityIconResource) {
+        if (mUseRotationTransition) {
             mBrandingAnimationDelegate.updateDrawableResource(securityIconResource);
         } else {
             boolean isActualResourceChange = true;
@@ -179,7 +180,7 @@
                 isActualResourceChange = securityIconResource != mSecurityIconRes;
             }
             mSecurityButtonAnimationDelegate.updateSecurityButton(
-                    securityIconResource, animate, isActualResourceChange);
+                    securityIconResource, /* animate= */ true, isActualResourceChange);
         }
         mSecurityIconRes = securityIconResource;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncher.java
index 73f12d41..d5d3c43 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncher.java
@@ -7,6 +7,8 @@
 import android.content.Intent;
 import android.net.Uri;
 
+import androidx.annotation.Nullable;
+
 import org.jni_zero.CalledByNative;
 
 import org.chromium.base.supplier.ObservableSupplier;
@@ -28,8 +30,11 @@
     }
 
     @CalledByNative
-    private static void launchCheckupOnDevice(
-            WindowAndroid windowAndroid, @PasswordCheckReferrer int passwordCheckReferrer) {
+    static void launchCheckupOnDevice(
+            WindowAndroid windowAndroid,
+            @PasswordCheckReferrer int passwordCheckReferrer,
+            @Nullable String accountEmail) {
+        assert accountEmail == null || !accountEmail.isEmpty();
         if (windowAndroid.getContext().get() == null) return; // Window not available yet/anymore.
 
         if (PasswordManagerHelper.canUseUpm()) {
@@ -37,7 +42,8 @@
                     windowAndroid.getContext().get(),
                     passwordCheckReferrer,
                     SyncServiceFactory.get(),
-                    getModalDialogManagerSupplier(windowAndroid));
+                    getModalDialogManagerSupplier(windowAndroid),
+                    accountEmail);
             return;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/MainSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/MainSettings.java
index 6d6ef09d..15188f3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/MainSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/MainSettings.java
@@ -420,7 +420,7 @@
         // TODO(crbug.com/1467623): Replace with a static string once name is finalized.
         String title =
                 ChromeFeatureList.getFieldTrialParamByFeature(
-                        ChromeFeatureList.PLUS_ADDRESSES_ENABLED, "suggestion-label");
+                        ChromeFeatureList.PLUS_ADDRESSES_ENABLED, "settings-label");
         if (ChromeFeatureList.isEnabled(ChromeFeatureList.PLUS_ADDRESSES_ENABLED)
                 && !title.isEmpty()) {
             addPreferenceIfAbsent(PREF_PLUS_ADDRESSES);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedNavigationBarColorController.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedNavigationBarColorController.java
index f8f201d..787910c7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedNavigationBarColorController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedNavigationBarColorController.java
@@ -229,8 +229,8 @@
     private @ColorInt int applyCurrentScrimToColor(@ColorInt int color) {
         // Apply a color overlay.
         float scrimColorAlpha = (mDefaultScrimColor >>> 24) / 255f;
-        int scrimColorOpaque = mDefaultScrimColor & 0xFF000000;
+        int scrimColorOpaque = mDefaultScrimColor | 0xFF000000;
         return ColorUtils.getColorWithOverlay(
-                color, scrimColorOpaque, mNavigationBarScrimFraction * scrimColorAlpha, true);
+                color, scrimColorOpaque, mNavigationBarScrimFraction * scrimColorAlpha);
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
index 285ff4a7..842ceef 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
@@ -372,9 +372,7 @@
         BookmarkTestUtil.waitForBookmarkModelLoaded();
 
         assertEquals(BookmarkUiMode.FOLDER, mDelegate.getCurrentUiMode());
-        assertEquals(
-                "chrome-native://bookmarks/folder/3",
-                BookmarkUtils.getLastUsedUrl(mActivityTestRule.getActivity()));
+        assertEquals("chrome-native://bookmarks/folder/3", BookmarkUtils.getLastUsedUrl());
     }
 
     @Test
@@ -774,7 +772,7 @@
                 () -> {
                     BookmarkId folderId = mBookmarkModel.getMobileFolderId();
                     String prefUrl = BookmarkUiState.createFolderUrl(folderId).toString();
-                    BookmarkUtils.setLastUsedUrl(mActivityTestRule.getActivity(), prefUrl);
+                    BookmarkUtils.setLastUsedUrl(prefUrl);
                 });
 
         // Prevent loading so we can verify we see the spinner initially.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java
index d556493..81af0112 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java
@@ -209,9 +209,7 @@
 
         // We should default to the root bookmark.
         Assert.assertEquals(BookmarkUiMode.FOLDER, delegate.getCurrentUiMode());
-        Assert.assertEquals(
-                "chrome-native://bookmarks/folder/0",
-                BookmarkUtils.getLastUsedUrl(mActivityTestRule.getActivity()));
+        Assert.assertEquals("chrome-native://bookmarks/folder/0", BookmarkUtils.getLastUsedUrl());
         Assert.assertEquals("Bookmarks", toolbar.getTitle());
 
         // When opening "Mobile bookmarks", we should come back to it when within the same session.
@@ -229,9 +227,7 @@
 
         // Reopen and make sure we're back in "Mobile bookmarks".
         Assert.assertEquals(BookmarkUiMode.FOLDER, delegate.getCurrentUiMode());
-        Assert.assertEquals(
-                "chrome-native://bookmarks/folder/3",
-                BookmarkUtils.getLastUsedUrl(mActivityTestRule.getActivity()));
+        Assert.assertEquals("chrome-native://bookmarks/folder/3", BookmarkUtils.getLastUsedUrl());
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
index 15065bf7..abe0003 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
@@ -630,7 +630,7 @@
     public void testPlusAddressesHiddenWhenLabelIsEmpty() {
         Assert.assertTrue(
                 ChromeFeatureList.getFieldTrialParamByFeature(
-                                ChromeFeatureList.PLUS_ADDRESSES_ENABLED, "suggestion-label")
+                                ChromeFeatureList.PLUS_ADDRESSES_ENABLED, "settings-label")
                         .isEmpty());
         launchSettingsActivity();
         Assert.assertNull(mMainSettings.findPreference(MainSettings.PREF_PLUS_ADDRESSES));
@@ -640,7 +640,7 @@
     @SmallTest
     @CommandLineFlags.Add({
         "enable-features=PlusAddressesEnabled:"
-                + "suggestion-label/PlusAddressesTestTitle/"
+                + "settings-label/PlusAddressesTestTitle/"
                 + "manage-url/https%3A%2F%2Ftest.plusaddresses.google.com"
     })
     public void testPlusAddressesEnabled() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java
index 31878ed..27e1e74 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java
@@ -679,29 +679,6 @@
 
     @Test
     @SmallTest
-    public void testMakeCredential_noEligibleParameters2() {
-        PublicKeyCredentialCreationOptions customOptions = mCreationOptions;
-        PublicKeyCredentialParameters parameters = new PublicKeyCredentialParameters();
-        parameters.type = 10; // Not a valid type.
-        customOptions.publicKeyParameters = new PublicKeyCredentialParameters[] {parameters};
-
-        mRequest.handleMakeCredentialRequest(
-                mContext,
-                customOptions,
-                mFrameHost,
-                /* maybeClientDataHash= */ null,
-                mOrigin,
-                (responseStatus, response) ->
-                        mCallback.onRegisterResponse(responseStatus, response),
-                errorStatus -> mCallback.onError(errorStatus));
-        mCallback.blockUntilCalled();
-        Assert.assertEquals(
-                mCallback.getStatus(), Integer.valueOf(AuthenticatorStatus.ALGORITHM_UNSUPPORTED));
-        Fido2ApiTestHelper.verifyRespondedBeforeTimeout(mStartTimeMs);
-    }
-
-    @Test
-    @SmallTest
     public void testMakeCredential_parametersContainEligibleAndNoneligible() {
         PublicKeyCredentialCreationOptions customOptions = mCreationOptions;
         PublicKeyCredentialParameters parameters = new PublicKeyCredentialParameters();
@@ -737,7 +714,7 @@
                 Fido2ApiTestHelper.createSuccessfulMakeCredentialIntent());
 
         PublicKeyCredentialCreationOptions customOptions = mCreationOptions;
-        customOptions.excludeCredentials = null;
+        customOptions.excludeCredentials = new PublicKeyCredentialDescriptor[0];
         mRequest.handleMakeCredentialRequest(
                 mContext,
                 customOptions,
@@ -1585,7 +1562,7 @@
     public void testGetAssertion_emptyAllowCredentials1() {
         // Passes conversion and gets rejected by GmsCore
         PublicKeyCredentialRequestOptions customOptions = mRequestOptions;
-        customOptions.allowCredentials = null;
+        customOptions.allowCredentials = new PublicKeyCredentialDescriptor[0];
         mIntentSender.setNextResultIntent(
                 Fido2ApiTestHelper.createErrorIntent(
                         Fido2Api.NOT_ALLOWED_ERR,
@@ -1618,7 +1595,7 @@
     public void testGetAssertion_emptyAllowCredentials2() {
         // Passes conversion and gets rejected by GmsCore
         PublicKeyCredentialRequestOptions customOptions = mRequestOptions;
-        customOptions.allowCredentials = null;
+        customOptions.allowCredentials = new PublicKeyCredentialDescriptor[0];
         mIntentSender.setNextResultIntent(
                 Fido2ApiTestHelper.createErrorIntent(
                         Fido2Api.NOT_ALLOWED_ERR,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderPickerMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderPickerMediatorTest.java
index b807b4da..a24ee16 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderPickerMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderPickerMediatorTest.java
@@ -375,7 +375,7 @@
         mModel.get(BookmarkFolderPickerProperties.MOVE_CLICK_LISTENER).run();
         verify(mFinishRunnable).run();
         verify(mBookmarkModel).moveBookmarks(Arrays.asList(mUserBookmarkId), mUserFolderId);
-        assertEquals(mUserFolderId, BookmarkUtils.getLastUsedParent(mActivity, mBookmarkModel));
+        assertEquals(mUserFolderId, BookmarkUtils.getLastUsedParent());
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionUnitTest.java
index d2b9f4a..d5bf56cd 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionUnitTest.java
@@ -176,7 +176,6 @@
 
     @Test
     @EnableFeatures({
-        ChromeFeatureList.CCT_BRAND_TRANSPARENCY,
         ChromeFeatureList.CCT_REAL_TIME_ENGAGEMENT_SIGNALS
     })
     public void setEngagementSignalsCallback_Available() {
@@ -192,7 +191,6 @@
 
     @Test
     @EnableFeatures({
-        ChromeFeatureList.CCT_BRAND_TRANSPARENCY,
         ChromeFeatureList.CCT_REAL_TIME_ENGAGEMENT_SIGNALS
     })
     public void setEngagementSignalsCallback_NotAvailable() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java
index a1b8d10..900e513 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java
@@ -12,7 +12,6 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -211,17 +210,6 @@
 
     @Test
     public void testToolbarBrandingDelegateImpl_EmptyToBranding() {
-        mLocationBar.setIconTransitionEnabled(true);
-        doTestToolbarBrandingDelegateImpl_EmptyToBranding(true);
-    }
-
-    @Test
-    public void testToolbarBrandingDelegateImpl_EmptyToBranding_DisableTransition() {
-        mLocationBar.setIconTransitionEnabled(false);
-        doTestToolbarBrandingDelegateImpl_EmptyToBranding(false);
-    }
-
-    private void doTestToolbarBrandingDelegateImpl_EmptyToBranding(boolean animateIconTransition) {
         assertUrlAndTitleVisible(/* titleVisible= */ false, /* urlVisible= */ true);
         mLocationBar.showEmptyLocationBar();
         assertUrlAndTitleVisible(/* titleVisible= */ false, /* urlVisible= */ false);
@@ -233,7 +221,7 @@
 
         mLocationBar.showBrandingLocationBar();
         assertUrlAndTitleVisible(/* titleVisible= */ false, /* urlVisible= */ true);
-        verify(mAnimationDelegate).updateSecurityButton(anyInt(), eq(animateIconTransition));
+        verify(mAnimationDelegate).updateSecurityButton(anyInt());
         assertBrandingTextShowingOnUrlBar();
 
         // Attempt to update title and URL to show Title only - should be ignored during branding.
@@ -520,25 +508,25 @@
 
     @Test
     public void testCookieControlsIcon_animateOnPageStoppedLoadingWithHighBreakageConfidence() {
-        verify(mAnimationDelegate, never()).updateSecurityButton(anyInt(), anyBoolean());
+        verify(mAnimationDelegate, never()).updateSecurityButton(anyInt());
 
         mLocationBar.onBreakageConfidenceLevelChanged(CookieControlsBreakageConfidenceLevel.HIGH);
-        verify(mAnimationDelegate, never()).updateSecurityButton(anyInt(), anyBoolean());
+        verify(mAnimationDelegate, never()).updateSecurityButton(anyInt());
         verify(mPageInfoIPHController, never()).showCookieControlsIPH(anyInt(), anyInt());
 
         mLocationBar.onPageLoadStopped();
-        verify(mAnimationDelegate, times(1)).updateSecurityButton(R.drawable.ic_eye_crossed, true);
+        verify(mAnimationDelegate, times(1)).updateSecurityButton(R.drawable.ic_eye_crossed);
         verify(mPageInfoIPHController, times(1)).showCookieControlsIPH(anyInt(), anyInt());
 
         mLocationBar.onBreakageConfidenceLevelChanged(CookieControlsBreakageConfidenceLevel.LOW);
         mLocationBar.onPageLoadStopped();
-        verify(mAnimationDelegate, times(1)).updateSecurityButton(R.drawable.ic_eye_crossed, true);
+        verify(mAnimationDelegate, times(1)).updateSecurityButton(R.drawable.ic_eye_crossed);
         verify(mPageInfoIPHController, times(1)).showCookieControlsIPH(anyInt(), anyInt());
     }
 
     @Test
     public void testCookieControlsIcon_trackingProtectionsEnabled_cookieBlockingEnabled() {
-        verify(mAnimationDelegate, never()).updateSecurityButton(anyInt(), anyBoolean());
+        verify(mAnimationDelegate, never()).updateSecurityButton(anyInt());
 
         mLocationBar.onBreakageConfidenceLevelChanged(CookieControlsBreakageConfidenceLevel.HIGH);
         mLocationBar.onStatusChanged(
@@ -555,7 +543,7 @@
 
     @Test
     public void testCookieControlsIcon_trackingProtectionsEnabled_cookieBlockingDisabled() {
-        verify(mAnimationDelegate, never()).updateSecurityButton(anyInt(), anyBoolean());
+        verify(mAnimationDelegate, never()).updateSecurityButton(anyInt());
 
         mLocationBar.onBreakageConfidenceLevelChanged(CookieControlsBreakageConfidenceLevel.HIGH);
         mLocationBar.onStatusChanged(
@@ -572,7 +560,7 @@
 
     @Test
     public void testCookieControlsIcon_trackingProtectionDisabled_cookieBlockingEnabled() {
-        verify(mAnimationDelegate, never()).updateSecurityButton(anyInt(), anyBoolean());
+        verify(mAnimationDelegate, never()).updateSecurityButton(anyInt());
 
         mLocationBar.onBreakageConfidenceLevelChanged(CookieControlsBreakageConfidenceLevel.HIGH);
         mLocationBar.onStatusChanged(
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt
index 2e32907..95a4144 100644
--- a/chrome/android/profiles/arm.newest.txt
+++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-122.0.6185.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-arm-122.0.6186.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 08f2e66..6efea0b 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -2870,6 +2870,22 @@
                desc="Tooltip label for the button to open all downloads">
         Show all downloads in a new tab
       </message>
+      <message name="IDS_DOWNLOAD_BUBBLE_ESB_PROMO"
+               desc="Text for the in-product-help bubble shown for the esb promo download bubble.">
+        Chrome just blocked a dangerous file from downloading. Get even stronger security with enhanced protection.
+      </message>
+      <message name="IDS_DOWNLOAD_BUBBLE_ESB_PROMO_CUSTOM_ACTION"
+               desc="Text for the custom action within the in-product-help bubble shown for the esb promo download bubble.">
+        Settings
+      </message>
+      <message name="IDS_DOWNLOAD_BUBBLE_ESB_PROMO_DISMISS"
+               desc="Text for the dismiss action within the in-product-help bubble shown for the esb promo download bubble.">
+        No thanks
+      </message>
+      <message name="IDS_DOWNLOAD_BUBBLE_ESB_PROMO_TITLE"
+               desc="Title for the in-product-help bubble shown for the esb promo download bubble.">
+        Improve download protection
+      </message>
       <message name="IDS_DOWNLOAD_BUBBLE_PROMO"
                desc="Text for the in-product-help bubble shown for the download bubble.">
         Manage files as they download, and open them when they're done
diff --git a/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_ESB_PROMO.png.sha1 b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_ESB_PROMO.png.sha1
new file mode 100644
index 0000000..641aece
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_ESB_PROMO.png.sha1
@@ -0,0 +1 @@
+53c2ea6d43f6002221d6aebd014f5b754564ae76
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_ESB_PROMO_CUSTOM_ACTION.png.sha1 b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_ESB_PROMO_CUSTOM_ACTION.png.sha1
new file mode 100644
index 0000000..641aece
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_ESB_PROMO_CUSTOM_ACTION.png.sha1
@@ -0,0 +1 @@
+53c2ea6d43f6002221d6aebd014f5b754564ae76
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_ESB_PROMO_DISMISS.png.sha1 b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_ESB_PROMO_DISMISS.png.sha1
new file mode 100644
index 0000000..641aece
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_ESB_PROMO_DISMISS.png.sha1
@@ -0,0 +1 @@
+53c2ea6d43f6002221d6aebd014f5b754564ae76
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_ESB_PROMO_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_ESB_PROMO_TITLE.png.sha1
new file mode 100644
index 0000000..641aece
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_ESB_PROMO_TITLE.png.sha1
@@ -0,0 +1 @@
+53c2ea6d43f6002221d6aebd014f5b754564ae76
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 5e9033f..798aa0c6 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -129,7 +129,7 @@
     Live Translate
   </message>
   <message name="IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_SUBTITLE" desc="Description text for Live Translate feature.">
-    Automatically translates captions
+    Sends captions to Google to automatically translate them
   </message>
   <if expr="chromeos_ash">
     <message name="IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE_BROWSER_ONLY_ENGLISH_ONLY" desc="Description text for Live Caption feature. When audio or video is playing, the user will see captions on the screen. Only works in Chrome browser initially. Currently only works for English media.">
@@ -143,7 +143,7 @@
     Automatically creates captions for English audio and video. Audio and captions never leave your device.
   </message>
   <message name="IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE" desc="Description text for Live Caption feature. When audio or video is playing, the user will see captions on the screen.">
-    Automatically creates captions for audio and video. Audio and captions never leave your device.
+    Automatically creates captions for audio and video
   </message>
   <message name="IDS_SETTINGS_CAPTIONS_LIVE_CAPTION_DOWNLOAD_PROGRESS" desc="Download progress indicator after Live Caption is enabled. The user needs to download certain files for the feature to work.">
     Downloading speech recognition files... <ph name="PERCENT">$1<ex>17</ex></ph>%
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE.png.sha1
index 60ad5857..369ac84 100644
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE.png.sha1
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE.png.sha1
@@ -1 +1 @@
-9f423dfc975e121564455e4fb2b8a43871f459d9
\ No newline at end of file
+daea0f80573ce482d1e344551aafcdbedf393427
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_SUBTITLE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_SUBTITLE.png.sha1
index dff6203..369ac84 100644
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_SUBTITLE.png.sha1
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_SUBTITLE.png.sha1
@@ -1 +1 @@
-3c57962ce9dbe1028b4634a36d0a1a2660a0ac73
\ No newline at end of file
+daea0f80573ce482d1e344551aafcdbedf393427
\ No newline at end of file
diff --git a/chrome/app/shared_settings_strings.grdp b/chrome/app/shared_settings_strings.grdp
index 0d12367f..925b2b67 100644
--- a/chrome/app/shared_settings_strings.grdp
+++ b/chrome/app/shared_settings_strings.grdp
@@ -101,7 +101,7 @@
     Manage languages
   </message>
   <message name="IDS_SETTINGS_CAPTIONS_MANAGE_LANGUAGES_SUBTITLE" desc="Subtitle for the caption settings section for managing language packs.">
-    Add and remove languages to caption
+    Language packs are used for Live Caption and are stored on your device
   </message>
   <message name="IDS_SETTINGS_CAPTIONS_LIVE_TRANSLATE_TARGET_LANGUAGE" desc="Description of the target language setting for the Live Translate feature.">
     Translate to
diff --git a/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_MANAGE_LANGUAGES_SUBTITLE.png.sha1 b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_MANAGE_LANGUAGES_SUBTITLE.png.sha1
index e22f1a2..369ac84 100644
--- a/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_MANAGE_LANGUAGES_SUBTITLE.png.sha1
+++ b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_MANAGE_LANGUAGES_SUBTITLE.png.sha1
@@ -1 +1 @@
-366fe5406f644a94206e52654b5e57aa80814a0a
\ No newline at end of file
+daea0f80573ce482d1e344551aafcdbedf393427
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index fa9a93f..5593cf8 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -26,7 +26,6 @@
 import("//components/optimization_guide/features.gni")
 import("//components/os_crypt/sync/features.gni")
 import("//components/services/screen_ai/buildflags/features.gni")
-import("//components/signin/features.gni")
 import("//components/spellcheck/spellcheck_build_features.gni")
 import("//crypto/features.gni")
 import("//device/vr/buildflags/buildflags.gni")
@@ -1103,10 +1102,10 @@
     "performance_manager/metrics/memory_pressure_metrics.h",
     "performance_manager/metrics/metrics_provider_common.cc",
     "performance_manager/metrics/metrics_provider_common.h",
-    "performance_manager/metrics/page_timeline_cpu_monitor.cc",
-    "performance_manager/metrics/page_timeline_cpu_monitor.h",
-    "performance_manager/metrics/page_timeline_monitor.cc",
-    "performance_manager/metrics/page_timeline_monitor.h",
+    "performance_manager/metrics/page_resource_cpu_monitor.cc",
+    "performance_manager/metrics/page_resource_cpu_monitor.h",
+    "performance_manager/metrics/page_resource_monitor.cc",
+    "performance_manager/metrics/page_resource_monitor.h",
     "performance_manager/observers/page_load_metrics_observer.cc",
     "performance_manager/observers/page_load_metrics_observer.h",
     "performance_manager/policies/page_freezing_policy.cc",
@@ -1902,6 +1901,14 @@
       "privacy_sandbox/tracking_protection_notice_factory.h",
       "privacy_sandbox/tracking_protection_notice_service.cc",
       "privacy_sandbox/tracking_protection_notice_service.h",
+      "search_engine_choice/search_engine_choice_client_side_trial.cc",
+      "search_engine_choice/search_engine_choice_client_side_trial.h",
+      "search_engine_choice/search_engine_choice_profile_tagger.cc",
+      "search_engine_choice/search_engine_choice_profile_tagger.h",
+      "search_engine_choice/search_engine_choice_service.cc",
+      "search_engine_choice/search_engine_choice_service.h",
+      "search_engine_choice/search_engine_choice_service_factory.cc",
+      "search_engine_choice/search_engine_choice_service_factory.h",
       "user_education/browser_feature_promo_storage_service.cc",
       "user_education/browser_feature_promo_storage_service.h",
       "user_education/browser_tutorial_service.cc",
@@ -2062,6 +2069,7 @@
     "//chrome/browser/profiling_host",
     "//chrome/browser/promos:utils",
     "//chrome/browser/push_messaging:budget_proto",
+    "//chrome/browser/push_notification:push_notification",
     "//chrome/browser/resource_coordinator:mojo_bindings",
     "//chrome/browser/resource_coordinator:tab_manager_features",
     "//chrome/browser/resources/accessibility:resources",
@@ -2565,6 +2573,7 @@
 
   if (!is_android) {
     deps += [
+      "//chrome/browser/ui/webui/search_engine_choice:mojo_bindings",
       "//components/manta",
       "//components/manta/proto",
       "//components/user_education/common",
@@ -2620,20 +2629,6 @@
     }
   }
 
-  if (enable_search_engine_choice) {
-    sources += [
-      "search_engine_choice/search_engine_choice_client_side_trial.cc",
-      "search_engine_choice/search_engine_choice_client_side_trial.h",
-      "search_engine_choice/search_engine_choice_profile_tagger.cc",
-      "search_engine_choice/search_engine_choice_profile_tagger.h",
-      "search_engine_choice/search_engine_choice_service.cc",
-      "search_engine_choice/search_engine_choice_service.h",
-      "search_engine_choice/search_engine_choice_service_factory.cc",
-      "search_engine_choice/search_engine_choice_service_factory.h",
-    ]
-    deps += [ "//chrome/browser/ui/webui/search_engine_choice:mojo_bindings" ]
-  }
-
   if (is_linux || is_chromeos) {
     deps += [ "//chrome/browser/error_reporting" ]
   }
@@ -5078,6 +5073,8 @@
       "policy/status_provider/device_local_account_policy_status_provider.h",
       "policy/status_provider/user_cloud_policy_status_provider_chromeos.cc",
       "policy/status_provider/user_cloud_policy_status_provider_chromeos.h",
+      "push_notification/push_notification_service_factory.cc",
+      "push_notification/push_notification_service_factory.h",
       "resource_coordinator/tab_manager_delegate_chromeos.cc",
       "resource_coordinator/tab_manager_delegate_chromeos.h",
       "scalable_iph/scalable_iph_factory_impl.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index b4518e07..3f06ff7 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -518,15 +518,6 @@
         {"Use Denylist", kCCTResizablePolicyParamUseDenylist,
          std::size(kCCTResizablePolicyParamUseDenylist), nullptr}};
 
-const FeatureEntry::FeatureParam kCCTBrandingTestFriendly[] = {
-    {"use_temporary_storage", "true"},
-    {"branding_cadence", "10000"}  // 10 seconds
-};
-
-const FeatureEntry::FeatureVariation kCctBrandTransparencyVariations[] = {
-    {"Test friendly mode", kCCTBrandingTestFriendly,
-     std::size(kCCTBrandingTestFriendly), nullptr}};
-
 const FeatureEntry::FeatureParam
     kCCTRealTimeEngagementSignalsParamRealValues[] = {{"real_values", "true"}};
 const FeatureEntry::FeatureParam
@@ -7238,14 +7229,6 @@
          "CCTRealTimeEngagementSignals")},
 #endif
 
-#if BUILDFLAG(IS_ANDROID)
-    {"cct-brand-transparency", flag_descriptions::kCCTBrandTransparencyName,
-     flag_descriptions::kCCTBrandTransparencyDescription, kOsAndroid,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kCCTBrandTransparency,
-                                    kCctBrandTransparencyVariations,
-                                    "CCTBrandTransparency")},
-#endif
-
 #if BUILDFLAG(IS_CHROMEOS)
     {"allow-dsp-based-aec", flag_descriptions::kCrOSDspBasedAecAllowedName,
      flag_descriptions::kCrOSDspBasedAecAllowedDescription, kOsCrOS | kOsLacros,
@@ -11062,6 +11045,11 @@
      FEATURE_VALUE_TYPE(features::kMacImeLiveConversionFix)},
 #endif
 
+    {"tear-off-web-app-tab-opens-web-app-window",
+     flag_descriptions::kTearOffWebAppAppTabOpensWebAppWindowName,
+     flag_descriptions::kTearOffWebAppAppTabOpensWebAppWindowDescription,
+     kOsAll, FEATURE_VALUE_TYPE(features::kTearOffWebAppTabOpensWebAppWindow)},
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
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 68f89840..a19cd35 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, "es-es", media::mojom::ConfidenceLevel::kHighlyConfident,
+      frame_host, "fr-fr", media::mojom::ConfidenceLevel::kHighlyConfident,
       media::mojom::AsrSwitchResult::kDefaultNoSwitch);
   OnLanguageIdentificationEvent(
-      frame_host, "es-es", media::mojom::ConfidenceLevel::kHighlyConfident,
+      frame_host, "fr-fr", media::mojom::ConfidenceLevel::kHighlyConfident,
       media::mojom::AsrSwitchResult::kDefaultNoSwitch);
 
   OnLanguageIdentificationEvent(
-      frame_host, "fr-fr", media::mojom::ConfidenceLevel::kHighlyConfident,
+      frame_host, "es-us", media::mojom::ConfidenceLevel::kHighlyConfident,
       media::mojom::AsrSwitchResult::kDefaultNoSwitch);
   OnLanguageIdentificationEvent(
-      frame_host, "fr-fr", media::mojom::ConfidenceLevel::kHighlyConfident,
+      frame_host, "es-us", media::mojom::ConfidenceLevel::kHighlyConfident,
       media::mojom::AsrSwitchResult::kDefaultNoSwitch);
   OnLanguageIdentificationEvent(
-      frame_host, "fr-fr", media::mojom::ConfidenceLevel::kHighlyConfident,
+      frame_host, "es-us", 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 fr-FR language
+  // The en-US language pack is downloaded by default. Only the es-ES language
   // pack should be automatically downloaded.
   ASSERT_TRUE(base::Contains(installed_languages, speech::LanguageCode::kEnUs));
-  ASSERT_TRUE(base::Contains(installed_languages, speech::LanguageCode::kFrFr));
+  ASSERT_TRUE(base::Contains(installed_languages, speech::LanguageCode::kEsEs));
 }
 
 }  // 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 f30baff..b7cb952 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,7 +329,8 @@
     if (language_identification_event_count_ ==
         kLanguageIdentificationEventCountThreshold) {
       std::optional<speech::SodaLanguagePackComponentConfig> language_config =
-          speech::GetLanguageComponentConfig(event->language);
+          speech::GetLanguageComponentConfigMatchingLanguageSubtag(
+              event->language);
 
       if (language_config.has_value() &&
           IsLanguageInstallable(language_config.value().language_name)) {
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingController.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingController.java
index bdaf7f9..ce447b178 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingController.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingController.java
@@ -20,9 +20,7 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.flags.BooleanCachedFieldTrialParameter;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.flags.IntCachedFieldTrialParameter;
 import org.chromium.components.crash.PureJavaExceptionReporter;
 import org.chromium.ui.widget.Toast;
 
@@ -31,13 +29,6 @@
 /** Controls the strategy to start branding, and the duration to show branding. */
 public class BrandingController {
     private static final String TAG = "CctBrand";
-    private static final String PARAM_BRANDING_CADENCE_NAME = "branding_cadence";
-    private static final String PARAM_MAX_BLANK_TOOLBAR_TIMEOUT_MS = "max_blank_toolbar_timeout";
-    private static final String PARAM_USE_TEMPORARY_STORAGE = "use_temporary_storage";
-    private static final String PARAM_ANIMATE_TOOLBAR_ICON_TRANSITION =
-            "animate_toolbar_transition";
-    private static final int DEFAULT_BRANDING_CADENCE_MS = (int) TimeUnit.HOURS.toMillis(1);
-    private static final int DEFAULT_MAX_BLANK_TOOLBAR_TIMEOUT_MS = 500;
 
     /**
      * The maximum time allowed from CCT Toolbar initialized until it should show the URL and title.
@@ -47,41 +38,14 @@
     /**
      * The maximum time allowed to leave CCT Toolbar blank until showing branding or URL and title.
      */
-    public static final IntCachedFieldTrialParameter MAX_BLANK_TOOLBAR_TIMEOUT_MS =
-            new IntCachedFieldTrialParameter(
-                    ChromeFeatureList.CCT_BRAND_TRANSPARENCY,
-                    PARAM_MAX_BLANK_TOOLBAR_TIMEOUT_MS,
-                    DEFAULT_MAX_BLANK_TOOLBAR_TIMEOUT_MS);
+    @VisibleForTesting static final int MAX_BLANK_TOOLBAR_TIMEOUT_MS = 500;
 
     /**
-     * The minimum time required between two branding events to shown. If time elapse since last
+     * The minimum time required between two branding events to shown. If time elapsed since last
      * branding is less than this cadence, the branding check decision will be {@link
      * BrandingDecision.NONE}.
      */
-    public static final IntCachedFieldTrialParameter BRANDING_CADENCE_MS =
-            new IntCachedFieldTrialParameter(
-                    ChromeFeatureList.CCT_BRAND_TRANSPARENCY,
-                    PARAM_BRANDING_CADENCE_NAME,
-                    DEFAULT_BRANDING_CADENCE_MS);
-
-    /**
-     * Use temporary storage for branding launch time. The launch time will not persists to the
-     * shared pref, but instead only lasts as long as Chrome is alive. This param is added for
-     * easier manual testing and should not be used for official channels.
-     */
-    public static final BooleanCachedFieldTrialParameter USE_TEMPORARY_STORAGE =
-            new BooleanCachedFieldTrialParameter(
-                    ChromeFeatureList.CCT_BRAND_TRANSPARENCY, PARAM_USE_TEMPORARY_STORAGE, false);
-
-    /**
-     * Whether animation transition will be used for the security icon during toolbar branding.
-     * If set to false, the icon transition will be disabled.
-     */
-    public static final BooleanCachedFieldTrialParameter ANIMATE_TOOLBAR_ICON_TRANSITION =
-            new BooleanCachedFieldTrialParameter(
-                    ChromeFeatureList.CCT_BRAND_TRANSPARENCY,
-                    PARAM_ANIMATE_TOOLBAR_ICON_TRANSITION,
-                    true);
+    @VisibleForTesting static final int BRANDING_CADENCE_MS = (int) TimeUnit.HOURS.toMillis(1);
 
     private final CallbackController mCallbackController = new CallbackController();
     private final @BrandingDecision OneshotSupplierImpl<Integer> mBrandingDecision =
@@ -89,7 +53,6 @@
     private final BrandingChecker mBrandingChecker;
     private final Context mContext;
     private final String mBrowserName;
-    private final boolean mEnableIconAnimation;
     @Nullable private final PureJavaExceptionReporter mExceptionReporter;
     private ToolbarBrandingDelegate mToolbarBrandingDelegate;
     private @Nullable Toast mToast;
@@ -112,7 +75,6 @@
         mContext = context;
         mBrowserName = browserName;
         mExceptionReporter = exceptionReporter;
-        mEnableIconAnimation = ANIMATE_TOOLBAR_ICON_TRANSITION.getValue();
         mBrandingDecision.onAvailable(
                 mCallbackController.makeCancelable((decision) -> maybeMakeBrandingDecision()));
         mReleaseStorageOnFinished =
@@ -124,7 +86,7 @@
                         appId,
                         SharedPreferencesBrandingTimeStorage.getInstance(),
                         mBrandingDecision::set,
-                        BRANDING_CADENCE_MS.getValue(),
+                        BRANDING_CADENCE_MS,
                         BrandingDecision.TOAST);
         mBrandingChecker.executeWithTaskTraits(TaskTraits.USER_VISIBLE_MAY_BLOCK);
     }
@@ -141,7 +103,6 @@
 
         mToolbarInitializedTime = SystemClock.elapsedRealtime();
         mToolbarBrandingDelegate = delegate;
-        mToolbarBrandingDelegate.setIconTransitionEnabled(mEnableIconAnimation);
 
         // Start the task to timeout the branding check. If mBrandingChecker already finished,
         // canceling the task does nothing. Does not interrupt if the task is running, since the
@@ -150,7 +111,7 @@
                 TaskTraits.UI_USER_VISIBLE,
                 mCallbackController.makeCancelable(
                         () -> mBrandingChecker.cancel(/* mayInterruptIfRunning= */ false)),
-                MAX_BLANK_TOOLBAR_TIMEOUT_MS.getValue());
+                MAX_BLANK_TOOLBAR_TIMEOUT_MS);
 
         // Set location bar to empty as controller is waiting for mBrandingDecision.
         // This should not cause any UI jank even if a decision is made immediately, as
@@ -260,9 +221,7 @@
 
                             // Release the in-memory share pref from the current session if branding
                             // checker didn't timeout.
-                            if (mReleaseStorageOnFinished
-                                    && !mBrandingChecker.isCancelled()
-                                    && !USE_TEMPORARY_STORAGE.getValue()) {
+                            if (mReleaseStorageOnFinished && !mBrandingChecker.isCancelled()) {
                                 SharedPreferencesBrandingTimeStorage.resetInstance();
                             }
                         }));
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingControllerUnitTest.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingControllerUnitTest.java
index 6c16410..783d6a5 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingControllerUnitTest.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingControllerUnitTest.java
@@ -10,6 +10,9 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import static org.chromium.chrome.browser.customtabs.features.branding.BrandingController.BRANDING_CADENCE_MS;
+import static org.chromium.chrome.browser.customtabs.features.branding.BrandingController.MAX_BLANK_TOOLBAR_TIMEOUT_MS;
+
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
@@ -54,9 +57,6 @@
         shadows = {ShadowSystemClock.class, ShadowPostTask.class, ShadowToast.class})
 @LooperMode(Mode.PAUSED)
 public class BrandingControllerUnitTest {
-    private static final int TEST_BRANDING_CADENCE = 10_000;
-    private static final int TEST_MAX_TOOLBAR_BLANK_TIMEOUT = 1000;
-
     @Rule public MockitoRule mTestRule = MockitoJUnit.rule();
     @Rule public FakeTimeTestRule mFakeTimeTestRule = new FakeTimeTestRule();
 
@@ -80,11 +80,6 @@
         ShadowPostTask.setTestImpl(mShadowPostTaskImpl);
 
         SystemClock.setCurrentTimeMillis(TimeUtils.currentTimeMillis());
-
-        BrandingController.BRANDING_CADENCE_MS.setForTesting(TEST_BRANDING_CADENCE);
-        BrandingController.MAX_BLANK_TOOLBAR_TIMEOUT_MS.setForTesting(
-                TEST_MAX_TOOLBAR_BLANK_TIMEOUT);
-        BrandingController.USE_TEMPORARY_STORAGE.setForTesting(false);
     }
 
     @After
@@ -143,7 +138,7 @@
                 .assertShownToastBranding(true)
                 .assertShownRegularLocationBar(true)
                 // Start 2nd branding with delay.
-                .advanceMills(TEST_BRANDING_CADENCE + 1)
+                .advanceMills(BRANDING_CADENCE_MS + 1)
                 .newBrandingController()
                 .idleMainLooper() // Finish branding checker.
                 .assertBrandingDecisionMade(BrandingDecision.TOOLBAR)
@@ -180,12 +175,12 @@
                 .assertShownToastBranding(true)
                 .newBrandingController()
                 .onToolbarInitialized()
-                .advanceMills(TEST_MAX_TOOLBAR_BLANK_TIMEOUT)
+                .advanceMills(MAX_BLANK_TOOLBAR_TIMEOUT_MS)
                 .idleMainLooper() // Branding checker is finished, but timed out comes first.
                 .assertBrandingDecisionMade(BrandingDecision.TOAST)
                 .assertShownRegularLocationBar(true);
 
-        // BrandingController.TOTAL_BRANDING_DELAY_MS - TEST_MAX_TOOLBAR_BLANK_TIMEOUT = 800
+        // BrandingController.TOTAL_BRANDING_DELAY_MS - MAX_BLANK_TOOLBAR_TIMEOUT_MS = 1300
         assertEquals(
                 "Toast duration is different.",
                 Toast.LENGTH_LONG,
@@ -193,38 +188,6 @@
     }
 
     @Test
-    public void testInMemoryStorage() {
-        BrandingController.USE_TEMPORARY_STORAGE.setForTesting(true);
-
-        new BrandingCheckTester()
-                .newBrandingController()
-                .idleMainLooper()
-                .onToolbarInitialized()
-                .assertBrandingDecisionMade(BrandingDecision.TOAST)
-                .assertShownBrandingLocationBar(false)
-                .advanceMills(TEST_BRANDING_CADENCE - 1)
-                .newBrandingController()
-                .idleMainLooper()
-                .onToolbarInitialized()
-                .assertBrandingDecisionMade(BrandingDecision.NONE)
-                // Advance one more bit so branding will show again with toolbar.
-                .advanceMills(1)
-                .newBrandingController()
-                .idleMainLooper()
-                .onToolbarInitialized()
-                .assertBrandingDecisionMade(BrandingDecision.TOOLBAR);
-
-        SharedPreferencesBrandingTimeStorage.resetInstance();
-
-        // After reset storage instance, decision should be in use again.
-        new BrandingCheckTester()
-                .newBrandingController()
-                .idleMainLooper()
-                .onToolbarInitialized()
-                .assertBrandingDecisionMade(BrandingDecision.TOAST);
-    }
-
-    @Test
     public void testDestroy() {
         // Inspired by https://crbug.com/1362437. Make sure callback are canceled once the branding
         // controller is destroyed.
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/SharedPreferencesBrandingTimeStorage.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/SharedPreferencesBrandingTimeStorage.java
index 3c1becb..de10fb9 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/SharedPreferencesBrandingTimeStorage.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/SharedPreferencesBrandingTimeStorage.java
@@ -35,11 +35,7 @@
     /** Shared pref that must be read / write on background thread. */
     private SharedPreferences mSharedPref;
 
-    private SharedPreferencesBrandingTimeStorage() {
-        if (BrandingController.USE_TEMPORARY_STORAGE.getValue()) {
-            resetSharedPref();
-        }
-    }
+    private SharedPreferencesBrandingTimeStorage() {}
 
     static SharedPreferencesBrandingTimeStorage getInstance() {
         if (sInstance == null) {
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingDelegate.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingDelegate.java
index db7258ff..be66637 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingDelegate.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingDelegate.java
@@ -19,12 +19,4 @@
 
     /** Show the regular location with URL and Title, with start transition. */
     void showRegularToolbar();
-
-    /**
-     * Whether the animation transition between state for toolbar icon should be disable. By
-     * default, the animation transition will be enabled if this is not set.
-     * @param enabled If true, the animation will be enabled; if false, the animation will be
-     *                disabled.
-     */
-    void setIconTransitionEnabled(boolean enabled);
 }
diff --git a/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java b/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java
index 4432de3d..b39bd06 100644
--- a/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java
+++ b/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java
@@ -106,7 +106,6 @@
     private static final String SHARED_PREF_SHOW_TITLE = "ShowTitle";
     private static final String SHARED_PREF_THEME = "Theme";
     private static final String SHARED_PREF_URL_HIDING = "UrlHiding";
-    private static final String SHARED_PREF_FORCE_ENGAGEMENT_SIGNALS = "ForceEngagementSignals";
     private static final String SHARED_PREF_SIDE_SHEET_MAX_BUTTON = "SideSheetMaxButton";
     private static final String SHARED_PREF_SIDE_SHEET_ROUNDED_CORNER = "RoundedCorner";
     private static final String SHARED_PREF_CONTENT_SCROLL = "ContentScrollMayResizeTab";
@@ -158,7 +157,6 @@
     private CheckBox mShowTitleCheckbox;
     private CheckBox mUrlHidingCheckbox;
     private CheckBox mBackgroundInteractCheckbox;
-    private CheckBox mForceEngagementSignalsCheckbox;
     private CheckBox mSideSheetMaxButtonCheckbox;
     private CheckBox mSideSheetRoundedCornerCheckbox;
     private CheckBox mContentScrollCheckbox;
@@ -652,9 +650,6 @@
         mBackgroundInteractCheckbox = findViewById(R.id.background_interact_checkbox);
         mBackgroundInteractCheckbox.setChecked(
                 mSharedPref.getInt(SHARED_PREF_BACKGROUND_INTERACT, CHECKED) == CHECKED);
-        mForceEngagementSignalsCheckbox = findViewById(R.id.force_engagement_signals_checkbox);
-        mForceEngagementSignalsCheckbox.setChecked(
-                mSharedPref.getInt(SHARED_PREF_FORCE_ENGAGEMENT_SIGNALS, CHECKED) == CHECKED);
         mSideSheetMaxButtonCheckbox = findViewById(R.id.side_sheet_max_button_checkbox);
         mSideSheetMaxButtonCheckbox.setChecked(
                 mSharedPref.getInt(SHARED_PREF_SIDE_SHEET_MAX_BUTTON, CHECKED) == CHECKED);
@@ -1016,15 +1011,6 @@
                     mCctType.equals("Incognito CCT"));
             customTabsIntent.intent.putExtra(EXTRA_CLOSE_BUTTON_POSITION, closeButtonPosition);
         }
-        if (mForceEngagementSignalsCheckbox.isChecked()) {
-            // NOTE: this may not work because this app is not a trusted 1st party app,
-            // and CCT requires that for this feature currently.
-            // Set the command-line-flag --cct-client-firstparty-override to fake 1st-party!
-            customTabsIntent.intent.putStringArrayListExtra(
-                    "org.chromium.chrome.browser.customtabs.EXPERIMENTS_ENABLE",
-                    new ArrayList<String>(
-                            List.of("CCTRealTimeEngagementSignals", "CCTBrandTransparency")));
-        }
 
         if (startActivityForResult) {
             customTabsIntent.intent.setData(Uri.parse(url));
@@ -1072,11 +1058,6 @@
         } else {
             editor.putInt(SHARED_PREF_URL_HIDING, UNCHECKED);
         }
-        if (mForceEngagementSignalsCheckbox.isChecked()) {
-            editor.putInt(SHARED_PREF_FORCE_ENGAGEMENT_SIGNALS, CHECKED);
-        } else {
-            editor.putInt(SHARED_PREF_FORCE_ENGAGEMENT_SIGNALS, UNCHECKED);
-        }
         boolean backgroundInteract = mBackgroundInteractCheckbox.isChecked();
         if (backgroundInteract) {
             editor.putInt(SHARED_PREF_BACKGROUND_INTERACT, CHECKED);
diff --git a/chrome/browser/android/examples/custom_tabs_client/src/res/layout/main.xml b/chrome/browser/android/examples/custom_tabs_client/src/res/layout/main.xml
index e869672..4724bb2 100644
--- a/chrome/browser/android/examples/custom_tabs_client/src/res/layout/main.xml
+++ b/chrome/browser/android/examples/custom_tabs_client/src/res/layout/main.xml
@@ -586,12 +586,6 @@
                 android:checked = "true"
                 android:text="@string/url_hiding_text" />
         </LinearLayout>
-        <CheckBox
-            android:id="@+id/force_engagement_signals_checkbox"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:checked = "false"
-            android:text="@string/force_engagement_signals" />
 
         <CheckBox
             android:id="@+id/background_interact_checkbox"
diff --git a/chrome/browser/android/examples/custom_tabs_client/src/res/values/strings.xml b/chrome/browser/android/examples/custom_tabs_client/src/res/values/strings.xml
index 2573368..30b567b 100644
--- a/chrome/browser/android/examples/custom_tabs_client/src/res/values/strings.xml
+++ b/chrome/browser/android/examples/custom_tabs_client/src/res/values/strings.xml
@@ -51,7 +51,6 @@
     <string name="pcct_initial_width_slider_label_desc">PCCT initial width slider</string>
     <string name="pcct_breakpoint_text">PCCT Breakpoint</string>
     <string name="pcct_breakpoint_slider_label_desc">PCCT breakpoint slider</string>
-    <string name="force_engagement_signals">Force Engagement Signals</string>
     <string name="px_template">%dpx</string>
     <string name="url_hiding_text">Enable URL Bar Hiding (PCCT ignores)</string>
     <string name="color">Toolbar Color:</string>
diff --git a/chrome/browser/android/omnibox/autocomplete_controller_android.cc b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
index 831097f..8f23499 100644
--- a/chrome/browser/android/omnibox/autocomplete_controller_android.cc
+++ b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
@@ -128,10 +128,6 @@
       AutocompleteControllerEmitter::GetForBrowserContext(profile_);
   if (emitter)
     autocomplete_controller_->AddObserver(emitter);
-
-  if (profile) {
-    profile_observation_.Observe(profile);
-  }
 }
 
 void AutocompleteControllerAndroid::Start(JNIEnv* env,
@@ -467,21 +463,6 @@
   delete this;
 }
 
-void AutocompleteControllerAndroid::OnProfileWillBeDestroyed(Profile* profile) {
-  // In the UrlKeyedDataCollectionConsentHelper there is an assumption
-  // that the PrefChangeRegistrar outlives the PrefService. However, in
-  // the case where AutocompleteControllerAndroid owns the above consent
-  // helper through the AutocompleteProviderClient from the
-  // AutocompleteController, the Profile and ProfileKeyedService (PrefService)
-  // is destroyed before the Java profile notifies the
-  // AutocompleteControllerAndroid to destroy itself via
-  // AutocompleteControllerAndroid::Destroy.
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_AutocompleteController_notifyNativeDestroyed(env, java_controller_);
-  java_controller_.Reset();
-  Destroy(env);
-}
-
 AutocompleteControllerAndroid::~AutocompleteControllerAndroid() = default;
 
 void AutocompleteControllerAndroid::OnResultChanged(
diff --git a/chrome/browser/android/omnibox/autocomplete_controller_android.h b/chrome/browser/android/omnibox/autocomplete_controller_android.h
index 14dda07..c398d8c 100644
--- a/chrome/browser/android/omnibox/autocomplete_controller_android.h
+++ b/chrome/browser/android/omnibox/autocomplete_controller_android.h
@@ -10,8 +10,6 @@
 #include "base/android/jni_android.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
-#include "base/scoped_observation.h"
-#include "chrome/browser/profiles/profile_observer.h"
 #include "components/omnibox/browser/autocomplete_controller.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 
@@ -20,8 +18,7 @@
 class Profile;
 
 // The native part of the Java AutocompleteController class.
-class AutocompleteControllerAndroid : public AutocompleteController::Observer,
-                                      ProfileObserver {
+class AutocompleteControllerAndroid : public AutocompleteController::Observer {
  public:
   AutocompleteControllerAndroid(
       JNIEnv* env,
@@ -103,9 +100,6 @@
   void OnResultChanged(AutocompleteController* controller,
                        bool default_match_changed) override;
 
-  // ProfileObserver overrides.
-  void OnProfileWillBeDestroyed(Profile* profile) override;
-
   // Notifies the Java AutocompleteController that suggestions were received
   // based on the text the user typed in last.
   void NotifySuggestionsReceived(
@@ -135,7 +129,7 @@
   // Destruction of Profile triggers destruction of both
   // C++ AutocompleteControllerAndroid and Java AutocompleteController objects.
   // Guaranteed to be non-null.
-  base::android::ScopedJavaGlobalRef<jobject> java_controller_;
+  const base::android::ScopedJavaGlobalRef<jobject> java_controller_;
 
   // AutocompleteController associated with this client. As this is directly
   // associated with the |provider_client_| and indirectly with |profile_|
@@ -145,8 +139,6 @@
   // destroyed.
   std::unique_ptr<AutocompleteController> autocomplete_controller_;
 
-  base::ScopedObservation<Profile, ProfileObserver> profile_observation_{this};
-
   // Factory used to create asynchronously invoked callbacks.
   // Retained throughout the lifetime of the AutocompleteControllerAndroid.
   const base::WeakPtrFactory<AutocompleteControllerAndroid> weak_ptr_factory_{
diff --git a/chrome/browser/ash/app_list/launcher_search_iph_browsertest.cc b/chrome/browser/ash/app_list/launcher_search_iph_browsertest.cc
index 619741d9..19cd5b53 100644
--- a/chrome/browser/ash/app_list/launcher_search_iph_browsertest.cc
+++ b/chrome/browser/ash/app_list/launcher_search_iph_browsertest.cc
@@ -499,12 +499,13 @@
   EXPECT_FALSE(search_box_view()->assistant_button()->GetBackground());
 }
 
-// The bool param indicates if the AssistantLearnMore feature is enabled or not.
+// The bool param indicates if the kIPHLauncherSearchHelpUiFeature feature is
+// enabled or not.
 class AppListIphBrowserTestAssistantZeroState : public AppListIphBrowserTest {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     scoped_feature_list_.InitAndEnableFeature(
-        ash::assistant::features::kEnableAssistantLearnMore);
+        feature_engagement::kIPHLauncherSearchHelpUiFeature);
 
     MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
   }
@@ -515,7 +516,7 @@
 
 IN_PROC_BROWSER_TEST_P(AppListIphBrowserTest, NoAssistantZeroStateIphFlagOff) {
   ASSERT_FALSE(base::FeatureList::IsEnabled(
-      ash::assistant::features::kEnableAssistantLearnMore));
+      feature_engagement::kIPHLauncherSearchHelpUiFeature));
 
   OpenAppList();
 
diff --git a/chrome/browser/ash/app_list/search/assistant_text_search_provider.cc b/chrome/browser/ash/app_list/search/assistant_text_search_provider.cc
index 9b590c6..72b75ec 100644
--- a/chrome/browser/ash/app_list/search/assistant_text_search_provider.cc
+++ b/chrome/browser/ash/app_list/search/assistant_text_search_provider.cc
@@ -14,12 +14,13 @@
 #include "ash/public/cpp/assistant/controller/assistant_controller.h"
 #include "ash/public/cpp/assistant/controller/assistant_suggestions_controller.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "base/feature_list.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ash/app_list/search/common/icon_constants.h"
 #include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
 #include "chromeos/ui/vector_icons/vector_icons.h"
+#include "components/feature_engagement/public/feature_constants.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/paint_vector_icon.h"
@@ -37,7 +38,8 @@
 // Returns if the Assistant omnibox search provider is allowed to contribute
 // results.
 bool AreResultsAllowed() {
-  if (ash::assistant::features::IsAssistantLearnMoreEnabled()) {
+  if (base::FeatureList::IsEnabled(
+          feature_engagement::kIPHLauncherSearchHelpUiFeature)) {
     return false;
   }
 
diff --git a/chrome/browser/ash/report_controller_initializer.cc b/chrome/browser/ash/report_controller_initializer.cc
index 71753f1..2422cd69e 100644
--- a/chrome/browser/ash/report_controller_initializer.cc
+++ b/chrome/browser/ash/report_controller_initializer.cc
@@ -200,12 +200,14 @@
 void ReportControllerInitializer::OwnershipStatusChanged() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (state_ != State::kWaitingForOwnership) {
+    LOG(ERROR) << "Invalid state - expected to be waiting for ownership.";
     return;
   }
 
   // Device should only get ownership taken at most once on a browser start up.
   if (ash::DeviceSettingsService::Get()->GetOwnershipStatus() !=
       ash::DeviceSettingsService::OwnershipStatus::kOwnershipTaken) {
+    LOG(ERROR) << "Ownership status is not taken yet, returning early.";
     return;
   }
 
@@ -347,6 +349,7 @@
     // until browser restarts. Client does not proceed without signature
     // verification, so retry is not attempted. This status may be caused
     // if the policy proto blob fails the signature check.
+    LOG(ERROR) << "CrosSettings status is not trusted yet.";
     return;
   }
 
diff --git a/chrome/browser/autofill/autofill_interactive_uitest.cc b/chrome/browser/autofill/autofill_interactive_uitest.cc
index 5f60f83..15fba76e 100644
--- a/chrome/browser/autofill/autofill_interactive_uitest.cc
+++ b/chrome/browser/autofill/autofill_interactive_uitest.cc
@@ -3887,20 +3887,23 @@
     ASSERT_TRUE(waiter.Wait(num_modified_textfields));
   }
 
+  struct NameValue {
+    std::u16string name;
+    std::u16string value;
+  };
+
+  [[nodiscard]] static auto HasNameValue(const NameValue& nv) {
+    return AllOf(Field("name", &FormFieldData::name, nv.name),
+                 Field("value", &FormFieldData::value, nv.value));
+  }
+
   [[nodiscard]] static auto HasExpectedValues() {
-    struct NameValue {
-      std::u16string name;
-      std::u16string value;
-    };
     std::vector<NameValue> expected = {{u"name", u"Sarah"},
                                        {u"address", u"123 Main Road"},
                                        {u"city", u""},
                                        {u"zip", u""},
                                        {u"state", u"WA"}};
-    return FieldsAre(Map(expected, [](const NameValue& nv) {
-      return AllOf(Field("name", &FormFieldData::name, nv.name),
-                   Field("value", &FormFieldData::value, nv.value));
-    }));
+    return FieldsAre(Map(expected, HasNameValue));
   }
 
   struct NameValueUserInput {
@@ -4239,4 +4242,89 @@
   run_loop.Run();
 }
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#define MAYBE_AutofillInteractiveFormlessFormSubmissionTest \
+  DISABLED_AutofillInteractiveFormlessFormSubmissionTest
+#else
+#define MAYBE_AutofillInteractiveFormlessFormSubmissionTest \
+  AutofillInteractiveFormlessFormSubmissionTest
+#endif
+class MAYBE_AutofillInteractiveFormlessFormSubmissionTest
+    : public MAYBE_AutofillInteractiveFormSubmissionTest {
+ public:
+  MAYBE_AutofillInteractiveFormlessFormSubmissionTest() {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::
+            kAutofillDontCheckForDisappearingFormlessElementsForSubmission);
+  }
+
+  void SetUpOnMainThread() override {
+    AutofillInteractiveTestBase::SetUpOnMainThread();
+
+    SetUpServer();
+
+    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestUrl()));
+    ASSERT_TRUE(WaitForMatchingForm(
+        autofill_manager(), base::BindRepeating([](const FormStructure& form) {
+          return form.active_field_count() == 3;
+        })));
+  }
+
+  void SetUpServer() {
+    SetTestUrlResponse(R"(
+        <html><body>
+        Name: <input type='text' id='name'><br>
+        Address: <input type='text' id='address'><br>
+        City: <input type='text' id='city'><br>
+    )");
+    SetResponseForUrlPath("/success.html", "<html><body>Happy times!");
+    SetResponseForUrlPath("/xhr", "<foo>Happy times!</foo>");
+  }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Tests that a submission is detected when the following steps are done in
+// sequence:
+// 1) User fills <input>s on page without <form>.
+// 2) The page does an XHR
+// 3) The page navigates
+IN_PROC_BROWSER_TEST_F(MAYBE_AutofillInteractiveFormlessFormSubmissionTest,
+                       NavigationAfterXhr) {
+  const std::vector<FieldValue> kEnteredValues = {
+      {"name", "Sarah"}, {"address", "123 Main Road"}, {"city", "Moonbeam"}};
+
+  EnterValues(kEnteredValues, /*num_modified_textfields=*/3u);
+
+  base::RunLoop run_loop;
+  // Ensure that only expected form submissions are recorded.
+  EXPECT_CALL(*autofill_manager(), OnFormSubmittedImpl).Times(0);
+  EXPECT_CALL(
+      *autofill_manager(),
+      OnFormSubmittedImpl(FieldsAre(Map(kEnteredValues,
+                                        [](const FieldValue& fv) {
+                                          return HasNameValue(
+                                              {base::UTF8ToUTF16(fv.id),
+                                               base::UTF8ToUTF16(fv.value)});
+                                        })),
+                          /*known_success=*/false,
+                          mojom::SubmissionSource::PROBABLY_FORM_SUBMITTED))
+      .Times(1)
+      .WillRepeatedly(InvokeClosure(run_loop.QuitClosure()));
+
+  // Simulate XHR, then page navigation.
+  ExecuteScript(
+      R"(
+      const xhr = new XMLHttpRequest();
+      xhr.open('GET', '/xhr', true);
+      xhr.onload = () => {
+        setTimeout(() => {
+            window.location.href = 'success.html';
+          }, 50);
+      }
+      xhr.send(null);
+      )");
+  run_loop.Run();
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 15e4fdc..479bfe2 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -169,6 +169,7 @@
 #include "chrome/browser/intranet_redirect_detector.h"
 #include "chrome/browser/lifetime/application_lifetime_desktop.h"
 #include "chrome/browser/resource_coordinator/tab_manager.h"
+#include "chrome/browser/search_engine_choice/search_engine_choice_profile_tagger.h"
 #include "chrome/browser/serial/serial_policy_allowed_ports.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -234,10 +235,6 @@
 #include "chrome/browser/usb/usb_status_icon.h"
 #endif
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-#include "chrome/browser/search_engine_choice/search_engine_choice_profile_tagger.h"
-#endif  // BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-
 #if BUILDFLAG(IS_WIN) || (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
 // How often to check if the persistent instance of Chrome needs to restart
 // to install an update.
@@ -1163,10 +1160,10 @@
   base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
   profile_manager_ = std::make_unique<ProfileManager>(user_data_dir);
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
+#if !BUILDFLAG(IS_ANDROID)
   search_engine_choice_profile_tagger_ =
       SearchEngineChoiceProfileTagger::Create(*profile_manager_.get());
-#endif  // BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
+#endif
 }
 
 void BrowserProcessImpl::PreCreateThreads() {
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index af96779..095d620 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -29,7 +29,6 @@
 #include "components/prefs/persistent_pref_store.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/services/screen_ai/buildflags/buildflags.h"
-#include "components/signin/public/base/signin_buildflags.h"
 #include "extensions/buildflags/buildflags.h"
 #include "media/media_buildflags.h"
 #include "ppapi/buildflags/buildflags.h"
@@ -456,6 +455,9 @@
   std::unique_ptr<UsbSystemTrayIcon> usb_system_tray_icon_;
 
   BuildState build_state_;
+
+  std::unique_ptr<SearchEngineChoiceProfileTagger>
+      search_engine_choice_profile_tagger_;
 #endif
 
 #if BUILDFLAG(IS_ANDROID)
@@ -469,11 +471,6 @@
 
   std::unique_ptr<os_crypt_async::OSCryptAsync> os_crypt_async_;
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-  std::unique_ptr<SearchEngineChoiceProfileTagger>
-      search_engine_choice_profile_tagger_;
-#endif  // BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
diff --git a/chrome/browser/chrome_browser_field_trials.cc b/chrome/browser/chrome_browser_field_trials.cc
index 26fedba..59cd3552 100644
--- a/chrome/browser/chrome_browser_field_trials.cc
+++ b/chrome/browser/chrome_browser_field_trials.cc
@@ -22,7 +22,6 @@
 #include "chrome/common/chrome_switches.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/persistent_histograms.h"
-#include "components/signin/public/base/signin_buildflags.h"
 #include "components/version_info/version_info.h"
 
 #if BUILDFLAG(IS_ANDROID)
@@ -32,6 +31,8 @@
 #include "chrome/browser/android/flags/chrome_cached_flags.h"
 #include "chrome/browser/flags/android/chrome_feature_list.h"
 #include "chrome/common/chrome_features.h"
+#else
+#include "chrome/browser/search_engine_choice/search_engine_choice_client_side_trial.h"
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -45,10 +46,6 @@
 #include "chromeos/startup/startup.h"  // nogncheck
 #endif
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-#include "chrome/browser/search_engine_choice/search_engine_choice_client_side_trial.h"
-#endif
-
 ChromeBrowserFieldTrials::ChromeBrowserFieldTrials(PrefService* local_state)
     : local_state_(local_state) {
   DCHECK(local_state_);
@@ -100,10 +97,10 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     ash::multidevice_setup::CreateFirstRunFieldTrial(feature_list);
 #endif
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
+#if !BUILDFLAG(IS_ANDROID)
     SearchEngineChoiceClientSideTrial::SetUpIfNeeded(
         entropy_providers.default_entropy(), feature_list, local_state_);
-#endif  // BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
+#endif
   }
 }
 
@@ -157,8 +154,7 @@
     ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
         kBackgroundThreadPoolTrial, group_name);
   }
-#endif  // BUILDFLAG(IS_ANDROID)
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
+#else
   SearchEngineChoiceClientSideTrial::RegisterSyntheticTrials();
-#endif  // BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
+#endif  // BUILDFLAG(IS_ANDROID)
 }
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index c0bb5257f..bdf1aba3 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -84,7 +84,6 @@
 #include "components/security_state/content/content_utils.h"
 #include "components/security_state/core/security_state.h"
 #include "components/services/screen_ai/buildflags/buildflags.h"
-#include "components/signin/public/base/signin_buildflags.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/site_engagement/core/mojom/site_engagement_details.mojom.h"
 #include "components/translate/content/common/translate.mojom.h"
@@ -184,6 +183,8 @@
 #include "chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.h"
 #include "chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h"
 #include "chrome/browser/ui/webui/password_manager/password_manager_ui.h"
+#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice.mojom.h"  // nogncheck crbug.com/1125897
+#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.h"
 #include "chrome/browser/ui/webui/settings/settings_ui.h"
 #include "chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.h"
 #include "chrome/browser/ui/webui/side_panel/companion/companion_side_panel_untrusted_ui.h"
@@ -466,11 +467,6 @@
 }
 #endif
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice.mojom.h"
-#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.h"
-#endif
-
 #if BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/ui/webui/dlp_internals/dlp_internals.mojom.h"
 #include "chrome/browser/ui/webui/dlp_internals/dlp_internals_ui.h"
@@ -1105,15 +1101,6 @@
                                          policy::DlpInternalsUI>(map);
 #endif
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-  if (search_engines::IsChoiceScreenFlagEnabled(
-          search_engines::ChoicePromo::kAny)) {
-    RegisterWebUIControllerInterfaceBinder<
-        search_engine_choice::mojom::PageHandlerFactory, SearchEngineChoiceUI>(
-        map);
-  }
-#endif
-
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
     BUILDFLAG(IS_FUCHSIA)
   RegisterWebUIControllerInterfaceBinder<
@@ -1121,6 +1108,13 @@
 #endif
 
 #if !BUILDFLAG(IS_ANDROID)
+  if (search_engines::IsChoiceScreenFlagEnabled(
+          search_engines::ChoicePromo::kAny)) {
+    RegisterWebUIControllerInterfaceBinder<
+        search_engine_choice::mojom::PageHandlerFactory, SearchEngineChoiceUI>(
+        map);
+  }
+
   RegisterWebUIControllerInterfaceBinder<downloads::mojom::PageHandlerFactory,
                                          DownloadsUI>(map);
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index c484f16c..8e13d15f 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -5256,8 +5256,7 @@
   auto* privacy_sandbox_settings =
       PrivacySandboxSettingsFactory::GetForProfile(profile);
   if (privacy_sandbox_settings &&
-      privacy_sandbox_settings->AreRelatedWebsiteSetsEnabled() &&
-      base::FeatureList::IsEnabled(features::kFirstPartySets)) {
+      privacy_sandbox_settings->AreRelatedWebsiteSetsEnabled()) {
     MaybeAddThrottle(first_party_sets::FirstPartySetsNavigationThrottle::
                          MaybeCreateNavigationThrottle(handle),
                      &throttles);
@@ -7848,8 +7847,7 @@
 #if BUILDFLAG(ENABLE_COMPONENT_UPDATER)
   return !is_minimal_mode_ &&
          !base::CommandLine::ForCurrentProcess()->HasSwitch(
-             switches::kDisableComponentUpdate) &&
-         base::FeatureList::IsEnabled(features::kFirstPartySets);
+             switches::kDisableComponentUpdate);
 #else
   return false;
 #endif  // BUILDFLAG(ENABLE_COMPONENT_UPDATER)
diff --git a/chrome/browser/chrome_for_testing/args.gni b/chrome/browser/chrome_for_testing/args.gni
index e466444..5684421 100644
--- a/chrome/browser/chrome_for_testing/args.gni
+++ b/chrome/browser/chrome_for_testing/args.gni
@@ -4,16 +4,12 @@
 
 import("//build/config/features.gni")
 import("//chrome/installer/installers.gni")
-import("//components/signin/features.gni")
 import("//media/media_options.gni")
 import("//third_party/ffmpeg/ffmpeg_options.gni")
 
 # chrome/installer/installers.gni
 enable_linux_installer = false
 
-# components/signin/features.gni
-enable_search_engine_choice = false
-
 # build/config/features.gni
 proprietary_codecs = true
 
diff --git a/chrome/browser/component_updater/first_party_sets_component_installer.h b/chrome/browser/component_updater/first_party_sets_component_installer.h
index 63f6c5d..1db97cc 100644
--- a/chrome/browser/component_updater/first_party_sets_component_installer.h
+++ b/chrome/browser/component_updater/first_party_sets_component_installer.h
@@ -54,24 +54,21 @@
                                        const base::FilePath& install_dir,
                                        base::StringPiece contents);
 
- private:
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
-                           NonexistentFile_OnComponentReady);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
-                           NonexistentFile_OnRegistrationComplete);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
-                           LoadsSets_OnComponentReady);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
-                           IgnoreNewSets_NoInitialComponent);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
-                           IgnoreNewSets_OnComponentReady);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
-                           IgnoreNewSets_OnNetworkRestart);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureDisabledTest,
-                           GetInstallerAttributes);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
-                           GetInstallerAttributes);
+  static base::FilePath GetInstalledPathForTesting(const base::FilePath& base) {
+    return GetInstalledPath(base);
+  }
 
+  void ComponentReadyForTesting(const base::Version& version,
+                                const base::FilePath& install_dir,
+                                base::Value::Dict manifest) {
+    ComponentReady(version, install_dir, std::move(manifest));
+  }
+
+  update_client::InstallerAttributes GetInstallerAttributesForTesting() const {
+    return GetInstallerAttributes();
+  }
+
+ private:
   // The following methods override ComponentInstallerPolicy.
   bool SupportsGroupPolicyEnabledComponentUpdates() const override;
   bool RequiresNetworkEncryption() const override;
diff --git a/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc b/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc
index ae7c813..73b5659 100644
--- a/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc
+++ b/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/functional/callback_helpers.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/version.h"
@@ -45,75 +44,34 @@
     FirstPartySetsComponentInstallerPolicy::ResetForTesting();
   }
 
-  // Subclasses are expected to call this in their constructors.
-  virtual void InitializeFeatureList() = 0;
-
  protected:
   base::test::TaskEnvironment env_;
 
   base::ScopedTempDir component_install_dir_;
-  base::test::ScopedFeatureList scoped_feature_list_;
   first_party_sets::ScopedMockFirstPartySetsHandler
       mock_first_party_sets_handler_;
 };
 
-class FirstPartySetsComponentInstallerFeatureEnabledTest
-    : public FirstPartySetsComponentInstallerTest {
- public:
-  FirstPartySetsComponentInstallerFeatureEnabledTest() {
-    InitializeFeatureList();
-  }
-
-  void InitializeFeatureList() override {
-    scoped_feature_list_.InitAndEnableFeature(features::kFirstPartySets);
-  }
-};
-
-class FirstPartySetsComponentInstallerFeatureDisabledTest
-    : public FirstPartySetsComponentInstallerTest {
- public:
-  FirstPartySetsComponentInstallerFeatureDisabledTest() {
-    InitializeFeatureList();
-  }
-
-  void InitializeFeatureList() override {
-    scoped_feature_list_.InitAndDisableFeature(features::kFirstPartySets);
-  }
-};
-
-TEST_F(FirstPartySetsComponentInstallerFeatureDisabledTest, FeatureDisabled) {
-  auto service =
-      std::make_unique<component_updater::MockComponentUpdateService>();
-
-  // We still install the component and subscribe to updates even when the
-  // feature is disabled, so that if the feature eventually gets enabled, we
-  // will already have the requisite data.
-  EXPECT_CALL(*service, RegisterComponent(_)).Times(1);
-  RegisterFirstPartySetsComponent(service.get());
-
-  env_.RunUntilIdle();
-}
-
-TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
-       NonexistentFile_OnComponentReady) {
-  ASSERT_TRUE(
-      base::DeleteFile(FirstPartySetsComponentInstallerPolicy::GetInstalledPath(
+TEST_F(FirstPartySetsComponentInstallerTest, NonexistentFile_OnComponentReady) {
+  ASSERT_TRUE(base::DeleteFile(
+      FirstPartySetsComponentInstallerPolicy::GetInstalledPathForTesting(
           component_install_dir_.GetPath())));
 
   base::test::TestFuture<base::Version, base::File> future;
   FirstPartySetsComponentInstallerPolicy(future.GetCallback())
-      .ComponentReady(base::Version(), component_install_dir_.GetPath(),
-                      base::Value::Dict());
+      .ComponentReadyForTesting(base::Version(),
+                                component_install_dir_.GetPath(),
+                                base::Value::Dict());
 
   std::tuple<base::Version, base::File> got = future.Take();
   EXPECT_FALSE(std::get<0>(got).IsValid());
   EXPECT_FALSE(std::get<1>(got).IsValid());
 }
 
-TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
+TEST_F(FirstPartySetsComponentInstallerTest,
        NonexistentFile_OnRegistrationComplete) {
-  ASSERT_TRUE(
-      base::DeleteFile(FirstPartySetsComponentInstallerPolicy::GetInstalledPath(
+  ASSERT_TRUE(base::DeleteFile(
+      FirstPartySetsComponentInstallerPolicy::GetInstalledPathForTesting(
           component_install_dir_.GetPath())));
 
   base::test::TestFuture<base::Version, base::File> future;
@@ -129,21 +87,20 @@
   env_.RunUntilIdle();
 }
 
-TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
-       LoadsSets_OnComponentReady) {
+TEST_F(FirstPartySetsComponentInstallerTest, LoadsSets_OnComponentReady) {
   const base::Version version = base::Version("0.0.1");
   const std::string expectation = "some first party sets";
   base::test::TestFuture<base::Version, base::File> future;
   auto policy = std::make_unique<FirstPartySetsComponentInstallerPolicy>(
       future.GetCallback());
 
-  ASSERT_TRUE(
-      base::WriteFile(FirstPartySetsComponentInstallerPolicy::GetInstalledPath(
-                          component_install_dir_.GetPath()),
-                      expectation));
+  ASSERT_TRUE(base::WriteFile(
+      FirstPartySetsComponentInstallerPolicy::GetInstalledPathForTesting(
+          component_install_dir_.GetPath()),
+      expectation));
 
-  policy->ComponentReady(version, component_install_dir_.GetPath(),
-                         base::Value::Dict());
+  policy->ComponentReadyForTesting(version, component_install_dir_.GetPath(),
+                                   base::Value::Dict());
 
   std::tuple<base::Version, base::File> got = future.Take();
   EXPECT_TRUE(std::get<0>(got).IsValid());
@@ -155,8 +112,7 @@
 // Test that when the first version of the component is installed,
 // ComponentReady is a no-op, because OnRegistrationComplete already executed
 // the OnceCallback.
-TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
-       IgnoreNewSets_NoInitialComponent) {
+TEST_F(FirstPartySetsComponentInstallerTest, IgnoreNewSets_NoInitialComponent) {
   base::test::TestFuture<base::Version, base::File> future;
   FirstPartySetsComponentInstallerPolicy policy(future.GetCallback());
 
@@ -169,20 +125,19 @@
   base::ScopedTempDir install_dir;
   ASSERT_TRUE(install_dir.CreateUniqueTempDirUnderPath(
       component_install_dir_.GetPath()));
-  ASSERT_TRUE(
-      base::WriteFile(FirstPartySetsComponentInstallerPolicy::GetInstalledPath(
-                          install_dir.GetPath()),
-                      "first party sets content"));
-  policy.ComponentReady(base::Version("0.0.1"), install_dir.GetPath(),
-                        base::Value::Dict());
+  ASSERT_TRUE(base::WriteFile(
+      FirstPartySetsComponentInstallerPolicy::GetInstalledPathForTesting(
+          install_dir.GetPath()),
+      "first party sets content"));
+  policy.ComponentReadyForTesting(base::Version("0.0.1"), install_dir.GetPath(),
+                                  base::Value::Dict());
 
   env_.RunUntilIdle();
 }
 
 // Test if a component has been installed, ComponentReady will be no-op when
 // newer versions are installed.
-TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
-       IgnoreNewSets_OnComponentReady) {
+TEST_F(FirstPartySetsComponentInstallerTest, IgnoreNewSets_OnComponentReady) {
   base::test::TestFuture<base::Version, base::File> future;
   FirstPartySetsComponentInstallerPolicy policy(future.GetCallback());
 
@@ -191,11 +146,12 @@
   base::ScopedTempDir dir_v1;
   ASSERT_TRUE(
       dir_v1.CreateUniqueTempDirUnderPath(component_install_dir_.GetPath()));
-  ASSERT_TRUE(
-      base::WriteFile(FirstPartySetsComponentInstallerPolicy::GetInstalledPath(
-                          dir_v1.GetPath()),
-                      sets_v1));
-  policy.ComponentReady(version, dir_v1.GetPath(), base::Value::Dict());
+  ASSERT_TRUE(base::WriteFile(
+      FirstPartySetsComponentInstallerPolicy::GetInstalledPathForTesting(
+          dir_v1.GetPath()),
+      sets_v1));
+  policy.ComponentReadyForTesting(version, dir_v1.GetPath(),
+                                  base::Value::Dict());
 
   std::tuple<base::Version, base::File> got = future.Take();
   EXPECT_TRUE(std::get<0>(got).IsValid());
@@ -209,28 +165,20 @@
   base::ScopedTempDir dir_v2;
   ASSERT_TRUE(
       dir_v2.CreateUniqueTempDirUnderPath(component_install_dir_.GetPath()));
-  ASSERT_TRUE(
-      base::WriteFile(FirstPartySetsComponentInstallerPolicy::GetInstalledPath(
-                          dir_v2.GetPath()),
-                      sets_v2));
-  policy.ComponentReady(base::Version("0.0.1"), dir_v2.GetPath(),
-                        base::Value::Dict());
+  ASSERT_TRUE(base::WriteFile(
+      FirstPartySetsComponentInstallerPolicy::GetInstalledPathForTesting(
+          dir_v2.GetPath()),
+      sets_v2));
+  policy.ComponentReadyForTesting(base::Version("0.0.1"), dir_v2.GetPath(),
+                                  base::Value::Dict());
 
   env_.RunUntilIdle();
 }
 
-TEST_F(FirstPartySetsComponentInstallerFeatureDisabledTest,
-       GetInstallerAttributes) {
+TEST_F(FirstPartySetsComponentInstallerTest, GetInstallerAttributes) {
   FirstPartySetsComponentInstallerPolicy policy(base::DoNothing());
 
-  EXPECT_THAT(policy.GetInstallerAttributes(), IsEmpty());
-}
-
-TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
-       GetInstallerAttributes) {
-  FirstPartySetsComponentInstallerPolicy policy(base::DoNothing());
-
-  EXPECT_THAT(policy.GetInstallerAttributes(), IsEmpty());
+  EXPECT_THAT(policy.GetInstallerAttributesForTesting(), IsEmpty());
 }
 
 }  // namespace component_updater
diff --git a/chrome/browser/compose/compose_enabling.cc b/chrome/browser/compose/compose_enabling.cc
index d89e1f3..1bb5683 100644
--- a/chrome/browser/compose/compose_enabling.cc
+++ b/chrome/browser/compose/compose_enabling.cc
@@ -316,7 +316,7 @@
     return base::unexpected(compose::ComposeShowStatus::kUnsupportedLanguage);
   }
 
-  // TODO(b/301609046): Check that we have enough space in the browser window to
+  // TODO(b/316628813): Check that we have enough space in the browser window to
   // show the dialog.
 
   return base::ok();
diff --git a/chrome/browser/compose/compose_session.cc b/chrome/browser/compose/compose_session.cc
index b01d7e7..257eea4 100644
--- a/chrome/browser/compose/compose_session.cc
+++ b/chrome/browser/compose/compose_session.cc
@@ -490,6 +490,15 @@
     // feedback to.
     return;
   }
+  OptimizationGuideKeyedService* opt_guide_keyed_service =
+      OptimizationGuideKeyedServiceFactory::GetForProfile(
+          Profile::FromBrowserContext(web_contents_->GetBrowserContext()));
+  if (!opt_guide_keyed_service ||
+      !opt_guide_keyed_service->ShouldFeatureBeCurrentlyAllowedForLogging(
+          optimization_guide::proto::MODEL_EXECUTION_FEATURE_COMPOSE)) {
+    return;
+  }
+
   // Add to most_recent_ok_state_ in case of undos.
   most_recent_ok_state_->mojo_state()->feedback = feedback;
 
diff --git a/chrome/browser/dev_ui_browser_resources.grd b/chrome/browser/dev_ui_browser_resources.grd
index e2988a11..205307e4 100644
--- a/chrome/browser/dev_ui_browser_resources.grd
+++ b/chrome/browser/dev_ui_browser_resources.grd
@@ -18,10 +18,6 @@
   </outputs>
   <release seq="1">
     <includes>
-      <include name="IDR_DEVICE_LOG_UI_HTML" file="resources\device_log_ui\device_log_ui.html" preprocess="true" type="BINDATA" />
-      <include name="IDR_DEVICE_LOG_UI_JS" file="resources\device_log_ui\device_log_ui.js" preprocess="true" type="BINDATA" />
-      <include name="IDR_DEVICE_LOG_UI_CSS" file="resources\device_log_ui\device_log_ui.css" type="BINDATA" />
-
       <if expr="enable_supervised_users">
         <include name="IDR_FAMILY_LINK_USER_INTERNALS_HTML" file="resources\family_link_user_internals\family_link_user_internals.html" allowexternalscript="true" type="BINDATA" />
         <include name="IDR_FAMILY_LINK_USER_INTERNALS_CSS" file="resources\family_link_user_internals\family_link_user_internals.css" type="BINDATA" />
diff --git a/chrome/browser/download/bubble/download_bubble_ui_controller.cc b/chrome/browser/download/bubble/download_bubble_ui_controller.cc
index 384502eb6..be600b7 100644
--- a/chrome/browser/download/bubble/download_bubble_ui_controller.cc
+++ b/chrome/browser/download/bubble/download_bubble_ui_controller.cc
@@ -177,6 +177,9 @@
   bool is_done = item->IsDone() ||
                  (item->GetState() == download::DownloadItem::IN_PROGRESS &&
                   !IsItemInProgress(item));
+  if (model.IsDangerous()) {
+    RecordDangerousDownloadShownToUser();
+  }
   display_controller_->OnUpdatedItem(is_done, may_show_details);
 }
 
@@ -379,6 +382,13 @@
   tracker->NotifyEvent("download_bubble_interaction");
 }
 
+void DownloadBubbleUIController::RecordDangerousDownloadShownToUser() {
+  feature_engagement::Tracker* tracker =
+      feature_engagement::TrackerFactory::GetForBrowserContext(
+          browser_->profile());
+  tracker->NotifyEvent("download_bubble_dangerous_download_detected");
+}
+
 base::WeakPtr<DownloadBubbleUIController>
 DownloadBubbleUIController::GetWeakPtr() {
   return weak_factory_.GetWeakPtr();
diff --git a/chrome/browser/download/bubble/download_bubble_ui_controller.h b/chrome/browser/download/bubble/download_bubble_ui_controller.h
index e2a20195..e5be284 100644
--- a/chrome/browser/download/bubble/download_bubble_ui_controller.h
+++ b/chrome/browser/download/bubble/download_bubble_ui_controller.h
@@ -108,6 +108,11 @@
   // quantitatively to count the number of such interactions.
   void RecordDownloadBubbleInteraction();
 
+  // Records that a dangerous download was shown to the user. This only
+  // records the fact that an interaction occurred, and should not be
+  // used quantitatively to count the number of such interactions.
+  void RecordDangerousDownloadShownToUser();
+
   // Returns the DownloadDisplayController. Should always return a valid
   // controller.
   DownloadDisplayController* GetDownloadDisplayController() {
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc
index dcb7256..47431bc 100644
--- a/chrome/browser/download/download_browsertest.cc
+++ b/chrome/browser/download/download_browsertest.cc
@@ -4578,13 +4578,6 @@
   bool final_state_seen_;
 };
 
-#if BUILDFLAG(IS_WIN)
-const char kDangerousMockFilePath[] = "/downloads/dangerous/dangerous.exe";
-#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
-// TODO(crbug.com/1264058): Find an actually "dangerous" extension for Fuchsia.
-const char kDangerousMockFilePath[] = "/downloads/dangerous/dangerous.sh";
-#endif
-
 }  // namespace
 
 IN_PROC_BROWSER_TEST_F(DownloadTest,
@@ -4593,7 +4586,8 @@
                                                true);
   embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory());
   ASSERT_TRUE(embedded_test_server()->Start());
-  GURL download_url = embedded_test_server()->GetURL(kDangerousMockFilePath);
+  GURL download_url =
+      embedded_test_server()->GetURL(DownloadTestBase::kDangerousMockFilePath);
 
   std::unique_ptr<content::DownloadTestObserver> dangerous_observer(
       DangerousDownloadWaiter(
@@ -4629,7 +4623,8 @@
                                                false);
   embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory());
   ASSERT_TRUE(embedded_test_server()->Start());
-  GURL download_url = embedded_test_server()->GetURL(kDangerousMockFilePath);
+  GURL download_url =
+      embedded_test_server()->GetURL(DownloadTestBase::kDangerousMockFilePath);
 
   std::unique_ptr<content::DownloadTestObserver> dangerous_observer(
       DangerousDownloadWaiter(
@@ -4804,7 +4799,8 @@
   // Make a dangerous file.
   embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory());
   ASSERT_TRUE(embedded_test_server()->Start());
-  GURL download_url = embedded_test_server()->GetURL(kDangerousMockFilePath);
+  GURL download_url =
+      embedded_test_server()->GetURL(DownloadTestBase::kDangerousMockFilePath);
 
   std::unique_ptr<content::DownloadTestObserver> dangerous_observer(
       DangerousDownloadWaiter(
@@ -4842,7 +4838,8 @@
   // Make a dangerous file.
   embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory());
   ASSERT_TRUE(embedded_test_server()->Start());
-  GURL download_url = embedded_test_server()->GetURL(kDangerousMockFilePath);
+  GURL download_url =
+      embedded_test_server()->GetURL(DownloadTestBase::kDangerousMockFilePath);
   auto* download_protection_service =
       static_cast<FakeDownloadProtectionService*>(
           g_browser_process->safe_browsing_service()
@@ -4892,7 +4889,8 @@
   // Make a dangerous file.
   embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory());
   ASSERT_TRUE(embedded_test_server()->Start());
-  GURL download_url = embedded_test_server()->GetURL(kDangerousMockFilePath);
+  GURL download_url =
+      embedded_test_server()->GetURL(DownloadTestBase::kDangerousMockFilePath);
   std::unique_ptr<content::DownloadTestObserver> dangerous_observer(
       DangerousDownloadWaiter(
           browser(), 1,
@@ -4929,7 +4927,8 @@
   // Make a dangerous file.
   embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory());
   ASSERT_TRUE(embedded_test_server()->Start());
-  GURL download_url = embedded_test_server()->GetURL(kDangerousMockFilePath);
+  GURL download_url =
+      embedded_test_server()->GetURL(DownloadTestBase::kDangerousMockFilePath);
 
   std::unique_ptr<content::DownloadTestObserver> dangerous_observer(
       DangerousDownloadWaiter(
diff --git a/chrome/browser/download/download_browsertest_utils.h b/chrome/browser/download/download_browsertest_utils.h
index c3d06e1..8a7095c 100644
--- a/chrome/browser/download/download_browsertest_utils.h
+++ b/chrome/browser/download/download_browsertest_utils.h
@@ -97,6 +97,15 @@
   };
 
   static constexpr char kDownloadTest1Path[] = "download-test1.lib";
+#if BUILDFLAG(IS_WIN)
+  static constexpr char kDangerousMockFilePath[] =
+      "/downloads/dangerous/dangerous.exe";
+#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
+  // TODO(crbug.com/1264058): Find an actually "dangerous" extension for
+  // Fuchsia.
+  static constexpr char kDangerousMockFilePath[] =
+      "/downloads/dangerous/dangerous.sh";
+#endif
 
   DownloadTestBase();
   ~DownloadTestBase() override;
diff --git a/chrome/browser/download/download_item_model_unittest.cc b/chrome/browser/download/download_item_model_unittest.cc
index bc93573..6d7ab4d5 100644
--- a/chrome/browser/download/download_item_model_unittest.cc
+++ b/chrome/browser/download/download_item_model_unittest.cc
@@ -1069,12 +1069,14 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-// Test file type warning where SB is on and verdict was obtained.
+// Test file type warning where verdict was obtained.
 TEST_F(DownloadItemModelImprovedDownloadBubbleWarningsTest,
-       FileTypeWarning_SafeBrowsingOn_HasVerdict) {
-  for (auto sb_state :
-       {safe_browsing::SafeBrowsingState::STANDARD_PROTECTION,
-        safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION}) {
+       FileTypeWarning_HasSafeBrowsingVerdict) {
+  for (auto sb_state : {safe_browsing::SafeBrowsingState::STANDARD_PROTECTION,
+                        safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION,
+                        // This can happen if the user subsequently turned off
+                        // SB after verdict was obtained.
+                        safe_browsing::SafeBrowsingState::NO_SAFE_BROWSING}) {
     SetSafeBrowsingState(profile()->GetPrefs(), sb_state);
     EXPECT_CALL(item(), GetDangerType())
         .WillRepeatedly(Return(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE));
diff --git a/chrome/browser/download/download_ui_model.cc b/chrome/browser/download/download_ui_model.cc
index fe26fffb..f600c95 100644
--- a/chrome/browser/download/download_ui_model.cc
+++ b/chrome/browser/download/download_ui_model.cc
@@ -1168,17 +1168,21 @@
       } else {
         if (base::FeatureList::IsEnabled(
                 safe_browsing::kImprovedDownloadBubbleWarnings)) {
+          if (WasSafeBrowsingVerdictObtained(GetDownloadItem())) {
+            return DownloadUIModel::BubbleUIInfo::SuspiciousUiPattern(
+                l10n_util::GetStringUTF16(
+                    IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_DANGEROUS_FILE_TYPE),
+                l10n_util::GetStringUTF16(
+                    IDS_DOWNLOAD_BUBBLE_CONTINUE_SUSPICIOUS_FILE));
+          }
           if (ShouldShowWarningForNoSafeBrowsing(profile())) {
             return GetBubbleUIInfoForFileTypeWarningNoSafeBrowsing();
           }
           return DownloadUIModel::BubbleUIInfo::SuspiciousUiPattern(
               l10n_util::GetStringUTF16(
                   IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_DANGEROUS_FILE_TYPE),
-              WasSafeBrowsingVerdictObtained(GetDownloadItem())
-                  ? l10n_util::GetStringUTF16(
-                        IDS_DOWNLOAD_BUBBLE_CONTINUE_SUSPICIOUS_FILE)
-                  : l10n_util::GetStringUTF16(
-                        IDS_DOWNLOAD_BUBBLE_CONTINUE_UNVERIFIED_FILE));
+              l10n_util::GetStringUTF16(
+                  IDS_DOWNLOAD_BUBBLE_CONTINUE_UNVERIFIED_FILE));
         }
         return DownloadUIModel::BubbleUIInfo()
             .AddSubpageSummary(
@@ -1705,21 +1709,21 @@
 
   switch (model_->GetDangerType()) {
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
+      if (model_->IsExtensionDownload()) {
+        // "Blocked • Unknown source"
+        return get_blocked_warning(IDS_DOWNLOAD_BUBBLE_STATUS_UNKNOWN_SOURCE);
+      }
       if (base::FeatureList::IsEnabled(
               safe_browsing::kImprovedDownloadBubbleWarnings)) {
-        if (ShouldShowWarningForNoSafeBrowsing(model_->profile()) ||
-            !WasSafeBrowsingVerdictObtained(model_->GetDownloadItem())) {
-          // "Unverified download blocked"
+        if (WasSafeBrowsingVerdictObtained(model_->GetDownloadItem())) {
+          // "Suspicious download blocked"
           return l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_STATUS_WARNING_UNVERIFIED);
+              IDS_DOWNLOAD_BUBBLE_STATUS_WARNING_SUSPICIOUS);
         }
-        // "Suspicious download blocked"
+        // "Unverified download blocked"
         return l10n_util::GetStringUTF16(
-            IDS_DOWNLOAD_BUBBLE_STATUS_WARNING_SUSPICIOUS);
+            IDS_DOWNLOAD_BUBBLE_STATUS_WARNING_UNVERIFIED);
       }
-      // "Blocked • Unknown source"
-      if (model_->IsExtensionDownload())
-        return get_blocked_warning(IDS_DOWNLOAD_BUBBLE_STATUS_UNKNOWN_SOURCE);
       [[fallthrough]];
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
diff --git a/chrome/browser/download/download_ui_model.h b/chrome/browser/download/download_ui_model.h
index 9b3d21b..ab06b0a2 100644
--- a/chrome/browser/download/download_ui_model.h
+++ b/chrome/browser/download/download_ui_model.h
@@ -110,6 +110,8 @@
   };
 
 #if !BUILDFLAG(IS_ANDROID)
+  // Keep UI logic in this class in sync with DownloadBubbleRowViewInfo.
+  // TODO(crbug.com/1482901): Unify the logic.
   struct BubbleUIInfo {
     struct SubpageButton {
       DownloadCommands::Command command;
diff --git a/chrome/browser/first_party_sets/first_party_sets_navigation_throttle.cc b/chrome/browser/first_party_sets/first_party_sets_navigation_throttle.cc
index cff8ff3..35e13ad 100644
--- a/chrome/browser/first_party_sets/first_party_sets_navigation_throttle.cc
+++ b/chrome/browser/first_party_sets/first_party_sets_navigation_throttle.cc
@@ -17,7 +17,6 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/content_features.h"
 #include "net/base/features.h"
 
 namespace first_party_sets {
@@ -50,7 +49,9 @@
                        weak_factory_.GetWeakPtr()));
     // Setup timer
     resume_navigation_timer_.Start(
-        FROM_HERE, features::kFirstPartySetsNavigationThrottleTimeout.Get(),
+        FROM_HERE,
+        net::features::kWaitForFirstPartySetsInitNavigationThrottleTimeout
+            .Get(),
         base::BindOnce(&FirstPartySetsNavigationThrottle::OnTimeOut,
                        weak_factory_.GetWeakPtr()));
 
@@ -85,8 +86,8 @@
   if (service->is_ready() ||
       !base::FeatureList::IsEnabled(
           net::features::kWaitForFirstPartySetsInit) ||
-      features::kFirstPartySetsNavigationThrottleTimeout.Get().is_zero() ||
-      !features::kFirstPartySetsClearSiteDataOnChangedSets.Get() ||
+      net::features::kWaitForFirstPartySetsInitNavigationThrottleTimeout.Get()
+          .is_zero() ||
       navigation_handle->GetParentFrameOrOuterDocument()) {
     return nullptr;
   }
diff --git a/chrome/browser/first_party_sets/first_party_sets_navigation_throttle_unittest.cc b/chrome/browser/first_party_sets/first_party_sets_navigation_throttle_unittest.cc
index 37d242f0..4c19956 100644
--- a/chrome/browser/first_party_sets/first_party_sets_navigation_throttle_unittest.cc
+++ b/chrome/browser/first_party_sets/first_party_sets_navigation_throttle_unittest.cc
@@ -35,11 +35,10 @@
             base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
     features_.InitWithFeaturesAndParameters(
         {
-            {features::kFirstPartySets,
-             {{features::kFirstPartySetsNavigationThrottleTimeout.name, "2s"},
-              {features::kFirstPartySetsClearSiteDataOnChangedSets.name,
-               "true"}}},
-            {net::features::kWaitForFirstPartySetsInit, {}},
+            {net::features::kWaitForFirstPartySetsInit,
+             {{net::features::
+                   kWaitForFirstPartySetsInitNavigationThrottleTimeout.name,
+               "2s"}}},
         },
         {{blink::features::kStorageAccessAPI}});
   }
@@ -78,20 +77,6 @@
 };
 
 TEST_F(FirstPartySetsNavigationThrottleTest,
-       MaybeCreateNavigationThrottle_ClearingFeatureDisabled) {
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeatureWithParameters(
-      features::kFirstPartySets,
-      {{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "false"}});
-
-  content::MockNavigationHandle handle(GURL(kExampleURL), main_rfh());
-  ASSERT_TRUE(handle.IsInOutermostMainFrame());
-
-  EXPECT_FALSE(
-      FirstPartySetsNavigationThrottle::MaybeCreateNavigationThrottle(&handle));
-}
-
-TEST_F(FirstPartySetsNavigationThrottleTest,
        MaybeCreateNavigationThrottle_ClearingFeatureEnabled) {
   content::MockNavigationHandle handle(GURL(kExampleURL), main_rfh());
   ASSERT_TRUE(handle.IsInOutermostMainFrame());
@@ -248,10 +233,11 @@
  public:
   FirstPartySetsNavigationThrottleNoDelayTest() {
     features_.InitAndEnableFeatureWithParameters(
-        features::kFirstPartySets,
+        net::features::kWaitForFirstPartySetsInit,
         {
-            {features::kFirstPartySetsClearSiteDataOnChangedSets.name, "true"},
-            {features::kFirstPartySetsNavigationThrottleTimeout.name, "0s"},
+            {net::features::kWaitForFirstPartySetsInitNavigationThrottleTimeout
+                 .name,
+             "0s"},
         });
   }
 
diff --git a/chrome/browser/first_party_sets/first_party_sets_policy_browsertest.cc b/chrome/browser/first_party_sets/first_party_sets_policy_browsertest.cc
index 7d93b0b..58c4380 100644
--- a/chrome/browser/first_party_sets/first_party_sets_policy_browsertest.cc
+++ b/chrome/browser/first_party_sets/first_party_sets_policy_browsertest.cc
@@ -47,7 +47,6 @@
 class EnabledPolicyBrowsertest
     : public PolicyTest,
       public ::testing::WithParamInterface<std::tuple<
-          bool,                       // Feature Enabled
           PolicyTest::BooleanPolicy,  // FirstPartySetsEnabled Policy State
           PolicyTest::BooleanPolicy,  // RelatedWebsiteSetsEnabled Policy State
           const char*                 // Overrides Policy
@@ -59,11 +58,6 @@
         blink::features::kStorageAccessAPI};
     std::vector<base::test::FeatureRef> disabled_features = {
         content_settings::features::kTrackingProtection3pcd};
-    if (IsFeatureEnabled()) {
-      enabled_features.emplace_back(features::kFirstPartySets);
-    } else {
-      disabled_features.emplace_back(features::kFirstPartySets);
-    }
     scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
   }
 
@@ -122,16 +116,11 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     PolicyTest::SetUpCommandLine(command_line);
-    if (IsFeatureEnabled()) {
-      // Only append this switch when the First-Party Sets base::Feature is
-      // enabled.
-      command_line->AppendSwitchASCII(
-          network::switches::kUseRelatedWebsiteSet,
-          base::StringPrintf(
-              R"({"primary": "https://%s",)"
-              R"("associatedSites": ["https://%s","https://%s"]})",
-              kHostA, kHostB, kHostC));
-    }
+    command_line->AppendSwitchASCII(
+        network::switches::kUseRelatedWebsiteSet,
+        base::StringPrintf(R"({"primary": "https://%s",)"
+                           R"("associatedSites": ["https://%s","https://%s"]})",
+                           kHostA, kHostB, kHostC));
   }
 
   content::RenderFrameHost* GetPrimaryMainFrame() {
@@ -167,7 +156,7 @@
   // enabled at the start of the test. This does not account for calls to
   // `SetEnabledPolicyStates`.
   bool IsRelatedWebsiteSetsEnabledInitially() {
-    return IsFeatureEnabled() && IsPrefEnabledInitially();
+    return IsPrefEnabledInitially();
   }
 
   void NavigateToPageWithFrame(const std::string& host) {
@@ -189,14 +178,13 @@
     return content::ExecJs(GetFrame(), "document.requestStorageAccess()");
   }
 
-  bool IsFeatureEnabled() { return std::get<0>(GetParam()); }
   PolicyTest::BooleanPolicy GetInitialFirstPartySetPolicyState() {
-    return std::get<1>(GetParam());
+    return std::get<0>(GetParam());
   }
   PolicyTest::BooleanPolicy GetInitialRelatedWebsiteSetPolicyState() {
-    return std::get<2>(GetParam());
+    return std::get<1>(GetParam());
   }
-  const char* GetOverridesPolicyName() { return std::get<3>(GetParam()); }
+  const char* GetOverridesPolicyName() { return std::get<2>(GetParam()); }
 
   // If the RelatedWebsiteSetEnabled policy is unset
   // SimpleDeprecatingPolicyHandler falls back to the FirstPartySetEnabled
@@ -232,13 +220,6 @@
     const testing::TestParamInfo<EnabledPolicyBrowsertest::ParamType>& info) {
   std::string name = base::NumberToString(info.index);
 
-  bool is_feature_enabled = std::get<0>(info.param);
-  if (is_feature_enabled) {
-    base::StrAppend(&name, {"_Enabled"});
-  } else {
-    base::StrAppend(&name, {"_Disabled"});
-  }
-
   auto policy_state_to_string =
       [](PolicyTest::BooleanPolicy state) -> std::string {
     switch (state) {
@@ -252,32 +233,32 @@
   };
 
   PolicyTest::BooleanPolicy first_party_sets_policy_state =
-      std::get<1>(info.param);
+      std::get<0>(info.param);
   base::StrAppend(&name,
                   {"_", policy_state_to_string(first_party_sets_policy_state)});
 
   PolicyTest::BooleanPolicy related_website_sets_policy_state =
-      std::get<2>(info.param);
+      std::get<1>(info.param);
   base::StrAppend(
       &name, {"_", policy_state_to_string(related_website_sets_policy_state)});
 
-  const char* override_policy_name = std::get<3>(info.param);
+  const char* override_policy_name = std::get<2>(info.param);
   base::StrAppend(&name, {"_", override_policy_name});
 
   return name;
 }
 
 IN_PROC_BROWSER_TEST_P(EnabledPolicyBrowsertest, ToggleFeature_Memberships) {
-  EXPECT_EQ(IsFeatureEnabled() && IsPrefEnabledInitially(),
+  EXPECT_EQ(IsPrefEnabledInitially(),
             AreSitesInSameRelatedWebsiteSet(kHostA, kHostC));
-  EXPECT_EQ(IsFeatureEnabled() && IsPrefEnabledInitially(),
+  EXPECT_EQ(IsPrefEnabledInitially(),
             AreSitesInSameRelatedWebsiteSet(kHostA, kHostB));
 
   SetEnabledPolicyStates(!IsPrefEnabledInitially());
 
-  EXPECT_EQ(IsFeatureEnabled() && !IsPrefEnabledInitially(),
+  EXPECT_EQ(!IsPrefEnabledInitially(),
             AreSitesInSameRelatedWebsiteSet(kHostA, kHostC));
-  EXPECT_EQ(IsFeatureEnabled() && !IsPrefEnabledInitially(),
+  EXPECT_EQ(!IsPrefEnabledInitially(),
             AreSitesInSameRelatedWebsiteSet(kHostA, kHostB));
 }
 
@@ -292,7 +273,6 @@
     FirstPartySets,
     EnabledPolicyBrowsertest,
     ::testing::Combine(
-        ::testing::Bool(),  // Feature Enabled
         ::testing::Values(
             PolicyTest::BooleanPolicy::kNotConfigured,
             PolicyTest::BooleanPolicy::kFalse,
@@ -330,7 +310,6 @@
     FirstPartySets,
     OverridesPolicyEmptyBrowsertest,
     ::testing::Combine(
-        ::testing::Bool(),  // Feature Enabled
         ::testing::Values(
             PolicyTest::BooleanPolicy::kNotConfigured,
             PolicyTest::BooleanPolicy::kFalse,
@@ -380,7 +359,6 @@
     FirstPartySets,
     OverridesPolicyReplacementBrowsertest,
     ::testing::Combine(
-        ::testing::Bool(),  // Feature Enabled
         ::testing::Values(
             PolicyTest::BooleanPolicy::kNotConfigured,
             PolicyTest::BooleanPolicy::kFalse,
@@ -430,7 +408,6 @@
     FirstPartySets,
     OverridesPolicyAdditionBrowsertest,
     ::testing::Combine(
-        ::testing::Bool(),  // Feature Enabled
         ::testing::Values(
             PolicyTest::BooleanPolicy::kNotConfigured,
             PolicyTest::BooleanPolicy::kFalse,
@@ -487,7 +464,6 @@
     FirstPartySets,
     OverridesPolicyReplacementAndAdditionBrowsertest,
     ::testing::Combine(
-        ::testing::Bool(),  // Feature Enabled
         ::testing::Values(
             PolicyTest::BooleanPolicy::kNotConfigured,
             PolicyTest::BooleanPolicy::kFalse,
diff --git a/chrome/browser/first_party_sets/first_party_sets_policy_service.cc b/chrome/browser/first_party_sets/first_party_sets_policy_service.cc
index 35d02ec..ee6d4a4 100644
--- a/chrome/browser/first_party_sets/first_party_sets_policy_service.cc
+++ b/chrome/browser/first_party_sets/first_party_sets_policy_service.cc
@@ -22,7 +22,6 @@
 #include "components/privacy_sandbox/tracking_protection_settings.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/first_party_sets_handler.h"
-#include "content/public/common/content_features.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/features.h"
 #include "net/base/schemeful_site.h"
@@ -94,14 +93,6 @@
 
 void FirstPartySetsPolicyService::Init() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  feature_enabled_ = base::FeatureList::IsEnabled(features::kFirstPartySets);
-
-  if (!feature_enabled_) {
-    pref_enabled_ = false;
-    OnReadyToNotifyDelegates(net::FirstPartySetsContextConfig(),
-                             net::FirstPartySetsCacheFilter());
-    return;
-  }
 
   Profile* profile = Profile::FromBrowserContext(browser_context_);
   // profile is guaranteed to be non-null since we create this service with a
@@ -370,7 +361,6 @@
 
 void FirstPartySetsPolicyService::ResetForTesting() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  feature_enabled_ = true;
   pref_enabled_ = true;
   access_delegates_.Clear();
   on_ready_callbacks_.clear();
diff --git a/chrome/browser/first_party_sets/first_party_sets_policy_service.h b/chrome/browser/first_party_sets/first_party_sets_policy_service.h
index 59b984df..295970ee 100644
--- a/chrome/browser/first_party_sets/first_party_sets_policy_service.h
+++ b/chrome/browser/first_party_sets/first_party_sets_policy_service.h
@@ -92,10 +92,10 @@
   // Exposes `Init` for use in tests.
   void InitForTesting();
 
-  // Returns true iff the preference and feature are both enabled.
+  // Returns true iff the preference is enabled.
   bool is_enabled() const {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    return feature_enabled_ && pref_enabled_;
+    return pref_enabled_;
   }
 
   // Returns true when this instance has received the config thus has been fully
@@ -188,13 +188,6 @@
   raw_ptr<content::BrowserContext> browser_context_
       GUARDED_BY_CONTEXT(sequence_checker_);
 
-  // Whether FPS is enabled globally.
-  //
-  // Initialized to true for the sake of tests, so that queries received before
-  // service initialization can be accumulated and answered after test setup,
-  // rather than answered immediately in the negative.
-  bool feature_enabled_ GUARDED_BY_CONTEXT(sequence_checker_) = true;
-
   // Whether FPS is enabled in this context. Note that this may be true even if
   // FPS is globally disabled.
   //
diff --git a/chrome/browser/first_party_sets/first_party_sets_policy_service_factory_unittest.cc b/chrome/browser/first_party_sets/first_party_sets_policy_service_factory_unittest.cc
index 8c36a1b..831013a 100644
--- a/chrome/browser/first_party_sets/first_party_sets_policy_service_factory_unittest.cc
+++ b/chrome/browser/first_party_sets/first_party_sets_policy_service_factory_unittest.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/first_party_sets/first_party_sets_policy_service_factory.h"
 
 #include "base/json/json_reader.h"
-#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/first_party_sets/first_party_sets_policy_service.h"
 #include "chrome/browser/first_party_sets/first_party_sets_pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
@@ -37,8 +36,6 @@
 
 TEST_F(FirstPartySetsPolicyServiceFactoryTest,
        ServiceCreatedRegardlessIfPolicyEnabled) {
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(features::kFirstPartySets);
   TestingProfile* disabled_profile =
       profile_manager().CreateTestingProfile("disabled");
   TestingProfile* enabled_profile =
@@ -74,8 +71,6 @@
 
 TEST_F(FirstPartySetsPolicyServiceFactoryTest,
        OffTheRecordProfile_DistinctAndDisabled) {
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(features::kFirstPartySets);
   TestingProfile* profile =
       profile_manager().CreateTestingProfile("TestProfile");
 
diff --git a/chrome/browser/first_party_sets/first_party_sets_policy_service_unittest.cc b/chrome/browser/first_party_sets/first_party_sets_policy_service_unittest.cc
index 54026ec..b695c01 100644
--- a/chrome/browser/first_party_sets/first_party_sets_policy_service_unittest.cc
+++ b/chrome/browser/first_party_sets/first_party_sets_policy_service_unittest.cc
@@ -118,8 +118,6 @@
 }
 
 TEST_F(DefaultFirstPartySetsPolicyServiceTest, GuestProfiles) {
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(features::kFirstPartySets);
   TestingProfile::Builder builder;
   builder.SetGuestSession();
   std::unique_ptr<TestingProfile> profile = builder.Build();
@@ -140,8 +138,6 @@
 }
 
 TEST_F(DefaultFirstPartySetsPolicyServiceTest, EnabledForLegitProfile) {
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(features::kFirstPartySets);
   TestingProfile profile;
   FirstPartySetsPolicyService* service =
       FirstPartySetsPolicyServiceFactory::GetForBrowserContext(&profile);
@@ -161,11 +157,7 @@
 class FirstPartySetsPolicyServiceTest
     : public DefaultFirstPartySetsPolicyServiceTest {
  public:
-  FirstPartySetsPolicyServiceTest() {
-    // Enable base::Feature for all tests since only the pref can change
-    // whether the service is enabled.
-    features_.InitAndEnableFeature(features::kFirstPartySets);
-  }
+  FirstPartySetsPolicyServiceTest() = default;
 
   void SetUp() override {
     DefaultFirstPartySetsPolicyServiceTest::SetUp();
@@ -224,7 +216,6 @@
   ScopedMockFirstPartySetsHandler first_party_sets_handler_;
   std::unique_ptr<TestingProfileManager> profile_manager_;
   raw_ptr<Profile, DanglingUntriaged> profile_;
-  base::test::ScopedFeatureList features_;
   raw_ptr<FirstPartySetsPolicyService, DanglingUntriaged> service_;
 };
 
@@ -305,51 +296,8 @@
   env().RunUntilIdle();
 }
 
-TEST_F(FirstPartySetsPolicyServiceTest,
-       IsSiteInManagedSet_SiteInConfig_FeatureDisabled) {
-  base::test::ScopedFeatureList features;
-  features.InitAndDisableFeature(features::kFirstPartySets);
-  net::SchemefulSite example_site(GURL("https://example.test"));
-  SetContextConfig(net::FirstPartySetsContextConfig(
-      {{example_site, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                          net::SchemefulSite(GURL("https://primary.test")),
-                          net::SiteType::kAssociated, absl::nullopt))}}));
-  service()->InitForTesting();
-  EXPECT_FALSE(service()->IsSiteInManagedSet(example_site));
-  env().RunUntilIdle();
-}
-
-TEST_P(FirstPartySetsPolicyServicePrefTest, FindEntry_FpsDisabledByFeature) {
-  base::HistogramTester histogram_tester;
-  base::test::ScopedFeatureList features;
-  net::SchemefulSite primary_site(GURL("https://primary.test"));
-  net::SchemefulSite associate1_site(GURL("https://associate1.test"));
-
-  // Create Global First-Party Sets with the following set:
-  // { primary: "https://primary.test",
-  // associatedSites: ["https://associate1.test"}
-  SetGlobalSets(net::GlobalFirstPartySets(
-      kVersion,
-      {{associate1_site,
-        {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated,
-                                 0)}}},
-      {}));
-  // Simulate the profile set overrides are empty.
-  service()->InitForTesting();
-
-  // Simulate First-Party Sets disabled by the feature.
-  features.InitAndDisableFeature(features::kFirstPartySets);
-  SetRwsEnabledViaPref(true);
-  // Verify that FindEntry doesn't return associate1's entry when FPS is off.
-  EXPECT_FALSE(service()->FindEntry(associate1_site));
-  histogram_tester.ExpectUniqueSample(
-      "Cookie.FirstPartySets.NumBrowserQueriesBeforeInitialization", 0, 1);
-  env().RunUntilIdle();
-}
-
 TEST_P(FirstPartySetsPolicyServicePrefTest, FindEntry_FpsDisabledByPref) {
   base::HistogramTester histogram_tester;
-  base::test::ScopedFeatureList features;
   net::SchemefulSite primary_site(GURL("https://primary.test"));
   net::SchemefulSite associate1_site(GURL("https://associate1.test"));
 
@@ -363,8 +311,6 @@
                                  0)}}},
       {}));
 
-  // Simulate First-Party Sets disabled by the preference.
-  features.InitAndEnableFeature(features::kFirstPartySets);
   SetRwsEnabledViaPref(false);
 
   service()->InitForTesting();
@@ -378,14 +324,11 @@
 
 TEST_P(FirstPartySetsPolicyServicePrefTest,
        FindEntry_FpsEnabled_ReturnsEmptyUntilAllSetsReady) {
-  base::test::ScopedFeatureList features;
   net::SchemefulSite primary_site(GURL("https://primary.test"));
   net::SchemefulSite associate1_site(GURL("https://associate1.test"));
   net::FirstPartySetEntry associate1_entry(
       net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated, 0));
 
-  // Fully enable First-Party Sets.
-  features.InitAndEnableFeature(features::kFirstPartySets);
   SetRwsEnabledViaPref(true);
   // Verify that FindEntry returns empty if the global sets and profile sets
   // aren't ready yet.
@@ -412,15 +355,12 @@
 TEST_P(FirstPartySetsPolicyServicePrefTest,
        FindEntry_NumQueriesRecorded_BeforeConfigReady) {
   base::HistogramTester histogram_tester;
-  base::test::ScopedFeatureList features;
 
   net::SchemefulSite primary_site(GURL("https://primary.test"));
   net::SchemefulSite associate_site(GURL("https://associate.test"));
   net::FirstPartySetEntry associate_entry(
       net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated, 0));
 
-  // Fully enable First-Party Sets.
-  features.InitAndEnableFeature(features::kFirstPartySets);
   SetRwsEnabledViaPref(true);
 
   // Simulate 3 FindEntry queries which all should return empty.
@@ -456,15 +396,12 @@
 TEST_P(FirstPartySetsPolicyServicePrefTest,
        FindEntry_NumQueriesRecorded_AfterConfigReady) {
   base::HistogramTester histogram_tester;
-  base::test::ScopedFeatureList features;
 
   net::SchemefulSite primary_site(GURL("https://primary.test"));
   net::SchemefulSite associate_site(GURL("https://associate.test"));
   net::FirstPartySetEntry associate_entry(
       net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated, 0));
 
-  // Fully enable First-Party Sets.
-  features.InitAndEnableFeature(features::kFirstPartySets);
   SetRwsEnabledViaPref(true);
 
   // Simulate the global First-Party Sets with the following set:
@@ -606,20 +543,7 @@
                                    Pair(service_site, override_entry)));
 }
 
-class FirstPartySetsPolicyServicePrefObserverTest
-    : public FirstPartySetsPolicyServiceTest {
- public:
-  FirstPartySetsPolicyServicePrefObserverTest() {
-    // Enable base::Feature for all tests since only the pref can change
-    // whether the service is enabled.
-    features_.InitAndEnableFeature(features::kFirstPartySets);
-  }
-
- private:
-  base::test::ScopedFeatureList features_;
-};
-
-TEST_F(FirstPartySetsPolicyServicePrefObserverTest,
+TEST_F(FirstPartySetsPolicyServicePrefTest,
        OnProfileConfigReady_InitDisabled_NotifiesReadyWithConfig) {
   net::SchemefulSite test_primary(GURL("https://a.test"));
   net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary,
@@ -636,7 +560,7 @@
   env().RunUntilIdle();
 }
 
-TEST_F(FirstPartySetsPolicyServicePrefObserverTest,
+TEST_F(FirstPartySetsPolicyServicePrefTest,
        OnFirstPartySetsEnabledChanged_Default_WithConfig) {
   service()->InitForTesting();
 
@@ -646,7 +570,7 @@
   env().RunUntilIdle();
 }
 
-TEST_F(FirstPartySetsPolicyServicePrefObserverTest,
+TEST_F(FirstPartySetsPolicyServicePrefTest,
        OnFirstPartySetsEnabledChanged_Default_WithoutConfig) {
   EXPECT_CALL(mock_delegate, SetEnabled(_)).Times(1);
   EXPECT_CALL(mock_delegate, NotifyReady(_)).Times(0);
@@ -654,7 +578,7 @@
   env().RunUntilIdle();
 }
 
-TEST_F(FirstPartySetsPolicyServicePrefObserverTest,
+TEST_F(FirstPartySetsPolicyServicePrefTest,
        OnFirstPartySetsEnabledChanged_Disables_WithConfig) {
   service()->InitForTesting();
   EXPECT_CALL(mock_delegate, SetEnabled(true)).Times(1);
@@ -667,7 +591,7 @@
   env().RunUntilIdle();
 }
 
-TEST_F(FirstPartySetsPolicyServicePrefObserverTest,
+TEST_F(FirstPartySetsPolicyServicePrefTest,
        OnFirstPartySetsEnabledChanged_Disables_WithoutConfig) {
   EXPECT_CALL(mock_delegate, SetEnabled(true)).Times(1);
 
@@ -679,7 +603,7 @@
   env().RunUntilIdle();
 }
 
-TEST_F(FirstPartySetsPolicyServicePrefObserverTest,
+TEST_F(FirstPartySetsPolicyServicePrefTest,
        OnFirstPartySetsEnabledChanged_Enables_WithConfig) {
   net::SchemefulSite test_primary(GURL("https://a.test"));
   net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary,
@@ -701,7 +625,7 @@
   env().RunUntilIdle();
 }
 
-TEST_F(FirstPartySetsPolicyServicePrefObserverTest,
+TEST_F(FirstPartySetsPolicyServicePrefTest,
        OnFirstPartySetsEnabledChanged_Enables_WithoutConfig) {
   service()->OnFirstPartySetsEnabledChanged(true);
 
@@ -712,7 +636,7 @@
   env().RunUntilIdle();
 }
 
-TEST_F(FirstPartySetsPolicyServicePrefObserverTest,
+TEST_F(FirstPartySetsPolicyServicePrefTest,
        OnFirstPartySetsEnabledChanged_OTRProfile) {
   testing::NiceMock<MockFirstPartySetsAccessDelegate> mock_delegate;
   EXPECT_CALL(mock_delegate, SetEnabled(false)).Times(1);
@@ -849,7 +773,6 @@
   ThirdPartyCookieBlockingFirstPartySetsPolicyServiceTest() {
     features_.InitWithFeatures(
         {
-            features::kFirstPartySets,
             net::features::kForceThirdPartyCookieBlocking,
         },
         {});
@@ -887,14 +810,11 @@
 }
 
 class FirstPartySetsPolicyServiceResumeThrottleTest
-    : public FirstPartySetsPolicyServiceTest,
-      public ::testing::WithParamInterface<bool> {
+    : public FirstPartySetsPolicyServiceTest {
  public:
   FirstPartySetsPolicyServiceResumeThrottleTest() {
     features_.InitAndEnableFeatureWithParameters(
-        features::kFirstPartySets,
-        {{features::kFirstPartySetsClearSiteDataOnChangedSets.name,
-          GetParam() ? "true" : "false"}});
+        net::features::kWaitForFirstPartySetsInit, {});
   }
 
  private:
@@ -902,7 +822,7 @@
 };
 
 // Verify the throttle resume callback is always invoked.
-TEST_P(FirstPartySetsPolicyServiceResumeThrottleTest,
+TEST_F(FirstPartySetsPolicyServiceResumeThrottleTest,
        RegisterThrottleResumeCallback) {
   SetInvokeCallbacksAsynchronously(true);
   service()->InitForTesting();
@@ -911,8 +831,4 @@
   run_loop.Run();
 }
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         FirstPartySetsPolicyServiceResumeThrottleTest,
-                         ::testing::Bool());
-
 }  // namespace first_party_sets
diff --git a/chrome/browser/first_party_sets/scoped_mock_first_party_sets_handler.cc b/chrome/browser/first_party_sets/scoped_mock_first_party_sets_handler.cc
index 3aa19dd4..47e6eef 100644
--- a/chrome/browser/first_party_sets/scoped_mock_first_party_sets_handler.cc
+++ b/chrome/browser/first_party_sets/scoped_mock_first_party_sets_handler.cc
@@ -6,11 +6,9 @@
 
 #include <string>
 
-#include "base/feature_list.h"
 #include "base/functional/callback.h"
 #include "base/task/sequenced_task_runner.h"
 #include "content/public/browser/first_party_sets_handler.h"
-#include "content/public/common/content_features.h"
 #include "net/first_party_sets/first_party_set_metadata.h"
 #include "net/first_party_sets/first_party_sets_cache_filter.h"
 #include "net/first_party_sets/first_party_sets_context_config.h"
@@ -40,9 +38,6 @@
 ScopedMockFirstPartySetsHandler::FindEntry(
     const net::SchemefulSite& site,
     const net::FirstPartySetsContextConfig& config) const {
-  if (!base::FeatureList::IsEnabled(features::kFirstPartySets)) {
-    return absl::nullopt;
-  }
   return global_sets_.FindEntry(site, config);
 }
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 1805e09..03c12b6b 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1075,11 +1075,6 @@
     "expiry_milestone": 121
   },
   {
-    "name": "cct-brand-transparency",
-    "owners": [ "wenyufu@chromium.org" ],
-    "expiry_milestone": 117
-  },
-  {
     "name": "cct-incognito",
     "owners": [ "chrome-incognito@chromium.org", "//chrome/android/java/src/org/chromium/chrome/browser/customtabs/OWNERS" ],
     "expiry_milestone": 140
@@ -6021,11 +6016,6 @@
     "expiry_milestone": 120
   },
   {
-    "name": "omnibox-tail-suggest",
-    "owners": [ "christianxu@chromium.org", "stkhapugin@chromium.org", "bling-flags@google.com" ],
-    "expiry_milestone": 120
-  },
-  {
     "name": "omnibox-ui-max-autocomplete-matches",
     "owners": [ "jdonnelly@chromium.org", "chrome-omnibox-team@google.com" ],
     "expiry_milestone": 120
@@ -7665,7 +7655,12 @@
   {
     "name": "tangible-sync",
     "owners": [ "aliceywang@chromium.org", "chrome-signin-team@google.com" ],
-    "expiry_milestone":120
+    "expiry_milestone": 120
+  },
+  {
+    "name": "tear-off-web-app-tab-opens-web-app-window",
+    "owners": [ "davidbienvenu@chromium.org", "dmurph@chromium.org" ],
+    "expiry_milestone": 135
   },
   {
     "name": "terminal-alternative-emulator",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 5bd172e..a6914a98 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3322,6 +3322,11 @@
 const char kTabSearchFuzzySearchDescription[] =
     "Enable fuzzy search for Tab Search.";
 
+const char kTearOffWebAppAppTabOpensWebAppWindowName[] = "Tear Off Web App Tab";
+const char kTearOffWebAppAppTabOpensWebAppWindowDescription[] =
+    "Open Web App window when tearing off a tab that's displaying a url "
+    "handled by an installed Web App.";
+
 const char kTextInShelfName[] = "Internal test: text in shelf";
 const char kTextInShelfDescription[] =
     "Extend text in shelf timeout to learn about user education";
@@ -3892,12 +3897,6 @@
 const char kBoardingPassDetectorName[] = "Boarding Pass Detector";
 const char kBoardingPassDetectorDescription[] = "Enable Boarding Pass Detector";
 
-const char kCCTBrandTransparencyName[] =
-    "Chrome Custom Tabs Brand Transparency";
-const char kCCTBrandTransparencyDescription[] =
-    "When enabled, CCT will show more Chrome branding information when start, "
-    "giving user more transparency that the web page is running in Chrome.";
-
 const char kCCTIncognitoAvailableToThirdPartyName[] =
     "Allow third party to open Custom Tabs Incognito mode";
 const char kCCTIncognitoAvailableToThirdPartyDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index b2520e42..4a8ba80 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1916,6 +1916,9 @@
 extern const char kTabSearchFuzzySearchName[];
 extern const char kTabSearchFuzzySearchDescription[];
 
+extern const char kTearOffWebAppAppTabOpensWebAppWindowName[];
+extern const char kTearOffWebAppAppTabOpensWebAppWindowDescription[];
+
 extern const char kTabletToolbarReorderingAndroidName[];
 extern const char kTabletToolbarReorderingAndroidDescription[];
 
@@ -2250,9 +2253,6 @@
 extern const char kBoardingPassDetectorName[];
 extern const char kBoardingPassDetectorDescription[];
 
-extern const char kCCTBrandTransparencyName[];
-extern const char kCCTBrandTransparencyDescription[];
-
 extern const char kCCTIncognitoAvailableToThirdPartyName[];
 extern const char kCCTIncognitoAvailableToThirdPartyDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 574d204..bd9fa27 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -179,7 +179,6 @@
     &kCastDeviceFilter,
     &kClearOmniboxFocusAfterNavigation,
     &kCreateNewTabInitializeRenderer,
-    &kCCTBrandTransparency,
     &kCCTBrandTransparencyMemoryImprovement,
     &kCCTClientDataHeader,
     &kCCTFeatureUsage,
@@ -275,9 +274,7 @@
     &kFeedPositionAndroid,
     &kSearchResumptionModuleAndroid,
     &kShareSheetMigrationAndroid,
-    &kSpecialLocaleWrapper,
     &kSuppressToolbarCaptures,
-    &kStoreHoursAndroid,
     &kTabDragDropAndroid,
     &kTabAndLinkDragDropAndroid,
     &kTabEngagementReportingAndroid,
@@ -288,7 +285,6 @@
     &kTestDefaultDisabled,
     &kTestDefaultEnabled,
     &kThumbnailPlaceholder,
-    &kToolbarMicIphAndroid,
     &kTrustedWebActivityPostMessage,
     &kStartSurfaceAndroid,
     &kStartSurfaceOnTablet,
@@ -369,7 +365,6 @@
     &syncer::kSyncAndroidLimitNTPPromoImpressions,
     &syncer::kSyncDecoupleAddressPaymentSettings,
     &syncer::kSyncEnableContactInfoDataTypeInTransportMode,
-    &subresource_filter::kSafeBrowsingSubresourceFilter,
     &webapps::features::kInstallableAmbientBadgeMessage,
     &webapps::features::kWebApkInstallFailureNotification,
     &webapps::features::kWebApkInstallFailureRetry,
@@ -495,10 +490,6 @@
              "CreateNewTabInitializeRenderer",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kCCTBrandTransparency,
-             "CCTBrandTransparency",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kCCTBrandTransparencyMemoryImprovement,
              "CCTBrandTransparencyMemoryImprovement",
              base::FEATURE_ENABLED_BY_DEFAULT);
@@ -874,14 +865,6 @@
              "ShareSheetMigrationAndroid",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kSpecialLocaleWrapper,
-             "SpecialLocaleWrapper",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-BASE_FEATURE(kStoreHoursAndroid,
-             "StoreHoursAndroid",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 BASE_FEATURE(kSuppressToolbarCaptures,
              "SuppressToolbarCaptures",
              base::FEATURE_ENABLED_BY_DEFAULT);
@@ -928,10 +911,6 @@
              "ThumbnailPlaceholder",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kToolbarMicIphAndroid,
-             "ToolbarMicIphAndroid",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 BASE_FEATURE(kTrustedWebActivityPostMessage,
              "TrustedWebActivityPostMessage",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index c9b15b4..ef23f22 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -42,7 +42,6 @@
 BASE_DECLARE_FEATURE(kClearOmniboxFocusAfterNavigation);
 BASE_DECLARE_FEATURE(kCreateNewTabInitializeRenderer);
 BASE_DECLARE_FEATURE(kCastDeviceFilter);
-BASE_DECLARE_FEATURE(kCCTBrandTransparency);
 BASE_DECLARE_FEATURE(kCCTBrandTransparencyMemoryImprovement);
 BASE_DECLARE_FEATURE(kCCTClientDataHeader);
 BASE_DECLARE_FEATURE(kCCTFeatureUsage);
@@ -153,8 +152,6 @@
 BASE_DECLARE_FEATURE(kScrollToTLDOptimization);
 BASE_DECLARE_FEATURE(kSearchResumptionModuleAndroid);
 BASE_DECLARE_FEATURE(kShareSheetMigrationAndroid);
-BASE_DECLARE_FEATURE(kSpecialLocaleWrapper);
-BASE_DECLARE_FEATURE(kStoreHoursAndroid);
 BASE_DECLARE_FEATURE(kSuppressToolbarCaptures);
 BASE_DECLARE_FEATURE(kTabDragDropAndroid);
 BASE_DECLARE_FEATURE(kTabAndLinkDragDropAndroid);
@@ -166,7 +163,6 @@
 BASE_DECLARE_FEATURE(kTestDefaultDisabled);
 BASE_DECLARE_FEATURE(kTestDefaultEnabled);
 BASE_DECLARE_FEATURE(kThumbnailPlaceholder);
-BASE_DECLARE_FEATURE(kToolbarMicIphAndroid);
 BASE_DECLARE_FEATURE(kToolbarUseHardwareBitmapDraw);
 BASE_DECLARE_FEATURE(kTrustedWebActivityPostMessage);
 BASE_DECLARE_FEATURE(kStartSurfaceAndroid);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 78d68eab..21dd3ad 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -170,7 +170,6 @@
     public static final String CACHE_ACTIVITY_TASKID = "CacheActivityTaskID";
     public static final String CAPTIVE_PORTAL_CERTIFICATE_LIST = "CaptivePortalCertificateList";
     public static final String CCT_AUTO_TRANSLATE = "CCTAutoTranslate";
-    public static final String CCT_BRAND_TRANSPARENCY = "CCTBrandTransparency";
     public static final String CCT_BRAND_TRANSPARENCY_MEMORY_IMPROVEMENT =
             "CCTBrandTransparencyMemoryImprovement";
     public static final String CCT_CLIENT_DATA_HEADER = "CCTClientDataHeader";
@@ -417,7 +416,6 @@
     public static final String ACCOUNT_REAUTHENTICATION_RECENT_TIME_WINDOW =
             "AccountReauthenticationRecentTimeWindow";
     public static final String START_SURFACE_WITH_ACCESSIBILITY = "StartSurfaceWithAccessibility";
-    public static final String STORE_HOURS = "StoreHoursAndroid";
     public static final String SUGGESTION_ANSWERS_COLOR_REVERSE = "SuggestionAnswersColorReverse";
     public static final String SUPPRESS_TOOLBAR_CAPTURES = "SuppressToolbarCaptures";
     public static final String SURFACE_POLISH = "SurfacePolish";
@@ -574,7 +572,6 @@
             new CachedFlag(ACCOUNT_REAUTHENTICATION_RECENT_TIME_WINDOW, true);
     public static final CachedFlag sStartSurfaceWithAccessibility =
             new CachedFlag(START_SURFACE_WITH_ACCESSIBILITY, true);
-    public static final CachedFlag sStoreHoursAndroid = new CachedFlag(STORE_HOURS, false);
     public static final CachedFlag sSurfacePolish = new CachedFlag(SURFACE_POLISH, false);
     public static final CachedFlag sTabDragDropAsWindowAndroid =
             new CachedFlag(TAB_DRAG_DROP_ANDROID, false);
@@ -657,7 +654,6 @@
                     sStartSurfaceReturnTime,
                     sAccountReauthenticationRecentTimeWindow,
                     sStartSurfaceWithAccessibility,
-                    sStoreHoursAndroid,
                     sSurfacePolish,
                     sTabDragDropAsWindowAndroid,
                     sTabLinkDragDropAndroid,
diff --git a/chrome/browser/k_anonymity_service/k_anonymity_service_client.cc b/chrome/browser/k_anonymity_service/k_anonymity_service_client.cc
index db70791..0059a16d 100644
--- a/chrome/browser/k_anonymity_service/k_anonymity_service_client.cc
+++ b/chrome/browser/k_anonymity_service/k_anonymity_service_client.cc
@@ -374,8 +374,6 @@
       url::Origin::Create(GURL(features::kKAnonymityServiceAuthServer.Get()));
   network::mojom::TrustTokenParamsPtr params =
       network::mojom::TrustTokenParams::New();
-  params->version =
-      network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1;
   params->operation = network::mojom::TrustTokenOperationType::kRedemption;
   params->refresh_policy = network::mojom::TrustTokenRefreshPolicy::kRefresh;
   params->custom_key_commitment = key_and_id.key_commitment;
diff --git a/chrome/browser/k_anonymity_service/k_anonymity_trust_token_getter.cc b/chrome/browser/k_anonymity_service/k_anonymity_trust_token_getter.cc
index 488cfc3..a233bde 100644
--- a/chrome/browser/k_anonymity_service/k_anonymity_trust_token_getter.cc
+++ b/chrome/browser/k_anonymity_service/k_anonymity_trust_token_getter.cc
@@ -447,8 +447,6 @@
 
   network::mojom::TrustTokenParamsPtr params =
       network::mojom::TrustTokenParams::New();
-  params->version =
-      network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1;
   params->operation = network::mojom::TrustTokenOperationType::kIssuance;
   params->custom_key_commitment = key_commitment->key_and_id.key_commitment;
   resource_request->trust_token_params = *params;
diff --git a/chrome/browser/net/system_network_context_manager_browsertest.cc b/chrome/browser/net/system_network_context_manager_browsertest.cc
index 65d5c4c..221a12cd 100644
--- a/chrome/browser/net/system_network_context_manager_browsertest.cc
+++ b/chrome/browser/net/system_network_context_manager_browsertest.cc
@@ -478,9 +478,8 @@
     SystemNetworkContextManagerBrowsertest::SetUpInProcessBrowserTestFixture();
     // Since we set kWaitForFirstPartySetsInit, all cookie-carrying network
     // requests are blocked until FPS is initialized.
-    feature_list_.InitWithFeatures(
-        {features::kFirstPartySets, net::features::kWaitForFirstPartySetsInit},
-        {});
+    feature_list_.InitWithFeatures({net::features::kWaitForFirstPartySetsInit},
+                                   {});
     CHECK(component_dir_.CreateUniqueTempDir());
     base::ScopedAllowBlockingForTesting allow_blocking;
 
diff --git a/chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h b/chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h
index 0c384f3..36d3891 100644
--- a/chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h
+++ b/chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h
@@ -60,6 +60,10 @@
               ShouldFeatureBeCurrentlyEnabledForUser,
               (optimization_guide::proto::ModelExecutionFeature),
               (const));
+  MOCK_METHOD(bool,
+              ShouldFeatureBeCurrentlyAllowedForLogging,
+              (optimization_guide::proto::ModelExecutionFeature feature),
+              (const));
   MOCK_METHOD(void,
               UploadModelQualityLogs,
               (std::unique_ptr<optimization_guide::ModelQualityLogEntry>));
diff --git a/chrome/browser/password_manager/android/BUILD.gn b/chrome/browser/password_manager/android/BUILD.gn
index d090647f..2358172 100644
--- a/chrome/browser/password_manager/android/BUILD.gn
+++ b/chrome/browser/password_manager/android/BUILD.gn
@@ -212,6 +212,7 @@
     "junit/src/org/chromium/chrome/browser/password_manager/FakePasswordStoreAndroidBackendTest.java",
     "junit/src/org/chromium/chrome/browser/password_manager/OutdatedGmsCoreDialogTest.java",
     "junit/src/org/chromium/chrome/browser/password_manager/PasswordCheckupClientMetricsRecorderTest.java",
+    "junit/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncherTest.java",
     "junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerAndroidBackendUtilTest.java",
     "junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerErrorMessageHelperBridgeTest.java",
     "junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java",
diff --git a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java
index c651c8de..10fc6878 100644
--- a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java
+++ b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java
@@ -17,6 +17,7 @@
 import android.os.SystemClock;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.google.android.gms.common.GoogleApiAvailability;
@@ -260,19 +261,25 @@
      * @param referrer the place that requested to show the UI.
      * @param syncService the service to query about the sync status.
      * @param modalDialogManagerSupplier The supplier of the ModalDialogManager to be used by
-     *         loading dialog.
+     *     loading dialog.
+     * @param accountEmail is the email of the account syncing passwords. If it's empty, the checkup
+     *     for local will show. The purpose of this is to enable showing the checkup for local
+     *     storage if the password checkup is launched from the leak detection dialog and the leaked
+     *     credential is only saved in the local password storage.
      */
     public static void showPasswordCheckup(
             Context context,
             @PasswordCheckReferrer int referrer,
             SyncService syncService,
-            Supplier<ModalDialogManager> modalDialogManagerSupplier) {
+            Supplier<ModalDialogManager> modalDialogManagerSupplier,
+            @Nullable String accountEmail) {
+        assert accountEmail == null || !accountEmail.isEmpty();
         assert canUseUpm();
 
+        // TODO(crbug.com/1504551): Change PasswordCheckupClientHelper.getPasswordCheckupIntent to
+        // take the accountEmail as String.
         Optional<String> account =
-                hasChosenToSyncPasswords(syncService)
-                        ? Optional.of(CoreAccountInfo.getEmailFrom(syncService.getAccountInfo()))
-                        : Optional.empty();
+                accountEmail == null ? Optional.empty() : Optional.of(accountEmail);
 
         LoadingModalDialogCoordinator loadingDialogCoordinator =
                 LoadingModalDialogCoordinator.create(modalDialogManagerSupplier, context);
@@ -516,6 +523,8 @@
         PasswordCheckupClientMetricsRecorder passwordCheckupMetricsRecorder =
                 new PasswordCheckupClientMetricsRecorder(
                         (PasswordCheckOperation.GET_PASSWORD_CHECKUP_INTENT));
+        // TODO(crbug.com/1504551): Change PasswordCheckupClientHelper.getPasswordCheckupIntent to
+        // take the accountEmail as String.
         checkupClient.getPasswordCheckupIntent(
                 referrer,
                 account,
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncherTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncherTest.java
new file mode 100644
index 0000000..e85583d
--- /dev/null
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncherTest.java
@@ -0,0 +1,174 @@
+// 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.
+
+package org.chromium.chrome.browser.password_manager;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static org.chromium.chrome.browser.password_manager.PasswordCheckReferrer.LEAK_DIALOG;
+
+import android.accounts.Account;
+import android.app.PendingIntent;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.CollectionUtil;
+import org.chromium.base.ContextUtils;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.password_manager.PasswordCheckupClientHelper.PasswordCheckBackendException;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.sync.SyncServiceFactory;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.prefs.PrefService;
+import org.chromium.components.signin.AccountManagerFacadeProvider;
+import org.chromium.components.signin.AccountUtils;
+import org.chromium.components.signin.base.CoreAccountInfo;
+import org.chromium.components.signin.test.util.FakeAccountManagerFacade;
+import org.chromium.components.sync.SyncService;
+import org.chromium.components.sync.UserSelectableType;
+import org.chromium.components.user_prefs.UserPrefs;
+import org.chromium.components.user_prefs.UserPrefsJni;
+import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+
+import java.lang.ref.WeakReference;
+
+/** Tests for password manager helper methods. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+@Batch(Batch.PER_CLASS)
+public class PasswordCheckupLauncherTest {
+    private static final String TEST_EMAIL_ADDRESS = "test@email.com";
+    private static final String TEST_NO_EMAIL_ADDRESS = null;
+
+    @Rule public TestRule mProcessor = new Features.JUnitProcessor();
+    @Rule public JniMocker mJniMocker = new JniMocker();
+
+    @Mock private Profile mProfile;
+
+    @Mock private PrefService mPrefService;
+
+    @Mock private UserPrefs.Natives mMockUserPrefsJni;
+
+    @Mock private PasswordManagerUtilBridge.Natives mMockPasswordManagerUtilBridgeJni;
+
+    @Mock private SyncService mMockSyncService;
+
+    @Mock WindowAndroid mMockWindowAndroid;
+
+    @Mock private PendingIntent mMockPendingIntentForLocalCheckup;
+
+    @Mock private PendingIntent mMockPendingIntentForAccountCheckup;
+
+    private FakePasswordManagerBackendSupportHelper mFakeBackendSupportHelper =
+            new FakePasswordManagerBackendSupportHelper();
+
+    private ModalDialogManager mModalDialogManager;
+
+    private FakePasswordCheckupClientHelperFactoryImpl mFakePasswordCheckupClientHelperFactory =
+            new FakePasswordCheckupClientHelperFactoryImpl();
+
+    private FakePasswordCheckupClientHelper mFakePasswordCheckupClientHelper;
+
+    private final FakeAccountManagerFacade mFakeAccountManagerFacade =
+            new FakeAccountManagerFacade();
+
+    @Before
+    public void setUp() throws PasswordCheckBackendException {
+        MockitoAnnotations.initMocks(this);
+        mJniMocker.mock(UserPrefsJni.TEST_HOOKS, mMockUserPrefsJni);
+        mJniMocker.mock(PasswordManagerUtilBridgeJni.TEST_HOOKS, mMockPasswordManagerUtilBridgeJni);
+
+        Profile.setLastUsedProfileForTesting(mProfile);
+        when(mMockUserPrefsJni.get(mProfile)).thenReturn(mPrefService);
+
+        SyncServiceFactory.setInstanceForTesting(mMockSyncService);
+        when(mMockSyncService.isSyncFeatureEnabled()).thenReturn(true);
+        when(mMockSyncService.isEngineInitialized()).thenReturn(true);
+        when(mMockSyncService.hasSyncConsent()).thenReturn(true);
+
+        AccountManagerFacadeProvider.setInstanceForTests(mFakeAccountManagerFacade);
+        Account newAccount = AccountUtils.createAccountFromName(TEST_EMAIL_ADDRESS);
+        mFakeAccountManagerFacade.addAccount(newAccount);
+        when(mMockSyncService.getAccountInfo())
+                .thenReturn(
+                        CoreAccountInfo.createFromEmailAndGaiaId(
+                                TEST_EMAIL_ADDRESS,
+                                mFakeAccountManagerFacade.getAccountGaiaId(TEST_EMAIL_ADDRESS)));
+
+        PasswordManagerBackendSupportHelper.setInstanceForTesting(mFakeBackendSupportHelper);
+        mFakeBackendSupportHelper.setBackendPresent(true);
+
+        PasswordCheckupClientHelperFactory.setFactoryForTesting(
+                mFakePasswordCheckupClientHelperFactory);
+        mFakePasswordCheckupClientHelper =
+                (FakePasswordCheckupClientHelper)
+                        mFakePasswordCheckupClientHelperFactory.createHelper();
+        mFakePasswordCheckupClientHelper.setIntentForLocalCheckup(
+                mMockPendingIntentForLocalCheckup);
+        mFakePasswordCheckupClientHelper.setIntentForAccountCheckup(
+                mMockPendingIntentForAccountCheckup);
+
+        when(mMockWindowAndroid.getContext())
+                .thenReturn(new WeakReference<>(ContextUtils.getApplicationContext()));
+        mModalDialogManager =
+                new ModalDialogManager(
+                        mock(ModalDialogManager.Presenter.class),
+                        ModalDialogManager.ModalDialogType.APP);
+        when(mMockWindowAndroid.getModalDialogManager()).thenReturn(mModalDialogManager);
+    }
+
+    @Test
+    public void testLaunchCheckupOnDeviceShowsPasswordCheckupForAccount()
+            throws PendingIntent.CanceledException {
+        when(mMockSyncService.getSelectedTypes())
+                .thenReturn(CollectionUtil.newHashSet(UserSelectableType.PASSWORDS));
+        when(mMockPasswordManagerUtilBridgeJni.canUseUPMBackend(true, mPrefService))
+                .thenReturn(true);
+
+        PasswordCheckupLauncher.launchCheckupOnDevice(
+                mMockWindowAndroid, LEAK_DIALOG, TEST_EMAIL_ADDRESS);
+
+        verify(mMockPendingIntentForAccountCheckup).send();
+    }
+
+    @Test
+    public void testLaunchCheckupOnDeviceShowsPasswordCheckupForLocalWhenNotSyncing()
+            throws PendingIntent.CanceledException {
+        when(mMockPasswordManagerUtilBridgeJni.canUseUPMBackend(false, mPrefService))
+                .thenReturn(true);
+
+        PasswordCheckupLauncher.launchCheckupOnDevice(
+                mMockWindowAndroid, LEAK_DIALOG, TEST_NO_EMAIL_ADDRESS);
+
+        verify(mMockPendingIntentForLocalCheckup).send();
+    }
+
+    @Test
+    public void testLaunchCheckupOnDeviceShowsPasswordCheckupForLocalWhenSyncing()
+            throws PendingIntent.CanceledException {
+        // Local checkup will be launched from the leak detection dialog if the leaked credential is
+        // stored only in the local store, even though the user is syncing passwords.
+        when(mMockSyncService.getSelectedTypes())
+                .thenReturn(CollectionUtil.newHashSet(UserSelectableType.PASSWORDS));
+        when(mMockPasswordManagerUtilBridgeJni.canUseUPMBackend(true, mPrefService))
+                .thenReturn(true);
+
+        PasswordCheckupLauncher.launchCheckupOnDevice(
+                mMockWindowAndroid, LEAK_DIALOG, TEST_NO_EMAIL_ADDRESS);
+
+        verify(mMockPendingIntentForLocalCheckup).send();
+    }
+}
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
index 31e7b995..4c9b0b7 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
@@ -52,6 +52,7 @@
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.loading_modal.LoadingModalDialogCoordinator;
@@ -90,8 +91,11 @@
 @Batch(Batch.PER_CLASS)
 public class PasswordManagerHelperTest {
     private static final String TEST_EMAIL_ADDRESS = "test@email.com";
+    private static final String TEST_NO_EMAIL_ADDRESS = null;
+
     // TODO(crbug.com/1500673): Deduplicate the histogram names with those in PasswordManagerHelper.
     // TODO(crbug.com/1500676): Use HistogramWatcher to check histogram records.
+    // TODO(crbug.com/1511244): Parametrize the tests for account and local storage.
     private static final String ACCOUNT_GET_INTENT_LATENCY_HISTOGRAM =
             "PasswordManager.CredentialManager.Account.GetIntent.Latency";
     private static final String ACCOUNT_GET_INTENT_SUCCESS_HISTOGRAM =
@@ -368,7 +372,7 @@
     }
 
     @Test
-    public void testShowsUpdateDialogOnShowPasswordCheckupWhenBackendUpdateNeeded()
+    public void testShowsUpdateDialogOnShowPasswordCheckupForAccountWhenBackendUpdateNeeded()
             throws PasswordCheckBackendException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
 
@@ -384,7 +388,31 @@
                 ContextUtils.getApplicationContext(),
                 PasswordCheckReferrer.SAFETY_CHECK,
                 mSyncServiceMock,
-                mModalDialogManagerSupplier);
+                mModalDialogManagerSupplier,
+                TEST_EMAIL_ADDRESS);
+
+        assertNotNull(mModalDialogManager.getCurrentDialogForTest());
+    }
+
+    @Test
+    public void testShowsUpdateDialogOnShowPasswordCheckupForLocalWhenBackendUpdateNeeded()
+            throws PasswordCheckBackendException {
+        chooseToSyncPasswordsWithoutCustomPassphrase();
+
+        when(mBackendSupportHelperMock.isBackendPresent()).thenReturn(true);
+        when(mBackendSupportHelperMock.isUpdateNeeded()).thenReturn(true);
+
+        when(mPasswordCheckupClientHelperFactoryMock.createHelper())
+                .thenThrow(
+                        new PasswordCheckBackendException(
+                                "", CredentialManagerError.BACKEND_VERSION_NOT_SUPPORTED));
+
+        PasswordManagerHelper.showPasswordCheckup(
+                ContextUtils.getApplicationContext(),
+                PasswordCheckReferrer.SAFETY_CHECK,
+                mSyncServiceMock,
+                mModalDialogManagerSupplier,
+                TEST_NO_EMAIL_ADDRESS);
 
         assertNotNull(mModalDialogManager.getCurrentDialogForTest());
     }
@@ -408,7 +436,7 @@
     }
 
     @Test
-    public void testDoesNotShowUpdateDialogOnShowPasswordCheckupWhenNoUpdateNeeded() {
+    public void testDoesNotShowUpdateDialogOnShowPasswordCheckupForAccountWhenNoUpdateNeeded() {
         chooseToSyncPasswordsWithoutCustomPassphrase();
 
         when(mBackendSupportHelperMock.isBackendPresent()).thenReturn(true);
@@ -418,7 +446,25 @@
                 ContextUtils.getApplicationContext(),
                 PasswordCheckReferrer.SAFETY_CHECK,
                 mSyncServiceMock,
-                mModalDialogManagerSupplier);
+                mModalDialogManagerSupplier,
+                TEST_EMAIL_ADDRESS);
+
+        assertNull(mModalDialogManager.getCurrentDialogForTest());
+    }
+
+    @Test
+    public void testDoesNotShowUpdateDialogOnShowPasswordCheckupForLocalWhenNoUpdateNeeded() {
+        chooseToSyncPasswordsWithoutCustomPassphrase();
+
+        when(mBackendSupportHelperMock.isBackendPresent()).thenReturn(true);
+        when(mBackendSupportHelperMock.isUpdateNeeded()).thenReturn(false);
+
+        PasswordManagerHelper.showPasswordCheckup(
+                ContextUtils.getApplicationContext(),
+                PasswordCheckReferrer.SAFETY_CHECK,
+                mSyncServiceMock,
+                mModalDialogManagerSupplier,
+                TEST_NO_EMAIL_ADDRESS);
 
         assertNull(mModalDialogManager.getCurrentDialogForTest());
     }
@@ -768,7 +814,8 @@
                 ContextUtils.getApplicationContext(),
                 PasswordCheckReferrer.SAFETY_CHECK,
                 mSyncServiceMock,
-                mModalDialogManagerSupplier);
+                mModalDialogManagerSupplier,
+                TEST_EMAIL_ADDRESS);
 
         verify(mPasswordCheckupClientHelperMock)
                 .getPasswordCheckupIntent(
@@ -779,27 +826,88 @@
     }
 
     @Test
-    public void testPasswordCheckupIntentCalledIfSuccess() throws CanceledException {
-        chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+    public void testRetrievesIntentForLocalCheckup() {
+        when(mPasswordManagerUtilBridgeJniMock.canUseUPMBackend(false, mPrefService))
+                .thenReturn(true);
+
         PasswordManagerHelper.showPasswordCheckup(
                 ContextUtils.getApplicationContext(),
                 PasswordCheckReferrer.SAFETY_CHECK,
                 mSyncServiceMock,
-                mModalDialogManagerSupplier);
+                mModalDialogManagerSupplier,
+                TEST_NO_EMAIL_ADDRESS);
+
+        verify(mPasswordCheckupClientHelperMock)
+                .getPasswordCheckupIntent(
+                        eq(PasswordCheckReferrer.SAFETY_CHECK),
+                        eq(Optional.empty()),
+                        any(Callback.class),
+                        any(Callback.class));
+    }
+
+    @Test
+    public void testPasswordCheckupIntentForAccountCalledIfSuccess() throws CanceledException {
+        chooseToSyncPasswordsWithoutCustomPassphrase();
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
+        PasswordManagerHelper.showPasswordCheckup(
+                ContextUtils.getApplicationContext(),
+                PasswordCheckReferrer.SAFETY_CHECK,
+                mSyncServiceMock,
+                mModalDialogManagerSupplier,
+                TEST_EMAIL_ADDRESS);
         verify(mPendingIntentMock).send();
     }
 
     @Test
-    public void testRecordsSuccessMetricsForPasswordCheckupIntent() {
+    public void testPasswordCheckupIntentForLocalCalledIfSuccess() throws CanceledException {
+        when(mPasswordManagerUtilBridgeJniMock.canUseUPMBackend(false, mPrefService))
+                .thenReturn(true);
+
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_NO_EMAIL_ADDRESS);
+        PasswordManagerHelper.showPasswordCheckup(
+                ContextUtils.getApplicationContext(),
+                PasswordCheckReferrer.SAFETY_CHECK,
+                mSyncServiceMock,
+                mModalDialogManagerSupplier,
+                TEST_NO_EMAIL_ADDRESS);
+        verify(mPendingIntentMock).send();
+    }
+
+    @Test
+    public void testRecordsSuccessMetricsForPasswordCheckupIntentForAccount() {
+        HistogramWatcher histogram =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(
+                                PASSWORD_CHECKUP_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM, 1)
+                        .build();
+
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
 
         PasswordManagerHelper.showPasswordCheckup(
                 ContextUtils.getApplicationContext(),
                 PasswordCheckReferrer.SAFETY_CHECK,
                 mSyncServiceMock,
-                mModalDialogManagerSupplier);
+                mModalDialogManagerSupplier,
+                TEST_EMAIL_ADDRESS);
+        checkPasswordCheckupSuccessHistogramsForOperation(
+                PasswordCheckOperation.GET_PASSWORD_CHECKUP_INTENT);
+
+        histogram.assertExpected();
+    }
+
+    @Test
+    public void testRecordsSuccessMetricsForPasswordCheckupIntentForLocal() {
+        when(mPasswordManagerUtilBridgeJniMock.canUseUPMBackend(false, mPrefService))
+                .thenReturn(true);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_NO_EMAIL_ADDRESS);
+
+        PasswordManagerHelper.showPasswordCheckup(
+                ContextUtils.getApplicationContext(),
+                PasswordCheckReferrer.SAFETY_CHECK,
+                mSyncServiceMock,
+                mModalDialogManagerSupplier,
+                TEST_NO_EMAIL_ADDRESS);
         checkPasswordCheckupSuccessHistogramsForOperation(
                 PasswordCheckOperation.GET_PASSWORD_CHECKUP_INTENT);
         assertEquals(
@@ -809,16 +917,18 @@
     }
 
     @Test
-    public void testRecordsErrorMetricsForPasswordCheckupIntent() {
+    public void testRecordsErrorMetricsForPasswordCheckupIntentForAccount() {
         chooseToSyncPasswordsWithoutCustomPassphrase();
         returnErrorWhenFetchingIntentForPasswordCheckup(
-                new PasswordCheckBackendException("", CredentialManagerError.UNCATEGORIZED));
+                new PasswordCheckBackendException("", CredentialManagerError.UNCATEGORIZED),
+                TEST_EMAIL_ADDRESS);
 
         PasswordManagerHelper.showPasswordCheckup(
                 ContextUtils.getApplicationContext(),
                 PasswordCheckReferrer.SAFETY_CHECK,
                 mSyncServiceMock,
-                mModalDialogManagerSupplier);
+                mModalDialogManagerSupplier,
+                TEST_EMAIL_ADDRESS);
 
         checkPasswordCheckupFailureHistogramsForOperation(
                 PasswordCheckOperation.GET_PASSWORD_CHECKUP_INTENT,
@@ -831,16 +941,75 @@
     }
 
     @Test
-    public void testRecordsApiErrorMetricsForPasswordCheckupIntent() {
-        chooseToSyncPasswordsWithoutCustomPassphrase();
+    public void testRecordsErrorMetricsForPasswordCheckupIntentForLocal() {
+        HistogramWatcher histogram =
+                HistogramWatcher.newBuilder()
+                        .expectNoRecords(
+                                PASSWORD_CHECKUP_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM)
+                        .build();
+
+        when(mPasswordManagerUtilBridgeJniMock.canUseUPMBackend(false, mPrefService))
+                .thenReturn(true);
         returnErrorWhenFetchingIntentForPasswordCheckup(
-                new ApiException(new Status(CommonStatusCodes.DEVELOPER_ERROR)));
+                new PasswordCheckBackendException("", CredentialManagerError.UNCATEGORIZED),
+                TEST_NO_EMAIL_ADDRESS);
 
         PasswordManagerHelper.showPasswordCheckup(
                 ContextUtils.getApplicationContext(),
                 PasswordCheckReferrer.SAFETY_CHECK,
                 mSyncServiceMock,
-                mModalDialogManagerSupplier);
+                mModalDialogManagerSupplier,
+                TEST_NO_EMAIL_ADDRESS);
+
+        checkPasswordCheckupFailureHistogramsForOperation(
+                PasswordCheckOperation.GET_PASSWORD_CHECKUP_INTENT,
+                CredentialManagerError.UNCATEGORIZED,
+                OptionalInt.empty());
+
+        histogram.assertExpected();
+    }
+
+    @Test
+    public void testRecordsApiErrorMetricsForPasswordCheckupIntentForAccount() {
+        HistogramWatcher histogram =
+                HistogramWatcher.newBuilder()
+                        .expectNoRecords(
+                                PASSWORD_CHECKUP_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM)
+                        .build();
+
+        chooseToSyncPasswordsWithoutCustomPassphrase();
+        returnErrorWhenFetchingIntentForPasswordCheckup(
+                new ApiException(new Status(CommonStatusCodes.DEVELOPER_ERROR)),
+                TEST_EMAIL_ADDRESS);
+
+        PasswordManagerHelper.showPasswordCheckup(
+                ContextUtils.getApplicationContext(),
+                PasswordCheckReferrer.SAFETY_CHECK,
+                mSyncServiceMock,
+                mModalDialogManagerSupplier,
+                TEST_EMAIL_ADDRESS);
+        checkPasswordCheckupFailureHistogramsForOperation(
+                PasswordCheckOperation.GET_PASSWORD_CHECKUP_INTENT,
+                CredentialManagerError.API_ERROR,
+                OptionalInt.of(CommonStatusCodes.DEVELOPER_ERROR));
+
+        histogram.assertExpected();
+    }
+
+    @Test
+    public void testRecordsApiErrorMetricsForPasswordCheckupIntentForLocal() {
+        when(mPasswordManagerUtilBridgeJniMock.canUseUPMBackend(false, mPrefService))
+                .thenReturn(true);
+        returnErrorWhenFetchingIntentForPasswordCheckup(
+                new ApiException(new Status(CommonStatusCodes.DEVELOPER_ERROR)),
+                TEST_NO_EMAIL_ADDRESS);
+
+        PasswordManagerHelper.showPasswordCheckup(
+                ContextUtils.getApplicationContext(),
+                PasswordCheckReferrer.SAFETY_CHECK,
+                mSyncServiceMock,
+                mModalDialogManagerSupplier,
+                TEST_NO_EMAIL_ADDRESS);
         checkPasswordCheckupFailureHistogramsForOperation(
                 PasswordCheckOperation.GET_PASSWORD_CHECKUP_INTENT,
                 CredentialManagerError.API_ERROR,
@@ -951,14 +1120,15 @@
     @Test
     public void testRecordsMetricsWhenPasswordCheckupIntentFails() throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
         doThrow(CanceledException.class).when(mPendingIntentMock).send();
 
         PasswordManagerHelper.showPasswordCheckup(
                 ContextUtils.getApplicationContext(),
                 PasswordCheckReferrer.SAFETY_CHECK,
                 mSyncServiceMock,
-                mModalDialogManagerSupplier);
+                mModalDialogManagerSupplier,
+                TEST_EMAIL_ADDRESS);
         checkPasswordCheckupSuccessHistogramsForOperation(
                 PasswordCheckOperation.GET_PASSWORD_CHECKUP_INTENT);
         assertEquals(
@@ -984,7 +1154,7 @@
     @Test
     public void testDismissesLoadingDialogWhenPasswordCheckupIntentSent() throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
 
         PasswordManagerHelper.launchPasswordCheckup(
                 PasswordCheckReferrer.SAFETY_CHECK,
@@ -1000,7 +1170,7 @@
     public void testDismissesLoadingDialogOnPasswordCheckupIntentSendError()
             throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
         doThrow(CanceledException.class).when(mPendingIntentMock).send();
 
         PasswordManagerHelper.launchPasswordCheckup(
@@ -1018,7 +1188,8 @@
             throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
         returnErrorWhenFetchingIntentForPasswordCheckup(
-                new PasswordCheckBackendException("", CredentialManagerError.UNCATEGORIZED));
+                new PasswordCheckBackendException("", CredentialManagerError.UNCATEGORIZED),
+                TEST_EMAIL_ADDRESS);
 
         PasswordManagerHelper.launchPasswordCheckup(
                 PasswordCheckReferrer.SAFETY_CHECK,
@@ -1034,7 +1205,7 @@
     public void testDoesNotLaunchPasswordCheckupIntentWhenLoadingDialogCancelled()
             throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
         when(mLoadingModalDialogCoordinator.getState())
                 .thenReturn(LoadingModalDialogCoordinator.State.CANCELLED);
 
@@ -1052,7 +1223,7 @@
     public void testDoesNotLaunchPasswordCheckupIntentWhenLoadingDialogTimedOut()
             throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
         when(mLoadingModalDialogCoordinator.getState())
                 .thenReturn(LoadingModalDialogCoordinator.State.TIMED_OUT);
 
@@ -1069,7 +1240,7 @@
     @Test
     public void testPasswordCheckupLaunchWaitsForDialogDismissability() throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
         when(mLoadingModalDialogCoordinator.getState())
                 .thenReturn(LoadingModalDialogCoordinator.State.SHOWN);
 
@@ -1377,7 +1548,7 @@
     @Test
     public void testRecordsCheckupLoadingDialogMetricsOnDialogNotShown() throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
         when(mLoadingModalDialogCoordinator.getState())
                 .thenReturn(LoadingModalDialogCoordinator.State.PENDING);
 
@@ -1394,7 +1565,7 @@
     @Test
     public void testRecordsCheckupLoadingDialogMetricsOnDialogShown() throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
         when(mLoadingModalDialogCoordinator.getState())
                 .thenReturn(LoadingModalDialogCoordinator.State.SHOWN);
         when(mLoadingModalDialogCoordinator.isImmediatelyDismissable()).thenReturn(true);
@@ -1414,7 +1585,7 @@
     public void testRecordsCheckupLoadingDialogMetricsOnDialogShownNonDismissable()
             throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
         when(mLoadingModalDialogCoordinator.getState())
                 .thenReturn(LoadingModalDialogCoordinator.State.SHOWN);
         when(mLoadingModalDialogCoordinator.isImmediatelyDismissable()).thenReturn(false);
@@ -1435,7 +1606,7 @@
     @Test
     public void testRecordsCheckupLoadingDialogMetricsOnDialogCancelled() throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
         when(mLoadingModalDialogCoordinator.getState())
                 .thenReturn(LoadingModalDialogCoordinator.State.CANCELLED);
 
@@ -1451,7 +1622,7 @@
     public void testRecordsCheckupLoadingDialogMetricsOnDialogCancelledDuringLoad()
             throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
         when(mLoadingModalDialogCoordinator.getState())
                 .thenReturn(LoadingModalDialogCoordinator.State.SHOWN);
 
@@ -1471,7 +1642,7 @@
     @Test
     public void testRecordsCheckupLoadingDialogMetricsOnDialogTimeout() throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
         when(mLoadingModalDialogCoordinator.getState())
                 .thenReturn(LoadingModalDialogCoordinator.State.TIMED_OUT);
 
@@ -1487,7 +1658,7 @@
     public void testRecordsCheckupLoadingDialogMetricsOnDialogTimeoutDuringLoad()
             throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
         when(mLoadingModalDialogCoordinator.getState())
                 .thenReturn(LoadingModalDialogCoordinator.State.SHOWN);
 
@@ -1509,7 +1680,8 @@
             throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
         returnErrorWhenFetchingIntentForPasswordCheckup(
-                new PasswordCheckBackendException("", CredentialManagerError.UNCATEGORIZED));
+                new PasswordCheckBackendException("", CredentialManagerError.UNCATEGORIZED),
+                TEST_EMAIL_ADDRESS);
         when(mLoadingModalDialogCoordinator.getState())
                 .thenReturn(LoadingModalDialogCoordinator.State.PENDING);
 
@@ -1526,7 +1698,7 @@
     @Test
     public void testRecordsCheckupLoadingDialogMetricsOnIntentSendError() throws CanceledException {
         chooseToSyncPasswordsWithoutCustomPassphrase();
-        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock);
+        setUpSuccessfulCheckupIntentFetching(mPendingIntentMock, TEST_EMAIL_ADDRESS);
         doThrow(CanceledException.class).when(mPendingIntentMock).send();
         when(mLoadingModalDialogCoordinator.getState())
                 .thenReturn(LoadingModalDialogCoordinator.State.PENDING);
@@ -1777,7 +1949,7 @@
                         any(Callback.class));
     }
 
-    private void setUpSuccessfulCheckupIntentFetching(PendingIntent intent) {
+    private void setUpSuccessfulCheckupIntentFetching(PendingIntent intent, String accountEmail) {
         doAnswer(
                         invocation -> {
                             Callback<PendingIntent> cb = invocation.getArgument(2);
@@ -1787,7 +1959,7 @@
                 .when(mPasswordCheckupClientHelperMock)
                 .getPasswordCheckupIntent(
                         anyInt(),
-                        eq(Optional.of(TEST_EMAIL_ADDRESS)),
+                        eq(accountEmail == null ? Optional.empty() : Optional.of(accountEmail)),
                         any(Callback.class),
                         any(Callback.class));
     }
@@ -1850,7 +2022,8 @@
                         any(Callback.class));
     }
 
-    private void returnErrorWhenFetchingIntentForPasswordCheckup(Exception error) {
+    private void returnErrorWhenFetchingIntentForPasswordCheckup(
+            Exception error, String accountEmail) {
         doAnswer(
                         invocation -> {
                             Callback<Exception> cb = invocation.getArgument(3);
@@ -1860,7 +2033,7 @@
                 .when(mPasswordCheckupClientHelperMock)
                 .getPasswordCheckupIntent(
                         anyInt(),
-                        eq(Optional.of(TEST_EMAIL_ADDRESS)),
+                        eq(accountEmail == null ? Optional.empty() : Optional.of(accountEmail)),
                         any(Callback.class),
                         any(Callback.class));
     }
diff --git a/chrome/browser/password_manager/android/password_checkup_launcher_helper_impl.cc b/chrome/browser/password_manager/android/password_checkup_launcher_helper_impl.cc
index 8b18f996..5040635 100644
--- a/chrome/browser/password_manager/android/password_checkup_launcher_helper_impl.cc
+++ b/chrome/browser/password_manager/android/password_checkup_launcher_helper_impl.cc
@@ -25,11 +25,12 @@
   if (!windowAndroid) {
     return;
   }
-  // TODO(b/306669939): Pass the |account_email| to Java and launch the
-  // appropriate checkup: for the account or local.
   Java_PasswordCheckupLauncher_launchCheckupOnDevice(
       env, windowAndroid->GetJavaObject(),
-      static_cast<int>(passwordCheckReferrer));
+      static_cast<int>(passwordCheckReferrer),
+      account_email.empty()
+          ? nullptr
+          : base::android::ConvertUTF8ToJavaString(env, account_email));
 }
 
 void PasswordCheckupLauncherHelperImpl::LaunchCheckupOnlineWithActivity(
diff --git a/chrome/browser/password_manager/android/test_support/java/src/org/chromium/chrome/browser/password_manager/FakePasswordCheckupClientHelper.java b/chrome/browser/password_manager/android/test_support/java/src/org/chromium/chrome/browser/password_manager/FakePasswordCheckupClientHelper.java
index 3625b1f..bbd6925c 100644
--- a/chrome/browser/password_manager/android/test_support/java/src/org/chromium/chrome/browser/password_manager/FakePasswordCheckupClientHelper.java
+++ b/chrome/browser/password_manager/android/test_support/java/src/org/chromium/chrome/browser/password_manager/FakePasswordCheckupClientHelper.java
@@ -14,7 +14,7 @@
 public class FakePasswordCheckupClientHelper implements PasswordCheckupClientHelper {
     private PendingIntent mPendingIntentForLocalCheckup;
     private PendingIntent mPendingIntentForAccountCheckup;
-    private Integer mBreachedCredentialsCount;
+    private Integer mBreachedCredentialsCount = 0;
     private Exception mError;
 
     public void setIntentForLocalCheckup(PendingIntent pendingIntent) {
diff --git a/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc b/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc
index e7238603..739629f 100644
--- a/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc
+++ b/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc
@@ -21,7 +21,7 @@
 #include "chrome/browser/performance_manager/decorators/page_live_state_decorator_delegate_impl.h"
 #include "chrome/browser/performance_manager/metrics/memory_pressure_metrics.h"
 #include "chrome/browser/performance_manager/metrics/metrics_provider_desktop.h"
-#include "chrome/browser/performance_manager/metrics/page_timeline_monitor.h"
+#include "chrome/browser/performance_manager/metrics/page_resource_monitor.h"
 #include "chrome/browser/performance_manager/observers/page_load_metrics_observer.h"
 #include "chrome/browser/performance_manager/policies/background_tab_loading_policy.h"
 #include "chrome/browser/performance_manager/policies/policy_features.h"
@@ -216,7 +216,7 @@
   if (base::FeatureList::IsEnabled(
           performance_manager::features::kPageTimelineMonitor)) {
     graph->PassToGraph(
-        std::make_unique<performance_manager::metrics::PageTimelineMonitor>());
+        std::make_unique<performance_manager::metrics::PageResourceMonitor>());
   }
 
   if (base::FeatureList::IsEnabled(
diff --git a/chrome/browser/performance_manager/metrics/page_timeline_cpu_monitor.cc b/chrome/browser/performance_manager/metrics/page_resource_cpu_monitor.cc
similarity index 92%
rename from chrome/browser/performance_manager/metrics/page_timeline_cpu_monitor.cc
rename to chrome/browser/performance_manager/metrics/page_resource_cpu_monitor.cc
index 5ed16db..d99b4629 100644
--- a/chrome/browser/performance_manager/metrics/page_timeline_cpu_monitor.cc
+++ b/chrome/browser/performance_manager/metrics/page_resource_cpu_monitor.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/performance_manager/metrics/page_timeline_cpu_monitor.h"
+#include "chrome/browser/performance_manager/metrics/page_resource_cpu_monitor.h"
 
 #include <map>
 #include <memory>
@@ -32,13 +32,13 @@
 using resource_attribution::PageContext;
 using resource_attribution::ResourceContext;
 
-PageTimelineCPUMonitor::PageTimelineCPUMonitor()
+PageResourceCPUMonitor::PageResourceCPUMonitor()
     : cpu_measurement_delegate_factory_(
           CPUMeasurementDelegate::GetDefaultFactory()) {}
 
-PageTimelineCPUMonitor::~PageTimelineCPUMonitor() = default;
+PageResourceCPUMonitor::~PageResourceCPUMonitor() = default;
 
-void PageTimelineCPUMonitor::SetCPUMeasurementDelegateFactoryForTesting(
+void PageResourceCPUMonitor::SetCPUMeasurementDelegateFactoryForTesting(
     Graph* graph,
     CPUMeasurementDelegate::Factory* factory) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -53,7 +53,7 @@
   cpu_measurement_delegate_factory_ = factory;
 }
 
-void PageTimelineCPUMonitor::StartMonitoring(Graph* graph) {
+void PageResourceCPUMonitor::StartMonitoring(Graph* graph) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   CHECK(last_measurement_time_.is_null());
@@ -76,7 +76,7 @@
   }
 }
 
-void PageTimelineCPUMonitor::StopMonitoring(Graph* graph) {
+void PageResourceCPUMonitor::StopMonitoring(Graph* graph) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK(!last_measurement_time_.is_null());
   last_measurement_time_ = base::TimeTicks();
@@ -90,7 +90,7 @@
   }
 }
 
-void PageTimelineCPUMonitor::UpdateCPUMeasurements(
+void PageResourceCPUMonitor::UpdateCPUMeasurements(
     base::OnceCallback<void(const CPUUsageMap&)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Update CPU metrics, attributing the cumulative CPU of each process to its
@@ -99,7 +99,7 @@
   const base::TimeTicks now = base::TimeTicks::Now();
   if (features::kUseResourceAttributionCPUMonitor.Get()) {
     cpu_query_->QueryOnce(base::BindOnce(
-        &PageTimelineCPUMonitor::UpdateResourceAttributionCPUMeasurements,
+        &PageResourceCPUMonitor::UpdateResourceAttributionCPUMeasurements,
         weak_factory_.GetWeakPtr(), std::move(callback),
         now - last_measurement_time_));
   } else {
@@ -116,7 +116,7 @@
 }
 
 // static
-double PageTimelineCPUMonitor::EstimatePageCPUUsage(
+double PageResourceCPUMonitor::EstimatePageCPUUsage(
     const PageNode* page_node,
     const CPUUsageMap& cpu_usage_map) {
   if (features::kUseResourceAttributionCPUMonitor.Get()) {
@@ -149,7 +149,7 @@
   return page_cpu_usage;
 }
 
-void PageTimelineCPUMonitor::OnProcessLifetimeChange(
+void PageResourceCPUMonitor::OnProcessLifetimeChange(
     const ProcessNode* process_node) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK(!features::kUseResourceAttributionCPUMonitor.Get());
@@ -168,14 +168,14 @@
   }
 }
 
-void PageTimelineCPUMonitor::OnBeforeProcessNodeRemoved(
+void PageResourceCPUMonitor::OnBeforeProcessNodeRemoved(
     const ProcessNode* process_node) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK(!features::kUseResourceAttributionCPUMonitor.Get());
   cpu_measurement_map_.erase(process_node);
 }
 
-void PageTimelineCPUMonitor::MonitorCPUUsage(const ProcessNode* process_node) {
+void PageResourceCPUMonitor::MonitorCPUUsage(const ProcessNode* process_node) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK(!features::kUseResourceAttributionCPUMonitor.Get());
   // Only measure renderers.
@@ -190,7 +190,7 @@
   CHECK(was_inserted);
 }
 
-void PageTimelineCPUMonitor::UpdateResourceAttributionCPUMeasurements(
+void PageResourceCPUMonitor::UpdateResourceAttributionCPUMeasurements(
     base::OnceCallback<void(const CPUUsageMap&)> callback,
     base::TimeDelta measurement_interval,
     const resource_attribution::QueryResultMap& results) {
@@ -296,7 +296,7 @@
   std::move(callback).Run(std::move(cpu_usage_map));
 }
 
-PageTimelineCPUMonitor::CPUMeasurement::CPUMeasurement(
+PageResourceCPUMonitor::CPUMeasurement::CPUMeasurement(
     std::unique_ptr<CPUMeasurementDelegate> delegate)
     : delegate_(std::move(delegate)),
       // Record the CPU usage immediately on starting to measure a process, so
@@ -304,16 +304,16 @@
       // time between the measurement starting and the snapshot.
       most_recent_measurement_(delegate_->GetCumulativeCPUUsage()) {}
 
-PageTimelineCPUMonitor::CPUMeasurement::~CPUMeasurement() = default;
+PageResourceCPUMonitor::CPUMeasurement::~CPUMeasurement() = default;
 
-PageTimelineCPUMonitor::CPUMeasurement::CPUMeasurement(
-    PageTimelineCPUMonitor::CPUMeasurement&& other) = default;
+PageResourceCPUMonitor::CPUMeasurement::CPUMeasurement(
+    PageResourceCPUMonitor::CPUMeasurement&& other) = default;
 
-PageTimelineCPUMonitor::CPUMeasurement&
-PageTimelineCPUMonitor::CPUMeasurement::operator=(
-    PageTimelineCPUMonitor::CPUMeasurement&& other) = default;
+PageResourceCPUMonitor::CPUMeasurement&
+PageResourceCPUMonitor::CPUMeasurement::operator=(
+    PageResourceCPUMonitor::CPUMeasurement&& other) = default;
 
-void PageTimelineCPUMonitor::CPUMeasurement::MeasureAndDistributeCPUUsage(
+void PageResourceCPUMonitor::CPUMeasurement::MeasureAndDistributeCPUUsage(
     const ProcessNode* process_node,
     base::TimeTicks measurement_interval_start,
     base::TimeTicks measurement_interval_end,
diff --git a/chrome/browser/performance_manager/metrics/page_timeline_cpu_monitor.h b/chrome/browser/performance_manager/metrics/page_resource_cpu_monitor.h
similarity index 90%
rename from chrome/browser/performance_manager/metrics/page_timeline_cpu_monitor.h
rename to chrome/browser/performance_manager/metrics/page_resource_cpu_monitor.h
index f6ce22c..320190a 100644
--- a/chrome/browser/performance_manager/metrics/page_timeline_cpu_monitor.h
+++ b/chrome/browser/performance_manager/metrics/page_resource_cpu_monitor.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_METRICS_PAGE_TIMELINE_CPU_MONITOR_H_
-#define CHROME_BROWSER_PERFORMANCE_MANAGER_METRICS_PAGE_TIMELINE_CPU_MONITOR_H_
+#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_METRICS_PAGE_RESOURCE_CPU_MONITOR_H_
+#define CHROME_BROWSER_PERFORMANCE_MANAGER_METRICS_PAGE_RESOURCE_CPU_MONITOR_H_
 
 #include <map>
 #include <memory>
@@ -27,8 +27,8 @@
 namespace metrics {
 
 // Periodically collect CPU usage from process nodes, for the UKM logged in
-// PageTimelineCPUMonitor.
-class PageTimelineCPUMonitor : public ProcessNode::ObserverDefaultImpl {
+// PageResourceMonitor.
+class PageResourceCPUMonitor : public ProcessNode::ObserverDefaultImpl {
  public:
   // A shim to request CPU measurements for a process. A new
   // CPUMeasurementDelegate object will be created for each ProcessNode to be
@@ -49,11 +49,11 @@
   // available.
   using CPUUsageMap = std::map<resource_attribution::ResourceContext, double>;
 
-  PageTimelineCPUMonitor();
-  ~PageTimelineCPUMonitor() override;
+  PageResourceCPUMonitor();
+  ~PageResourceCPUMonitor() override;
 
-  PageTimelineCPUMonitor(const PageTimelineCPUMonitor& other) = delete;
-  PageTimelineCPUMonitor& operator=(const PageTimelineCPUMonitor&) = delete;
+  PageResourceCPUMonitor(const PageResourceCPUMonitor& other) = delete;
+  PageResourceCPUMonitor& operator=(const PageResourceCPUMonitor&) = delete;
 
   // The given `factory` will be used to create a CPUMeasurementDelegate for
   // each ProcessNode in `graph` to be measured.
@@ -85,7 +85,7 @@
   void OnBeforeProcessNodeRemoved(const ProcessNode* process_node) override;
 
  private:
-  friend class PageTimelineCPUMonitorTest;
+  friend class PageResourceCPUMonitorTest;
 
   // Holds a CPUMeasurementDelegate object to measure CPU usage and metadata
   // about the measurements. One CPUMeasurement will be created for each
@@ -150,7 +150,7 @@
       GUARDED_BY_CONTEXT(sequence_checker_);
 
   // If the kUseResourceAttributionCPUMonitor feature parameter is enabled,
-  // PageTimelineCPUMonitor will get CPU measurements from this, otherwise it
+  // PageResourceCPUMonitor will get CPU measurements from this, otherwise it
   // will perform its own measurements.
   std::unique_ptr<resource_attribution::ScopedCPUQuery> cpu_query_
       GUARDED_BY_CONTEXT(sequence_checker_);
@@ -161,10 +161,10 @@
   resource_attribution::QueryResultMap cached_cpu_measurements_
       GUARDED_BY_CONTEXT(sequence_checker_);
 
-  base::WeakPtrFactory<PageTimelineCPUMonitor> weak_factory_{this};
+  base::WeakPtrFactory<PageResourceCPUMonitor> weak_factory_{this};
 };
 
 }  // namespace metrics
 }  // namespace performance_manager
 
-#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_METRICS_PAGE_TIMELINE_CPU_MONITOR_H_
+#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_METRICS_PAGE_RESOURCE_CPU_MONITOR_H_
diff --git a/chrome/browser/performance_manager/metrics/page_timeline_cpu_monitor_unittest.cc b/chrome/browser/performance_manager/metrics/page_resource_cpu_monitor_unittest.cc
similarity index 94%
rename from chrome/browser/performance_manager/metrics/page_timeline_cpu_monitor_unittest.cc
rename to chrome/browser/performance_manager/metrics/page_resource_cpu_monitor_unittest.cc
index 81bece2f..3301ff9 100644
--- a/chrome/browser/performance_manager/metrics/page_timeline_cpu_monitor_unittest.cc
+++ b/chrome/browser/performance_manager/metrics/page_resource_cpu_monitor_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/performance_manager/metrics/page_timeline_cpu_monitor.h"
+#include "chrome/browser/performance_manager/metrics/page_resource_cpu_monitor.h"
 
 #include <memory>
 #include <utility>
@@ -75,7 +75,7 @@
 // Helpers to lookup measurement results from TestNodeWrapper's.
 
 absl::optional<double> GetMeasurementResult(
-    const PageTimelineCPUMonitor::CPUUsageMap& cpu_usage_map,
+    const PageResourceCPUMonitor::CPUUsageMap& cpu_usage_map,
     const ResourceContext& context) {
   const auto it = cpu_usage_map.find(context);
   if (it == cpu_usage_map.end()) {
@@ -85,14 +85,14 @@
 }
 
 absl::optional<double> GetMeasurementResult(
-    const PageTimelineCPUMonitor::CPUUsageMap& cpu_usage_map,
+    const PageResourceCPUMonitor::CPUUsageMap& cpu_usage_map,
     const TestNodeWrapper<FrameNodeImpl>& frame_wrapper) {
   return GetMeasurementResult(cpu_usage_map,
                               frame_wrapper->GetResourceContext());
 }
 
 absl::optional<double> GetMeasurementResult(
-    const PageTimelineCPUMonitor::CPUUsageMap& cpu_usage_map,
+    const PageResourceCPUMonitor::CPUUsageMap& cpu_usage_map,
     const TestNodeWrapper<WorkerNodeImpl>& worker_wrapper) {
   return GetMeasurementResult(cpu_usage_map,
                               worker_wrapper->GetResourceContext());
@@ -115,12 +115,12 @@
 
 }  // namespace
 
-class PageTimelineCPUMonitorTest : public GraphTestHarness,
+class PageResourceCPUMonitorTest : public GraphTestHarness,
                                    public ::testing::WithParamInterface<bool> {
  protected:
   using Super = GraphTestHarness;
 
-  PageTimelineCPUMonitorTest() {
+  PageResourceCPUMonitorTest() {
     scoped_feature_list_.InitAndEnableFeatureWithParameters(
         features::kPageTimelineMonitor,
         {{"use_resource_attribution_cpu_monitor",
@@ -196,12 +196,12 @@
     delegate_factory_.GetDelegate(process_node).SetError(usage_error);
   }
 
-  PageTimelineCPUMonitor::CPUUsageMap WaitForCPUMeasurements() {
-    PageTimelineCPUMonitor::CPUUsageMap cpu_usage_map;
+  PageResourceCPUMonitor::CPUUsageMap WaitForCPUMeasurements() {
+    PageResourceCPUMonitor::CPUUsageMap cpu_usage_map;
     base::RunLoop run_loop;
     cpu_monitor_.UpdateCPUMeasurements(
         base::BindLambdaForTesting(
-            [&](const PageTimelineCPUMonitor::CPUUsageMap& results) {
+            [&](const PageResourceCPUMonitor::CPUUsageMap& results) {
               cpu_usage_map = results;
             })
             .Then(run_loop.QuitClosure()));
@@ -218,7 +218,7 @@
       delegate_factory_;
 
   // The object under test.
-  PageTimelineCPUMonitor cpu_monitor_;
+  PageResourceCPUMonitor cpu_monitor_;
 
   TestNodeWrapper<ProcessNodeImpl> mock_utility_process_;
 
@@ -226,9 +226,9 @@
       mock_graph_;
 };
 
-INSTANTIATE_TEST_SUITE_P(All, PageTimelineCPUMonitorTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, PageResourceCPUMonitorTest, ::testing::Bool());
 
-TEST_P(PageTimelineCPUMonitorTest, CPUMeasurement) {
+TEST_P(PageResourceCPUMonitorTest, CPUMeasurement) {
   // Create several renderer processes to measure. Put one page and one frame in
   // each renderer, so CPU measurements for the renderer are all assigned to
   // that frame for easy validation.
@@ -382,7 +382,7 @@
   SetProcessExited(renderer4.process_node.get());
 }
 
-TEST_P(PageTimelineCPUMonitorTest, CPUDistribution) {
+TEST_P(PageResourceCPUMonitorTest, CPUDistribution) {
   // Track CPU usage of the mock utility process to make sure that measuring it
   // doesn't crash. Currently only measurements of renderer processes are
   // stored anywhere, so there are no other expectations to verify.
@@ -432,10 +432,10 @@
     // `other_page` gets the sum of `other_frame`, `child_frame` and
     // `other_worker`. See the chart in
     // MockMultiplePagesAndWorkersWithMultipleProcessesGraph.
-    EXPECT_THAT(PageTimelineCPUMonitor::EstimatePageCPUUsage(
+    EXPECT_THAT(PageResourceCPUMonitor::EstimatePageCPUUsage(
                     mock_graph_->page.get(), measurements),
                 DoubleEq(0.4));
-    EXPECT_THAT(PageTimelineCPUMonitor::EstimatePageCPUUsage(
+    EXPECT_THAT(PageResourceCPUMonitor::EstimatePageCPUUsage(
                     mock_graph_->other_page.get(), measurements),
                 DoubleEq(0.7));
   }
@@ -468,10 +468,10 @@
     // `page` gets its CPU usage from the sum of `frame` and `worker`.
     // `other_page` gets the sum of `other_frame`, `child_frame` and
     // `other_worker`.
-    EXPECT_THAT(PageTimelineCPUMonitor::EstimatePageCPUUsage(
+    EXPECT_THAT(PageResourceCPUMonitor::EstimatePageCPUUsage(
                     mock_graph_->page.get(), measurements),
                 DoubleEq(0.2));
-    EXPECT_THAT(PageTimelineCPUMonitor::EstimatePageCPUUsage(
+    EXPECT_THAT(PageResourceCPUMonitor::EstimatePageCPUUsage(
                     mock_graph_->other_page.get(), measurements),
                 DoubleEq(0.9));
   }
@@ -504,10 +504,10 @@
     // `page` gets its CPU usage from the sum of `frame` and `worker`.
     // `other_page` gets the sum of `other_frame`, `child_frame` and
     // `other_worker`.
-    EXPECT_THAT(PageTimelineCPUMonitor::EstimatePageCPUUsage(
+    EXPECT_THAT(PageResourceCPUMonitor::EstimatePageCPUUsage(
                     mock_graph_->page.get(), measurements),
                 DoubleEq(0.2));
-    EXPECT_THAT(PageTimelineCPUMonitor::EstimatePageCPUUsage(
+    EXPECT_THAT(PageResourceCPUMonitor::EstimatePageCPUUsage(
                     mock_graph_->other_page.get(), measurements),
                 DoubleEq(0.1));
   }
@@ -515,7 +515,7 @@
   cpu_monitor_.StopMonitoring(graph());
 }
 
-TEST_P(PageTimelineCPUMonitorTest, CPUMeasurementError) {
+TEST_P(PageResourceCPUMonitorTest, CPUMeasurementError) {
   const SinglePageRendererNodes renderer1 = CreateSimpleCPUTrackingRenderer();
   SetProcessId(renderer1.process_node.get());
   const SinglePageRendererNodes renderer2 = CreateSimpleCPUTrackingRenderer();
@@ -561,13 +561,13 @@
   cpu_monitor_.StopMonitoring(graph());
 }
 
-class PageTimelineCPUMonitorTimingTest
+class PageResourceCPUMonitorTimingTest
     : public ChromeRenderViewHostTestHarness,
       public ::testing::WithParamInterface<bool> {
  protected:
   using Super = ChromeRenderViewHostTestHarness;
 
-  PageTimelineCPUMonitorTimingTest() {
+  PageResourceCPUMonitorTimingTest() {
     scoped_feature_list_.InitAndEnableFeatureWithParameters(
         features::kPageTimelineMonitor,
         {{"use_resource_attribution_cpu_monitor",
@@ -581,7 +581,7 @@
     }
     pm_helper_.SetUp();
     RunInGraph([&](Graph* graph) {
-      cpu_monitor_ = std::make_unique<PageTimelineCPUMonitor>();
+      cpu_monitor_ = std::make_unique<PageResourceCPUMonitor>();
       cpu_monitor_->StartMonitoring(graph);
     });
   }
@@ -606,7 +606,7 @@
   static void TestPageMeasurement(
       base::WeakPtr<FrameNode> frame_node,
       base::FunctionRef<void(absl::optional<double>)> matcher_callback,
-      const PageTimelineCPUMonitor::CPUUsageMap& measurements) {
+      const PageResourceCPUMonitor::CPUUsageMap& measurements) {
     absl::optional<double> measurement_result;
     if (features::kUseResourceAttributionCPUMonitor.Get()) {
       // Resource Attribution stores page estimates directly in
@@ -624,14 +624,14 @@
 
   base::test::ScopedFeatureList scoped_feature_list_;
   PerformanceManagerTestHarnessHelper pm_helper_;
-  std::unique_ptr<PageTimelineCPUMonitor> cpu_monitor_;
+  std::unique_ptr<PageResourceCPUMonitor> cpu_monitor_;
 };
 
 INSTANTIATE_TEST_SUITE_P(All,
-                         PageTimelineCPUMonitorTimingTest,
+                         PageResourceCPUMonitorTimingTest,
                          ::testing::Bool());
 
-TEST_P(PageTimelineCPUMonitorTimingTest, ProcessLifetime) {
+TEST_P(PageResourceCPUMonitorTimingTest, ProcessLifetime) {
   SetContents(CreateTestWebContents());
   content::NavigationSimulator::NavigateAndCommitFromBrowser(
       web_contents(), GURL("https://www.example.com/"));
diff --git a/chrome/browser/performance_manager/metrics/page_timeline_monitor.cc b/chrome/browser/performance_manager/metrics/page_resource_monitor.cc
similarity index 92%
rename from chrome/browser/performance_manager/metrics/page_timeline_monitor.cc
rename to chrome/browser/performance_manager/metrics/page_resource_monitor.cc
index 8bed370..f3c350a 100644
--- a/chrome/browser/performance_manager/metrics/page_timeline_monitor.cc
+++ b/chrome/browser/performance_manager/metrics/page_resource_monitor.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/performance_manager/metrics/page_timeline_monitor.h"
+#include "chrome/browser/performance_manager/metrics/page_resource_monitor.h"
 
 #include <stdint.h>
 #include <algorithm>
@@ -46,7 +46,7 @@
 namespace {
 
 using PageMeasurementBackgroundState =
-    PageTimelineMonitor::PageMeasurementBackgroundState;
+    PageResourceMonitor::PageMeasurementBackgroundState;
 
 // CPU usage metrics are provided as a double in the [0.0, number of cores *
 // 100.0] range. The CPU usage is usually below 1%, so the UKM is
@@ -82,7 +82,7 @@
 
 }  // namespace
 
-PageTimelineMonitor::PageTimelineMonitor(bool enable_system_cpu_probe)
+PageResourceMonitor::PageResourceMonitor(bool enable_system_cpu_probe)
     // These counters are initialized to a random value due to privacy concerns,
     // so that we cannot tie either the startup time of a specific tab or the
     // recording time of a specific slice to the browser startup time.
@@ -92,22 +92,22 @@
   collect_slice_timer_.Start(
       FROM_HERE,
       performance_manager::features::kPageTimelineStateIntervalTime.Get(), this,
-      &PageTimelineMonitor::CollectSlice);
+      &PageResourceMonitor::CollectSlice);
 
   // PageResourceUsage is collected on a different schedule from PageTimeline.
   collect_page_resource_usage_timer_.Start(
       FROM_HERE, base::Minutes(2),
-      base::BindRepeating(&PageTimelineMonitor::CollectPageResourceUsage,
+      base::BindRepeating(&PageResourceMonitor::CollectPageResourceUsage,
                           weak_factory_.GetWeakPtr(), base::DoNothing()));
   if (system_cpu_probe_) {
     system_cpu_probe_->StartSampling();
   }
 }
 
-PageTimelineMonitor::~PageTimelineMonitor() = default;
+PageResourceMonitor::~PageResourceMonitor() = default;
 
-PageTimelineMonitor::PageState
-PageTimelineMonitor::PageNodeInfo::GetPageState() {
+PageResourceMonitor::PageState
+PageResourceMonitor::PageNodeInfo::GetPageState() {
   switch (current_lifecycle) {
     case PageNode::LifecycleState::kRunning: {
       if (currently_visible) {
@@ -125,17 +125,17 @@
   }
 }
 
-void PageTimelineMonitor::CollectPageResourceUsage(
+void PageResourceMonitor::CollectPageResourceUsage(
     base::OnceClosure done_closure) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CalculatePageCPUUsage(
       /*use_delayed_system_cpu_probe=*/false,
-      base::BindOnce(&PageTimelineMonitor::OnPageResourceUsageResult,
+      base::BindOnce(&PageResourceMonitor::OnPageResourceUsageResult,
                      weak_factory_.GetWeakPtr())
           .Then(std::move(done_closure)));
 }
 
-void PageTimelineMonitor::OnPageResourceUsageResult(
+void PageResourceMonitor::OnPageResourceUsageResult(
     const PageCPUUsageVector& page_cpu_usage,
     absl::optional<PressureSample> system_cpu) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -194,7 +194,7 @@
           log_cpu_on_delay_timer_.Start(
               FROM_HERE,
               performance_manager::features::kDelayBeforeLogging.Get(), this,
-              &PageTimelineMonitor::CheckDelayedCPUInterventionMetrics);
+              &PageResourceMonitor::CheckDelayedCPUInterventionMetrics);
         }
       }
     } else if (!is_cpu_over_threshold) {
@@ -211,7 +211,7 @@
 #endif
 }
 
-void PageTimelineMonitor::CollectSlice() {
+void PageResourceMonitor::CollectSlice() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // We only collect a slice randomly every ~20 times this gets called for
   // privacy purposes. Always fall through when we're in a test.
@@ -309,7 +309,7 @@
   }
 }
 
-bool PageTimelineMonitor::ShouldCollectSlice() const {
+bool PageResourceMonitor::ShouldCollectSlice() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (should_collect_slice_callback_) {
     return should_collect_slice_callback_.Run();
@@ -319,17 +319,17 @@
   return base::RandInt(0, 19) == 1;
 }
 
-void PageTimelineMonitor::CheckDelayedCPUInterventionMetrics() {
+void PageResourceMonitor::CheckDelayedCPUInterventionMetrics() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK(performance_manager::features::kUseResourceAttributionCPUMonitor.Get());
   CalculatePageCPUUsage(
       /*use_delayed_system_cpu_probe=*/true,
       base::BindOnce(
-          &PageTimelineMonitor::OnDelayedCPUInterventionMetricsResult,
+          &PageResourceMonitor::OnDelayedCPUInterventionMetricsResult,
           weak_factory_.GetWeakPtr()));
 }
 
-void PageTimelineMonitor::OnDelayedCPUInterventionMetricsResult(
+void PageResourceMonitor::OnDelayedCPUInterventionMetricsResult(
     const PageCPUUsageVector& page_cpu_usage,
     absl::optional<PressureSample> system_cpu) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -351,7 +351,7 @@
   }
 }
 
-void PageTimelineMonitor::LogCPUInterventionMetrics(
+void PageResourceMonitor::LogCPUInterventionMetrics(
     const PageCPUUsageVector& page_cpu_usage,
     const absl::optional<PressureSample>& system_cpu,
     const base::TimeTicks now,
@@ -520,21 +520,21 @@
   }
 }
 
-void PageTimelineMonitor::CalculatePageCPUUsage(
+void PageResourceMonitor::CalculatePageCPUUsage(
     bool use_delayed_system_cpu_probe,
     base::OnceCallback<void(const PageCPUUsageVector&,
                             absl::optional<PressureSample>)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   cpu_monitor_.UpdateCPUMeasurements(base::BindOnce(
-      &PageTimelineMonitor::OnPageCPUUsageResult, weak_factory_.GetWeakPtr(),
+      &PageResourceMonitor::OnPageCPUUsageResult, weak_factory_.GetWeakPtr(),
       use_delayed_system_cpu_probe, std::move(callback)));
 }
 
-void PageTimelineMonitor::OnPageCPUUsageResult(
+void PageResourceMonitor::OnPageCPUUsageResult(
     bool use_delayed_system_cpu_probe,
     base::OnceCallback<void(const PageCPUUsageVector&,
                             absl::optional<PressureSample>)> callback,
-    const PageTimelineCPUMonitor::CPUUsageMap& cpu_usage_map) {
+    const PageResourceCPUMonitor::CPUUsageMap& cpu_usage_map) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // Calculate the overall CPU usage.
@@ -544,7 +544,7 @@
     const PageNode* page_node = tab_handle->page_node();
     CheckPageState(page_node, *info_ptr);
     double cpu_usage =
-        PageTimelineCPUMonitor::EstimatePageCPUUsage(page_node, cpu_usage_map);
+        PageResourceCPUMonitor::EstimatePageCPUUsage(page_node, cpu_usage_map);
     page_cpu_usage.emplace_back(page_node->GetResourceContext(), cpu_usage);
   }
 
@@ -560,22 +560,22 @@
   }
 }
 
-void PageTimelineMonitor::SetTriggerCollectionManuallyForTesting() {
+void PageResourceMonitor::SetTriggerCollectionManuallyForTesting() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   collect_slice_timer_.Stop();
   collect_page_resource_usage_timer_.Stop();
   log_cpu_on_delay_timer_.Stop();
 }
 
-void PageTimelineMonitor::SetShouldCollectSliceCallbackForTesting(
+void PageResourceMonitor::SetShouldCollectSliceCallbackForTesting(
     base::RepeatingCallback<bool()> should_collect_slice_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   should_collect_slice_callback_ = should_collect_slice_callback;
 }
 
-void PageTimelineMonitor::SetCPUMeasurementDelegateFactoryForTesting(
+void PageResourceMonitor::SetCPUMeasurementDelegateFactoryForTesting(
     Graph* graph,
-    PageTimelineCPUMonitor::CPUMeasurementDelegate::Factory* factory) {
+    PageResourceCPUMonitor::CPUMeasurementDelegate::Factory* factory) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Callback should be installed before `cpu_monitor_` starts
   // measuring the graph.
@@ -585,13 +585,13 @@
       graph, factory);
 }
 
-PageTimelineMonitor::PageNodeInfoMap&
-PageTimelineMonitor::GetPageNodeInfoForTesting() {
+PageResourceMonitor::PageNodeInfoMap&
+PageResourceMonitor::GetPageNodeInfoForTesting() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return page_node_info_map_;
 }
 
-void PageTimelineMonitor::OnPassedToGraph(Graph* graph) {
+void PageResourceMonitor::OnPassedToGraph(Graph* graph) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   graph_ = graph;
   graph_->AddPageNodeObserver(this);
@@ -600,7 +600,7 @@
   cpu_monitor_.StartMonitoring(graph_);
 }
 
-void PageTimelineMonitor::OnTakenFromGraph(Graph* graph) {
+void PageResourceMonitor::OnTakenFromGraph(Graph* graph) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   cpu_monitor_.StopMonitoring(graph_);
 
@@ -617,13 +617,13 @@
   graph_ = nullptr;
 }
 
-void PageTimelineMonitor::OnTabAdded(TabPageDecorator::TabHandle* tab_handle) {
+void PageResourceMonitor::OnTabAdded(TabPageDecorator::TabHandle* tab_handle) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   page_node_info_map_[tab_handle] = std::make_unique<PageNodeInfo>(
       base::TimeTicks::Now(), tab_handle->page_node(), slice_id_counter_++);
 }
 
-void PageTimelineMonitor::OnTabAboutToBeDiscarded(
+void PageResourceMonitor::OnTabAboutToBeDiscarded(
     const PageNode* old_page_node,
     TabPageDecorator::TabHandle* tab_handle) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -634,13 +634,13 @@
   CheckPageState(tab_handle->page_node(), *it->second);
 }
 
-void PageTimelineMonitor::OnBeforeTabRemoved(
+void PageResourceMonitor::OnBeforeTabRemoved(
     TabPageDecorator::TabHandle* tab_handle) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   page_node_info_map_.erase(tab_handle);
 }
 
-void PageTimelineMonitor::OnIsVisibleChanged(const PageNode* page_node) {
+void PageResourceMonitor::OnIsVisibleChanged(const PageNode* page_node) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (page_node->GetType() != performance_manager::PageType::kTab) {
     return;
@@ -655,7 +655,7 @@
   // 3. Tab is detached from the tabstrip, causing its web contents to become
   // "occluded", which triggers a visibility change notification
   // 4. The old web contents (and page node) are deleted
-  // In the case of PageTimelineMonitor, the page_node is removed from the map
+  // In the case of PageResourceMonitor, the page_node is removed from the map
   // on step 2, so the notification from step 3 has to be ignored.
   if (!tab_handle) {
     return;
@@ -683,7 +683,7 @@
   info->time_of_most_recent_state_change = now;
 }
 
-void PageTimelineMonitor::OnPageLifecycleStateChanged(
+void PageResourceMonitor::OnPageLifecycleStateChanged(
     const PageNode* page_node) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (page_node->GetType() != performance_manager::PageType::kTab) {
@@ -708,12 +708,12 @@
   it->second->time_of_most_recent_state_change = base::TimeTicks::Now();
 }
 
-void PageTimelineMonitor::SetBatterySaverEnabled(bool enabled) {
+void PageResourceMonitor::SetBatterySaverEnabled(bool enabled) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   battery_saver_enabled_ = enabled;
 }
 
-void PageTimelineMonitor::CheckPageState(const PageNode* page_node,
+void PageResourceMonitor::CheckPageState(const PageNode* page_node,
                                          const PageNodeInfo& info) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // There's a window after OnAboutToBeDiscarded() where a discarded placeholder
diff --git a/chrome/browser/performance_manager/metrics/page_timeline_monitor.h b/chrome/browser/performance_manager/metrics/page_resource_monitor.h
similarity index 87%
rename from chrome/browser/performance_manager/metrics/page_timeline_monitor.h
rename to chrome/browser/performance_manager/metrics/page_resource_monitor.h
index 8ecc9d07..f6e2a00 100644
--- a/chrome/browser/performance_manager/metrics/page_timeline_monitor.h
+++ b/chrome/browser/performance_manager/metrics/page_resource_monitor.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_METRICS_PAGE_TIMELINE_MONITOR_H_
-#define CHROME_BROWSER_PERFORMANCE_MANAGER_METRICS_PAGE_TIMELINE_MONITOR_H_
+#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_METRICS_PAGE_RESOURCE_MONITOR_H_
+#define CHROME_BROWSER_PERFORMANCE_MANAGER_METRICS_PAGE_RESOURCE_MONITOR_H_
 
 #include <map>
 #include <memory>
@@ -18,7 +18,7 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/performance_manager/metrics/cpu_probe/pressure_sample.h"
-#include "chrome/browser/performance_manager/metrics/page_timeline_cpu_monitor.h"
+#include "chrome/browser/performance_manager/metrics/page_resource_cpu_monitor.h"
 #include "components/performance_manager/public/decorators/tab_page_decorator.h"
 #include "components/performance_manager/public/graph/graph.h"
 #include "components/performance_manager/public/graph/graph_registered.h"
@@ -29,13 +29,13 @@
 namespace performance_manager::metrics {
 
 class CpuProbe;
-class PageTimelineMonitorUnitTest;
+class PageResourceMonitorUnitTest;
 
 // Periodically reports tab state via UKM, to enable analysis of usage patterns
 // over time.
-class PageTimelineMonitor : public PageNode::ObserverDefaultImpl,
+class PageResourceMonitor : public PageNode::ObserverDefaultImpl,
                             public GraphOwned,
-                            public GraphRegisteredImpl<PageTimelineMonitor>,
+                            public GraphRegisteredImpl<PageResourceMonitor>,
                             public TabPageObserver {
  public:
   // These values are logged to UKM. Entries should not be renumbered and
@@ -65,11 +65,11 @@
 
   // If `enable_system_cpu_probe` is false, `system_cpu_probe_` will be left
   // null. This is mainly intended for tests.
-  explicit PageTimelineMonitor(bool enable_system_cpu_probe = true);
+  explicit PageResourceMonitor(bool enable_system_cpu_probe = true);
 
-  ~PageTimelineMonitor() override;
-  PageTimelineMonitor(const PageTimelineMonitor& other) = delete;
-  PageTimelineMonitor& operator=(const PageTimelineMonitor&) = delete;
+  ~PageResourceMonitor() override;
+  PageResourceMonitor(const PageResourceMonitor& other) = delete;
+  PageResourceMonitor& operator=(const PageResourceMonitor&) = delete;
 
   // GraphOwned:
   void OnPassedToGraph(Graph* graph) override;
@@ -89,18 +89,18 @@
   void SetBatterySaverEnabled(bool enabled);
 
  private:
-  friend class PageTimelineMonitorBrowserTest;
-  friend PageTimelineMonitorUnitTest;
+  friend class PageResourceMonitorBrowserTest;
+  friend PageResourceMonitorUnitTest;
   FRIEND_TEST_ALL_PREFIXES(
-      PageTimelineMonitorUnitTest,
+      PageResourceMonitorUnitTest,
       TestPageTimelineDoesntRecordIfShouldCollectSliceReturnsFalse);
-  FRIEND_TEST_ALL_PREFIXES(PageTimelineMonitorUnitTest,
+  FRIEND_TEST_ALL_PREFIXES(PageResourceMonitorUnitTest,
                            TestUpdateFaviconInBackground);
-  FRIEND_TEST_ALL_PREFIXES(PageTimelineMonitorUnitTest,
+  FRIEND_TEST_ALL_PREFIXES(PageResourceMonitorUnitTest,
                            TestUpdateTitleInBackground);
-  FRIEND_TEST_ALL_PREFIXES(PageTimelineMonitorUnitTest,
+  FRIEND_TEST_ALL_PREFIXES(PageResourceMonitorUnitTest,
                            TestUpdateLifecycleState);
-  FRIEND_TEST_ALL_PREFIXES(PageTimelineMonitorUnitTest,
+  FRIEND_TEST_ALL_PREFIXES(PageResourceMonitorUnitTest,
                            TestUpdatePageNodeBeforeTypeChange);
 
   struct PageNodeInfo {
@@ -112,7 +112,7 @@
     int total_foreground_milliseconds{0};
     int tab_id;
 
-    PageTimelineMonitor::PageState GetPageState();
+    PageResourceMonitor::PageState GetPageState();
 
     explicit PageNodeInfo(base::TimeTicks time_of_creation,
                           const PageNode* page_node,
@@ -194,7 +194,7 @@
       bool use_delayed_system_cpu_probe,
       base::OnceCallback<void(const PageCPUUsageVector&,
                               absl::optional<PressureSample>)> callback,
-      const PageTimelineCPUMonitor::CPUUsageMap& cpu_usage_map);
+      const PageResourceCPUMonitor::CPUUsageMap& cpu_usage_map);
 
   // If this is called, CollectSlice() and CollectPageResourceUsage() will not
   // be called on a timer. Tests can call them manually.
@@ -204,10 +204,10 @@
   // ShouldCollectSlice().
   void SetShouldCollectSliceCallbackForTesting(base::RepeatingCallback<bool()>);
 
-  // Passes the given `factory` to PageTimelineCPUMonitor.
+  // Passes the given `factory` to PageResourceCPUMonitor.
   void SetCPUMeasurementDelegateFactoryForTesting(
       Graph* graph,
-      PageTimelineCPUMonitor::CPUMeasurementDelegate::Factory* factory);
+      PageResourceCPUMonitor::CPUMeasurementDelegate::Factory* factory);
 
   // Lets tests examine the contents of `page_node_info_map_`.
   PageNodeInfoMap& GetPageNodeInfoForTesting();
@@ -260,7 +260,7 @@
   bool battery_saver_enabled_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
 
   // Helper to take CPU measurements for the UKM.
-  PageTimelineCPUMonitor cpu_monitor_ GUARDED_BY_CONTEXT(sequence_checker_);
+  PageResourceCPUMonitor cpu_monitor_ GUARDED_BY_CONTEXT(sequence_checker_);
 
   // Helpers to take system CPU measurements for UMA.
   std::unique_ptr<CpuProbe> system_cpu_probe_
@@ -269,9 +269,9 @@
       GUARDED_BY_CONTEXT(sequence_checker_);
 
   // WeakPtrFactory for the RepeatingTimer to call a method on this object.
-  base::WeakPtrFactory<PageTimelineMonitor> weak_factory_{this};
+  base::WeakPtrFactory<PageResourceMonitor> weak_factory_{this};
 };
 
 }  // namespace performance_manager::metrics
 
-#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_METRICS_PAGE_TIMELINE_MONITOR_H_
+#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_METRICS_PAGE_RESOURCE_MONITOR_H_
diff --git a/chrome/browser/performance_manager/metrics/page_timeline_monitor_browsertest.cc b/chrome/browser/performance_manager/metrics/page_resource_monitor_browsertest.cc
similarity index 94%
rename from chrome/browser/performance_manager/metrics/page_timeline_monitor_browsertest.cc
rename to chrome/browser/performance_manager/metrics/page_resource_monitor_browsertest.cc
index 4002d8e8..1165643 100644
--- a/chrome/browser/performance_manager/metrics/page_timeline_monitor_browsertest.cc
+++ b/chrome/browser/performance_manager/metrics/page_resource_monitor_browsertest.cc
@@ -6,7 +6,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/performance_manager/mechanisms/page_discarder.h"
-#include "chrome/browser/performance_manager/metrics/page_timeline_monitor.h"
+#include "chrome/browser/performance_manager/metrics/page_resource_monitor.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -23,9 +23,9 @@
 
 namespace performance_manager::metrics {
 
-class PageTimelineMonitorBrowserTest : public InProcessBrowserTest {
+class PageResourceMonitorBrowserTest : public InProcessBrowserTest {
  public:
-  PageTimelineMonitorBrowserTest() {
+  PageResourceMonitorBrowserTest() {
     feature_list_.InitAndEnableFeature(features::kPageTimelineMonitor);
   }
 
@@ -34,7 +34,7 @@
   void SetUpOnPMSequence() {
     PerformanceManager::CallOnGraph(
         FROM_HERE, base::BindOnce([](Graph* graph) {
-          auto* monitor = graph->GetRegisteredObjectAs<PageTimelineMonitor>();
+          auto* monitor = graph->GetRegisteredObjectAs<PageResourceMonitor>();
           monitor->SetTriggerCollectionManuallyForTesting();
           monitor->SetShouldCollectSliceCallbackForTesting(
               base::BindRepeating([]() { return true; }));
@@ -58,7 +58,7 @@
               ukm::TestAutoSetUkmRecorder ukm_recorder;
 
               auto* monitor =
-                  graph->GetRegisteredObjectAs<PageTimelineMonitor>();
+                  graph->GetRegisteredObjectAs<PageResourceMonitor>();
               monitor->CollectSlice();
 
               auto entries = ukm_recorder.GetEntriesByName(
@@ -91,7 +91,7 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(PageTimelineMonitorBrowserTest,
+IN_PROC_BROWSER_TEST_F(PageResourceMonitorBrowserTest,
                        TestDiscardedTabsRecordedInCorrectState) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -141,7 +141,7 @@
   EXPECT_EQ(tab_id, CollectSliceOnPMSequence(2UL, /*discarded*/ 5));
 }
 
-IN_PROC_BROWSER_TEST_F(PageTimelineMonitorBrowserTest,
+IN_PROC_BROWSER_TEST_F(PageResourceMonitorBrowserTest,
                        TestFrozenToDiscardedTab) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
diff --git a/chrome/browser/performance_manager/metrics/page_timeline_monitor_unittest.cc b/chrome/browser/performance_manager/metrics/page_resource_monitor_unittest.cc
similarity index 94%
rename from chrome/browser/performance_manager/metrics/page_timeline_monitor_unittest.cc
rename to chrome/browser/performance_manager/metrics/page_resource_monitor_unittest.cc
index 5065f15..0deb9d664 100644
--- a/chrome/browser/performance_manager/metrics/page_timeline_monitor_unittest.cc
+++ b/chrome/browser/performance_manager/metrics/page_resource_monitor_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/performance_manager/metrics/page_timeline_monitor.h"
+#include "chrome/browser/performance_manager/metrics/page_resource_monitor.h"
 
 #include <map>
 #include <memory>
@@ -25,7 +25,7 @@
 #include "base/test/test_timeouts.h"
 #include "base/threading/platform_thread.h"
 #include "build/build_config.h"
-#include "chrome/browser/performance_manager/metrics/page_timeline_cpu_monitor.h"
+#include "chrome/browser/performance_manager/metrics/page_resource_cpu_monitor.h"
 #include "components/performance_manager/embedder/graph_features.h"
 #include "components/performance_manager/public/decorators/page_live_state_decorator.h"
 #include "components/performance_manager/public/decorators/tab_page_decorator.h"
@@ -51,7 +51,7 @@
 namespace {
 
 using PageMeasurementBackgroundState =
-    PageTimelineMonitor::PageMeasurementBackgroundState;
+    PageResourceMonitor::PageMeasurementBackgroundState;
 
 // Helper class to repeatedly test a HistogramTester for histograms with a
 // common naming pattern. The default pattern is
@@ -172,13 +172,13 @@
 
 }  // namespace
 
-class PageTimelineMonitorUnitTest : public GraphTestHarness {
+class PageResourceMonitorUnitTest : public GraphTestHarness {
  public:
-  PageTimelineMonitorUnitTest() = default;
-  ~PageTimelineMonitorUnitTest() override = default;
-  PageTimelineMonitorUnitTest(const PageTimelineMonitorUnitTest& other) =
+  PageResourceMonitorUnitTest() = default;
+  ~PageResourceMonitorUnitTest() override = default;
+  PageResourceMonitorUnitTest(const PageResourceMonitorUnitTest& other) =
       delete;
-  PageTimelineMonitorUnitTest& operator=(const PageTimelineMonitorUnitTest&) =
+  PageResourceMonitorUnitTest& operator=(const PageResourceMonitorUnitTest&) =
       delete;
 
   void SetUp() override {
@@ -190,8 +190,8 @@
     // Return 50% CPU used by default.
     cpu_delegate_factory_.SetDefaultCPUUsage(0.5);
 
-    std::unique_ptr<PageTimelineMonitor> monitor =
-        std::make_unique<PageTimelineMonitor>(enable_system_cpu_probe_);
+    std::unique_ptr<PageResourceMonitor> monitor =
+        std::make_unique<PageResourceMonitor>(enable_system_cpu_probe_);
     monitor_ = monitor.get();
     monitor_->SetTriggerCollectionManuallyForTesting();
     monitor_->SetShouldCollectSliceCallbackForTesting(
@@ -208,7 +208,7 @@
   }
 
   // To allow tests to call its methods and view its state.
-  raw_ptr<PageTimelineMonitor> monitor_;
+  raw_ptr<PageResourceMonitor> monitor_;
 
   // Factory to return CPUMeasurementDelegates. This must be deleted after
   // `monitor_` to ensure that it outlives all delegates it creates.
@@ -217,7 +217,7 @@
 
  protected:
   ukm::TestUkmRecorder* test_ukm_recorder() { return test_ukm_recorder_.get(); }
-  PageTimelineMonitor* monitor() { return monitor_; }
+  PageResourceMonitor* monitor() { return monitor_; }
 
   void TriggerCollectSlice() { monitor_->CollectSlice(); }
 
@@ -248,7 +248,7 @@
       std::map<ukm::SourceId, PageMeasurementBackgroundState> expected_states);
 
   // Subclasses can override this before calling
-  // PageTimelineMonitorUnitTest::SetUp() to simulate an environment where
+  // PageResourceMonitorUnitTest::SetUp() to simulate an environment where
   // CPUProbe::Create() returns nullptr.
   bool enable_system_cpu_probe_ = true;
 
@@ -256,7 +256,7 @@
   std::unique_ptr<ukm::TestUkmRecorder> test_ukm_recorder_;
 };
 
-void PageTimelineMonitorUnitTest::TestBackgroundStates(
+void PageResourceMonitorUnitTest::TestBackgroundStates(
     std::map<ukm::SourceId, PageMeasurementBackgroundState> expected_states) {
   TriggerCollectPageResourceUsage();
   auto entries = test_ukm_recorder()->GetEntriesByName(
@@ -273,11 +273,11 @@
 
 // A test that runs with various values of the kUseResourceAttributionCPUMonitor
 // feature flag.
-class PageTimelineMonitorWithFeatureTest
-    : public PageTimelineMonitorUnitTest,
+class PageResourceMonitorWithFeatureTest
+    : public PageResourceMonitorUnitTest,
       public ::testing::WithParamInterface<bool> {
  public:
-  PageTimelineMonitorWithFeatureTest() {
+  PageResourceMonitorWithFeatureTest() {
     scoped_feature_list_.InitWithFeaturesAndParameters(
         {
             {features::kPageTimelineMonitor,
@@ -293,14 +293,14 @@
     if (features::kUseResourceAttributionCPUMonitor.Get()) {
       GetGraphFeatures().EnableResourceAttributionScheduler();
     }
-    PageTimelineMonitorUnitTest::SetUp();
+    PageResourceMonitorUnitTest::SetUp();
   }
 
   void TearDown() override {
     // Destroy `monitor_` before `scoped_feature_list_` so that the feature flag
     // doesn't change during its destructor.
     graph()->TakeFromGraph(monitor_.ExtractAsDangling());
-    PageTimelineMonitorUnitTest::TearDown();
+    PageResourceMonitorUnitTest::TearDown();
   }
 
  private:
@@ -308,13 +308,13 @@
 };
 
 INSTANTIATE_TEST_SUITE_P(All,
-                         PageTimelineMonitorWithFeatureTest,
+                         PageResourceMonitorWithFeatureTest,
                          ::testing::Bool());
 
 // A test of CPU intervention logging when the system CPUProbe is not available.
-class PageTimelineMonitorNoCPUProbeTest : public PageTimelineMonitorUnitTest {
+class PageResourceMonitorNoCPUProbeTest : public PageResourceMonitorUnitTest {
  public:
-  PageTimelineMonitorNoCPUProbeTest() {
+  PageResourceMonitorNoCPUProbeTest() {
     scoped_feature_list_.InitWithFeaturesAndParameters(
         {
             {performance_manager::features::kCPUInterventionEvaluationLogging,
@@ -328,7 +328,7 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-TEST_F(PageTimelineMonitorUnitTest, TestPageTimeline) {
+TEST_F(PageResourceMonitorUnitTest, TestPageTimeline) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -349,7 +349,7 @@
   EXPECT_TRUE(entries2.empty());
 }
 
-TEST_F(PageTimelineMonitorUnitTest, TestPageResourceUsage) {
+TEST_F(PageResourceMonitorUnitTest, TestPageResourceUsage) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -370,7 +370,7 @@
   EXPECT_TRUE(entries2.empty());
 }
 
-TEST_F(PageTimelineMonitorUnitTest,
+TEST_F(PageResourceMonitorUnitTest,
        TestPageTimelineDoesntRecordIfShouldCollectSliceReturnsFalse) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
@@ -388,7 +388,7 @@
   EXPECT_EQ(entries.size(), 0UL);
 }
 
-TEST_F(PageTimelineMonitorUnitTest, TestPageTimelineNavigation) {
+TEST_F(PageResourceMonitorUnitTest, TestPageTimelineNavigation) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id =
       ukm::AssignNewSourceId();  // ukm::NoURLSourceId();
@@ -429,7 +429,7 @@
   EXPECT_NE(ids[0], ids[1]);
 }
 
-TEST_F(PageTimelineMonitorUnitTest, TestOnlyRecordTabs) {
+TEST_F(PageResourceMonitorUnitTest, TestOnlyRecordTabs) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
   mock_graph.page->SetUkmSourceId(mock_source_id);
@@ -447,7 +447,7 @@
   EXPECT_EQ(entries2.size(), 0UL);
 }
 
-TEST_F(PageTimelineMonitorUnitTest, TestUpdateTitleOrFaviconInBackground) {
+TEST_F(PageResourceMonitorUnitTest, TestUpdateTitleOrFaviconInBackground) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -473,7 +473,7 @@
       entries[1], "ChangedFaviconOrTitleInBackground", true);
 }
 
-TEST_F(PageTimelineMonitorUnitTest, TestUpdateLifecycleState) {
+TEST_F(PageResourceMonitorUnitTest, TestUpdateLifecycleState) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
   mock_graph.page->SetUkmSourceId(mock_source_id);
@@ -489,7 +489,7 @@
 }
 
 #if !BUILDFLAG(IS_ANDROID)
-TEST_F(PageTimelineMonitorUnitTest, TestHighEfficiencyMode) {
+TEST_F(PageResourceMonitorUnitTest, TestHighEfficiencyMode) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -526,7 +526,7 @@
   test_ukm_recorder()->ExpectEntryMetric(entries[2], "HighEfficiencyMode", 1);
 }
 
-TEST_F(PageTimelineMonitorUnitTest, TestBatterySaverMode) {
+TEST_F(PageResourceMonitorUnitTest, TestBatterySaverMode) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -551,7 +551,7 @@
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
-TEST_F(PageTimelineMonitorUnitTest, TestHasNotificationsPermission) {
+TEST_F(PageResourceMonitorUnitTest, TestHasNotificationsPermission) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -584,7 +584,7 @@
                                          "HasNotificationPermission", 0);
 }
 
-TEST_F(PageTimelineMonitorUnitTest, TestCapturingMedia) {
+TEST_F(PageResourceMonitorUnitTest, TestCapturingMedia) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -611,7 +611,7 @@
   test_ukm_recorder()->ExpectEntryMetric(entries[1], "IsCapturingMedia", 1);
 }
 
-TEST_F(PageTimelineMonitorUnitTest, TestConnectedToDevice) {
+TEST_F(PageResourceMonitorUnitTest, TestConnectedToDevice) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -638,7 +638,7 @@
   test_ukm_recorder()->ExpectEntryMetric(entries[1], "IsConnectedToDevice", 1);
 }
 
-TEST_F(PageTimelineMonitorUnitTest, TestAudible) {
+TEST_F(PageResourceMonitorUnitTest, TestAudible) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -661,7 +661,7 @@
   test_ukm_recorder()->ExpectEntryMetric(entries[1], "IsPlayingAudio", 1);
 }
 
-TEST_F(PageTimelineMonitorUnitTest, TestIsActiveTab) {
+TEST_F(PageResourceMonitorUnitTest, TestIsActiveTab) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -688,7 +688,7 @@
   test_ukm_recorder()->ExpectEntryMetric(entries[1], "IsActiveTab", 1);
 }
 
-TEST_F(PageTimelineMonitorUnitTest, TestMemory) {
+TEST_F(PageResourceMonitorUnitTest, TestMemory) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -706,7 +706,7 @@
   test_ukm_recorder()->ExpectEntryMetric(entries[0], "PrivateFootprint", 456);
 }
 
-TEST_F(PageTimelineMonitorUnitTest, TestUpdatePageNodeBeforeTypeChange) {
+TEST_F(PageResourceMonitorUnitTest, TestUpdatePageNodeBeforeTypeChange) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   ukm::SourceId mock_source_id = ukm::NoURLSourceId();
   mock_graph.page->SetIsVisible(false);
@@ -728,7 +728,7 @@
   TriggerCollectSlice();
 }
 
-TEST_P(PageTimelineMonitorWithFeatureTest, TestResourceUsage) {
+TEST_P(PageResourceMonitorWithFeatureTest, TestResourceUsage) {
   MockMultiplePagesWithMultipleProcessesGraph mock_graph(graph());
   const ukm::SourceId mock_source_id = ukm::AssignNewSourceId();
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -788,7 +788,7 @@
   }
 }
 
-TEST_F(PageTimelineMonitorUnitTest, TestResourceUsageBackgroundState) {
+TEST_F(PageResourceMonitorUnitTest, TestResourceUsageBackgroundState) {
   MockMultiplePagesWithMultipleProcessesGraph mock_graph(graph());
   const ukm::SourceId mock_source_id = ukm::AssignNewSourceId();
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -838,7 +838,7 @@
 }
 
 #if !BUILDFLAG(IS_ANDROID)
-TEST_P(PageTimelineMonitorWithFeatureTest, TestCPUInterventionMetrics) {
+TEST_P(PageResourceMonitorWithFeatureTest, TestCPUInterventionMetrics) {
   MockMultiplePagesWithMultipleProcessesGraph mock_graph(graph());
 
   // Foreground page.
@@ -1080,7 +1080,7 @@
   }
 }
 
-TEST_P(PageTimelineMonitorWithFeatureTest,
+TEST_P(PageResourceMonitorWithFeatureTest,
        CPUInterventionMetricsNoForegroundTabs) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -1116,7 +1116,7 @@
   immediate.ExpectUniqueSample("TopNBackgroundCPU.2", 100);
 }
 
-TEST_P(PageTimelineMonitorWithFeatureTest,
+TEST_P(PageResourceMonitorWithFeatureTest,
        CPUInterventionMetricsNoBackgroundTabs) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   mock_graph.page->SetType(performance_manager::PageType::kTab);
@@ -1154,7 +1154,7 @@
   immediate.ExpectUniqueSample("TopNBackgroundCPU.2", 0);
 }
 
-TEST_F(PageTimelineMonitorNoCPUProbeTest,
+TEST_F(PageResourceMonitorNoCPUProbeTest,
        CPUInterventionMetricsWithoutSystemCPU) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
   mock_graph.page->SetType(performance_manager::PageType::kTab);
diff --git a/chrome/browser/performance_manager/user_tuning/battery_saver_mode_manager.cc b/chrome/browser/performance_manager/user_tuning/battery_saver_mode_manager.cc
index befdb27..c5f5e08 100644
--- a/chrome/browser/performance_manager/user_tuning/battery_saver_mode_manager.cc
+++ b/chrome/browser/performance_manager/user_tuning/battery_saver_mode_manager.cc
@@ -16,7 +16,7 @@
 #include "base/power_monitor/power_observer.h"
 #include "base/run_loop.h"
 #include "base/values.h"
-#include "chrome/browser/performance_manager/metrics/page_timeline_monitor.h"
+#include "chrome/browser/performance_manager/metrics/page_resource_monitor.h"
 #include "components/performance_manager/public/features.h"
 #include "components/performance_manager/public/performance_manager.h"
 #include "components/performance_manager/public/user_tuning/prefs.h"
@@ -63,24 +63,24 @@
  public:
   void StartThrottlingAllFrameSinks() override {
     content::StartThrottlingAllFrameSinks(base::Hertz(30));
-    NotifyPageTimelineMonitor(/*battery_saver_mode_enabled=*/true);
+    NotifyPageResourceMonitor(/*battery_saver_mode_enabled=*/true);
   }
 
   void StopThrottlingAllFrameSinks() override {
     content::StopThrottlingAllFrameSinks();
-    NotifyPageTimelineMonitor(/*battery_saver_mode_enabled=*/false);
+    NotifyPageResourceMonitor(/*battery_saver_mode_enabled=*/false);
   }
 
   ~FrameThrottlingDelegateImpl() override = default;
 
  private:
-  void NotifyPageTimelineMonitor(bool battery_saver_mode_enabled) {
+  void NotifyPageResourceMonitor(bool battery_saver_mode_enabled) {
     performance_manager::PerformanceManager::CallOnGraph(
         FROM_HERE,
         base::BindOnce(
             [](bool enabled, performance_manager::Graph* graph) {
               auto* monitor = graph->GetRegisteredObjectAs<
-                  performance_manager::metrics::PageTimelineMonitor>();
+                  performance_manager::metrics::PageResourceMonitor>();
               // It's possible for this to be null if the PageTimeline finch
               // feature is disabled.
               if (monitor) {
diff --git a/chrome/browser/performance_manager/user_tuning/user_performance_tuning_manager.cc b/chrome/browser/performance_manager/user_tuning/user_performance_tuning_manager.cc
index b5595c2..8164246 100644
--- a/chrome/browser/performance_manager/user_tuning/user_performance_tuning_manager.cc
+++ b/chrome/browser/performance_manager/user_tuning/user_performance_tuning_manager.cc
@@ -14,7 +14,7 @@
 #include "base/notreached.h"
 #include "base/run_loop.h"
 #include "base/values.h"
-#include "chrome/browser/performance_manager/metrics/page_timeline_monitor.h"
+#include "chrome/browser/performance_manager/metrics/page_resource_monitor.h"
 #include "chrome/browser/performance_manager/policies/memory_saver_mode_policy.h"
 #include "chrome/browser/performance_manager/policies/page_discarding_helper.h"
 #include "chrome/browser/performance_manager/user_tuning/user_performance_tuning_notifier.h"
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 9d11d79..3bb3c80 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -165,7 +165,6 @@
 #include "components/services/screen_ai/buildflags/buildflags.h"
 #include "components/services/storage/public/cpp/storage_prefs.h"
 #include "components/sessions/core/session_id_generator.h"
-#include "components/signin/public/base/signin_buildflags.h"
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/site_engagement/content/site_engagement_service.h"
@@ -230,13 +229,6 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
-// `search_engine_choice_service.h` includes `chrome/browser/ui/browser_list.h`
-// that can't be included on android.
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-#include "chrome/browser/search_engine_choice/search_engine_choice_client_side_trial.h"
-#include "chrome/browser/search_engine_choice/search_engine_choice_service.h"
-#endif
-
 #if BUILDFLAG(ENABLE_PDF)
 #include "chrome/browser/pdf/pdf_pref_names.h"
 #endif  // BUILDFLAG(ENABLE_PDF)
@@ -292,6 +284,8 @@
 #include "chrome/browser/new_tab_page/promos/promo_service.h"
 #include "chrome/browser/policy/developer_tools_policy_handler.h"
 #include "chrome/browser/search/background/ntp_custom_background_service.h"
+#include "chrome/browser/search_engine_choice/search_engine_choice_client_side_trial.h"
+#include "chrome/browser/search_engine_choice/search_engine_choice_service.h"
 #include "chrome/browser/serial/serial_policy_allowed_ports.h"
 #include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/commerce/commerce_ui_tab_helper.h"
@@ -1479,6 +1473,8 @@
   metrics::TabStatsTracker::RegisterPrefs(registry);
   performance_manager::user_tuning::prefs::RegisterLocalStatePrefs(registry);
   RegisterBrowserPrefs(registry);
+  SearchEngineChoiceService::RegisterLocalStatePrefs(registry);
+  SearchEngineChoiceClientSideTrial::RegisterLocalStatePrefs(registry);
   speech::SodaInstaller::RegisterLocalStatePrefs(registry);
   StartupBrowserCreator::RegisterLocalStatePrefs(registry);
   task_manager::TaskManagerInterface::RegisterPrefs(registry);
@@ -1625,11 +1621,6 @@
   PlatformAuthPolicyObserver::RegisterPrefs(registry);
 #endif  // BUILDFLAG(IS_WIN)
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-  SearchEngineChoiceService::RegisterLocalStatePrefs(registry);
-  SearchEngineChoiceClientSideTrial::RegisterLocalStatePrefs(registry);
-#endif
-
   // Platform-specific and compile-time conditional individual preferences.
   // If you have multiple preferences that should clearly be grouped together,
   // please group them together into a helper function called above. Please
diff --git a/chrome/browser/printing/print_job.cc b/chrome/browser/printing/print_job.cc
index 57c9c44..c60e208 100644
--- a/chrome/browser/printing/print_job.cc
+++ b/chrome/browser/printing/print_job.cc
@@ -10,6 +10,7 @@
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/location.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/observer_list.h"
 #include "base/run_loop.h"
 #include "base/task/thread_pool.h"
@@ -27,12 +28,14 @@
 
 #if BUILDFLAG(IS_WIN)
 #include "base/command_line.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/pdf/pdf_pref_names.h"
 #include "chrome/browser/printing/pdf_to_emf_converter.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "printing/backend/win_helper.h"
 #include "printing/page_number.h"
 #include "printing/pdf_render_settings.h"
 #include "printing/printed_page_win.h"
@@ -218,6 +221,20 @@
     return;
   }
 
+#if BUILDFLAG(IS_WIN)
+  // Do not collect duration metric if the print job will need to invoke a
+  // "Save Print Output As" dialog that waits on a user to select a filename.
+  const std::string printer_name =
+      base::UTF16ToUTF8(document_->settings().device_name());
+  const bool capture_printing_time =
+      !DoesDriverDisplayFileDialogForPrinting(printer_name);
+#else
+  constexpr bool capture_printing_time = true;
+#endif
+  if (capture_printing_time) {
+    printing_start_time_ = base::TimeTicks::Now();
+  }
+
   // Real work is done in `PrintJobWorker::StartPrinting()`.
   worker_->PostTask(
       FROM_HERE, base::BindOnce(&HoldRefCallback, base::WrapRefCounted(this),
@@ -552,6 +569,13 @@
 void PrintJob::OnDocDone(int job_id, PrintedDocument* document) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
+  if (printing_start_time_.has_value()) {
+    base::UmaHistogramMediumTimes(
+        "Printing.PrintDuration.Success",
+        base::TimeTicks::Now() - printing_start_time_.value());
+    printing_start_time_.reset();
+  }
+
   print_job_manager_->OnDocDone(this, document, job_id);
 
   for (auto& observer : observers_) {
diff --git a/chrome/browser/printing/print_job.h b/chrome/browser/printing/print_job.h
index 83caebf..1a7d416 100644
--- a/chrome/browser/printing/print_job.h
+++ b/chrome/browser/printing/print_job.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_PRINTING_PRINT_JOB_H_
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/functional/callback.h"
@@ -13,6 +14,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "content/public/browser/global_routing_id.h"
@@ -268,6 +270,9 @@
   // The printed document.
   scoped_refptr<PrintedDocument> document_;
 
+  // Time at start of printing.  Used for metrics.
+  std::optional<base::TimeTicks> printing_start_time_;
+
   // Is the worker thread printing.
   bool is_job_pending_ = false;
 
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc
index 333c0861..ee11e882 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc
@@ -1025,12 +1025,9 @@
 
   // Simulate 3PC are allowed while:
   // - FPS pref is enabled
-  // - FPS backend Feature is enabled
   // - FPS UI Feature is enabled
   feature_list()->InitWithFeatures(
-      {privacy_sandbox::kPrivacySandboxFirstPartySetsUI,
-       features::kFirstPartySets},
-      {});
+      {privacy_sandbox::kPrivacySandboxFirstPartySetsUI}, {});
   CreateService();
   ClearFpsUserPrefs(prefs());
   prefs()->SetUserPref(prefs::kCookieControlsMode,
@@ -1065,12 +1062,9 @@
 
   // Simulate all cookies are blocked while:
   // - FPS pref is enabled
-  // - FPS backend Feature is enabled
   // - FPS UI Feature is enabled
   feature_list()->InitWithFeatures(
-      {privacy_sandbox::kPrivacySandboxFirstPartySetsUI,
-       features::kFirstPartySets},
-      {});
+      {privacy_sandbox::kPrivacySandboxFirstPartySetsUI}, {});
   prefs()->SetUserPref(
       prefs::kCookieControlsMode,
       std::make_unique<base::Value>(static_cast<int>(
@@ -1107,11 +1101,9 @@
 
   // Simulate FPS UI feature disabled while:
   // - FPS pref is enabled
-  // - FPS backend Feature is enabled
   // - 3PC are being blocked
   feature_list()->InitWithFeatures(
-      {features::kFirstPartySets},
-      {privacy_sandbox::kPrivacySandboxFirstPartySetsUI});
+      {}, {privacy_sandbox::kPrivacySandboxFirstPartySetsUI});
   privacy_sandbox_test_util::SetupTestState(
       prefs(), host_content_settings_map(),
       /*privacy_sandbox_enabled=*/true,
@@ -1135,50 +1127,6 @@
 }
 
 TEST_F(PrivacySandboxServiceTest,
-       GetFirstPartySetOwner_SimulatedFpsData_DisabledByFpsFeature) {
-  GURL associate1_gurl("https://associate1.test");
-  net::SchemefulSite primary_site(GURL("https://primary.test"));
-  net::SchemefulSite associate1_site(associate1_gurl);
-
-  // Create Global First-Party Sets with the following set:
-  // { primary: "https://primary.test",
-  // associatedSites: ["https://associate1.test"}
-  net::GlobalFirstPartySets global_sets(
-      kFirstPartySetsVersion,
-      {{associate1_site,
-        {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated,
-                                 0)}}},
-      {});
-
-  // Simulate FPS backend feature disabled while:
-  // - FPS pref is enabled
-  // - FPS UI Feature is enabled
-  // - 3PC are being blocked
-  feature_list()->InitWithFeatures(
-      {privacy_sandbox::kPrivacySandboxFirstPartySetsUI},
-      {features::kFirstPartySets});
-  privacy_sandbox_test_util::SetupTestState(
-      prefs(), host_content_settings_map(),
-      /*privacy_sandbox_enabled=*/true,
-      /*block_third_party_cookies=*/true,
-      /*default_cookie_setting=*/ContentSetting::CONTENT_SETTING_ALLOW,
-      /*user_cookie_exceptions=*/{},
-      /*managed_cookie_setting=*/privacy_sandbox_test_util::kNoSetting,
-      /*managed_cookie_exceptions=*/{});
-  CreateService();
-  ClearFpsUserPrefs(prefs());
-  prefs()->SetUserPref(prefs::kPrivacySandboxRelatedWebsiteSetsEnabled,
-                       std::make_unique<base::Value>(true));
-
-  mock_first_party_sets_handler().SetGlobalSets(global_sets.Clone());
-  first_party_sets_policy_service()->InitForTesting();
-
-  // We shouldn't get associate1's owner since FPS is disabled.
-  EXPECT_EQ(privacy_sandbox_service()->GetFirstPartySetOwner(associate1_gurl),
-            absl::nullopt);
-}
-
-TEST_F(PrivacySandboxServiceTest,
        GetFirstPartySetOwner_SimulatedFpsData_DisabledByFpsPref) {
   GURL associate1_gurl("https://associate1.test");
   net::SchemefulSite primary_site(GURL("https://primary.test"));
@@ -1196,12 +1144,9 @@
 
   // Simulate FPS pref disabled while:
   // - FPS UI Feature is enabled
-  // - FPS backend Feature is enabled
   // - 3PC are being blocked
   feature_list()->InitWithFeatures(
-      {features::kFirstPartySets,
-       privacy_sandbox::kPrivacySandboxFirstPartySetsUI},
-      {});
+      {privacy_sandbox::kPrivacySandboxFirstPartySetsUI}, {});
   privacy_sandbox_test_util::SetupTestState(
       prefs(), host_content_settings_map(),
       /*privacy_sandbox_enabled=*/true,
@@ -1234,11 +1179,9 @@
   net::SchemefulSite associate2_site(associate2_gurl);
 
   // Set up state that fully enables the First-Party Sets for UI; blocking 3PC,
-  // and enabling the FPS UI and backend features and the FPS enabled pref.
+  // and enabling the FPS UI feature and the FPS enabled pref.
   feature_list()->InitWithFeatures(
-      {features::kFirstPartySets,
-       privacy_sandbox::kPrivacySandboxFirstPartySetsUI},
-      {});
+      {privacy_sandbox::kPrivacySandboxFirstPartySetsUI}, {});
   privacy_sandbox_test_util::SetupTestState(
       prefs(), host_content_settings_map(),
       /*privacy_sandbox_enabled=*/true,
@@ -1270,11 +1213,9 @@
   net::SchemefulSite associate2_site(associate2_gurl);
 
   // Set up state that fully enables the First-Party Sets for UI; blocking 3PC,
-  // and enabling the FPS UI and backend features and the FPS enabled pref.
+  // and enabling the FPS UI feature and the FPS enabled pref.
   feature_list()->InitWithFeatures(
-      {features::kFirstPartySets,
-       privacy_sandbox::kPrivacySandboxFirstPartySetsUI},
-      {});
+      {privacy_sandbox::kPrivacySandboxFirstPartySetsUI}, {});
   privacy_sandbox_test_util::SetupTestState(
       prefs(), host_content_settings_map(),
       /*privacy_sandbox_enabled=*/true,
@@ -1390,12 +1331,11 @@
   // First-Party Sets queries instead of the actual sets.
 
   // Set up state that fully enables the First-Party Sets for UI; blocking
-  // 3PC, and enabling the FPS UI and backend features and the FPS enabled pref.
+  // 3PC, and enabling the FPS UI feature and the FPS enabled pref.
   //
   // Note: this indicates that the sample sets should be used.
   feature_list()->InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kFirstPartySets, {}},
-                            {privacy_sandbox::kPrivacySandboxFirstPartySetsUI,
+      /*enabled_features=*/{{privacy_sandbox::kPrivacySandboxFirstPartySetsUI,
                              {{"use-sample-sets", "true"}}}},
       /*disabled_features=*/{});
   privacy_sandbox_test_util::SetupTestState(
@@ -1450,9 +1390,7 @@
 
   feature_list()->Reset();
   feature_list()->InitWithFeatures(
-      {features::kFirstPartySets,
-       privacy_sandbox::kPrivacySandboxFirstPartySetsUI},
-      {});
+      {privacy_sandbox::kPrivacySandboxFirstPartySetsUI}, {});
   privacy_sandbox_test_util::SetupTestState(
       prefs(), host_content_settings_map(),
       /*privacy_sandbox_enabled=*/true,
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 3ad2f42..5bf1035 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -149,7 +149,6 @@
 #include "chrome/browser/safe_browsing/tailored_security/tailored_security_service_factory.h"
 #include "chrome/browser/safe_browsing/url_lookup_service_factory.h"
 #include "chrome/browser/safe_browsing/verdict_cache_manager_factory.h"
-#include "chrome/browser/search_engine_choice/search_engine_choice_service_factory.h"
 #include "chrome/browser/search_engines/template_url_fetcher_factory.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/search_provider_logos/logo_service_factory.h"
@@ -226,7 +225,6 @@
 #include "components/reading_list/features/reading_list_switches.h"
 #include "components/safe_browsing/buildflags.h"
 #include "components/services/screen_ai/buildflags/buildflags.h"
-#include "components/signin/public/base/signin_buildflags.h"
 #include "components/signin/public/base/signin_switches.h"
 #include "components/site_engagement/content/site_engagement_service.h"
 #include "components/spellcheck/spellcheck_buildflags.h"
@@ -272,6 +270,7 @@
 #include "chrome/browser/profile_resetter/reset_report_uploader_factory.h"
 #include "chrome/browser/profiles/profile_theme_update_service_factory.h"
 #include "chrome/browser/search/instant_service_factory.h"
+#include "chrome/browser/search_engine_choice/search_engine_choice_service_factory.h"
 #include "chrome/browser/storage/storage_notification_service_factory.h"
 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
 #include "chrome/browser/ui/media_router/media_router_ui_service_factory.h"
@@ -296,6 +295,7 @@
 #include "chrome/browser/ash/system_extensions/api/window_management/cros_window_management_context_factory.h"
 #include "chrome/browser/ash/system_extensions/system_extensions_provider_factory.h"
 #include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h"
+#include "chrome/browser/push_notification/push_notification_service_factory.h"
 #include "chromeos/constants/chromeos_features.h"
 #else
 #include "chrome/browser/policy/cloud/user_policy_signin_service_factory.h"
@@ -914,6 +914,13 @@
   PreloadingModelKeyedServiceFactory::GetInstance();
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   NearbySharingServiceFactory::GetInstance();
+  if (base::FeatureList::IsEnabled(ash::features::kNearbyPresence)) {
+    // PushNotificationService is only enabled for ash for MVP. As more features
+    // are added to use the PushNotificationService, this can be reevaluated and
+    // expanded to enable building the PushNotificationService for all Chrome
+    // Desktop builds.
+    push_notification::PushNotificationServiceFactory::GetInstance();
+  }
 #endif
   NotificationDisplayServiceFactory::GetInstance();
   NotificationMetricsLoggerFactory::GetInstance();
@@ -1112,7 +1119,7 @@
 #if BUILDFLAG(IS_ANDROID)
   search_resumption_module::StartSuggestServiceFactory::GetInstance();
 #endif
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
+#if !BUILDFLAG(IS_ANDROID)
   SearchEngineChoiceServiceFactory::GetInstance();
 #endif
 #if BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/push_notification/BUILD.gn b/chrome/browser/push_notification/BUILD.gn
new file mode 100644
index 0000000..cf323a44
--- /dev/null
+++ b/chrome/browser/push_notification/BUILD.gn
@@ -0,0 +1,35 @@
+# 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("push_notification") {
+  sources = [
+    "push_notification_service_desktop_impl.cc",
+    "push_notification_service_desktop_impl.h",
+  ]
+
+  deps = [
+    "//base",
+    "//chrome/browser/profiles:profile",
+    "//components/cross_device/logging:logging",
+    "//components/keyed_service/content:content",
+    "//components/keyed_service/core:core",
+    "//components/prefs:prefs",
+    "//components/push_notification",
+    "//components/user_manager:user_manager",
+    "//content/public/browser:browser",
+  ]
+}
+source_set("unit_tests") {
+  testonly = true
+  sources = [ "push_notification_service_desktop_impl_unittest.cc" ]
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//chrome/browser/push_notification:push_notification",
+    "//chrome/test:test_support",
+    "//content/public/browser",
+    "//content/test:test_support",
+    "//testing/gtest",
+  ]
+}
diff --git a/chrome/browser/push_notification/DEPS b/chrome/browser/push_notification/DEPS
new file mode 100644
index 0000000..a96ab6b
--- /dev/null
+++ b/chrome/browser/push_notification/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+components/keyed_service/core/keyed_service.h",
+  "+components/push_notification",
+]
diff --git a/chrome/browser/push_notification/DIR_METADATA b/chrome/browser/push_notification/DIR_METADATA
new file mode 100644
index 0000000..b06d6ee2
--- /dev/null
+++ b/chrome/browser/push_notification/DIR_METADATA
@@ -0,0 +1,4 @@
+buganizer {
+  component_id: 1342830
+}
+team_email: "chromeos-cross-device-eng@google.com "
diff --git a/chrome/browser/push_notification/OWNERS b/chrome/browser/push_notification/OWNERS
new file mode 100644
index 0000000..ce22c0b
--- /dev/null
+++ b/chrome/browser/push_notification/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/ash/nearby/OWNERS
diff --git a/chrome/browser/push_notification/push_notification_service_desktop_impl.cc b/chrome/browser/push_notification/push_notification_service_desktop_impl.cc
new file mode 100644
index 0000000..960db76
--- /dev/null
+++ b/chrome/browser/push_notification/push_notification_service_desktop_impl.cc
@@ -0,0 +1,17 @@
+// 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 "chrome/browser/push_notification/push_notification_service_desktop_impl.h"
+#include "base/check.h"
+
+namespace push_notification {
+
+PushNotificationServiceDesktopImpl::PushNotificationServiceDesktopImpl() =
+    default;
+PushNotificationServiceDesktopImpl::~PushNotificationServiceDesktopImpl() =
+    default;
+
+void PushNotificationServiceDesktopImpl::Shutdown() {}
+
+}  // namespace push_notification
diff --git a/chrome/browser/push_notification/push_notification_service_desktop_impl.h b/chrome/browser/push_notification/push_notification_service_desktop_impl.h
new file mode 100644
index 0000000..708688390
--- /dev/null
+++ b/chrome/browser/push_notification/push_notification_service_desktop_impl.h
@@ -0,0 +1,30 @@
+// 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 CHROME_BROWSER_PUSH_NOTIFICATION_PUSH_NOTIFICATION_SERVICE_DESKTOP_IMPL_H_
+#define CHROME_BROWSER_PUSH_NOTIFICATION_PUSH_NOTIFICATION_SERVICE_DESKTOP_IMPL_H_
+
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/push_notification/push_notification_service.h"
+
+namespace push_notification {
+
+class PushNotificationServiceDesktopImpl : public PushNotificationService,
+                                           public KeyedService {
+ public:
+  PushNotificationServiceDesktopImpl();
+  PushNotificationServiceDesktopImpl(
+      const PushNotificationServiceDesktopImpl&) = delete;
+  PushNotificationServiceDesktopImpl& operator=(
+      const PushNotificationServiceDesktopImpl&) = delete;
+  ~PushNotificationServiceDesktopImpl() override;
+
+ private:
+  // KeyedService:
+  void Shutdown() override;
+};
+
+}  // namespace push_notification
+
+#endif  // CHROME_BROWSER_PUSH_NOTIFICATION_PUSH_NOTIFICATION_SERVICE_DESKTOP_IMPL_H_
diff --git a/chrome/browser/push_notification/push_notification_service_desktop_impl_unittest.cc b/chrome/browser/push_notification/push_notification_service_desktop_impl_unittest.cc
new file mode 100644
index 0000000..5291d84
--- /dev/null
+++ b/chrome/browser/push_notification/push_notification_service_desktop_impl_unittest.cc
@@ -0,0 +1,32 @@
+// 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 "chrome/browser/push_notification/push_notification_service_desktop_impl.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include <memory>
+
+namespace push_notification {
+
+class PushNotificationServiceDesktopImplTest : public testing::Test {
+ public:
+  PushNotificationServiceDesktopImplTest() = default;
+  ~PushNotificationServiceDesktopImplTest() override = default;
+
+  // testing::Test:
+  void SetUp() override {
+    push_notification_service_ =
+        std::make_unique<PushNotificationServiceDesktopImpl>();
+  }
+
+  std::unique_ptr<PushNotificationServiceDesktopImpl>
+      push_notification_service_;
+};
+
+TEST_F(PushNotificationServiceDesktopImplTest, StartService) {
+  EXPECT_TRUE(push_notification_service_);
+}
+
+}  // namespace push_notification
diff --git a/chrome/browser/push_notification/push_notification_service_factory.cc b/chrome/browser/push_notification/push_notification_service_factory.cc
new file mode 100644
index 0000000..643fa30e
--- /dev/null
+++ b/chrome/browser/push_notification/push_notification_service_factory.cc
@@ -0,0 +1,88 @@
+// 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 "chrome/browser/push_notification/push_notification_service_factory.h"
+
+#include <memory>
+
+#include "base/memory/singleton.h"
+#include "build/build_config.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/push_notification/push_notification_service_desktop_impl.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/prefs/pref_service.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/browser_context.h"
+
+namespace {
+
+constexpr char kServiceName[] = "PushNotificationService";
+
+}  // namespace
+
+namespace push_notification {
+
+// static
+PushNotificationServiceFactory* PushNotificationServiceFactory::GetInstance() {
+  return base::Singleton<PushNotificationServiceFactory>::get();
+}
+
+// static
+PushNotificationService* PushNotificationServiceFactory::GetForBrowserContext(
+    content::BrowserContext* context) {
+  // PushNotificationService is currently only implemented for ChromeOS Desktop.
+  // If/when iOS and/or Android decide on a Push Notification Service
+  // implementation, this CHECK can be revisited.
+  CHECK(BUILDFLAG(IS_CHROMEOS_ASH));
+  return static_cast<PushNotificationServiceDesktopImpl*>(
+      GetInstance()->GetServiceForBrowserContext(context, /*create=*/true));
+}
+
+PushNotificationServiceFactory::PushNotificationServiceFactory()
+    : ProfileKeyedServiceFactory(kServiceName) {}
+
+PushNotificationServiceFactory::~PushNotificationServiceFactory() = default;
+
+std::unique_ptr<KeyedService>
+PushNotificationServiceFactory::BuildServiceInstanceForBrowserContext(
+    content::BrowserContext* context) const {
+  // PushNotificationService is currently only implemented for ChromeOS Desktop.
+  // If/when iOS and/or Android decide on a Push Notification Service
+  // implementation, this CHECK can be revisited.
+  CHECK(BUILDFLAG(IS_CHROMEOS_ASH));
+  if (!context) {
+    return nullptr;
+  }
+
+  Profile* profile = Profile::FromBrowserContext(context);
+  if (!profile) {
+    return nullptr;
+  }
+
+  // Chime is not supported for secondary profiles.
+  if (!user_manager::UserManager::Get()->IsPrimaryUser(
+          ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile))) {
+    return nullptr;
+  }
+
+  // Guest/incognito profiles cannot use Chime.
+  if (profile->IsOffTheRecord()) {
+    return nullptr;
+  }
+
+  // Likewise, kiosk users are ineligible.
+  if (user_manager::UserManager::Get()->IsLoggedInAsAnyKioskApp()) {
+    return nullptr;
+  }
+
+  VLOG(1) << __func__ << ": creating PushNotificationService.";
+  return std::make_unique<PushNotificationServiceDesktopImpl>();
+}
+
+void PushNotificationServiceFactory::RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {}
+
+}  // namespace push_notification
diff --git a/chrome/browser/push_notification/push_notification_service_factory.h b/chrome/browser/push_notification/push_notification_service_factory.h
new file mode 100644
index 0000000..9bd1404
--- /dev/null
+++ b/chrome/browser/push_notification/push_notification_service_factory.h
@@ -0,0 +1,50 @@
+// 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 CHROME_BROWSER_PUSH_NOTIFICATION_PUSH_NOTIFICATION_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_PUSH_NOTIFICATION_PUSH_NOTIFICATION_SERVICE_FACTORY_H_
+
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}  // namespace base
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace push_notification {
+
+class PushNotificationService;
+
+class PushNotificationServiceFactory : public ProfileKeyedServiceFactory {
+ public:
+  PushNotificationServiceFactory(const PushNotificationServiceFactory&) =
+      delete;
+  PushNotificationServiceFactory& operator=(
+      const PushNotificationServiceFactory&) = delete;
+
+  static PushNotificationServiceFactory* GetInstance();
+
+  static PushNotificationService* GetForBrowserContext(
+      content::BrowserContext* context);
+
+ private:
+  friend struct base::DefaultSingletonTraits<PushNotificationServiceFactory>;
+
+  PushNotificationServiceFactory();
+  ~PushNotificationServiceFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
+      content::BrowserContext* context) const override;
+  void RegisterProfilePrefs(
+      user_prefs::PrefRegistrySyncable* registry) override;
+};
+
+}  // namespace push_notification
+
+#endif  // CHROME_BROWSER_PUSH_NOTIFICATION_PUSH_NOTIFICATION_SERVICE_FACTORY_H_
diff --git a/chrome/browser/readaloud/android/BUILD.gn b/chrome/browser/readaloud/android/BUILD.gn
index 4e64d61d..4b42934 100644
--- a/chrome/browser/readaloud/android/BUILD.gn
+++ b/chrome/browser/readaloud/android/BUILD.gn
@@ -82,8 +82,8 @@
     "java/res/drawable/baseline_more_horiz_24.xml",
     "java/res/drawable/error_24.xml",
     "java/res/drawable/format_ink_highlighter_24.xml",
-    "java/res/drawable/forward_30_24.xml",
-    "java/res/drawable/forward_30_button.xml",
+    "java/res/drawable/forward_10_24.xml",
+    "java/res/drawable/forward_10_button.xml",
     "java/res/drawable/mini_pause_button.xml",
     "java/res/drawable/mini_play_button.xml",
     "java/res/drawable/pause_24.xml",
diff --git a/chrome/browser/readaloud/android/java/res/drawable/forward_10_24.xml b/chrome/browser/readaloud/android/java/res/drawable/forward_10_24.xml
new file mode 100644
index 0000000..c3e621b1
--- /dev/null
+++ b/chrome/browser/readaloud/android/java/res/drawable/forward_10_24.xml
@@ -0,0 +1,14 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+  <path android:fillColor="@android:color/white"
+      android:pathData="M360,640L360,460L300,460L300,400L420,400L420,640L360,640ZM500,640Q483,640 471.5,628.5Q460,617 460,600L460,440Q460,423 471.5,411.5Q483,400 500,400L580,400Q597,400 608.5,411.5Q620,423 620,440L620,600Q620,617 608.5,628.5Q597,640 580,640L500,640ZM520,580L560,580Q560,580 560,580Q560,580 560,580L560,460Q560,460 560,460Q560,460 560,460L520,460Q520,460 520,460Q520,460 520,460L520,580Q520,580 520,580Q520,580 520,580ZM480,880Q405,880 339.5,851.5Q274,823 225.5,774.5Q177,726 148.5,660.5Q120,595 120,520Q120,445 148.5,379.5Q177,314 225.5,265.5Q274,217 339.5,188.5Q405,160 480,160L486,160L424,98L480,40L640,200L480,360L424,302L486,240L480,240Q363,240 281.5,321.5Q200,403 200,520Q200,637 281.5,718.5Q363,800 480,800Q597,800 678.5,718.5Q760,637 760,520L840,520Q840,595 811.5,660.5Q783,726 734.5,774.5Q686,823 620.5,851.5Q555,880 480,880Z"/>
+</vector>
\ No newline at end of file
diff --git a/chrome/browser/readaloud/android/java/res/drawable/forward_30_button.xml b/chrome/browser/readaloud/android/java/res/drawable/forward_10_button.xml
similarity index 89%
rename from chrome/browser/readaloud/android/java/res/drawable/forward_30_button.xml
rename to chrome/browser/readaloud/android/java/res/drawable/forward_10_button.xml
index 74ff5cc..94aa7b8 100644
--- a/chrome/browser/readaloud/android/java/res/drawable/forward_30_button.xml
+++ b/chrome/browser/readaloud/android/java/res/drawable/forward_10_button.xml
@@ -7,7 +7,7 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:drawable="@drawable/secondary_gray_rounded"/>
     <item
-        android:drawable="@drawable/forward_30_24"
+        android:drawable="@drawable/forward_10_24"
         android:left="20dp"
         android:top="20dp"
         android:right="20dp"
diff --git a/chrome/browser/readaloud/android/java/res/drawable/forward_30_24.xml b/chrome/browser/readaloud/android/java/res/drawable/forward_30_24.xml
deleted file mode 100644
index 217c44d..0000000
--- a/chrome/browser/readaloud/android/java/res/drawable/forward_30_24.xml
+++ /dev/null
@@ -1,15 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="960"
-    android:viewportHeight="960"
-    android:tint="?attr/colorControlNormal">
-  <path
-      android:fillColor="@android:color/white"
-      android:pathData="M300,640L300,580L400,580L400,540L340,540L340,500L400,500L400,460L300,460L300,400L420,400Q437,400 448.5,411.5Q460,423 460,440L460,600Q460,617 448.5,628.5Q437,640 420,640L300,640ZM540,640Q523,640 511.5,628.5Q500,617 500,600L500,440Q500,423 511.5,411.5Q523,400 540,400L620,400Q637,400 648.5,411.5Q660,423 660,440L660,600Q660,617 648.5,628.5Q637,640 620,640L540,640ZM560,580L600,580Q600,580 600,580Q600,580 600,580L600,460Q600,460 600,460Q600,460 600,460L560,460Q560,460 560,460Q560,460 560,460L560,580Q560,580 560,580Q560,580 560,580ZM480,880Q405,880 339.5,851.5Q274,823 225.5,774.5Q177,726 148.5,660.5Q120,595 120,520Q120,445 148.5,379.5Q177,314 225.5,265.5Q274,217 339.5,188.5Q405,160 480,160L486,160L424,98L480,40L640,200L480,360L424,302L486,240L480,240Q363,240 281.5,321.5Q200,403 200,520Q200,637 281.5,718.5Q363,800 480,800Q597,800 678.5,718.5Q760,637 760,520L840,520Q840,595 811.5,660.5Q783,726 734.5,774.5Q686,823 620.5,851.5Q555,880 480,880Z"/>
-</vector>
\ No newline at end of file
diff --git a/chrome/browser/readaloud/android/java/res/layout/readaloud_expanded_player_layout.xml b/chrome/browser/readaloud/android/java/res/layout/readaloud_expanded_player_layout.xml
index 80819e9..35d605cd 100644
--- a/chrome/browser/readaloud/android/java/res/layout/readaloud_expanded_player_layout.xml
+++ b/chrome/browser/readaloud/android/java/res/layout/readaloud_expanded_player_layout.xml
@@ -204,7 +204,7 @@
                 android:id="@+id/readaloud_seek_forward_button"
                 android:layout_width="@dimen/readaloud_control_button_diameter"
                 android:layout_height="@dimen/readaloud_control_button_diameter"
-                android:src="@drawable/forward_30_button"
+                android:src="@drawable/forward_10_button"
                 android:contentDescription="@string/readaloud_forward"/>
         </LinearLayout>
 
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerMediator.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerMediator.java
index f0b2938..4244edb 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerMediator.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerMediator.java
@@ -26,7 +26,7 @@
 /** Mediator class in charge of updating player UI property model. */
 class PlayerMediator implements InteractionHandler {
     private static final long SEEK_BACK_NANOS = -10 * 1_000_000_000L;
-    private static final long SEEK_FORWARD_NANOS = 30 * 1_000_000_000L;
+    private static final long SEEK_FORWARD_NANOS = 10 * 1_000_000_000L;
     private final PlayerCoordinator mCoordinator;
     private final PlayerCoordinator.Delegate mDelegate;
     private final PropertyModel mModel;
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerMediatorUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerMediatorUnitTest.java
index a445db3..4d86adfe 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerMediatorUnitTest.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerMediatorUnitTest.java
@@ -288,7 +288,7 @@
         mModel.set(PlayerProperties.DURATION_NANOS, 40 * 1_000_000_000L);
 
         mMediator.onSeekForwardClick();
-        verify(mPlayback).seekRelative(30 * 1_000_000_000L);
+        verify(mPlayback).seekRelative(10 * 1_000_000_000L);
     }
 
     @Test
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContent.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContent.java
index 3f0f436..2e68a9b 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContent.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContent.java
@@ -29,10 +29,10 @@
 
 public class ExpandedPlayerSheetContent implements BottomSheetContent {
     private static final String TAG = "RAPlayerSheet";
-    // Note: if these times need to change, the "back 10" and "forward 30" icons
+    // Note: if these times need to change, the "back 10" and "forward 10" icons
     // should also be changed.
     private static final int BACK_SECONDS = 10;
-    private static final int FORWARD_SECONDS = 30;
+    private static final int FORWARD_SECONDS = 10;
 
     private final Context mContext;
     private final BottomSheetController mBottomSheetController;
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java
index 8a49021..635cd56f 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java
@@ -104,7 +104,7 @@
         assertEquals("1x", mSpeedView.getText());
         assertEquals("1x increase/decrease speed.", mSpeedView.getContentDescription());
         assertEquals("Go back 10 seconds", mBackButton.getContentDescription());
-        assertEquals("Fast forward 30 seconds", mForwardButton.getContentDescription());
+        assertEquals("Fast forward 10 seconds", mForwardButton.getContentDescription());
     }
 
     @Test
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index e63fe6a..a141278 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -7,7 +7,6 @@
 import("//chrome/browser/buildflags.gni")
 import("//chrome/common/features.gni")
 import("//components/compose/features.gni")
-import("//components/signin/features.gni")
 import("//pdf/features.gni")
 import("//printing/buildflags/buildflags.gni")
 import("//third_party/closure_compiler/compile_js.gni")
@@ -44,6 +43,7 @@
       "password_manager:resources",
       "privacy_sandbox:resources",
       "profile_internals:resources",
+      "search_engine_choice:resources",
       "settings:resources",
       "settings_shared:resources",
       "side_panel/bookmarks:resources",
@@ -123,10 +123,6 @@
     ]
   }
 
-  if (enable_search_engine_choice) {
-    public_deps += [ "search_engine_choice:resources" ]
-  }
-
   if (enable_webui_tab_strip) {
     public_deps += [ "tab_strip:resources" ]
   }
@@ -231,6 +227,7 @@
     "bluetooth_internals:resources",
     "browsing_topics:resources",
     "components:resources",
+    "device_log:resources",
     "engagement:resources",
     "internals:resources",
     "location_internals:resources",
@@ -288,6 +285,7 @@
     "$root_gen_dir/chrome/browsing_topics_internals_resources.pak",
     "$root_gen_dir/chrome/components_resources.pak",
     "$root_gen_dir/chrome/dev_ui_browser_resources.pak",
+    "$root_gen_dir/chrome/device_log_resources.pak",
     "$root_gen_dir/chrome/engagement_resources.pak",
     "$root_gen_dir/chrome/internals_resources.pak",
     "$root_gen_dir/chrome/location_internals_resources.pak",
diff --git a/chrome/browser/resources/PRESUBMIT.py b/chrome/browser/resources/PRESUBMIT.py
index 3e2e038..a9d4f8b7 100644
--- a/chrome/browser/resources/PRESUBMIT.py
+++ b/chrome/browser/resources/PRESUBMIT.py
@@ -136,7 +136,7 @@
     'chrome/browser/resources/ash/settings/.eslintrc',
     'chrome/browser/resources/bluetooth_internals/',
     'chrome/browser/resources/chromeos/',
-    'chrome/browser/resources/device_log_ui/',
+    'chrome/browser/resources/device_log/',
     'chrome/browser/resources/explore_sites_internals/',
     'chrome/browser/resources/family_link_user_internals/',
     'chrome/browser/resources/feed_internals/',
diff --git a/chrome/browser/resources/accessibility/BUILD.gn b/chrome/browser/resources/accessibility/BUILD.gn
index e0343f9..6c2d768 100644
--- a/chrome/browser/resources/accessibility/BUILD.gn
+++ b/chrome/browser/resources/accessibility/BUILD.gn
@@ -5,8 +5,8 @@
 import("//build/config/chromeos/ui_mode.gni")
 import("//ui/webui/resources/tools/build_webui.gni")
 if (is_chromeos_lacros) {
+  import("//chrome/browser/resources/chromeos/accessibility/tools/manifest.gni")
   import("strings/extension_strings.gni")
-  import("tools/manifest.gni")
 }
 
 build_webui("build") {
diff --git a/chrome/browser/resources/accessibility/OWNERS b/chrome/browser/resources/accessibility/OWNERS
index 03bd397..976b955 100644
--- a/chrome/browser/resources/accessibility/OWNERS
+++ b/chrome/browser/resources/accessibility/OWNERS
@@ -1,2 +1 @@
 file://ui/accessibility/OWNERS
-anastasi@google.com
diff --git a/chrome/browser/resources/ash/settings/device_page/customize_button_select.html b/chrome/browser/resources/ash/settings/device_page/customize_button_select.html
index a54e62f..066155b 100644
--- a/chrome/browser/resources/ash/settings/device_page/customize_button_select.html
+++ b/chrome/browser/resources/ash/settings/device_page/customize_button_select.html
@@ -1,15 +1,15 @@
 <style include="md-select settings-shared input-device-settings-shared">
-  iron-list {
-    max-height: 50vh;
+  #menuContainer {
+    display: table;
     width: 200px;
   }
 
-  #menuContainer {
+  iron-dropdown {
     background-color: var(--cros-bg-color-elevation-3);
     border-radius: 2px;
     box-shadow: var(--cr-elevation-3);
-    outline: 1px solid var(--cros-separator-color);
-    display: table;
+    max-height: 50vh;
+    overflow: auto;
   }
 
   .label-container {
@@ -49,14 +49,13 @@
   </template>
 </div>
 <iron-dropdown opened="[[shouldShowDropdownMenu_]]"
-    no-cancel-on-outside-click>
+    no-cancel-on-outside-click
+    dynamic-align>
   <div id="menuContainer" slot="dropdown-content">
-    <iron-list items="[[menu]]">
-      <template>
-        <customize-button-dropdown-item option="[[item]]"
-            selected="[[isItemSelected_(item, selectedItem)]]">
-        </customize-button-dropdown-item>
-      </template>
-    </iron-list>
+    <template is="dom-repeat" items="[[menu]]">
+      <customize-button-dropdown-item option="[[item]]"
+          selected="[[isItemSelected_(item, selectedItem)]]">
+      </customize-button-dropdown-item>
+    </template>
   </div>
 </iron-dropdown>
\ No newline at end of file
diff --git a/chrome/browser/resources/ash/settings/os_settings_ui/os_settings_ui.html b/chrome/browser/resources/ash/settings/os_settings_ui/os_settings_ui.html
index 9c58d813..d9ce265 100644
--- a/chrome/browser/resources/ash/settings/os_settings_ui/os_settings_ui.html
+++ b/chrome/browser/resources/ash/settings/os_settings_ui/os_settings_ui.html
@@ -79,7 +79,10 @@
 
   :host-context(body.revamp-wayfinding-enabled) #drawer {
     --cr-drawer-border-start-end-radius: 20px;
-    --cr-drawer-header-padding: 28px;
+    --cr-drawer-border-end-end-radius: 20px;
+    --cr-drawer-header-color: var(--cros-sys-primary);
+    --cr-drawer-header-font: var(--cros-title-1-font);
+    --cr-drawer-header-padding: 22px;
     --cr-drawer-width: var(--settings-menu-width);
   }
 
@@ -104,15 +107,16 @@
     }
   }
 
-  #iconButton {
+  #drawerIcon {
     cursor: pointer;
     margin-inline-end: 14px;
     margin-inline-start: 0;
     outline: none;
   }
 
-  :host-context(body.revamp-wayfinding-enabled) #iconButton {
-    margin-inline-end: 12px;
+  :host-context(body.revamp-wayfinding-enabled) #drawerIcon {
+    --iron-icon-fill-color: var(--cros-sys-primary);
+    margin-inline-end: 6px;
   }
 </style>
 <settings-prefs id="prefs" prefs="{{prefs}}"></settings-prefs>
@@ -135,10 +139,12 @@
       <!-- Hidden from a11y because this icon is decorative. Clicking closes
           the dialog, but screen reader users can do this by pressing ESC,
           so aria-hidden is OK here. -->
-      <iron-icon id="iconButton" icon="cr20:menu"
-          on-click="onDrawerIconClick_" title="$i18n{close}"
+      <cr-icon-button id="drawerIcon"
+          iron-icon="cr20:menu"
+          on-click="onDrawerIconClick_"
+          title="$i18n{close}"
           aria-hidden="true">
-      </iron-icon>
+      </cr-icon-button>
     </div>
     <div slot="body">
       <template is="dom-if" id="drawerTemplate">
diff --git a/chrome/browser/resources/ash/settings/os_settings_ui/os_settings_ui.ts b/chrome/browser/resources/ash/settings/os_settings_ui/os_settings_ui.ts
index c7e571e..09148aa 100644
--- a/chrome/browser/resources/ash/settings/os_settings_ui/os_settings_ui.ts
+++ b/chrome/browser/resources/ash/settings/os_settings_ui/os_settings_ui.ts
@@ -10,10 +10,10 @@
  *
  *    <settings-ui prefs="{{prefs}}"></settings-ui>
  */
-import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import 'chrome://resources/polymer/v3_0/iron-media-query/iron-media-query.js';
 import 'chrome://resources/cr_components/settings_prefs/prefs.js';
 import 'chrome://resources/cr_elements/cr_drawer/cr_drawer.js';
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import 'chrome://resources/cr_elements/cr_page_host_style.css.js';
 import 'chrome://resources/cr_elements/icons.html.js';
 import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/BUILD.gn
index e8e5611..088b2783 100644
--- a/chrome/browser/resources/chromeos/accessibility/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/BUILD.gn
@@ -4,10 +4,10 @@
 
 import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/features.gni")
-import("//chrome/browser/resources/accessibility/tools/manifest.gni")
 import("//chrome/common/features.gni")
 import("//chrome/test/base/ash/js2gtest.gni")
 import("strings/accessibility_strings.gni")
+import("tools/manifest.gni")
 import("tools/run_jsbundler.gni")
 
 assert(is_chromeos_ash)
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
index 44e5b42c..58aada4 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
@@ -50,6 +50,7 @@
   "dictation/macros/stop_listening_macro.ts",
   "facegaze/facegaze.ts",
   "facegaze/camera_stream.ts",
+  "magnifier/magnifier.ts",
 ]
 
 # JS files needed by the TS compiler.
@@ -121,7 +122,6 @@
     "dictation/parse/simple_parse_strategy.js",
     "dictation/parse/speech_parser.js",
     "facegaze/camera_stream.html",
-    "magnifier/magnifier.js",
   ]
 
   foreach(_ts_file, ts_modules) {
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier.ts
similarity index 67%
rename from chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier.js
rename to chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier.ts
index 2980f3f7..e9a6a3f3 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier.ts
@@ -8,87 +8,78 @@
 import {FlagName, Flags} from '../../common/flags.js';
 import {RectUtil} from '../../common/rect_util.js';
 
-const EventType = chrome.automation.EventType;
-const RoleType = chrome.automation.RoleType;
+import AutomationEvent = chrome.automation.AutomationEvent;
+import EventType = chrome.automation.EventType;
+import PrefObject = chrome.settingsPrivate.PrefObject;
+import RoleType = chrome.automation.RoleType;
+import ScreenRect = chrome.accessibilityPrivate.ScreenRect;
 
-/**
- * Main class for the Chrome OS magnifier.
- */
+/** Main class for the Chrome OS magnifier. */
 export class Magnifier {
+  type: Magnifier.Type;
   /**
-   * @param {!Magnifier.Type} type The type of magnifier in use.
+   * Whether focus following is enabled or not, based on
+   * settings.a11y.screen_magnifier_focus_following preference.
    */
-  constructor(type) {
-    /** @const {!Magnifier.Type} */
+  private screenMagnifierFocusFollowing_: boolean|undefined;
+  /**
+   * Whether magnifier is current initializing, and so should ignore
+   * focus updates.
+   */
+  private isInitializing_ = true;
+  /** Last time mouse has moved (from last onMouseMovedOrDragged). */
+  private lastMouseMovedTime_: Date|undefined;
+  private focusHandler_: EventHandler;
+  private activeDescendantHandler_: EventHandler;
+  private selectionHandler_: EventHandler;
+  private onCaretBoundsChangedHandler: EventHandler;
+  private onMagnifierBoundsChangedHandler_:
+      ChromeEventHandler<[bounds: ScreenRect]>;
+  private updateFromPrefsHandler_: ChromeEventHandler<[prefs: PrefObject[]]>;
+  private onMouseMovedHandler_: EventHandler;
+  private onMouseDraggedHandler_: EventHandler;
+  private onLoadDesktopCallbackForTest_: (() => void)|null;
+
+  constructor(type: Magnifier.Type) {
     this.type = type;
-
-    /**
-     * Whether focus following is enabled or not, based on
-     * settings.a11y.screen_magnifier_focus_following preference.
-     * @private {boolean}
-     */
-    this.screenMagnifierFocusFollowing_;
-
-    /**
-     * Whether magnifier is current initializing, and so should ignore
-     * focus updates.
-     * @private {boolean}
-     */
-    this.isInitializing_ = true;
-
-    /**
-     * Last time mouse has moved (from last onMouseMovedOrDragged).
-     * @private {Date}
-     */
-    this.lastMouseMovedTime_;
-
-    /** @private {!EventHandler} */
     this.focusHandler_ = new EventHandler(
         [], EventType.FOCUS, event => this.onFocusOrSelectionChanged_(event));
 
-    /** @private {!EventHandler} */
     this.activeDescendantHandler_ = new EventHandler(
         [], EventType.ACTIVE_DESCENDANT_CHANGED,
         event => this.onActiveDescendantChanged_(event));
 
-    /** @private {!EventHandler} */
     this.selectionHandler_ = new EventHandler(
         [], EventType.SELECTION,
         event => this.onFocusOrSelectionChanged_(event));
 
-    /** @private {!EventHandler} */
     this.onCaretBoundsChangedHandler = new EventHandler(
         [], EventType.CARET_BOUNDS_CHANGED,
         event => this.onCaretBoundsChanged(event));
 
-    /** @private {!ChromeEventHandler} */
     this.onMagnifierBoundsChangedHandler_ = new ChromeEventHandler(
         chrome.accessibilityPrivate.onMagnifierBoundsChanged,
         bounds => this.onMagnifierBoundsChanged_(bounds));
 
-    /** @private {ChromeEventHandler} */
     this.updateFromPrefsHandler_ = new ChromeEventHandler(
         chrome.settingsPrivate.onPrefsChanged,
         prefs => this.updateFromPrefs_(prefs));
 
-    /** @private {!EventHandler} */
     this.onMouseMovedHandler_ = new EventHandler(
         [], chrome.automation.EventType.MOUSE_MOVED,
-        event => this.onMouseMovedOrDragged_(event));
+        () => this.onMouseMovedOrDragged_());
 
-    /** @private {!EventHandler} */
     this.onMouseDraggedHandler_ = new EventHandler(
         [], chrome.automation.EventType.MOUSE_DRAGGED,
-        event => this.onMouseMovedOrDragged_(event));
+        () => this.onMouseMovedOrDragged_());
 
-    /** @private {?function()} */
     this.onLoadDesktopCallbackForTest_ = null;
 
     this.init_();
   }
 
   /** Destructor to remove listener. */
-  onMagnifierDisabled() {
+  onMagnifierDisabled(): void {
     this.focusHandler_.stop();
     this.activeDescendantHandler_.stop();
     this.selectionHandler_.stop();
@@ -99,11 +90,8 @@
     this.onMouseDraggedHandler_.stop();
   }
 
-  /**
-   * Initializes Magnifier.
-   * @private
-   */
-  init_() {
+  /** Initializes Magnifier. */
+  private init_(): void {
     chrome.settingsPrivate.getAllPrefs(prefs => this.updateFromPrefs_(prefs));
     this.updateFromPrefsHandler_.start();
 
@@ -137,19 +125,11 @@
     }, Magnifier.IGNORE_FOCUS_UPDATES_INITIALIZATION_MS);
   }
 
-  /**
-   * @return {boolean}
-   * @private
-   */
-  drawDebugRect_() {
-    return Flags.isEnabled(FlagName.MAGNIFIER_DEBUG_DRAW_RECT);
+  private drawDebugRect_(): boolean {
+    return Boolean(Flags.isEnabled(FlagName.MAGNIFIER_DEBUG_DRAW_RECT));
   }
 
-  /**
-   * @param {!chrome.accessibilityPrivate.ScreenRect} bounds
-   * @private
-   */
-  onMagnifierBoundsChanged_(bounds) {
+  private onMagnifierBoundsChanged_(bounds: ScreenRect): void {
     if (this.drawDebugRect_()) {
       chrome.accessibilityPrivate.setFocusRings(
           [{
@@ -164,15 +144,11 @@
   /**
    * Sets |isInitializing_| inside tests to skip ignoring initial focus updates.
    */
-  setIsInitializingForTest(isInitializing) {
+  setIsInitializingForTest(isInitializing: boolean): void {
     this.isInitializing_ = isInitializing;
   }
 
-  /**
-   * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs
-   * @private
-   */
-  updateFromPrefs_(prefs) {
+  private updateFromPrefs_(prefs: PrefObject[]): void {
     prefs.forEach(pref => {
       switch (pref.key) {
         case Magnifier.Prefs.SCREEN_MAGNIFIER_FOCUS_FOLLOWING:
@@ -191,11 +167,12 @@
    * TODO(crbug.com/1146595): Add Chrome OS preference to allow disabling focus
    * following for docked magnifier.
    */
-  shouldFollowFocus() {
-    return !this.isInitializing_ &&
+  shouldFollowFocus(): boolean {
+    return Boolean(
+        !this.isInitializing_ &&
         (this.type === Magnifier.Type.DOCKED ||
          this.type === Magnifier.Type.FULL_SCREEN &&
-             this.screenMagnifierFocusFollowing_);
+             this.screenMagnifierFocusFollowing_));
   }
 
   /**
@@ -208,17 +185,16 @@
    * shake screen.
    * TODO(accessibility): On page load, sometimes viewport moves to center of
    * webpage instead of spotlighting first focusable page element.
-   *
-   * @param {!chrome.automation.AutomationEvent} event
-   * @private
    */
-  onFocusOrSelectionChanged_(event) {
+  private onFocusOrSelectionChanged_(event: AutomationEvent): void {
     const node = event.target;
     if (!node.location || !this.shouldFollowFocus()) {
       return;
     }
 
-    if (new Date() - this.lastMouseMovedTime_ <
+    // TODO(b/267329383): Clean this up, since Number(undefined) is NaN, and
+    // NaN should be avoided if possible.
+    if (Number(new Date()) - Number(this.lastMouseMovedTime_) <
         Magnifier.IGNORE_FOCUS_UPDATES_AFTER_MOUSE_MOVE_MS) {
       return;
     }
@@ -237,10 +213,8 @@
   /**
    * Listener for when active descendant is changed. Moves magnifier to include
    * active descendant in viewport.
-   * @param {!chrome.automation.AutomationEvent} event
-   * @private
    */
-  onActiveDescendantChanged_(event) {
+  private onActiveDescendantChanged_(event: AutomationEvent): void {
     const {activeDescendant} = event.target;
     if (!activeDescendant || !this.shouldFollowFocus()) {
       return;
@@ -257,16 +231,16 @@
   /**
    * Listener for when caret bounds have changed. Moves magnifier to include
    * caret in viewport.
-   * @param {!chrome.automation.AutomationEvent} event
-   * @private
    */
-  onCaretBoundsChanged(event) {
+  private onCaretBoundsChanged(event: AutomationEvent): void {
     const {target} = event;
     if (!target || !target.caretBounds) {
       return;
     }
 
-    if (new Date() - this.lastMouseMovedTime_ <
+    // TODO(b/267329383): Clean this up, since Number(undefined) is NaN, and
+    // NaN should be avoided if possible.
+    if (Number(new Date()) - Number(this.lastMouseMovedTime_) <
         Magnifier.IGNORE_FOCUS_UPDATES_AFTER_MOUSE_MOVE_MS) {
       return;
     }
@@ -284,21 +258,16 @@
     chrome.accessibilityPrivate.magnifierCenterOnPoint(caretBoundsCenter);
   }
 
-  /**
-   * Listener for when mouse moves or drags.
-   * @param {!chrome.automation.AutomationEvent} event
-   * @private
-   */
-  onMouseMovedOrDragged_(event) {
+  /** Listener for when mouse moves or drags. */
+  private onMouseMovedOrDragged_(): void {
     this.lastMouseMovedTime_ = new Date();
   }
 
   /**
    * Used by C++ tests to ensure Magnifier load is competed.
-   * @param {!function()} callback Callback for when desktop is loaded from
-   * automation.
+   * @param callback Callback for when desktop is loaded from automation.
    */
-  setOnLoadDesktopCallbackForTest(callback) {
+  setOnLoadDesktopCallbackForTest(callback: () => void): void {
     if (!this.focusHandler_.listening()) {
       this.onLoadDesktopCallbackForTest_ = callback;
       return;
@@ -308,36 +277,28 @@
   }
 }
 
-/**
- * Magnifier types.
- * @enum {string}
- * @const
- */
-Magnifier.Type = {
-  FULL_SCREEN: 'fullScreen',
-  DOCKED: 'docked',
-};
+export namespace Magnifier {
+  /** Magnifier types. */
+  export enum Type {
+    FULL_SCREEN = 'fullScreen',
+    DOCKED = 'docked',
+  }
 
-/**
- * Preferences that are configurable for Magnifier.
- * @enum {string}
- * @const
- */
-Magnifier.Prefs = {
-  SCREEN_MAGNIFIER_FOCUS_FOLLOWING:
-      'settings.a11y.screen_magnifier_focus_following',
-};
+  /** Preferences that are configurable for Magnifier. */
+  export enum Prefs {
+    SCREEN_MAGNIFIER_FOCUS_FOLLOWING =
+        'settings.a11y.screen_magnifier_focus_following'
+  }
 
-/**
- * Duration of time directly after startup of magnifier to ignore focus updates,
- * to prevent the magnified region from jumping.
- * @const {number}
- */
-Magnifier.IGNORE_FOCUS_UPDATES_INITIALIZATION_MS = 500;
+  /**
+   * Duration of time directly after startup of magnifier to ignore focus
+   * updates, to prevent the magnified region from jumping.
+   */
+  export const IGNORE_FOCUS_UPDATES_INITIALIZATION_MS = 500;
 
-/**
- * Duration of time directly after a mouse move or drag to ignore focus updates,
- * to prevent the magnified region from jumping.
- * @const {number}
- */
-Magnifier.IGNORE_FOCUS_UPDATES_AFTER_MOUSE_MOVE_MS = 250;
+  /**
+   * Duration of time directly after a mouse move or drag to ignore focus
+   * updates, to prevent the magnified region from jumping.
+   */
+  export const IGNORE_FOCUS_UPDATES_AFTER_MOUSE_MOVE_MS = 250;
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index 139441d3..a26861f 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 = []
+ts_modules = [ "background/input/keyboard_handler.ts" ]
 
 # TS files needed from ../common/.
 common_ts_modules = [
@@ -107,7 +107,6 @@
   "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",
@@ -152,7 +151,6 @@
   "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",
@@ -208,8 +206,23 @@
 ts_library("ts_build") {
   root_dir = "../"
   out_dir = tsc_out_dir
-  deps = [ "../common:ts_build" ]
-  definitions = []
+
+  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",
+  ]
 
   in_files = []
   foreach(_js_file, js_deps) {
@@ -218,6 +231,8 @@
   foreach(_ts_file, ts_modules) {
     in_files += [ "chromevox/" + _ts_file ]
   }
+
+  tsconfig_base = "../tsconfig.base.json"
 }
 
 group("build") {
@@ -244,7 +259,10 @@
 run_jsbundler("chromevox_copied_files") {
   mode = "copy"
   dest_dir = chromevox_out_dir
-  deps = [ "../common:ts_build" ]
+  deps = [
+    ":ts_build",
+    "../common:ts_build",
+  ]
   sources = [
     "background/background.html",
     "earcons/chromevox_loaded.ogg",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/command_handler.js
index da1cd84..18e111b 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/command_handler.js
@@ -100,7 +100,14 @@
         SmartStickyMode.instance.toggle();
         return false;
       case Command.PASS_THROUGH_MODE:
-        BackgroundKeyboardHandler.enablePassThroughMode();
+        if (ChromeVoxPrefs.isStickyModeOn()) {
+          new Output()
+              .withString(
+                  Msgs.getMsg('pass_through_unavailable_with_sticky_mode'))
+              .go();
+        } else {
+          BackgroundKeyboardHandler.enablePassThroughMode();
+        }
         return true;
       case Command.SHOW_LEARN_MODE_PAGE:
         this.showLearnModePage_();
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler.ts
similarity index 68%
rename from chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler.js
rename to chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler.ts
index 5dcb7d9..87144ea 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/input/keyboard_handler.ts
@@ -6,7 +6,6 @@
  * @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';
@@ -20,36 +19,35 @@
 import {ChromeVoxPrefs} from '../prefs.js';
 
 /**
- * @enum {string}
  * Internal pass through mode state (see usage below).
- * @private
  */
-const KeyboardPassThroughState_ = {
+enum 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',
-};
+  PENDING_SHORTCUT_KEYUPS = 'pending_shortcut_keyups',
+}
+
+class InternalKeyEvent extends KeyboardEvent {
+  stickyMode?: boolean;
+}
 
 export class BackgroundKeyboardHandler {
-  /** @private */
-  constructor() {
-    /** @private {Set} */
+  static instance?: BackgroundKeyboardHandler;
+  private static passThroughModeEnabled_: boolean = false;
+  private eatenKeyDowns_: Set<number>;
+  private passThroughState_: KeyboardPassThroughState;
+  private passedThroughKeyDowns_: Set<number>;
+
+  private constructor() {
     this.eatenKeyDowns_ = new Set();
-
-    /** @private {boolean} */
-    this.passThroughModeEnabled_ = false;
-
-    /** @private {!KeyboardPassThroughState_} */
-    this.passThroughState_ = KeyboardPassThroughState_.NO_PASS_THROUGH;
-
-    /** @private {Set} */
+    this.passThroughState_ = KeyboardPassThroughState.NO_PASS_THROUGH;
     this.passedThroughKeyDowns_ = new Set();
 
     document.addEventListener(
@@ -60,25 +58,24 @@
         true, ChromeVoxPrefs.isStickyPrefOn);
   }
 
-  static init() {
+  static init(): void {
     if (BackgroundKeyboardHandler.instance) {
       throw 'Error: trying to create two instances of singleton BackgroundKeyboardHandler.';
     }
     BackgroundKeyboardHandler.instance = new BackgroundKeyboardHandler();
   }
 
-  static enablePassThroughMode() {
+  static enablePassThroughMode(): void {
     ChromeVox.tts.speak(Msgs.getMsg('pass_through_key'), QueueMode.QUEUE);
-    BackgroundKeyboardHandler.instance.passThroughModeEnabled_ = true;
+    BackgroundKeyboardHandler.passThroughModeEnabled_ = true;
   }
 
   /**
    * Handles key down events.
-   * @param {Event} evt The key down event to process.
-   * @return {boolean} This value has no effect since we ignore it in
+   * The return value has no effect since we ignore it in
    *     SpokenFeedbackEventRewriterDelegate::HandleKeyboardEvent.
    */
-  onKeyDown(evt) {
+  onKeyDown(evt: InternalKeyEvent): boolean {
     EventSource.set(EventSourceType.STANDARD_KEYBOARD);
     evt.stickyMode = ChromeVoxPrefs.isStickyModeOn();
 
@@ -90,7 +87,7 @@
       this.passedThroughKeyDowns_.clear();
     }
 
-    if (this.passThroughModeEnabled_) {
+    if (BackgroundKeyboardHandler.passThroughModeEnabled_) {
       this.passedThroughKeyDowns_.add(evt.keyCode);
       return false;
     }
@@ -102,9 +99,9 @@
 
     if (!this.callOnKeyDownHandlers_(evt) ||
         this.shouldConsumeSearchKey_(evt)) {
-      if (this.passThroughModeEnabled_) {
+      if (BackgroundKeyboardHandler.passThroughModeEnabled_) {
         this.passThroughState_ =
-            KeyboardPassThroughState_.PENDING_PASS_THROUGH_SHORTCUT_KEYUPS;
+            KeyboardPassThroughState.PENDING_PASS_THROUGH_SHORTCUT_KEYUPS;
       }
       evt.preventDefault();
       evt.stopPropagation();
@@ -114,12 +111,7 @@
     return false;
   }
 
-  /**
-   * @param {Event} evt The key down event to process.
-   * @return {boolean} Whether the event should continue propagating.
-   * @private
-   */
-  callOnKeyDownHandlers_(evt) {
+  private callOnKeyDownHandlers_(evt: Event): boolean {
     // Defer first to the math handler, if it exists, then ordinary keyboard
     // commands.
     if (!MathHandler.onKeyDown(evt)) {
@@ -134,12 +126,7 @@
     return ChromeVoxKbHandler.basicKeyDownActionsListener(evt);
   }
 
-  /**
-   * @param {Event} evt The key down event to evaluate.
-   * @return {boolean} Whether the event should be consumed.
-   * @private
-   */
-  shouldConsumeSearchKey_(evt) {
+  private shouldConsumeSearchKey_(evt: InternalKeyEvent): boolean {
     // 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
@@ -148,45 +135,44 @@
       return false;
     }
 
-    return Boolean(evt.metaKey) || evt.keyCode === KeyCode.SEARCH;
+    return Boolean(evt.metaKey) || evt.keyCode === KeyCode['SEARCH'];
   }
 
   /**
-   * 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
+   * The return value has no effect since we ignore it in
    *     SpokenFeedbackEventRewriterDelegate::HandleKeyboardEvent.
    */
-  onKeyUp(evt) {
+  onKeyUp(evt: InternalKeyEvent): boolean {
     if (this.eatenKeyDowns_.has(evt.keyCode)) {
       evt.preventDefault();
       evt.stopPropagation();
       this.eatenKeyDowns_.delete(evt.keyCode);
     }
 
-    if (this.passThroughModeEnabled_) {
+    if (BackgroundKeyboardHandler.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).
-        this.passThroughModeEnabled_ = false;
-        this.passThroughState_ = KeyboardPassThroughState_.NO_PASS_THROUGH;
+        BackgroundKeyboardHandler.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 c3d3faa..0149195 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.instance.passThroughModeEnabled_);
+      assertFalse(BackgroundKeyboardHandler.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.instance.passThroughModeEnabled_);
+      assertFalse(BackgroundKeyboardHandler.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.instance.passThroughModeEnabled_);
+      assertFalse(BackgroundKeyboardHandler.passThroughModeEnabled_);
 
       const searchShiftEsc = TestUtils.createMockKeyEvent(
           KeyCode.ESCAPE, {metaKey: true, shiftKey: true});
@@ -86,28 +86,32 @@
       assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_pass_through_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
 
       keyboardHandler.onKeyUp(searchShiftEsc);
       assertEquals(2, keyboardHandler.eatenKeyDowns_.size);
       assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_pass_through_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
 
-      keyboardHandler.onKeyUp(searchShift);
+      const searchShiftUp = TestUtils.createMockKeyEvent(
+          KeyCode.SHIFT, {metaKey: true, shiftKey: false});
+      keyboardHandler.onKeyUp(searchShiftUp);
       assertEquals(1, keyboardHandler.eatenKeyDowns_.size);
       assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_pass_through_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
 
-      keyboardHandler.onKeyUp(search);
+      const searchUp =
+          TestUtils.createMockKeyEvent(KeyCode.SEARCH, {metaKey: false});
+      keyboardHandler.onKeyUp(searchUp);
       assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
       assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_shortcut_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
 
       // Now, the next series of key downs should be passed through.
       // Try Search+Ctrl+M.
@@ -116,7 +120,7 @@
       assertEquals(1, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_shortcut_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
 
       const searchCtrl = TestUtils.createMockKeyEvent(
           KeyCode.CONTROL, {metaKey: true, ctrlKey: true});
@@ -125,7 +129,7 @@
       assertEquals(2, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_shortcut_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
 
       const searchCtrlM = TestUtils.createMockKeyEvent(
           KeyCode.M, {metaKey: true, ctrlKey: true});
@@ -134,27 +138,27 @@
       assertEquals(3, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_shortcut_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
 
       keyboardHandler.onKeyUp(searchCtrlM);
       assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
       assertEquals(2, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_shortcut_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
 
       keyboardHandler.onKeyUp(searchCtrl);
       assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
       assertEquals(1, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals(
           'pending_shortcut_keyups', keyboardHandler.passThroughState_);
-      assertTrue(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
+      assertTrue(BackgroundKeyboardHandler.passThroughModeEnabled_);
 
       keyboardHandler.onKeyUp(search);
       assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
       assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
       assertEquals('no_pass_through', keyboardHandler.passThroughState_);
-      assertFalse(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
+      assertFalse(BackgroundKeyboardHandler.passThroughModeEnabled_);
     });
 
 AX_TEST_F(
@@ -162,7 +166,7 @@
     async function() {
       await this.runWithLoadedTree('<p>test</p>');
       function assertNoPassThrough() {
-        assertFalse(BackgroundKeyboardHandler.instance.passThroughModeEnabled_);
+        assertFalse(BackgroundKeyboardHandler.passThroughModeEnabled_);
         assertEquals('no_pass_through', keyboardHandler.passThroughState_);
         assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
       }
@@ -242,7 +246,7 @@
     'UnexpectedKeyDownUpPairsPassThrough', async function() {
       await this.runWithLoadedTree('<p>test</p>');
       // Force pass through mode.
-      BackgroundKeyboardHandler.instance.passThroughModeEnabled_ = true;
+      BackgroundKeyboardHandler.passThroughModeEnabled_ = true;
 
       // Send a few key downs (which are passed through).
       const search =
diff --git a/chrome/browser/resources/accessibility/tools/generate_manifest.py b/chrome/browser/resources/chromeos/accessibility/chromevox/tools/generate_manifest.py
similarity index 91%
rename from chrome/browser/resources/accessibility/tools/generate_manifest.py
rename to chrome/browser/resources/chromeos/accessibility/chromevox/tools/generate_manifest.py
index 855eb5c..978224c 100755
--- a/chrome/browser/resources/accessibility/tools/generate_manifest.py
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/tools/generate_manifest.py
@@ -10,15 +10,16 @@
 import os
 import sys
 
-src_dir_path = os.path.normpath(
-    os.path.join(os.path.abspath(__file__), *[os.path.pardir] * 6))
-
 jinja2_path = os.path.normpath(
-    os.path.join(src_dir_path, 'third_party'))
+    os.path.join(
+        os.path.abspath(__file__), *[os.path.pardir] * 8 + ['third_party']))
 nom_path = os.path.normpath(
-    os.path.join(src_dir_path, 'tools/json_comment_eater'))
+    os.path.join(
+        os.path.abspath(__file__),
+        *[os.path.pardir] * 8 + ['tools/json_comment_eater']))
 version_py_path = os.path.normpath(
-    os.path.join(src_dir_path, 'build/util'))
+    os.path.join(
+        os.path.abspath(__file__), *[os.path.pardir] * 8 + ['build/util']))
 sys.path.insert(0, jinja2_path)
 sys.path.insert(0, nom_path)
 sys.path.insert(0, version_py_path)
diff --git a/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js b/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
index 4d8d7c1..7e366d1c 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
@@ -109,7 +109,7 @@
 export class AutomationPredicate {
   /**
    * Constructs a predicate given a list of roles.
-   * @param {!Array<Role>} roles
+   * @param {!Array<chrome.automation.RoleType>} roles
    * @return {!AutomationPredicate.Unary}
    */
   static roles(roles) {
diff --git a/chrome/browser/resources/chromeos/accessibility/strings/chromevox_strings.grdp b/chrome/browser/resources/chromeos/accessibility/strings/chromevox_strings.grdp
index 88cef8fc..65f0005 100644
--- a/chrome/browser/resources/chromeos/accessibility/strings/chromevox_strings.grdp
+++ b/chrome/browser/resources/chromeos/accessibility/strings/chromevox_strings.grdp
@@ -1960,6 +1960,9 @@
   <message desc="Describes the pass through key command. Shown in options page." name="IDS_CHROMEVOX_PASS_THROUGH_KEY_DESCRIPTION">
     Pass through key
   </message>
+  <message desc="Spoken after pressing the pass through key command while sticky mode is enabled." name="IDS_CHROMEVOX_PASS_THROUGH_UNAVAILABLE_WITH_STICKY_MODE" is_accessibility_with_no_ui="true">
+    Pass through is unavailable with sticky mode on
+  </message>
   <message desc="Describes the show context menu command. Shown in options page." name="IDS_CHROMEVOX_SHOW_CONTEXT_MENU">
     Show context menu
   </message>
diff --git a/chrome/browser/resources/accessibility/tools/manifest.gni b/chrome/browser/resources/chromeos/accessibility/tools/manifest.gni
similarity index 90%
rename from chrome/browser/resources/accessibility/tools/manifest.gni
rename to chrome/browser/resources/chromeos/accessibility/tools/manifest.gni
index 36bb5a01..5b44aef 100644
--- a/chrome/browser/resources/accessibility/tools/manifest.gni
+++ b/chrome/browser/resources/chromeos/accessibility/tools/manifest.gni
@@ -10,7 +10,7 @@
   output_file = invoker.output_file
   key = invoker.key
   action(target_name) {
-    script = "//chrome/browser/resources/accessibility/tools/generate_manifest.py"
+    script = "//chrome/browser/resources/chromeos/accessibility/chromevox/tools/generate_manifest.py"
     inputs = [
       version_file,
       version_script,
diff --git a/chrome/browser/resources/compose/app.html b/chrome/browser/resources/compose/app.html
index f22af68..9a7601a 100644
--- a/chrome/browser/resources/compose/app.html
+++ b/chrome/browser/resources/compose/app.html
@@ -1,4 +1,4 @@
-<style include="cr-hidden-style md-select">
+<style include="md-select">
   :host {
     --gap-between-sections_: 16px;
     --padding_: 20px;
@@ -310,6 +310,19 @@
     padding: 0;
     visibility: visible;
   }
+
+  /**
+   * cr-hidden-style stamps an !important on [hidden] elements, which prevents
+   * any animations/transitions from applying. Instead of using cr-hidden-style,
+   * list all the [hidden] elements below. Not all elements need this, just
+   * the ones that override the default 'display' property.
+   */
+  .dialog[hidden],
+  .footer[hidden],
+  #loading[hidden],
+  #editContainer[hidden] {
+    display: none;
+  }
 </style>
 
 <div id="consentDialog" class="dialog" hidden="[[showMainAppDialog_]]">
diff --git a/chrome/browser/resources/compose/app.ts b/chrome/browser/resources/compose/app.ts
index a945318..4b0a49da 100644
--- a/chrome/browser/resources/compose/app.ts
+++ b/chrome/browser/resources/compose/app.ts
@@ -6,7 +6,6 @@
 import './strings.m.js';
 import './textarea.js';
 import '//resources/cr_elements/cr_button/cr_button.js';
-import '//resources/cr_elements/cr_hidden_style.css.js';
 import '//resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.js';
 import '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import '//resources/cr_elements/cr_loading_gradient/cr_loading_gradient.js';
diff --git a/chrome/browser/resources/compose/textarea.html b/chrome/browser/resources/compose/textarea.html
index 2ddef8e..dbbe1a8 100644
--- a/chrome/browser/resources/compose/textarea.html
+++ b/chrome/browser/resources/compose/textarea.html
@@ -1,4 +1,4 @@
-<style include="cr-hidden-style">
+<style>
   :host {
     display: flex;
     flex-direction: column;
@@ -131,6 +131,17 @@
   cr-icon-button {
     margin: 0;
   }
+
+  /**
+   * cr-hidden-style stamps an !important on [hidden] elements, which prevents
+   * any animations/transitions from applying. Instead of using cr-hidden-style,
+   * list all the [hidden] elements below. Not all elements need this, just
+   * the ones that override the default 'display' property.
+   */
+  #readonlyContainer[hidden],
+  #editButtonContainer[hidden] {
+    display: none;
+  }
 </style>
 
 <div id="inputContainer" hidden$="[[readonly]]">
diff --git a/chrome/browser/resources/compose/textarea.ts b/chrome/browser/resources/compose/textarea.ts
index a4dd840..9163611 100644
--- a/chrome/browser/resources/compose/textarea.ts
+++ b/chrome/browser/resources/compose/textarea.ts
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import './icons.html.js';
-import '//resources/cr_elements/cr_hidden_style.css.js';
 import '//resources/cr_elements/cr_shared_vars.css.js';
 import '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
 
diff --git a/chrome/browser/resources/device_log/BUILD.gn b/chrome/browser/resources/device_log/BUILD.gn
new file mode 100644
index 0000000..81f59d9
--- /dev/null
+++ b/chrome/browser/resources/device_log/BUILD.gn
@@ -0,0 +1,20 @@
+# 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("//ui/webui/resources/tools/build_webui.gni")
+
+build_webui("build") {
+  grd_prefix = "device_log"
+
+  static_files = [
+    "device_log_ui.css",
+    "device_log_ui.html",
+  ]
+
+  # TODO(crbug.com/1511758): Migrate device-log-ui to TypeScript.
+  non_web_component_files = [ "device_log_ui.js" ]
+
+  ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
+  ts_deps = [ "//ui/webui/resources/js:build_ts" ]
+}
diff --git a/chrome/browser/resources/device_log_ui/OWNERS b/chrome/browser/resources/device_log/OWNERS
similarity index 100%
rename from chrome/browser/resources/device_log_ui/OWNERS
rename to chrome/browser/resources/device_log/OWNERS
diff --git a/chrome/browser/resources/device_log_ui/device_log_ui.css b/chrome/browser/resources/device_log/device_log_ui.css
similarity index 100%
rename from chrome/browser/resources/device_log_ui/device_log_ui.css
rename to chrome/browser/resources/device_log/device_log_ui.css
diff --git a/chrome/browser/resources/device_log_ui/device_log_ui.html b/chrome/browser/resources/device_log/device_log_ui.html
similarity index 100%
rename from chrome/browser/resources/device_log_ui/device_log_ui.html
rename to chrome/browser/resources/device_log/device_log_ui.html
diff --git a/chrome/browser/resources/device_log_ui/device_log_ui.js b/chrome/browser/resources/device_log/device_log_ui.js
similarity index 100%
rename from chrome/browser/resources/device_log_ui/device_log_ui.js
rename to chrome/browser/resources/device_log/device_log_ui.js
diff --git a/chrome/browser/resources/downloads/item.ts b/chrome/browser/resources/downloads/item.ts
index 439a5818..3c70c9a 100644
--- a/chrome/browser/resources/downloads/item.ts
+++ b/chrome/browser/resources/downloads/item.ts
@@ -368,11 +368,8 @@
       // Mimics logic in download_ui_model.cc for downloads with danger_type
       // DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE.
       case DangerType.kDangerousFile:
-        return this.data.safeBrowsingState ===
-                SafeBrowsingState.kNoSafeBrowsing ?
-            DisplayType.UNVERIFIED :
-            (this.data.hasSafeBrowsingVerdict ? DisplayType.SUSPICIOUS :
-                                                DisplayType.UNVERIFIED);
+        return this.data.hasSafeBrowsingVerdict ? DisplayType.SUSPICIOUS :
+                                                  DisplayType.UNVERIFIED;
 
       case DangerType.kDangerousUrl:
       case DangerType.kDangerousContent:
diff --git a/chrome/browser/resources/search_engine_choice/BUILD.gn b/chrome/browser/resources/search_engine_choice/BUILD.gn
index 5d52312..a1228d1 100644
--- a/chrome/browser/resources/search_engine_choice/BUILD.gn
+++ b/chrome/browser/resources/search_engine_choice/BUILD.gn
@@ -2,11 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//components/signin/features.gni")
 import("//ui/webui/resources/tools/build_webui.gni")
 
-assert(enable_search_engine_choice)
-
 build_webui("build") {
   grd_prefix = "search_engine_choice"
 
diff --git a/chrome/browser/resources/search_engine_choice/app.ts b/chrome/browser/resources/search_engine_choice/app.ts
index 4acab20..e2d91ae 100644
--- a/chrome/browser/resources/search_engine_choice/app.ts
+++ b/chrome/browser/resources/search_engine_choice/app.ts
@@ -116,7 +116,6 @@
   private hasUserScrolledToTheBottom_: boolean;
   private withForcedScroll_: boolean;
   private actionButtonText_: string;
-  private scrollBehavior_: ScrollBehavior = 'smooth';
 
   constructor() {
     super();
@@ -184,11 +183,9 @@
     if (this.needsScrollToTheBottom_()) {
       if (this.isChoiceListScrollable_()) {
         const choiceList = this.$.choiceList;
-        choiceList.scrollTo(
-            {top: choiceList.scrollHeight, behavior: this.scrollBehavior_});
+        choiceList.scrollTo({top: choiceList.scrollHeight, behavior: 'smooth'});
       } else if (this.isPageScrollable_()) {
-        window.scrollTo(
-            {top: document.body.scrollHeight, behavior: this.scrollBehavior_});
+        window.scrollTo({top: document.body.scrollHeight, behavior: 'smooth'});
       }
       return;
     }
@@ -196,11 +193,6 @@
         parseInt(this.selectedChoice_));
   }
 
-  // Removes the scrolling animation to inscrease test stability.
-  setInstantScrollBehaviorForTest() {
-    this.scrollBehavior_ = 'instant';
-  }
-
   private onInfoDialogButtonClicked_() {
     this.$.infoDialog.close();
   }
diff --git a/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service.cc b/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service.cc
index c1cde6c..8544d98 100644
--- a/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service.cc
+++ b/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service.cc
@@ -15,6 +15,7 @@
 #include "components/policy/core/common/cloud/dm_token.h"
 #include "components/policy/core/common/management/management_service.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h"
 #include "components/safe_browsing/core/browser/realtime/policy_engine.h"
 #include "components/safe_browsing/core/browser/realtime/url_lookup_service_base.h"
 #include "components/safe_browsing/core/browser/referrer_chain_provider.h"
@@ -40,11 +41,13 @@
         std::unique_ptr<SafeBrowsingTokenFetcher> token_fetcher,
         enterprise_connectors::ConnectorsService* connectors_service,
         ReferrerChainProvider* referrer_chain_provider)
-    : RealTimeUrlLookupServiceBase(url_loader_factory,
-                                   cache_manager,
-                                   get_user_population_callback,
-                                   referrer_chain_provider,
-                                   /* pref_service= */ nullptr),
+    : RealTimeUrlLookupServiceBase(
+          url_loader_factory,
+          cache_manager,
+          get_user_population_callback,
+          referrer_chain_provider,
+          /*pref_service=*/nullptr,
+          /*webui_delegate=*/WebUIInfoSingleton::GetInstance()),
       profile_(profile),
       connectors_service_(connectors_service),
       token_fetcher_(std::move(token_fetcher)) {}
@@ -106,28 +109,25 @@
     const GURL& url,
     const GURL& last_committed_url,
     bool is_mainframe,
-    RTLookupRequestCallback request_callback,
     RTLookupResponseCallback response_callback,
     scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
   token_fetcher_->Start(base::BindOnce(
       &ChromeEnterpriseRealTimeUrlLookupService::OnGetAccessToken,
       weak_factory_.GetWeakPtr(), url, last_committed_url, is_mainframe,
-      std::move(request_callback), std::move(response_callback),
-      std::move(callback_task_runner), base::TimeTicks::Now()));
+      std::move(response_callback), std::move(callback_task_runner),
+      base::TimeTicks::Now()));
 }
 
 void ChromeEnterpriseRealTimeUrlLookupService::OnGetAccessToken(
     const GURL& url,
     const GURL& last_committed_url,
     bool is_mainframe,
-    RTLookupRequestCallback request_callback,
     RTLookupResponseCallback response_callback,
     scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
     base::TimeTicks get_token_start_time,
     const std::string& access_token) {
   SendRequest(url, last_committed_url, is_mainframe, access_token,
-              std::move(request_callback), std::move(response_callback),
-              std::move(callback_task_runner),
+              std::move(response_callback), std::move(callback_task_runner),
               /* is_sampled_report */ false);
 }
 
diff --git a/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service.h b/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service.h
index d137a63..07fef33 100644
--- a/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service.h
+++ b/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service.h
@@ -73,7 +73,6 @@
       const GURL& url,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       RTLookupResponseCallback response_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override;
 
@@ -82,7 +81,6 @@
       const GURL& url,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       RTLookupResponseCallback response_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
       base::TimeTicks get_token_start_time,
diff --git a/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service_unittest.cc b/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service_unittest.cc
index 8096b22..f6644ac9 100644
--- a/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service.h"
 
 #include "base/functional/bind.h"
+#include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "chrome/browser/enterprise/connectors/connectors_service.h"
@@ -67,6 +68,26 @@
                                  ReferrerChain* out_referrer_chain));
 };
 
+bool GetRequestProto(const network::ResourceRequest& request,
+                     RTLookupRequest* request_proto) {
+  if (!request.request_body || !request.request_body->elements()) {
+    return false;
+  }
+
+  // Supporting one DataElementBytes is sufficient here. If request
+  // protos grow to need data pipes, we would need further test code
+  // to read the contents of the pipe.
+  const std::vector<network::DataElement>* elements =
+      request.request_body->elements();
+  if (elements->size() != 1 ||
+      elements->at(0).type() !=
+          network::mojom::DataElementDataView::Tag::kBytes) {
+    return false;
+  }
+  return request_proto->ParseFromString(std::string(
+      elements->at(0).As<network::DataElementBytes>().AsStringPiece()));
+}
+
 }  // namespace
 
 class ChromeEnterpriseRealTimeUrlLookupServiceTest : public PlatformTest {
@@ -217,14 +238,15 @@
                                RTLookupResponse::ThreatInfo::COVERING_MATCH);
   task_environment_.RunUntilIdle();
 
-  base::MockCallback<RTLookupRequestCallback> request_callback;
+  base::MockCallback<network::TestURLLoaderFactory::Interceptor>
+      request_callback;
   base::MockCallback<RTLookupResponseCallback> response_callback;
-  enterprise_rt_service()->StartLookup(
-      url, last_committed_url_, is_mainframe_, request_callback.Get(),
-      response_callback.Get(), content::GetIOThreadTaskRunner({}));
+  enterprise_rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
+                                       response_callback.Get(),
+                                       content::GetIOThreadTaskRunner({}));
 
-  // |request_callback| should not be called if the verdict is already cached.
-  EXPECT_CALL(request_callback, Run(_, _)).Times(0);
+  test_url_loader_factory_.SetInterceptor(request_callback.Get());
+  EXPECT_CALL(request_callback, Run(_)).Times(0);
   EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ true,
                                      /* is_cached_response */ true, _));
 
@@ -246,30 +268,43 @@
                       Return(ReferrerChainProvider::SUCCESS)));
 
   base::MockCallback<RTLookupResponseCallback> response_callback;
-  enterprise_rt_service()->StartLookup(
-      url, last_committed_url_, is_mainframe_,
-      base::BindOnce(
-          [](std::unique_ptr<RTLookupRequest> request, std::string token) {
-            EXPECT_EQ("http://example.test/", request->url());
-            EXPECT_EQ("dm_token", request->dm_token());
-            EXPECT_EQ(ChromeUserPopulation::SAFE_BROWSING,
-                      request->population().user_population());
-            EXPECT_TRUE(request->population().is_history_sync_enabled());
-            EXPECT_EQ(ChromeUserPopulation::NOT_MANAGED,
-                      request->population().profile_management_status());
-            EXPECT_TRUE(request->population().is_under_advanced_protection());
-            EXPECT_EQ("access_token_string", token);
-          }),
-      response_callback.Get(), content::GetIOThreadTaskRunner({}));
+  enterprise_rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
+                                       response_callback.Get(),
+                                       content::GetIOThreadTaskRunner({}));
 
   EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ true,
                                      /* is_cached_response */ false, _));
 
+  bool request_validated;
+  test_url_loader_factory_.SetInterceptor(
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        RTLookupRequest request_proto;
+        ASSERT_TRUE(GetRequestProto(request, &request_proto));
+        EXPECT_EQ("http://example.test/", request_proto.url());
+        EXPECT_EQ("dm_token", request_proto.dm_token());
+        EXPECT_EQ(ChromeUserPopulation::SAFE_BROWSING,
+                  request_proto.population().user_population());
+        EXPECT_TRUE(request_proto.population().is_history_sync_enabled());
+        EXPECT_EQ(ChromeUserPopulation::NOT_MANAGED,
+                  request_proto.population().profile_management_status());
+        EXPECT_TRUE(request_proto.population().is_under_advanced_protection());
+
+        std::string header_value;
+        bool found_header = request.headers.GetHeader(
+            net::HttpRequestHeaders::kAuthorization, &header_value);
+        EXPECT_TRUE(found_header);
+        EXPECT_EQ(header_value, "Bearer access_token_string");
+
+        request_validated = true;
+      }));
+
   EXPECT_TRUE(raw_token_fetcher()->WasStartCalled());
   FulfillAccessTokenRequest("access_token_string");
 
   task_environment_.RunUntilIdle();
 
+  EXPECT_TRUE(request_validated);
+
   // Check the response is cached.
   EXPECT_NE(nullptr, GetCachedRealTimeUrlVerdict(url));
 }
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_file_processor.cc b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_file_processor.cc
index 20907ff3..9c9ef85 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_file_processor.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_file_processor.cc
@@ -18,6 +18,12 @@
 namespace safe_browsing {
 
 namespace {
+// Max number of files to process per extension.
+constexpr int64_t kMaxFilesToProcess = 50;
+
+// Max file size to process - 100KB.
+constexpr int64_t kMaxFileSizeBytes = 100 * 1024;
+
 // Max number of files to read per extension.
 constexpr int64_t kMaxFilesToRead = 1000;
 
@@ -79,8 +85,8 @@
 ExtensionTelemetryFileProcessor::~ExtensionTelemetryFileProcessor() = default;
 
 ExtensionTelemetryFileProcessor::ExtensionTelemetryFileProcessor()
-    : max_files_to_process_(kExtensionTelemetryFileDataMaxFilesToProcess.Get()),
-      max_file_size_(kExtensionTelemetryFileDataMaxFileSizeBytes.Get()),
+    : max_files_to_process_(kMaxFilesToProcess),
+      max_file_size_(kMaxFileSizeBytes),
       max_files_to_read_(kMaxFilesToRead) {}
 
 base::Value::Dict ExtensionTelemetryFileProcessor::ProcessExtension(
@@ -196,6 +202,15 @@
          file_path.MatchesExtension(kCSSFileSuffix);
 }
 
+void ExtensionTelemetryFileProcessor::SetMaxFilesToProcessForTest(
+    int64_t max_files_to_process) {
+  max_files_to_process_ = max_files_to_process;
+}
+void ExtensionTelemetryFileProcessor::SetMaxFileSizeBytesForTest(
+    int64_t max_file_size) {
+  max_file_size_ = max_file_size;
+}
+
 void ExtensionTelemetryFileProcessor::SetMaxFilesToReadForTest(
     int64_t max_files_to_read) {
   max_files_to_read_ = max_files_to_read;
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_file_processor.h b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_file_processor.h
index 21f7ff8a..a195a00 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_file_processor.h
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_file_processor.h
@@ -45,6 +45,8 @@
   // file is unhashed.
   base::Value::Dict ProcessExtension(const base::FilePath& root_dir);
 
+  void SetMaxFilesToProcessForTest(int64_t max_files_to_process);
+  void SetMaxFileSizeBytesForTest(int64_t max_file_size);
   void SetMaxFilesToReadForTest(int64_t max_files_to_read);
 
  protected:
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_file_processor_unittest.cc b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_file_processor_unittest.cc
index 779710a..1c1e28d 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_file_processor_unittest.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_file_processor_unittest.cc
@@ -9,9 +9,7 @@
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/thread_pool.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/threading/sequence_bound.h"
-#include "components/safe_browsing/core/common/features.h"
 #include "content/public/test/browser_task_environment.h"
 #include "crypto/sha2.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -55,13 +53,7 @@
 class ExtensionTelemetryFileProcessorTest : public ::testing::Test {
  public:
   ExtensionTelemetryFileProcessorTest()
-      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
-    feature_list_.InitWithFeaturesAndParameters(
-        /*enabled_features=*/{{kExtensionTelemetryFileData,
-                               {{"MaxFilesToProcess", "50"},
-                                {"MaxFileSizeBytes", "102400"}}}},
-        /*disabled_features=*/{});
-  }
+      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
 
   void SetUp() override {
     // Set up temp directory.
@@ -72,7 +64,11 @@
     extension_root_dir_ = temp_dir_.GetPath().AppendASCII(kExtensionId);
     ASSERT_TRUE(base::CreateDirectory(extension_root_dir_));
 
-    InitProcessor();
+    processor_ = base::SequenceBound<ExtensionTelemetryFileProcessor>(
+        base::ThreadPool::CreateSequencedTaskRunner(
+            {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+             base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}));
+    task_environment_.RunUntilIdle();
   }
 
   void SetUpExtensionFiles() {
@@ -98,14 +94,6 @@
     WriteExtensionFile(extension_sub_dir_, kCSSFile2, kCSSFile2);
   }
 
-  void InitProcessor() {
-    processor_ = base::SequenceBound<ExtensionTelemetryFileProcessor>(
-        base::ThreadPool::CreateSequencedTaskRunner(
-            {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-             base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}));
-    task_environment_.RunUntilIdle();
-  }
-
   void CallbackHelper(base::Value::Dict data) {
     extensions_data_ = std::move(data);
   }
@@ -120,7 +108,6 @@
   base::FilePath extension_root_dir_;
   base::FilePath extension_sub_dir_;
 
-  base::test::ScopedFeatureList feature_list_;
   base::SequenceBound<safe_browsing::ExtensionTelemetryFileProcessor>
       processor_;
   content::BrowserTaskEnvironment task_environment_;
@@ -303,13 +290,9 @@
 TEST_F(ExtensionTelemetryFileProcessorTest, EnforcesMaxNumFilesLimit) {
   SetUpExtensionFiles();
   // Set max_files_to_process to 4.
-  feature_list_.Reset();
-  feature_list_.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{kExtensionTelemetryFileData,
-                             {{"MaxFilesToProcess", "4"},
-                              {"MaxFileSizeBytes", "102400"}}}},
-      /*disabled_features=*/{});
-  InitProcessor();
+  processor_
+      .AsyncCall(&ExtensionTelemetryFileProcessor::SetMaxFilesToProcessForTest)
+      .WithArgs(4);
 
   auto callback =
       base::BindOnce(&ExtensionTelemetryFileProcessorTest::CallbackHelper,
@@ -339,13 +322,12 @@
 
   // Set max_file_size to 50 bytes.
   int64_t max_file_size = 50;
-  feature_list_.Reset();
-  feature_list_.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{kExtensionTelemetryFileData,
-                             {{"MaxFilesToProcess", "50"},
-                              {"MaxFileSizeBytes", "50"}}}},
-      /*disabled_features=*/{});
-  InitProcessor();
+  processor_
+      .AsyncCall(&ExtensionTelemetryFileProcessor::SetMaxFilesToProcessForTest)
+      .WithArgs(50);
+  processor_
+      .AsyncCall(&ExtensionTelemetryFileProcessor::SetMaxFileSizeBytesForTest)
+      .WithArgs(max_file_size);
 
   auto callback =
       base::BindOnce(&ExtensionTelemetryFileProcessorTest::CallbackHelper,
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
index 73942c3..1fed28e 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
@@ -113,6 +113,16 @@
 // Delay before the Telemetry Service checks its last upload time.
 base::TimeDelta kStartupUploadCheckDelaySeconds = base::Seconds(15);
 
+// Interval for extension telemetry to start another off-store extension
+// file data collection process.
+base::TimeDelta kOffstoreFileDataCollectionIntervalSeconds =
+    base::Seconds(7200);
+
+// Initial delay for extension telemetry to start collecting
+// off-store extension file data.
+base::TimeDelta kOffstoreFileDataCollectionStartupDelaySeconds =
+    base::Seconds(300);
+
 // Limit the off-store file data collection duration.
 base::TimeDelta kOffstoreFileDataCollectionDurationLimitSeconds =
     base::Seconds(60);
@@ -399,15 +409,13 @@
       config_manager_->LoadConfig();
     }
 
-    if (base::FeatureList::IsEnabled(kExtensionTelemetryFileData)) {
       file_processor_ = base::SequenceBound<ExtensionTelemetryFileProcessor>(
           base::ThreadPool::CreateSequencedTaskRunner(
               {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
                base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}));
       offstore_file_data_collection_timer_.Start(
-          FROM_HERE,
-          base::Seconds(kExtensionTelemetryFileDataStartupDelaySeconds.Get()),
-          this, &ExtensionTelemetryService::StartOffstoreFileDataCollection);
+          FROM_HERE, kOffstoreFileDataCollectionStartupDelaySeconds, this,
+          &ExtensionTelemetryService::StartOffstoreFileDataCollection);
       if (base::FeatureList::IsEnabled(
               kExtensionTelemetryFileDataForCommandLineExtensions)) {
         base::ThreadPool::PostTaskAndReplyWithResult(
@@ -417,7 +425,6 @@
                                OnCommandLineExtensionsInfoCollected,
                            weak_factory_.GetWeakPtr()));
       }
-    }
 
     if (current_reporting_interval_.is_positive()) {
       int max_files_supported =
@@ -1030,7 +1037,6 @@
   extension_info->set_disable_reasons(
       extension_prefs_->GetDisableReasons(extension.id()));
 
-  if (base::FeatureList::IsEnabled(kExtensionTelemetryFileData)) {
     absl::optional<OffstoreExtensionFileData> offstore_file_data =
         RetrieveOffstoreFileDataForReport(extension.id());
 
@@ -1041,7 +1047,6 @@
         extension_info->mutable_file_infos()->Add(std::move(file_info));
       }
     }
-  }
 
   return extension_info;
 }
@@ -1168,10 +1173,8 @@
       (base::TimeTicks::Now() - offstore_file_data_collection_start_time_) >=
           offstore_file_data_collection_duration_limit_) {
     offstore_file_data_collection_timer_.Start(
-        FROM_HERE,
-        base::Seconds(
-            kExtensionTelemetryFileDataCollectionIntervalSeconds.Get()),
-        this, &ExtensionTelemetryService::StartOffstoreFileDataCollection);
+        FROM_HERE, kOffstoreFileDataCollectionIntervalSeconds, this,
+        &ExtensionTelemetryService::StartOffstoreFileDataCollection);
 
     // Record only if there are off-store extensions installed.
     if (!offstore_extension_dirs_.empty()) {
@@ -1252,4 +1255,14 @@
       blocklist_states);
 }
 
+base::TimeDelta
+ExtensionTelemetryService::GetOffstoreFileDataCollectionStartupDelaySeconds() {
+  return kOffstoreFileDataCollectionStartupDelaySeconds;
+}
+
+base::TimeDelta
+ExtensionTelemetryService::GetOffstoreFileDataCollectionIntervalSeconds() {
+  return kOffstoreFileDataCollectionIntervalSeconds;
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
index 33677132..522ef964 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
@@ -112,6 +112,9 @@
   // KeyedService:
   void Shutdown() override;
 
+  base::TimeDelta GetOffstoreFileDataCollectionStartupDelaySeconds();
+  base::TimeDelta GetOffstoreFileDataCollectionIntervalSeconds();
+
  private:
   // Called when prefs that affect extension telemetry service are changed.
   void OnPrefChanged();
@@ -314,9 +317,9 @@
   base::flat_set<OffstoreExtensionFileDataContext>
       offstore_extension_file_data_contexts_;
   // Used to start the initial offstore extension file data collection based on
-  // |kExtensionTelemetryFileDataStartupDelaySeconds| - default: 5 mins.
+  // |kOffstoreFileDataCollectionStartupDelaySeconds| - default: 5 mins.
   // Then repeat the collection based on
-  // |kExtensionTelemetryFileDataProcessIntervalSeconds| - default: 2 hours.
+  // |kOffstoreFileDataCollectionIntervalSeconds| - default: 2 hours.
   base::OneShotTimer offstore_file_data_collection_timer_;
   base::TimeTicks offstore_file_data_collection_start_time_;
   base::TimeDelta offstore_file_data_collection_duration_limit_;
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_unittest.cc b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_unittest.cc
index 12c29e8..0b4fca1e 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_unittest.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_unittest.cc
@@ -66,11 +66,6 @@
 constexpr char kManifestFile[] = "manifest.json";
 constexpr char kJavaScriptFile[] = "js_file.js";
 
-// Delay (5 minutes) to start the initial offstore file data collection.
-constexpr int kFileDataStartUpDelaySeconds = 300;
-// Interval (2 hours) to repeat the offstore file data collection.
-constexpr int kFileDataCollectionIntervalSeconds = 300;
-
 }  // namespace
 
 class ExtensionTelemetryServiceTest : public ::testing::Test {
@@ -688,14 +683,8 @@
 }
 
 TEST_F(ExtensionTelemetryServiceTest, FileData_ProcessesOffstoreExtensions) {
-  // Enable |kExtensionTelemetryFileData| feature and process.
-  telemetry_service_->SetEnabled(false);
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      {kExtensionTelemetryFileData},
-      {{"StartupDelaySeconds",
-        base::NumberToString(kFileDataStartUpDelaySeconds)}});
-  telemetry_service_->SetEnabled(true);
-  task_environment_.FastForwardBy(base::Seconds(kFileDataStartUpDelaySeconds));
+  task_environment_.FastForwardBy(
+      telemetry_service_->GetOffstoreFileDataCollectionStartupDelaySeconds());
   task_environment_.RunUntilIdle();
 
   auto& file_data_dict =
@@ -742,14 +731,8 @@
                                         ManifestLocation::kExternalComponent,
                                         Extension::NO_FLAGS);
 
-  // Enable |kExtensionTelemetryFileData| feature and process.
-  telemetry_service_->SetEnabled(false);
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      {kExtensionTelemetryFileData},
-      {{"StartupDelaySeconds",
-        base::NumberToString(kFileDataStartUpDelaySeconds)}});
-  telemetry_service_->SetEnabled(true);
-  task_environment_.FastForwardBy(base::Seconds(kFileDataStartUpDelaySeconds));
+  task_environment_.FastForwardBy(
+      telemetry_service_->GetOffstoreFileDataCollectionStartupDelaySeconds());
   task_environment_.RunUntilIdle();
 
   auto& file_data_dict =
@@ -765,20 +748,16 @@
 
 TEST_F(ExtensionTelemetryServiceTest, FileData_RemovesStaleExtensionFromPref) {
   // Process extension 0 and 1 and save to prefs.
-  telemetry_service_->SetEnabled(false);
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      {kExtensionTelemetryFileData},
-      {{"StartupDelaySeconds",
-        base::NumberToString(kFileDataStartUpDelaySeconds)}});
-  telemetry_service_->SetEnabled(true);
-  task_environment_.FastForwardBy(base::Seconds(kFileDataStartUpDelaySeconds));
+  task_environment_.FastForwardBy(
+      telemetry_service_->GetOffstoreFileDataCollectionStartupDelaySeconds());
   task_environment_.RunUntilIdle();
 
   UnregisterExtensionWithExtensionService(kExtensionId[0]);
 
   telemetry_service_->SetEnabled(false);
   telemetry_service_->SetEnabled(true);
-  task_environment_.FastForwardBy(base::Seconds(kFileDataStartUpDelaySeconds));
+  task_environment_.FastForwardBy(
+      telemetry_service_->GetOffstoreFileDataCollectionStartupDelaySeconds());
   task_environment_.RunUntilIdle();
 
   auto& file_data_dict =
@@ -792,15 +771,8 @@
 TEST_F(ExtensionTelemetryServiceTest,
        FileData_ProcessesEachExtensionOncePerDay) {
   // Process extension 0 and 1 and save to prefs.
-  telemetry_service_->SetEnabled(false);
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      {kExtensionTelemetryFileData},
-      {{"StartupDelaySeconds",
-        base::NumberToString(kFileDataStartUpDelaySeconds)},
-       {"CollectionIntervalSeconds",
-        base::NumberToString(kFileDataCollectionIntervalSeconds)}});
-  telemetry_service_->SetEnabled(true);
-  task_environment_.FastForwardBy(base::Seconds(kFileDataStartUpDelaySeconds));
+  task_environment_.FastForwardBy(
+      telemetry_service_->GetOffstoreFileDataCollectionStartupDelaySeconds());
   task_environment_.RunUntilIdle();
 
   // Save first processed timestamp.
@@ -815,7 +787,7 @@
                                         ManifestLocation::kUnpacked,
                                         Extension::NO_FLAGS);
   task_environment_.FastForwardBy(
-      base::Seconds(kFileDataCollectionIntervalSeconds));
+      telemetry_service_->GetOffstoreFileDataCollectionIntervalSeconds());
   task_environment_.RunUntilIdle();
 
   // Extensions 0 and 1 match first processed timestamp.
@@ -849,13 +821,8 @@
                                std::move(empty_timestamps_dict));
 
   // Process extension 0 and 1 and save to prefs.
-  telemetry_service_->SetEnabled(false);
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      {kExtensionTelemetryFileData},
-      {{"StartupDelaySeconds",
-        base::NumberToString(kFileDataStartUpDelaySeconds)}});
-  telemetry_service_->SetEnabled(true);
-  task_environment_.FastForwardBy(base::Seconds(kFileDataStartUpDelaySeconds));
+  task_environment_.FastForwardBy(
+      telemetry_service_->GetOffstoreFileDataCollectionStartupDelaySeconds());
   task_environment_.RunUntilIdle();
 
   auto& file_data_dict =
@@ -878,14 +845,8 @@
 
 TEST_F(ExtensionTelemetryServiceTest,
        FileData_AttachesOffstoreFileDataToReport) {
-  // Enable |kExtensionTelemetryFileData| feature and starts collection.
-  telemetry_service_->SetEnabled(false);
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      {kExtensionTelemetryFileData},
-      {{"StartupDelaySeconds",
-        base::NumberToString(kFileDataStartUpDelaySeconds)}});
-  telemetry_service_->SetEnabled(true);
-  task_environment_.FastForwardBy(base::Seconds(kFileDataStartUpDelaySeconds));
+  task_environment_.FastForwardBy(
+      telemetry_service_->GetOffstoreFileDataCollectionStartupDelaySeconds());
   task_environment_.RunUntilIdle();
 
   std::unique_ptr<TelemetryReport> telemetry_report_pb = GetTelemetryReport();
@@ -924,10 +885,7 @@
   // Enable necessary features.
   scoped_feature_list.InitWithFeaturesAndParameters(
       // enabled_features
-      {{kExtensionTelemetryFileDataForCommandLineExtensions, {}},
-       {kExtensionTelemetryFileData,
-        {{"StartupDelaySeconds",
-          base::NumberToString(kFileDataStartUpDelaySeconds)}}}},
+      {{kExtensionTelemetryFileDataForCommandLineExtensions, {}}},
       // disabled_features
       {});
   // Create a commandline extension, set up the --load-extension commandline
@@ -936,7 +894,8 @@
   base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
       extensions::switches::kLoadExtension, path);
   telemetry_service_->SetEnabled(true);
-  task_environment_.FastForwardBy(base::Seconds(kFileDataStartUpDelaySeconds));
+  task_environment_.FastForwardBy(
+      telemetry_service_->GetOffstoreFileDataCollectionStartupDelaySeconds());
   task_environment_.RunUntilIdle();
 
   // Generate and verify telemetry report contents.
@@ -968,14 +927,8 @@
   RegisterExtensionWithExtensionService(kExtensionId[2], kExtensionName[2],
                                         ManifestLocation::kInternal,
                                         Extension::FROM_WEBSTORE);
-  // Enable |kExtensionTelemetryFileData| feature and starts collection.
-  telemetry_service_->SetEnabled(false);
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      {kExtensionTelemetryFileData},
-      {{"StartupDelaySeconds",
-        base::NumberToString(kFileDataStartUpDelaySeconds)}});
-  telemetry_service_->SetEnabled(true);
-  task_environment_.FastForwardBy(base::Seconds(kFileDataStartUpDelaySeconds));
+  task_environment_.FastForwardBy(
+      telemetry_service_->GetOffstoreFileDataCollectionStartupDelaySeconds());
   task_environment_.RunUntilIdle();
 
   std::unique_ptr<TelemetryReport> telemetry_report_pb = GetTelemetryReport();
@@ -999,14 +952,8 @@
 }
 
 TEST_F(ExtensionTelemetryServiceTest, FileData_HandlesEmptyFileDataInPrefs) {
-  // Enable |kExtensionTelemetryFileData| feature and starts collection.
-  telemetry_service_->SetEnabled(false);
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      {kExtensionTelemetryFileData},
-      {{"StartupDelaySeconds",
-        base::NumberToString(kFileDataStartUpDelaySeconds)}});
-  telemetry_service_->SetEnabled(true);
-  task_environment_.FastForwardBy(base::Seconds(kFileDataStartUpDelaySeconds));
+  task_environment_.FastForwardBy(
+      telemetry_service_->GetOffstoreFileDataCollectionStartupDelaySeconds());
   task_environment_.RunUntilIdle();
 
   // Set up pref dict:
@@ -1035,17 +982,11 @@
 
 TEST_F(ExtensionTelemetryServiceTest,
        FileData_EnforcesCollectionDurationLimit) {
-  // Enable |kExtensionTelemetryFileData| feature and starts collection.
-  telemetry_service_->SetEnabled(false);
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      {kExtensionTelemetryFileData},
-      {{"StartupDelaySeconds",
-        base::NumberToString(kFileDataStartUpDelaySeconds)}});
   // Set collection duration limit to 0 milliseconds.
   telemetry_service_->offstore_file_data_collection_duration_limit_ =
       base::Milliseconds(0);
-  telemetry_service_->SetEnabled(true);
-  task_environment_.FastForwardBy(base::Seconds(kFileDataStartUpDelaySeconds));
+  task_environment_.FastForwardBy(
+      telemetry_service_->GetOffstoreFileDataCollectionStartupDelaySeconds());
   task_environment_.RunUntilIdle();
 
   std::unique_ptr<TelemetryReport> telemetry_report_pb = GetTelemetryReport();
diff --git a/chrome/browser/safe_browsing/url_lookup_service_factory.cc b/chrome/browser/safe_browsing/url_lookup_service_factory.cc
index d1db370..c495e668 100644
--- a/chrome/browser/safe_browsing/url_lookup_service_factory.cc
+++ b/chrome/browser/safe_browsing/url_lookup_service_factory.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/sync/sync_service_factory.h"
 #include "components/safe_browsing/buildflags.h"
 #include "components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.h"
+#include "components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h"
 #include "components/safe_browsing/core/browser/realtime/url_lookup_service.h"
 #include "components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher.h"
 #include "components/safe_browsing/core/browser/sync/sync_utils.h"
@@ -88,7 +89,8 @@
                           IdentityManagerFactory::GetForProfile(profile)),
       profile->IsOffTheRecord(), g_browser_process->variations_service(),
       SafeBrowsingNavigationObserverManagerFactory::GetForBrowserContext(
-          profile));
+          profile),
+      WebUIInfoSingleton::GetInstance());
 }
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/safety_check/android/BUILD.gn b/chrome/browser/safety_check/android/BUILD.gn
index 45106d11..14be878 100644
--- a/chrome/browser/safety_check/android/BUILD.gn
+++ b/chrome/browser/safety_check/android/BUILD.gn
@@ -120,6 +120,7 @@
     "//components/user_prefs/android:java",
 
     # Robolectric needs the full PasswordCheckFactory implementation.
+    "//chrome/browser/loading_modal/android:java",
     "//chrome/browser/password_check/android/internal:internal_factory_java",
     "//chrome/browser/password_manager/android:java",
     "//chrome/browser/password_manager/android:settings_interface_java",
@@ -131,9 +132,11 @@
     "//chrome/test/android:chrome_java_unit_test_support",
     "//components/browser_ui/settings/android:java",
     "//components/commerce/core:proto_java",
+    "//components/password_manager/core/browser:password_manager_java_enums",
     "//components/sync/android:sync_java",
     "//content/public/android:content_full_java",
     "//third_party/android_deps:guava_android_java",
+    "//third_party/androidx:androidx_preference_preference_java",
     "//third_party/androidx:androidx_test_core_java",
     "//third_party/jni_zero:jni_zero_java",
     "//third_party/junit:junit",
diff --git a/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediator.java b/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediator.java
index a5ba9b9..6c16d01 100644
--- a/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediator.java
+++ b/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediator.java
@@ -209,9 +209,11 @@
             SyncConsentActivityLauncher signinLauncher,
             SyncService syncService,
             PasswordStoreBridge bridge,
-            Handler handler) {
+            Handler handler,
+            ObservableSupplier<ModalDialogManager> modalDialogManagerSupplier) {
         this(model, client, settingsLauncher, signinLauncher, syncService, handler);
         mPasswordStoreBridge = bridge;
+        mModalDialogManagerSupplier = modalDialogManagerSupplier;
     }
 
     SafetyCheckMediator(
@@ -620,11 +622,17 @@
                             PasswordCheckFactory.getOrCreate(mSettingsLauncher)
                                     .showUi(p.getContext(), PasswordCheckReferrer.SAFETY_CHECK);
                         } else {
+                            String account =
+                                    PasswordManagerHelper.hasChosenToSyncPasswords(mSyncService)
+                                            ? CoreAccountInfo.getEmailFrom(
+                                                    mSyncService.getAccountInfo())
+                                            : null;
                             PasswordManagerHelper.showPasswordCheckup(
                                     p.getContext(),
                                     PasswordCheckReferrer.SAFETY_CHECK,
                                     mSyncService,
-                                    mModalDialogManagerSupplier);
+                                    mModalDialogManagerSupplier,
+                                    account);
                         }
                         return true;
                     };
diff --git a/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediatorTest.java b/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediatorTest.java
index 9d99e79..601c93c 100644
--- a/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediatorTest.java
+++ b/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediatorTest.java
@@ -10,12 +10,15 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static org.chromium.chrome.browser.password_manager.PasswordCheckReferrer.SAFETY_CHECK;
 import static org.chromium.chrome.browser.safety_check.SafetyCheckProperties.COMPROMISED_PASSWORDS;
 import static org.chromium.chrome.browser.safety_check.SafetyCheckProperties.PASSWORDS_STATE;
 import static org.chromium.chrome.browser.safety_check.SafetyCheckProperties.SAFE_BROWSING_STATE;
@@ -23,6 +26,7 @@
 
 import android.os.Handler;
 
+import androidx.preference.Preference;
 import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
@@ -42,8 +46,10 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.shared_preferences.SharedPreferencesManager;
+import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRule;
 import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.loading_modal.LoadingModalDialogCoordinator;
 import org.chromium.chrome.browser.password_check.PasswordCheck;
 import org.chromium.chrome.browser.password_check.PasswordCheckFactory;
 import org.chromium.chrome.browser.password_check.PasswordCheckUIStatus;
@@ -75,6 +81,7 @@
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.components.user_prefs.UserPrefsJni;
 import org.chromium.content_public.browser.BrowserContextHandle;
+import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modelutil.PropertyModel;
 
 import java.lang.ref.WeakReference;
@@ -124,8 +131,8 @@
 
     // TODO(crbug.com/1346235): Use fake instead of mocking
     @Mock private PasswordManagerBackendSupportHelper mBackendSupportHelperMock;
-
     @Mock private PasswordManagerUtilBridge.Natives mPasswordManagerUtilBridgeNativeMock;
+    @Mock LoadingModalDialogCoordinator mLoadingModalDialogCoordinator;
 
     private SafetyCheckMediator mMediator;
 
@@ -139,6 +146,13 @@
 
     private boolean mUseGmsApi;
 
+    private ModalDialogManager mModalDialogManager;
+
+    private final ObservableSupplierImpl<ModalDialogManager> mModalDialogManagerSupplier =
+            new ObservableSupplierImpl<>();
+
+    private LoadingModalDialogCoordinator.Observer mLoadingDialogCoordinatorObserver;
+
     @Parameters
     public static Collection<Object[]> data() {
         return Arrays.asList(new Object[][] {{false}, {true}});
@@ -270,6 +284,10 @@
         when(mSyncService.hasSyncConsent()).thenReturn(true);
         when(mSyncService.getAccountInfo())
                 .thenReturn(CoreAccountInfo.createFromEmailAndGaiaId(TEST_EMAIL_ADDRESS, "0"));
+
+        // TODO(crbug.com/1511255): Parametrize the tests in SafetyCheckMediatorTest for local and
+        // account storage.
+        // This will no longer be true once the local and account store split happens.
         if (mUseGmsApi) {
             when(mSyncService.getSelectedTypes())
                     .thenReturn(CollectionUtil.newHashSet(UserSelectableType.PASSWORDS));
@@ -319,7 +337,8 @@
                             mSigninLauncher,
                             mSyncService,
                             mPasswordStoreBridge,
-                            mHandler);
+                            mHandler,
+                            mModalDialogManagerSupplier);
         } else {
             PasswordCheckFactory.setPasswordCheckForTesting(mPasswordCheck);
             mMediator =
@@ -330,7 +349,8 @@
                             mSigninLauncher,
                             mSyncService,
                             null,
-                            mHandler);
+                            mHandler,
+                            mModalDialogManagerSupplier);
         }
 
         // Execute any delayed tasks immediately.
@@ -346,6 +366,19 @@
         doReturn(true).when(mSafetyCheckBridge).userSignedIn(any(BrowserContextHandle.class));
         // Reset the histogram count.
         UmaRecorderHolder.resetForTesting();
+
+        mModalDialogManager =
+                new ModalDialogManager(
+                        mock(ModalDialogManager.Presenter.class),
+                        ModalDialogManager.ModalDialogType.APP);
+        mModalDialogManagerSupplier.set(mModalDialogManager);
+        doAnswer(
+                        invocation -> {
+                            mLoadingDialogCoordinatorObserver = invocation.getArgument(0);
+                            return null;
+                        })
+                .when(mLoadingModalDialogCoordinator)
+                .addObserver(any(LoadingModalDialogCoordinator.Observer.class));
     }
 
     @Test
@@ -843,4 +876,71 @@
         mMediator.destroy();
         failBreachedPasswordsFetch();
     }
+
+    @Test
+    public void testClickListenerLeadsToUPMAccountPasswordCheckup() {
+        // Order: initial state -> safety check triggered -> check done -> load completed.
+        mMediator.setInitialState();
+        assertEquals(PasswordsState.CHECKING, mModel.get(PASSWORDS_STATE));
+
+        captureRunPasswordCheckCallback();
+        mMediator.performSafetyCheck();
+        assertEquals(PasswordsState.CHECKING, mModel.get(PASSWORDS_STATE));
+
+        captureBreachPasswordsCallback();
+        setPasswordCheckResult(/* hasError= */ false);
+        assertEquals(PasswordsState.CHECKING, mModel.get(PASSWORDS_STATE));
+
+        fetchSavedPasswords(20);
+        assertEquals(PasswordsState.CHECKING, mModel.get(PASSWORDS_STATE));
+
+        fetchBreachedPasswords(18);
+        assertEquals(PasswordsState.COMPROMISED_EXIST, mModel.get(PASSWORDS_STATE));
+
+        Preference.OnPreferenceClickListener listener =
+                (Preference.OnPreferenceClickListener)
+                        mModel.get(SafetyCheckProperties.PASSWORDS_CLICK_LISTENER);
+        listener.onPreferenceClick(new Preference(ContextUtils.getApplicationContext()));
+
+        verify(mPasswordCheckupHelper, times(mUseGmsApi ? 1 : 0))
+                .getPasswordCheckupIntent(
+                        eq(SAFETY_CHECK), eq(Optional.of(TEST_EMAIL_ADDRESS)), any(), any());
+    }
+
+    @Test
+    public void testClickListenerLeadsToUPMLocalPasswordCheckup() {
+        // TODO(crbug.com/1511255): Parametrize the tests in SafetyCheckMediatorTest for local and
+        // account storage.
+        // These behaviours are set here again because the tests are currently not parametrised in
+        // a way to support UPM for non password syncing users.
+        when(mPasswordManagerUtilBridgeNativeMock.canUseUPMBackend(false, mPrefService))
+                .thenReturn(mUseGmsApi);
+        when(mSyncService.getSelectedTypes()).thenReturn(new HashSet<>());
+
+        // Order: initial state -> safety check triggered -> check done -> load completed.
+        mMediator.setInitialState();
+        assertEquals(PasswordsState.CHECKING, mModel.get(PASSWORDS_STATE));
+
+        captureRunPasswordCheckCallback();
+        mMediator.performSafetyCheck();
+        assertEquals(PasswordsState.CHECKING, mModel.get(PASSWORDS_STATE));
+
+        captureBreachPasswordsCallback();
+        setPasswordCheckResult(/* hasError= */ false);
+        assertEquals(PasswordsState.CHECKING, mModel.get(PASSWORDS_STATE));
+
+        fetchSavedPasswords(20);
+        assertEquals(PasswordsState.CHECKING, mModel.get(PASSWORDS_STATE));
+
+        fetchBreachedPasswords(18);
+        assertEquals(PasswordsState.COMPROMISED_EXIST, mModel.get(PASSWORDS_STATE));
+
+        Preference.OnPreferenceClickListener listener =
+                (Preference.OnPreferenceClickListener)
+                        mModel.get(SafetyCheckProperties.PASSWORDS_CLICK_LISTENER);
+        listener.onPreferenceClick(new Preference(ContextUtils.getApplicationContext()));
+
+        verify(mPasswordCheckupHelper, times(mUseGmsApi ? 1 : 0))
+                .getPasswordCheckupIntent(eq(SAFETY_CHECK), eq(Optional.empty()), any(), any());
+    }
 }
diff --git a/chrome/browser/search_engine_choice/search_engine_choice_client_side_trial.h b/chrome/browser/search_engine_choice/search_engine_choice_client_side_trial.h
index 237bc97f..2c0fd98 100644
--- a/chrome/browser/search_engine_choice/search_engine_choice_client_side_trial.h
+++ b/chrome/browser/search_engine_choice/search_engine_choice_client_side_trial.h
@@ -6,11 +6,6 @@
 #define CHROME_BROWSER_SEARCH_ENGINE_CHOICE_SEARCH_ENGINE_CHOICE_CLIENT_SIDE_TRIAL_H_
 
 #include "base/metrics/field_trial.h"
-#include "components/signin/public/base/signin_buildflags.h"
-
-#if !BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-#error "Unsupported platform"
-#endif
 
 namespace base {
 class FeatureList;
diff --git a/chrome/browser/search_engine_choice/search_engine_choice_client_side_trial_unittest.cc b/chrome/browser/search_engine_choice/search_engine_choice_client_side_trial_unittest.cc
index 6bada79..7d53a99 100644
--- a/chrome/browser/search_engine_choice/search_engine_choice_client_side_trial_unittest.cc
+++ b/chrome/browser/search_engine_choice/search_engine_choice_client_side_trial_unittest.cc
@@ -26,7 +26,6 @@
 #include "components/prefs/testing_pref_service.h"
 #include "components/search_engines/search_engines_pref_names.h"
 #include "components/search_engines/search_engines_switches.h"
-#include "components/signin/public/base/signin_buildflags.h"
 #include "components/signin/public/base/signin_switches.h"
 #include "components/variations/active_field_trials.h"
 #include "components/variations/synthetic_trial_registry.h"
@@ -36,10 +35,6 @@
 #include "components/version_info/channel.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if !BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-#error "Unsupported platform"
-#endif
-
 namespace {
 
 // TODO(b/313407392): Move the class to some utils file.
@@ -183,7 +178,7 @@
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)
   EXPECT_TRUE(base::FieldTrialList::IsTrialActive("WaffleStudy"));
-  
+
   std::string expected_group_name =
       GetParam().expect_study_enabled
           ? GetParam().expect_feature_enabled
diff --git a/chrome/browser/search_engine_choice/search_engine_choice_profile_tagger.h b/chrome/browser/search_engine_choice/search_engine_choice_profile_tagger.h
index 57eafe5a..714511cd 100644
--- a/chrome/browser/search_engine_choice/search_engine_choice_profile_tagger.h
+++ b/chrome/browser/search_engine_choice/search_engine_choice_profile_tagger.h
@@ -7,11 +7,6 @@
 
 #include "base/scoped_observation.h"
 #include "chrome/browser/profiles/profile_manager_observer.h"
-#include "components/signin/public/base/signin_buildflags.h"
-
-#if !BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-#error Not supported for this platform.
-#endif
 
 class ProfileManager;
 
diff --git a/chrome/browser/search_engine_choice/search_engine_choice_service.h b/chrome/browser/search_engine_choice/search_engine_choice_service.h
index dfdffa98..f525bc0 100644
--- a/chrome/browser/search_engine_choice/search_engine_choice_service.h
+++ b/chrome/browser/search_engine_choice/search_engine_choice_service.h
@@ -20,7 +20,6 @@
 #include "components/search_engines/template_url_service.h"
 
 class Browser;
-class BrowserListObserver;
 
 namespace search_engines {
 
diff --git a/chrome/browser/search_engine_choice/search_engine_choice_service_factory.cc b/chrome/browser/search_engine_choice/search_engine_choice_service_factory.cc
index b05b1c43..7ca98e2 100644
--- a/chrome/browser/search_engine_choice/search_engine_choice_service_factory.cc
+++ b/chrome/browser/search_engine_choice/search_engine_choice_service_factory.cc
@@ -130,6 +130,9 @@
 std::unique_ptr<KeyedService>
 SearchEngineChoiceServiceFactory::BuildServiceInstanceForBrowserContext(
     content::BrowserContext* context) const {
+#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(CHROME_FOR_TESTING)
+  return nullptr;
+#else
   if (!g_is_chrome_build) {
     return nullptr;
   }
@@ -148,4 +151,5 @@
       CHECK_DEREF(TemplateURLServiceFactory::GetForProfile(&profile));
   return std::make_unique<SearchEngineChoiceService>(profile,
                                                      template_url_service);
+#endif
 }
diff --git a/chrome/browser/search_engine_choice/search_engine_choice_service_unittest.cc b/chrome/browser/search_engine_choice/search_engine_choice_service_unittest.cc
index 906bb90..6728bdee 100644
--- a/chrome/browser/search_engine_choice/search_engine_choice_service_unittest.cc
+++ b/chrome/browser/search_engine_choice/search_engine_choice_service_unittest.cc
@@ -58,6 +58,7 @@
   std::unique_ptr<base::AutoReset<bool>> scoped_chrome_build_override_;
 };
 
+#if !BUILDFLAG(CHROME_FOR_TESTING)
 TEST_F(SearchEngineChoiceServiceTest, HandleLearnMoreLinkClicked) {
   SearchEngineChoiceService* search_engine_choice_service =
       SearchEngineChoiceServiceFactory::GetForProfile(profile());
@@ -128,3 +129,10 @@
           kProfileCreationDefaultWasSet,
       1);
 }
+#else
+TEST_F(SearchEngineChoiceServiceTest, ServiceNotInitializedInChromeForTesting) {
+  SearchEngineChoiceService* search_engine_choice_service =
+      SearchEngineChoiceServiceFactory::GetForProfile(profile());
+  ASSERT_EQ(search_engine_choice_service, nullptr);
+}
+#endif
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.cc b/chrome/browser/signin/dice_web_signin_interceptor.cc
index b6053f0..ac3acfaf 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor.cc
@@ -971,13 +971,11 @@
       profiles::GetDefaultNameForNewSignedInProfile(account_info);
   ProfilePresets profile_presets(profile_color);
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
   if (search_engines::IsChoiceScreenFlagEnabled(
           search_engines::ChoicePromo::kAny)) {
     profile_presets.search_engine_choice_data =
         SearchEngineChoiceService::GetChoiceDataFromProfile(*profile_);
   }
-#endif
 
   DCHECK(!state_->dice_signed_in_profile_creator_);
   // Unretained is fine because the profile creator is owned by this.
@@ -1076,7 +1074,6 @@
             ->BuildAutogeneratedThemeFromColor(profile_presets->profile_color);
       }
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
       // The new profile inherits the default search provider and the search
       // engine choice timestamp from the previous profile.
       if (search_engines::IsChoiceScreenFlagEnabled(
@@ -1084,7 +1081,6 @@
         SearchEngineChoiceService::UpdateProfileFromChoiceData(
             *new_profile, profile_presets->search_engine_choice_data);
       }
-#endif
     }
 
     // TODO(crbug/1450011): Move this to DiceSignedInProfileCreator when
diff --git a/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc b/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
index 594b8ab5..da9ca7b7 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
@@ -72,9 +72,7 @@
 
 namespace {
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
 const char kCustomSearchEngineDomain[] = "bar.com";
-#endif
 
 // Fake response for OAuth multilogin.
 const char kMultiloginSuccessResponse[] =
@@ -214,7 +212,6 @@
                                       outcome, 1);
 }
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
 void SetUserSelectedDefaultSearchProvider(
     TemplateURLService* template_url_service) {
   TemplateURLData data;
@@ -232,7 +229,6 @@
       template_url_service->Add(std::make_unique<TemplateURL>(data));
   template_url_service->SetUserSelectedDefaultSearchProvider(template_url);
 }
-#endif
 
 }  // namespace
 
@@ -1854,7 +1850,6 @@
 
   SetupGaiaResponses();
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
   int64_t search_engine_choice_timestamp =
       base::Time::Now().ToDeltaSinceWindowsEpoch().InSeconds();
   const char kChoiceVersion[] = "1.2.3.4";
@@ -1871,7 +1866,6 @@
         TemplateURLServiceFactory::GetForProfile(browser()->profile());
     SetUserSelectedDefaultSearchProvider(template_url_service);
   }
-#endif
 
   // Add a tab.
   GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
@@ -1911,7 +1905,6 @@
                     ->UsingAutogeneratedTheme());
   }
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
   if (WithSearchEngineChoiceEnabled()) {
     PrefService* new_pref_service = new_profile->GetPrefs();
     EXPECT_EQ(new_pref_service->GetInt64(
@@ -1927,7 +1920,6 @@
         new_template_url_service->GetDefaultSearchProvider()->short_name(),
         base::UTF8ToUTF16(std::string(kCustomSearchEngineDomain)));
   }
-#endif
 
   // A browser has been created for the new profile and the tab was moved there.
   Browser* added_browser = ui_test_utils::WaitForBrowserToOpen();
diff --git a/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc b/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc
index ce5b56d..40fa74e 100644
--- a/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc
+++ b/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc
@@ -31,7 +31,6 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/storage_partition.h"
-#include "content/public/common/content_features.h"
 #include "net/base/schemeful_site.h"
 #include "net/cookies/cookie_setting_override.h"
 #include "net/cookies/site_for_cookies.h"
@@ -64,9 +63,8 @@
 }
 
 bool NeedsFirstPartySetMetadata() {
-  return base::FeatureList::IsEnabled(features::kFirstPartySets) &&
-         (blink::features::kStorageAccessAPIAutoGrantInFPS.Get() ||
-          ShouldAutoDenyOutsideFPS());
+  return blink::features::kStorageAccessAPIAutoGrantInFPS.Get() ||
+         ShouldAutoDenyOutsideFPS();
 }
 
 // Returns true if the request wasn't answered by the user explicitly.
diff --git a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
index 244d14e..e7802db 100644
--- a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
+++ b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
@@ -543,8 +543,7 @@
   StorageAccessGrantPermissionContextAPIWithImplicitGrantsTest() {
     features_.InitWithFeaturesAndParameters(
         /*enabled_features=*/
-        {{features::kFirstPartySets, {}},
-         {blink::features::kStorageAccessAPI,
+        {{blink::features::kStorageAccessAPI,
           {
               {
                   blink::features::kStorageAccessAPIAutoGrantInFPS.name,
@@ -782,8 +781,7 @@
   StorageAccessGrantPermissionContextAPIWithFirstPartySetsTest() {
     features_.InitWithFeaturesAndParameters(
         /*enabled_features=*/
-        {{features::kFirstPartySets, {}},
-         {blink::features::kStorageAccessAPI,
+        {{blink::features::kStorageAccessAPI,
           {
               {
                   blink::features::kStorageAccessAPIAutoGrantInFPS.name,
diff --git a/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc b/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc
index 7da13b9..9d3e5ad 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc
@@ -44,7 +44,6 @@
 namespace subresource_filter {
 
 // static
-const char SubresourceFilterBrowserTest::kDocumentLoadActivationLevel[];
 const char SubresourceFilterBrowserTest::kSubresourceLoadsTotalForPage[];
 const char SubresourceFilterBrowserTest::kSubresourceLoadsEvaluatedForPage[];
 const char SubresourceFilterBrowserTest::kSubresourceLoadsMatchedRulesForPage[];
diff --git a/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h b/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h
index 924d2d6..689808b 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h
+++ b/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h
@@ -94,8 +94,6 @@
   ~SubresourceFilterBrowserTest() override;
 
   // Names of DocumentLoad histograms.
-  static constexpr const char kDocumentLoadActivationLevel[] =
-      "SubresourceFilter.DocumentLoad.ActivationState";
 
   static constexpr const char kSubresourceLoadsTotalForPage[] =
       "SubresourceFilter.PageLoad.NumSubresourceLoads.Total";
diff --git a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
index 9829a294..b09afda3c8 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
@@ -1022,11 +1022,6 @@
                           time_recorded ? num_subresource_checks : 0);
   tester.ExpectTotalCount(SubresourceFilterBrowserTest::kEvaluationCPUDuration,
                           time_recorded ? num_subresource_checks : 0);
-
-  tester.ExpectUniqueSample(
-      SubresourceFilterBrowserTest::kDocumentLoadActivationLevel,
-      static_cast<base::Histogram::Sample>(mojom::ActivationLevel::kEnabled),
-      6);
 }
 
 }  // namespace
@@ -1095,10 +1090,6 @@
   tester.ExpectTotalCount(kEvaluationCPUDuration, 0);
 
   // Although SubresourceFilterAgents still record the activation decision.
-  tester.ExpectUniqueSample(
-      kDocumentLoadActivationLevel,
-      static_cast<base::Histogram::Sample>(mojom::ActivationLevel::kDisabled),
-      6);
 }
 
 IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest,
diff --git a/chrome/browser/top_level_storage_access_api/request_storage_access_for_browsertest.cc b/chrome/browser/top_level_storage_access_api/request_storage_access_for_browsertest.cc
index e4e3ea3..f27cd79 100644
--- a/chrome/browser/top_level_storage_access_api/request_storage_access_for_browsertest.cc
+++ b/chrome/browser/top_level_storage_access_api/request_storage_access_for_browsertest.cc
@@ -780,54 +780,6 @@
             "");
 }
 
-// Tests to validate `requestStorageAccessFor` behavior with FPS disabled.
-// For now, that entails auto-denial of requests.
-class RequestStorageAccessForWithFirstPartySetsDisabledBrowserTest
-    : public RequestStorageAccessForBaseBrowserTest {
- public:
- protected:
-  std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override {
-    return {{blink::features::kStorageAccessAPIForOriginExtension, {}},
-            {blink::features::kStorageAccessAPI, {}}};
-  }
-  std::vector<base::test::FeatureRef> GetDisabledFeatures() override {
-    return {features::kFirstPartySets};
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(
-    RequestStorageAccessForWithFirstPartySetsDisabledBrowserTest,
-    PermissionAutodenied) {
-  SetBlockThirdPartyCookies(true);
-  base::HistogramTester histogram_tester;
-
-  // Set cross-site cookies on all hosts.
-  SetCrossSiteCookieOnHost(kHostA);
-  SetCrossSiteCookieOnHost(kHostD);
-
-  NavigateToPageWithFrame(kHostA);
-
-  NavigateFrameTo(kHostD, "/echoheader?cookie");
-  EXPECT_EQ(GetFrameContent(), "None");
-  EXPECT_EQ(ReadCookiesViaJS(GetFrame()), "");
-  // `kHostD` cannot be granted access via `RequestStorageAccessFor` in
-  // this configuration, because the requesting site (`kHostA`) is not in the
-  // same First-Party Set as the requested site (`kHostD`).
-  EXPECT_FALSE(storage::test::RequestStorageAccessForOrigin(
-      GetPrimaryMainFrame(), GetURL(kHostD).spec()));
-
-  // Navigate iframe to a cross-site, cookie-reading endpoint, and verify that
-  // the cookie is not sent.
-  NavigateFrameTo(kHostD, "/echoheader?cookie");
-  EXPECT_EQ(GetFrameContent(), "None");
-  EXPECT_EQ(ReadCookiesViaJS(GetFrame()), "");
-
-  EXPECT_THAT(histogram_tester.GetBucketCount(
-                  kRequestOutcomeHistogram,
-                  TopLevelStorageAccessRequestOutcome::kDeniedByPrerequisites),
-              Gt(0));
-}
-
 // Tests to validate that, when the `requestStorageAccessFor` extension is
 // explicitly disabled, or if the larger Storage Access API is disabled, it does
 // not leak onto the document object.
diff --git a/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context.cc b/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context.cc
index e004114..52929ef 100644
--- a/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context.cc
+++ b/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context.cc
@@ -23,7 +23,6 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/storage_partition.h"
-#include "content/public/common/content_features.h"
 #include "net/base/schemeful_site.h"
 #include "net/first_party_sets/first_party_set_entry.h"
 #include "net/first_party_sets/first_party_set_metadata.h"
@@ -80,14 +79,6 @@
     return;
   }
 
-  if (!base::FeatureList::IsEnabled(features::kFirstPartySets)) {
-    // First-Party Sets is disabled, so reject the request.
-    RecordOutcomeSample(
-        TopLevelStorageAccessRequestOutcome::kDeniedByPrerequisites);
-    std::move(callback).Run(CONTENT_SETTING_BLOCK);
-    return;
-  }
-
   net::SchemefulSite embedding_site(request_data.embedding_origin);
   net::SchemefulSite requesting_site(request_data.requesting_origin);
 
diff --git a/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context_unittest.cc b/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context_unittest.cc
index f01422a..6e8a9fdf 100644
--- a/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context_unittest.cc
+++ b/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context_unittest.cc
@@ -190,7 +190,6 @@
     features_.InitWithFeatures(
         /*enabled_features=*/
         {
-            features::kFirstPartySets,
             blink::features::kStorageAccessAPIForOriginExtension,
         },
         /*disabled_features=*/{});
@@ -381,53 +380,3 @@
                                      GetRequesterURL(), GetDummyEmbeddingUrl())
                 .status);
 }
-
-class TopLevelStorageAccessPermissionContextAPIFirstPartySetsDisabledTest
-    : public TopLevelStorageAccessPermissionContextTestAPIEnabledTest {
- public:
-  TopLevelStorageAccessPermissionContextAPIFirstPartySetsDisabledTest() {
-    features_.InitWithFeatures(
-        /*enabled_features=*/
-        {blink::features::kStorageAccessAPIForOriginExtension},
-        /*disabled_features=*/{features::kFirstPartySets});
-  }
-
- private:
-  base::test::ScopedFeatureList features_;
-};
-
-TEST_F(TopLevelStorageAccessPermissionContextAPIFirstPartySetsDisabledTest,
-       PermissionDeniedWithFPSDisabled) {
-  TopLevelStorageAccessPermissionContext permission_context(profile());
-  permissions::PermissionRequestID fake_id = CreateFakeID();
-
-  HostContentSettingsMap* settings_map =
-      HostContentSettingsMapFactory::GetForProfile(profile());
-  CHECK(settings_map);
-
-  // Check no `SessionModel::NonRestorableUserSession` setting exists yet.
-  ContentSettingsForOneType non_restorable_grants =
-      settings_map->GetSettingsForOneType(
-          ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS,
-          content_settings::SessionModel::NonRestorableUserSession);
-  EXPECT_EQ(0u, non_restorable_grants.size());
-
-  base::test::TestFuture<ContentSetting> future;
-  permission_context.DecidePermissionForTesting(
-      permissions::PermissionRequestData(&permission_context, fake_id,
-                                         /*user_gesture=*/true,
-                                         GetRequesterURL(), GetTopLevelURL()),
-      future.GetCallback());
-
-  EXPECT_EQ(CONTENT_SETTING_BLOCK, future.Get());
-  EXPECT_EQ(histogram_tester().GetBucketCount(
-                kRequestOutcomeHistogram,
-                TopLevelStorageAccessRequestOutcome::kDeniedByPrerequisites),
-            1);
-
-  // Check the `SessionModel::NonRestorableUserSession` settings granted by FPS.
-  non_restorable_grants = settings_map->GetSettingsForOneType(
-      ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS,
-      content_settings::SessionModel::NonRestorableUserSession);
-  EXPECT_EQ(0u, non_restorable_grants.size());
-}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index b751b74..7d84fe8 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -20,7 +20,6 @@
 import("//components/nacl/features.gni")
 import("//components/offline_pages/buildflags/features.gni")
 import("//components/services/screen_ai/buildflags/features.gni")
-import("//components/signin/features.gni")
 import("//crypto/features.gni")
 import("//device/vr/buildflags/buildflags.gni")
 import("//extensions/buildflags/buildflags.gni")
@@ -261,8 +260,8 @@
     "webui/constrained_web_dialog_ui.h",
     "webui/cookies_tree_model_util.cc",
     "webui/cookies_tree_model_util.h",
-    "webui/device_log_ui.cc",
-    "webui/device_log_ui.h",
+    "webui/device_log/device_log_ui.cc",
+    "webui/device_log/device_log_ui.h",
     "webui/download_internals/download_internals_ui.cc",
     "webui/download_internals/download_internals_ui.h",
     "webui/download_internals/download_internals_ui_message_handler.cc",
@@ -741,20 +740,6 @@
     deps += [ "//chrome/browser/enterprise/data_controls" ]
   }
 
-  if (enable_search_engine_choice) {
-    deps += [ "//chrome/browser/ui/webui/search_engine_choice:mojo_bindings" ]
-    sources += [
-      "search_engine_choice/search_engine_choice_tab_helper.cc",
-      "search_engine_choice/search_engine_choice_tab_helper.h",
-      "views/search_engine_choice/search_engine_choice_dialog_view.cc",
-      "views/search_engine_choice/search_engine_choice_dialog_view.h",
-      "webui/search_engine_choice/search_engine_choice_handler.cc",
-      "webui/search_engine_choice/search_engine_choice_handler.h",
-      "webui/search_engine_choice/search_engine_choice_ui.cc",
-      "webui/search_engine_choice/search_engine_choice_ui.h",
-    ]
-  }
-
   if (is_chromeos) {
     sources += [
       "webui/dlp_internals/dlp_internals_page_handler.cc",
@@ -1414,6 +1399,8 @@
       "search/search_ipc_router_policy_impl.h",
       "search/search_tab_helper.cc",
       "search/search_tab_helper.h",
+      "search_engine_choice/search_engine_choice_tab_helper.cc",
+      "search_engine_choice/search_engine_choice_tab_helper.h",
       "serial/serial_chooser.cc",
       "serial/serial_chooser.h",
       "serial/serial_chooser_controller.cc",
@@ -1619,6 +1606,8 @@
       "views/eye_dropper/eye_dropper.h",
       "views/file_system_access/file_system_access_scroll_panel.cc",
       "views/file_system_access/file_system_access_scroll_panel.h",
+      "views/search_engine_choice/search_engine_choice_dialog_view.cc",
+      "views/search_engine_choice/search_engine_choice_dialog_view.h",
       "webui/access_code_cast/access_code_cast_dialog.cc",
       "webui/access_code_cast/access_code_cast_dialog.h",
       "webui/access_code_cast/access_code_cast_handler.cc",
@@ -1792,6 +1781,10 @@
       "webui/search_engine_choice/generated_icon_utils.cc",
       "webui/search_engine_choice/icon_utils.cc",
       "webui/search_engine_choice/icon_utils.h",
+      "webui/search_engine_choice/search_engine_choice_handler.cc",
+      "webui/search_engine_choice/search_engine_choice_handler.h",
+      "webui/search_engine_choice/search_engine_choice_ui.cc",
+      "webui/search_engine_choice/search_engine_choice_ui.h",
       "webui/settings/about_handler.cc",
       "webui/settings/about_handler.h",
       "webui/settings/accessibility_main_handler.cc",
@@ -2030,6 +2023,7 @@
       "//chrome/browser/ui/webui/new_tab_page:mojo_bindings",
       "//chrome/browser/ui/webui/new_tab_page_third_party:mojo_bindings",
       "//chrome/browser/ui/webui/on_device_internals:mojom",
+      "//chrome/browser/ui/webui/search_engine_choice:mojo_bindings",
       "//chrome/browser/ui/webui/side_panel/bookmarks:mojo_bindings",
       "//chrome/browser/ui/webui/side_panel/customize_chrome:mojo_bindings",
       "//chrome/browser/ui/webui/side_panel/performance_controls:mojo_bindings",
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
index ed0bc1bb..6b40b25 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
@@ -81,15 +81,9 @@
         assert mNativeController != 0 : "Failed to instantiate native AutocompleteController";
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    @CalledByNative
-    void notifyNativeDestroyed() {
-        mNativeController = 0;
-    }
-
     /**
-      * @param listener The listener to be notified when new suggestions are available.
-      */
+     * @param listener The listener to be notified when new suggestions are available.
+     */
     public void addOnSuggestionsReceivedListener(@NonNull OnSuggestionsReceivedListener listener) {
         mListeners.add(listener);
     }
diff --git a/chrome/browser/ui/android/page_insights/java/src/org/chromium/chrome/browser/page_insights/PageInsightsMediator.java b/chrome/browser/ui/android/page_insights/java/src/org/chromium/chrome/browser/page_insights/PageInsightsMediator.java
index 846edaf..41e39e2 100644
--- a/chrome/browser/ui/android/page_insights/java/src/org/chromium/chrome/browser/page_insights/PageInsightsMediator.java
+++ b/chrome/browser/ui/android/page_insights/java/src/org/chromium/chrome/browser/page_insights/PageInsightsMediator.java
@@ -15,6 +15,7 @@
 import android.text.format.DateUtils;
 import android.view.View;
 
+import androidx.annotation.ColorInt;
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
@@ -749,14 +750,13 @@
         } else if (ratioOfCompletionFromPeekToExpanded <= 0) {
             colorRatio = 0;
         }
-        int surfaceColor = mContext.getColor(R.color.gm3_baseline_surface);
+        @ColorInt int surfaceColor = mContext.getColor(R.color.gm3_baseline_surface);
+        @ColorInt
         int surfaceContainerColor = mContext.getColor(R.color.gm3_baseline_surface_container);
         mBackgroundDrawable.setColor(
-                ColorUtils.getColorWithOverlay(
-                        surfaceContainerColor, surfaceColor, colorRatio, false));
+                ColorUtils.getColorWithOverlay(surfaceContainerColor, surfaceColor, colorRatio));
         mSheetContent.setPrivacyCardColor(
-                ColorUtils.getColorWithOverlay(
-                        surfaceColor, surfaceContainerColor, colorRatio, false));
+                ColorUtils.getColorWithOverlay(surfaceColor, surfaceContainerColor, colorRatio));
     }
 
     @Override
diff --git a/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_prompt.xml b/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_prompt.xml
index 988f300..46b49b9cc 100644
--- a/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_prompt.xml
+++ b/chrome/browser/ui/android/plus_addresses/java/res/layout/plus_address_creation_prompt.xml
@@ -35,6 +35,7 @@
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:textAlignment="center"
+      android:breakStrategy="simple"
       android:layout_marginBottom="@dimen/list_item_default_margin"
       android:textAppearance="@style/TextAppearance.TextMedium.Secondary"/>
 
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabStripTransitionCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabStripTransitionCoordinator.java
index c4cb5667..685a1a9 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabStripTransitionCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabStripTransitionCoordinator.java
@@ -24,6 +24,7 @@
 import org.chromium.chrome.browser.toolbar.R;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.ViewUtils;
+import org.chromium.ui.util.TokenHolder;
 
 /** Subclass used to manage tab strip visibility and height presents. */
 public class TabStripTransitionCoordinator implements ComponentCallbacks {
@@ -60,13 +61,35 @@
     private final View mControlContainer;
     private final View mToolbarLayout;
     private final int mTabStripHeightFromResource;
+    private final TokenHolder mDeferTransitionTokenHolder;
 
+    /**
+     * Current height of the tab strip represented by the space reserved on top of the toolbar
+     * layout. Could be inconsistent with {@link #mTabStripVisible} during the transition.
+     */
     private int mTabStripHeight;
+
     private int mTabStripTransitionThreshold;
+
+    /**
+     * The stable state whether the tab strip will be visible or not. This value will be changed at
+     * the beginning of the transition.
+     */
     private boolean mTabStripVisible;
+
+    /**
+     * Internal state used to block the transition until the TRANSITION_DELAY_MS after the last
+     * #onLayout pass.
+     */
+    private int mOnLayoutToken = TokenHolder.INVALID_TOKEN;
+
+    /** Tracks the last width seen for the mControlContainer. */
+    private int mControlContainerLayoutWidth;
+
+    private OnLayoutChangeListener mOnLayoutChangedListener;
+    private @Nullable Runnable mLayoutTransitionTask;
+
     private @Nullable BrowserControlsStateProvider.Observer mBrowserControlsObserver;
-    private @Nullable OnLayoutChangeListener mOnLayoutChangedListener;
-    private @Nullable Runnable mLastTransitionTask;
 
     /**
      * Create the coordinator managing transitions for when showing / hiding the tab strip.
@@ -92,12 +115,13 @@
         mOnLayoutChangedListener =
                 (view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
                     int windowWidth = Math.abs(right - left);
-                    maybeUpdateTabStripVisibility(windowWidth);
+                    onLayoutWidthChanged(windowWidth);
                 };
         mControlContainer.addOnLayoutChangeListener(mOnLayoutChangedListener);
+        mDeferTransitionTokenHolder = new TokenHolder(this::onTokenUpdate);
 
         updateTabStripTransitionThreshold();
-        maybeUpdateTabStripVisibility(mControlContainer.getWidth());
+        onLayoutWidthChanged(mControlContainer.getWidth());
     }
 
     /** Return the current tab strip height. */
@@ -115,6 +139,19 @@
         mTabStripHeightObservers.removeObserver(observer);
     }
 
+    /** Request the token to defer the tab strip transition to a later time. */
+    public int requestDeferTabStripTransitionToken() {
+        return mDeferTransitionTokenHolder.acquireToken();
+    }
+
+    /**
+     * Release the token acquired from {@link #requestDeferTabStripTransitionToken()} so tab strip
+     * can transition based on its current sizes.
+     */
+    public void releaseTabStripToken(int token) {
+        mDeferTransitionTokenHolder.releaseToken(token);
+    }
+
     /** Remove observers and release reference to dependencies. */
     public void destroy() {
         if (mBrowserControlsObserver != null) {
@@ -137,33 +174,58 @@
     @Override
     public void onLowMemory() {}
 
+    private void onTokenUpdate() {
+        maybeUpdateTabStripVisibility(mControlContainer.getWidth());
+    }
+
     private void updateTabStripTransitionThreshold() {
         DisplayMetrics displayMetrics = mControlContainer.getResources().getDisplayMetrics();
         mTabStripTransitionThreshold =
                 ViewUtils.dpToPx(displayMetrics, getScreenWidthThresholdDp());
 
         if (sMinScreenWidthForTesting != null) {
-            maybeUpdateTabStripVisibility(displayMetrics.widthPixels);
+            onTokenUpdate();
         }
     }
 
-    private void maybeUpdateTabStripVisibility(int windowWidth) {
-        // Invalid width will be ignored. This can happen when the mControlContainer are created
-        // hidden after theme changes. See crbug.com/1511599.
-        if (windowWidth <= 0) return;
-
-        boolean showTabStrip = windowWidth >= mTabStripTransitionThreshold;
-        if (showTabStrip == mTabStripVisible) return;
+    /**
+     * Always wait for a short delay after the last #onLayout pass for the control container to make
+     * sure the UI is in a stable state.
+     *
+     * @param newWidth The current width of control container.
+     */
+    private void onLayoutWidthChanged(int newWidth) {
+        if (newWidth == mControlContainerLayoutWidth) return;
+        mControlContainerLayoutWidth = newWidth;
 
         // Kick off tab strip transition once tab strip visibility is confirmed to be
         // changed. Do not change the mTabStripVisible until the transition actually
         // started.
-        if (mLastTransitionTask != null) {
-            mHandler.removeCallbacks(mLastTransitionTask);
+        if (mLayoutTransitionTask != null) {
+            mHandler.removeCallbacks(mLayoutTransitionTask);
         }
-        mLastTransitionTask =
-                mCallbackController.makeCancelable(() -> setTabStripVisibility(showTabStrip));
-        mHandler.postDelayed(mLastTransitionTask, TRANSITION_DELAY_MS);
+        int oldToken = mOnLayoutToken;
+        mOnLayoutToken = mDeferTransitionTokenHolder.acquireToken();
+        mDeferTransitionTokenHolder.releaseToken(oldToken);
+        mLayoutTransitionTask =
+                mCallbackController.makeCancelable(
+                        () -> mDeferTransitionTokenHolder.releaseToken(mOnLayoutToken));
+        mHandler.postDelayed(mLayoutTransitionTask, TRANSITION_DELAY_MS);
+    }
+
+    private void maybeUpdateTabStripVisibility(int tabStripWidth) {
+        // Block new request for transitions as long as there's any token left. Once the token
+        // clears out, #onTokenUpdated will route into this method again.
+        if (mDeferTransitionTokenHolder.hasTokens()) return;
+
+        // Invalid width will be ignored. This can happen when the mControlContainer is created
+        // hidden after theme changes. See crbug.com/1511599.
+        if (tabStripWidth <= 0) return;
+
+        boolean showTabStrip = tabStripWidth >= mTabStripTransitionThreshold;
+        if (showTabStrip == mTabStripVisible) return;
+
+        setTabStripVisibility(showTabStrip);
     }
 
     /**
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabStripTransitionCoordinatorUnitTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabStripTransitionCoordinatorUnitTest.java
index d9194fe..298f620 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabStripTransitionCoordinatorUnitTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabStripTransitionCoordinatorUnitTest.java
@@ -52,6 +52,7 @@
 public class TabStripTransitionCoordinatorUnitTest {
     private static final int TEST_TAB_STRIP_HEIGHT = 40;
     private static final int TEST_TOOLBAR_HEIGHT = 56;
+    private static final int NOTHING_OBSERVED = -1;
 
     @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
 
@@ -66,8 +67,8 @@
     private TestActivity mActivity;
     private TestControlContainer mSpyControlContainer;
 
-    private int mObservedOnHeightRequested = -1;
-    private int mObservedOnHeightChanged = -1;
+    private int mObservedOnHeightRequested = NOTHING_OBSERVED;
+    private int mObservedOnHeightChanged = NOTHING_OBSERVED;
 
     @Before
     public void setup() {
@@ -137,7 +138,7 @@
         setDeviceWidthDp(480);
 
         // Simulate top controls size change from browser. Input values doesn't matter in this call.
-        mBrowserControlsObserver.getValue().onControlsOffsetChanged(0, 0, 0, 0, false);
+        getBrowserControlsObserver().onControlsOffsetChanged(0, 0, 0, 0, false);
         assertTabStripHeightForMargins(0);
         assertObservedHeight(0);
     }
@@ -148,7 +149,7 @@
 
         // Simulate top controls size change from browser.
         doReturn(true).when(mBrowserControlsVisibilityManager).offsetOverridden();
-        mBrowserControlsObserver.getValue().onTopControlsHeightChanged(TEST_TOOLBAR_HEIGHT, 0);
+        getBrowserControlsObserver().onTopControlsHeightChanged(TEST_TOOLBAR_HEIGHT, 0);
         assertTabStripHeightForMargins(0);
         assertObservedHeight(0);
     }
@@ -159,7 +160,7 @@
         setDeviceWidthDp(600);
 
         // Simulate top controls size change from browser. Input values doesn't matter in this call.
-        mBrowserControlsObserver.getValue().onControlsOffsetChanged(0, 0, 0, 0, false);
+        getBrowserControlsObserver().onControlsOffsetChanged(0, 0, 0, 0, false);
         assertTabStripHeightForMargins(TEST_TAB_STRIP_HEIGHT);
         assertObservedHeight(TEST_TAB_STRIP_HEIGHT);
     }
@@ -170,12 +171,68 @@
         setDeviceWidthDp(600);
         // Simulate top controls size change from browser.
         doReturn(true).when(mBrowserControlsVisibilityManager).offsetOverridden();
-        mBrowserControlsObserver.getValue().onTopControlsHeightChanged(TEST_TOOLBAR_HEIGHT, 0);
+        getBrowserControlsObserver().onTopControlsHeightChanged(TEST_TOOLBAR_HEIGHT, 0);
         assertTabStripHeightForMargins(TEST_TAB_STRIP_HEIGHT);
         assertObservedHeight(TEST_TAB_STRIP_HEIGHT);
     }
 
     @Test
+    @Config(qualifiers = "w480dp")
+    public void showTabStrip_TokenBeforeLayout() {
+        int token = mCoordinator.requestDeferTabStripTransitionToken();
+        setDeviceWidthDp(600);
+        Assert.assertEquals(
+                "Height request should be blocked by the token.", 0, mObservedOnHeightRequested);
+
+        mCoordinator.releaseTabStripToken(token);
+        Assert.assertEquals(
+                "Height request should go through after the token released.",
+                TEST_TAB_STRIP_HEIGHT,
+                mObservedOnHeightRequested);
+    }
+
+    @Test
+    @Config(qualifiers = "w480dp")
+    public void showTabStrip_TokenDuringLayout() {
+        setConfigurationWithNewWidth(600);
+
+        // Layout pass will trigger the delayed task for layout transition.
+        simulateLayoutChange(600);
+        ShadowLooper.idleMainLooper(100, TimeUnit.MILLISECONDS);
+        int token = mCoordinator.requestDeferTabStripTransitionToken();
+        Assert.assertEquals(
+                "Height request should be blocked by the token.", 0, mObservedOnHeightRequested);
+
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        Assert.assertEquals(
+                "Height request should be blocked by the token.", 0, mObservedOnHeightRequested);
+
+        mCoordinator.releaseTabStripToken(token);
+        Assert.assertEquals(
+                "Height request should go through after the token released.",
+                TEST_TAB_STRIP_HEIGHT,
+                mObservedOnHeightRequested);
+    }
+
+    @Test
+    @Config(qualifiers = "w480dp")
+    public void showTabStrip_TokenReleaseEarly() {
+        int token = mCoordinator.requestDeferTabStripTransitionToken();
+        setConfigurationWithNewWidth(600);
+        simulateLayoutChange(600);
+        ShadowLooper.idleMainLooper(100, TimeUnit.MILLISECONDS);
+        mCoordinator.releaseTabStripToken(token);
+        Assert.assertEquals(
+                "Height request should be blocked by the token.", 0, mObservedOnHeightRequested);
+
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        Assert.assertEquals(
+                "Height request should go through after the token released.",
+                TEST_TAB_STRIP_HEIGHT,
+                mObservedOnHeightRequested);
+    }
+
+    @Test
     public void configurationChangedDuringDelayedTask() {
         setConfigurationWithNewWidth(480);
         simulateLayoutChange(480);
@@ -246,6 +303,12 @@
                 mObservedOnHeightChanged);
     }
 
+    private BrowserControlsStateProvider.Observer getBrowserControlsObserver() {
+        var observer = mBrowserControlsObserver.getValue();
+        Assert.assertNotNull("Browser controls observer not attached.", observer);
+        return observer;
+    }
+
     private void simulateLayoutChange(int width) {
         Assert.assertNotNull(mSpyControlContainer.onLayoutChangeListener);
         mSpyControlContainer.onLayoutChangeListener.onLayoutChange(
@@ -286,7 +349,7 @@
             doReturn(controlContainer.findToolbar)
                     .when(controlContainer)
                     .findViewById(R.id.find_toolbar);
-            doReturn(context.getResources().getDisplayMetrics().widthPixels)
+            doAnswer(args -> context.getResources().getDisplayMetrics().widthPixels)
                     .when(controlContainer)
                     .getWidth();
             doAnswer(
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
index 5ce393bd..c5131a95 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
@@ -99,8 +99,11 @@
 
     private TopToolbarOverlayCoordinator mOverlayCoordinator;
 
-    protected BrowserStateBrowserControlsVisibilityDelegate mBrowserControlsVisibilityDelegate;
-    protected int mShowBrowserControlsToken = TokenHolder.INVALID_TOKEN;
+    private BrowserStateBrowserControlsVisibilityDelegate mBrowserControlsVisibilityDelegate;
+    private int mShowBrowserControlsToken = TokenHolder.INVALID_TOKEN;
+
+    private TabStripTransitionCoordinator mTabStripTransitionCoordinator;
+    private int mTabStripTransitionToken = TokenHolder.INVALID_TOKEN;
 
     protected final DestroyChecker mDestroyChecker;
 
@@ -829,14 +832,22 @@
         mBrowserControlsVisibilityDelegate = controlsVisibilityDelegate;
     }
 
+    // TODO(crbug.com/1512232): Rework the API if this method is called by multiple clients.
     protected void keepControlsShownForAnimation() {
         // isShown() being false implies that the toolbar isn't visible. We don't want to force it
         // back into visibility just so that we can show an animation.
-        if (isShown() && mBrowserControlsVisibilityDelegate != null) {
+        if (!isShown()) return;
+
+        if (mBrowserControlsVisibilityDelegate != null) {
             mShowBrowserControlsToken =
                     mBrowserControlsVisibilityDelegate.showControlsPersistentAndClearOldToken(
                             mShowBrowserControlsToken);
         }
+        if (mTabStripTransitionCoordinator != null
+                && mTabStripTransitionToken == TokenHolder.INVALID_TOKEN) {
+            mTabStripTransitionToken =
+                    mTabStripTransitionCoordinator.requestDeferTabStripTransitionToken();
+        }
     }
 
     protected void allowBrowserControlsHide() {
@@ -845,6 +856,15 @@
                     mShowBrowserControlsToken);
             mShowBrowserControlsToken = TokenHolder.INVALID_TOKEN;
         }
+        if (mTabStripTransitionCoordinator != null) {
+            mTabStripTransitionCoordinator.releaseTabStripToken(mTabStripTransitionToken);
+            mTabStripTransitionToken = TokenHolder.INVALID_TOKEN;
+        }
+    }
+
+    /** Set the {@link TabStripTransitionCoordinator} that manages interactions around tab strip. */
+    public void setTabStripTransitionCoordinator(TabStripTransitionCoordinator coordinator) {
+        mTabStripTransitionCoordinator = coordinator;
     }
 
     /**
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 50c46e4..64ab84eb 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -51,7 +51,6 @@
 import org.chromium.base.MathUtils;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.supplier.ObservableSupplier;
-import org.chromium.chrome.browser.browser_controls.BrowserStateBrowserControlsVisibilityDelegate;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.omnibox.LocationBar;
@@ -382,12 +381,6 @@
     }
 
     @Override
-    public void setBrowserControlsVisibilityDelegate(
-            BrowserStateBrowserControlsVisibilityDelegate controlsVisibilityDelegate) {
-        mBrowserControlsVisibilityDelegate = controlsVisibilityDelegate;
-    }
-
-    @Override
     public void destroy() {
         cancelAnimations();
         Handler handler = getHandler();
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
index c9f3c1c..2a7ce3f 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
@@ -865,9 +865,6 @@
 
                     @Override
                     public void onAnimationEnd(Animator animation) {
-                        // Reset the capture state token to force a recapture. This is required
-                        // as the capture state token only checks the button's visibility.
-                        mLastCaptureStateToken = null;
                         mButtonVisibilityAnimators = null;
                         allowBrowserControlsHide();
                     }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTabletUnitTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTabletUnitTest.java
index d060fc1..c016093 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTabletUnitTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTabletUnitTest.java
@@ -7,6 +7,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -32,10 +34,12 @@
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
 import org.robolectric.Shadows;
 import org.robolectric.annotation.LooperMode;
+import org.robolectric.shadows.ShadowLooper;
 import org.robolectric.shadows.ShadowToast;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
@@ -68,7 +72,7 @@
     @Mock private LocationBarCoordinatorTablet mLocationBarTablet;
     @Mock private StatusCoordinator mStatusCoordinator;
     @Mock private MenuButtonCoordinator mMenuButtonCoordinator;
-    @Mock private View mContainerView;
+    @Mock private TabStripTransitionCoordinator mTabStripTransitionCoordinator;
     private Activity mActivity;
     private ToolbarTablet mToolbarTablet;
     private LinearLayout mToolbarTabletLayout;
@@ -87,14 +91,16 @@
         MockitoAnnotations.initMocks(this);
         mActivity = Robolectric.buildActivity(Activity.class).setup().get();
         mActivity.setTheme(R.style.Theme_BrowserUI_DayNight);
-        mToolbarTablet =
+        ToolbarTablet realView =
                 (ToolbarTablet)
                         mActivity.getLayoutInflater().inflate(R.layout.toolbar_tablet, null);
+        mToolbarTablet = Mockito.spy(realView);
         when(mLocationBar.getTabletCoordinator()).thenReturn(mLocationBarTablet);
         mToolbarTablet.setLocationBarCoordinator(mLocationBar);
         LocationBarLayout locationBarLayout = mToolbarTablet.findViewById(R.id.location_bar);
         locationBarLayout.setStatusCoordinatorForTesting(mStatusCoordinator);
         mToolbarTablet.setMenuButtonCoordinatorForTesting(mMenuButtonCoordinator);
+        mToolbarTablet.setTabStripTransitionCoordinator(mTabStripTransitionCoordinator);
         mToolbarTabletLayout =
                 (LinearLayout) mToolbarTablet.findViewById(R.id.toolbar_tablet_layout);
         mHomeButton = mToolbarTablet.findViewById(R.id.home_button);
@@ -207,6 +213,7 @@
 
     @Test
     public void onMeasureSmallWidthWithAnimation_hidesToolbarButtons() {
+        doReturn(true).when(mToolbarTablet).isShown();
         for (ImageButton btn : mToolbarTablet.getToolbarButtons()) {
             when(mLocationBar.createHideButtonAnimatorForTablet(btn))
                     .thenReturn(ObjectAnimator.ofFloat(btn, View.ALPHA, 0.f));
@@ -217,6 +224,7 @@
         mToolbarTablet.enableButtonVisibilityChangeAnimationForTesting();
         // Call
         mToolbarTablet.measure(300, 300);
+        verify(mTabStripTransitionCoordinator).requestDeferTabStripTransitionToken();
         Shadows.shadowOf(Looper.getMainLooper()).idle();
         // Verify
         ImageButton[] btns = mToolbarTablet.getToolbarButtons();
@@ -224,10 +232,12 @@
             assertEquals(
                     "Toolbar button visibility is not as expected", View.GONE, btn.getVisibility());
         }
+        verify(mTabStripTransitionCoordinator, atLeastOnce()).releaseTabStripToken(anyInt());
     }
 
     @Test
     public void onMeasureLargeWidthWithAnimation_showsToolbarButtons() {
+        doReturn(true).when(mToolbarTablet).isShown();
         mToolbarTablet.setToolbarButtonsVisibleForTesting(false);
         mToolbarTablet.enableButtonVisibilityChangeAnimationForTesting();
         for (ImageButton btn : mToolbarTablet.getToolbarButtons()) {
@@ -238,6 +248,7 @@
                 .thenReturn(new ArrayList<>());
         // Call
         mToolbarTablet.measure(700, 300);
+        verify(mTabStripTransitionCoordinator).requestDeferTabStripTransitionToken();
         Shadows.shadowOf(Looper.getMainLooper()).idle();
         // Verify
         ImageButton[] btns = mToolbarTablet.getToolbarButtons();
@@ -247,6 +258,7 @@
                     View.VISIBLE,
                     btn.getVisibility());
         }
+        verify(mTabStripTransitionCoordinator, atLeastOnce()).releaseTabStripToken(anyInt());
     }
 
     @Test
@@ -494,6 +506,11 @@
         Assert.assertEquals(
                 TopToolbarBlockCaptureReason.TABLET_BUTTON_ANIMATION_IN_PROGRESS,
                 result.blockReason);
+
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        result = mToolbarTablet.isReadyForTextureCapture();
+        Assert.assertTrue(result.isReady);
+        Assert.assertEquals(TopToolbarAllowCaptureReason.SNAPSHOT_DIFFERENCE, result.allowReason);
     }
 
     @Test
@@ -514,6 +531,11 @@
         Assert.assertEquals(
                 TopToolbarBlockCaptureReason.TABLET_BUTTON_ANIMATION_IN_PROGRESS,
                 result.blockReason);
+
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        result = mToolbarTablet.isReadyForTextureCapture();
+        Assert.assertTrue(result.isReady);
+        Assert.assertEquals(TopToolbarAllowCaptureReason.SNAPSHOT_DIFFERENCE, result.allowReason);
     }
 
     @Test
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
index e6a52b9e..aea69f2 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
@@ -371,6 +371,7 @@
                             mToolbarLayout,
                             tabStripHeightResource);
             mToolbarLayout.getContext().registerComponentCallbacks(mTabStripTransitionCoordinator);
+            mToolbarLayout.setTabStripTransitionCoordinator(mTabStripTransitionCoordinator);
             addTabStripHeightObserver(
                     new TabStripHeightObserver() {
                         @Override
diff --git a/chrome/browser/ui/ash/capture_mode/recording_service_browsertest.cc b/chrome/browser/ui/ash/capture_mode/recording_service_browsertest.cc
index 5865951..cbb668687 100644
--- a/chrome/browser/ui/ash/capture_mode/recording_service_browsertest.cc
+++ b/chrome/browser/ui/ash/capture_mode/recording_service_browsertest.cc
@@ -17,6 +17,7 @@
 #include "base/run_loop.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/bind.h"
+#include "base/test/gtest_tags.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
 #include "build/chromeos_buildflags.h"
@@ -387,6 +388,9 @@
 // Records a GIF image of a region that fills the entire screen, then attempts
 // to decode the resulting file to verify the GIF encoding was successful.
 IN_PROC_BROWSER_TEST_F(GifRecordingBrowserTest, SuccessfulEncodeDecode) {
+  base::AddFeatureIdTagToTestResult(
+      "screenplay-9c11ed80-9e97-482c-9562-450bd891732b");
+
   aura::Window* browser_window = browser()->window()->GetNativeWindow();
   ash::CaptureModeTestApi test_api;
   test_api.SetRecordingType(ash::RecordingType::kGif);
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_client_impl_browsertest.cc b/chrome/browser/ui/ash/holding_space/holding_space_client_impl_browsertest.cc
index 850171ab..5fdfb56 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_client_impl_browsertest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_client_impl_browsertest.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_browsertest_base.h"
+#include "chrome/browser/ui/ash/holding_space/holding_space_test_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/drive/drive_pref_names.h"
@@ -35,9 +36,18 @@
 #include "ui/gfx/image/image_skia.h"
 
 namespace ash {
-
 namespace {
 
+// Aliases ---------------------------------------------------------------------
+
+using ::testing::AllOf;
+using ::testing::Eq;
+using ::testing::IsFalse;
+using ::testing::IsTrue;
+using ::testing::ResultOf;
+
+// Constants -------------------------------------------------------------------
+
 // File paths for test data.
 constexpr char kTestDataDir[] = "chrome/test/data/chromeos/file_manager/";
 constexpr char kImageFilePath[] = "image.png";
@@ -111,12 +121,21 @@
     const std::string& expected_id =
         client->AddItemOfType(expected_type, expected_file_path);
 
-    // Insertion into the model should only fail if the item is a Camera app
-    // item and Camera app integration is disabled.
+    // Insertion into the model should only fail if the item is:
+    // (a) a Camera app item and Camera app integration is disabled, or
+    // (b) a Photoshop Web item and Photoshop Web integration is disabled.
     if (expected_id.empty()) {
       EXPECT_EQ(model->items().size(), expected_count);
-      EXPECT_TRUE(HoldingSpaceItem::IsCameraAppType(expected_type));
-      EXPECT_FALSE(features::IsHoldingSpaceCameraAppIntegrationEnabled());
+      EXPECT_THAT(
+          expected_type,
+          ::testing::AnyOf(
+              AllOf(ResultOf(&HoldingSpaceItem::IsCameraAppType, IsTrue()),
+                    And(features::IsHoldingSpaceCameraAppIntegrationEnabled(),
+                        IsFalse())),
+              AllOf(
+                  Eq(HoldingSpaceItem::Type::kPhotoshopWeb),
+                  And(features::IsHoldingSpacePhotoshopWebIntegrationEnabled(),
+                      IsFalse()))));
       continue;
     }
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
index 752fa696..95b5dee 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
@@ -408,6 +408,13 @@
       result.push_back(std::cref(base::EmptyString()));
       continue;
     }
+    // Ignore any `items` that are from Photoshop Web if Photoshop Web
+    // integration is disabled.
+    if (item->type() == HoldingSpaceItem::Type::kPhotoshopWeb &&
+        !features::IsHoldingSpacePhotoshopWebIntegrationEnabled()) {
+      result.push_back(std::cref(base::EmptyString()));
+      continue;
+    }
     // Ignore any `items` that already exist in the `holding_space_model_` if
     // `allow_duplicates` is false.
     if (!allow_duplicates && holding_space_model_.ContainsItem(
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
index 5d22a79b..918541d 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
@@ -66,6 +66,7 @@
 #include "storage/browser/file_system/file_system_url.h"
 #include "storage/browser/test/async_file_test_helper.h"
 #include "storage/browser/test/test_file_system_context.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/chromeos/styles/cros_styles.h"
@@ -76,10 +77,20 @@
 #include "ui/gfx/skia_util.h"
 
 namespace ash {
+namespace {
+
+// Aliases ---------------------------------------------------------------------
 
 using holding_space::ScopedTestMountPoint;
+using ::testing::AllOf;
+using ::testing::Eq;
 using ::testing::Field;
-namespace {
+using ::testing::IsFalse;
+using ::testing::IsTrue;
+using ::testing::ResultOf;
+using ::testing::Value;
+
+// Helpers ---------------------------------------------------------------------
 
 // Returns whether the bitmaps backing the specified `gfx::ImageSkia` are equal.
 bool BitmapsAreEqual(const gfx::ImageSkia& a, const gfx::ImageSkia& b) {
@@ -148,6 +159,10 @@
       !features::IsHoldingSpaceCameraAppIntegrationEnabled()) {
     return false;
   }
+  if (type == HoldingSpaceItem::Type::kPhotoshopWeb &&
+      !features::IsHoldingSpacePhotoshopWebIntegrationEnabled()) {
+    return false;
+  }
   if (HoldingSpaceItem::IsSuggestionType(type) &&
       !features::IsHoldingSpaceSuggestionsEnabled()) {
     return false;
@@ -2629,18 +2644,23 @@
 }
 
 // Base class for tests which verify adding and removing items from holding
-// space works as intended, parameterized by holding space item type and
-// whether Camera app integration is enabled.
+// space works as intended, parameterized by:
+// (a) holding space item type,
+// (b) whether Camera app integration is enabled, and
+// (c) whether Photoshop Web integration is enabled.
 class HoldingSpaceKeyedServiceAddAndRemoveItemTest
     : public HoldingSpaceKeyedServiceTest,
       public ::testing::WithParamInterface<
           std::tuple<HoldingSpaceItem::Type,
-                     /*enable_camera_app_integration=*/bool>> {
+                     /*enable_camera_app_integration=*/bool,
+                     /*enable_photoshop_web_integration=*/bool>> {
  public:
   HoldingSpaceKeyedServiceAddAndRemoveItemTest() {
-    scoped_feature_list_.InitWithFeatureState(
-        features::kHoldingSpaceCameraAppIntegration,
-        /*enable_camera_app_integration=*/std::get<1>(GetParam()));
+    scoped_feature_list_.InitWithFeatureStates(
+        {{features::kHoldingSpaceCameraAppIntegration,
+          /*enable_camera_app_integration=*/std::get<1>(GetParam())},
+         {features::kHoldingSpacePhotoshopWebIntegration,
+          /*enable_photoshop_web_integration=*/std::get<2>(GetParam())}});
   }
 
   // Returns the holding space service associated with the specified `profile`.
@@ -2707,6 +2727,14 @@
                                 file_path, HoldingSpaceProgress())
                 .empty());
         break;
+      case HoldingSpaceItem::Type::kPhotoshopWeb: {
+        const auto& id = holding_space_service->AddItemOfType(type, file_path);
+        if (!features::IsHoldingSpacePhotoshopWebIntegrationEnabled()) {
+          EXPECT_TRUE(id.empty());
+          return id;
+        }
+        break;
+      }
       case HoldingSpaceItem::Type::kPrintedPdf:
         holding_space_service->AddPrintedPdf(file_path,
                                              /*from_incognito_profile=*/false);
@@ -2732,7 +2760,8 @@
     All,
     HoldingSpaceKeyedServiceAddAndRemoveItemTest,
     testing::Combine(testing::ValuesIn(GetHoldingSpaceItemTypes()),
-                     /*enable_camera_app_integration=*/testing::Bool()));
+                     /*enable_camera_app_integration=*/testing::Bool(),
+                     /*enable_photoshop_web_integration=*/testing::Bool()));
 
 TEST_P(HoldingSpaceKeyedServiceAddAndRemoveItemTest, AddAndRemoveItem) {
   // Wait for the holding space model to attach.
@@ -2765,12 +2794,20 @@
   // Add a holding space item of the type under test.
   const std::string id = AddItem(profile, GetType(), file_path);
 
-  // Insertion into the model should only fail if the item is a Camera app item
-  // and Camera app integration is disabled.
+  // Insertion into the model should only fail if the item is:
+  // (a) a Camera app item and Camera app integration is disabled, or
+  // (b) a Photoshop Web item and Photoshop Web integration is disabled.
   if (id.empty()) {
     EXPECT_EQ(model->items().size(), 0u);
-    EXPECT_TRUE(HoldingSpaceItem::IsCameraAppType(GetType()));
-    EXPECT_FALSE(features::IsHoldingSpaceCameraAppIntegrationEnabled());
+    EXPECT_THAT(
+        GetType(),
+        AnyOf(
+            AllOf(ResultOf(&HoldingSpaceItem::IsCameraAppType, IsTrue()),
+                  And(features::IsHoldingSpaceCameraAppIntegrationEnabled(),
+                      IsFalse())),
+            AllOf(Eq(HoldingSpaceItem::Type::kPhotoshopWeb),
+                  And(features::IsHoldingSpacePhotoshopWebIntegrationEnabled(),
+                      IsFalse()))));
     return;
   }
 
@@ -2858,12 +2895,20 @@
   // Add a holding space item of the type under test.
   const auto& id = GetService(profile)->AddItemOfType(GetType(), file_path);
 
-  // Insertion into the model should only fail if the item is a Camera app item
-  // and Camera app integration is disabled.
+  // Insertion into the model should only fail if the item is:
+  // (a) a Camera app item and Camera app integration is disabled, or
+  // (b) a Photoshop Web item and Photoshop Web integration is disabled.
   if (id.empty()) {
     EXPECT_EQ(model->items().size(), 0u);
-    EXPECT_TRUE(HoldingSpaceItem::IsCameraAppType(GetType()));
-    EXPECT_FALSE(features::IsHoldingSpaceCameraAppIntegrationEnabled());
+    EXPECT_THAT(
+        GetType(),
+        AnyOf(
+            AllOf(ResultOf(&HoldingSpaceItem::IsCameraAppType, IsTrue()),
+                  And(features::IsHoldingSpaceCameraAppIntegrationEnabled(),
+                      IsFalse())),
+            AllOf(Eq(HoldingSpaceItem::Type::kPhotoshopWeb),
+                  And(features::IsHoldingSpacePhotoshopWebIntegrationEnabled(),
+                      IsFalse()))));
     return;
   }
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc
index 021e3675..20d5db3 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc
@@ -169,11 +169,14 @@
 
   const bool remove_camera_app_items =
       !features::IsHoldingSpaceCameraAppIntegrationEnabled();
+  const bool remove_photoshop_web_items =
+      !features::IsHoldingSpacePhotoshopWebIntegrationEnabled();
   const bool remove_suggestion_items =
       !features::IsHoldingSpaceSuggestionsEnabled();
 
   // No-op when there are no item types we'd attempt to remove.
-  if (!remove_camera_app_items && !remove_suggestion_items) {
+  if (!remove_camera_app_items && !remove_photoshop_web_items &&
+      !remove_suggestion_items) {
     return;
   }
 
@@ -181,6 +184,8 @@
   update->EraseIf([&](const base::Value& persisted_item) {
     auto type = HoldingSpaceItem::DeserializeType(persisted_item.GetDict());
     if ((remove_camera_app_items && HoldingSpaceItem::IsCameraAppType(type)) ||
+        (remove_photoshop_web_items &&
+         type == HoldingSpaceItem::Type::kPhotoshopWeb) ||
         (remove_suggestion_items && HoldingSpaceItem::IsSuggestionType(type))) {
       return true;
     }
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_test_util.h b/chrome/browser/ui/ash/holding_space/holding_space_test_util.h
index 5bd302a..0ebad5e 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_test_util.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_test_util.h
@@ -10,6 +10,7 @@
 
 #include "ash/public/cpp/holding_space/holding_space_item.h"
 #include "base/functional/function_ref.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/keycodes/keyboard_codes_posix.h"
 
@@ -23,6 +24,16 @@
 enum class HoldingSpaceCommandId;
 class HoldingSpaceModel;
 
+// Matchers --------------------------------------------------------------------
+
+// Matcher which allows a new argument to be the subject of comparison, e.g.
+// EXPECT_THAT(a, AllOf(IsFalse(), And(b, IsTrue())));
+MATCHER_P2(And, value, matcher, "") {
+  return ::testing::Value(value, matcher);
+}
+
+// Methods ---------------------------------------------------------------------
+
 // Returns the suggestion items in `model`.
 std::vector<std::pair<HoldingSpaceItem::Type, base::FilePath>>
 GetSuggestionsInModel(const HoldingSpaceModel& model);
diff --git a/chrome/browser/ui/download/download_bubble_row_view_info.cc b/chrome/browser/ui/download/download_bubble_row_view_info.cc
index 98ea000..74eca17 100644
--- a/chrome/browser/ui/download/download_bubble_row_view_info.cc
+++ b/chrome/browser/ui/download/download_bubble_row_view_info.cc
@@ -166,6 +166,10 @@
       } else {
         if (base::FeatureList::IsEnabled(
                 safe_browsing::kImprovedDownloadBubbleWarnings)) {
+          if (WasSafeBrowsingVerdictObtained(model_->GetDownloadItem())) {
+            PopulateSuspiciousUiPattern();
+            return;
+          }
           if (ShouldShowWarningForNoSafeBrowsing(model_->profile())) {
             PopulateForFileTypeWarningNoSafeBrowsing();
             return;
diff --git a/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper.cc b/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper.cc
index 4ecd9928..5894a87 100644
--- a/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper.cc
+++ b/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper.cc
@@ -154,7 +154,6 @@
 
 // `SearchEngineChoiceService` may need to suppress this dialog to avoid
 // dialog conflicts and too frequent promos.
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
   SearchEngineChoiceService* search_engine_choice_service =
       SearchEngineChoiceServiceFactory::GetForProfile(profile());
   if (search_engine_choice_service &&
@@ -164,7 +163,6 @@
                                       kSearchEngineChoiceDialogShown);
     return;
   }
-#endif
 
   auto* browser =
       chrome::FindBrowserWithTab(navigation_handle->GetWebContents());
diff --git a/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper_browsertest.cc b/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper_browsertest.cc
index 59f01fe..7edd9cbd 100644
--- a/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper_browsertest.cc
+++ b/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper_browsertest.cc
@@ -20,7 +20,6 @@
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/privacy_sandbox/privacy_sandbox_features.h"
 #include "components/search_engines/search_engines_switches.h"
-#include "components/signin/public/base/signin_buildflags.h"
 #include "components/signin/public/base/signin_switches.h"
 #include "components/sync/test/test_sync_service.h"
 #include "content/public/common/url_constants.h"
@@ -658,9 +657,6 @@
                     PrivacySandboxService::PromptType::kM1NoticeROW,
                     PrivacySandboxService::PromptType::kM1NoticeRestricted));
 
-// Checking the  `ENABLE_SEARCH_ENGINE_CHOICE` build flag is needed because the
-// test runs on Fuchsia while the search engine choice code doesn't.
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
 class PrivacySandboxPromptHelperTestWithSearchEngineChoiceEnabled
     : public PrivacySandboxPromptHelperTestWithParam {
  public:
@@ -730,4 +726,3 @@
     testing::Values(PrivacySandboxService::PromptType::kM1Consent,
                     PrivacySandboxService::PromptType::kM1NoticeEEA,
                     PrivacySandboxService::PromptType::kM1NoticeROW));
-#endif  // BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 62b3d7d..bdf33f24 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -95,7 +95,6 @@
 #include "chrome/browser/ui/recently_audible_helper.h"
 #include "chrome/browser/ui/safety_hub/unused_site_permissions_service.h"
 #include "chrome/browser/ui/safety_hub/unused_site_permissions_service_factory.h"
-#include "chrome/browser/ui/search_engine_choice/search_engine_choice_tab_helper.h"
 #include "chrome/browser/ui/search_engines/search_engine_tab_helper.h"
 #include "chrome/browser/ui/side_panel/companion/companion_utils.h"
 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
@@ -190,6 +189,7 @@
 #include "chrome/browser/ui/javascript_dialogs/javascript_tab_modal_dialog_manager_delegate_desktop.h"
 #include "chrome/browser/ui/sad_tab_helper.h"
 #include "chrome/browser/ui/search/search_tab_helper.h"
+#include "chrome/browser/ui/search_engine_choice/search_engine_choice_tab_helper.h"
 #include "chrome/browser/ui/side_panel/companion/companion_tab_helper.h"
 #include "chrome/browser/ui/side_panel/companion/exps_registration_success_observer.h"
 #include "chrome/browser/ui/side_panel/customize_chrome/customize_chrome_tab_helper.h"
@@ -569,12 +569,10 @@
     PrivacySandboxPromptHelper::CreateForWebContents(web_contents);
   }
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
   if (search_engines::IsChoiceScreenFlagEnabled(
           search_engines::ChoicePromo::kDialog)) {
     SearchEngineChoiceTabHelper::CreateForWebContents(web_contents);
   }
-#endif
 
   SadTabHelper::CreateForWebContents(web_contents);
   SearchTabHelper::CreateForWebContents(web_contents);
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 39a628a..36a9e14 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -335,6 +335,12 @@
              "TabSearchUseMetricsReporter",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Enables creating a web app window when tearing off a tab with a url
+// controlled by a web app.
+BASE_FEATURE(kTearOffWebAppTabOpensWebAppWindow,
+             "TearOffWebAppTabOpensWebAppWindow",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kToolbarUseHardwareBitmapDraw,
              "ToolbarUseHardwareBitmapDraw",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index 1f36a74a..678bc8ca 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -213,6 +213,8 @@
 
 BASE_DECLARE_FEATURE(kTabSearchUseMetricsReporter);
 
+BASE_DECLARE_FEATURE(kTearOffWebAppTabOpensWebAppWindow);
+
 // Determines how screenshots of the toolbar uses Software or Hardware drawing.
 // Works on Android 10+.
 BASE_DECLARE_FEATURE(kToolbarUseHardwareBitmapDraw);
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
index 40e65298..c7a2f4f 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
@@ -333,7 +333,7 @@
 void SavedTabGroupBar::HandleDrop() {
   saved_tab_group_model_->ReorderGroupLocally(drag_data_->guid(),
                                               GetDropIndex().value());
-  drag_data_.release();
+  drag_data_.reset();
   SchedulePaint();
 }
 
@@ -397,12 +397,6 @@
   SchedulePaint();
 }
 
-void SavedTabGroupBar::OnDragDone() {
-  drag_data_.release();
-  HideOverflowMenu();
-  SchedulePaint();
-}
-
 views::View::DropCallback SavedTabGroupBar::GetDropCallback(
     const ui::DropTargetEvent& event) {
   return base::BindOnce(
@@ -481,6 +475,7 @@
   const int last_visible_button_index =
       CalculateLastVisibleButtonIndexForWidth(width());
   UpdateButtonVisibilities(should_show_overflow, last_visible_button_index);
+  UpdateOverflowMenu();
 }
 
 int SavedTabGroupBar::CalculatePreferredWidthRestrictedBy(int max_width) const {
@@ -541,15 +536,13 @@
   }
   AddTabGroupButton(*saved_tab_group_model_->Get(guid), index.value());
 
-  HideOverflowMenu();
-  PreferredSizeChanged();
+  InvalidateLayout();
 }
 
 void SavedTabGroupBar::SavedTabGroupRemoved(const base::Uuid& guid) {
   RemoveTabGroupButton(guid);
 
-  HideOverflowMenu();
-  PreferredSizeChanged();
+  InvalidateLayout();
 }
 
 void SavedTabGroupBar::SavedTabGroupUpdated(const base::Uuid& guid) {
@@ -564,8 +557,7 @@
 
   button->UpdateButtonData(*group);
 
-  HideOverflowMenu();
-  PreferredSizeChanged();
+  InvalidateLayout();
 }
 
 void SavedTabGroupBar::SavedTabGroupReordered() {
@@ -590,8 +582,7 @@
   // Ensure the overflow button is the last button in the view hierarchy.
   ReorderChildView(overflow_button_, children().size());
 
-  HideOverflowMenu();
-  PreferredSizeChanged();
+  InvalidateLayout();
 }
 
 void SavedTabGroupBar::LoadAllButtonsFromModel() {
@@ -655,33 +646,11 @@
     return;
   }
 
-  // 1. Build the vertical list of buttons in the over flow menu.
   auto overflow_menu = std::make_unique<OverflowMenu>(*this);
   overflow_menu->SetProperty(views::kElementIdentifierKey,
                              kSavedTabGroupOverflowMenuId);
 
-  // Add all buttons that are not currently visible to the overflow menu.
-  for (const auto* const child : children()) {
-    if (child->GetVisible() ||
-        !views::IsViewClass<SavedTabGroupButton>(child)) {
-      continue;
-    }
-
-    const SavedTabGroupButton* const button =
-        views::AsViewClass<SavedTabGroupButton>(child);
-    const SavedTabGroup* const group =
-        saved_tab_group_model_->Get(button->guid());
-
-    overflow_menu->AddChildView(std::make_unique<SavedTabGroupButton>(
-        *group,
-        base::BindRepeating(&SavedTabGroupBar::page_navigator,
-                            base::Unretained(this)),
-        base::BindRepeating(&SavedTabGroupBar::OnTabGroupButtonPressed,
-                            base::Unretained(this), group->saved_guid()),
-        browser_, animations_enabled_));
-  }
-
-  // Make the list of buttons vertical.
+  // 1. Layout the menu as a vertical list.
   const gfx::Insets insets = gfx::Insets::TLBR(16, 16, 16, 48);
   auto box = std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical, insets,
@@ -707,16 +676,48 @@
   overflow_menu_ =
       views::AsViewClass<OverflowMenu>(bubble_delegate->GetContentsView());
 
-  // 3. Display the menu.
+  // 3. Populate the menu.
+  UpdateOverflowMenu();
+
+  // 4. Display the menu.
   auto* const widget =
       views::BubbleDialogDelegate::CreateBubble(std::move(bubble_delegate));
   widget_observation_.Observe(widget);
   widget->Show();
 }
 
-void SavedTabGroupBar::HideOverflowMenu() {
-  if (bubble_delegate_) {
-    bubble_delegate_->CancelDialog();
+void SavedTabGroupBar::UpdateOverflowMenu() {
+  // Don't update the overflow menu if it doesn't exist.
+  if (!overflow_menu_) {
+    return;
+  }
+
+  // Remove all existing children.
+  overflow_menu_->RemoveAllChildViews();
+
+  // Add all buttons that are not currently visible to the overflow menu.
+  for (const auto* const child : children()) {
+    if (child->GetVisible() ||
+        !views::IsViewClass<SavedTabGroupButton>(child)) {
+      continue;
+    }
+
+    const SavedTabGroupButton* const button =
+        views::AsViewClass<SavedTabGroupButton>(child);
+    const SavedTabGroup* const group =
+        saved_tab_group_model_->Get(button->guid());
+
+    overflow_menu_->AddChildView(std::make_unique<SavedTabGroupButton>(
+        *group,
+        base::BindRepeating(&SavedTabGroupBar::page_navigator,
+                            base::Unretained(this)),
+        base::BindRepeating(&SavedTabGroupBar::OnTabGroupButtonPressed,
+                            base::Unretained(this), group->saved_guid()),
+        browser_, animations_enabled_));
+  }
+
+  if (overflow_menu_->GetWidget()) {
+    bubble_delegate_->SizeToContents();
   }
 }
 
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.h b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.h
index acee723..a0563045 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.h
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.h
@@ -65,7 +65,6 @@
   void OnDragEntered(const ui::DropTargetEvent& event) override;
   int OnDragUpdated(const ui::DropTargetEvent& event) override;
   void OnDragExited() override;
-  void OnDragDone() override;
   views::View::DropCallback GetDropCallback(
       const ui::DropTargetEvent& event) override;
   void OnPaint(gfx::Canvas* canvas) override;
@@ -144,8 +143,8 @@
   // group into the tabstrip.
   void MaybeShowOverflowMenu();
 
-  // Hides the overflow menu if it is open.
-  void HideOverflowMenu();
+  // Updates the contents of the overflow menu if it is open.
+  void UpdateOverflowMenu();
 
   // TODO: Move implementation inside of STGOverflowButton.
   void HideOverflowButton();
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc
index c521d8e..f357afb 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc
@@ -147,6 +147,26 @@
     });
   }
 
+  StepBuilder SaveGroupViaModel(const tab_groups::TabGroupId local_group) {
+    return Do([=]() {
+      SavedTabGroupKeyedService* const service =
+          SavedTabGroupServiceFactory::GetForProfile(browser()->profile());
+
+      service->SaveGroup(local_group);
+      ASSERT_NE(nullptr, service->model()->Get(local_group));
+    });
+  }
+
+  StepBuilder UnsaveGroupViaModel(const tab_groups::TabGroupId local_group) {
+    return Do([=]() {
+      SavedTabGroupKeyedService* const service =
+          SavedTabGroupServiceFactory::GetForProfile(browser()->profile());
+
+      service->model()->Remove(local_group);
+      ASSERT_EQ(nullptr, service->model()->Get(local_group));
+    });
+  }
+
   std::unique_ptr<content::WebContents> CreateWebContents() {
     return content::WebContents::Create(
         content::WebContents::CreateParams(browser()->profile()));
@@ -524,3 +544,98 @@
       SavedTabGroupServiceFactory::GetForProfile(browser()->profile())->model();
   EXPECT_EQ(1, model->GetIndexOf(group_id_1).value());
 }
+
+IN_PROC_BROWSER_TEST_F(SavedTabGroupInteractiveTest,
+                       OverflowMenuUpdatesWhileOpen) {
+  // Add 5 additional tabs to the browser.
+  ASSERT_TRUE(
+      AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED));
+  ASSERT_TRUE(
+      AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED));
+  ASSERT_TRUE(
+      AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED));
+  ASSERT_TRUE(
+      AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED));
+  ASSERT_TRUE(
+      AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED));
+  ASSERT_EQ(6, browser()->tab_strip_model()->count());
+
+  // Add each tab to a separate group.
+  const tab_groups::TabGroupId group_1 =
+      browser()->tab_strip_model()->AddToNewGroup({0});
+  const tab_groups::TabGroupId group_2 =
+      browser()->tab_strip_model()->AddToNewGroup({1});
+  const tab_groups::TabGroupId group_3 =
+      browser()->tab_strip_model()->AddToNewGroup({2});
+  const tab_groups::TabGroupId group_4 =
+      browser()->tab_strip_model()->AddToNewGroup({3});
+  const tab_groups::TabGroupId group_5 =
+      browser()->tab_strip_model()->AddToNewGroup({4});
+  const tab_groups::TabGroupId group_6 =
+      browser()->tab_strip_model()->AddToNewGroup({5});
+
+  int menu_widget_height;
+
+  RunTestSequence(
+      // Show the bookmarks bar where the buttons will be displayed.
+      FinishTabstripAnimations(), ShowBookmarksBar(),
+      // Ensure no saved group buttons in the bookmarks bar are present.
+      EnsureNotPresent(kSavedTabGroupButtonElementId),
+
+      // Add views until we get an overflow button.
+      SaveGroupAndCloseEditorBubble(group_1), FinishTabstripAnimations(),
+      SaveGroupAndCloseEditorBubble(group_2), FinishTabstripAnimations(),
+      SaveGroupAndCloseEditorBubble(group_3), FinishTabstripAnimations(),
+      SaveGroupAndCloseEditorBubble(group_4), FinishTabstripAnimations(),
+      SaveGroupAndCloseEditorBubble(group_5), FinishTabstripAnimations(),
+      WaitForShow(kSavedTabGroupOverflowButtonElementId), FlushEvents(),
+
+      // Show the overflow menu.
+      PressButton(kSavedTabGroupOverflowButtonElementId),
+      WaitForShow(kSavedTabGroupOverflowMenuId, true), Do([=]() {
+        BrowserView::GetBrowserViewForBrowser(browser())
+            ->GetWidget()
+            ->LayoutRootViewIfNecessary();
+      }),
+      FlushEvents(),
+      CheckView(kSavedTabGroupOverflowMenuId,
+                [&menu_widget_height](views::View* el) {
+                  menu_widget_height = el->GetWidget()->GetSize().height();
+                  return true;
+                }),
+
+      // Verify the overflow menu expands if another group is added.
+      SaveGroupViaModel(group_6), Do([=]() {
+        BrowserView::GetBrowserViewForBrowser(browser())
+            ->GetWidget()
+            ->LayoutRootViewIfNecessary();
+      }),
+      FlushEvents(),
+      CheckView(kSavedTabGroupOverflowMenuId,
+                [&menu_widget_height](views::View* el) {
+                  const int old_height = menu_widget_height;
+                  menu_widget_height = el->GetWidget()->GetSize().height();
+                  return menu_widget_height > old_height;
+                }),
+
+      // Verify the overflow menu shrinks if a group is removed.
+      UnsaveGroupViaModel(group_6), Do([=]() {
+        BrowserView::GetBrowserViewForBrowser(browser())
+            ->GetWidget()
+            ->LayoutRootViewIfNecessary();
+      }),
+      FlushEvents(),
+      CheckView(kSavedTabGroupOverflowMenuId,
+                [&menu_widget_height](views::View* el) {
+                  const int old_height = menu_widget_height;
+                  menu_widget_height = el->GetWidget()->GetSize().height();
+                  return menu_widget_height < old_height;
+                }),
+
+      // Hide the overflow menu.
+      FlushEvents(),
+      SendAccelerator(
+          kSavedTabGroupOverflowMenuId,
+          ui::Accelerator(ui::KeyboardCode::VKEY_ESCAPE, ui::EF_NONE)),
+      WaitForHide(kSavedTabGroupOverflowMenuId));
+}
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_interactive_uitest.cc b/chrome/browser/ui/views/download/bubble/download_bubble_interactive_uitest.cc
index 5bdeb805..4381513 100644
--- a/chrome/browser/ui/views/download/bubble/download_bubble_interactive_uitest.cc
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_interactive_uitest.cc
@@ -6,7 +6,10 @@
 #include "build/buildflag.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/download/bubble/download_bubble_prefs.h"
+#include "chrome/browser/download/chrome_download_manager_delegate.h"
 #include "chrome/browser/download/download_browsertest_utils.h"
+#include "chrome/browser/download/download_core_service.h"
+#include "chrome/browser/download/download_core_service_factory.h"
 #include "chrome/browser/ui/accelerator_utils.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_test.h"
@@ -16,12 +19,13 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/common/chrome_features.h"
+#include "chrome/test/base/ui_test_utils.h"
 #include "chrome/test/interaction/interactive_browser_test.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/feature_engagement/test/scoped_iph_feature_list.h"
 #include "components/user_education/test/feature_promo_test_util.h"
 #include "content/public/test/browser_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
+#include "content/public/test/download_test_observer.h"
 #include "ui/views/widget/any_widget_observer.h"
 #include "url/gurl.h"
 
@@ -56,12 +60,51 @@
 }
 #endif
 
+// TODO(chlily): Deduplicate this helper class into a test utils file.
+class TestDownloadManagerDelegate : public ChromeDownloadManagerDelegate {
+ public:
+  explicit TestDownloadManagerDelegate(Profile* profile)
+      : ChromeDownloadManagerDelegate(profile) {
+    GetDownloadIdReceiverCallback().Run(download::DownloadItem::kInvalidId + 1);
+  }
+  ~TestDownloadManagerDelegate() override {}
+
+  bool DetermineDownloadTarget(
+      download::DownloadItem* item,
+      content::DownloadTargetCallback* callback) override {
+    content::DownloadTargetCallback dangerous_callback = base::BindOnce(
+        &TestDownloadManagerDelegate::SetDangerous, std::move(*callback));
+    bool run = ChromeDownloadManagerDelegate::DetermineDownloadTarget(
+        item, &dangerous_callback);
+    // ChromeDownloadManagerDelegate::DetermineDownloadTarget() needs to run the
+    // |callback|.
+    DCHECK(run);
+    DCHECK(!dangerous_callback);
+    return true;
+  }
+
+  static void SetDangerous(content::DownloadTargetCallback callback,
+                           const base::FilePath& target_path,
+                           download::DownloadItem::TargetDisposition disp,
+                           download::DownloadDangerType danger_type,
+                           download::DownloadItem::InsecureDownloadStatus ids,
+                           const base::FilePath& intermediate_path,
+                           const base::FilePath& display_name,
+                           const std::string& mime_type,
+                           download::DownloadInterruptReason reason) {
+    std::move(callback).Run(target_path, disp,
+                            download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, ids,
+                            intermediate_path, display_name, mime_type, reason);
+  }
+};
+
 class DownloadBubbleInteractiveUiTest : public DownloadTestBase,
                                         public InteractiveBrowserTestApi {
  public:
   DownloadBubbleInteractiveUiTest() {
     test_features_.InitAndEnableFeatures(
-        {feature_engagement::kIPHDownloadToolbarButtonFeature
+        {feature_engagement::kIPHDownloadEsbPromoFeature,
+         feature_engagement::kIPHDownloadToolbarButtonFeature
 #if BUILDFLAG(IS_MAC)
          ,
          features::kImmersiveFullscreen
@@ -124,17 +167,15 @@
         download_toolbar_button(), active);
   }
 
-  auto DownloadBubblePromoIsActive(bool active) {
+  auto DownloadBubblePromoIsActive(bool active, const base::Feature& feature) {
     return base::BindOnce(
         [](DownloadToolbarButtonView* download_toolbar_button, Browser* browser,
-           bool active) {
-          return active ==
-                 BrowserView::GetBrowserViewForBrowser(browser)
-                     ->GetFeaturePromoController()
-                     ->IsPromoActive(
-                         feature_engagement::kIPHDownloadToolbarButtonFeature);
+           bool active, const base::Feature& feature) {
+          return active == BrowserView::GetBrowserViewForBrowser(browser)
+                               ->GetFeaturePromoController()
+                               ->IsPromoActive(feature);
         },
-        download_toolbar_button(), browser(), active);
+        download_toolbar_button(), browser(), active, std::cref(feature));
   }
 
   auto ChangeButtonVisibility(bool visible) {
@@ -168,6 +209,26 @@
         [this, url]() { DownloadAndWait(browser(), url); });
   }
 
+  auto DownloadDangerousTestFile() {
+    // Set up the fake delegate that forces the download to be malicious.
+    std::unique_ptr<TestDownloadManagerDelegate> test_delegate(
+        new TestDownloadManagerDelegate(browser()->profile()));
+    DownloadCoreServiceFactory::GetForBrowserContext(browser()->profile())
+        ->SetDownloadManagerDelegateForTesting(std::move(test_delegate));
+    GURL url = embedded_test_server()->GetURL(
+        DownloadTestBase::kDangerousMockFilePath);
+
+    EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+    return base::BindLambdaForTesting([this]() {
+      std::unique_ptr<content::DownloadTestObserver> waiter{
+          DangerousDownloadWaiter(
+              browser(), /*num_downloads=*/1,
+              content::DownloadTestObserver::DangerousDownloadAction::
+                  ON_DANGEROUS_DOWNLOAD_QUIT)};
+      waiter->WaitForFinished();
+    });
+  }
+
 #if !BUILDFLAG(IS_MAC)
   // Check for whether the exclusive access bubble is shown ("Press Esc to
   // exit fullscreen" or other similar message).
@@ -242,21 +303,23 @@
 
 IN_PROC_BROWSER_TEST_F(DownloadBubbleInteractiveUiTest,
                        DownloadBubbleInteractedWith_NoIPHShown) {
-  RunTestSequence(Do(ChangeButtonVisibility(true)),
-                  WaitForShow(kToolbarDownloadButtonElementId),
-                  Check(DownloadBubbleIsShowingDetails(false)),
-                  // Press the button to register an interaction (which should
-                  // suppress the IPH) which opens the main view.
-                  PressButton(kToolbarDownloadButtonElementId),
-                  // Close the main view.
-                  Do(ChangeBubbleVisibility(false)),
-                  // Now download a file to show the partial view, if enabled.
-                  Do(DownloadTestFile()),
-                  Check(DownloadBubbleIsShowingDetails(IsPartialViewEnabled())),
-                  // Hide the partial view, if enabled. No IPH is shown.
-                  Do(ChangeBubbleVisibility(false)),
-                  Check(DownloadBubbleIsShowingDetails(false)),
-                  Check(DownloadBubblePromoIsActive(false)));
+  RunTestSequence(
+      Do(ChangeButtonVisibility(true)),
+      WaitForShow(kToolbarDownloadButtonElementId),
+      Check(DownloadBubbleIsShowingDetails(false)),
+      // Press the button to register an interaction (which should
+      // suppress the IPH) which opens the main view.
+      PressButton(kToolbarDownloadButtonElementId),
+      // Close the main view.
+      Do(ChangeBubbleVisibility(false)),
+      // Now download a file to show the partial view, if enabled.
+      Do(DownloadTestFile()),
+      Check(DownloadBubbleIsShowingDetails(IsPartialViewEnabled())),
+      // Hide the partial view, if enabled. No IPH is shown.
+      Do(ChangeBubbleVisibility(false)),
+      Check(DownloadBubbleIsShowingDetails(false)),
+      Check(DownloadBubblePromoIsActive(
+          false, feature_engagement::kIPHDownloadToolbarButtonFeature)));
 }
 
 IN_PROC_BROWSER_TEST_F(DownloadBubbleInteractiveUiTest,
@@ -267,9 +330,41 @@
                   // Hide the partial view, if enabled. The IPH should be shown.
                   Do(ChangeBubbleVisibility(false)),
                   Check(DownloadBubbleIsShowingDetails(false)),
-                  Check(DownloadBubblePromoIsActive(IsPartialViewEnabled())));
+                  Check(DownloadBubblePromoIsActive(
+                      IsPartialViewEnabled(),
+                      feature_engagement::kIPHDownloadToolbarButtonFeature)));
 }
 
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+IN_PROC_BROWSER_TEST_F(DownloadBubbleInteractiveUiTest,
+                       DownloadBubbleEsbShownAfterDownload_IPHShown) {
+  RunTestSequence(Do(DownloadDangerousTestFile()),
+                  WaitForShow(kToolbarDownloadButtonElementId),
+                  Check(DownloadBubbleIsShowingDetails(IsPartialViewEnabled())),
+                  // Hide the partial view, if enabled. The IPH should be shown.
+                  Do(ChangeBubbleVisibility(false)),
+                  Check(DownloadBubbleIsShowingDetails(false)),
+                  Check(DownloadBubblePromoIsActive(
+                      IsPartialViewEnabled(),
+                      feature_engagement::kIPHDownloadEsbPromoFeature)));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    DownloadBubbleInteractiveUiTest,
+    DownloadBubbleEsbShownAfterDownloadWithoutSafeBrowsing_NoIPHShown) {
+  browser()->profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled,
+                                               false);
+  RunTestSequence(Do(DownloadDangerousTestFile()),
+                  WaitForShow(kToolbarDownloadButtonElementId),
+                  Check(DownloadBubbleIsShowingDetails(IsPartialViewEnabled())),
+                  // Hide the partial view, if enabled. The IPH should be shown.
+                  Do(ChangeBubbleVisibility(false)),
+                  Check(DownloadBubbleIsShowingDetails(false)),
+                  Check(DownloadBubblePromoIsActive(
+                      false, feature_engagement::kIPHDownloadEsbPromoFeature)));
+}
+#endif
+
 // This test is only for ChromeOS and Mac where we have immersive fullscreen.
 #if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_MAC)
 IN_PROC_BROWSER_TEST_F(DownloadBubbleInteractiveUiTest,
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
index 21d3794..dfbc53e 100644
--- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
+++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
@@ -41,6 +41,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/safe_browsing/core/common/features.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/user_education/common/user_education_class_properties.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -716,6 +717,17 @@
 }
 
 void DownloadToolbarButtonView::OnPartialViewClosed() {
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  Profile* profile = browser_->profile();
+  if (safe_browsing::GetSafeBrowsingState(*profile->GetPrefs()) ==
+          safe_browsing::SafeBrowsingState::STANDARD_PROTECTION &&
+      !profile->IsOffTheRecord() &&
+      browser_->window()->MaybeShowFeaturePromo(
+          feature_engagement::kIPHDownloadEsbPromoFeature)) {
+    return;
+  }
+#endif
+
   if (download::ShouldSuppressDownloadBubbleIph(
           browser_->profile()->GetOriginalProfile())) {
     return;
diff --git a/chrome/browser/ui/views/find_bar_view.cc b/chrome/browser/ui/views/find_bar_view.cc
index 793816d..a2f89eec 100644
--- a/chrome/browser/ui/views/find_bar_view.cc
+++ b/chrome/browser/ui/views/find_bar_view.cc
@@ -65,6 +65,9 @@
 
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(FindBarView, kElementId);
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(FindBarView, kTextField);
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(FindBarView, kPreviousButtonElementId);
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(FindBarView, kNextButtonElementId);
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(FindBarView, kCloseButtonElementId);
 
 class FindBarMatchCountLabel : public views::Label {
  public:
@@ -224,6 +227,8 @@
               .SetAccessibleName(
                   l10n_util::GetStringUTF16(IDS_ACCNAME_PREVIOUS))
               .SetID(VIEW_ID_FIND_IN_PAGE_PREVIOUS_BUTTON)
+              .SetProperty(views::kElementIdentifierKey,
+                           kPreviousButtonElementId)
               .SetTooltipText(
                   l10n_util::GetStringUTF16(IDS_FIND_IN_PAGE_PREVIOUS_TOOLTIP))
               .SetCallback(base::BindRepeating(&FindBarView::FindNext,
@@ -233,6 +238,7 @@
               .CopyAddressTo(&find_next_button_)
               .SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_NEXT))
               .SetID(VIEW_ID_FIND_IN_PAGE_NEXT_BUTTON)
+              .SetProperty(views::kElementIdentifierKey, kNextButtonElementId)
               .SetTooltipText(
                   l10n_util::GetStringUTF16(IDS_FIND_IN_PAGE_NEXT_TOOLTIP))
               .SetCallback(base::BindRepeating(&FindBarView::FindNext,
@@ -241,6 +247,7 @@
           views::Builder<views::ImageButton>()
               .CopyAddressTo(&close_button_)
               .SetID(VIEW_ID_FIND_IN_PAGE_CLOSE_BUTTON)
+              .SetProperty(views::kElementIdentifierKey, kCloseButtonElementId)
               .SetTooltipText(
                   l10n_util::GetStringUTF16(IDS_FIND_IN_PAGE_CLOSE_TOOLTIP))
               .SetAnimationDuration(base::TimeDelta())
diff --git a/chrome/browser/ui/views/find_bar_view.h b/chrome/browser/ui/views/find_bar_view.h
index 6f0e3a1b..a29fc8c 100644
--- a/chrome/browser/ui/views/find_bar_view.h
+++ b/chrome/browser/ui/views/find_bar_view.h
@@ -51,6 +51,9 @@
   // Element IDs for ui::ElementTracker
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kElementId);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kTextField);
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kPreviousButtonElementId);
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kNextButtonElementId);
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kCloseButtonElementId);
 
   explicit FindBarView(FindBarHost* host = nullptr);
 
diff --git a/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc b/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc
index 5689554..cbfc95e 100644
--- a/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc
+++ b/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc
@@ -64,6 +64,53 @@
 DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTabBId);
 }  // namespace
 
+struct FindResultState {
+  static const int kInitialActiveMatchOrdinalCount = -1;
+  static const int kInitialNumberOfMatches = -1;
+  int active_match_ordinal = kInitialActiveMatchOrdinalCount;
+  int number_of_matches = kInitialNumberOfMatches;
+  FindResultState()
+      : FindResultState(kInitialActiveMatchOrdinalCount,
+                        kInitialNumberOfMatches) {}
+  FindResultState(int active_match_ordinal, int number_of_matches)
+      : active_match_ordinal(active_match_ordinal),
+        number_of_matches(number_of_matches) {}
+
+  bool operator==(const FindResultState& b) const = default;
+};
+
+class FindResulStateObserver : public ui::test::ObservationStateObserver<
+                                   FindResultState,
+                                   find_in_page::FindTabHelper,
+                                   find_in_page::FindResultObserver> {
+ public:
+  explicit FindResulStateObserver(find_in_page::FindTabHelper* find_tab_helper)
+      : ObservationStateObserver(find_tab_helper) {}
+  ~FindResulStateObserver() override = default;
+
+  // ObservationStateObserver:
+  FindResultState GetStateObserverInitialState() const override {
+    return FindResultState();
+  }
+
+  // FindResultObserver:
+  void OnFindResultAvailable(content::WebContents* web_contents) override {
+    const find_in_page::FindNotificationDetails& find_details =
+        find_in_page::FindTabHelper::FromWebContents(web_contents)
+            ->find_result();
+
+    if (!find_details.final_update()) {
+      return;
+    }
+
+    FindResultState result = {find_details.active_match_ordinal(),
+                              find_details.number_of_matches()};
+
+    OnStateObserverStateChanged(result);
+  }
+};
+DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(FindResulStateObserver, kFindResultState);
+
 // TODO(crbug.com/1509945): Remaining tests should be migrated to
 // FindInPageTest.
 class LegacyFindInPageTest : public InProcessBrowserTest {
@@ -286,7 +333,7 @@
   EXPECT_TRUE(IsViewFocused(browser(), VIEW_ID_FIND_IN_PAGE_PREVIOUS_BUTTON));
 }
 
-IN_PROC_BROWSER_TEST_F(LegacyFindInPageTest, ButtonsDisabledWithoutText) {
+IN_PROC_BROWSER_TEST_F(FindInPageTest, ButtonsDisabledWithoutText) {
   if (browser()
           ->GetFindBarController()
           ->find_bar()
@@ -296,22 +343,17 @@
     return;
   }
 
-  ASSERT_TRUE(embedded_test_server()->Start());
-  // Make sure Chrome is in the foreground, otherwise sending input
-  // won't do anything and the test will hang.
-  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
-  // First we navigate to any page.
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), embedded_test_server()->GetURL(kSimplePage)));
-  // Show the Find bar.
-  browser()->GetFindBarController()->Show();
-  EXPECT_TRUE(IsViewFocused(browser(), VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
+  const GURL page_a = embedded_test_server()->GetURL("/a.html");
 
-  // The buttons should be disabled as there is no text entered in the find bar
-  // and no search has been issued yet.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_TAB, false,
-                                              false, false, false));
-  EXPECT_TRUE(IsViewFocused(browser(), VIEW_ID_FIND_IN_PAGE_CLOSE_BUTTON));
+  RunTestSequence(InstrumentTab(kTabId), NavigateWebContents(kTabId, page_a),
+                  ShowFindBar(),
+                  CheckViewProperty(FindBarView::kPreviousButtonElementId,
+                                    &views::View::GetEnabled, false),
+                  CheckViewProperty(FindBarView::kNextButtonElementId,
+                                    &views::View::GetEnabled, false),
+                  SendKeyPress(ui::VKEY_TAB, false, false),
+                  CheckViewProperty(FindBarView::kCloseButtonElementId,
+                                    &views::View::HasFocus, true));
 }
 
 IN_PROC_BROWSER_TEST_F(LegacyFindInPageTest, FocusRestore) {
@@ -797,28 +839,19 @@
 }
 
 // See http://crbug.com/1142027
-IN_PROC_BROWSER_TEST_F(LegacyFindInPageTest, MatchOrdinalStableWhileTyping) {
-  // Make sure Chrome is in the foreground, otherwise sending input
-  // won't do anything and the test will hang.
-  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
-                                           GURL("data:text/html,foo foo foo")));
-  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-
-  browser()->GetFindBarController()->Show();
-  EXPECT_TRUE(IsViewFocused(browser(), VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
-
-  ui_test_utils::FindResultWaiter waiter1(web_contents, 1 /*request_offset*/);
-  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
-      browser(), ui::VKEY_F, false, false, false, false));
-  waiter1.Wait();
-  EXPECT_EQ(1, waiter1.active_match_ordinal());
-  EXPECT_EQ(3, waiter1.number_of_matches());
-
-  ui_test_utils::FindResultWaiter waiter2(web_contents, 1 /*request_offset*/);
-  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
-      browser(), ui::VKEY_O, false, false, false, false));
-  waiter2.Wait();
-  EXPECT_EQ(1, waiter2.active_match_ordinal());
-  EXPECT_EQ(3, waiter2.number_of_matches());
+IN_PROC_BROWSER_TEST_F(FindInPageTest, MatchOrdinalStableWhileTyping) {
+  const GURL page_foo =
+      embedded_test_server()->GetURL("/find_in_page/foo.html");
+  RunTestSequence(
+      InstrumentTab(kTabId), NavigateWebContents(kTabId, page_foo),
+      ShowFindBar(),
+      ObserveState(kFindResultState,
+                   [this]() {
+                     return find_in_page::FindTabHelper::FromWebContents(
+                         browser()->tab_strip_model()->GetActiveWebContents());
+                   }),
+      EnterText(FindBarView::kTextField, u"f"),
+      WaitForState(kFindResultState, []() { return FindResultState(1, 3); }),
+      EnterText(FindBarView::kTextField, u"o", TextEntryMode::kAppend),
+      WaitForState(kFindResultState, []() { return FindResultState(1, 3); }));
 }
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc b/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
index b92a2fb..60ca5cb 100644
--- a/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
@@ -920,7 +920,13 @@
   EXPECT_TRUE(IsPlayingSessionDisplayedFirst());
 }
 
-IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest, LiveCaption) {
+// TODO(crbug.com/1425041): Live captioning not supported on Arm64 Windows.
+#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_ARM64)
+#define MAYBE_LiveCaption DISABLED_LiveCaption
+#else
+#define MAYBE_LiveCaption LiveCaption
+#endif
+IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest, MAYBE_LiveCaption) {
   // Open a tab and play media.
   OpenTestURL();
   StartPlayback();
diff --git a/chrome/browser/ui/views/profiles/first_run_flow_controller_dice.cc b/chrome/browser/ui/views/profiles/first_run_flow_controller_dice.cc
index 4900403..aa1f0dd 100644
--- a/chrome/browser/ui/views/profiles/first_run_flow_controller_dice.cc
+++ b/chrome/browser/ui/views/profiles/first_run_flow_controller_dice.cc
@@ -41,9 +41,7 @@
 #include "google_apis/gaia/core_account_id.h"
 #include "url/gurl.h"
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
 #include "chrome/browser/search_engine_choice/search_engine_choice_service_factory.h"
-#endif
 
 namespace {
 
@@ -509,7 +507,6 @@
 base::queue<ProfileManagementFlowController::Step>
 FirstRunFlowControllerDice::RegisterPostIdentitySteps() {
   base::queue<ProfileManagementFlowController::Step> post_identity_steps;
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
   auto search_engine_choice_step_completed =
       base::BindOnce(&FirstRunFlowControllerDice::AdvanceToNextPostIdentityStep,
                      base::Unretained(this));
@@ -523,7 +520,6 @@
           std::move(search_engine_choice_step_completed)));
   post_identity_steps.emplace(
       ProfileManagementFlowController::Step::kSearchEngineChoice);
-#endif
 
   auto default_browser_promo_step_completed =
       base::BindOnce(&FirstRunFlowControllerDice::AdvanceToNextPostIdentityStep,
diff --git a/chrome/browser/ui/views/profiles/first_run_interactive_uitest.cc b/chrome/browser/ui/views/profiles/first_run_interactive_uitest.cc
index ca75c4ad..804a90b 100644
--- a/chrome/browser/ui/views/profiles/first_run_interactive_uitest.cc
+++ b/chrome/browser/ui/views/profiles/first_run_interactive_uitest.cc
@@ -48,11 +48,9 @@
 #include "ui/views/interaction/element_tracker_views.h"
 #include "ui/views/view_class_properties.h"
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
 #include "chrome/browser/search_engine_choice/search_engine_choice_service_factory.h"
 #include "components/search_engines/search_engine_choice_utils.h"
 #include "components/search_engines/search_engines_switches.h"
-#endif
 
 #if !BUILDFLAG(ENABLE_DICE_SUPPORT)
 #error "Unsupported platform"
@@ -96,7 +94,6 @@
     {.test_suffix = "Default"},
     {.test_suffix = "WithDefaultBrowserStep",
      .with_default_browser_step = true},
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
     {.test_suffix = "WithSearchEngineChoiceStep",
      .with_search_engine_choice_step = true},
     {.test_suffix = "WithDefaultBrowserAndSearchEngineChoiceSteps",
@@ -105,7 +102,6 @@
     {.test_suffix = "WithSearchEngineChoiceAndPrivacySandboxEnabled",
      .with_search_engine_choice_step = true,
      .with_privacy_sandbox_enabled = true},
-#endif
 };
 
 }  // namespace
@@ -266,7 +262,6 @@
            WithDefaultBrowserStep() ? "forced" : "no"}}});
 
     if (WithSearchEngineChoiceStep()) {
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
       scoped_chrome_build_override_ = std::make_unique<base::AutoReset<bool>>(
           SearchEngineChoiceServiceFactory::ScopedChromeBuildOverrideForTesting(
               /*force_chrome_build=*/true));
@@ -278,9 +273,6 @@
              { switches::kWithForcedScrollEnabled.name,
                "true" }
            }});
-#else
-      NOTREACHED_NORETURN();
-#endif
     } else {
       disabled_features.push_back(switches::kSearchEngineChoice);
       disabled_features.push_back(switches::kSearchEngineChoiceFre);
@@ -329,12 +321,10 @@
       embedded_test_server()->StartAcceptingConnections();
     }
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
     if (WithSearchEngineChoiceStep()) {
       SearchEngineChoiceService::SetDialogDisabledForTests(
           /*dialog_disabled=*/false);
     }
-#endif
   }
 
   bool WithDefaultBrowserStep() const {
@@ -349,7 +339,6 @@
     return GetParam().with_privacy_sandbox_enabled;
   }
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
   auto CompleteSearchEngineChoiceStep() {
     const DeepQuery first_search_engine = {"search-engine-choice-app",
                                            "cr-radio-button"};
@@ -366,7 +355,6 @@
         WaitForButtonEnabled(kWebContentsId, kSearchEngineChoiceActionButton),
         PressJsButton(kWebContentsId, kSearchEngineChoiceActionButton));
   }
-#endif
 
   auto CompleteDefaultBrowserStep() {
     return Steps(
@@ -519,10 +507,8 @@
       PressJsButton(kWebContentsId, kOptInSyncButton)
           .SetMustRemainVisible(false),
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
       If([&] { return WithSearchEngineChoiceStep(); },
          CompleteSearchEngineChoiceStep()),
-#endif
 
       If([&] { return WithDefaultBrowserStep(); },
          CompleteDefaultBrowserStep()));
@@ -551,13 +537,11 @@
         DefaultBrowserChoice::kClickSetAsDefault, 1);
   }
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
   if (WithSearchEngineChoiceStep()) {
     histogram_tester.ExpectBucketCount(
         search_engines::kSearchEngineChoiceScreenEventsHistogram,
         search_engines::SearchEngineChoiceScreenEvents::kFreDefaultWasSet, 1);
   }
-#endif
 
   EXPECT_TRUE(proceed_future.Get());
 
@@ -620,10 +604,8 @@
       EnsurePresent(kWebContentsId, kDontSyncButton),
       PressJsButton(kWebContentsId, kDontSyncButton),
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
       If([&] { return WithSearchEngineChoiceStep(); },
          CompleteSearchEngineChoiceStep()),
-#endif
       If([&] { return WithDefaultBrowserStep(); },
          CompleteDefaultBrowserStep()));
 
@@ -692,13 +674,11 @@
       browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
       GURL(chrome::kChromeUISettingsURL).Resolve(chrome::kSyncSetupSubPage));
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
   if (WithSearchEngineChoiceStep()) {
     SearchEngineChoiceService* search_engine_choice_service =
         SearchEngineChoiceServiceFactory::GetForProfile(profile());
     EXPECT_FALSE(search_engine_choice_service->IsShowingDialog(browser()));
   }
-#endif
 
   EXPECT_TRUE(proceed_future.Get());
   EXPECT_EQ(base::ASCIIToUTF16(kTestGivenName), GetProfileName());
@@ -754,10 +734,8 @@
       CheckJsResultAt(kWebContentsId, kDontSignInButton, "(e) => !e.disabled"),
       PressJsButton(kWebContentsId, kDontSignInButton),
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
       If([&] { return WithSearchEngineChoiceStep(); },
          CompleteSearchEngineChoiceStep()),
-#endif
       If([&] { return WithDefaultBrowserStep(); },
          CompleteDefaultBrowserStep()));
 
@@ -827,10 +805,8 @@
       EnsurePresent(kWebContentsId, kDeclineManagementButton),
       PressJsButton(kWebContentsId, kDeclineManagementButton),
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
       If([&] { return WithSearchEngineChoiceStep(); },
          CompleteSearchEngineChoiceStep()),
-#endif
       If([&] { return WithDefaultBrowserStep(); },
          CompleteDefaultBrowserStep()));
 
diff --git a/chrome/browser/ui/views/profiles/profile_management_flow_controller.h b/chrome/browser/ui/views/profiles/profile_management_flow_controller.h
index 136b77c..ff0bcca8 100644
--- a/chrome/browser/ui/views/profiles/profile_management_flow_controller.h
+++ b/chrome/browser/ui/views/profiles/profile_management_flow_controller.h
@@ -11,7 +11,6 @@
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ui/views/profiles/profile_management_types.h"
 #include "chrome/browser/ui/views/profiles/profile_picker_web_contents_host.h"
-#include "components/signin/public/base/signin_buildflags.h"
 
 class Profile;
 class ProfileManagementStepController;
@@ -58,10 +57,8 @@
     // Renders a default browser promo.
     kDefaultBrowser,
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
     // Renders the search engine choice screen.
     kSearchEngineChoice,
-#endif
 
     kFinishFlow,
   };
diff --git a/chrome/browser/ui/views/profiles/profile_management_step_controller.cc b/chrome/browser/ui/views/profiles/profile_management_step_controller.cc
index bf8139a..bd926040 100644
--- a/chrome/browser/ui/views/profiles/profile_management_step_controller.cc
+++ b/chrome/browser/ui/views/profiles/profile_management_step_controller.cc
@@ -20,17 +20,14 @@
 #include "chrome/browser/ui/views/profiles/profile_management_types.h"
 #include "chrome/browser/ui/views/profiles/profile_picker_signed_in_flow_controller.h"
 #include "chrome/browser/ui/views/profiles/profile_picker_web_contents_host.h"
+#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.h"
+#include "components/search_engines/search_engine_choice_utils.h"
 #include "google_apis/gaia/core_account_id.h"
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 #include "chrome/browser/ui/views/profiles/profile_picker_dice_sign_in_provider.h"
 #endif
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.h"
-#include "components/search_engines/search_engine_choice_utils.h"
-#endif
-
 namespace {
 class ProfilePickerAppStepController : public ProfileManagementStepController {
  public:
@@ -285,7 +282,6 @@
   base::OnceClosure finish_flow_and_run_in_browser_callback_;
 };
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
 class SearchEngineChoiceStepController
     : public ProfileManagementStepController {
  public:
@@ -377,8 +373,6 @@
   // The web contents in which we want to display the screen.
   raw_ptr<content::WebContents> web_contents_;
 };
-#endif  // BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-
 }  // namespace
 
 // static
@@ -424,7 +418,6 @@
                                                     std::move(signed_in_flow));
 }
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
 // static
 std::unique_ptr<ProfileManagementStepController>
 ProfileManagementStepController::CreateForSearchEngineChoice(
@@ -437,7 +430,6 @@
       host, search_engine_choice_service, web_contents, entry_point,
       std::move(callback));
 }
-#endif
 
 // static
 std::unique_ptr<ProfileManagementStepController>
diff --git a/chrome/browser/ui/views/profiles/profile_management_step_controller.h b/chrome/browser/ui/views/profiles/profile_management_step_controller.h
index 7adc542c..bf0a63fd 100644
--- a/chrome/browser/ui/views/profiles/profile_management_step_controller.h
+++ b/chrome/browser/ui/views/profiles/profile_management_step_controller.h
@@ -62,7 +62,6 @@
       ProfilePickerWebContentsHost* host,
       std::unique_ptr<ProfilePickerSignedInFlowController> signed_in_flow);
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
   static std::unique_ptr<ProfileManagementStepController>
   CreateForSearchEngineChoice(
       ProfilePickerWebContentsHost* host,
@@ -70,7 +69,6 @@
       content::WebContents* web_contents,
       SearchEngineChoiceService::EntryPoint entry_point,
       base::OnceClosure callback);
-#endif
 
   // Creates the step that will finish the flow and launch the browser.
   static std::unique_ptr<ProfileManagementStepController>
diff --git a/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc b/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc
index 9e25560..b1bb6e3 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc
@@ -548,7 +548,6 @@
   CHECK(created_profile_);
   base::queue<ProfileManagementFlowController::Step> post_identity_steps;
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
   if (weak_signed_in_flow_controller_) {
     auto search_engine_choice_step_completed = base::BindOnce(
         &ProfilePickerFlowController::AdvanceToNextPostIdentityStep,
@@ -566,7 +565,6 @@
     post_identity_steps.emplace(
         ProfileManagementFlowController::Step::kSearchEngineChoice);
   }
-#endif
 
   RegisterStep(
       Step::kFinishFlow,
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
index 70dee09..56f8dec7 100644
--- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
+++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
@@ -34,7 +34,6 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/constrained_window/constrained_window_views.h"
-#include "components/signin/public/base/signin_buildflags.h"
 #include "components/signin/public/base/signin_metrics.h"
 #include "components/web_modal/web_contents_modal_dialog_host.h"
 #include "content/public/browser/render_widget_host_view.h"
@@ -86,14 +85,14 @@
     return;
 
   browser->signin_view_controller()->CloseModalSignin();
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
+
   SearchEngineChoiceService* search_engine_choice_service =
       SearchEngineChoiceServiceFactory::GetForProfile(browser->profile());
   if (search_engine_choice_service &&
       search_engine_choice_service->CanShowDialog(CHECK_DEREF(browser.get()))) {
     ShowSearchEngineChoiceDialog(*browser);
   }
-#endif
+
   if (show_profile_switch_iph) {
     browser->window()->MaybeShowProfileSwitchIPH();
   }
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index 9256236..cbd9636f 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <limits>
+#include <optional>
 #include <set>
 #include <utility>
 
@@ -52,6 +53,8 @@
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
 #include "chrome/browser/ui/web_applications/web_app_tabbed_utils.h"
+#include "chrome/browser/web_applications/web_app_helpers.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/grit/chrome_unscaled_resources.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "content/public/browser/web_contents.h"
@@ -2398,6 +2401,26 @@
   attached_context_->SetBoundsForDrag(attached_views_, *drag_bounds);
 }
 
+std::optional<webapps::AppId> TabDragController::GetControllingAppForDrag(
+    Browser* browser) {
+  content::WebContents* active_contents = source_dragged_contents();
+  if (!base::FeatureList::IsEnabled(
+          features::kTearOffWebAppTabOpensWebAppWindow) ||
+      drag_data_.size() != 1 || !active_contents) {
+    return std::nullopt;
+  }
+  const web_app::WebAppProvider* provider =
+      web_app::WebAppProvider::GetForWebApps(browser->profile());
+  const base::flat_map<webapps::AppId, std::string> all_controlling_apps =
+      provider->registrar_unsafe().GetAllAppsControllingUrl(
+          active_contents->GetLastCommittedURL());
+  if (all_controlling_apps.size() != 1) {
+    return std::nullopt;
+  }
+
+  return all_controlling_apps.begin()->first;
+}
+
 Browser* TabDragController::CreateBrowserForDrag(
     TabDragContext* source,
     const gfx::Point& point_in_screen,
@@ -2415,14 +2438,34 @@
       CalculateDraggedBrowserBounds(source, point_in_screen, drag_bounds));
   *drag_offset = point_in_screen - new_bounds.origin();
 
+  // Find if there's a controlling app, and thus we should open an app window.
+  Browser* from_browser = BrowserView::GetBrowserViewForNativeWindow(
+                              GetAttachedBrowserWidget()->GetNativeWindow())
+                              ->browser();
+
+  const std::optional<webapps::AppId> controlling_app =
+      GetControllingAppForDrag(from_browser);
+  const bool open_as_web_app = controlling_app.has_value();
+
   Browser::CreateParams create_params =
-      BrowserView::GetBrowserViewForNativeWindow(
-          GetAttachedBrowserWidget()->GetNativeWindow())
-          ->browser()
-          ->create_params();
+      open_as_web_app
+          ? Browser::CreateParams::CreateForApp(
+                web_app::GenerateApplicationNameFromAppId(
+                    controlling_app.value()),
+                /* trusted_source=*/true, gfx::Rect(), from_browser->profile(),
+                /* user_gesture=*/true)
+          : BrowserView::GetBrowserViewForNativeWindow(
+                GetAttachedBrowserWidget()->GetNativeWindow())
+                ->browser()
+                ->create_params();
+
+  // Web app windows have their own initial size independent of the source
+  // browser window.
+  if (!open_as_web_app) {
+    create_params.initial_bounds = new_bounds;
+  }
   create_params.user_gesture = true;
   create_params.in_tab_dragging = true;
-  create_params.initial_bounds = new_bounds;
 #if BUILDFLAG(IS_CHROMEOS)
   // Do not copy attached window's restore id as this will cause Full Restore to
   // restore the newly created browser using the original browser's stored data.
@@ -2457,7 +2500,9 @@
   // If the window is created maximized then the bounds we supplied are ignored.
   // We need to reset them again so they are honored. On ChromeOS, this is
   // handled in NativeWidgetAura.
-  browser->window()->SetBounds(new_bounds);
+  if (!open_as_web_app) {
+    browser->window()->SetBounds(new_bounds);
+  }
 #endif
 
   return browser;
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.h b/chrome/browser/ui/views/tabs/tab_drag_controller.h
index ab35df2..3fcc9762 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.h
@@ -22,6 +22,7 @@
 #include "chrome/browser/ui/views/tabs/tab_strip_scroll_session.h"
 #include "chrome/browser/ui/views/tabs/tab_strip_types.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "components/webapps/common/web_app_id.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
 #include "ui/base/models/list_selection_model.h"
 #include "ui/gfx/geometry/rect.h"
@@ -560,6 +561,11 @@
                                         gfx::Vector2d* drag_offset,
                                         std::vector<gfx::Rect>* drag_bounds);
 
+  // If the user is dragging a single tab that is controlled by one web app,
+  // and features::kTearOffWebAppTabOpensWebAppWindow is enabled,
+  // returns the app id of that web app, nullopt otherwise.
+  std::optional<webapps::AppId> GetControllingAppForDrag(Browser* browser);
+
   // Creates and returns a new Browser to handle the drag.
   Browser* CreateBrowserForDrag(TabDragContext* source,
                                 const gfx::Point& point_in_screen,
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index 7589e7d..4416c3d 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -543,19 +543,25 @@
 
 class DetachToBrowserTabDragControllerTest
     : public TabDragControllerTest,
-      public ::testing::WithParamInterface<testing::tuple<bool, const char*>> {
+      public ::testing::WithParamInterface<
+          testing::tuple<bool, bool, const char*>> {
  public:
   DetachToBrowserTabDragControllerTest() {
+    std::vector<base::test::FeatureRef> enabled_features = {};
+    if (std::get<0>(GetParam())) {
+      enabled_features.push_back(features::kSplitTabStrip);
+    }
+    if (std::get<1>(GetParam())) {
+      enabled_features.push_back(features::kTearOffWebAppTabOpensWebAppWindow);
+    }
     scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/std::get<0>(GetParam())
-            ? std::vector<base::test::FeatureRef>{features::kSplitTabStrip}
-            : std::vector<base::test::FeatureRef>{},
+        /*enabled_features=*/enabled_features,
         /*disabled_features=*/{
-          features::kWebUITabStrip,
+            features::kWebUITabStrip,
 #if BUILDFLAG(IS_WIN)
-              // Disable NativeWinOcclusion to avoid it interfering with test
-              // for dragging over occluded browser window.
-              features::kCalculateNativeWinOcclusion,
+            // Disable NativeWinOcclusion to avoid it interfering with test
+            // for dragging over occluded browser window.
+            features::kCalculateNativeWinOcclusion,
 #endif  // BUILDFLAG(IS_WIN)
         });
   }
@@ -581,7 +587,7 @@
   }
 
   InputSource input_source() const {
-    return strstr(std::get<1>(GetParam()), "mouse") ? INPUT_SOURCE_MOUSE
+    return strstr(std::get<2>(GetParam()), "mouse") ? INPUT_SOURCE_MOUSE
                                                     : INPUT_SOURCE_TOUCH;
   }
 
@@ -709,14 +715,15 @@
             base::Milliseconds(1));
         return;
       }
-
-      // The tab getting dragged into the new browser should have the same width
-      // as before it was dragged.
-      EXPECT_EQ(
-          first_dragged_tab_width,
-          GetTabStripForBrowser(browser_list->get(1))->tab_at(0)->width());
+      // Only check tab width if dragging tabs between windows of the same type.
+      if (browser_list->get(0)->type() == browser_list->get(1)->type()) {
+        // The tab getting dragged into the new browser should have the same
+        // width as before it was dragged.
+        EXPECT_EQ(
+            first_dragged_tab_width,
+            GetTabStripForBrowser(browser_list->get(1))->tab_at(0)->width());
+      }
     }
-
     // Windows hangs if you use a sync mouse event here.
     ASSERT_TRUE(ReleaseInput(0, true));
   }
@@ -2983,6 +2990,60 @@
   EXPECT_EQ(browser2_groups[0], group);
 }
 
+using DetachTabWithUrlControlledByWebApp = DetachToBrowserTabDragControllerTest;
+
+// Test tearing off a tab displaying a url controlled by a web app.
+// The kTearOffWebAppTabOpensWebAppWindow experiment determines whether the new
+// browser window will be a normal browser window or an app window.
+IN_PROC_BROWSER_TEST_P(DetachTabWithUrlControlledByWebApp, TearOffWebApp) {
+  // Install tabbed web app.
+  auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
+  web_app_info->start_url = GURL("https://www.example.com");
+  web_app_info->title = u"A tabbed web app";
+  web_app_info->user_display_mode =
+      web_app::mojom::UserDisplayMode::kStandalone;
+  webapps::AppId app_id = web_app::test::InstallWebApp(browser()->profile(),
+                                                       std::move(web_app_info));
+
+  // Load URL controlled by installed web app.
+  AddTabsAndResetBrowser(browser(), 1, GURL("https://www.example.com/"));
+
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+  EXPECT_EQ(browser()->tab_strip_model()->count(), 2);
+
+  // Move to the second tab and drag it enough that it detaches.
+  DragTabAndNotify(
+      tab_strip,
+      base::BindOnce(&DetachToBrowserTabDragControllerTest::
+                         ReleaseInputAfterWindowDetached,
+                     base::Unretained(this), tab_strip->tab_at(1)->width()),
+      1);
+
+  EXPECT_EQ(2u, browser_list->size());
+
+  // Expect first window is left with just the start tab.
+  TabStripModel* source_tab_strip_model =
+      browser_list->get(0)->tab_strip_model();
+  EXPECT_EQ(source_tab_strip_model->count(), 1);
+  EXPECT_FALSE(source_tab_strip_model->IsTabPinned(0));
+  EXPECT_EQ(source_tab_strip_model->GetWebContentsAt(0)->GetVisibleURL(),
+            GURL("about:blank"));
+
+  // Expect the newly created window has the dragged tab.
+  TabStripModel* dest_tab_strip_model = browser_list->get(1)->tab_strip_model();
+  EXPECT_EQ(dest_tab_strip_model->count(), 1);
+  EXPECT_EQ(dest_tab_strip_model->GetWebContentsAt(0)->GetVisibleURL(),
+            web_app::WebAppProvider::GetForTest(browser()->profile())
+                ->registrar_unsafe()
+                .GetAppStartUrl(app_id));
+  EXPECT_EQ(dest_tab_strip_model->active_index(), 0);
+
+  // Check that right type of browser window is opened, depending on the value
+  // of kTearOffWebAppTabOpensWebAppWindow experiment.
+  EXPECT_EQ(browser_list->get(1)->type(),
+            std::get<1>(GetParam()) ? Browser::TYPE_APP : Browser::TYPE_NORMAL);
+}
+
 // Detachable tabs are not supported for PWAs on Mac so these tests don't apply.
 #if !BUILDFLAG(IS_MAC)
 class DetachToBrowserTabDragControllerTestWithTabbedWebApp
@@ -4855,54 +4916,89 @@
 INSTANTIATE_TEST_SUITE_P(
     TabDragging,
     DetachToBrowserTabDragControllerTest,
-    ::testing::Combine(::testing::Bool(), ::testing::Values("mouse", "touch")));
+    ::testing::Combine(
+        ::testing::Bool(),
+        /*kTearOffWebAppTabOpensWebAppWindow=*/::testing::Values(false),
+        ::testing::Values("mouse", "touch")));
 INSTANTIATE_TEST_SUITE_P(
     TabDragging,
     DetachToBrowserTabDragControllerTestWithScrollableTabStripEnabled,
-    ::testing::Combine(::testing::Bool(), ::testing::Values("mouse", "touch")));
+    ::testing::Combine(
+        ::testing::Bool(),
+        /*kTearOffWebAppTabOpensWebAppWindow=*/::testing::Values(false),
+        ::testing::Values("mouse", "touch")));
 #else
 INSTANTIATE_TEST_SUITE_P(TabDragging,
                          DetachToBrowserTabDragControllerTest,
                          ::testing::Combine(::testing::Bool(),
+                                            ::testing::Bool(),
                                             ::testing::Values("mouse")));
 INSTANTIATE_TEST_SUITE_P(
     TabDragging,
     DetachToBrowserTabDragControllerTestWithScrollableTabStripEnabled,
-    ::testing::Combine(::testing::Bool(), ::testing::Values("mouse")));
+    ::testing::Combine(::testing::Bool(),
+                       ::testing::Bool(),
+                       ::testing::Values("mouse")));
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // TODO(crbug.com/1488094): Enable Multi Display Test on lacros
-INSTANTIATE_TEST_SUITE_P(TabDragging,
-                         DetachToBrowserInSeparateDisplayTabDragControllerTest,
-                         ::testing::Combine(::testing::Bool(),
-                                            ::testing::Values("mouse")));
-INSTANTIATE_TEST_SUITE_P(TabDragging,
-                         DifferentDeviceScaleFactorDisplayTabDragControllerTest,
-                         ::testing::Combine(::testing::Bool(),
-                                            ::testing::Values("mouse")));
+INSTANTIATE_TEST_SUITE_P(
+    TabDragging,
+    DetachToBrowserInSeparateDisplayTabDragControllerTest,
+    ::testing::Combine(
+        ::testing::Bool(),
+        /*kTearOffWebAppTabOpensWebAppWindow=*/::testing::Values(false),
+        ::testing::Values("mouse")));
+INSTANTIATE_TEST_SUITE_P(
+    TabDragging,
+    DifferentDeviceScaleFactorDisplayTabDragControllerTest,
+    ::testing::Combine(
+        ::testing::Bool(),
+        /*kTearOffWebAppTabOpensWebAppWindow=*/::testing::Values(false),
+        ::testing::Values("mouse")));
 INSTANTIATE_TEST_SUITE_P(
     TabDragging,
     DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest,
-    ::testing::Combine(::testing::Bool(), ::testing::Values("mouse")));
-INSTANTIATE_TEST_SUITE_P(TabDragging,
-                         DetachToBrowserTabDragControllerTestTouch,
-                         ::testing::Combine(::testing::Bool(),
-                                            ::testing::Values("touch")));
+    ::testing::Combine(::testing::Bool(),
+                       ::testing::Values(false),
+                       ::testing::Values("mouse")));
+INSTANTIATE_TEST_SUITE_P(
+    TabDragging,
+    DetachToBrowserTabDragControllerTestTouch,
+    ::testing::Combine(
+        ::testing::Bool(),
+        /*kTearOffWebAppTabOpensWebAppWindow=*/::testing::Values(false),
+        ::testing::Values("touch")));
 INSTANTIATE_TEST_SUITE_P(
     TabDragging,
     DetachToBrowserTabDragControllerTestWithTabbedSystemApp,
-    ::testing::Combine(::testing::Bool(), ::testing::Values("mouse", "touch")));
+    ::testing::Combine(
+        ::testing::Bool(),
+        /*kTearOffWebAppTabOpensWebAppWindow=*/::testing::Values(false),
+        ::testing::Values("mouse", "touch")));
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS)
 INSTANTIATE_TEST_SUITE_P(
     TabDragging,
     DetachToBrowserTabDragControllerTestWithTabbedWebApp,
-    ::testing::Combine(::testing::Bool(), ::testing::Values("mouse", "touch")));
+    ::testing::Combine(
+        ::testing::Bool(),
+        /*kTearOffWebAppTabOpensWebAppWindow=*/::testing::Values(false),
+        ::testing::Values("mouse", "touch")));
 #elif !BUILDFLAG(IS_MAC)
-INSTANTIATE_TEST_SUITE_P(TabDragging,
-                         DetachToBrowserTabDragControllerTestWithTabbedWebApp,
-                         ::testing::Combine(::testing::Bool(),
-                                            ::testing::Values("mouse")));
+INSTANTIATE_TEST_SUITE_P(
+    TabDragging,
+    DetachToBrowserTabDragControllerTestWithTabbedWebApp,
+    ::testing::Combine(
+        ::testing::Bool(),
+        /*kTearOffWebAppTabOpensWebAppWindow=*/::testing::Values(false),
+        ::testing::Values("mouse")));
 #endif
+
+INSTANTIATE_TEST_SUITE_P(TabDragging,
+                         DetachTabWithUrlControlledByWebApp,
+                         ::testing::Combine(::testing::Bool(),
+                                            ::testing::Bool(),
+                                            ::testing::Values("mouse")));
diff --git a/chrome/browser/ui/views/toolbar/toolbar_controller.cc b/chrome/browser/ui/views/toolbar/toolbar_controller.cc
index 1ab235b..d7bb1ec6 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_controller.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_controller.cc
@@ -10,6 +10,8 @@
 #include "base/no_destructor.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/side_panel/side_panel_enums.h"
 #include "chrome/browser/ui/toolbar_controller_util.h"
 #include "chrome/browser/ui/views/frame/browser_actions.h"
@@ -17,6 +19,7 @@
 #include "chrome/browser/ui/views/toolbar/overflow_button.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/views/controls/menu/menu_item_view.h"
@@ -77,6 +80,17 @@
   controller_->EndPopOut(identifier_);
 }
 
+ToolbarController::ResponsiveElementInfo::ResponsiveElementInfo(
+    absl::variant<ElementIdInfo, actions::ActionId> overflow_id,
+    bool is_section_end,
+    std::optional<ui::ElementIdentifier> observed_identifier)
+    : overflow_id(overflow_id),
+      is_section_end(is_section_end),
+      observed_identifier(observed_identifier) {}
+ToolbarController::ResponsiveElementInfo::ResponsiveElementInfo(
+    const ResponsiveElementInfo& info) = default;
+ToolbarController::ResponsiveElementInfo::~ResponsiveElementInfo() = default;
+
 ToolbarController::ToolbarController(
     const std::vector<ToolbarController::ResponsiveElementInfo>&
         responsive_elements,
@@ -149,16 +163,21 @@
 
 std::vector<ToolbarController::ResponsiveElementInfo>
 ToolbarController::GetDefaultResponsiveElements(Browser* browser) {
+  bool is_refresh = features::IsChromeRefresh2023();
+  bool is_incognito = browser->profile()->IsIncognitoProfile();
   // TODO(crbug.com/1445573): Fill in observed identifier.
   // Order matters because it should match overflow menu order top to bottom.
   std::vector<ToolbarController::ResponsiveElementInfo> elements = {
-      {ToolbarController::ElementIdInfo{kToolbarForwardButtonElementId,
-                                        IDS_OVERFLOW_MENU_ITEM_TEXT_FORWARD,
-                                        kToolbarForwardButtonElementId},
+      {ToolbarController::ElementIdInfo{
+           kToolbarForwardButtonElementId, IDS_OVERFLOW_MENU_ITEM_TEXT_FORWARD,
+           is_refresh ? &vector_icons::kForwardArrowChromeRefreshIcon
+                      : &vector_icons::kForwardArrowIcon,
+           kToolbarForwardButtonElementId},
        /*is_section_end=*/false},
-      {ToolbarController::ElementIdInfo{kToolbarHomeButtonElementId,
-                                        IDS_OVERFLOW_MENU_ITEM_TEXT_HOME,
-                                        kToolbarHomeButtonElementId},
+      {ToolbarController::ElementIdInfo{
+           kToolbarHomeButtonElementId, IDS_OVERFLOW_MENU_ITEM_TEXT_HOME,
+           is_refresh ? &kNavigateHomeChromeRefreshIcon : &kNavigateHomeIcon,
+           kToolbarHomeButtonElementId},
        /*is_section_end=*/true}};
 
   // Support actions items.
@@ -183,26 +202,41 @@
 
   elements.insert(
       elements.end(),
-      {{ToolbarController::ElementIdInfo{kToolbarChromeLabsButtonElementId,
-                                         IDS_OVERFLOW_MENU_ITEM_TEXT_LABS,
-                                         kToolbarChromeLabsButtonElementId},
+      {{ToolbarController::ElementIdInfo{
+            kToolbarChromeLabsButtonElementId, IDS_OVERFLOW_MENU_ITEM_TEXT_LABS,
+            is_refresh ? &kChromeLabsChromeRefreshIcon : &kChromeLabsIcon,
+            kToolbarChromeLabsButtonElementId},
         /*is_section_end=*/false, kToolbarChromeLabsBubbleElementId},
        {ToolbarController::ElementIdInfo{
             kToolbarMediaButtonElementId,
             IDS_OVERFLOW_MENU_ITEM_TEXT_MEDIA_CONTROLS,
+            is_refresh ? &kMediaToolbarButtonChromeRefreshIcon
+                       : &kMediaToolbarButtonIcon,
             kToolbarMediaButtonElementId},
         /*is_section_end=*/false, kToolbarMediaBubbleElementId},
-       {ToolbarController::ElementIdInfo{kToolbarDownloadButtonElementId,
-                                         IDS_OVERFLOW_MENU_ITEM_TEXT_DOWNLOADS,
-                                         kToolbarDownloadButtonElementId},
+       {ToolbarController::ElementIdInfo{
+            kToolbarDownloadButtonElementId,
+            IDS_OVERFLOW_MENU_ITEM_TEXT_DOWNLOADS,
+            is_refresh ? &kDownloadToolbarButtonChromeRefreshIcon
+                       : &kDownloadToolbarButtonIcon,
+            kToolbarDownloadButtonElementId},
         /*is_section_end=*/true, kToolbarDownloadBubbleElementId},
        {ToolbarController::ElementIdInfo{kToolbarNewTabButtonElementId,
                                          IDS_OVERFLOW_MENU_ITEM_TEXT_NEW_TAB,
+#if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
+                                         &kNewTabToolbarButtonIcon,
+#else
+                                         nullptr,
+#endif  // BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
                                          kToolbarNewTabButtonElementId},
         /*is_section_end=*/true},
-       {ToolbarController::ElementIdInfo{kToolbarAvatarButtonElementId,
-                                         IDS_OVERFLOW_MENU_ITEM_TEXT_PROFILE,
-                                         kToolbarAvatarButtonElementId},
+       {ToolbarController::ElementIdInfo{
+            kToolbarAvatarButtonElementId, IDS_OVERFLOW_MENU_ITEM_TEXT_PROFILE,
+            is_incognito ? (is_refresh ? &kIncognitoRefreshMenuIcon
+                                       : &kIncognitoProfileIcon)
+                         : (is_refresh ? &kUserAccountAvatarRefreshIcon
+                                       : &kUserAccountAvatarIcon),
+            kToolbarAvatarButtonElementId},
         /*is_section_end=*/false, kToolbarAvatarBubbleElementId}});
   return elements;
 }
@@ -331,6 +365,26 @@
       element_info.overflow_id);
 }
 
+std::optional<ui::ImageModel> ToolbarController::GetMenuIcon(
+    const ResponsiveElementInfo& element_info) const {
+  return absl::visit(
+      base::Overloaded{
+          [this](actions::ActionId id) {
+            return std::make_optional(
+                pinned_actions_delegate_->GetActionItemFor(id)->GetImage());
+          },
+          [&](ToolbarController::ElementIdInfo info)
+              -> std::optional<ui::ImageModel> {
+            if (!info.menu_icon) {
+              return std::nullopt;
+            }
+            return std::make_optional(ui::ImageModel::FromVectorIcon(
+                *info.menu_icon, ui::kColorMenuIcon,
+                ui::SimpleMenuModel::kDefaultIconSize));
+          }},
+      element_info.overflow_id);
+}
+
 views::View* ToolbarController::FindToolbarElementWithId(
     views::View* view,
     ui::ElementIdentifier id) {
@@ -396,7 +450,13 @@
       if (pre_separator_pending && menu_model->GetItemCount() > 0) {
         menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
       }
-      menu_model->AddItem(i, GetMenuText(element));
+      const auto image_model = GetMenuIcon(element);
+      if (image_model.has_value()) {
+        menu_model->AddItemWithIcon(i, GetMenuText(element),
+                                    image_model.value());
+      } else {
+        menu_model->AddItem(i, GetMenuText(element));
+      }
       pre_separator_pending = false;
     }
     if (element.is_section_end) {
diff --git a/chrome/browser/ui/views/toolbar/toolbar_controller.h b/chrome/browser/ui/views/toolbar/toolbar_controller.h
index 0b963571..ebc5aeb 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_controller.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_controller.h
@@ -46,6 +46,9 @@
     // items.
     int menu_text_id;
 
+    // Menu item icon. nullptr if this menu item has no icon.
+    raw_ptr<const gfx::VectorIcon> menu_icon = nullptr;
+
     // The toolbar button to be activated with menu text pressed. This is not
     // necessarily the same as the element that overflows. E.g. when the
     // overflowed element is kToolbarExtensionsContainerElementId the
@@ -77,6 +80,12 @@
     // | Profile         |
     // |-----------------|
 
+    ResponsiveElementInfo(absl::variant<ElementIdInfo, actions::ActionId>,
+                          bool = false,
+                          std::optional<ui::ElementIdentifier> = std::nullopt);
+    ResponsiveElementInfo(const ResponsiveElementInfo&);
+    ~ResponsiveElementInfo();
+
     // The toolbar element that potentially overflows.
     absl::variant<ElementIdInfo, actions::ActionId> overflow_id;
 
@@ -188,6 +197,10 @@
   virtual std::u16string GetMenuText(
       const ResponsiveElementInfo& element_info) const;
 
+  // Get menu icon from the responsive element.
+  std::optional<ui::ImageModel> GetMenuIcon(
+      const ResponsiveElementInfo& element_info) const;
+
   // Utility that recursively searches for a view with `id` from `view`.
   static views::View* FindToolbarElementWithId(views::View* view,
                                                ui::ElementIdentifier id);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_controller_unittest.cc b/chrome/browser/ui/views/toolbar/toolbar_controller_unittest.cc
index 9351699..60c1d41 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_controller_unittest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_controller_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/test/views/chrome_views_test_base.h"
+#include "components/vector_icons/vector_icons.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/actions/actions.h"
 #include "ui/base/models/simple_menu_model.h"
@@ -145,8 +146,8 @@
 
   MockToolbarController toolbar_controller(
       std::vector<ToolbarController::ResponsiveElementInfo>(
-          {{ToolbarController::ElementIdInfo{kDummyButton, 0,
-                                             kDummyActivateView},
+          {{ToolbarController::ElementIdInfo{
+                kDummyButton, 0, &vector_icons::kErrorIcon, kDummyActivateView},
             false, kDummyObservedView}}),
       std::vector<ui::ElementIdentifier>({kDummyButton}), 1, container_view(),
       overflow_button(), test_delegate.get());
@@ -241,12 +242,15 @@
     toolbar_controller_ = std::make_unique<TestToolbarController>(
         std::vector<ToolbarController::ResponsiveElementInfo>(
             {{ToolbarController::ElementIdInfo{kDummyButton1, 0,
+                                               &vector_icons::kErrorIcon,
                                                kDummyActivateView},
               false, kDummyObservedView},
              {ToolbarController::ElementIdInfo{kDummyButton2, 0,
+                                               &vector_icons::kErrorIcon,
                                                kDummyActivateView},
               true, kDummyObservedView},
              {ToolbarController::ElementIdInfo{kDummyButton3, 0,
+                                               &vector_icons::kErrorIcon,
                                                kDummyActivateView},
               true, kDummyObservedView}}),
         std::vector<ui::ElementIdentifier>(
@@ -398,12 +402,15 @@
       std::make_unique<TestToolbarController>(
           std::vector<ToolbarController::ResponsiveElementInfo>(
               {{ToolbarController::ElementIdInfo{kDummyButton1, 0,
+                                                 &vector_icons::kErrorIcon,
                                                  kDummyActivateView},
                 true},
                {ToolbarController::ElementIdInfo{kDummyButton2, 0,
+                                                 &vector_icons::kErrorIcon,
                                                  kDummyActivateView},
                 true},
                {ToolbarController::ElementIdInfo{kDummyButton3, 0,
+                                                 &vector_icons::kErrorIcon,
                                                  kDummyActivateView},
                 true}}),
           std::vector<ui::ElementIdentifier>(
@@ -443,12 +450,15 @@
       std::make_unique<TestToolbarController>(
           std::vector<ToolbarController::ResponsiveElementInfo>(
               {{ToolbarController::ElementIdInfo{kDummyButton1, 0,
+                                                 &vector_icons::kErrorIcon,
                                                  kDummyActivateView},
                 true},
                {ToolbarController::ElementIdInfo{kDummyButton2, 0,
+                                                 &vector_icons::kErrorIcon,
                                                  kDummyActivateView},
                 true},
                {ToolbarController::ElementIdInfo{kDummyButton3, 0,
+                                                 &vector_icons::kErrorIcon,
                                                  kDummyActivateView},
                 true}}),
           std::vector<ui::ElementIdentifier>(
@@ -488,12 +498,15 @@
       std::make_unique<TestToolbarController>(
           std::vector<ToolbarController::ResponsiveElementInfo>(
               {{ToolbarController::ElementIdInfo{kDummyButton1, 0,
+                                                 &vector_icons::kErrorIcon,
                                                  kDummyActivateView},
                 true},
                {ToolbarController::ElementIdInfo{kDummyButton2, 0,
+                                                 &vector_icons::kErrorIcon,
                                                  kDummyActivateView},
                 true},
                {ToolbarController::ElementIdInfo{kDummyButton3, 0,
+                                                 &vector_icons::kErrorIcon,
                                                  kDummyActivateView},
                 true}}),
           std::vector<ui::ElementIdentifier>(
@@ -624,12 +637,15 @@
       std::make_unique<TestToolbarController>(
           std::vector<ToolbarController::ResponsiveElementInfo>(
               {{ToolbarController::ElementIdInfo{kDummyButton1, 0,
+                                                 &vector_icons::kErrorIcon,
                                                  kDummyActivateView},
                 false},
                {ToolbarController::ElementIdInfo{kDummyButton2, 0,
+                                                 &vector_icons::kErrorIcon,
                                                  kDummyActivateView},
                 false},
                {ToolbarController::ElementIdInfo{kDummyButton3, 0,
+                                                 &vector_icons::kErrorIcon,
                                                  kDummyActivateView},
                 false}}),
           std::vector<ui::ElementIdentifier>(
diff --git a/chrome/browser/ui/views/user_education/browser_feature_promo_controller.cc b/chrome/browser/ui/views/user_education/browser_feature_promo_controller.cc
index f2ff1b75..39993ba 100644
--- a/chrome/browser/ui/views/user_education/browser_feature_promo_controller.cc
+++ b/chrome/browser/ui/views/user_education/browser_feature_promo_controller.cc
@@ -152,7 +152,6 @@
 
   // Turn off IPH while a required search engine choice dialog is visible or
   // pending.
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
   if (search_engines::IsChoiceScreenFlagEnabled(
           search_engines::ChoicePromo::kDialog)) {
     Browser& browser = *browser_view_->browser();
@@ -163,7 +162,6 @@
       return false;
     }
   }
-#endif
 
   // Don't show IPH if the toolbar is collapsed in Responsive Mode/the overflow
   // button is visible.
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service.cc b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
index 6018a8dd..7d64486 100644
--- a/chrome/browser/ui/views/user_education/browser_user_education_service.cc
+++ b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
@@ -36,6 +36,7 @@
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/feature_engagement/public/feature_constants.h"
+#include "components/safe_browsing/core/common/safebrowsing_referral_methods.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/user_education/common/feature_promo_handle.h"
 #include "components/user_education/common/feature_promo_registry.h"
@@ -683,6 +684,31 @@
         kToolbarSidePanelButtonElementId, IDS_PRICE_TRACKING_SIDE_PANEL_IPH));
   }
 
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  // kIPHDownloadEsbPromoFeature:
+  registry.RegisterFeature(std::move(
+      FeaturePromoSpecification::CreateForCustomAction(
+          feature_engagement::kIPHDownloadEsbPromoFeature,
+          kToolbarDownloadButtonElementId, IDS_DOWNLOAD_BUBBLE_ESB_PROMO,
+          IDS_DOWNLOAD_BUBBLE_ESB_PROMO_CUSTOM_ACTION,
+          base::BindRepeating(
+              [](ui::ElementContext ctx,
+                 user_education::FeaturePromoHandle promo_handle) {
+                auto* browser = chrome::FindBrowserWithUiElementContext(ctx);
+                if (!browser) {
+                  return;
+                }
+                chrome::ShowSafeBrowsingEnhancedProtectionWithIph(
+                    browser, safe_browsing::SafeBrowsingSettingReferralMethod::
+                                 kDownloadButtonIphPromo);
+              }))
+          .SetCustomActionIsDefault(true)
+          .SetBubbleArrow(HelpBubbleArrow::kTopRight)
+          .SetBubbleTitleText(IDS_DOWNLOAD_BUBBLE_ESB_PROMO_TITLE)
+          .SetCustomActionDismissText(IDS_DOWNLOAD_BUBBLE_ESB_PROMO_DISMISS)
+          .SetBubbleIcon(&vector_icons::kGshieldIcon)));
+#endif
+
   // kIPHDownloadToolbarButtonFeature:
   registry.RegisterFeature(
       std::move(FeaturePromoSpecification::CreateForToastPromo(
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index ba672b4..e1fb8c46 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -37,7 +37,7 @@
 #include "chrome/browser/ui/webui/components/components_ui.h"
 #include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
 #include "chrome/browser/ui/webui/crashes_ui.h"
-#include "chrome/browser/ui/webui/device_log_ui.h"
+#include "chrome/browser/ui/webui/device_log/device_log_ui.h"
 #include "chrome/browser/ui/webui/download_internals/download_internals_ui.h"
 #include "chrome/browser/ui/webui/engagement/site_engagement_ui.h"
 #include "chrome/browser/ui/webui/flags/flags_ui.h"
@@ -96,7 +96,6 @@
 #include "components/security_interstitials/content/connection_help_ui.h"
 #include "components/security_interstitials/content/known_interception_disclosure_ui.h"
 #include "components/security_interstitials/content/urls.h"
-#include "components/signin/public/base/signin_buildflags.h"
 #include "components/site_engagement/content/site_engagement_service.h"
 #include "components/supervised_user/core/common/buildflags.h"
 #include "content/public/browser/web_contents.h"
@@ -157,6 +156,7 @@
 #include "chrome/browser/ui/webui/password_manager/password_manager_ui.h"
 #include "chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_ui.h"
 #include "chrome/browser/ui/webui/profile_internals/profile_internals_ui.h"
+#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.h"
 #include "chrome/browser/ui/webui/settings/settings_ui.h"
 #include "chrome/browser/ui/webui/settings/settings_utils.h"
 #include "chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.h"
@@ -298,10 +298,6 @@
 #include "chrome/browser/ui/webui/ash/chromebox_for_meetings/network_settings_dialog.h"
 #endif  // BUILDFLAG(PLATFORM_CFM)
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.h"
-#endif
-
 #if BUILDFLAG(ENABLE_COMPOSE)
 #include "chrome/browser/ui/webui/compose/compose_ui.h"
 #include "components/compose/core/browser/compose_features.h"
@@ -717,14 +713,6 @@
     return &NewWebUI<SigninEmailConfirmationUI>;
 #endif
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-  if (url.host_piece() == chrome::kChromeUISearchEngineChoiceHost &&
-      search_engines::IsChoiceScreenFlagEnabled(
-          search_engines::ChoicePromo::kAny)) {
-    return &NewWebUI<SearchEngineChoiceUI>;
-  }
-#endif
-
 #if BUILDFLAG(ENABLE_NACL)
   if (url.host_piece() == chrome::kChromeUINaClHost)
     return &NewWebUI<NaClUI>;
@@ -877,6 +865,12 @@
 #if !BUILDFLAG(IS_ANDROID)
   if (url.host_piece() == chrome::kChromeUIPrivacySandboxDialogHost)
     return &NewWebUI<PrivacySandboxDialogUI>;
+
+  if (url.host_piece() == chrome::kChromeUISearchEngineChoiceHost &&
+      search_engines::IsChoiceScreenFlagEnabled(
+          search_engines::ChoicePromo::kAny)) {
+    return &NewWebUI<SearchEngineChoiceUI>;
+  }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES)
diff --git a/chrome/browser/ui/webui/device_log_ui.cc b/chrome/browser/ui/webui/device_log/device_log_ui.cc
similarity index 94%
rename from chrome/browser/ui/webui/device_log_ui.cc
rename to chrome/browser/ui/webui/device_log/device_log_ui.cc
index 67f0064..55cb279f 100644
--- a/chrome/browser/ui/webui/device_log_ui.cc
+++ b/chrome/browser/ui/webui/device_log/device_log_ui.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/webui/device_log_ui.h"
+#include "chrome/browser/ui/webui/device_log/device_log_ui.h"
 
 #include <memory>
 #include <string>
@@ -12,7 +12,8 @@
 #include "base/values.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/url_constants.h"
-#include "chrome/grit/dev_ui_browser_resources.h"
+#include "chrome/grit/device_log_resources.h"
+#include "chrome/grit/device_log_resources_map.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/device_event_log/device_event_log.h"
 #include "content/public/browser/web_contents.h"
@@ -150,9 +151,9 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
   html->UseStringsJs();
-  html->AddResourcePath("device_log_ui.css", IDR_DEVICE_LOG_UI_CSS);
-  html->AddResourcePath("device_log_ui.js", IDR_DEVICE_LOG_UI_JS);
-  html->SetDefaultResource(IDR_DEVICE_LOG_UI_HTML);
+  html->AddResourcePaths(base::make_span(kDeviceLogResources,
+                                         kDeviceLogResourcesSize));
+  html->SetDefaultResource(IDR_DEVICE_LOG_DEVICE_LOG_UI_HTML);
 }
 
 DeviceLogUI::~DeviceLogUI() {
diff --git a/chrome/browser/ui/webui/device_log_ui.h b/chrome/browser/ui/webui/device_log/device_log_ui.h
similarity index 81%
rename from chrome/browser/ui/webui/device_log_ui.h
rename to chrome/browser/ui/webui/device_log/device_log_ui.h
index 58eb6294..c99c20e 100644
--- a/chrome/browser/ui/webui/device_log_ui.h
+++ b/chrome/browser/ui/webui/device_log/device_log_ui.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_DEVICE_LOG_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_DEVICE_LOG_UI_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_DEVICE_LOG_DEVICE_LOG_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_DEVICE_LOG_DEVICE_LOG_UI_H_
 
 #include "content/public/browser/web_ui_controller.h"
 
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_test_base.h b/chrome/browser/ui/webui/extensions/extension_settings_test_base.h
index 00d1ff8..5f58f27a 100644
--- a/chrome/browser/ui/webui/extensions/extension_settings_test_base.h
+++ b/chrome/browser/ui/webui/extensions/extension_settings_test_base.h
@@ -12,6 +12,11 @@
 #include "chrome/test/base/web_ui_mocha_browser_test.h"
 #include "extensions/browser/scoped_ignore_content_verifier_for_test.h"
 
+#if BUILDFLAG(IS_WIN)
+#include "base/base_paths_win.h"
+#include "base/test/scoped_path_override.h"
+#endif  // BUILDFLAG(IS_WIN)
+
 namespace extensions {
 class Extension;
 class ScopedTestDialogAutoConfirm;
@@ -61,6 +66,14 @@
 
   const base::FilePath test_data_dir_;
 
+#if BUILDFLAG(IS_WIN)
+  // This is needed to stop tests creating a shortcut in the Windows start menu.
+  // The override needs to last until the test is destroyed, because Windows
+  // shortcut tasks which create the shortcut can run after the test body
+  // returns.
+  base::ScopedPathOverride override_start_dir{base::DIR_START_MENU};
+#endif  // BUILDFLAG(IS_WIN)
+
   // Disable extension content verification.
   extensions::ScopedIgnoreContentVerifierForTest ignore_content_verification_;
 
diff --git a/chrome/browser/ui/webui/search_engine_choice/BUILD.gn b/chrome/browser/ui/webui/search_engine_choice/BUILD.gn
index 951cdb7..c95a84f 100644
--- a/chrome/browser/ui/webui/search_engine_choice/BUILD.gn
+++ b/chrome/browser/ui/webui/search_engine_choice/BUILD.gn
@@ -2,11 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//components/signin/features.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 
-assert(enable_search_engine_choice)
-
 mojom("mojo_bindings") {
   sources = [ "search_engine_choice.mojom" ]
   webui_module_path = "/"
diff --git a/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui_browsertest.cc b/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui_browsertest.cc
index cf4bd0cf..f50a0e1 100644
--- a/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui_browsertest.cc
@@ -22,7 +22,6 @@
 #include "components/search_engines/template_url_data.h"
 #include "components/search_engines/template_url_prepopulate_data.h"
 #include "components/search_engines/template_url_service.h"
-#include "components/signin/public/base/signin_buildflags.h"
 #include "components/signin/public/base/signin_switches.h"
 #include "content/public/browser/host_zoom_map.h"
 #include "content/public/browser/navigation_handle.h"
@@ -34,10 +33,6 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/views/widget/any_widget_observer.h"
 
-#if !BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-#error Platform not supported
-#endif
-
 // Tests for the chrome://search-engine-choice WebUI page.
 namespace {
 // This is the maximum dialog height for pixel tests on Windows.
diff --git a/chrome/browser/ui/webui/settings/search_engines_handler.cc b/chrome/browser/ui/webui/settings/search_engines_handler.cc
index 6850b71..9efc69b 100644
--- a/chrome/browser/ui/webui/settings/search_engines_handler.cc
+++ b/chrome/browser/ui/webui/settings/search_engines_handler.cc
@@ -292,7 +292,6 @@
 
   list_controller_.MakeDefaultTemplateURL(index);
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
   Profile& profile = CHECK_DEREF(Profile::FromWebUI(web_ui()));
   TemplateURLService* template_url_service =
       TemplateURLServiceFactory::GetForProfile(&profile);
@@ -307,7 +306,6 @@
   // search engine.
   search_engines::RecordChoiceMade(profile.GetPrefs(), choice_made_location,
                                    template_url_service);
-#endif
 
   base::RecordAction(base::UserMetricsAction("Options_SearchEngineSetDefault"));
 }
diff --git a/chrome/browser/ui/webui/settings/search_engines_handler_unittest.cc b/chrome/browser/ui/webui/settings/search_engines_handler_unittest.cc
index 715ac00e..5a6df83d 100644
--- a/chrome/browser/ui/webui/settings/search_engines_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/search_engines_handler_unittest.cc
@@ -19,7 +19,6 @@
 #include "components/search_engines/search_engines_pref_names.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
-#include "components/signin/public/base/signin_buildflags.h"
 #include "components/signin/public/base/signin_switches.h"
 #include "components/version_info/version_info.h"
 #include "content/public/test/browser_task_environment.h"
@@ -156,7 +155,6 @@
   EXPECT_EQ("search-engines-changed", second_call_data.arg1()->GetString());
 }
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
 TEST_P(SearchEnginesHandlerParametrizedTest,
        SettingTheDefaultSearchEngineRecordsHistogram) {
   base::Value::List first_call_args;
@@ -265,5 +263,4 @@
       search_engines::kSearchEngineChoiceScreenDefaultSearchEngineTypeHistogram,
       SearchEngineType::SEARCH_ENGINE_BING, 1);
 }
-#endif
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index df24547..20f1110 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -2483,763 +2483,778 @@
 void AddSiteSettingsStrings(content::WebUIDataSource* html_source,
                             Profile* profile) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
-    {"addSite", IDS_SETTINGS_ADD_SITE},
-    {"addSiteTitle", IDS_SETTINGS_ADD_SITE_TITLE},
-    {"addSitesTitle", IDS_SETTINGS_ADD_SITES_TITLE},
-    {"embeddedOnAnyHost", IDS_SETTINGS_EXCEPTIONS_EMBEDDED_ON_ANY_HOST},
-    {"embeddedOnHost", IDS_SETTINGS_EXCEPTIONS_EMBEDDED_ON_HOST},
-    {"editSiteTitle", IDS_SETTINGS_EDIT_SITE_TITLE},
-    {"noBluetoothDevicesFound", IDS_SETTINGS_NO_BLUETOOTH_DEVICES_FOUND},
-    {"noHidDevicesFound", IDS_SETTINGS_NO_HID_DEVICES_FOUND},
-    {"noSerialPortsFound", IDS_SETTINGS_NO_SERIAL_PORTS_FOUND},
-    {"noUsbDevicesFound", IDS_SETTINGS_NO_USB_DEVICES_FOUND},
-    {"resetBluetoothConfirmation", IDS_SETTINGS_RESET_BLUETOOTH_CONFIRMATION},
-    {"resetHidConfirmation", IDS_SETTINGS_RESET_HID_CONFIRMATION},
-    {"resetSerialPortsConfirmation",
-     IDS_SETTINGS_RESET_SERIAL_PORTS_CONFIRMATION},
-    {"resetUsbConfirmation", IDS_SETTINGS_RESET_USB_CONFIRMATION},
-    {"siteSettingsRecentPermissionsSectionLabel",
-     IDS_SETTINGS_SITE_SETTINGS_RECENT_ACTIVITY},
-    {"siteSettingsCategoryCamera", IDS_SITE_SETTINGS_TYPE_CAMERA},
-    {"siteSettingsCameraLabel", IDS_SITE_SETTINGS_TYPE_CAMERA},
-    {"thirdPartyCookiesPageTitle", IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_TITLE},
-    {"thirdPartyCookiesLinkRowLabel",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_LINK_ROW_LABEL},
-    {"thirdPartyCookiesLinkRowSublabelEnabled",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_LINK_ROW_SUB_LABEL_ENABLED},
-    {"thirdPartyCookiesLinkRowSublabelDisabledIncognito",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_LINK_ROW_SUB_LABEL_DISABLED_INCOGNITO},
-    {"thirdPartyCookiesLinkRowSublabelDisabled",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_LINK_ROW_SUB_LABEL_DISABLED},
-    {"thirdPartyCookiesPageAllowRadioLabel",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_ALLOW_RADIO_LABEL},
-    {"thirdPartyCookiesPageAllowExpandA11yLabel",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_ALLOW_EXPAND_A11Y_LABEL},
-    {"thirdPartyCookiesPageAllowBulOne",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_ALLOW_BULLET_1},
-    {"thirdPartyCookiesPageAllowBulTwo",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_ALLOW_BULLET_2},
-    {"thirdPartyCookiesPageBlockIncognitoRadioLabel",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_INCOGNITO_RADIO_LABEL},
-    {"thirdPartyCookiesPageBlockIncognitoExpandA11yLabel",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_INCOGNITO_EXPAND_A11Y_LABEL},
-    {"thirdPartyCookiesPageBlockIncognitoBulOne",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_INCOGNITO_BULLET_1},
-    {"thirdPartyCookiesPageBlockIncognitoBulTwo",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_INCOGNITO_BULLET_2},
-    {"thirdPartyCookiesPageBlockRadioLabel",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_RADIO_LABEL},
-    {"thirdPartyCookiesPageBlockExpandA11yLabel",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_EXPAND_A11Y_LABEL},
-    {"thirdPartyCookiesPageBlockBulOne",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_BULLET_1},
-    {"thirdPartyCookiesPageBlockBulTwo",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_BULLET_2},
-    {"trackingProtectionLinkRowLabel",
-     IDS_SETTINGS_TRACKING_PROTECTION_LINK_ROW_LABEL},
-    {"trackingProtectionLinkRowSubLabel",
-     IDS_SETTINGS_TRACKING_PROTECTION_LINK_ROW_SUB_LABEL},
+      {"addSite", IDS_SETTINGS_ADD_SITE},
+      {"addSiteTitle", IDS_SETTINGS_ADD_SITE_TITLE},
+      {"addSitesTitle", IDS_SETTINGS_ADD_SITES_TITLE},
+      {"embeddedOnAnyHost", IDS_SETTINGS_EXCEPTIONS_EMBEDDED_ON_ANY_HOST},
+      {"embeddedOnHost", IDS_SETTINGS_EXCEPTIONS_EMBEDDED_ON_HOST},
+      {"editSiteTitle", IDS_SETTINGS_EDIT_SITE_TITLE},
+      {"noBluetoothDevicesFound", IDS_SETTINGS_NO_BLUETOOTH_DEVICES_FOUND},
+      {"noHidDevicesFound", IDS_SETTINGS_NO_HID_DEVICES_FOUND},
+      {"noSerialPortsFound", IDS_SETTINGS_NO_SERIAL_PORTS_FOUND},
+      {"noUsbDevicesFound", IDS_SETTINGS_NO_USB_DEVICES_FOUND},
+      {"resetBluetoothConfirmation", IDS_SETTINGS_RESET_BLUETOOTH_CONFIRMATION},
+      {"resetHidConfirmation", IDS_SETTINGS_RESET_HID_CONFIRMATION},
+      {"resetSerialPortsConfirmation",
+       IDS_SETTINGS_RESET_SERIAL_PORTS_CONFIRMATION},
+      {"resetUsbConfirmation", IDS_SETTINGS_RESET_USB_CONFIRMATION},
+      {"siteSettingsRecentPermissionsSectionLabel",
+       IDS_SETTINGS_SITE_SETTINGS_RECENT_ACTIVITY},
+      {"siteSettingsCategoryCamera", IDS_SITE_SETTINGS_TYPE_CAMERA},
+      {"siteSettingsCameraLabel", IDS_SITE_SETTINGS_TYPE_CAMERA},
+      {"thirdPartyCookiesPageTitle",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_TITLE},
+      {"thirdPartyCookiesLinkRowLabel",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_LINK_ROW_LABEL},
+      {"thirdPartyCookiesLinkRowSublabelEnabled",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_LINK_ROW_SUB_LABEL_ENABLED},
+      {"thirdPartyCookiesLinkRowSublabelDisabledIncognito",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_LINK_ROW_SUB_LABEL_DISABLED_INCOGNITO},
+      {"thirdPartyCookiesLinkRowSublabelDisabled",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_LINK_ROW_SUB_LABEL_DISABLED},
+      {"thirdPartyCookiesPageAllowRadioLabel",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_ALLOW_RADIO_LABEL},
+      {"thirdPartyCookiesPageAllowExpandA11yLabel",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_ALLOW_EXPAND_A11Y_LABEL},
+      {"thirdPartyCookiesPageAllowBulOne",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_ALLOW_BULLET_1},
+      {"thirdPartyCookiesPageAllowBulTwo",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_ALLOW_BULLET_2},
+      {"thirdPartyCookiesPageBlockIncognitoRadioLabel",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_INCOGNITO_RADIO_LABEL},
+      {"thirdPartyCookiesPageBlockIncognitoExpandA11yLabel",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_INCOGNITO_EXPAND_A11Y_LABEL},
+      {"thirdPartyCookiesPageBlockIncognitoBulOne",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_INCOGNITO_BULLET_1},
+      {"thirdPartyCookiesPageBlockIncognitoBulTwo",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_INCOGNITO_BULLET_2},
+      {"thirdPartyCookiesPageBlockRadioLabel",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_RADIO_LABEL},
+      {"thirdPartyCookiesPageBlockExpandA11yLabel",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_EXPAND_A11Y_LABEL},
+      {"thirdPartyCookiesPageBlockBulOne",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_BULLET_1},
+      {"thirdPartyCookiesPageBlockBulTwo",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_BLOCK_BULLET_2},
+      {"trackingProtectionLinkRowLabel",
+       IDS_SETTINGS_TRACKING_PROTECTION_LINK_ROW_LABEL},
+      {"trackingProtectionLinkRowSubLabel",
+       IDS_SETTINGS_TRACKING_PROTECTION_LINK_ROW_SUB_LABEL},
 
-    {"thirdPartyCookiesPageDescription",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_DESCRIPTION},
-    {"thirdPartyCookiesPageDefaultBehaviorHeading",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_DEFAULT_BEHAVIOR_HEADING},
-    {"thirdPartyCookiesPageDefaultBehaviorDescription",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_DEFAULT_BEHAVIOR_DESCRIPTION},
-    {"thirdPartyCookiesPageCustomizedBehaviorHeading",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_CUSTOMIZED_BEHAVIOR_HEADING},
-    {"thirdPartyCookiesPageCustomizedBehaviorDescription",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_CUSTOMIZED_BEHAVIOR_DESCRIPTION},
-    {"thirdPartyCookiesPageAllowExceptionsSubHeading",
-     IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_ALLOW_EXCEPTIONS_SUB_HEADING},
-    {"cookiePageBlockThirdIncognitoBulTwoFps",
-     IDS_SETTINGS_COOKIES_BLOCK_THIRD_PARTY_INCOGNITO_BULLET_TWO_FPS},
-    {"cookiePageFpsLabel", IDS_SETTINGS_COOKIES_FIRST_PARTY_SETS_TOGGLE_LABEL},
-    {"cookiePageFpsSubLabel",
-     IDS_SETTINGS_COOKIES_FIRST_PARTY_SETS_TOGGLE_SUB_LABEL},
-    {"cookiePageAllSitesLink", IDS_SETTINGS_COOKIES_ALL_SITES_LINK},
-    {"trackingProtectionPageTitle",
-     IDS_SETTINGS_TRACKING_PROTECTION_PAGE_TITLE},
-    {"trackingProtectionPageDescription",
-     IDS_SETTINGS_TRACKING_PROTECTION_PAGE_DESCRIPTION},
-    {"trackingProtectionBulletOne",
-     IDS_SETTINGS_TRACKING_PROTECTION_BULLET_ONE},
-    {"trackingProtectionBulletOneDescription",
-     IDS_SETTINGS_TRACKING_PROTECTION_BULLET_ONE_DESCRIPTION},
-    {"trackingProtectionBulletTwo",
-     IDS_SETTINGS_TRACKING_PROTECTION_BULLET_TWO},
-    {"trackingProtectionAdvancedLabel",
-     IDS_SETTINGS_TRACKING_PROTECTION_ADVANCED_LABEL},
-    {"trackingProtectionThirdPartyCookiesToggleLabel",
-     IDS_SETTINGS_TRACKING_PROTECTION_THIRD_PARTY_COOKIES_TOGGLE_LABEL},
-    {"trackingProtectionThirdPartyCookiesToggleSubLabel",
-     IDS_SETTINGS_TRACKING_PROTECTION_THIRD_PARTY_COOKIES_TOGGLE_SUB_LABEL},
-    {"trackingProtectionThirdPartyCookiesLearnMoreAriaLabel",
-     IDS_SETTINGS_TRACKING_PROTECTION_THIRD_PARTY_COOKIES_LEARN_MORE_ARIA_LABEL},
-    {"trackingProtectionDoNotTrackToggleSubLabel",
-     IDS_SETTINGS_TRACKING_PROTECTION_DO_NOT_TRACK_TOGGLE_SUB_LABEL},
-    {"trackingProtectionSitesAllowedCookiesTitle",
-     IDS_SETTINGS_TRACKING_PROTECTION_SITES_ALLOWED_COOKIES_TITLE},
-    {"trackingProtectionSitesAllowedCookiesDescription",
-     IDS_SETTINGS_TRACKING_PROTECTION_SITES_ALLOWED_COOKIES_DESCRIPTION},
-    {"siteSettingsCategoryFederatedIdentityApi",
-     IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API},
-    {"siteSettingsCategoryHandlers", IDS_SITE_SETTINGS_TYPE_HANDLERS},
-    {"siteSettingsCategoryImages", IDS_SITE_SETTINGS_TYPE_IMAGES},
-    {"siteSettingsCategoryInsecureContent",
-     IDS_SITE_SETTINGS_TYPE_INSECURE_CONTENT},
-    {"siteSettingsCategoryLocation", IDS_SITE_SETTINGS_TYPE_LOCATION},
-    {"siteSettingsCategoryJavascript", IDS_SITE_SETTINGS_TYPE_JAVASCRIPT},
-    {"siteSettingsCategoryMicrophone", IDS_SITE_SETTINGS_TYPE_MIC},
-    {"siteSettingsMicrophoneLabel", IDS_SITE_SETTINGS_TYPE_MIC},
-    {"siteSettingsCategoryNotifications", IDS_SITE_SETTINGS_TYPE_NOTIFICATIONS},
-    {"siteSettingsCategoryPopups", IDS_SITE_SETTINGS_TYPE_POPUPS_REDIRECTS},
-    {"siteSettingsCategoryZoomLevels", IDS_SITE_SETTINGS_TYPE_ZOOM_LEVELS},
-    {"siteSettingsAllSites", IDS_SETTINGS_SITE_SETTINGS_ALL_SITES},
-    {"siteSettingsAllSitesDescription",
-     IDS_SETTINGS_SITE_SETTINGS_ALL_SITES_DESCRIPTION},
-    {"siteSettingsAllSitesSearch", IDS_SETTINGS_SITE_SETTINGS_ALL_SITES_SEARCH},
-    {"siteSettingsAllSitesSort", IDS_SETTINGS_SITE_SETTINGS_ALL_SITES_SORT},
-    {"siteSettingsAllSitesSortMethodMostVisited",
-     IDS_SETTINGS_SITE_SETTINGS_ALL_SITES_SORT_METHOD_MOST_VISITED},
-    {"siteSettingsAllSitesSortMethodStorage",
-     IDS_SETTINGS_SITE_SETTINGS_ALL_SITES_SORT_METHOD_STORAGE},
-    {"siteSettingsAllSitesSortMethodName",
-     IDS_SETTINGS_SITE_SETTINGS_ALL_SITES_SORT_METHOD_NAME},
-    {"siteSettingsFileSystemSiteListHeader",
-     IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_SITE_LIST_HEADER},
-    {"siteSettingsFileSystemSiteListEditHeader",
-     IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_SITE_LIST_EDIT_HEADER},
-    {"siteSettingsFileSystemSiteListRemoveGrantLabel",
-     IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_SITE_LIST_REMOVE_GRANT_LABEL},
-    {"siteSettingsFileSystemSiteListRemoveGrants",
-     IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_SITE_LIST_REMOVE_GRANTS},
-    {"siteSettingsFileSystemSiteListViewHeader",
-     IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_SITE_LIST_VIEW_HEADER},
-    {"siteSettingsFileSystemSiteListViewSiteDetails",
-     IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_SITE_LIST_VIEW_SITE_DETAILS},
-    {"siteSettingsSiteEntryPartitionedLabel",
-     IDS_SETTINGS_SITE_SETTINGS_SITE_ENTRY_PARTITIONED_LABEL},
-    {"siteSettingsSiteRepresentationSeparator",
-     IDS_SETTINGS_SITE_SETTINGS_SITE_REPRESENTATION_SEPARATOR},
-    {"siteSettingsAppProtocolHandlers",
-     IDS_SETTINGS_SITE_SETTINGS_APP_PROTOCOL_HANDLERS},
-    {"siteSettingsAppAllowedProtocolHandlersDescription",
-     IDS_SETTINGS_SITE_SETTINGS_APP_ALLOWED_PROTOCOL_HANDLERS_DESCRIPTION},
-    {"siteSettingsAppDisallowedProtocolHandlersDescription",
-     IDS_SETTINGS_SITE_SETTINGS_APP_DISALLOWED_PROTOCOL_HANDLERS_DESCRIPTION},
-    {"siteSettingsAutomaticDownloads",
-     IDS_SITE_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS},
-    {"siteSettingsAutomaticDownloadsMidSentence",
-     IDS_SITE_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS_MID_SENTENCE},
-    {"siteSettingsAutoPictureInPicture",
-     IDS_SITE_SETTINGS_TYPE_AUTO_PICTURE_IN_PICTURE},
-    {"siteSettingsAutoPictureInPictureMidSentence",
-     IDS_SITE_SETTINGS_TYPE_AUTO_PICTURE_IN_PICTURE_MID_SENTENCE},
-    {"siteSettingsBackgroundSync", IDS_SITE_SETTINGS_TYPE_BACKGROUND_SYNC},
-    {"siteSettingsBackgroundSyncMidSentence",
-     IDS_SITE_SETTINGS_TYPE_BACKGROUND_SYNC_MID_SENTENCE},
-    {"siteSettingsCamera", IDS_SITE_SETTINGS_TYPE_CAMERA},
-    {"siteSettingsCameraMidSentence",
-     IDS_SITE_SETTINGS_TYPE_CAMERA_MID_SENTENCE},
-    {"siteSettingsClipboard", IDS_SITE_SETTINGS_TYPE_CLIPBOARD},
-    {"siteSettingsClipboardMidSentence",
-     IDS_SITE_SETTINGS_TYPE_CLIPBOARD_MID_SENTENCE},
-    {"siteSettingsCookies", IDS_SITE_SETTINGS_TYPE_COOKIES},
-    {"siteSettingsCookiesMidSentence",
-     IDS_SITE_SETTINGS_TYPE_COOKIES_MID_SENTENCE},
-    {"siteSettingsHandlers", IDS_SITE_SETTINGS_TYPE_HANDLERS},
-    {"siteSettingsHandlersMidSentence",
-     IDS_SITE_SETTINGS_TYPE_HANDLERS_MID_SENTENCE},
-    {"siteSettingsLocation", IDS_SITE_SETTINGS_TYPE_LOCATION},
-    {"siteSettingsLocationMidSentence",
-     IDS_SITE_SETTINGS_TYPE_LOCATION_MID_SENTENCE},
-    {"siteSettingsMic", IDS_SITE_SETTINGS_TYPE_MIC},
-    {"siteSettingsMicMidSentence", IDS_SITE_SETTINGS_TYPE_MIC_MID_SENTENCE},
-    {"siteSettingsNotifications", IDS_SITE_SETTINGS_TYPE_NOTIFICATIONS},
-    {"siteSettingsNotificationsMidSentence",
-     IDS_SITE_SETTINGS_TYPE_NOTIFICATIONS_MID_SENTENCE},
-    {"siteSettingsImages", IDS_SITE_SETTINGS_TYPE_IMAGES},
-    {"siteSettingsImagesMidSentence",
-     IDS_SITE_SETTINGS_TYPE_IMAGES_MID_SENTENCE},
-    {"siteSettingsInsecureContent", IDS_SITE_SETTINGS_TYPE_INSECURE_CONTENT},
-    {"siteSettingsInsecureContentMidSentence",
-     IDS_SITE_SETTINGS_TYPE_INSECURE_CONTENT_MID_SENTENCE},
-    {"siteSettingsInsecureContentBlock",
-     IDS_SETTINGS_SITE_SETTINGS_INSECURE_CONTENT_BLOCK},
-    {"siteSettingsJavascript", IDS_SITE_SETTINGS_TYPE_JAVASCRIPT},
-    {"siteSettingsJavascriptMidSentence",
-     IDS_SITE_SETTINGS_TYPE_JAVASCRIPT_MID_SENTENCE},
-    {"siteSettingsSound", IDS_SITE_SETTINGS_TYPE_SOUND},
-    {"siteSettingsSoundMidSentence", IDS_SITE_SETTINGS_TYPE_SOUND_MID_SENTENCE},
-    {"siteSettingsPdfDocuments", IDS_SITE_SETTINGS_TYPE_PDF_DOCUMENTS},
-    {"siteSettingsPdfDownloadPdfs",
-     IDS_SETTINGS_SITE_SETTINGS_PDF_DOWNLOAD_PDFS},
-    {"siteSettingsProtectedContent", IDS_SITE_SETTINGS_TYPE_PROTECTED_MEDIA_ID},
-    {"siteSettingsProtectedContentMidSentence",
-     IDS_SITE_SETTINGS_TYPE_PROTECTED_MEDIA_ID_MID_SENTENCE},
-    {"siteSettingsProtectedContentIdentifiers",
-     IDS_SITE_SETTINGS_TYPE_PROTECTED_MEDIA_ID},
-    {"siteSettingsProtectedContentDescription",
-     IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_DESCRIPTION},
-    {"siteSettingsProtectedContentAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_ALLOWED},
-    {"siteSettingsProtectedContentBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_BLOCKED},
-    {"siteSettingsProtectedContentBlockedSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_BLOCKED_SUB_LABEL},
+      {"thirdPartyCookiesPageDescription",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_DESCRIPTION},
+      {"thirdPartyCookiesPageDefaultBehaviorHeading",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_DEFAULT_BEHAVIOR_HEADING},
+      {"thirdPartyCookiesPageDefaultBehaviorDescription",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_DEFAULT_BEHAVIOR_DESCRIPTION},
+      {"thirdPartyCookiesPageCustomizedBehaviorHeading",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_CUSTOMIZED_BEHAVIOR_HEADING},
+      {"thirdPartyCookiesPageCustomizedBehaviorDescription",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_CUSTOMIZED_BEHAVIOR_DESCRIPTION},
+      {"thirdPartyCookiesPageAllowExceptionsSubHeading",
+       IDS_SETTINGS_THIRD_PARTY_COOKIES_PAGE_ALLOW_EXCEPTIONS_SUB_HEADING},
+      {"cookiePageBlockThirdIncognitoBulTwoFps",
+       IDS_SETTINGS_COOKIES_BLOCK_THIRD_PARTY_INCOGNITO_BULLET_TWO_FPS},
+      {"cookiePageFpsLabel",
+       IDS_SETTINGS_COOKIES_FIRST_PARTY_SETS_TOGGLE_LABEL},
+      {"cookiePageFpsSubLabel",
+       IDS_SETTINGS_COOKIES_FIRST_PARTY_SETS_TOGGLE_SUB_LABEL},
+      {"cookiePageAllSitesLink", IDS_SETTINGS_COOKIES_ALL_SITES_LINK},
+      {"trackingProtectionPageTitle",
+       IDS_SETTINGS_TRACKING_PROTECTION_PAGE_TITLE},
+      {"trackingProtectionPageDescription",
+       IDS_SETTINGS_TRACKING_PROTECTION_PAGE_DESCRIPTION},
+      {"trackingProtectionBulletOne",
+       IDS_SETTINGS_TRACKING_PROTECTION_BULLET_ONE},
+      {"trackingProtectionBulletOneDescription",
+       IDS_SETTINGS_TRACKING_PROTECTION_BULLET_ONE_DESCRIPTION},
+      {"trackingProtectionBulletTwo",
+       IDS_SETTINGS_TRACKING_PROTECTION_BULLET_TWO},
+      {"trackingProtectionAdvancedLabel",
+       IDS_SETTINGS_TRACKING_PROTECTION_ADVANCED_LABEL},
+      {"trackingProtectionThirdPartyCookiesToggleLabel",
+       IDS_SETTINGS_TRACKING_PROTECTION_THIRD_PARTY_COOKIES_TOGGLE_LABEL},
+      {"trackingProtectionThirdPartyCookiesToggleSubLabel",
+       IDS_SETTINGS_TRACKING_PROTECTION_THIRD_PARTY_COOKIES_TOGGLE_SUB_LABEL},
+      {"trackingProtectionThirdPartyCookiesLearnMoreAriaLabel",
+       IDS_SETTINGS_TRACKING_PROTECTION_THIRD_PARTY_COOKIES_LEARN_MORE_ARIA_LABEL},
+      {"trackingProtectionDoNotTrackToggleSubLabel",
+       IDS_SETTINGS_TRACKING_PROTECTION_DO_NOT_TRACK_TOGGLE_SUB_LABEL},
+      {"trackingProtectionSitesAllowedCookiesTitle",
+       IDS_SETTINGS_TRACKING_PROTECTION_SITES_ALLOWED_COOKIES_TITLE},
+      {"trackingProtectionSitesAllowedCookiesDescription",
+       IDS_SETTINGS_TRACKING_PROTECTION_SITES_ALLOWED_COOKIES_DESCRIPTION},
+      {"siteSettingsCategoryFederatedIdentityApi",
+       IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API},
+      {"siteSettingsCategoryHandlers", IDS_SITE_SETTINGS_TYPE_HANDLERS},
+      {"siteSettingsCategoryImages", IDS_SITE_SETTINGS_TYPE_IMAGES},
+      {"siteSettingsCategoryInsecureContent",
+       IDS_SITE_SETTINGS_TYPE_INSECURE_CONTENT},
+      {"siteSettingsCategoryLocation", IDS_SITE_SETTINGS_TYPE_LOCATION},
+      {"siteSettingsCategoryJavascript", IDS_SITE_SETTINGS_TYPE_JAVASCRIPT},
+      {"siteSettingsCategoryMicrophone", IDS_SITE_SETTINGS_TYPE_MIC},
+      {"siteSettingsMicrophoneLabel", IDS_SITE_SETTINGS_TYPE_MIC},
+      {"siteSettingsCategoryNotifications",
+       IDS_SITE_SETTINGS_TYPE_NOTIFICATIONS},
+      {"siteSettingsCategoryPopups", IDS_SITE_SETTINGS_TYPE_POPUPS_REDIRECTS},
+      {"siteSettingsCategoryZoomLevels", IDS_SITE_SETTINGS_TYPE_ZOOM_LEVELS},
+      {"siteSettingsAllSites", IDS_SETTINGS_SITE_SETTINGS_ALL_SITES},
+      {"siteSettingsAllSitesDescription",
+       IDS_SETTINGS_SITE_SETTINGS_ALL_SITES_DESCRIPTION},
+      {"siteSettingsAllSitesSearch",
+       IDS_SETTINGS_SITE_SETTINGS_ALL_SITES_SEARCH},
+      {"siteSettingsAllSitesSort", IDS_SETTINGS_SITE_SETTINGS_ALL_SITES_SORT},
+      {"siteSettingsAllSitesSortMethodMostVisited",
+       IDS_SETTINGS_SITE_SETTINGS_ALL_SITES_SORT_METHOD_MOST_VISITED},
+      {"siteSettingsAllSitesSortMethodStorage",
+       IDS_SETTINGS_SITE_SETTINGS_ALL_SITES_SORT_METHOD_STORAGE},
+      {"siteSettingsAllSitesSortMethodName",
+       IDS_SETTINGS_SITE_SETTINGS_ALL_SITES_SORT_METHOD_NAME},
+      {"siteSettingsFileSystemSiteListHeader",
+       IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_SITE_LIST_HEADER},
+      {"siteSettingsFileSystemSiteListEditHeader",
+       IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_SITE_LIST_EDIT_HEADER},
+      {"siteSettingsFileSystemSiteListRemoveGrantLabel",
+       IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_SITE_LIST_REMOVE_GRANT_LABEL},
+      {"siteSettingsFileSystemSiteListRemoveGrants",
+       IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_SITE_LIST_REMOVE_GRANTS},
+      {"siteSettingsFileSystemSiteListViewHeader",
+       IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_SITE_LIST_VIEW_HEADER},
+      {"siteSettingsFileSystemSiteListViewSiteDetails",
+       IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_SITE_LIST_VIEW_SITE_DETAILS},
+      {"siteSettingsSiteEntryPartitionedLabel",
+       IDS_SETTINGS_SITE_SETTINGS_SITE_ENTRY_PARTITIONED_LABEL},
+      {"siteSettingsSiteRepresentationSeparator",
+       IDS_SETTINGS_SITE_SETTINGS_SITE_REPRESENTATION_SEPARATOR},
+      {"siteSettingsAppProtocolHandlers",
+       IDS_SETTINGS_SITE_SETTINGS_APP_PROTOCOL_HANDLERS},
+      {"siteSettingsAppAllowedProtocolHandlersDescription",
+       IDS_SETTINGS_SITE_SETTINGS_APP_ALLOWED_PROTOCOL_HANDLERS_DESCRIPTION},
+      {"siteSettingsAppDisallowedProtocolHandlersDescription",
+       IDS_SETTINGS_SITE_SETTINGS_APP_DISALLOWED_PROTOCOL_HANDLERS_DESCRIPTION},
+      {"siteSettingsAutomaticDownloads",
+       IDS_SITE_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS},
+      {"siteSettingsAutomaticDownloadsMidSentence",
+       IDS_SITE_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS_MID_SENTENCE},
+      {"siteSettingsAutoPictureInPicture",
+       IDS_SITE_SETTINGS_TYPE_AUTO_PICTURE_IN_PICTURE},
+      {"siteSettingsAutoPictureInPictureMidSentence",
+       IDS_SITE_SETTINGS_TYPE_AUTO_PICTURE_IN_PICTURE_MID_SENTENCE},
+      {"siteSettingsBackgroundSync", IDS_SITE_SETTINGS_TYPE_BACKGROUND_SYNC},
+      {"siteSettingsBackgroundSyncMidSentence",
+       IDS_SITE_SETTINGS_TYPE_BACKGROUND_SYNC_MID_SENTENCE},
+      {"siteSettingsCamera", IDS_SITE_SETTINGS_TYPE_CAMERA},
+      {"siteSettingsCameraMidSentence",
+       IDS_SITE_SETTINGS_TYPE_CAMERA_MID_SENTENCE},
+      {"siteSettingsClipboard", IDS_SITE_SETTINGS_TYPE_CLIPBOARD},
+      {"siteSettingsClipboardMidSentence",
+       IDS_SITE_SETTINGS_TYPE_CLIPBOARD_MID_SENTENCE},
+      {"siteSettingsCookies", IDS_SITE_SETTINGS_TYPE_COOKIES},
+      {"siteSettingsCookiesMidSentence",
+       IDS_SITE_SETTINGS_TYPE_COOKIES_MID_SENTENCE},
+      {"siteSettingsHandlers", IDS_SITE_SETTINGS_TYPE_HANDLERS},
+      {"siteSettingsHandlersMidSentence",
+       IDS_SITE_SETTINGS_TYPE_HANDLERS_MID_SENTENCE},
+      {"siteSettingsLocation", IDS_SITE_SETTINGS_TYPE_LOCATION},
+      {"siteSettingsLocationMidSentence",
+       IDS_SITE_SETTINGS_TYPE_LOCATION_MID_SENTENCE},
+      {"siteSettingsMic", IDS_SITE_SETTINGS_TYPE_MIC},
+      {"siteSettingsMicMidSentence", IDS_SITE_SETTINGS_TYPE_MIC_MID_SENTENCE},
+      {"siteSettingsNotifications", IDS_SITE_SETTINGS_TYPE_NOTIFICATIONS},
+      {"siteSettingsNotificationsMidSentence",
+       IDS_SITE_SETTINGS_TYPE_NOTIFICATIONS_MID_SENTENCE},
+      {"siteSettingsImages", IDS_SITE_SETTINGS_TYPE_IMAGES},
+      {"siteSettingsImagesMidSentence",
+       IDS_SITE_SETTINGS_TYPE_IMAGES_MID_SENTENCE},
+      {"siteSettingsInsecureContent", IDS_SITE_SETTINGS_TYPE_INSECURE_CONTENT},
+      {"siteSettingsInsecureContentMidSentence",
+       IDS_SITE_SETTINGS_TYPE_INSECURE_CONTENT_MID_SENTENCE},
+      {"siteSettingsInsecureContentBlock",
+       IDS_SETTINGS_SITE_SETTINGS_INSECURE_CONTENT_BLOCK},
+      {"siteSettingsJavascript", IDS_SITE_SETTINGS_TYPE_JAVASCRIPT},
+      {"siteSettingsJavascriptMidSentence",
+       IDS_SITE_SETTINGS_TYPE_JAVASCRIPT_MID_SENTENCE},
+      {"siteSettingsSound", IDS_SITE_SETTINGS_TYPE_SOUND},
+      {"siteSettingsSoundMidSentence",
+       IDS_SITE_SETTINGS_TYPE_SOUND_MID_SENTENCE},
+      {"siteSettingsPdfDocuments", IDS_SITE_SETTINGS_TYPE_PDF_DOCUMENTS},
+      {"siteSettingsPdfDownloadPdfs",
+       IDS_SETTINGS_SITE_SETTINGS_PDF_DOWNLOAD_PDFS},
+      {"siteSettingsProtectedContent",
+       IDS_SITE_SETTINGS_TYPE_PROTECTED_MEDIA_ID},
+      {"siteSettingsProtectedContentMidSentence",
+       IDS_SITE_SETTINGS_TYPE_PROTECTED_MEDIA_ID_MID_SENTENCE},
+      {"siteSettingsProtectedContentIdentifiers",
+       IDS_SITE_SETTINGS_TYPE_PROTECTED_MEDIA_ID},
+      {"siteSettingsProtectedContentDescription",
+       IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_DESCRIPTION},
+      {"siteSettingsProtectedContentAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_ALLOWED},
+      {"siteSettingsProtectedContentBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_BLOCKED},
+      {"siteSettingsProtectedContentBlockedSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_BLOCKED_SUB_LABEL},
 #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
-    {"siteSettingsProtectedContentIdentifiersExplanation",
-     IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_EXPLANATION},
-    {"siteSettingsProtectedContentIdentifiersAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_ALLOWED},
-    {"siteSettingsProtectedContentIdentifiersBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_BLOCKED},
-    {"siteSettingsProtectedContentIdentifiersBlockedSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_BLOCKED_SUB_LABEL},
-    {"siteSettingsProtectedContentIdentifiersAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_ALLOWED_EXCEPTIONS},
-    {"siteSettingsProtectedContentIdentifiersBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_BLOCKED_EXCEPTIONS},
+      {"siteSettingsProtectedContentIdentifiersExplanation",
+       IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_EXPLANATION},
+      {"siteSettingsProtectedContentIdentifiersAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_ALLOWED},
+      {"siteSettingsProtectedContentIdentifiersBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_BLOCKED},
+      {"siteSettingsProtectedContentIdentifiersBlockedSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_BLOCKED_SUB_LABEL},
+      {"siteSettingsProtectedContentIdentifiersAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_ALLOWED_EXCEPTIONS},
+      {"siteSettingsProtectedContentIdentifiersBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_BLOCKED_EXCEPTIONS},
 #endif
 #if BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
-    {"siteSettingsProtectedContentIdentifiersAllowedSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_ALLOWED_SUB_LABEL},
+      {"siteSettingsProtectedContentIdentifiersAllowedSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_ALLOWED_SUB_LABEL},
 #endif
-    {"siteSettingsPopups", IDS_SITE_SETTINGS_TYPE_POPUPS_REDIRECTS},
-    {"siteSettingsPopupsMidSentence",
-     IDS_SITE_SETTINGS_TYPE_POPUPS_REDIRECTS_MID_SENTENCE},
-    {"siteSettingsHidDevices", IDS_SITE_SETTINGS_TYPE_HID_DEVICES},
-    {"siteSettingsHidDevicesMidSentence",
-     IDS_SITE_SETTINGS_TYPE_HID_DEVICES_MID_SENTENCE},
-    {"siteSettingsHidDevicesAsk", IDS_SETTINGS_SITE_SETTINGS_HID_DEVICES_ASK},
-    {"siteSettingsHidDevicesBlock",
-     IDS_SETTINGS_SITE_SETTINGS_HID_DEVICES_BLOCK},
-    {"siteSettingsMidiDevices", IDS_SITE_SETTINGS_TYPE_MIDI},
-    {"siteSettingsMidiDevicesMidSentence",
-     IDS_SITE_SETTINGS_TYPE_MIDI_MID_SENTENCE},
-    {"siteSettingsSerialPorts", IDS_SITE_SETTINGS_TYPE_SERIAL_PORTS},
-    {"siteSettingsSerialPortsMidSentence",
-     IDS_SITE_SETTINGS_TYPE_SERIAL_PORTS_MID_SENTENCE},
-    {"siteSettingsUsbDevices", IDS_SITE_SETTINGS_TYPE_USB_DEVICES},
-    {"siteSettingsUsbDevicesMidSentence",
-     IDS_SITE_SETTINGS_TYPE_USB_DEVICES_MID_SENTENCE},
-    {"siteSettingsBluetoothDevices", IDS_SITE_SETTINGS_TYPE_BLUETOOTH_DEVICES},
-    {"siteSettingsBluetoothDevicesMidSentence",
-     IDS_SITE_SETTINGS_TYPE_BLUETOOTH_DEVICES_MID_SENTENCE},
-    {"siteSettingsFileSystemWrite",
-     IDS_SITE_SETTINGS_TYPE_FILE_SYSTEM_ACCESS_WRITE},
-    {"siteSettingsFileSystemWriteMidSentence",
-     IDS_SITE_SETTINGS_TYPE_FILE_SYSTEM_ACCESS_WRITE_MID_SENTENCE},
-    {"siteSettingsRemoveZoomLevel",
-     IDS_SETTINGS_SITE_SETTINGS_REMOVE_ZOOM_LEVEL},
-    {"siteSettingsZoomLevels", IDS_SITE_SETTINGS_TYPE_ZOOM_LEVELS},
-    {"siteSettingsZoomLevelsMidSentence",
-     IDS_SITE_SETTINGS_TYPE_ZOOM_LEVELS_MID_SENTENCE},
-    {"siteSettingsNoZoomedSites", IDS_SETTINGS_SITE_SETTINGS_NO_ZOOMED_SITES},
-    {"siteSettingsAskBeforeSending",
-     IDS_SETTINGS_SITE_SETTINGS_ASK_BEFORE_SENDING},
-    {"siteSettingsHandlersAskRecommended",
-     IDS_SETTINGS_SITE_SETTINGS_HANDLERS_ASK_RECOMMENDED},
-    {"siteSettingsHandlersBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_HANDLERS_BLOCKED},
-    {"siteSettingsCookiesAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_COOKIES_ALLOW_SITES},
-    {"siteSettingsAllow", IDS_SETTINGS_SITE_SETTINGS_ALLOW},
-    {"siteSettingsBlock", IDS_SETTINGS_SITE_SETTINGS_BLOCK},
-    {"siteSettingsSessionOnly", IDS_SETTINGS_SITE_SETTINGS_SESSION_ONLY},
-    {"siteSettingsBlocked", IDS_SETTINGS_SITE_SETTINGS_BLOCKED},
-    {"siteSettingsActionAskDefault",
-     IDS_SETTINGS_SITE_SETTINGS_ASK_DEFAULT_MENU},
-    {"siteSettingsActionAllowDefault",
-     IDS_SETTINGS_SITE_SETTINGS_ALLOW_DEFAULT_MENU},
-    {"siteSettingsActionAutomaticDefault",
-     IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DEFAULT_MENU},
-    {"siteSettingsActionBlockDefault",
-     IDS_SETTINGS_SITE_SETTINGS_BLOCK_DEFAULT_MENU},
-    {"siteSettingsActionMuteDefault",
-     IDS_SETTINGS_SITE_SETTINGS_MUTE_DEFAULT_MENU},
-    {"siteSettingsActionAllow", IDS_SETTINGS_SITE_SETTINGS_ALLOW_MENU},
-    {"siteSettingsActionBlock", IDS_SETTINGS_SITE_SETTINGS_BLOCK_MENU},
-    {"siteSettingsActionAsk", IDS_SETTINGS_SITE_SETTINGS_ASK_MENU},
-    {"siteSettingsActionMute", IDS_SETTINGS_SITE_SETTINGS_MUTE_MENU},
-    {"siteSettingsActionReset", IDS_SETTINGS_SITE_SETTINGS_RESET_MENU},
-    {"siteSettingsActionSessionOnly",
-     IDS_SETTINGS_SITE_SETTINGS_SESSION_ONLY_MENU},
-    {"siteSettingsUsage", IDS_SETTINGS_SITE_SETTINGS_USAGE},
-    {"siteSettingsUsageNone", IDS_SETTINGS_SITE_SETTINGS_USAGE_NONE},
-    {"siteSettingsPermissions", IDS_SETTINGS_SITE_SETTINGS_PERMISSIONS},
-    {"siteSettingsPermissionsMore",
-     IDS_SETTINGS_SITE_SETTINGS_PERMISSIONS_MORE},
-    {"siteSettingsContent", IDS_SETTINGS_SITE_SETTINGS_CONTENT},
-    {"siteSettingsContentMore", IDS_SETTINGS_SITE_SETTINGS_CONTENT_MORE},
-    {"siteSettingsSourceExtensionAllow",
-     IDS_PAGE_INFO_PERMISSION_ALLOWED_BY_EXTENSION},
-    {"siteSettingsSourceExtensionBlock",
-     IDS_PAGE_INFO_PERMISSION_BLOCKED_BY_EXTENSION},
-    {"siteSettingsSourceExtensionAsk",
-     IDS_PAGE_INFO_PERMISSION_ASK_BY_EXTENSION},
-    {"siteSettingsSourcePolicyAllow",
-     IDS_PAGE_INFO_PERMISSION_ALLOWED_BY_POLICY},
-    {"siteSettingsSourcePolicyBlock",
-     IDS_PAGE_INFO_PERMISSION_BLOCKED_BY_POLICY},
-    {"siteSettingsSourcePolicyAsk", IDS_PAGE_INFO_PERMISSION_ASK_BY_POLICY},
-    {"siteSettingsAdsBlockNotBlocklistedSingular",
-     IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK_NOT_BLOCKLISTED_SINGULAR},
-    {"siteSettingsAllowlisted", IDS_SETTINGS_SITE_SETTINGS_ALLOWLISTED},
-    {"siteSettingsAdsBlockBlocklistedSingular",
-     IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK_BLOCKLISTED_SINGULAR},
-    {"siteSettingsSourceEmbargo",
-     IDS_PAGE_INFO_PERMISSION_AUTOMATICALLY_BLOCKED},
-    {"siteSettingsSourceInsecureOrigin",
-     IDS_SETTINGS_SITE_SETTINGS_SOURCE_INSECURE_ORIGIN},
-    {"siteSettingsSourceKillSwitch",
-     IDS_SETTINGS_SITE_SETTINGS_SOURCE_KILL_SWITCH},
-    {"siteSettingsReset", IDS_SETTINGS_SITE_SETTINGS_RESET_BUTTON},
-    {"siteSettingsCookiesThirdPartyExceptionLabel",
-     IDS_SETTINGS_SITE_SETTINGS_THIRD_PARTY_COOKIES_EXCEPTION_LABEL},
-    {"siteSettingsCookieRemoveSite",
-     IDS_SETTINGS_SITE_SETTINGS_COOKIE_REMOVE_SITE},
-    {"siteSettingsDelete", IDS_SETTINGS_SITE_SETTINGS_DELETE},
-    {"siteSettingsDeleteAllStorageDialogTitle",
-     IDS_SETTINGS_SITE_SETTINGS_DELETE_ALL_STORAGE_DIALOG_TITLE},
-    {"siteSettingsDeleteDisplayedStorageDialogTitle",
-     IDS_SETTINGS_SITE_SETTINGS_DELETE_DISPLAYED_STORAGE_DIALOG_TITLE},
-    {"siteSettingsFirstPartySetsLearnMore",
-     IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_LEARN_MORE},
-    {"siteSettingsFirstPartySetsLearnMoreAccessibility",
-     IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_LEARN_MORE_ACCESSIBILITY},
-    {"siteSettingsClearAllStorageDescription",
-     IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_DESCRIPTION},
-    {"siteSettingsClearDisplayedStorageDescription",
-     IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_DESCRIPTION},
-    {"siteSettingsDeleteAllStorageLabel",
-     IDS_SETTINGS_SITE_SETTINGS_DELETE_ALL_STORAGE_LABEL},
-    {"siteSettingsDeleteDisplayedStorageLabel",
-     IDS_SETTINGS_SITE_SETTINGS_DELETE_DISPLAYED_STORAGE_LABEL},
-    {"siteSettingsDeleteAllStorageConfirmation",
-     IDS_SETTINGS_SITE_SETTINGS_DELETE_ALL_STORAGE_CONFIRMATION},
-    {"siteSettingsDeleteDisplayedStorageConfirmation",
-     IDS_SETTINGS_SITE_SETTINGS_DELETE_DISPLAYED_STORAGE_CONFIRMATION},
-    {"siteSettingsDeleteAllStorageConfirmationInstalled",
-     IDS_SETTINGS_SITE_SETTINGS_DELETE_ALL_STORAGE_CONFIRMATION_INSTALLED},
-    {"siteSettingsDeleteDisplayedStorageConfirmationInstalled",
-     IDS_SETTINGS_SITE_SETTINGS_DELETE_DISPLAYED_STORAGE_CONFIRMATION_INSTALLED},
-    {"siteSettingsClearAllStorageSignOut",
-     IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_SIGN_OUT},
-    {"siteSettingsClearDisplayedStorageSignOut",
-     IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_SIGN_OUT},
-    {"siteSettingsSiteDetailsSubpageAccessibilityLabel",
-     IDS_SETTINGS_SITE_SETTINGS_SITE_DETAILS_SUBPAGE_ACCESSIBILITY_LABEL},
-    {"firstPartySetsMoreActionsTitle",
-     IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_MORE_ACTIONS_TITLE},
-    {"firstPartySetsShowRelatedSitesButton",
-     IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_SHOW_RELATED_SITES_BUTTON},
-    {"firstPartySetsSiteDeleteStorageButton",
-     IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_SITE_DELETE_STORAGE_BUTTON},
-    {"siteSettingsSiteClearStorage",
-     IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_STORAGE},
-    {"siteSettingsSiteClearStorageConfirmation",
-     IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_STORAGE_CONFIRMATION},
-    {"siteSettingsSiteClearStorageConfirmationNew",
-     IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_STORAGE_CONFIRMATION_NEW},
-    {"siteSettingsSiteDeleteStorageDialogTitle",
-     IDS_SETTINGS_SITE_SETTINGS_SITE_DELETE_STORAGE_DIALOG_TITLE},
-    {"siteSettingsSiteClearStorageSignOut",
-     IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_STORAGE_SIGN_OUT},
-    {"siteSettingsSiteDeleteStorageOfflineData",
-     IDS_SETTINGS_SITE_SETTINGS_SITE_DELETE_STORAGE_OFFLINE_DATA},
-    {"siteSettingsRemoveSiteAdPersonalization",
-     IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_AD_PERSONALIZATION},
-    {"siteSettingsSiteGroupDeleteOfflineData",
-     IDS_SETTINGS_SITE_SETTINGS_SITE_GROUP_DELETE_OFFLINE_DATA},
-    {"siteSettingsSiteResetAll", IDS_SETTINGS_SITE_SETTINGS_SITE_RESET_ALL},
-    {"siteSettingsSiteResetConfirmation",
-     IDS_SETTINGS_SITE_SETTINGS_SITE_RESET_CONFIRMATION},
-    {"siteSettingsRemoveSiteOriginDialogTitle",
-     IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_ORIGIN_DIALOG_TITLE},
-    {"siteSettingsRemoveSiteOriginAppDialogTitle",
-     IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_ORIGIN_APP_DIALOG_TITLE},
-    {"siteSettingsRemoveSiteOriginPartitionedDialogTitle",
-     IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_ORIGIN_PARTITIONED_DIALOG_TITLE},
-    {"siteSettingsRemoveSiteGroupDialogTitle",
-     IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_GROUP_DIALOG_TITLE},
-    {"siteSettingsRemoveSiteGroupAppDialogTitle",
-     IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_GROUP_APP_DIALOG_TITLE},
-    {"siteSettingsRemoveSiteGroupAppPluralDialogTitle",
-     IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_GROUP_APP_PLURAL_DIALOG_TITLE},
-    {"siteSettingsRemoveSiteOriginLogout",
-     IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_ORIGIN_LOGOUT},
-    {"siteSettingsRemoveSiteGroupLogout",
-     IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_GROUP_LOGOUT},
-    {"siteSettingsRemoveSiteOfflineData",
-     IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_OFFLINE_DATA},
-    {"siteSettingsRemoveSitePermissions",
-     IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_PERMISSIONS},
-    {"siteSettingsRemoveSiteConfirm",
-     IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_CONFIRM},
-    {"thirdPartyCookie", IDS_NEW_TAB_OTR_THIRD_PARTY_COOKIE},
-    {"thirdPartyCookieSublabel", IDS_NEW_TAB_OTR_THIRD_PARTY_COOKIE_SUBLABEL},
-    {"handlerIsDefault", IDS_SETTINGS_SITE_SETTINGS_HANDLER_IS_DEFAULT},
-    {"handlerSetDefault", IDS_SETTINGS_SITE_SETTINGS_HANDLER_SET_DEFAULT},
-    {"handlerRemove", IDS_SETTINGS_SITE_SETTINGS_REMOVE},
-    {"incognitoSiteOnly", IDS_SETTINGS_SITE_SETTINGS_INCOGNITO_ONLY},
-    {"incognitoSiteExceptionDesc",
-     IDS_SETTINGS_SITE_SETTINGS_INCOGNITO_SITE_EXCEPTION_DESC},
-    {"noSitesAdded", IDS_SETTINGS_SITE_NO_SITES_ADDED},
-    {"siteSettingsDefaultBehavior",
-     IDS_SETTINGS_SITE_SETTINGS_DEFAULT_BEHAVIOR},
-    {"siteSettingsDefaultBehaviorDescription",
-     IDS_SETTINGS_SITE_SETTINGS_DEFAULT_BEHAVIOR_DESCRIPTION},
-    {"siteSettingsNotificationsDefaultBehaviorDescription",
-     IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_DEFAULT_BEHAVIOR_DESC},
-    {"siteSettingsCustomizedBehaviors",
-     IDS_SETTINGS_SITE_SETTINGS_CUSTOMIZED_BEHAVIORS},
-    {"siteSettingsCustomizedBehaviorsDescription",
-     IDS_SETTINGS_SITE_SETTINGS_CUSTOMIZED_BEHAVIORS_DESCRIPTION},
-    {"siteSettingsCustomizedBehaviorsDescriptionShort",
-     IDS_SETTINGS_SITE_SETTINGS_CUSTOMIZED_BEHAVIORS_DESCRIPTION_SHORT},
-    {"siteSettingsAdsDescription", IDS_SETTINGS_SITE_SETTINGS_ADS_DESCRIPTION},
-    {"siteSettingsAdsAllowed", IDS_SETTINGS_SITE_SETTINGS_ADS_ALLOWED},
-    {"siteSettingsAdsBlocked", IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCKED},
-    {"siteSettingsAdsAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_ADS_ALLOWED_EXCEPTIONS},
-    {"siteSettingsAdsBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCKED_EXCEPTIONS},
-    {"siteSettingsArDescription", IDS_SETTINGS_SITE_SETTINGS_AR_DESCRIPTION},
-    {"siteSettingsArAllowed", IDS_SETTINGS_SITE_SETTINGS_AR_ALLOWED},
-    {"siteSettingsArBlocked", IDS_SETTINGS_SITE_SETTINGS_AR_BLOCKED},
-    {"siteSettingsArAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_AR_ALLOWED_EXCEPTIONS},
-    {"siteSettingsArBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_AR_BLOCKED_EXCEPTIONS},
-    {"siteSettingsAutomaticDownloadsDescription",
-     IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOADS_DESCRIPTION},
-    {"siteSettingsAutomaticDownloadsAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOADS_ALLOWED},
-    {"siteSettingsAutomaticDownloadsBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOADS_BLOCKED},
-    {"siteSettingsAutomaticDownloadsAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOADS_ALLOWED_EXCEPTIONS},
-    {"siteSettingsAutomaticDownloadsBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOADS_BLOCKED_EXCEPTIONS},
-    {"siteSettingsAutoPictureInPictureDescription",
-     IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_DESCRIPTION},
-    {"siteSettingsAutoPictureInPictureAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_ALLOWED},
-    {"siteSettingsAutoPictureInPictureBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_BLOCKED},
-    {"siteSettingsAutoPictureInPictureAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_ALLOWED_EXCEPTIONS},
-    {"siteSettingsAutoPictureInPictureBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_BLOCKED_EXCEPTIONS},
-    {"siteSettingsBackgroundSyncDescription",
-     IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_DESCRIPTION},
-    {"siteSettingsBackgroundSyncAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_ALLOWED},
-    {"siteSettingsBackgroundSyncBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_BLOCKED},
-    {"siteSettingsBackgroundSyncBlockedSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_BLOCKED_SUB_LABEL},
-    {"siteSettingsBackgroundSyncAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_ALLOWED_EXCEPTIONS},
-    {"siteSettingsBackgroundSyncBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_BLOCKED_EXCEPTIONS},
-    {"siteSettingsBluetoothDevicesDescription",
-     IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_DEVICES_DESCRIPTION},
-    {"siteSettingsBluetoothDevicesAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_DEVICES_ALLOWED},
-    {"siteSettingsBluetoothDevicesBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_DEVICES_BLOCKED},
-    {"siteSettingsCameraDescription",
-     IDS_SETTINGS_SITE_SETTINGS_CAMERA_DESCRIPTION},
-    {"siteSettingsCameraAllowed", IDS_SETTINGS_SITE_SETTINGS_CAMERA_ALLOWED},
-    {"siteSettingsCameraBlocked", IDS_SETTINGS_SITE_SETTINGS_CAMERA_BLOCKED},
-    {"siteSettingsCameraBlockedSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_CAMERA_BLOCKED_SUB_LABEL},
-    {"siteSettingsCameraAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_CAMERA_ALLOWED_EXCEPTIONS},
-    {"siteSettingsCameraBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_CAMERA_BLOCKED_EXCEPTIONS},
-    {"siteSettingsClipboardDescription",
-     IDS_SETTINGS_SITE_SETTINGS_CLIPBOARD_DESCRIPTION},
-    {"siteSettingsClipboardAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_CLIPBOARD_ALLOWED},
-    {"siteSettingsClipboardBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_CLIPBOARD_BLOCKED},
-    {"siteSettingsClipboardAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_CLIPBOARD_ALLOWED_EXCEPTIONS},
-    {"siteSettingsClipboardBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_CLIPBOARD_BLOCKED_EXCEPTIONS},
-    {"siteSettingsDeviceUseDescription",
-     IDS_SETTINGS_SITE_SETTINGS_DEVICE_USE_DESCRIPTION},
-    {"siteSettingsDeviceUseAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_DEVICE_USE_ALLOWED},
-    {"siteSettingsDeviceUseBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_DEVICE_USE_BLOCKED},
-    {"siteSettingsDeviceUseAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_DEVICE_USE_ALLOWED_EXCEPTIONS},
-    {"siteSettingsDeviceUseBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_DEVICE_USE_BLOCKED_EXCEPTIONS},
-    {"siteSettingsFederatedIdentityApi",
-     IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API},
-    {"siteSettingsFederatedIdentityApiAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_FEDERATED_IDENTITY_API_ALLOWED},
-    {"siteSettingsFederatedIdentityApiBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_FEDERATED_IDENTITY_API_BLOCKED},
-    {"siteSettingsFederatedIdentityApiAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_FEDERATED_IDENTITY_API_ALLOWED_EXCEPTIONS},
-    {"siteSettingsFederatedIdentityApiBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_FEDERATED_IDENTITY_API_BLOCKED_EXCEPTIONS},
-    {"siteSettingsFederatedIdentityApiDescription",
-     IDS_SETTINGS_SITE_SETTINGS_FEDERATED_IDENTITY_API_DESCRIPTION},
-    {"siteSettingsFederatedIdentityApiMidSentence",
-     IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API_MID_SENTENCE},
-    {"siteSettingsFileSystemWriteDescription",
-     IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_WRITE_DESCRIPTION},
-    {"siteSettingsFileSystemWriteAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_WRITE_ALLOWED},
-    {"siteSettingsFileSystemWriteBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_WRITE_BLOCKED},
-    {"siteSettingsFileSystemWriteBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_WRITE_BLOCKED_EXCEPTIONS},
-    {"siteSettingsFontsDescription",
-     IDS_SETTINGS_SITE_SETTINGS_FONTS_DESCRIPTION},
-    {"siteSettingsFontsAllowed", IDS_SETTINGS_SITE_SETTINGS_FONTS_ALLOWED},
-    {"siteSettingsFontsBlocked", IDS_SETTINGS_SITE_SETTINGS_FONTS_BLOCKED},
-    {"siteSettingsFontsAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_FONTS_ALLOWED_EXCEPTIONS},
-    {"siteSettingsFontsBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_FONTS_BLOCKED_EXCEPTIONS},
-    {"siteSettingsHidDevicesDescription",
-     IDS_SETTINGS_SITE_SETTINGS_HID_DEVICES_DESCRIPTION},
-    {"siteSettingsHidDevicesAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_HID_DEVICES_ALLOWED},
-    {"siteSettingsHidDevicesBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_HID_DEVICES_BLOCKED},
-    {"siteSettingsImagesDescription",
-     IDS_SETTINGS_SITE_SETTINGS_IMAGES_DESCRIPTION},
-    {"siteSettingsImagesAllowed", IDS_SETTINGS_SITE_SETTINGS_IMAGES_ALLOWED},
-    {"siteSettingsImagesBlocked", IDS_SETTINGS_SITE_SETTINGS_IMAGES_BLOCKED},
-    {"siteSettingsImagesBlockedSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_IMAGES_BLOCKED_SUB_LABEL},
-    {"siteSettingsImagesAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_IMAGES_ALLOWED_EXCEPTIONS},
-    {"siteSettingsImagedBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_IMAGES_BLOCKED_EXCEPTIONS},
-    {"siteSettingsInsecureContentDescription",
-     IDS_SETTINGS_SITE_SETTINGS_INSECURE_CONTENT_DESCRIPTION},
-    {"siteSettingsInsecureContentAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_INSECURE_CONTENT_ALLOWED_EXCEPTIONS},
-    {"siteSettingsInsecureContentBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_INSECURE_CONTENT_BLOCKED_EXCEPTIONS},
-    {"siteSettingsJavascriptDescription",
-     IDS_SETTINGS_SITE_SETTINGS_JAVASCRIPT_DESCRIPTION},
-    {"siteSettingsJavascriptAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_JAVASCRIPT_ALLOWED},
-    {"siteSettingsJavascriptBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_JAVASCRIPT_BLOCKED},
-    {"siteSettingsJavascriptAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_JAVASCRIPT_ALLOWED_EXCEPTIONS},
-    {"siteSettingsJavascriptBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_JAVASCRIPT_BLOCKED_EXCEPTIONS},
-    {"siteSettingsLocationDescription",
-     IDS_SETTINGS_SITE_SETTINGS_LOCATION_DESCRIPTION},
-    {"siteSettingsLocationAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_LOCATION_ALLOWED},
-    {"siteSettingsLocationAskQuiet",
-     IDS_SETTINGS_SITE_SETTINGS_PERMISSION_QUIET},
-    {"siteSettingsLocationAskCPSS", IDS_SETTINGS_SITE_SETTINGS_PERMISSION_CPSS},
-    {"siteSettingsLocationAskLoud", IDS_SETTINGS_SITE_SETTINGS_PERMISSION_LOUD},
-    {"siteSettingsLocationBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_LOCATION_BLOCKED},
-    {"siteSettingsLocationBlockedSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_LOCATION_BLOCKED_SUB_LABEL},
-    {"siteSettingsLocationAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_LOCATION_ALLOWED_EXCEPTIONS},
-    {"siteSettingsLocationBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_LOCATION_BLOCKED_EXCEPTIONS},
-    {"siteSettingsMicDescription", IDS_SETTINGS_SITE_SETTINGS_MIC_DESCRIPTION},
-    {"siteSettingsMicAllowed", IDS_SETTINGS_SITE_SETTINGS_MIC_ALLOWED},
-    {"siteSettingsMicBlocked", IDS_SETTINGS_SITE_SETTINGS_MIC_BLOCKED},
-    {"siteSettingsMicBlockedSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_MIC_BLOCKED_SUB_LABEL},
-    {"siteSettingsMicAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_MIC_ALLOWED_EXCEPTIONS},
-    {"siteSettingsMicBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_MIC_BLOCKED_EXCEPTIONS},
-    {"siteSettingsMidiDescription",
-     IDS_SETTINGS_SITE_SETTINGS_MIDI_DESCRIPTION},
-    {"siteSettingsMidiAllowed", IDS_SETTINGS_SITE_SETTINGS_MIDI_ALLOWED},
-    {"siteSettingsMidiBlocked", IDS_SETTINGS_SITE_SETTINGS_MIDI_BLOCKED},
-    {"siteSettingsMidiAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_MIDI_ALLOWED_EXCEPTIONS},
-    {"siteSettingsMidiBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_MIDI_BLOCKED_EXCEPTIONS},
-    {"siteSettingsMotionSensorsDescription",
-     IDS_SETTINGS_SITE_SETTINGS_MOTION_SENSORS_DESCRIPTION},
-    {"siteSettingsMotionSensorsAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_MOTION_SENSORS_ALLOWED},
-    {"siteSettingsMotionSensorsBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_MOTION_SENSORS_BLOCKED},
-    {"siteSettingsMotionSensorsBlockedSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_MOTION_SENSORS_BLOCKED_SUB_LABEL},
-    {"siteSettingsMotionSensorsAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_MOTION_SENSORS_ALLOWED_EXCEPTIONS},
-    {"siteSettingsMotionSensorsBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_MOTION_SENSORS_BLOCKED_EXCEPTIONS},
-    {"siteSettingsNotificationsDescription",
-     IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_DESCRIPTION},
-    {"siteSettingsNotificationsAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_ALLOWED},
-    {"siteSettingsNotificationsPartial",
-     IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_PARTIAL},
-    {"siteSettingsNotificationsPartialSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_PARTIAL_SUB_LABEL},
-    {"siteSettingsNotificationsAskState",
-     IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_ASK_STATE},
-    {"siteSettingsNotificationsAskQuiet",
-     IDS_SETTINGS_SITE_SETTINGS_PERMISSION_QUIET},
-    {"siteSettingsNotificationsAskCPSS",
-     IDS_SETTINGS_SITE_SETTINGS_PERMISSION_CPSS},
-    {"siteSettingsNotificationsAskLoud",
-     IDS_SETTINGS_SITE_SETTINGS_PERMISSION_LOUD},
-    {"siteSettingsNotificationsBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_BLOCKED},
-    {"siteSettingsNotificationsBlockedSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_BLOCKED_SUB_LABEL},
-    {"siteSettingsNotificationsAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_ALLOWED_EXCEPTIONS},
-    {"siteSettingsNotificationsBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_BLOCKED_EXCEPTIONS},
-    {"siteSettingsPaymentHandlersDescription",
-     IDS_SETTINGS_SITE_SETTINGS_PAYMENT_HANDLERS_DESCRIPTION},
-    {"siteSettingsPaymentHandlersAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_PAYMENT_HANDLERS_ALLOWED},
-    {"siteSettingsPaymentHandlersBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_PAYMENT_HANDLERS_BLOCKED},
-    {"siteSettingsPaymentHandlersAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_PAYMENT_HANDLERS_ALLOWED_EXCEPTIONS},
-    {"siteSettingsPaymentHandlersBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_PAYMENT_HANDLERS_BLOCKED_EXCEPTIONS},
-    {"siteSettingsPdfsDescription",
-     IDS_SETTINGS_SITE_SETTINGS_PDFS_DESCRIPTION},
-    {"siteSettingsPdfsAllowed", IDS_SETTINGS_SITE_SETTINGS_PDFS_ALLOWED},
-    {"siteSettingsPdfsBlocked", IDS_SETTINGS_SITE_SETTINGS_PDFS_BLOCKED},
-    {"siteSettingsPopupsDescription",
-     IDS_SETTINGS_SITE_SETTINGS_POPUPS_DESCRIPTION},
-    {"siteSettingsPopupsAllowed", IDS_SETTINGS_SITE_SETTINGS_POPUPS_ALLOWED},
-    {"siteSettingsPopupsBlocked", IDS_SETTINGS_SITE_SETTINGS_POPUPS_BLOCKED},
-    {"siteSettingsPopupsAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_POPUPS_ALLOWED_EXCEPTIONS},
-    {"siteSettingsPopupsBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_POPUPS_BLOCKED_EXCEPTIONS},
-    {"siteSettingsProtocolHandlersDescription",
-     IDS_SETTINGS_SITE_SETTINGS_PROTOCOL_HANDLERS_DESCRIPTION},
-    {"siteSettingsProtocolHandlersAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_PROTOCOL_HANDLERS_ALLOWED},
-    {"siteSettingsProtocolHandlersBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_PROTOCOL_HANDLERS_BLOCKED},
-    {"siteSettingsProtocolHandlersBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_PROTOCOL_HANDLERS_BLOCKED_EXCEPTIONS},
-    {"siteSettingsSerialPortsDescription",
-     IDS_SETTINGS_SITE_SETTINGS_SERIAL_PORTS_DESCRIPTION},
-    {"siteSettingsSerialPortsAllowed",
-     IDS_SETTINGS_SITE_SETTINGS_SERIAL_PORTS_ALLOWED},
-    {"siteSettingsSerialPortsBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_SERIAL_PORTS_BLOCKED},
-    {"siteSettingsSoundDescription",
-     IDS_SETTINGS_SITE_SETTINGS_SOUND_DESCRIPTION},
-    {"siteSettingsSoundAllowed", IDS_SETTINGS_SITE_SETTINGS_SOUND_ALLOWED},
-    {"siteSettingsSoundBlocked", IDS_SETTINGS_SITE_SETTINGS_SOUND_BLOCKED},
-    {"siteSettingsSoundBlockedSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_SOUND_BLOCKED_SUB_LABEL},
-    {"siteSettingsSoundAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_SOUND_ALLOWED_EXCEPTIONS},
-    {"siteSettingsSoundBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_SOUND_BLOCKED_EXCEPTIONS},
-    {"siteSettingsUsbDescription", IDS_SETTINGS_SITE_SETTINGS_USB_DESCRIPTION},
-    {"siteSettingsUsbAllowed", IDS_SETTINGS_SITE_SETTINGS_USB_ALLOWED},
-    {"siteSettingsUsbBlocked", IDS_SETTINGS_SITE_SETTINGS_USB_BLOCKED},
-    {"siteSettingsVrDescription", IDS_SETTINGS_SITE_SETTINGS_VR_DESCRIPTION},
-    {"siteSettingsVrAllowed", IDS_SETTINGS_SITE_SETTINGS_VR_ALLOWED},
-    {"siteSettingsVrBlocked", IDS_SETTINGS_SITE_SETTINGS_VR_BLOCKED},
-    {"siteSettingsVrAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_VR_ALLOWED_EXCEPTIONS},
-    {"siteSettingsVrBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_VR_BLOCKED_EXCEPTIONS},
-    {"siteSettingsZoomLevelsDescription",
-     IDS_SETTINGS_SITE_SETTINGS_ZOOM_LEVELS_DESCRIPTION},
-    {"siteSettingsAds", IDS_SITE_SETTINGS_TYPE_ADS},
-    {"siteSettingsAdsMidSentence", IDS_SITE_SETTINGS_TYPE_ADS_MID_SENTENCE},
-    {"siteSettingsPaymentHandler", IDS_SITE_SETTINGS_TYPE_PAYMENT_HANDLER},
-    {"siteSettingsPaymentHandlerMidSentence",
-     IDS_SITE_SETTINGS_TYPE_PAYMENT_HANDLER_MID_SENTENCE},
-    {"siteSettingsBlockAutoplaySetting",
-     IDS_SETTINGS_SITE_SETTINGS_BLOCK_AUTOPLAY},
-    {"emptyAllSitesPage", IDS_SETTINGS_SITE_SETTINGS_EMPTY_ALL_SITES_PAGE},
-    {"noSitesFound", IDS_SETTINGS_SITE_SETTINGS_NO_SITES_FOUND},
-    {"siteSettingsBluetoothScanning",
-     IDS_SITE_SETTINGS_TYPE_BLUETOOTH_SCANNING},
-    {"siteSettingsBluetoothScanningMidSentence",
-     IDS_SITE_SETTINGS_TYPE_BLUETOOTH_SCANNING_MID_SENTENCE},
-    {"siteSettingsBluetoothScanningDescription",
-     IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_SCANNING_DESCRIPTION},
-    {"siteSettingsBluetoothScanningAsk",
-     IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_SCANNING_ASK},
-    {"siteSettingsBluetoothScanningBlock",
-     IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_SCANNING_BLOCK},
-    {"siteSettingsBluetoothScanningAllowedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_SCANNING_ALLOWED_EXCEPTIONS},
-    {"siteSettingsBluetoothScanningBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_SCANNING_BLOCKED_EXCEPTIONS},
-    {"siteSettingsAr", IDS_SITE_SETTINGS_TYPE_AR},
-    {"siteSettingsArMidSentence", IDS_SITE_SETTINGS_TYPE_AR_MID_SENTENCE},
-    {"siteSettingsArAsk", IDS_SETTINGS_SITE_SETTINGS_AR_ASK},
-    {"siteSettingsArBlock", IDS_SETTINGS_SITE_SETTINGS_AR_BLOCK},
-    {"siteSettingsVr", IDS_SITE_SETTINGS_TYPE_VR},
-    {"siteSettingsVrMidSentence", IDS_SITE_SETTINGS_TYPE_VR_MID_SENTENCE},
-    {"siteSettingsWindowManagement", IDS_SITE_SETTINGS_TYPE_WINDOW_MANAGEMENT},
-    {"siteSettingsWindowManagementMidSentence",
-     IDS_SITE_SETTINGS_TYPE_WINDOW_MANAGEMENT_MID_SENTENCE},
-    {"siteSettingsWindowManagementDescription",
-     IDS_SETTINGS_SITE_SETTINGS_WINDOW_MANAGEMENT_DESCRIPTION},
-    {"siteSettingsWindowManagementAsk",
-     IDS_SETTINGS_SITE_SETTINGS_WINDOW_MANAGEMENT_ASK},
-    {"siteSettingsWindowManagementBlocked",
-     IDS_SETTINGS_SITE_SETTINGS_WINDOW_MANAGEMENT_BLOCKED},
-    {"siteSettingsWindowManagementAskExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_WINDOW_MANAGEMENT_ASK_EXCEPTIONS},
-    {"siteSettingsWindowManagementBlockedExceptions",
-     IDS_SETTINGS_SITE_SETTINGS_WINDOW_MANAGEMENT_BLOCKED_EXCEPTIONS},
-    {"siteSettingsFontAccessMidSentence",
-     IDS_SITE_SETTINGS_TYPE_FONT_ACCESS_MID_SENTENCE},
-    {"siteSettingsIdleDetection", IDS_SITE_SETTINGS_TYPE_IDLE_DETECTION},
-    {"siteSettingsIdleDetectionMidSentence",
-     IDS_SITE_SETTINGS_TYPE_IDLE_DETECTION_MID_SENTENCE},
-    {"siteSettingsExtensionIdDescription",
-     IDS_SETTINGS_SITE_SETTINGS_EXTENSION_ID_DESCRIPTION},
-    {"siteSettingsSiteDataAllowedSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_PAGE_SITE_DATA_ALLOWED_SUB_LABEL},
-    {"siteSettingsSiteDataBlockedSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_PAGE_SITE_DATA_BLOCKED_SUB_LABEL},
-    {"siteSettingsSiteDataDeleteOnExitSubLabel",
-     IDS_SETTINGS_SITE_SETTINGS_PAGE_SITE_DATA_DELETE_ON_EXIT_SUB_LABEL},
-    {"siteSettingsAntiAbuse", IDS_SITE_SETTINGS_TYPE_ANTI_ABUSE},
-    {"siteSettingsAntiAbuseDescription", IDS_SETTINGS_ANTI_ABUSE_DESCRIPTION},
-    {"siteSettingsAntiAbuseEnabledSubLabel",
-     IDS_SETTINGS_ANTI_ABUSE_ENABLED_SUB_LABEL},
-    {"siteSettingsAntiAbuseDisabledSubLabel",
-     IDS_SETTINGS_ANTI_ABUSE_DISABLED_SUB_LABEL},
-    {"antiAbuseWhenOnHeader", IDS_SETTINGS_ANTI_ABUSE_WHEN_ON_HEADER},
-    {"antiAbuseWhenOnSectionOne", IDS_SETTINGS_ANTI_ABUSE_WHEN_ON_SECTION_ONE},
-    {"antiAbuseWhenOnSectionTwo", IDS_SETTINGS_ANTI_ABUSE_WHEN_ON_SECTION_TWO},
-    {"antiAbuseWhenOnSectionThree",
-     IDS_SETTINGS_ANTI_ABUSE_WHEN_ON_SECTION_THREE},
-    {"antiAbuseThingsToConsiderHeader",
-     IDS_SETTINGS_ANTI_ABUSE_THINGS_TO_CONSIDER_HEADER},
-    {"antiAbuseThingsToConsiderSectionOne",
-     IDS_SETTINGS_ANTI_ABUSE_THINGS_TO_CONSIDER_SECTION_ONE},
+      {"siteSettingsPopups", IDS_SITE_SETTINGS_TYPE_POPUPS_REDIRECTS},
+      {"siteSettingsPopupsMidSentence",
+       IDS_SITE_SETTINGS_TYPE_POPUPS_REDIRECTS_MID_SENTENCE},
+      {"siteSettingsHidDevices", IDS_SITE_SETTINGS_TYPE_HID_DEVICES},
+      {"siteSettingsHidDevicesMidSentence",
+       IDS_SITE_SETTINGS_TYPE_HID_DEVICES_MID_SENTENCE},
+      {"siteSettingsHidDevicesAsk", IDS_SETTINGS_SITE_SETTINGS_HID_DEVICES_ASK},
+      {"siteSettingsHidDevicesBlock",
+       IDS_SETTINGS_SITE_SETTINGS_HID_DEVICES_BLOCK},
+      {"siteSettingsMidiDevices", IDS_SITE_SETTINGS_TYPE_MIDI},
+      {"siteSettingsMidiDevicesMidSentence",
+       IDS_SITE_SETTINGS_TYPE_MIDI_MID_SENTENCE},
+      {"siteSettingsSerialPorts", IDS_SITE_SETTINGS_TYPE_SERIAL_PORTS},
+      {"siteSettingsSerialPortsMidSentence",
+       IDS_SITE_SETTINGS_TYPE_SERIAL_PORTS_MID_SENTENCE},
+      {"siteSettingsUsbDevices", IDS_SITE_SETTINGS_TYPE_USB_DEVICES},
+      {"siteSettingsUsbDevicesMidSentence",
+       IDS_SITE_SETTINGS_TYPE_USB_DEVICES_MID_SENTENCE},
+      {"siteSettingsBluetoothDevices",
+       IDS_SITE_SETTINGS_TYPE_BLUETOOTH_DEVICES},
+      {"siteSettingsBluetoothDevicesMidSentence",
+       IDS_SITE_SETTINGS_TYPE_BLUETOOTH_DEVICES_MID_SENTENCE},
+      {"siteSettingsFileSystemWrite",
+       IDS_SITE_SETTINGS_TYPE_FILE_SYSTEM_ACCESS_WRITE},
+      {"siteSettingsFileSystemWriteMidSentence",
+       IDS_SITE_SETTINGS_TYPE_FILE_SYSTEM_ACCESS_WRITE_MID_SENTENCE},
+      {"siteSettingsRemoveZoomLevel",
+       IDS_SETTINGS_SITE_SETTINGS_REMOVE_ZOOM_LEVEL},
+      {"siteSettingsZoomLevels", IDS_SITE_SETTINGS_TYPE_ZOOM_LEVELS},
+      {"siteSettingsZoomLevelsMidSentence",
+       IDS_SITE_SETTINGS_TYPE_ZOOM_LEVELS_MID_SENTENCE},
+      {"siteSettingsNoZoomedSites", IDS_SETTINGS_SITE_SETTINGS_NO_ZOOMED_SITES},
+      {"siteSettingsAskBeforeSending",
+       IDS_SETTINGS_SITE_SETTINGS_ASK_BEFORE_SENDING},
+      {"siteSettingsHandlersAskRecommended",
+       IDS_SETTINGS_SITE_SETTINGS_HANDLERS_ASK_RECOMMENDED},
+      {"siteSettingsHandlersBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_HANDLERS_BLOCKED},
+      {"siteSettingsCookiesAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_COOKIES_ALLOW_SITES},
+      {"siteSettingsAllow", IDS_SETTINGS_SITE_SETTINGS_ALLOW},
+      {"siteSettingsBlock", IDS_SETTINGS_SITE_SETTINGS_BLOCK},
+      {"siteSettingsSessionOnly", IDS_SETTINGS_SITE_SETTINGS_SESSION_ONLY},
+      {"siteSettingsBlocked", IDS_SETTINGS_SITE_SETTINGS_BLOCKED},
+      {"siteSettingsActionAskDefault",
+       IDS_SETTINGS_SITE_SETTINGS_ASK_DEFAULT_MENU},
+      {"siteSettingsActionAllowDefault",
+       IDS_SETTINGS_SITE_SETTINGS_ALLOW_DEFAULT_MENU},
+      {"siteSettingsActionAutomaticDefault",
+       IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DEFAULT_MENU},
+      {"siteSettingsActionBlockDefault",
+       IDS_SETTINGS_SITE_SETTINGS_BLOCK_DEFAULT_MENU},
+      {"siteSettingsActionMuteDefault",
+       IDS_SETTINGS_SITE_SETTINGS_MUTE_DEFAULT_MENU},
+      {"siteSettingsActionAllow", IDS_SETTINGS_SITE_SETTINGS_ALLOW_MENU},
+      {"siteSettingsActionBlock", IDS_SETTINGS_SITE_SETTINGS_BLOCK_MENU},
+      {"siteSettingsActionAsk", IDS_SETTINGS_SITE_SETTINGS_ASK_MENU},
+      {"siteSettingsActionMute", IDS_SETTINGS_SITE_SETTINGS_MUTE_MENU},
+      {"siteSettingsActionReset", IDS_SETTINGS_SITE_SETTINGS_RESET_MENU},
+      {"siteSettingsActionSessionOnly",
+       IDS_SETTINGS_SITE_SETTINGS_SESSION_ONLY_MENU},
+      {"siteSettingsUsage", IDS_SETTINGS_SITE_SETTINGS_USAGE},
+      {"siteSettingsUsageNone", IDS_SETTINGS_SITE_SETTINGS_USAGE_NONE},
+      {"siteSettingsPermissions", IDS_SETTINGS_SITE_SETTINGS_PERMISSIONS},
+      {"siteSettingsPermissionsMore",
+       IDS_SETTINGS_SITE_SETTINGS_PERMISSIONS_MORE},
+      {"siteSettingsContent", IDS_SETTINGS_SITE_SETTINGS_CONTENT},
+      {"siteSettingsContentMore", IDS_SETTINGS_SITE_SETTINGS_CONTENT_MORE},
+      {"siteSettingsSourceExtensionAllow",
+       IDS_PAGE_INFO_PERMISSION_ALLOWED_BY_EXTENSION},
+      {"siteSettingsSourceExtensionBlock",
+       IDS_PAGE_INFO_PERMISSION_BLOCKED_BY_EXTENSION},
+      {"siteSettingsSourceExtensionAsk",
+       IDS_PAGE_INFO_PERMISSION_ASK_BY_EXTENSION},
+      {"siteSettingsSourcePolicyAllow",
+       IDS_PAGE_INFO_PERMISSION_ALLOWED_BY_POLICY},
+      {"siteSettingsSourcePolicyBlock",
+       IDS_PAGE_INFO_PERMISSION_BLOCKED_BY_POLICY},
+      {"siteSettingsSourcePolicyAsk", IDS_PAGE_INFO_PERMISSION_ASK_BY_POLICY},
+      {"siteSettingsAdsBlockNotBlocklistedSingular",
+       IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK_NOT_BLOCKLISTED_SINGULAR},
+      {"siteSettingsAllowlisted", IDS_SETTINGS_SITE_SETTINGS_ALLOWLISTED},
+      {"siteSettingsAdsBlockBlocklistedSingular",
+       IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK_BLOCKLISTED_SINGULAR},
+      {"siteSettingsSourceEmbargo",
+       IDS_PAGE_INFO_PERMISSION_AUTOMATICALLY_BLOCKED},
+      {"siteSettingsSourceInsecureOrigin",
+       IDS_SETTINGS_SITE_SETTINGS_SOURCE_INSECURE_ORIGIN},
+      {"siteSettingsSourceKillSwitch",
+       IDS_SETTINGS_SITE_SETTINGS_SOURCE_KILL_SWITCH},
+      {"siteSettingsReset", IDS_SETTINGS_SITE_SETTINGS_RESET_BUTTON},
+      {"siteSettingsCookiesThirdPartyExceptionLabel",
+       IDS_SETTINGS_SITE_SETTINGS_THIRD_PARTY_COOKIES_EXCEPTION_LABEL},
+      {"siteSettingsCookieRemoveSite",
+       IDS_SETTINGS_SITE_SETTINGS_COOKIE_REMOVE_SITE},
+      {"siteSettingsDelete", IDS_SETTINGS_SITE_SETTINGS_DELETE},
+      {"siteSettingsDeleteAllStorageDialogTitle",
+       IDS_SETTINGS_SITE_SETTINGS_DELETE_ALL_STORAGE_DIALOG_TITLE},
+      {"siteSettingsDeleteDisplayedStorageDialogTitle",
+       IDS_SETTINGS_SITE_SETTINGS_DELETE_DISPLAYED_STORAGE_DIALOG_TITLE},
+      {"siteSettingsFirstPartySetsLearnMore",
+       IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_LEARN_MORE},
+      {"siteSettingsFirstPartySetsLearnMoreAccessibility",
+       IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_LEARN_MORE_ACCESSIBILITY},
+      {"siteSettingsClearAllStorageDescription",
+       IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_DESCRIPTION},
+      {"siteSettingsClearDisplayedStorageDescription",
+       IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_DESCRIPTION},
+      {"siteSettingsDeleteAllStorageLabel",
+       IDS_SETTINGS_SITE_SETTINGS_DELETE_ALL_STORAGE_LABEL},
+      {"siteSettingsDeleteDisplayedStorageLabel",
+       IDS_SETTINGS_SITE_SETTINGS_DELETE_DISPLAYED_STORAGE_LABEL},
+      {"siteSettingsDeleteAllStorageConfirmation",
+       IDS_SETTINGS_SITE_SETTINGS_DELETE_ALL_STORAGE_CONFIRMATION},
+      {"siteSettingsDeleteDisplayedStorageConfirmation",
+       IDS_SETTINGS_SITE_SETTINGS_DELETE_DISPLAYED_STORAGE_CONFIRMATION},
+      {"siteSettingsDeleteAllStorageConfirmationInstalled",
+       IDS_SETTINGS_SITE_SETTINGS_DELETE_ALL_STORAGE_CONFIRMATION_INSTALLED},
+      {"siteSettingsDeleteDisplayedStorageConfirmationInstalled",
+       IDS_SETTINGS_SITE_SETTINGS_DELETE_DISPLAYED_STORAGE_CONFIRMATION_INSTALLED},
+      {"siteSettingsClearAllStorageSignOut",
+       IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_SIGN_OUT},
+      {"siteSettingsClearDisplayedStorageSignOut",
+       IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_SIGN_OUT},
+      {"siteSettingsSiteDetailsSubpageAccessibilityLabel",
+       IDS_SETTINGS_SITE_SETTINGS_SITE_DETAILS_SUBPAGE_ACCESSIBILITY_LABEL},
+      {"firstPartySetsMoreActionsTitle",
+       IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_MORE_ACTIONS_TITLE},
+      {"firstPartySetsShowRelatedSitesButton",
+       IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_SHOW_RELATED_SITES_BUTTON},
+      {"firstPartySetsSiteDeleteStorageButton",
+       IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_SITE_DELETE_STORAGE_BUTTON},
+      {"siteSettingsSiteClearStorage",
+       IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_STORAGE},
+      {"siteSettingsSiteClearStorageConfirmation",
+       IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_STORAGE_CONFIRMATION},
+      {"siteSettingsSiteClearStorageConfirmationNew",
+       IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_STORAGE_CONFIRMATION_NEW},
+      {"siteSettingsSiteDeleteStorageDialogTitle",
+       IDS_SETTINGS_SITE_SETTINGS_SITE_DELETE_STORAGE_DIALOG_TITLE},
+      {"siteSettingsSiteClearStorageSignOut",
+       IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_STORAGE_SIGN_OUT},
+      {"siteSettingsSiteDeleteStorageOfflineData",
+       IDS_SETTINGS_SITE_SETTINGS_SITE_DELETE_STORAGE_OFFLINE_DATA},
+      {"siteSettingsRemoveSiteAdPersonalization",
+       IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_AD_PERSONALIZATION},
+      {"siteSettingsSiteGroupDeleteOfflineData",
+       IDS_SETTINGS_SITE_SETTINGS_SITE_GROUP_DELETE_OFFLINE_DATA},
+      {"siteSettingsSiteResetAll", IDS_SETTINGS_SITE_SETTINGS_SITE_RESET_ALL},
+      {"siteSettingsSiteResetConfirmation",
+       IDS_SETTINGS_SITE_SETTINGS_SITE_RESET_CONFIRMATION},
+      {"siteSettingsRemoveSiteOriginDialogTitle",
+       IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_ORIGIN_DIALOG_TITLE},
+      {"siteSettingsRemoveSiteOriginAppDialogTitle",
+       IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_ORIGIN_APP_DIALOG_TITLE},
+      {"siteSettingsRemoveSiteOriginPartitionedDialogTitle",
+       IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_ORIGIN_PARTITIONED_DIALOG_TITLE},
+      {"siteSettingsRemoveSiteGroupDialogTitle",
+       IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_GROUP_DIALOG_TITLE},
+      {"siteSettingsRemoveSiteGroupAppDialogTitle",
+       IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_GROUP_APP_DIALOG_TITLE},
+      {"siteSettingsRemoveSiteGroupAppPluralDialogTitle",
+       IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_GROUP_APP_PLURAL_DIALOG_TITLE},
+      {"siteSettingsRemoveSiteOriginLogout",
+       IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_ORIGIN_LOGOUT},
+      {"siteSettingsRemoveSiteGroupLogout",
+       IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_GROUP_LOGOUT},
+      {"siteSettingsRemoveSiteOfflineData",
+       IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_OFFLINE_DATA},
+      {"siteSettingsRemoveSitePermissions",
+       IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_PERMISSIONS},
+      {"siteSettingsRemoveSiteConfirm",
+       IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_CONFIRM},
+      {"thirdPartyCookie", IDS_NEW_TAB_OTR_THIRD_PARTY_COOKIE},
+      {"thirdPartyCookieSublabel", IDS_NEW_TAB_OTR_THIRD_PARTY_COOKIE_SUBLABEL},
+      {"handlerIsDefault", IDS_SETTINGS_SITE_SETTINGS_HANDLER_IS_DEFAULT},
+      {"handlerSetDefault", IDS_SETTINGS_SITE_SETTINGS_HANDLER_SET_DEFAULT},
+      {"handlerRemove", IDS_SETTINGS_SITE_SETTINGS_REMOVE},
+      {"incognitoSiteOnly", IDS_SETTINGS_SITE_SETTINGS_INCOGNITO_ONLY},
+      {"incognitoSiteExceptionDesc",
+       IDS_SETTINGS_SITE_SETTINGS_INCOGNITO_SITE_EXCEPTION_DESC},
+      {"noSitesAdded", IDS_SETTINGS_SITE_NO_SITES_ADDED},
+      {"siteSettingsDefaultBehavior",
+       IDS_SETTINGS_SITE_SETTINGS_DEFAULT_BEHAVIOR},
+      {"siteSettingsDefaultBehaviorDescription",
+       IDS_SETTINGS_SITE_SETTINGS_DEFAULT_BEHAVIOR_DESCRIPTION},
+      {"siteSettingsNotificationsDefaultBehaviorDescription",
+       IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_DEFAULT_BEHAVIOR_DESC},
+      {"siteSettingsCustomizedBehaviors",
+       IDS_SETTINGS_SITE_SETTINGS_CUSTOMIZED_BEHAVIORS},
+      {"siteSettingsCustomizedBehaviorsDescription",
+       IDS_SETTINGS_SITE_SETTINGS_CUSTOMIZED_BEHAVIORS_DESCRIPTION},
+      {"siteSettingsCustomizedBehaviorsDescriptionShort",
+       IDS_SETTINGS_SITE_SETTINGS_CUSTOMIZED_BEHAVIORS_DESCRIPTION_SHORT},
+      {"siteSettingsAdsDescription",
+       IDS_SETTINGS_SITE_SETTINGS_ADS_DESCRIPTION},
+      {"siteSettingsAdsAllowed", IDS_SETTINGS_SITE_SETTINGS_ADS_ALLOWED},
+      {"siteSettingsAdsBlocked", IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCKED},
+      {"siteSettingsAdsAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_ADS_ALLOWED_EXCEPTIONS},
+      {"siteSettingsAdsBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCKED_EXCEPTIONS},
+      {"siteSettingsArDescription", IDS_SETTINGS_SITE_SETTINGS_AR_DESCRIPTION},
+      {"siteSettingsArAllowed", IDS_SETTINGS_SITE_SETTINGS_AR_ALLOWED},
+      {"siteSettingsArBlocked", IDS_SETTINGS_SITE_SETTINGS_AR_BLOCKED},
+      {"siteSettingsArAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_AR_ALLOWED_EXCEPTIONS},
+      {"siteSettingsArBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_AR_BLOCKED_EXCEPTIONS},
+      {"siteSettingsAutomaticDownloadsDescription",
+       IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOADS_DESCRIPTION},
+      {"siteSettingsAutomaticDownloadsAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOADS_ALLOWED},
+      {"siteSettingsAutomaticDownloadsBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOADS_BLOCKED},
+      {"siteSettingsAutomaticDownloadsAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOADS_ALLOWED_EXCEPTIONS},
+      {"siteSettingsAutomaticDownloadsBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOADS_BLOCKED_EXCEPTIONS},
+      {"siteSettingsAutoPictureInPictureDescription",
+       IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_DESCRIPTION},
+      {"siteSettingsAutoPictureInPictureAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_ALLOWED},
+      {"siteSettingsAutoPictureInPictureBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_BLOCKED},
+      {"siteSettingsAutoPictureInPictureAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_ALLOWED_EXCEPTIONS},
+      {"siteSettingsAutoPictureInPictureBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_BLOCKED_EXCEPTIONS},
+      {"siteSettingsBackgroundSyncDescription",
+       IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_DESCRIPTION},
+      {"siteSettingsBackgroundSyncAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_ALLOWED},
+      {"siteSettingsBackgroundSyncBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_BLOCKED},
+      {"siteSettingsBackgroundSyncBlockedSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_BLOCKED_SUB_LABEL},
+      {"siteSettingsBackgroundSyncAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_ALLOWED_EXCEPTIONS},
+      {"siteSettingsBackgroundSyncBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_BLOCKED_EXCEPTIONS},
+      {"siteSettingsBluetoothDevicesDescription",
+       IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_DEVICES_DESCRIPTION},
+      {"siteSettingsBluetoothDevicesAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_DEVICES_ALLOWED},
+      {"siteSettingsBluetoothDevicesBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_DEVICES_BLOCKED},
+      {"siteSettingsCameraDescription",
+       IDS_SETTINGS_SITE_SETTINGS_CAMERA_DESCRIPTION},
+      {"siteSettingsCameraAllowed", IDS_SETTINGS_SITE_SETTINGS_CAMERA_ALLOWED},
+      {"siteSettingsCameraBlocked", IDS_SETTINGS_SITE_SETTINGS_CAMERA_BLOCKED},
+      {"siteSettingsCameraBlockedSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_CAMERA_BLOCKED_SUB_LABEL},
+      {"siteSettingsCameraAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_CAMERA_ALLOWED_EXCEPTIONS},
+      {"siteSettingsCameraBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_CAMERA_BLOCKED_EXCEPTIONS},
+      {"siteSettingsClipboardDescription",
+       IDS_SETTINGS_SITE_SETTINGS_CLIPBOARD_DESCRIPTION},
+      {"siteSettingsClipboardAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_CLIPBOARD_ALLOWED},
+      {"siteSettingsClipboardBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_CLIPBOARD_BLOCKED},
+      {"siteSettingsClipboardAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_CLIPBOARD_ALLOWED_EXCEPTIONS},
+      {"siteSettingsClipboardBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_CLIPBOARD_BLOCKED_EXCEPTIONS},
+      {"siteSettingsDeviceUseDescription",
+       IDS_SETTINGS_SITE_SETTINGS_DEVICE_USE_DESCRIPTION},
+      {"siteSettingsDeviceUseAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_DEVICE_USE_ALLOWED},
+      {"siteSettingsDeviceUseBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_DEVICE_USE_BLOCKED},
+      {"siteSettingsDeviceUseAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_DEVICE_USE_ALLOWED_EXCEPTIONS},
+      {"siteSettingsDeviceUseBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_DEVICE_USE_BLOCKED_EXCEPTIONS},
+      {"siteSettingsFederatedIdentityApi",
+       IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API},
+      {"siteSettingsFederatedIdentityApiAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_FEDERATED_IDENTITY_API_ALLOWED},
+      {"siteSettingsFederatedIdentityApiBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_FEDERATED_IDENTITY_API_BLOCKED},
+      {"siteSettingsFederatedIdentityApiAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_FEDERATED_IDENTITY_API_ALLOWED_EXCEPTIONS},
+      {"siteSettingsFederatedIdentityApiBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_FEDERATED_IDENTITY_API_BLOCKED_EXCEPTIONS},
+      {"siteSettingsFederatedIdentityApiDescription",
+       IDS_SETTINGS_SITE_SETTINGS_FEDERATED_IDENTITY_API_DESCRIPTION},
+      {"siteSettingsFederatedIdentityApiMidSentence",
+       IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API_MID_SENTENCE},
+      {"siteSettingsFileSystemWriteDescription",
+       IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_WRITE_DESCRIPTION},
+      {"siteSettingsFileSystemWriteAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_WRITE_ALLOWED},
+      {"siteSettingsFileSystemWriteBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_WRITE_BLOCKED},
+      {"siteSettingsFileSystemWriteBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_FILE_SYSTEM_WRITE_BLOCKED_EXCEPTIONS},
+      {"siteSettingsFontsDescription",
+       IDS_SETTINGS_SITE_SETTINGS_FONTS_DESCRIPTION},
+      {"siteSettingsFontsAllowed", IDS_SETTINGS_SITE_SETTINGS_FONTS_ALLOWED},
+      {"siteSettingsFontsBlocked", IDS_SETTINGS_SITE_SETTINGS_FONTS_BLOCKED},
+      {"siteSettingsFontsAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_FONTS_ALLOWED_EXCEPTIONS},
+      {"siteSettingsFontsBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_FONTS_BLOCKED_EXCEPTIONS},
+      {"siteSettingsHidDevicesDescription",
+       IDS_SETTINGS_SITE_SETTINGS_HID_DEVICES_DESCRIPTION},
+      {"siteSettingsHidDevicesAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_HID_DEVICES_ALLOWED},
+      {"siteSettingsHidDevicesBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_HID_DEVICES_BLOCKED},
+      {"siteSettingsImagesDescription",
+       IDS_SETTINGS_SITE_SETTINGS_IMAGES_DESCRIPTION},
+      {"siteSettingsImagesAllowed", IDS_SETTINGS_SITE_SETTINGS_IMAGES_ALLOWED},
+      {"siteSettingsImagesBlocked", IDS_SETTINGS_SITE_SETTINGS_IMAGES_BLOCKED},
+      {"siteSettingsImagesBlockedSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_IMAGES_BLOCKED_SUB_LABEL},
+      {"siteSettingsImagesAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_IMAGES_ALLOWED_EXCEPTIONS},
+      {"siteSettingsImagedBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_IMAGES_BLOCKED_EXCEPTIONS},
+      {"siteSettingsInsecureContentDescription",
+       IDS_SETTINGS_SITE_SETTINGS_INSECURE_CONTENT_DESCRIPTION},
+      {"siteSettingsInsecureContentAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_INSECURE_CONTENT_ALLOWED_EXCEPTIONS},
+      {"siteSettingsInsecureContentBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_INSECURE_CONTENT_BLOCKED_EXCEPTIONS},
+      {"siteSettingsJavascriptDescription",
+       IDS_SETTINGS_SITE_SETTINGS_JAVASCRIPT_DESCRIPTION},
+      {"siteSettingsJavascriptAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_JAVASCRIPT_ALLOWED},
+      {"siteSettingsJavascriptBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_JAVASCRIPT_BLOCKED},
+      {"siteSettingsJavascriptAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_JAVASCRIPT_ALLOWED_EXCEPTIONS},
+      {"siteSettingsJavascriptBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_JAVASCRIPT_BLOCKED_EXCEPTIONS},
+      {"siteSettingsLocationDescription",
+       IDS_SETTINGS_SITE_SETTINGS_LOCATION_DESCRIPTION},
+      {"siteSettingsLocationAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_LOCATION_ALLOWED},
+      {"siteSettingsLocationAskQuiet",
+       IDS_SETTINGS_SITE_SETTINGS_PERMISSION_QUIET},
+      {"siteSettingsLocationAskCPSS",
+       IDS_SETTINGS_SITE_SETTINGS_PERMISSION_CPSS},
+      {"siteSettingsLocationAskLoud",
+       IDS_SETTINGS_SITE_SETTINGS_PERMISSION_LOUD},
+      {"siteSettingsLocationBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_LOCATION_BLOCKED},
+      {"siteSettingsLocationBlockedSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_LOCATION_BLOCKED_SUB_LABEL},
+      {"siteSettingsLocationAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_LOCATION_ALLOWED_EXCEPTIONS},
+      {"siteSettingsLocationBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_LOCATION_BLOCKED_EXCEPTIONS},
+      {"siteSettingsMicDescription",
+       IDS_SETTINGS_SITE_SETTINGS_MIC_DESCRIPTION},
+      {"siteSettingsMicAllowed", IDS_SETTINGS_SITE_SETTINGS_MIC_ALLOWED},
+      {"siteSettingsMicBlocked", IDS_SETTINGS_SITE_SETTINGS_MIC_BLOCKED},
+      {"siteSettingsMicBlockedSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_MIC_BLOCKED_SUB_LABEL},
+      {"siteSettingsMicAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_MIC_ALLOWED_EXCEPTIONS},
+      {"siteSettingsMicBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_MIC_BLOCKED_EXCEPTIONS},
+      {"siteSettingsMidiDescription",
+       IDS_SETTINGS_SITE_SETTINGS_MIDI_DESCRIPTION},
+      {"siteSettingsMidiAllowed", IDS_SETTINGS_SITE_SETTINGS_MIDI_ALLOWED},
+      {"siteSettingsMidiBlocked", IDS_SETTINGS_SITE_SETTINGS_MIDI_BLOCKED},
+      {"siteSettingsMidiAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_MIDI_ALLOWED_EXCEPTIONS},
+      {"siteSettingsMidiBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_MIDI_BLOCKED_EXCEPTIONS},
+      {"siteSettingsMotionSensorsDescription",
+       IDS_SETTINGS_SITE_SETTINGS_MOTION_SENSORS_DESCRIPTION},
+      {"siteSettingsMotionSensorsAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_MOTION_SENSORS_ALLOWED},
+      {"siteSettingsMotionSensorsBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_MOTION_SENSORS_BLOCKED},
+      {"siteSettingsMotionSensorsBlockedSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_MOTION_SENSORS_BLOCKED_SUB_LABEL},
+      {"siteSettingsMotionSensorsAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_MOTION_SENSORS_ALLOWED_EXCEPTIONS},
+      {"siteSettingsMotionSensorsBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_MOTION_SENSORS_BLOCKED_EXCEPTIONS},
+      {"siteSettingsNotificationsDescription",
+       IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_DESCRIPTION},
+      {"siteSettingsNotificationsAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_ALLOWED},
+      {"siteSettingsNotificationsPartial",
+       IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_PARTIAL},
+      {"siteSettingsNotificationsPartialSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_PARTIAL_SUB_LABEL},
+      {"siteSettingsNotificationsAskState",
+       IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_ASK_STATE},
+      {"siteSettingsNotificationsAskQuiet",
+       IDS_SETTINGS_SITE_SETTINGS_PERMISSION_QUIET},
+      {"siteSettingsNotificationsAskCPSS",
+       IDS_SETTINGS_SITE_SETTINGS_PERMISSION_CPSS},
+      {"siteSettingsNotificationsAskLoud",
+       IDS_SETTINGS_SITE_SETTINGS_PERMISSION_LOUD},
+      {"siteSettingsNotificationsBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_BLOCKED},
+      {"siteSettingsNotificationsBlockedSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_BLOCKED_SUB_LABEL},
+      {"siteSettingsNotificationsAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_ALLOWED_EXCEPTIONS},
+      {"siteSettingsNotificationsBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS_BLOCKED_EXCEPTIONS},
+      {"siteSettingsPaymentHandlersDescription",
+       IDS_SETTINGS_SITE_SETTINGS_PAYMENT_HANDLERS_DESCRIPTION},
+      {"siteSettingsPaymentHandlersAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_PAYMENT_HANDLERS_ALLOWED},
+      {"siteSettingsPaymentHandlersBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_PAYMENT_HANDLERS_BLOCKED},
+      {"siteSettingsPaymentHandlersAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_PAYMENT_HANDLERS_ALLOWED_EXCEPTIONS},
+      {"siteSettingsPaymentHandlersBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_PAYMENT_HANDLERS_BLOCKED_EXCEPTIONS},
+      {"siteSettingsPdfsDescription",
+       IDS_SETTINGS_SITE_SETTINGS_PDFS_DESCRIPTION},
+      {"siteSettingsPdfsAllowed", IDS_SETTINGS_SITE_SETTINGS_PDFS_ALLOWED},
+      {"siteSettingsPdfsBlocked", IDS_SETTINGS_SITE_SETTINGS_PDFS_BLOCKED},
+      {"siteSettingsPopupsDescription",
+       IDS_SETTINGS_SITE_SETTINGS_POPUPS_DESCRIPTION},
+      {"siteSettingsPopupsAllowed", IDS_SETTINGS_SITE_SETTINGS_POPUPS_ALLOWED},
+      {"siteSettingsPopupsBlocked", IDS_SETTINGS_SITE_SETTINGS_POPUPS_BLOCKED},
+      {"siteSettingsPopupsAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_POPUPS_ALLOWED_EXCEPTIONS},
+      {"siteSettingsPopupsBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_POPUPS_BLOCKED_EXCEPTIONS},
+      {"siteSettingsProtocolHandlersDescription",
+       IDS_SETTINGS_SITE_SETTINGS_PROTOCOL_HANDLERS_DESCRIPTION},
+      {"siteSettingsProtocolHandlersAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_PROTOCOL_HANDLERS_ALLOWED},
+      {"siteSettingsProtocolHandlersBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_PROTOCOL_HANDLERS_BLOCKED},
+      {"siteSettingsProtocolHandlersBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_PROTOCOL_HANDLERS_BLOCKED_EXCEPTIONS},
+      {"siteSettingsSerialPortsDescription",
+       IDS_SETTINGS_SITE_SETTINGS_SERIAL_PORTS_DESCRIPTION},
+      {"siteSettingsSerialPortsAllowed",
+       IDS_SETTINGS_SITE_SETTINGS_SERIAL_PORTS_ALLOWED},
+      {"siteSettingsSerialPortsBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_SERIAL_PORTS_BLOCKED},
+      {"siteSettingsSoundDescription",
+       IDS_SETTINGS_SITE_SETTINGS_SOUND_DESCRIPTION},
+      {"siteSettingsSoundAllowed", IDS_SETTINGS_SITE_SETTINGS_SOUND_ALLOWED},
+      {"siteSettingsSoundBlocked", IDS_SETTINGS_SITE_SETTINGS_SOUND_BLOCKED},
+      {"siteSettingsSoundBlockedSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_SOUND_BLOCKED_SUB_LABEL},
+      {"siteSettingsSoundAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_SOUND_ALLOWED_EXCEPTIONS},
+      {"siteSettingsSoundBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_SOUND_BLOCKED_EXCEPTIONS},
+      {"siteSettingsUsbDescription",
+       IDS_SETTINGS_SITE_SETTINGS_USB_DESCRIPTION},
+      {"siteSettingsUsbAllowed", IDS_SETTINGS_SITE_SETTINGS_USB_ALLOWED},
+      {"siteSettingsUsbBlocked", IDS_SETTINGS_SITE_SETTINGS_USB_BLOCKED},
+      {"siteSettingsVrDescription", IDS_SETTINGS_SITE_SETTINGS_VR_DESCRIPTION},
+      {"siteSettingsVrAllowed", IDS_SETTINGS_SITE_SETTINGS_VR_ALLOWED},
+      {"siteSettingsVrBlocked", IDS_SETTINGS_SITE_SETTINGS_VR_BLOCKED},
+      {"siteSettingsVrAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_VR_ALLOWED_EXCEPTIONS},
+      {"siteSettingsVrBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_VR_BLOCKED_EXCEPTIONS},
+      {"siteSettingsZoomLevelsDescription",
+       IDS_SETTINGS_SITE_SETTINGS_ZOOM_LEVELS_DESCRIPTION},
+      {"siteSettingsAds", IDS_SITE_SETTINGS_TYPE_ADS},
+      {"siteSettingsAdsMidSentence", IDS_SITE_SETTINGS_TYPE_ADS_MID_SENTENCE},
+      {"siteSettingsPaymentHandler", IDS_SITE_SETTINGS_TYPE_PAYMENT_HANDLER},
+      {"siteSettingsPaymentHandlerMidSentence",
+       IDS_SITE_SETTINGS_TYPE_PAYMENT_HANDLER_MID_SENTENCE},
+      {"siteSettingsBlockAutoplaySetting",
+       IDS_SETTINGS_SITE_SETTINGS_BLOCK_AUTOPLAY},
+      {"emptyAllSitesPage", IDS_SETTINGS_SITE_SETTINGS_EMPTY_ALL_SITES_PAGE},
+      {"noSitesFound", IDS_SETTINGS_SITE_SETTINGS_NO_SITES_FOUND},
+      {"siteSettingsBluetoothScanning",
+       IDS_SITE_SETTINGS_TYPE_BLUETOOTH_SCANNING},
+      {"siteSettingsBluetoothScanningMidSentence",
+       IDS_SITE_SETTINGS_TYPE_BLUETOOTH_SCANNING_MID_SENTENCE},
+      {"siteSettingsBluetoothScanningDescription",
+       IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_SCANNING_DESCRIPTION},
+      {"siteSettingsBluetoothScanningAsk",
+       IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_SCANNING_ASK},
+      {"siteSettingsBluetoothScanningBlock",
+       IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_SCANNING_BLOCK},
+      {"siteSettingsBluetoothScanningAllowedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_SCANNING_ALLOWED_EXCEPTIONS},
+      {"siteSettingsBluetoothScanningBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_BLUETOOTH_SCANNING_BLOCKED_EXCEPTIONS},
+      {"siteSettingsAr", IDS_SITE_SETTINGS_TYPE_AR},
+      {"siteSettingsArMidSentence", IDS_SITE_SETTINGS_TYPE_AR_MID_SENTENCE},
+      {"siteSettingsArAsk", IDS_SETTINGS_SITE_SETTINGS_AR_ASK},
+      {"siteSettingsArBlock", IDS_SETTINGS_SITE_SETTINGS_AR_BLOCK},
+      {"siteSettingsVr", IDS_SITE_SETTINGS_TYPE_VR},
+      {"siteSettingsVrMidSentence", IDS_SITE_SETTINGS_TYPE_VR_MID_SENTENCE},
+      {"siteSettingsWindowManagement",
+       IDS_SITE_SETTINGS_TYPE_WINDOW_MANAGEMENT},
+      {"siteSettingsWindowManagementMidSentence",
+       IDS_SITE_SETTINGS_TYPE_WINDOW_MANAGEMENT_MID_SENTENCE},
+      {"siteSettingsWindowManagementDescription",
+       IDS_SETTINGS_SITE_SETTINGS_WINDOW_MANAGEMENT_DESCRIPTION},
+      {"siteSettingsWindowManagementAsk",
+       IDS_SETTINGS_SITE_SETTINGS_WINDOW_MANAGEMENT_ASK},
+      {"siteSettingsWindowManagementBlocked",
+       IDS_SETTINGS_SITE_SETTINGS_WINDOW_MANAGEMENT_BLOCKED},
+      {"siteSettingsWindowManagementAskExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_WINDOW_MANAGEMENT_ASK_EXCEPTIONS},
+      {"siteSettingsWindowManagementBlockedExceptions",
+       IDS_SETTINGS_SITE_SETTINGS_WINDOW_MANAGEMENT_BLOCKED_EXCEPTIONS},
+      {"siteSettingsFontAccessMidSentence",
+       IDS_SITE_SETTINGS_TYPE_FONT_ACCESS_MID_SENTENCE},
+      {"siteSettingsIdleDetection", IDS_SITE_SETTINGS_TYPE_IDLE_DETECTION},
+      {"siteSettingsIdleDetectionMidSentence",
+       IDS_SITE_SETTINGS_TYPE_IDLE_DETECTION_MID_SENTENCE},
+      {"siteSettingsExtensionIdDescription",
+       IDS_SETTINGS_SITE_SETTINGS_EXTENSION_ID_DESCRIPTION},
+      {"siteSettingsSiteDataAllowedSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_PAGE_SITE_DATA_ALLOWED_SUB_LABEL},
+      {"siteSettingsSiteDataBlockedSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_PAGE_SITE_DATA_BLOCKED_SUB_LABEL},
+      {"siteSettingsSiteDataDeleteOnExitSubLabel",
+       IDS_SETTINGS_SITE_SETTINGS_PAGE_SITE_DATA_DELETE_ON_EXIT_SUB_LABEL},
+      {"siteSettingsAntiAbuse", IDS_SITE_SETTINGS_TYPE_ANTI_ABUSE},
+      {"siteSettingsAntiAbuseDescription", IDS_SETTINGS_ANTI_ABUSE_DESCRIPTION},
+      {"siteSettingsAntiAbuseEnabledSubLabel",
+       IDS_SETTINGS_ANTI_ABUSE_ENABLED_SUB_LABEL},
+      {"siteSettingsAntiAbuseDisabledSubLabel",
+       IDS_SETTINGS_ANTI_ABUSE_DISABLED_SUB_LABEL},
+      {"antiAbuseWhenOnHeader", IDS_SETTINGS_ANTI_ABUSE_WHEN_ON_HEADER},
+      {"antiAbuseWhenOnSectionOne",
+       IDS_SETTINGS_ANTI_ABUSE_WHEN_ON_SECTION_ONE},
+      {"antiAbuseWhenOnSectionTwo",
+       IDS_SETTINGS_ANTI_ABUSE_WHEN_ON_SECTION_TWO},
+      {"antiAbuseWhenOnSectionThree",
+       IDS_SETTINGS_ANTI_ABUSE_WHEN_ON_SECTION_THREE},
+      {"antiAbuseThingsToConsiderHeader",
+       IDS_SETTINGS_ANTI_ABUSE_THINGS_TO_CONSIDER_HEADER},
+      {"antiAbuseThingsToConsiderSectionOne",
+       IDS_SETTINGS_ANTI_ABUSE_THINGS_TO_CONSIDER_SECTION_ONE},
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_handler.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_handler.cc
index 2ab7409..199c51f 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_handler.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_handler.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/background/wallpaper_search/wallpaper_search_background_manager.h"
+#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
@@ -364,7 +365,18 @@
     return;
   }
 #endif  // BUILDFLAG(IS_CHROMEOS)
-
+  Browser* browser = chrome::FindLastActive();
+  if (!browser) {
+    return;
+  }
+  OptimizationGuideKeyedService* opt_guide_keyed_service =
+      OptimizationGuideKeyedServiceFactory::GetForProfile(browser->profile());
+  if (!opt_guide_keyed_service ||
+      !opt_guide_keyed_service->ShouldFeatureBeCurrentlyAllowedForLogging(
+          optimization_guide::proto::
+              MODEL_EXECUTION_FEATURE_WALLPAPER_SEARCH)) {
+    return;
+  }
   base::Value::Dict feedback_metadata;
   if (!log_entries_.empty()) {
     feedback_metadata.Set("log_id", log_entries_.back()
@@ -372,7 +384,6 @@
                                         ->mutable_model_execution_info()
                                         ->server_execution_id());
   }
-  Browser* browser = chrome::FindLastActive();
   chrome::ShowFeedbackPage(
       browser, chrome::kFeedbackSourceAI,
       /*description_template=*/std::string(),
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_interactive_uitest.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_interactive_uitest.cc
index 38d4f37..4bf620c 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_interactive_uitest.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_interactive_uitest.cc
@@ -285,6 +285,12 @@
 #if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(WallpaperSearchInteractiveTest,
                        FeedbackDialogShowsOnThumbsDown) {
+  EXPECT_CALL(mock_optimization_guide_keyed_service(),
+              ShouldFeatureBeCurrentlyAllowedForLogging(
+                  optimization_guide::proto::ModelExecutionFeature::
+                      MODEL_EXECUTION_FEATURE_WALLPAPER_SEARCH))
+      .WillOnce(testing::Return(true));
+
   // Intercept Wallpaper Search descriptor fetches, and respond with data.
   std::unique_ptr<content::URLLoaderInterceptor> descriptors_fetch_interceptor =
       SetUpDescriptorsResponseWithData();
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index c3e7430c..fc7ac73f 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -9,7 +9,6 @@
 import("//chrome/browser/buildflags.gni")
 import("//chrome/common/features.gni")
 import("//components/compose/features.gni")
-import("//components/signin/features.gni")
 import("//extensions/buildflags/buildflags.gni")
 import("//headless/headless.gni")
 import("//pdf/features.gni")
@@ -172,6 +171,7 @@
         "$root_gen_dir/chrome/password_manager_resources.pak",
         "$root_gen_dir/chrome/privacy_sandbox_resources.pak",
         "$root_gen_dir/chrome/profile_internals_resources.pak",
+        "$root_gen_dir/chrome/search_engine_choice_resources.pak",
         "$root_gen_dir/chrome/settings_resources.pak",
         "$root_gen_dir/chrome/side_panel_bookmarks_resources.pak",
         "$root_gen_dir/chrome/side_panel_commerce_resources.pak",
@@ -197,6 +197,7 @@
       deps += [
         "//chrome/browser/resources:component_extension_resources",
         "//chrome/browser/resources:dev_ui_paks",
+        "//chrome/browser/resources/search_engine_choice:resources",
         "//content/browser/devtools:devtools_resources",
         "//content/browser/tracing:resources",
       ]
@@ -397,11 +398,6 @@
       ]
     }
 
-    if (enable_search_engine_choice) {
-      sources += [ "$root_gen_dir/chrome/search_engine_choice_resources.pak" ]
-      deps += [ "//chrome/browser/resources/search_engine_choice:resources" ]
-    }
-
     if (is_win || is_mac || is_linux || is_fuchsia) {
       sources += [ "$root_gen_dir/chrome/app_home_resources.pak" ]
       deps += [ "//chrome/browser/resources/app_home:resources" ]
diff --git a/chrome/common/features.gni b/chrome/common/features.gni
index cbe6975..9effd0b4 100644
--- a/chrome/common/features.gni
+++ b/chrome/common/features.gni
@@ -104,7 +104,6 @@
   "enable_dice_support=$enable_dice_support",
   "enable_pdf=$enable_pdf",
   "enable_print_preview=$enable_print_preview",
-  "enable_search_engine_choice=$enable_search_engine_choice",
   "enable_screen_ai_service=$enable_screen_ai_service",
   "enable_supervised_users=$enable_supervised_users",
   "enable_vr=$enable_vr",
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index fe9ae1c..4f4d24f 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -15,7 +15,6 @@
 #include "components/optimization_guide/optimization_guide_internals/webui/url_constants.h"
 #include "components/password_manager/content/common/web_ui_constants.h"
 #include "components/safe_browsing/core/common/web_ui_constants.h"
-#include "components/signin/public/base/signin_buildflags.h"
 #include "device/vr/buildflags/buildflags.h"
 #include "extensions/buildflags/buildflags.h"
 #include "third_party/blink/public/common/chrome_debug_urls.h"
@@ -158,6 +157,8 @@
 const char kChromeUIOmniboxURL[] = "chrome://omnibox/";
 #if !BUILDFLAG(IS_ANDROID)
 const char kChromeUIOnDeviceInternalsHost[] = "on-device-internals";
+const char kChromeUISearchEngineChoiceHost[] = "search-engine-choice";
+const char kChromeUISearchEngineChoiceURL[] = "chrome://search-engine-choice";
 #endif
 const char kChromeUIPasswordManagerInternalsHost[] =
     "password-manager-internals";
@@ -655,11 +656,6 @@
 const char kChromeUICastFeedbackHost[] = "cast-feedback";
 #endif
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-const char kChromeUISearchEngineChoiceHost[] = "search-engine-choice";
-const char kChromeUISearchEngineChoiceURL[] = "chrome://search-engine-choice";
-#endif
-
 #if BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES)
 const char kChromeUILensURL[] = "chrome://lens/";
 const char kChromeUILensUntrustedURL[] = "chrome-untrusted://lens/";
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 386050e..49b858b 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -16,7 +16,6 @@
 #include "build/chromeos_buildflags.h"
 #include "build/config/chromebox_for_meetings/buildflags.h"
 #include "chrome/common/buildflags.h"
-#include "components/signin/public/base/signin_buildflags.h"
 #include "components/supervised_user/core/common/buildflags.h"
 #include "content/public/common/url_constants.h"
 #include "media/media_buildflags.h"
@@ -152,6 +151,8 @@
 extern const char kChromeUIOmniboxPopupURL[];
 #if !BUILDFLAG(IS_ANDROID)
 extern const char kChromeUIOnDeviceInternalsHost[];
+extern const char kChromeUISearchEngineChoiceURL[];
+extern const char kChromeUISearchEngineChoiceHost[];
 #endif
 extern const char kChromeUISuggestInternalsHost[];
 extern const char kChromeUISuggestInternalsURL[];
@@ -460,11 +461,6 @@
 extern const char kChromeUISandboxHost[];
 #endif
 
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
-extern const char kChromeUISearchEngineChoiceURL[];
-extern const char kChromeUISearchEngineChoiceHost[];
-#endif
-
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_FUCHSIA) || \
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index dce4acd5..cc3ee3e 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -35,7 +35,6 @@
 import("//components/os_crypt/sync/features.gni")
 import("//components/safe_browsing/buildflags.gni")
 import("//components/services/screen_ai/buildflags/features.gni")
-import("//components/signin/features.gni")
 import("//components/soda/buildflags.gni")
 import("//components/spellcheck/spellcheck_build_features.gni")
 import("//crypto/features.gni")
@@ -2480,7 +2479,7 @@
       "../browser/performance_manager/background_tab_loading_policy_browsertest.cc",
       "../browser/performance_manager/frame_node_impl_browsertest.cc",
       "../browser/performance_manager/mechanisms/page_discarder_browsertest.cc",
-      "../browser/performance_manager/metrics/page_timeline_monitor_browsertest.cc",
+      "../browser/performance_manager/metrics/page_resource_monitor_browsertest.cc",
       "../browser/performance_manager/observers/page_load_metrics_observer_browsertest.cc",
       "../browser/performance_manager/page_load_tracker_decorator_browsertest.cc",
       "../browser/performance_manager/page_node_browsertest.cc",
@@ -2882,6 +2881,12 @@
       "v8/wasm_trap_handler_browsertest.cc",
     ]
 
+    if (!is_chrome_for_testing) {
+      sources += [
+        "../browser/search_engine_choice/search_engine_choice_browsertest.cc",
+      ]
+    }
+
     if (enable_compose) {
       sources += [
         "../browser/compose/compose_dialog_browsertest.cc",
@@ -2907,12 +2912,6 @@
       deps += [ "//components/supervised_user/test_support" ]
     }
 
-    if (enable_search_engine_choice) {
-      sources += [
-        "../browser/search_engine_choice/search_engine_choice_browsertest.cc",
-      ]
-    }
-
     if (is_win || is_mac || is_linux || is_chromeos_ash) {
       sources += [ "../browser/ui/views/device_signals_consent/consent_dialog_browsertest.cc" ]
     }
@@ -3477,7 +3476,7 @@
       ]
     }
 
-    if (enable_search_engine_choice && (is_win || is_linux)) {
+    if (is_win || is_linux) {
       sources += [ "../browser/ui/webui/search_engine_choice/search_engine_choice_ui_browsertest.cc" ]
     }
 
@@ -6291,8 +6290,8 @@
       "../browser/performance_manager/metrics/cpu_probe/cpu_probe_unittest.cc",
       "../browser/performance_manager/metrics/memory_pressure_metrics_unittest.cc",
       "../browser/performance_manager/metrics/metrics_provider_common_unittest.cc",
-      "../browser/performance_manager/metrics/page_timeline_cpu_monitor_unittest.cc",
-      "../browser/performance_manager/metrics/page_timeline_monitor_unittest.cc",
+      "../browser/performance_manager/metrics/page_resource_cpu_monitor_unittest.cc",
+      "../browser/performance_manager/metrics/page_resource_monitor_unittest.cc",
       "../browser/performance_manager/policies/page_freezing_policy_unittest.cc",
       "../browser/performance_manager/policies/working_set_trimmer_policy_unittest.cc",
       "../browser/performance_manager/test_support/page_aggregator.cc",
@@ -6524,6 +6523,8 @@
         "../browser/picture_in_picture/auto_pip_setting_view_unittest.cc",
         "../browser/picture_in_picture/picture_in_picture_occlusion_tracker_unittest.cc",
         "../browser/policy/messaging_layer/util/reporting_server_connector_unittest.cc",
+        "../browser/search_engine_choice/search_engine_choice_client_side_trial_unittest.cc",
+        "../browser/search_engine_choice/search_engine_choice_service_unittest.cc",
         "../browser/segmentation_platform/client_util/local_tab_handler_unittest.cc",
         "../browser/ssl/generated_https_first_mode_pref_unittest.cc",
         "../browser/ui/download/download_bubble_row_list_view_info_unittest.cc",
@@ -6538,13 +6539,6 @@
       ]
     }
 
-    if (enable_search_engine_choice) {
-      sources += [
-        "../browser/search_engine_choice/search_engine_choice_client_side_trial_unittest.cc",
-        "../browser/search_engine_choice/search_engine_choice_service_unittest.cc",
-      ]
-    }
-
     if (is_linux) {
       sources += [ "../browser/metrics/pressure/pressure_metrics_unittest.cc" ]
     }
@@ -8463,6 +8457,7 @@
         "../browser/performance_manager/mechanisms/working_set_trimmer_chromeos_unittest.cc",
         "../browser/performance_manager/policies/working_set_trimmer_policy_arcvm_unittest.cc",
         "../browser/performance_manager/policies/working_set_trimmer_policy_chromeos_unittest.cc",
+        "../browser/push_notification/push_notification_service_desktop_impl_unittest.cc",
         "../browser/resource_coordinator/tab_manager_delegate_chromeos_unittest.cc",
         "../browser/sharesheet/share_action/share_action_unittest.cc",
         "../browser/speech/cros_speech_recognition_service_factory_unittest.cc",
@@ -8613,6 +8608,7 @@
         "//chrome/browser/nearby_sharing/public/cpp",
         "//chrome/browser/policy:onc",
         "//chrome/browser/policy:unit_tests",
+        "//chrome/browser/push_notification:push_notification",
         "//chrome/browser/screen_ai:unit_tests",
         "//chrome/browser/ui/ash/holding_space:test_support",
         "//chrome/browser/ui/quick_answers",
diff --git a/chrome/test/base/chromeos/ash_browser_test_starter.cc b/chrome/test/base/chromeos/ash_browser_test_starter.cc
index f4b3065..04aaf5d 100644
--- a/chrome/test/base/chromeos/ash_browser_test_starter.cc
+++ b/chrome/test/base/chromeos/ash_browser_test_starter.cc
@@ -11,6 +11,7 @@
 #include "base/command_line.h"
 #include "base/environment.h"
 #include "base/files/file_util.h"
+#include "base/rand_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
 #include "base/test/test_future.h"
@@ -139,17 +140,32 @@
         command_line->GetSwitchValuePath(switches::kTestLauncherSummaryOutput);
     base::FilePath test_output_folder =
         output_file_path.DirName().Append(test_name);
-    // Handle retry logic. When a test fail and retry, we need to use a new
-    // folder for user data dir.
+    // Need to create a unique user-data-dir across all test runners.
+    // Using test name as folder name is usually enough. But there are edge
+    // cases it needs to handle:
+    //  * Test retry. On bots, usually we retry 1 time for test failures.
+    //  * Flakiness endorser. It will run new tests 20 times to ensure it's not
+    //    flaky.
+    //  * Developer modified builder config. Developer may modify infra to run
+    //    a test multiple times(e.g. 100) on bot to check flakiness.
+    // To handle those edge cases, we would create the user-data-dir using some
+    // random numbers to ensure it's unique.
     int retry_count = 1;
-    while (base::PathExists(test_output_folder) && retry_count < 5) {
-      test_output_folder = output_file_path.DirName().Append(
-          test_name + ".retry_" + base::NumberToString(retry_count));
+    bool success = false;
+    while (!success && retry_count < 100) {
+      if (base::PathExists(test_output_folder)) {
+        test_output_folder = output_file_path.DirName().Append(
+            (test_name + ".attempt_" +
+             base::NumberToString(base::RandInt(0, 10000))));
+      } else {
+        success = base::CreateDirectory(test_output_folder);
+      }
       ++retry_count;
     }
-    // Unlikely the path still exist. But in case it happens, we would let
+
+    // If we didn't create a unique directory, we would let
     // the browser test framework to create the tmp folder as usual.
-    if (!base::PathExists(test_output_folder)) {
+    if (success) {
       command_line->AppendSwitchPath(switches::kUserDataDir,
                                      test_output_folder);
       ash_user_data_dir_for_cleanup_ = test_output_folder;
diff --git a/chrome/test/base/chromeos/ash_browser_test_starter_unittest.cc b/chrome/test/base/chromeos/ash_browser_test_starter_unittest.cc
index c01d1efd7..b051db25 100644
--- a/chrome/test/base/chromeos/ash_browser_test_starter_unittest.cc
+++ b/chrome/test/base/chromeos/ash_browser_test_starter_unittest.cc
@@ -71,63 +71,8 @@
   base::FilePath user_data_dir =
       command_line->GetSwitchValuePath(switches::kUserDataDir);
   base::FilePath expected_user_data_dir(summary_path.Append(
-      "AshBrowserTestStarterTest.TestLacrosLogFolderWithOneRetry.retry_1"));
-  EXPECT_EQ(user_data_dir, expected_user_data_dir);
+      "AshBrowserTestStarterTest.TestLacrosLogFolderWithOneRetry.attempt_"));
+  EXPECT_TRUE(user_data_dir.AsUTF8Unsafe().starts_with(
+      expected_user_data_dir.AsUTF8Unsafe()));
 }
 
-TEST_F(AshBrowserTestStarterTest, TestLacrosLogFolderWithTwoRetry) {
-  base::FilePath summary_path;
-  base::ScopedTempDir scoped_summary_dir;
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  bool on_bot = GetSummaryOutputFolder(summary_path);
-  if (!on_bot) {
-    ASSERT_TRUE(scoped_summary_dir.CreateUniqueTempDir());
-    command_line->AppendSwitchPath(
-        switches::kTestLauncherSummaryOutput,
-        scoped_summary_dir.GetPath().Append("output.json"));
-    summary_path = scoped_summary_dir.GetPath();
-  }
-  command_line->AppendSwitchASCII(ash::switches::kLacrosChromePath, "/tmp/bbb");
-  ASSERT_TRUE(base::CreateDirectory(summary_path.Append(
-      "AshBrowserTestStarterTest.TestLacrosLogFolderWithTwoRetry")));
-  ASSERT_TRUE(base::CreateDirectory(summary_path.Append(
-      "AshBrowserTestStarterTest.TestLacrosLogFolderWithTwoRetry.retry_1")));
-  test::AshBrowserTestStarter starter;
-  bool success = starter.PrepareEnvironmentForLacros();
-  EXPECT_TRUE(success);
-  EXPECT_TRUE(command_line->HasSwitch(switches::kUserDataDir));
-  base::FilePath user_data_dir =
-      command_line->GetSwitchValuePath(switches::kUserDataDir);
-  base::FilePath expected_user_data_dir(summary_path.Append(
-      "AshBrowserTestStarterTest.TestLacrosLogFolderWithTwoRetry.retry_2"));
-  EXPECT_EQ(user_data_dir, expected_user_data_dir);
-}
-
-TEST_F(AshBrowserTestStarterTest, TestLacrosLogFolderWithFiveRetry) {
-  base::FilePath summary_path;
-  base::ScopedTempDir scoped_summary_dir;
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  bool on_bot = GetSummaryOutputFolder(summary_path);
-  if (!on_bot) {
-    ASSERT_TRUE(scoped_summary_dir.CreateUniqueTempDir());
-    command_line->AppendSwitchPath(
-        switches::kTestLauncherSummaryOutput,
-        scoped_summary_dir.GetPath().Append("output.json"));
-    summary_path = scoped_summary_dir.GetPath();
-  }
-  command_line->AppendSwitchASCII(ash::switches::kLacrosChromePath, "/tmp/bbb");
-  ASSERT_TRUE(base::CreateDirectory(summary_path.Append(
-      "AshBrowserTestStarterTest.TestLacrosLogFolderWithFiveRetry")));
-  ASSERT_TRUE(base::CreateDirectory(summary_path.Append(
-      "AshBrowserTestStarterTest.TestLacrosLogFolderWithFiveRetry.retry_1")));
-  ASSERT_TRUE(base::CreateDirectory(summary_path.Append(
-      "AshBrowserTestStarterTest.TestLacrosLogFolderWithFiveRetry.retry_2")));
-  ASSERT_TRUE(base::CreateDirectory(summary_path.Append(
-      "AshBrowserTestStarterTest.TestLacrosLogFolderWithFiveRetry.retry_3")));
-  ASSERT_TRUE(base::CreateDirectory(summary_path.Append(
-      "AshBrowserTestStarterTest.TestLacrosLogFolderWithFiveRetry.retry_4")));
-  test::AshBrowserTestStarter starter;
-  bool success = starter.PrepareEnvironmentForLacros();
-  EXPECT_TRUE(success);
-  EXPECT_FALSE(command_line->HasSwitch(switches::kUserDataDir));
-}
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index f01709c..b9a99b2b 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -50,7 +50,6 @@
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_test_util.h"
-#include "chrome/browser/search_engine_choice/search_engine_choice_service.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -114,6 +113,7 @@
 #endif
 
 #if !BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/search_engine_choice/search_engine_choice_service.h"
 #include "chrome/browser/ui/webui/whats_new/whats_new_util.h"
 #include "components/storage_monitor/test_storage_monitor.h"
 #endif
@@ -606,10 +606,10 @@
   // expect this can allow the prompt as desired.
   PrivacySandboxService::SetPromptDisabledForTests(true);
 
+#if !BUILDFLAG(IS_ANDROID)
   // The Search Engine Choice service may attempt to show a modal dialog to the
   // profile on browser start, which is unexpected by mosts tests. Tests which
   // expect this can allow the prompt as desired.
-#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
   if (search_engines::IsChoiceScreenFlagEnabled(
           search_engines::ChoicePromo::kDialog)) {
     SearchEngineChoiceService::SetDialogDisabledForTests(
diff --git a/chrome/test/data/find_in_page/foo.html b/chrome/test/data/find_in_page/foo.html
new file mode 100644
index 0000000..0e590aee
--- /dev/null
+++ b/chrome/test/data/find_in_page/foo.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+  <title>Find in page</title>
+</head>
+<body>
+  foo foo foo
+</body>
+</html>
\ No newline at end of file
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 1f28b9da..7458e52 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -7,7 +7,6 @@
 import("//chrome/common/features.gni")
 import("//chrome/test/include_js_tests.gni")
 import("//components/compose/features.gni")
-import("//components/signin/features.gni")
 import("//crypto/features.gni")
 import("//extensions/buildflags/buildflags.gni")
 import("//pdf/features.gni")
@@ -54,6 +53,7 @@
     "password_manager_internals/password_manager_internals_browsertest.cc",
     "privacy_sandbox/privacy_sandbox_dialog_browsertest.cc",
     "sandbox/sandbox_browsertest.cc",
+    "search_engine_choice/search_engine_choice_js_browsertest.cc",
     "settings/settings_browsertest.cc",
     "side_panel/bookmarks/sp_bookmarks_browsertest.cc",
     "side_panel/commerce/commerce_browsertest.cc",
@@ -83,10 +83,6 @@
     sources += [ "tab_strip/tab_strip_browsertest.cc" ]
   }
 
-  if (enable_search_engine_choice) {
-    sources += [ "search_engine_choice/search_engine_choice_js_browsertest.cc" ]
-  }
-
   if (enable_print_preview) {
     sources += [ "print_preview/print_preview_browsertest.cc" ]
   }
@@ -366,6 +362,7 @@
     "password_manager_internals:build_grdp",
     "privacy_sandbox:build_grdp",
     "sandbox:build_grdp",
+    "search_engine_choice:build_grdp",
     "settings:build_grdp",
     "side_panel:build_grdp",
     "side_panel/customize_chrome:build_grdp",
@@ -394,6 +391,7 @@
     "$target_gen_dir/histograms/resources.grdp",
     "$target_gen_dir/history/resources.grdp",
     "$target_gen_dir/history_clusters_internals/resources.grdp",
+    "$target_gen_dir/search_engine_choice/resources.grdp",
     "$target_gen_dir/identity_internals/resources.grdp",
     "$target_gen_dir/inspect/resources.grdp",
     "$target_gen_dir/js/resources.grdp",
@@ -448,11 +446,6 @@
     grdp_files += [ "$target_gen_dir/../pdf/resources.grdp" ]
   }
 
-  if (enable_search_engine_choice) {
-    deps += [ "search_engine_choice:build_grdp" ]
-    grdp_files += [ "$target_gen_dir/search_engine_choice/resources.grdp" ]
-  }
-
   if (is_chrome_branded) {
     deps += [ "media_router:build_grdp" ]
     grdp_files += [ "$target_gen_dir/media_router/resources.grdp" ]
@@ -490,6 +483,7 @@
       "chromeos/inline_login:build_grdp",
       "chromeos/manage_mirrorsync:build_grdp",
       "chromeos/office_fallback:build_grdp",
+      "chromeos/os_feedback_ui:build_grdp",
       "chromeos/parent_access:build_grdp",
       "chromeos/personalization_app:build_grdp",
       "chromeos/print_management:build_grdp",
@@ -520,6 +514,7 @@
       "$target_gen_dir/chromeos/resources.grdp",
       "$target_gen_dir/chromeos/vc_background_ui/resources.grdp",
       "$target_gen_dir/chromeos/scanning/resources.grdp",
+      "$target_gen_dir/chromeos/os_feedback_ui/resources.grdp",
       "$target_gen_dir/chromeos/shortcut_customization/resources.grdp",
       "$target_gen_dir/chromeos/web_ui_test_mojo_lite_resources.grdp",
       "$target_gen_dir/chromeos/web_ui_test_mojo_resources.grdp",
diff --git a/chrome/test/data/webui/chromeos/BUILD.gn b/chrome/test/data/webui/chromeos/BUILD.gn
index 81a28217..48ceb43 100644
--- a/chrome/test/data/webui/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/BUILD.gn
@@ -195,12 +195,6 @@
     "arc_account_picker/arc_account_picker_test.js",
     "arc_account_picker/test_util.js",
     "gaia_action_buttons/gaia_action_buttons_test.js",
-    "os_feedback_ui/confirmation_page_test.js",
-    "os_feedback_ui/feedback_flow_test.js",
-    "os_feedback_ui/file_attachment_test.js",
-    "os_feedback_ui/help_content_test.js",
-    "os_feedback_ui/search_page_test.js",
-    "os_feedback_ui/share_data_page_test.js",
     "set_time_dialog/set_time_dialog_test.js",
     "set_time_dialog/test_set_time_browser_proxy.js",
     "shimless_rma/all_inputs_disabled_test.js",
@@ -270,7 +264,7 @@
   webui_module_path = "/"
 
   # Used by legacy MojoWebUIBrowserTest.
-  use_typescript_sources = false
+  generate_webui_js_bindings = true
 }
 
 generate_grd("build_web_ui_test_mojo_grdp") {
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn b/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
new file mode 100644
index 0000000..e6c7ad6
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
@@ -0,0 +1,35 @@
+# 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("../../build_webui_tests.gni")
+
+build_webui_tests("build_webui_tests") {
+  ts_path_mappings = [
+    "chrome://os-feedback/*|" +
+        rebase_path("$root_gen_dir/ash/webui/os_feedback_ui/resources/tsc/*",
+                    target_gen_dir),
+    "chrome://webui-test/chromeos/*|" +
+        rebase_path("$root_gen_dir/chrome/test/data/webui/chromeos/tsc/*",
+                    target_gen_dir),
+  ]
+
+  files = [
+    "confirmation_page_test.js",
+    "feedback_flow_test.js",
+    "file_attachment_test.js",
+    "help_content_test.js",
+    "search_page_test.js",
+    "share_data_page_test.js",
+  ]
+
+  ts_deps = [
+    "//ash/webui/common/resources:build_ts",
+    "//ash/webui/os_feedback_ui/resources:build_ts",
+    "//chrome/test/data/webui/chromeos:build_ts",
+    "//third_party/polymer/v3_0:library",
+    "//ui/webui/resources/cr_elements:build_ts",
+    "//ui/webui/resources/js:build_ts",
+    "//ui/webui/resources/mojo:build_ts",
+  ]
+}
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.ts
index e34411f..673537c 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.ts
@@ -8,7 +8,7 @@
 
 import {GooglePhotosAlbum, Paths, PersonalizationBreadcrumbElement, PersonalizationRouterElement, SeaPenTemplateId, TopicSource} from 'chrome://personalization/js/personalization_app.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 
 import {baseSetup, initElement} from './personalization_app_test_utils.js';
@@ -458,4 +458,85 @@
     assertEquals(Paths.SEA_PEN_COLLECTION, path);
     assertDeepEquals({}, queryParams);
   });
+
+  test('hide dropdown icon for sea pen templates', async () => {
+    loadTimeData.overrideValues({isSeaPenEnabled: true});
+    breadcrumbElement = initElement(PersonalizationBreadcrumbElement, {
+      'path': Paths.SEA_PEN_COLLECTION,
+    });
+
+    const dropdownIcon =
+        breadcrumbElement.shadowRoot!.querySelector('#seaPenDropdown');
+
+    assertFalse(!!dropdownIcon);
+  });
+
+  test('show dropdown icon for SeaPen results', async () => {
+    loadTimeData.overrideValues({isSeaPenEnabled: true});
+    breadcrumbElement = initElement(PersonalizationBreadcrumbElement, {
+      'path': Paths.SEA_PEN_RESULTS,
+      'seaPenTemplateId': SeaPenTemplateId.kFlower.toString(),
+    });
+
+    const dropdownIcon =
+        breadcrumbElement.shadowRoot!.querySelector('#seaPenDropdown');
+
+    assertTrue(!!dropdownIcon);
+  });
+
+  test('show dropdown menu for SeaPen results', async () => {
+    loadTimeData.overrideValues({isSeaPenEnabled: true});
+    breadcrumbElement = initElement(PersonalizationBreadcrumbElement, {
+      'path': Paths.SEA_PEN_RESULTS,
+      'seaPenTemplateId': SeaPenTemplateId.kFlower.toString(),
+    });
+
+    const dropdownIcon = breadcrumbElement.shadowRoot!.querySelector(
+                             '#seaPenDropdown') as HTMLElement;
+    dropdownIcon!.click();
+
+    const dropdownMenu =
+        breadcrumbElement.shadowRoot!.querySelector('cr-action-menu');
+    assertTrue(!!dropdownMenu);
+    const allMenuItems = dropdownMenu.querySelectorAll('button');
+    assertTrue(allMenuItems.length > 1);
+    const selectedElement =
+        dropdownMenu.querySelectorAll('button[aria-selected=\'true\']');
+    assertEquals(1, selectedElement.length);
+    assertEquals('Airbrushed', (selectedElement[0] as HTMLElement)!.innerText);
+  });
+
+  test('navigates with SeaPen dropdown', async () => {
+    loadTimeData.overrideValues({isSeaPenEnabled: true});
+    breadcrumbElement = initElement(PersonalizationBreadcrumbElement, {
+      'path': Paths.SEA_PEN_RESULTS,
+      'seaPenTemplateId': SeaPenTemplateId.kFlower.toString(),
+    });
+    const dropdownIcon = breadcrumbElement.shadowRoot!.querySelector(
+                             '#seaPenDropdown') as HTMLElement;
+    dropdownIcon!.click();
+    const dropdownMenu =
+        breadcrumbElement.shadowRoot!.querySelector('cr-action-menu');
+    const template =
+        (dropdownMenu!.querySelectorAll('button[aria-selected=\'false\']')[0] as
+         HTMLElement);
+
+    const original = PersonalizationRouterElement.instance;
+    const goToRoutePromise = new Promise<[Paths, Object]>(resolve => {
+      PersonalizationRouterElement.instance = () => {
+        return {
+          goToRoute(path: Paths, queryParams: Object = {}) {
+            resolve([path, queryParams]);
+            PersonalizationRouterElement.instance = original;
+          },
+        } as PersonalizationRouterElement;
+      };
+    });
+
+    template!.click();
+
+    const [path, queryParams] = await goToRoutePromise;
+    assertEquals(Paths.SEA_PEN_RESULTS, path);
+    assertDeepEquals({'seaPenTemplateId': template.dataset['id']}, queryParams);
+  });
 });
diff --git a/chrome/test/data/webui/chromeos/personalization_app/sea_pen_controller_test.ts b/chrome/test/data/webui/chromeos/personalization_app/sea_pen_controller_test.ts
index f44d818..995360c 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/sea_pen_controller_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/sea_pen_controller_test.ts
@@ -59,10 +59,14 @@
                 recentImageData: {},
                 recentImages: false,
                 thumbnails: true,
+                currentSelected: false,
+                setImage: 0,
               },
               recentImageData: {},
               recentImages: null,
               thumbnails: null,
+              pendingSelected: null,
+              currentSelected: null,
             }),
           },
           {
@@ -71,10 +75,14 @@
                 recentImageData: {},
                 recentImages: false,
                 thumbnails: false,
+                currentSelected: false,
+                setImage: 0,
               },
               recentImageData: {},
               recentImages: null,
               thumbnails: seaPenProvider.images,
+              pendingSelected: null,
+              currentSelected: null,
             }),
           },
         ],
diff --git a/chrome/test/data/webui/chromeos/personalization_app/sea_pen_recent_wallpapers_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/sea_pen_recent_wallpapers_element_test.ts
index 47e172d6..c9432af2 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/sea_pen_recent_wallpapers_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/sea_pen_recent_wallpapers_element_test.ts
@@ -7,7 +7,7 @@
 
 import {emptyState, SeaPenActionName, SeaPenRecentWallpapersElement, WallpaperGridItemElement} from 'chrome://personalization/js/personalization_app.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertNull, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 
 import {baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
 import {TestPersonalizationStore} from './test_personalization_store.js';
@@ -48,7 +48,6 @@
   teardown(async () => {
     await teardownElement(seaPenRecentWallpapersElement);
     seaPenRecentWallpapersElement = null;
-    await flushTasks();
   });
 
   test('displays recently used Sea Pen wallpapers', async () => {
@@ -64,6 +63,8 @@
       },
       recentImages: false,
       thumbnails: false,
+      currentSelected: false,
+      setImage: 0,
     };
 
     // Initialize |seaPenRecentWallpapersElement|.
@@ -136,6 +137,8 @@
         recentImageData: {},
         recentImages: false,
         thumbnails: false,
+        currentSelected: false,
+        setImage: 0,
       },
       recentImages: seaPenProvider.recentImages,
       recentImageData: seaPenProvider.recentImageData,
@@ -164,6 +167,8 @@
         },
         recentImages: false,
         thumbnails: false,
+        currentSelected: false,
+        setImage: 0,
       },
     };
     personalizationStore.notifyObservers();
@@ -183,6 +188,8 @@
         },
         recentImages: false,
         thumbnails: false,
+        currentSelected: false,
+        setImage: 0,
       },
     };
     personalizationStore.notifyObservers();
@@ -200,6 +207,8 @@
         recentImageData: {},
         recentImages: false,
         thumbnails: false,
+        currentSelected: false,
+        setImage: 0,
       },
       recentImageData: seaPenProvider.recentImageData,
       recentImages: seaPenProvider.recentImages,
@@ -223,6 +232,8 @@
         },
         recentImages: false,
         thumbnails: false,
+        currentSelected: false,
+        setImage: 0,
       },
     };
     personalizationStore.notifyObservers();
@@ -252,6 +263,8 @@
         },
         recentImages: false,
         thumbnails: false,
+        currentSelected: false,
+        setImage: 0,
       },
     };
     personalizationStore.notifyObservers();
@@ -388,6 +401,8 @@
         '/sea_pen/333.jpg': false,
       },
       thumbnails: false,
+      currentSelected: false,
+      setImage: 0,
     };
 
     // Initialize |seaPenRecentWallpapersElement|.
@@ -445,15 +460,26 @@
   test('clicks on a recent wallpaper to set wallpaper', async () => {
     personalizationStore.data.wallpaper.seaPen.recentImages =
         seaPenProvider.recentImages;
+    personalizationStore.data.wallpaper.seaPen.recentImageData =
+        seaPenProvider.recentImageData;
+    personalizationStore.data.wallpaper.seaPen.loading = {
+      recentImageData: {
+        '/sea_pen/111.jpg': false,
+        '/sea_pen/222.jpg': false,
+        '/sea_pen/333.jpg': false,
+      },
+      recentImages: false,
+      thumbnails: false,
+      currentSelected: false,
+      setImage: 0,
+    };
 
     // Initialize |seaPenRecentWallpapersElement|.
     seaPenRecentWallpapersElement = initElement(SeaPenRecentWallpapersElement);
     await waitAfterNextRender(seaPenRecentWallpapersElement);
 
     // Sea Pen wallpaper thumbnails should display.
-    const recentImages =
-        seaPenRecentWallpapersElement.shadowRoot!.querySelectorAll(
-            'div:not([hidden]) .sea-pen-image');
+    const recentImages = getDisplayedRecentImages();
     assertEquals(3, recentImages!.length, 'should be 3 images available.');
 
     // Click on the second image to set it as wallpaper.
@@ -464,4 +490,48 @@
         seaPenProvider.recentImages[1], filePath,
         'file_path sent for the second Sea Pen image');
   });
+
+  test('sets selected if image name matches currently selected', async () => {
+    personalizationStore.data.wallpaper.seaPen = {
+      ...personalizationStore.data.wallpaper.seaPen,
+      // No image data loaded.
+      loading: {
+        recentImageData: {
+          '/sea_pen/111.jpg': false,
+          '/sea_pen/222.jpg': false,
+          '/sea_pen/333.jpg': false,
+        },
+        recentImages: false,
+        thumbnails: false,
+        currentSelected: false,
+        setImage: 0,
+      },
+      recentImages: seaPenProvider.recentImages,
+      recentImageData: seaPenProvider.recentImageData,
+    };
+
+    // Initialize |seaPenRecentWallpapersElement|.
+    seaPenRecentWallpapersElement = initElement(SeaPenRecentWallpapersElement);
+    await waitAfterNextRender(seaPenRecentWallpapersElement);
+
+    // Sea Pen wallpaper thumbnails should display.
+    const recentImages = getDisplayedRecentImages();
+    assertEquals(3, recentImages.length);
+
+    // Every image is not selected.
+    assertTrue(recentImages.every(image => !image.selected));
+
+    // Update currentSelected state to a sea pen image.
+    personalizationStore.data.wallpaper.seaPen = {
+      ...personalizationStore.data.wallpaper.seaPen,
+      currentSelected: '/sea_pen/333.jpg',
+    };
+    personalizationStore.notifyObservers();
+
+    assertEquals(
+        3, recentImages.length, 'there should be 3 recent sea pen images');
+    assertFalse(recentImages[0]!.selected!, 'element 0 should not be selected');
+    assertFalse(recentImages[1]!.selected!, 'element 1 should not be selected');
+    assertTrue(recentImages[2]!.selected!, 'element 2 should be selected');
+  });
 });
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_observer_test.ts b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_observer_test.ts
index ff65744..9d73eb9 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_observer_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_observer_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://personalization/strings.m.js';
 
-import {emptyState, SetSelectedImageAction, WallpaperActionName, WallpaperObserver} from 'chrome://personalization/js/personalization_app.js';
+import {emptyState, SeaPenActionName, SetSelectedImageAction, SetSelectedRecentSeaPenImageAction, WallpaperActionName, WallpaperLayout, WallpaperObserver, WallpaperType} from 'chrome://personalization/js/personalization_app.js';
 import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 import {baseSetup} from './personalization_app_test_utils.js';
@@ -53,6 +53,40 @@
     assertDeepEquals(wallpaperProvider.currentWallpaper, image);
   });
 
+  test('sets selected sea pen wallpaper data in store on changed', async () => {
+    // Make sure state starts as expected.
+    assertDeepEquals(emptyState(), personalizationStore.data);
+
+    personalizationStore.expectAction(WallpaperActionName.SET_SELECTED_IMAGE);
+    personalizationStore.expectAction(
+        SeaPenActionName.SET_SELECTED_RECENT_SEA_PEN_IMAGE);
+
+    const selectedSeaPenWallpaper = {
+      descriptionContent: 'test content',
+      descriptionTitle: 'test title',
+      key: '/test/seapen/111.jpg',
+      layout: WallpaperLayout.kCenter,
+      type: WallpaperType.kSeaPen,
+    };
+
+    wallpaperProvider.wallpaperObserverRemote!.onWallpaperChanged(
+        selectedSeaPenWallpaper);
+
+    const {image} =
+        await personalizationStore.waitForAction(
+            WallpaperActionName.SET_SELECTED_IMAGE) as SetSelectedImageAction;
+
+    assertDeepEquals(
+        selectedSeaPenWallpaper, image,
+        'selected image should be a Sea Pen image');
+
+    const {key} = await personalizationStore.waitForAction(
+                      SeaPenActionName.SET_SELECTED_RECENT_SEA_PEN_IMAGE) as
+        SetSelectedRecentSeaPenImageAction;
+
+    assertEquals(selectedSeaPenWallpaper.key, key, 'selected key should match');
+  });
+
   test('sets selected wallpaper if null', async () => {
     // Make sure state starts as expected.
     assertDeepEquals(emptyState(), personalizationStore.data);
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_subpage_top_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_subpage_top_element_test.ts
index 91d6272..c0bb900 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_subpage_top_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_subpage_top_element_test.ts
@@ -220,6 +220,8 @@
         recentImageData: {},
         recentImages: false,
         thumbnails: false,
+        currentSelected: false,
+        setImage: 0,
       },
       recentImageData: {},
       recentImages: null,
@@ -241,6 +243,8 @@
           image: {url: 'https://sea-pen-images.googleusercontent.com/4'},
         },
       ],
+      pendingSelected: null,
+      currentSelected: null,
     };
     assertDeepEquals(
         expectedState,
diff --git a/chrome/test/data/webui/search_engine_choice/BUILD.gn b/chrome/test/data/webui/search_engine_choice/BUILD.gn
index 77cd4dba..ab936f3 100644
--- a/chrome/test/data/webui/search_engine_choice/BUILD.gn
+++ b/chrome/test/data/webui/search_engine_choice/BUILD.gn
@@ -2,11 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//components/signin/features.gni")
 import("../build_webui_tests.gni")
 
-assert(enable_search_engine_choice)
-
 build_webui_tests("build") {
   files = [ "search_engine_choice_test.ts" ]
 
diff --git a/chrome/test/data/webui/search_engine_choice/search_engine_choice_js_browsertest.cc b/chrome/test/data/webui/search_engine_choice/search_engine_choice_js_browsertest.cc
index fd03d1250..8ca2107b 100644
--- a/chrome/test/data/webui/search_engine_choice/search_engine_choice_js_browsertest.cc
+++ b/chrome/test/data/webui/search_engine_choice/search_engine_choice_js_browsertest.cc
@@ -29,7 +29,8 @@
           /*force_chrome_build=*/true);
 };
 
+// TODO(crbug.com/1509119) Fix test flakes and re-enable it.
 IN_PROC_BROWSER_TEST_F(SearchEngineChoiceJsBrowserTest,
-                       SearchEngineChoiceTest) {
+                       DISABLED_SearchEngineChoiceTest) {
   RunTest("search_engine_choice/search_engine_choice_test.js", "mocha.run()");
 }
diff --git a/chrome/test/data/webui/search_engine_choice/search_engine_choice_test.ts b/chrome/test/data/webui/search_engine_choice/search_engine_choice_test.ts
index 5962e3e..01d2cacf 100644
--- a/chrome/test/data/webui/search_engine_choice/search_engine_choice_test.ts
+++ b/chrome/test/data/webui/search_engine_choice/search_engine_choice_test.ts
@@ -15,7 +15,19 @@
   let testElement: SearchEngineChoiceAppElement;
   let handler: TestMock<PageHandlerRemote>&PageHandlerRemote;
 
-  setup(async function() {
+  /**
+   * Async spin until predicate() returns true.
+   */
+  function waitFor(predicate: () => boolean): Promise<void> {
+    if (predicate()) {
+      return Promise.resolve();
+    }
+    return new Promise(resolve => setTimeout(() => {
+                         resolve(waitFor(predicate));
+                       }, 0));
+  }
+
+  setup(function() {
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
     handler = TestMock.fromClass(PageHandlerRemote);
     SearchEngineChoiceBrowserProxy.setInstance(
@@ -23,8 +35,7 @@
 
     testElement = document.createElement('search-engine-choice-app');
     document.body.appendChild(testElement);
-    await waitBeforeNextRender(testElement);
-    testElement.setInstantScrollBehaviorForTest();
+    return waitBeforeNextRender(testElement);
   });
 
   teardown(function() {
@@ -54,9 +65,9 @@
     // The action button text should become "Set as default" after being clicked
     // but still be disabled because we haven't yet made a choice.
     actionButton.click();
-    await waitBeforeNextRender(actionButton);
-    assertEquals(
-        actionButton.textContent!.trim(), testElement.i18n('submitButtonText'));
+    await waitFor(
+        () => actionButton.textContent!.trim() ===
+            testElement.i18n('submitButtonText'));
     assertTrue(actionButton.disabled);
 
     // The action button should be enabled after making a choice.
diff --git a/chrome/test/data/webui/settings/chromeos/crostini_page/bruschetta_subpage_test.ts b/chrome/test/data/webui/settings/chromeos/crostini_page/bruschetta_subpage_test.ts
index 35aa17f..d8872e5 100644
--- a/chrome/test/data/webui/settings/chromeos/crostini_page/bruschetta_subpage_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/crostini_page/bruschetta_subpage_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://os-settings/lazy_load.js';
 
-import {BruschettaSubpageElement, CrostiniBrowserProxyImpl, CrostiniPortSetting, SettingsCrostiniPageElement} from 'chrome://os-settings/lazy_load.js';
+import {BruschettaSubpageElement, CrostiniBrowserProxyImpl, CrostiniPortSetting} from 'chrome://os-settings/lazy_load.js';
 import {Router, routes} from 'chrome://os-settings/os_settings.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -15,7 +15,6 @@
 
 import {TestCrostiniBrowserProxy} from './test_crostini_browser_proxy.js';
 
-let crostiniPage: SettingsCrostiniPageElement;
 let subpage: BruschettaSubpageElement;
 let crostiniBrowserProxy: TestCrostiniBrowserProxy;
 
@@ -34,7 +33,7 @@
   arcEnabled = false,
   bruschettaInstalled = false,
 }: PrefParams = {}): void {
-  crostiniPage.prefs = {
+  subpage.prefs = {
     arc: {
       enabled: {value: arcEnabled},
     },
@@ -56,49 +55,35 @@
 }
 
 suite('<settings-bruschetta-subpage>', () => {
+  suiteSetup(() => {
+    disableAnimationsAndTransitions();
+  });
+
   setup(async () => {
     loadTimeData.overrideValues({
       isCrostiniAllowed: true,
       isCrostiniSupported: true,
+      showBruschetta: true,
     });
     crostiniBrowserProxy = new TestCrostiniBrowserProxy();
     CrostiniBrowserProxyImpl.setInstanceForTesting(crostiniBrowserProxy);
 
-    crostiniPage = document.createElement('settings-crostini-page');
-    document.body.appendChild(crostiniPage);
-    flush();
+    Router.getInstance().navigateTo(routes.BRUSCHETTA_DETAILS);
 
-    disableAnimationsAndTransitions();
-
-    Router.getInstance().navigateTo(routes.CROSTINI);
+    subpage = document.createElement('settings-bruschetta-subpage');
+    document.body.appendChild(subpage);
     setCrostiniPrefs(false, {bruschettaInstalled: true});
-    flush();
-
-    const crostiniSettingsCard =
-        crostiniPage.shadowRoot!.querySelector('crostini-settings-card');
-    assertTrue(!!crostiniSettingsCard);
-    crostiniSettingsCard.set('showBruschetta_', true);
-    await flushTasks();
-    const button =
-        crostiniSettingsCard.shadowRoot!.querySelector<HTMLButtonElement>(
-            '#bruschetta');
-    assertTrue(!!button);
-    button.click();
-
-    await flushTasks();
-    const subpageElement =
-        crostiniPage.shadowRoot!.querySelector('settings-bruschetta-subpage');
-    assertTrue(!!subpageElement);
-    subpage = subpageElement;
+    flushTasks();
   });
 
   teardown(() => {
-    crostiniPage.remove();
+    subpage.remove();
     Router.getInstance().resetRouteForTesting();
+    crostiniBrowserProxy.reset();
   });
 
   test('Navigate to shared USB devices', async () => {
-    const link = subpage.shadowRoot!.querySelector<HTMLButtonElement>(
+    const link = subpage.shadowRoot!.querySelector<HTMLElement>(
         '#bruschettaSharedUsbDevicesRow');
     assertTrue(!!link);
     link.click();
@@ -108,10 +93,6 @@
         routes.BRUSCHETTA_SHARED_USB_DEVICES,
         Router.getInstance().currentRoute);
 
-    assertTrue(!!crostiniPage.shadowRoot!.querySelector(
-        'settings-guest-os-shared-usb-devices[guest-os-type="bruschetta"]'));
-    // Functionality is tested in guest_os_shared_usb_devices_test.js
-
     // Navigate back
     const popStateEventPromise = eventToPromise('popstate', window);
     Router.getInstance().navigateToPreviousRoute();
@@ -132,10 +113,6 @@
     assertEquals(
         routes.BRUSCHETTA_SHARED_PATHS, Router.getInstance().currentRoute);
 
-    assertTrue(!!crostiniPage.shadowRoot!.querySelector(
-        'settings-guest-os-shared-paths[guest-os-type="bruschetta"]'));
-    // Functionality is tested in guest_os_shared_paths_test.js
-
     // Navigate back
     const popStateEventPromise = eventToPromise('popstate', window);
     Router.getInstance().navigateToPreviousRoute();
@@ -146,9 +123,9 @@
         link, subpage.shadowRoot!.activeElement, `${link} should be focused.`);
   });
 
-  test('Remove bruschetta', async () => {
+  test('Removing bruschetta navigates to the previous route', async () => {
     const button = subpage.shadowRoot!.querySelector<HTMLButtonElement>(
-        '#remove cr-button');
+        '#remove > cr-button');
     assertTrue(!!button);
     assertFalse(button.disabled);
     button.click();
@@ -158,15 +135,6 @@
         1,
         crostiniBrowserProxy.getCallCount('requestBruschettaUninstallerView'));
     setCrostiniPrefs(false, {bruschettaInstalled: false});
-
     await eventToPromise('popstate', window);
-
-    assertEquals(routes.CROSTINI, Router.getInstance().currentRoute);
-
-    const crostiniSettingsCard =
-        crostiniPage.shadowRoot!.querySelector('crostini-settings-card');
-    assertTrue(!!crostiniSettingsCard);
-    assertTrue(!!crostiniSettingsCard.shadowRoot!.querySelector(
-        '#enableBruschettaButton'));
   });
 });
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 46c50ee..24cb92f8 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -170,6 +170,23 @@
   mocha.run();
 });
 
+var OSSettingsCrostiniPageBruschettaSubpageRevampTest =
+    class extends OSSettingsCrostiniPageBruschettaSubpageTest {
+  /** @override */
+  get featureList() {
+    return {
+      enabled: super.featureList.enabled.concat([
+        'ash::features::kOsSettingsRevampWayfinding',
+      ]),
+    };
+  }
+};
+
+TEST_F(
+    'OSSettingsCrostiniPageBruschettaSubpageRevampTest', 'AllJsTests', () => {
+      mocha.run();
+    });
+
 var OSSettingsCrostiniPageCrostiniArcAdbTest =
     class extends OSSettingsBrowserTest {
   /** @override */
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_test.ts b/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_test.ts
index c6cb33f..99dccc3b0 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_test.ts
@@ -100,7 +100,7 @@
     await eventToPromise('cr-drawer-opened', drawer);
 
     // Clicking the drawer icon closes the drawer.
-    ui.shadowRoot!.querySelector<HTMLElement>('#iconButton')!.click();
+    ui.shadowRoot!.querySelector<HTMLElement>('#drawerIcon')!.click();
     await eventToPromise('close', drawer);
     assertFalse(drawer.open);
     assertTrue(drawer.wasCanceled());
diff --git a/chrome/updater/certificate_tag.cc b/chrome/updater/certificate_tag.cc
index d00129c..dd19570 100644
--- a/chrome/updater/certificate_tag.cc
+++ b/chrome/updater/certificate_tag.cc
@@ -529,8 +529,35 @@
                                            uint64_t stream_size,
                                            bool force_fat,
                                            bool free_data) {
-  // TODO(crbug.com/1422360): handle the mini fat format.
-  CHECK(force_fat || stream_size >= kMiniStreamCutoffSize);
+  uint64_t sector_size = sector_format_.size;
+  std::optional<std::vector<uint32_t>> mini_fat_entries;
+  std::optional<std::vector<uint8_t>> mini_contents;
+
+  // Code that manages mini fat will probably not run in prod.
+  if (!force_fat && stream_size < kMiniStreamCutoffSize) {
+    // Load the mini fat.
+    std::vector<uint8_t> stream = ReadStream(
+        "mini fat", header_.first_mini_fat_sector,
+        header_.num_mini_fat_sectors * sector_format_.size, true, false);
+    mini_fat_entries = std::vector<uint32_t>();
+    for (size_t offset = 0; offset < stream.size(); offset += 4) {
+      mini_fat_entries->push_back(
+          *reinterpret_cast<uint32_t*>(&stream[offset]));
+    }
+
+    // Load the mini stream, the root directory's stream. root must be dir entry
+    // zero.
+    MSIDirEntry root;
+    const uint64_t offset = header_.first_dir_sector * sector_format_.size;
+    std::memcpy(&root, &contents_[offset], sizeof(MSIDirEntry));
+    mini_contents = ReadStream("mini stream", root.stream_first_sector,
+                               root.stream_size, true, false);
+    sector_size = kMiniStreamSectorSize;
+  }
+
+  std::vector<uint32_t>* fat_entries =
+      mini_fat_entries ? &*mini_fat_entries : &fat_entries_;
+  std::vector<uint8_t>* contents = mini_contents ? &*mini_contents : &contents_;
 
   uint32_t sector = start;
   uint64_t size = stream_size;
@@ -541,12 +568,12 @@
       return {};
     }
     uint64_t n = size;
-    if (n > sector_format_.size) {
-      n = sector_format_.size;
+    if (n > sector_size) {
+      n = sector_size;
     }
-    const uint64_t offset = sector_format_.size * sector;
-    stream.insert(stream.end(), contents_.begin() + offset,
-                  contents_.begin() + offset + n);
+    const uint64_t offset = sector_size * sector;
+    stream.insert(stream.end(), contents->begin() + offset,
+                  contents->begin() + offset + n);
     size -= n;
 
     // Zero out the existing stream bytes, if requested.
@@ -556,15 +583,15 @@
     // file are typically zeroed. Set the data in the sector to zero.
     if (free_data) {
       for (uint64_t i = 0; i < n; ++i) {
-        contents_[offset + i] = 0;
+        (*contents)[offset + i] = 0;
       }
     }
 
     // Find the next sector, then free the fat entry of the current sector.
     uint32_t old = sector;
-    sector = fat_entries_[sector];
+    sector = (*fat_entries)[sector];
     if (free_data) {
-      fat_entries_[old] = kFatFreeSector;
+      (*fat_entries)[old] = kFatFreeSector;
     }
   }
   return stream;
@@ -598,9 +625,25 @@
         &header_bytes_[kNumHeaderContentBytes + i * 4]);
   }
 
-  // TODO(crbug.com/1422360): handle additional difat sectors.
-  CHECK(!header_.num_difat_sectors);
+  // Code here that manages additional difat sectors will probably not run in
+  // prod, but is implemented to avoid a scaling limit. (109 difat sector
+  // entries) x (1024 fat sector entries/difat sector) x (4096
+  // bytes/ fat sector)
+  // => files greater than ~457 MB in size require additional difat sectors.
+  std::vector<uint32_t> difat_sectors;
+  for (uint32_t i = 0; i < header_.num_difat_sectors; ++i) {
+    uint32_t sector = 0;
+    sector = i == 0 ? header_.first_difat_sector
+                    : difat_entries[difat_entries.size() - 1];
+    difat_sectors.push_back(sector);
+    uint64_t start = sector * sector_format_.size;
+    for (int j = 0; j < sector_format_.ints; ++j) {
+      difat_entries.push_back(
+          *reinterpret_cast<uint32_t*>(&contents_[start + j * 4]));
+    }
+  }
   difat_entries_ = difat_entries;
+  difat_sectors_ = difat_sectors;
 }
 
 SignedDataDir MSIBinary::SignedDataDirFromSector(uint64_t dir_sector) {
@@ -844,8 +887,16 @@
   std::memcpy(&new_contents[sig_dir_offset_], &new_sig_dir_entry,
               sizeof(MSIDirEntry));
 
-  // TODO(crbug.com/1422360): handle additional difat sectors.
-  CHECK(!difat_sectors_.size());
+  // ...difat entries,
+  // In case difat sectors were added for huge files.
+  for (size_t i = 0; i < difat_sectors_.size(); ++i) {
+    const int index = kNumDifatHeaderEntries + i * sector_format_.ints;
+    uint64_t offset = difat_sectors_[i] * sector_format_.size;
+    for (int j = 0; j < sector_format_.ints; ++j) {
+      std::memcpy(&new_contents[offset + j * 4], &difat_entries_[index + j],
+                  sizeof(uint32_t));
+    }
+  }
 
   // ...fat entries from local modified copy,
   int index = 0;
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 3248c36..fa1ae43 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-15708.0.0
\ No newline at end of file
+15711.0.0
\ No newline at end of file
diff --git a/chromeos/ash/services/assistant/public/cpp/features.cc b/chromeos/ash/services/assistant/public/cpp/features.cc
index b6596a7..f5abcc1 100644
--- a/chromeos/ash/services/assistant/public/cpp/features.cc
+++ b/chromeos/ash/services/assistant/public/cpp/features.cc
@@ -52,10 +52,6 @@
              "LibAssistantDLC",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kEnableAssistantLearnMore,
-             "AssistantLearnMore",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 BASE_FEATURE(kEnableAssistantOnboarding,
              "AssistantOnboarding",
              base::FEATURE_DISABLED_BY_DEFAULT);
@@ -108,10 +104,6 @@
   return base::FeatureList::IsEnabled(kEnableLibAssistantDLC);
 }
 
-bool IsAssistantLearnMoreEnabled() {
-  return base::FeatureList::IsEnabled(kEnableAssistantLearnMore);
-}
-
 bool IsOnboardingEnabled() {
   return base::FeatureList::IsEnabled(kEnableAssistantOnboarding);
 }
diff --git a/chromeos/ash/services/assistant/public/cpp/features.h b/chromeos/ash/services/assistant/public/cpp/features.h
index c39a4f8d..a09a037 100644
--- a/chromeos/ash/services/assistant/public/cpp/features.h
+++ b/chromeos/ash/services/assistant/public/cpp/features.h
@@ -50,9 +50,6 @@
 BASE_DECLARE_FEATURE(kEnableLibAssistantDLC);
 
 COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC)
-BASE_DECLARE_FEATURE(kEnableAssistantLearnMore);
-
-COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC)
 BASE_DECLARE_FEATURE(kEnableAssistantOnboarding);
 
 COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC) bool IsAppSupportEnabled();
@@ -80,8 +77,6 @@
 
 COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC) bool IsLibAssistantDLCEnabled();
 
-COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC) bool IsAssistantLearnMoreEnabled();
-
 COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC) bool IsOnboardingEnabled();
 
 }  // namespace ash::assistant::features
diff --git a/chromeos/ash/services/cros_healthd/private/cpp/data_collector.cc b/chromeos/ash/services/cros_healthd/private/cpp/data_collector.cc
index 65b4032..07bf256 100644
--- a/chromeos/ash/services/cros_healthd/private/cpp/data_collector.cc
+++ b/chromeos/ash/services/cros_healthd/private/cpp/data_collector.cc
@@ -14,6 +14,7 @@
 #include "base/notreached.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/task/sequenced_task_runner.h"
+#include "base/task/thread_pool.h"
 #include "chromeos/ash/components/audio/cras_audio_handler.h"
 #include "chromeos/ash/components/mojo_service_manager/connection.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -182,7 +183,11 @@
 
 void DataCollector::GetTouchpadLibraryName(
     GetTouchpadLibraryNameCallback callback) {
-  std::move(callback).Run(delegate_->GetTouchpadLibraryName());
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+      base::BindOnce(&Delegate::GetTouchpadLibraryName,
+                     base::Unretained(delegate_)),
+      std::move(callback));
 }
 
 void DataCollector::SetPrivacyScreenState(
diff --git a/chromeos/ash/services/recording/gif_encoder.cc b/chromeos/ash/services/recording/gif_encoder.cc
index 71b16cd..eccf443f 100644
--- a/chromeos/ash/services/recording/gif_encoder.cc
+++ b/chromeos/ash/services/recording/gif_encoder.cc
@@ -32,14 +32,14 @@
 // built the color palette, before we build a new one. Since we are dithering
 // the image, we can work with an old color palette for a larger number of
 // frames before we have to rebuild it.
-constexpr uint8_t kMinNumberOfFramesBetweenPaletteRebuilds = 60;
+constexpr uint8_t kMinNumberOfFramesBetweenPaletteRebuilds = 20;
 
 // If the screen doesn't have any damage, video frames may never be generated.
 // As a result, there can be a large time interval between one frame and the
 // next, in which case the existing color palette might be very stale, and needs
 // to be rebuilt.
 constexpr base::TimeDelta kMaxDurationBetweenSuccessiveFrames =
-    base::Seconds(2);
+    base::Seconds(1);
 
 // Calculates and returns the color bit depth based on the size of the given
 // `color_palette`. The color bit depth is the least number of bits needed to be
@@ -193,29 +193,12 @@
     // to something smaller than the initial size.
     return false;
   }
+
+  bool SupportsRgbVideoFrame() const override { return true; }
 };
 
 // -----------------------------------------------------------------------------
 
-// Wraps the given `video_frame` pixels in a bitmap and returns it. Note that
-// this does not copy the pixels bytes from `video_frame` to the returned
-// bitmap, and hence the bitmap is safe to access as long as the `video_frame`
-// is alive.
-SkBitmap WrapVideoFrameInBitmap(const media::VideoFrame& video_frame) {
-  const gfx::Size visible_size = video_frame.visible_rect().size();
-  const SkImageInfo image_info = SkImageInfo::MakeN32(
-      visible_size.width(), visible_size.height(), kPremul_SkAlphaType,
-      video_frame.ColorSpace().ToSkColorSpace());
-
-  SkBitmap bitmap;
-  const uint8_t* pixels =
-      video_frame.visible_data(media::VideoFrame::kARGBPlane);
-  bitmap.installPixels(
-      SkPixmap(image_info, pixels,
-               video_frame.row_bytes(media::VideoFrame::kARGBPlane)));
-  return bitmap;
-}
-
 QuantizerPalettePair CreateQuantizer(const RgbVideoFrame& rgb_video_frame) {
   return QuantizerPalettePair(OctreeColorQuantizer(rgb_video_frame));
 }
@@ -292,33 +275,13 @@
 }
 
 void GifEncoder::EncodeVideo(scoped_refptr<media::VideoFrame> frame) {
+  NOTREACHED();
+}
+
+void GifEncoder::EncodeRgbVideo(RgbVideoFrame rgb_video_frame) {
   ++frame_count_;
 
-  // Extract the frame time first thing in case we need to call
-  // `TimeTicks::Now()`.
-  const auto frame_time =
-      frame->metadata().reference_time.value_or(base::TimeTicks::Now());
-
-  // This bitmap is backed up by the same memory containing the bytes of the
-  // frame. `SkBitmap` makes it more convenient to extract the colors from the
-  // video frame. `RgbVideoFrame` will copy only the RGB pixels out of the
-  // bitmap. This is needed so that we can modify the colors of these pixels
-  // when we implement dithering. The video `frame`'s memory itself cannot be
-  // modified, as it is backed by a read-only shared memory region. Once we copy
-  // the pixel colors into `rgb_video_frame`, we no longer need the video
-  // `frame`.
-  RgbVideoFrame rgb_video_frame(WrapVideoFrameInBitmap(*frame));
-
-  const gfx::Size visible_size = frame->visible_rect().size();
-  DCHECK_EQ(rgb_video_frame.num_pixels(),
-            static_cast<size_t>(visible_size.GetArea()));
-
-  // We're done with the frame, release it immediately before we spend cycles
-  // doing the encoding and writing to the file. This returns it back to the
-  // video frame buffer pool in Viz, which has a maximum number of in-flight
-  // frames. Releasing the frame as soon as we're done with it helps to avoid
-  // reaching that limit often.
-  frame.reset();
+  const auto frame_time = rgb_video_frame.frame_time();
 
   // If this is the very first frame ever, we must build a new color palette
   // synchronously here, and proceed with the rest of encoding.
@@ -327,9 +290,7 @@
   // received since the last time we built a color palette. At which point, we
   // send a request to rebuild a new color palette on the
   // `color_palette_task_runner_` sequence, so as not to block the encoding task
-  // sequence. We don't want the in-flight frame pool in
-  // `FrameSinkVideoCapturerImpl` to fill up because we're not returning the
-  // frames quick enough.
+  // sequence.
   // Note that we don't allow the duration between any two successive frames to
   // exceed `kMaxDurationBetweenSuccessiveFrames` without rebuilding the color
   // palette as it may be very stale.
diff --git a/chromeos/ash/services/recording/gif_encoder.h b/chromeos/ash/services/recording/gif_encoder.h
index b37727e..fab66674 100644
--- a/chromeos/ash/services/recording/gif_encoder.h
+++ b/chromeos/ash/services/recording/gif_encoder.h
@@ -86,6 +86,7 @@
   void InitializeVideoEncoder(
       const media::VideoEncoder::Options& video_encoder_options) override;
   void EncodeVideo(scoped_refptr<media::VideoFrame> frame) override;
+  void EncodeRgbVideo(RgbVideoFrame rgb_video_frame) override;
   EncodeAudioCallback GetEncodeAudioCallback() override;
   void FlushAndFinalize(base::OnceClosure on_done) override;
 
diff --git a/chromeos/ash/services/recording/recording_encoder.h b/chromeos/ash/services/recording/recording_encoder.h
index 6a099c2..5e9c6e6 100644
--- a/chromeos/ash/services/recording/recording_encoder.h
+++ b/chromeos/ash/services/recording/recording_encoder.h
@@ -11,6 +11,7 @@
 #include "base/thread_annotations.h"
 #include "base/time/time.h"
 #include "chromeos/ash/services/recording/recording_file_io_helper.h"
+#include "chromeos/ash/services/recording/rgb_video_frame.h"
 #include "media/base/encoder_status.h"
 #include "media/base/video_encoder.h"
 #include "media/base/video_types.h"
@@ -62,6 +63,12 @@
     // Size changes during recording require a reconfiguration of the video
     // encoder.
     virtual bool SupportsVideoFrameSizeChanges() const = 0;
+
+    // Returns whether the encoder supports the extracted `RgbVideoFrame` from
+    // the `media::VideoFrame` directly. This allows us to discard the
+    // `media::VideoFrame` very early upon reception so that it can be returned
+    // immediately to viz capturer buffer pool (see b/316588576).
+    virtual bool SupportsRgbVideoFrame() const = 0;
   };
 
   explicit RecordingEncoder(OnFailureCallback on_failure_callback);
@@ -82,8 +89,11 @@
   virtual void InitializeVideoEncoder(
       const media::VideoEncoder::Options& video_encoder_options) = 0;
 
-  // Encodes and muxes the given video `frame`.
+  // Encodes and muxes the given video `frame`. Clients must check first
+  // `Capabilities::SupportsRgbVideoFrame()` to determine which frame type to
+  // provide (i.e. `media::VideoFrame` or `RgbVideoFrame`.
   virtual void EncodeVideo(scoped_refptr<media::VideoFrame> frame) = 0;
+  virtual void EncodeRgbVideo(RgbVideoFrame rgb_video_frame) = 0;
 
   // Returns a callback bound to this object that can be called repeatedly by
   // the client to provide the audio buses along with their timestamps so that
diff --git a/chromeos/ash/services/recording/recording_service.cc b/chromeos/ash/services/recording/recording_service.cc
index b65f06c6..6f825ef9 100644
--- a/chromeos/ash/services/recording/recording_service.cc
+++ b/chromeos/ash/services/recording/recording_service.cc
@@ -25,6 +25,7 @@
 #include "chromeos/ash/services/recording/gif_encoder.h"
 #include "chromeos/ash/services/recording/recording_encoder.h"
 #include "chromeos/ash/services/recording/recording_service_constants.h"
+#include "chromeos/ash/services/recording/rgb_video_frame.h"
 #include "chromeos/ash/services/recording/video_capture_params.h"
 #include "chromeos/ash/services/recording/webm_encoder_muxer.h"
 #include "media/audio/audio_device_description.h"
@@ -413,6 +414,22 @@
         .Run(*frame, content_rect);
   }
 
+  if (encoder_capabilities_->SupportsRgbVideoFrame()) {
+    // This is the GIF encoding path.
+    encoder_muxer_.AsyncCall(&RecordingEncoder::EncodeRgbVideo)
+        .WithArgs(RgbVideoFrame(*frame));
+
+    // Note that we no longer need `frame`. `RgbVideoFrame` already copied the
+    // pixel colors (which is needed to be able to modify them later when we
+    // dither the image). Note that the video `frame`'s memory itself cannot be
+    // modified, as it is backed by a read-only shared memory region. This
+    // allows us to return the frame early to Viz capturer buffer pool, which
+    // has a maximum number of in-flight frames (See b/316588576).
+    frame.reset();
+    return;
+  }
+
+  // This is the WebM path.
   encoder_muxer_.AsyncCall(&RecordingEncoder::EncodeVideo)
       .WithArgs(std::move(frame));
 }
diff --git a/chromeos/ash/services/recording/rgb_video_frame.cc b/chromeos/ash/services/recording/rgb_video_frame.cc
index fc0dd24..762b071 100644
--- a/chromeos/ash/services/recording/rgb_video_frame.cc
+++ b/chromeos/ash/services/recording/rgb_video_frame.cc
@@ -11,9 +11,38 @@
 
 namespace recording {
 
-RgbVideoFrame::RgbVideoFrame(const SkBitmap& bitmap)
+namespace {
+
+// Wraps the given `video_frame` pixels in a bitmap and returns it. Note that
+// this does not copy the pixels bytes from `video_frame` to the returned
+// bitmap, and hence the bitmap is safe to access as long as the `video_frame`
+// is alive.
+SkBitmap WrapVideoFrameInBitmap(const media::VideoFrame& video_frame) {
+  const gfx::Size visible_size = video_frame.visible_rect().size();
+  const SkImageInfo image_info = SkImageInfo::MakeN32(
+      visible_size.width(), visible_size.height(), kPremul_SkAlphaType,
+      video_frame.ColorSpace().ToSkColorSpace());
+
+  SkBitmap bitmap;
+  const uint8_t* pixels =
+      video_frame.visible_data(media::VideoFrame::kARGBPlane);
+  bitmap.installPixels(
+      SkPixmap(image_info, pixels,
+               video_frame.row_bytes(media::VideoFrame::kARGBPlane)));
+  return bitmap;
+}
+
+}  // namespace
+
+RgbVideoFrame::RgbVideoFrame(const media::VideoFrame& video_frame)
+    : RgbVideoFrame(WrapVideoFrameInBitmap(video_frame),
+                    video_frame.metadata().reference_time.value_or(
+                        base::TimeTicks::Now())) {}
+
+RgbVideoFrame::RgbVideoFrame(const SkBitmap& bitmap, base::TimeTicks frame_time)
     : width_(bitmap.width()),
       height_(bitmap.height()),
+      frame_time_(frame_time),
       data_(new RgbColor[width_ * height_]) {
   DCHECK_EQ(kN32_SkColorType, bitmap.colorType());
 
diff --git a/chromeos/ash/services/recording/rgb_video_frame.h b/chromeos/ash/services/recording/rgb_video_frame.h
index a10f5f0..c7f3e97 100644
--- a/chromeos/ash/services/recording/rgb_video_frame.h
+++ b/chromeos/ash/services/recording/rgb_video_frame.h
@@ -8,6 +8,8 @@
 #include <cstdint>
 #include <memory>
 
+#include "base/time/time.h"
+#include "media/base/video_frame.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkTypes.h"
@@ -65,13 +67,15 @@
 // platform (see above `RgbColor` members order).
 class RgbVideoFrame {
  public:
-  explicit RgbVideoFrame(const SkBitmap& bitmap);
+  explicit RgbVideoFrame(const media::VideoFrame& video_frame);
+  RgbVideoFrame(const SkBitmap& bitmap, base::TimeTicks frame_time);
   RgbVideoFrame(RgbVideoFrame&&);
   RgbVideoFrame& operator=(const RgbVideoFrame&) = delete;
   ~RgbVideoFrame();
 
   int width() const { return width_; }
   int height() const { return height_; }
+  base::TimeTicks frame_time() const { return frame_time_; }
 
   size_t num_pixels() const { return width_ * height_; }
 
@@ -95,6 +99,8 @@
   const int width_;
   const int height_;
 
+  const base::TimeTicks frame_time_;
+
   // The pixel color data.
   std::unique_ptr<RgbColor[]> data_;
 };
diff --git a/chromeos/ash/services/recording/webm_encoder_muxer.cc b/chromeos/ash/services/recording/webm_encoder_muxer.cc
index ef8c7bac..e3becb6 100644
--- a/chromeos/ash/services/recording/webm_encoder_muxer.cc
+++ b/chromeos/ash/services/recording/webm_encoder_muxer.cc
@@ -9,6 +9,7 @@
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
+#include "base/notreached.h"
 #include "base/task/sequenced_task_runner.h"
 #include "chromeos/ash/services/recording/public/mojom/recording_service.mojom.h"
 #include "chromeos/ash/services/recording/recording_file_io_helper.h"
@@ -58,6 +59,8 @@
   }
 
   bool SupportsVideoFrameSizeChanges() const override { return true; }
+
+  bool SupportsRgbVideoFrame() const override { return false; }
 };
 
 // -----------------------------------------------------------------------------
@@ -227,6 +230,10 @@
   }
 }
 
+void WebmEncoderMuxer::EncodeRgbVideo(RgbVideoFrame rgb_video_frame) {
+  NOTREACHED();
+}
+
 EncodeAudioCallback WebmEncoderMuxer::GetEncodeAudioCallback() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(audio_encoder_);
diff --git a/chromeos/ash/services/recording/webm_encoder_muxer.h b/chromeos/ash/services/recording/webm_encoder_muxer.h
index a5232bb6..2e5142b 100644
--- a/chromeos/ash/services/recording/webm_encoder_muxer.h
+++ b/chromeos/ash/services/recording/webm_encoder_muxer.h
@@ -93,6 +93,7 @@
   void InitializeVideoEncoder(
       const media::VideoEncoder::Options& video_encoder_options) override;
   void EncodeVideo(scoped_refptr<media::VideoFrame> frame) override;
+  void EncodeRgbVideo(RgbVideoFrame rgb_video_frame) override;
   EncodeAudioCallback GetEncodeAudioCallback() override;
   void FlushAndFinalize(base::OnceClosure on_done) override;
 
diff --git a/chromeos/profiles/arm-exp.afdo.newest.txt b/chromeos/profiles/arm-exp.afdo.newest.txt
index 3440e79a..532f6a5b 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.6184.0-r1-redacted.afdo.xz
+chromeos-chrome-arm-exp-121-6167.9-1702295304-benchmark-122.0.6185.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/arm.afdo.newest.txt b/chromeos/profiles/arm.afdo.newest.txt
index 11e4ea1..6188342 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.6185.0-r1-redacted.afdo.xz
+chromeos-chrome-arm-none-121-6154.0-1702299075-benchmark-122.0.6186.0-r1-redacted.afdo.xz
diff --git a/clank b/clank
index 4131ebd..68277d9 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 4131ebd0819f60c9621c3e17362fc1e6e0bd14ed
+Subproject commit 68277d9bc7d17ef27563d83fcc443e33fbeec22f
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc
index 43edcf5..3557706f 100644
--- a/components/autofill/content/renderer/autofill_agent.cc
+++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -1689,11 +1689,16 @@
 std::optional<FormData> AutofillAgent::GetSubmittedForm() const {
   // Checks whether all elements represented by `element_ids` in `document` have
   // disappeared (removed/hidden).
-  // TODO(crbug.com/1427131): Remove document parameter after launching
+  // TODO(crbug.com/1427131): Remove `render_frame` parameter after launching
   // AutofillUseDomNodeIdForRendererId.
   auto all_control_elements_disappeared =
-      [](const blink::WebDocument& document,
+      [](content::RenderFrame* render_frame,
          const std::set<FieldRendererId>& element_ids) {
+        if (!render_frame) {
+          return false;
+        }
+        const blink::WebDocument& document =
+            render_frame->GetWebFrame()->GetDocument();
         std::vector<FieldRendererId> elements(element_ids.begin(),
                                               element_ids.end());
         return base::ranges::none_of(
@@ -1720,14 +1725,17 @@
   // - Formless elements were autofilled.
   // - The user has edited formless elements and all those elements disappeared
   //   (removed/hidden).
-  // TODO(crbug.com/1427131): Remove render_frame condition after launching
-  // AutofillUseDomNodeIdForRendererId.
+  // - The user has edited formless elements and the
+  //   kAutofillDontCheckForDisappearingFormlessElementsForSubmission feature is
+  //   enabled.
   if (auto* render_frame = unsafe_render_frame();
       formless_elements_were_autofilled_ ||
-      (render_frame && !formless_elements_user_edited_.empty() &&
-       all_control_elements_disappeared(
-           render_frame->GetWebFrame()->GetDocument(),
-           formless_elements_user_edited_))) {
+      (!formless_elements_user_edited_.empty() &&
+       (all_control_elements_disappeared(render_frame,
+                                         formless_elements_user_edited_) ||
+        base::FeatureList::IsEnabled(
+            features::
+                kAutofillDontCheckForDisappearingFormlessElementsForSubmission)))) {
     // Return the extracted form or `provisionally_saved_form_` as a fallback if
     // extraction fails.
     if (std::optional<FormData> form = CollectFormlessElements()) {
diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc
index e0e4cc6..ebb93a4 100644
--- a/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/components/autofill/core/browser/autofill_external_delegate.cc
@@ -282,6 +282,15 @@
   if (should_show_cards_from_account_option_) {
     suggestions.emplace_back(
         l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ACCOUNT_CARDS));
+    autofill_metrics::LogAutofillShowCardsFromGoogleAccountButtonEventMetric(
+        autofill_metrics::ShowCardsFromGoogleAccountButtonEvent::
+            kButtonAppeared);
+    if (!show_cards_from_account_suggestion_added_) {
+      show_cards_from_account_suggestion_added_ = true;
+      autofill_metrics::LogAutofillShowCardsFromGoogleAccountButtonEventMetric(
+          autofill_metrics::ShowCardsFromGoogleAccountButtonEvent::
+              kButtonAppearedOnce);
+    }
     suggestions.back().popup_item_id = PopupItemId::kShowAccountCards;
     suggestions.back().icon = Suggestion::Icon::kGoogle;
   }
@@ -605,6 +614,9 @@
           AutofillTriggerSource::kKeyboardAccessory));
       break;
     case PopupItemId::kShowAccountCards:
+      autofill_metrics::LogAutofillShowCardsFromGoogleAccountButtonEventMetric(
+          autofill_metrics::ShowCardsFromGoogleAccountButtonEvent::
+              kButtonClicked);
       manager_->OnUserAcceptedCardsFromAccountOption();
       break;
     case PopupItemId::kVirtualCreditCardEntry:
diff --git a/components/autofill/core/browser/autofill_external_delegate.h b/components/autofill/core/browser/autofill_external_delegate.h
index 394ee76..5ef6aa5d7 100644
--- a/components/autofill/core/browser/autofill_external_delegate.h
+++ b/components/autofill/core/browser/autofill_external_delegate.h
@@ -249,6 +249,7 @@
   PopupType popup_type_ = PopupType::kUnspecified;
 
   bool should_show_cards_from_account_option_ = false;
+  bool show_cards_from_account_suggestion_added_ = false;
 
   std::vector<PopupItemId> shown_suggestion_types_;
 
diff --git a/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
index a70b4df..3a9c1f08 100644
--- a/components/autofill/core/browser/autofill_external_delegate_unittest.cc
+++ b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -34,6 +34,7 @@
 #include "components/autofill/core/browser/metrics/autofill_metrics.h"
 #include "components/autofill/core/browser/metrics/granular_filling_metrics.h"
 #include "components/autofill/core/browser/metrics/log_event.h"
+#include "components/autofill/core/browser/metrics/suggestions_list_metrics.h"
 #include "components/autofill/core/browser/mock_autofill_compose_delegate.h"
 #include "components/autofill/core/browser/mock_single_field_form_fill_router.h"
 #include "components/autofill/core/browser/personal_data_manager_observer.h"
@@ -2195,6 +2196,71 @@
                                             std::vector<Suggestion>());
 }
 
+TEST_F(AutofillExternalDelegateCardsFromAccountTest,
+       LogShowCardsFromGoogleAccountButtonMetrics) {
+  FormData form = test::CreateTestCreditCardFormData(/*is_https=*/true,
+                                                     /*use_month_type=*/false);
+  manager().OnFormsSeen({form}, {});
+  external_delegate().OnQuery(form, form.fields[0], gfx::RectF(),
+                              kDefaultTriggerSource);
+  base::HistogramTester histogram_tester;
+  const auto kExpectedSuggestions = SuggestionVectorMainTextsAre(
+      Suggestion::Text(std::u16string(), Suggestion::Text::IsPrimary(true)),
+      Suggestion::Text(
+          l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ACCOUNT_CARDS),
+          Suggestion::Text::IsPrimary(true)),
+#if !BUILDFLAG(IS_ANDROID)
+      Suggestion::Text(std::u16string(), Suggestion::Text::IsPrimary(false)),
+#endif
+      Suggestion::Text(
+          l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE_PAYMENT_METHODS),
+          Suggestion::Text::IsPrimary(true)));
+  EXPECT_CALL(client(),
+              ShowAutofillPopup(PopupOpenArgsAre(kExpectedSuggestions), _));
+  std::vector<Suggestion> autofill_item;
+  autofill_item.emplace_back(/*main_text=*/u"", PopupItemId::kCreditCardEntry);
+  autofill_item[0].main_text.is_primary = Suggestion::Text::IsPrimary(true);
+
+  external_delegate().OnSuggestionsReturned(form.fields[0].global_id(),
+                                            autofill_item);
+
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples(
+          "Autofill.ButterForPayments.ShowCardsFromGoogleAccountButtonEvents"),
+      BucketsAre(
+          base::Bucket(autofill_metrics::ShowCardsFromGoogleAccountButtonEvent::
+                           kButtonAppeared,
+                       1),
+          base::Bucket(autofill_metrics::ShowCardsFromGoogleAccountButtonEvent::
+                           kButtonAppearedOnce,
+                       1)));
+
+  // Simulate suggestions returned again.
+  EXPECT_CALL(client(),
+              ShowAutofillPopup(PopupOpenArgsAre(SuggestionVectorIdsAre(
+                                    PopupItemId::kShowAccountCards)),
+                                _));
+  autofill_item.clear();
+
+  external_delegate().OnSuggestionsReturned(form.fields[0].global_id(),
+                                            autofill_item);
+
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples(
+          "Autofill.ButterForPayments.ShowCardsFromGoogleAccountButtonEvents"),
+      BucketsAre(
+          base::Bucket(autofill_metrics::ShowCardsFromGoogleAccountButtonEvent::
+                           kButtonAppeared,
+                       2),
+          base::Bucket(autofill_metrics::ShowCardsFromGoogleAccountButtonEvent::
+                           kButtonAppearedOnce,
+                       1)));
+}
+
+// TODO(crbug.com/1510618): Add test case where 'Show cards from your Google
+// account' button is clicked. Encountered issues with test sync setup when
+// attempting to make it.
+
 #if BUILDFLAG(IS_IOS)
 // Tests that outdated returned suggestions are discarded.
 TEST_F(AutofillExternalDelegateCardsFromAccountTest,
diff --git a/components/autofill/core/browser/form_parsing/address_field.cc b/components/autofill/core/browser/form_parsing/address_field.cc
index 17bfe95..5f76db8 100644
--- a/components/autofill/core/browser/form_parsing/address_field.cc
+++ b/components/autofill/core/browser/form_parsing/address_field.cc
@@ -23,6 +23,16 @@
 
 namespace {
 
+base::span<const MatchPatternRef> GetMatchPatterns(base::StringPiece name,
+                                                   ParsingContext& context) {
+  return GetMatchPatterns(name, context.page_language, context.pattern_source);
+}
+
+base::span<const MatchPatternRef> GetMatchPatterns(ServerFieldType type,
+                                                   ParsingContext& context) {
+  return GetMatchPatterns(type, context.page_language, context.pattern_source);
+}
+
 bool SetFieldAndAdvanceCursor(AutofillScanner* scanner,
                               raw_ptr<AutofillField>* field) {
   *field = scanner->Cursor();
@@ -110,33 +120,31 @@
 }  // namespace
 
 // static
-std::unique_ptr<FormField> AddressField::Parse(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
-  if (scanner->IsEnd())
+std::unique_ptr<FormField> AddressField::Parse(ParsingContext& context,
+                                               AutofillScanner* scanner,
+                                               LogManager* log_manager) {
+  if (scanner->IsEnd()) {
     return nullptr;
+  }
 
   std::unique_ptr<AddressField> address_field(new AddressField(log_manager));
   const AutofillField* const initial_field = scanner->Cursor();
   size_t saved_cursor = scanner->SaveCursor();
 
   base::span<const MatchPatternRef> email_patterns =
-      GetMatchPatterns("EMAIL_ADDRESS", page_language, pattern_source);
+      GetMatchPatterns("EMAIL_ADDRESS", context);
 
   base::span<const MatchPatternRef> address_patterns =
-      GetMatchPatterns("ADDRESS_LOOKUP", page_language, pattern_source);
+      GetMatchPatterns("ADDRESS_LOOKUP", context);
 
   base::span<const MatchPatternRef> address_ignore_patterns =
-      GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language, pattern_source);
+      GetMatchPatterns("ADDRESS_NAME_IGNORED", context);
 
   base::span<const MatchPatternRef> attention_ignore_patterns =
-      GetMatchPatterns("ATTENTION_IGNORED", page_language, pattern_source);
+      GetMatchPatterns("ATTENTION_IGNORED", context);
 
   base::span<const MatchPatternRef> region_ignore_patterns =
-      GetMatchPatterns("REGION_IGNORED", page_language, pattern_source);
+      GetMatchPatterns("REGION_IGNORED", context);
 
   // Allow address fields to appear in any order.
   size_t begin_trailing_non_labeled_fields = 0;
@@ -144,41 +152,39 @@
   while (!scanner->IsEnd()) {
     const size_t cursor = scanner->SaveCursor();
     // Ignore "Address Lookup" field. http://crbug.com/427622
-    if (ParseField(scanner, kAddressLookupRe, address_patterns, nullptr,
-                   {log_manager, "kAddressLookupRe"}) ||
+    if (ParseField(context, scanner, kAddressLookupRe, address_patterns,
+                   nullptr, {log_manager, "kAddressLookupRe"}) ||
         // This pattern fully migrated to the MatchPattern mechanism. There
         // is no regular expression in autofill_regex_constants.h anymore.
-        ParseField(scanner, kNoLegacyPattern, address_ignore_patterns, nullptr,
-                   {log_manager, "kAddressNameIgnoreRe"})) {
+        ParseField(context, scanner, kNoLegacyPattern, address_ignore_patterns,
+                   nullptr, {log_manager, "kAddressNameIgnoreRe"})) {
       continue;
       // Ignore email addresses.
     } else if (ParseFieldSpecifics(
-                   scanner, kEmailRe,
+                   context, scanner, kEmailRe,
                    kDefaultMatchParamsWith<MatchFieldType::kTextArea>,
                    email_patterns, nullptr, {log_manager, "kEmailRe"},
                    [](const MatchingPattern& p) {
                      return WithFieldType(p, MatchFieldType::kTextArea);
                    })) {
       continue;
-    } else if (address_field->ParseAddress(scanner, client_country,
-                                           page_language, pattern_source) ||
-               address_field->ParseAddressField(
-                   scanner, client_country, page_language, pattern_source) ||
-               address_field->ParseCompany(scanner, page_language,
-                                           pattern_source)) {
+    } else if (address_field->ParseAddress(context, scanner) ||
+               address_field->ParseAddressField(context, scanner) ||
+               address_field->ParseCompany(context, scanner)) {
       has_trailing_non_labeled_fields = false;
       continue;
-    } else if (ParseField(scanner, kAttentionIgnoredRe,
+    } else if (ParseField(context, scanner, kAttentionIgnoredRe,
                           attention_ignore_patterns, nullptr,
                           {log_manager, "kAttentionIgnoredRe"}) ||
-               ParseField(scanner, kRegionIgnoredRe, region_ignore_patterns,
-                          nullptr, {log_manager, "kRegionIgnoredRe"})) {
+               ParseField(context, scanner, kRegionIgnoredRe,
+                          region_ignore_patterns, nullptr,
+                          {log_manager, "kRegionIgnoredRe"})) {
       // We ignore the following:
       // * Attention.
       // * Province/Region/Other.
       continue;
     } else if (scanner->Cursor() != initial_field &&
-               ParseEmptyLabel(scanner, nullptr)) {
+               ParseEmptyLabel(context, scanner, nullptr)) {
       // Ignore non-labeled fields within an address; the page
       // MapQuest Driving Directions North America.html contains such a field.
       // We only ignore such fields after we've parsed at least one other field;
@@ -232,10 +238,8 @@
 
 // static
 std::unique_ptr<FormField> AddressField::ParseStandaloneZip(
+    ParsingContext& context,
     AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
     LogManager* log_manager) {
   if (!base::FeatureList::IsEnabled(
           features::kAutofillEnableZipOnlyAddressForms)) {
@@ -249,7 +253,7 @@
   std::unique_ptr<AddressField> address_field(new AddressField(log_manager));
   size_t saved_cursor = scanner->SaveCursor();
 
-  address_field->ParseZipCode(scanner, page_language, pattern_source);
+  address_field->ParseZipCode(context, scanner);
   if (address_field->zip_) {
     return std::move(address_field);
   }
@@ -317,24 +321,20 @@
                     field_candidates);
 }
 
-bool AddressField::ParseCompany(AutofillScanner* scanner,
-                                const LanguageCode& page_language,
-                                PatternSource pattern_source) {
+bool AddressField::ParseCompany(ParsingContext& context,
+                                AutofillScanner* scanner) {
   if (company_)
     return false;
 
   base::span<const MatchPatternRef> company_patterns =
-      GetMatchPatterns("COMPANY_NAME", page_language, pattern_source);
+      GetMatchPatterns("COMPANY_NAME", context);
 
-  return ParseField(scanner, kCompanyRe, company_patterns, &company_,
+  return ParseField(context, scanner, kCompanyRe, company_patterns, &company_,
                     {log_manager_, "kCompanyRe"});
 }
 
-bool AddressField::ParseAddressFieldSequence(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
+bool AddressField::ParseAddressFieldSequence(ParsingContext& context,
+                                             AutofillScanner* scanner) {
   // Search for an uninterrupted sequence of fields that indicate that a form
   // asks for structured information.
   //
@@ -365,27 +365,26 @@
   // arbitrary order the parsing is considered successful.
   const size_t saved_cursor_position = scanner->CursorPosition();
 
-  base::span<const MatchPatternRef> street_location_patterns = GetMatchPatterns(
-      ADDRESS_HOME_STREET_LOCATION, page_language, pattern_source);
+  base::span<const MatchPatternRef> street_location_patterns =
+      GetMatchPatterns(ADDRESS_HOME_STREET_LOCATION, context);
   base::span<const MatchPatternRef> street_name_patterns =
-      GetMatchPatterns(ADDRESS_HOME_STREET_NAME, page_language, pattern_source);
-  base::span<const MatchPatternRef> house_number_patterns = GetMatchPatterns(
-      ADDRESS_HOME_HOUSE_NUMBER, page_language, pattern_source);
+      GetMatchPatterns(ADDRESS_HOME_STREET_NAME, context);
+  base::span<const MatchPatternRef> house_number_patterns =
+      GetMatchPatterns(ADDRESS_HOME_HOUSE_NUMBER, context);
   base::span<const MatchPatternRef> apartment_number_patterns =
-      GetMatchPatterns(ADDRESS_HOME_APT_NUM, page_language, pattern_source);
+      GetMatchPatterns(ADDRESS_HOME_APT_NUM, context);
   base::span<const MatchPatternRef> overflow_patterns =
-      GetMatchPatterns("OVERFLOW", page_language, pattern_source);
+      GetMatchPatterns("OVERFLOW", context);
   base::span<const MatchPatternRef> overflow_and_landmark_patterns =
-      GetMatchPatterns("OVERFLOW_AND_LANDMARK", page_language, pattern_source);
+      GetMatchPatterns("OVERFLOW_AND_LANDMARK", context);
   base::span<const MatchPatternRef> between_streets_or_landmark_patterns =
-      GetMatchPatterns("BETWEEN_STREETS_OR_LANDMARK", page_language,
-                       pattern_source);
+      GetMatchPatterns("BETWEEN_STREETS_OR_LANDMARK", context);
   base::span<const MatchPatternRef> between_streets_patterns =
-      GetMatchPatterns("BETWEEN_STREETS", page_language, pattern_source);
+      GetMatchPatterns("BETWEEN_STREETS", context);
   base::span<const MatchPatternRef> between_streets_line_1_patterns =
-      GetMatchPatterns("BETWEEN_STREETS_LINE_1", page_language, pattern_source);
+      GetMatchPatterns("BETWEEN_STREETS_LINE_1", context);
   base::span<const MatchPatternRef> between_streets_line_2_patterns =
-      GetMatchPatterns("BETWEEN_STREETS_LINE_2", page_language, pattern_source);
+      GetMatchPatterns("BETWEEN_STREETS_LINE_2", context);
 
   AutofillField* old_street_location = street_location_;
   AutofillField* old_street_name = street_name_;
@@ -400,7 +399,7 @@
   AutofillField* old_zip4 = zip4_;
   AutofillField* old_apartment_number = apartment_number_;
 
-  AddressCountryCode country_code = AddressCountryCode(client_country.value());
+  AddressCountryCode country_code(context.client_country.value());
 
   while (!scanner->IsEnd()) {
     // We look for street location before street name, because the name/label of
@@ -413,8 +412,8 @@
         // street location and address overflow fields.
         base::FeatureList::IsEnabled(
             features::kAutofillEnableParsingOfStreetLocation) &&
-        client_country == GeoIpCountryCode("MX") &&
-        ParseFieldSpecifics(scanner, kStreetLocationRe,
+        context.client_country == GeoIpCountryCode("MX") &&
+        ParseFieldSpecifics(context, scanner, kStreetLocationRe,
                             kStreetLocationMatchType, street_location_patterns,
                             &street_location_,
                             {log_manager_, "kStreetLocationRe"})) {
@@ -424,14 +423,14 @@
     // TODO(crbug.com/1474308) Factor out these ParseFieldSpecifics into
     // ParseStreetName and similar functions.
     if (!street_name_ && !street_location_ &&
-        ParseFieldSpecifics(scanner, kStreetNameRe,
+        ParseFieldSpecifics(context, scanner, kStreetNameRe,
                             kDefaultMatchParamsWith<MatchFieldType::kSearch>,
                             street_name_patterns, &street_name_,
                             {log_manager_, "kStreetNameRe"})) {
       continue;
     }
 
-    if (ParseZipCode(scanner, page_language, pattern_source)) {
+    if (ParseZipCode(context, scanner)) {
       continue;
     }
     if (!(between_streets_or_landmark_ || between_streets_ ||
@@ -440,7 +439,7 @@
             features::kAutofillEnableSupportForBetweenStreetsOrLandmark) &&
         i18n_model_definition::IsTypeEnabledForCountry(
             ADDRESS_HOME_BETWEEN_STREETS_OR_LANDMARK, country_code) &&
-        ParseFieldSpecifics(scanner, kBetweenStreetsOrLandmarkRe,
+        ParseFieldSpecifics(context, scanner, kBetweenStreetsOrLandmarkRe,
                             kBetweenStreetsOrLandmarkMatchType,
                             between_streets_or_landmark_patterns,
                             &between_streets_or_landmark_,
@@ -453,10 +452,11 @@
             features::kAutofillEnableSupportForAddressOverflowAndLandmark) &&
         i18n_model_definition::IsTypeEnabledForCountry(
             ADDRESS_HOME_OVERFLOW_AND_LANDMARK, country_code) &&
-        ParseFieldSpecifics(
-            scanner, kOverflowAndLandmarkRe, kOverflowAndLandmarkMatchType,
-            overflow_and_landmark_patterns, &overflow_and_landmark_,
-            {log_manager_, "kOverflowAndLandmarkRe"})) {
+        ParseFieldSpecifics(context, scanner, kOverflowAndLandmarkRe,
+                            kOverflowAndLandmarkMatchType,
+                            overflow_and_landmark_patterns,
+                            &overflow_and_landmark_,
+                            {log_manager_, "kOverflowAndLandmarkRe"})) {
       continue;
     }
 
@@ -468,14 +468,14 @@
             features::kAutofillEnableSupportForAddressOverflow) &&
         i18n_model_definition::IsTypeEnabledForCountry(ADDRESS_HOME_OVERFLOW,
                                                        country_code) &&
-        ParseFieldSpecifics(scanner, kOverflowRe, kOverflowMatchType,
+        ParseFieldSpecifics(context, scanner, kOverflowRe, kOverflowMatchType,
                             overflow_patterns, &overflow_,
                             {log_manager_, "kOverflowRe"})) {
       continue;
     }
 
     if (!house_number_ && !street_location_ &&
-        ParseFieldSpecifics(scanner, kHouseNumberRe,
+        ParseFieldSpecifics(context, scanner, kHouseNumberRe,
                             kDefaultMatchParamsWith<MatchFieldType::kNumber,
                                                     MatchFieldType::kTelephone>,
                             house_number_patterns, &house_number_,
@@ -489,7 +489,7 @@
         !apartment_number_ &&
         i18n_model_definition::IsTypeEnabledForCountry(ADDRESS_HOME_APT_NUM,
                                                        country_code) &&
-        ParseFieldSpecifics(scanner, kApartmentNumberRe,
+        ParseFieldSpecifics(context, scanner, kApartmentNumberRe,
                             kDefaultMatchParamsWith<MatchFieldType::kNumber,
                                                     MatchFieldType::kTelephone>,
                             apartment_number_patterns, &apartment_number_,
@@ -502,7 +502,7 @@
         i18n_model_definition::IsTypeEnabledForCountry(
             ADDRESS_HOME_BETWEEN_STREETS, country_code)) {
       if (!between_streets_ && !between_streets_line_1_ &&
-          ParseFieldSpecifics(scanner, kBetweenStreetsRe,
+          ParseFieldSpecifics(context, scanner, kBetweenStreetsRe,
                               kBetweenStreetsMatchType,
                               between_streets_patterns, &between_streets_,
                               {log_manager_, "kBetweenStreetsRe"})) {
@@ -510,19 +510,21 @@
       }
 
       if (!between_streets_line_1_ &&
-          ParseFieldSpecifics(
-              scanner, kBetweenStreetsLine1Re, kBetweenStreetsMatchType,
-              between_streets_line_1_patterns, &between_streets_line_1_,
-              {log_manager_, "kBetweenStreetsLine1Re"})) {
+          ParseFieldSpecifics(context, scanner, kBetweenStreetsLine1Re,
+                              kBetweenStreetsMatchType,
+                              between_streets_line_1_patterns,
+                              &between_streets_line_1_,
+                              {log_manager_, "kBetweenStreetsLine1Re"})) {
         continue;
       }
 
       if ((between_streets_ || between_streets_line_1_) &&
           !between_streets_line_2_ &&
-          ParseFieldSpecifics(
-              scanner, kBetweenStreetsLine2Re, kBetweenStreetsMatchType,
-              between_streets_line_2_patterns, &between_streets_line_2_,
-              {log_manager_, "kBetweenStreetsLine2Re"})) {
+          ParseFieldSpecifics(context, scanner, kBetweenStreetsLine2Re,
+                              kBetweenStreetsMatchType,
+                              between_streets_line_2_patterns,
+                              &between_streets_line_2_,
+                              {log_manager_, "kBetweenStreetsLine2Re"})) {
         continue;
       }
     }
@@ -559,10 +561,8 @@
   return false;
 }
 
-bool AddressField::ParseAddress(AutofillScanner* scanner,
-                                const GeoIpCountryCode& client_country,
-                                const LanguageCode& page_language,
-                                PatternSource pattern_source) {
+bool AddressField::ParseAddress(ParsingContext& context,
+                                AutofillScanner* scanner) {
   // The following if-statements ensure in particular that we don't try to parse
   // a form as an address-line 1, 2, 3 form because we have collected enough
   // evidence that the current form is a structured form. If structured form
@@ -574,21 +574,18 @@
 
   // Do not inline these calls: After passing an address field sequence, there
   // might be an additional address line 2 to parse afterwards.
-  bool has_field_sequence = ParseAddressFieldSequence(
-      scanner, client_country, page_language, pattern_source);
+  bool has_field_sequence = ParseAddressFieldSequence(context, scanner);
   if (base::FeatureList::IsEnabled(
           features::kAutofillStructuredFieldsDisableAddressLines) &&
       has_field_sequence) {
     return true;
   }
-  bool has_address_lines =
-      ParseAddressLines(scanner, page_language, pattern_source);
+  bool has_address_lines = ParseAddressLines(context, scanner);
   return has_field_sequence || has_address_lines;
 }
 
-bool AddressField::ParseAddressLines(AutofillScanner* scanner,
-                                     const LanguageCode& page_language,
-                                     PatternSource pattern_source) {
+bool AddressField::ParseAddressLines(ParsingContext& context,
+                                     AutofillScanner* scanner) {
   // We only match the string "address" in page text, not in element names,
   // because sometimes every element in a group of address fields will have
   // a name containing the string "address"; for example, on the page
@@ -604,7 +601,7 @@
   std::u16string label_pattern = kAddressLine1LabelRe;
 
   base::span<const MatchPatternRef> address_line1_patterns =
-      GetMatchPatterns("ADDRESS_LINE_1", page_language, pattern_source);
+      GetMatchPatterns("ADDRESS_LINE_1", context);
 
   // TODO(crbug.com/1121990): Remove duplicate calls when launching
   // AutofillParsingPatternProvider. The old code calls ParseFieldSpecifics()
@@ -613,17 +610,17 @@
   // Address line 1 is skipped if a |street_name_|, |house_number_| combination
   // is present.
   if (!(street_name_ && house_number_) &&
-      !ParseFieldSpecifics(scanner, pattern,
+      !ParseFieldSpecifics(context, scanner, pattern,
                            kDefaultMatchParamsWith<MatchFieldType::kSearch>,
                            address_line1_patterns, &address1_,
                            {log_manager_, "kAddressLine1Re"}) &&
       !ParseFieldSpecifics(
-          scanner, label_pattern,
+          context, scanner, label_pattern,
           MatchParams({MatchAttribute::kLabel},
                       {MatchFieldType::kSearch, MatchFieldType::kText}),
           address_line1_patterns, &address1_,
           {log_manager_, "kAddressLine1LabelRe"}) &&
-      !ParseFieldSpecifics(scanner, pattern,
+      !ParseFieldSpecifics(context, scanner, pattern,
                            kDefaultMatchParamsWith<MatchFieldType::kSearch,
                                                    MatchFieldType::kTextArea>,
                            address_line1_patterns, &street_address_,
@@ -632,7 +629,7 @@
                              return WithFieldType(p, MatchFieldType::kTextArea);
                            }) &&
       !ParseFieldSpecifics(
-          scanner, label_pattern,
+          context, scanner, label_pattern,
           MatchParams({MatchAttribute::kLabel},
                       {MatchFieldType::kSearch, MatchFieldType::kTextArea}),
           address_line1_patterns, &street_address_,
@@ -652,27 +649,28 @@
   label_pattern = kAddressLine2LabelRe;
 
   base::span<const MatchPatternRef> address_line2_patterns =
-      GetMatchPatterns("ADDRESS_LINE_2", page_language, pattern_source);
+      GetMatchPatterns("ADDRESS_LINE_2", context);
 
-  if (!ParseField(scanner, pattern, address_line2_patterns, &address2_,
+  if (!ParseField(context, scanner, pattern, address_line2_patterns, &address2_,
                   {log_manager_, "kAddressLine2Re"}) &&
       !ParseFieldSpecifics(
-          scanner, label_pattern,
+          context, scanner, label_pattern,
           MatchParams({MatchAttribute::kLabel}, {MatchFieldType::kText}),
           address_line2_patterns, &address2_,
-          {log_manager_, "kAddressLine2LabelRe"}))
+          {log_manager_, "kAddressLine2LabelRe"})) {
     return true;
+  }
 
   base::span<const MatchPatternRef> address_line_extra_patterns =
-      GetMatchPatterns("ADDRESS_LINE_EXTRA", page_language, pattern_source);
+      GetMatchPatterns("ADDRESS_LINE_EXTRA", context);
 
   // Optionally parse address line 3. This uses the same label regexp as
   // address 2 above.
   pattern = kAddressLinesExtraRe;
-  if (!ParseField(scanner, pattern, address_line_extra_patterns, &address3_,
-                  {log_manager_, "kAddressLinesExtraRe"}) &&
+  if (!ParseField(context, scanner, pattern, address_line_extra_patterns,
+                  &address3_, {log_manager_, "kAddressLinesExtraRe"}) &&
       !ParseFieldSpecifics(
-          scanner, label_pattern,
+          context, scanner, label_pattern,
           MatchParams({MatchAttribute::kLabel}, {MatchFieldType::kText}),
           address_line2_patterns, &address3_,
           {log_manager_, "kAddressLine2LabelRe"})) {
@@ -685,25 +683,24 @@
   // Since these are rare, don't bother considering unlabeled lines as extra
   // address lines.
   pattern = kAddressLinesExtraRe;
-  while (ParseField(scanner, pattern, address_line_extra_patterns, nullptr,
-                    {log_manager_, "kAddressLinesExtraRe"})) {
+  while (ParseField(context, scanner, pattern, address_line_extra_patterns,
+                    nullptr, {log_manager_, "kAddressLinesExtraRe"})) {
     // Consumed a surplus line, try for another.
   }
   return true;
 }
 
-bool AddressField::ParseZipCode(AutofillScanner* scanner,
-                                const LanguageCode& page_language,
-                                PatternSource pattern_source) {
+bool AddressField::ParseZipCode(ParsingContext& context,
+                                AutofillScanner* scanner) {
   if (zip_)
     return false;
 
   base::span<const MatchPatternRef> zip_code_patterns =
-      GetMatchPatterns("ZIP_CODE", page_language, pattern_source);
+      GetMatchPatterns("ZIP_CODE", context);
 
   base::span<const MatchPatternRef> four_digit_zip_code_patterns =
-      GetMatchPatterns("ZIP_4", page_language, pattern_source);
-  if (!ParseFieldSpecifics(scanner, kZipCodeRe, kZipCodeMatchType,
+      GetMatchPatterns("ZIP_4", context);
+  if (!ParseFieldSpecifics(context, scanner, kZipCodeRe, kZipCodeMatchType,
                            zip_code_patterns, &zip_,
                            {log_manager_, "kZipCodeRe"})) {
     return false;
@@ -711,38 +708,38 @@
 
   // Look for a zip+4, whose field name will also often contain
   // the substring "zip".
-  ParseFieldSpecifics(scanner, kZip4Re, kZipCodeMatchType,
+  ParseFieldSpecifics(context, scanner, kZip4Re, kZipCodeMatchType,
                       four_digit_zip_code_patterns, &zip4_,
                       {log_manager_, "kZip4Re"});
   return true;
 }
 
-bool AddressField::ParseCity(AutofillScanner* scanner,
-                             const LanguageCode& page_language,
-                             PatternSource pattern_source) {
+bool AddressField::ParseCity(ParsingContext& context,
+                             AutofillScanner* scanner) {
   if (city_)
     return false;
 
   base::span<const MatchPatternRef> city_patterns =
-      GetMatchPatterns("CITY", page_language, pattern_source);
-  return ParseFieldSpecifics(scanner, kCityRe, kCityMatchType, city_patterns,
-                             &city_, {log_manager_, "kCityRe"});
+      GetMatchPatterns("CITY", context);
+  return ParseFieldSpecifics(context, scanner, kCityRe, kCityMatchType,
+                             city_patterns, &city_, {log_manager_, "kCityRe"});
 }
 
-bool AddressField::ParseState(AutofillScanner* scanner,
-                              const LanguageCode& page_language,
-                              PatternSource pattern_source) {
+bool AddressField::ParseState(ParsingContext& context,
+                              AutofillScanner* scanner) {
   if (state_)
     return false;
 
   base::span<const MatchPatternRef> patterns_state =
-      GetMatchPatterns("STATE", page_language, pattern_source);
-  return ParseFieldSpecifics(scanner, kStateRe, kStateMatchType, patterns_state,
-                             &state_, {log_manager_, "kStateRe"});
+      GetMatchPatterns("STATE", context);
+  return ParseFieldSpecifics(context, scanner, kStateRe, kStateMatchType,
+                             patterns_state, &state_,
+                             {log_manager_, "kStateRe"});
 }
 
 // static
 AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelSeparately(
+    ParsingContext& context,
     AutofillScanner* scanner,
     const std::u16string& pattern,
     MatchParams match_type,
@@ -755,14 +752,16 @@
   raw_ptr<AutofillField> cur_match = nullptr;
   size_t saved_cursor = scanner->SaveCursor();
   bool parsed_name = ParseFieldSpecifics(
-      scanner, pattern, WithoutAttribute(match_type, MatchAttribute::kLabel),
-      patterns, &cur_match, logging, [](const MatchingPattern& p) {
+      context, scanner, pattern,
+      WithoutAttribute(match_type, MatchAttribute::kLabel), patterns,
+      &cur_match, logging, [](const MatchingPattern& p) {
         return WithoutAttribute(p, MatchAttribute::kLabel);
       });
   scanner->RewindTo(saved_cursor);
   bool parsed_label = ParseFieldSpecifics(
-      scanner, pattern, WithoutAttribute(match_type, MatchAttribute::kName),
-      patterns, &cur_match, logging, [](const MatchingPattern& p) {
+      context, scanner, pattern,
+      WithoutAttribute(match_type, MatchAttribute::kName), patterns, &cur_match,
+      logging, [](const MatchingPattern& p) {
         return WithoutAttribute(p, MatchAttribute::kName);
       });
   if (parsed_name && parsed_label) {
@@ -779,41 +778,35 @@
   return RESULT_MATCH_NONE;
 }
 
-bool AddressField::ParseAddressField(AutofillScanner* scanner,
-                                     const GeoIpCountryCode& client_country,
-                                     const LanguageCode& page_language,
-                                     PatternSource pattern_source) {
+bool AddressField::ParseAddressField(ParsingContext& context,
+                                     AutofillScanner* scanner) {
   // The |scanner| is not pointing at a field.
   if (scanner->IsEnd())
     return false;
 
   // Check for matches to both the name and the label.
   ParseNameLabelResult dependent_locality_result =
-      ParseNameAndLabelForDependentLocality(scanner, page_language,
-                                            pattern_source);
+      ParseNameAndLabelForDependentLocality(context, scanner);
   if (dependent_locality_result == RESULT_MATCH_NAME_LABEL)
     return true;
-  ParseNameLabelResult city_result =
-      ParseNameAndLabelForCity(scanner, page_language, pattern_source);
+  ParseNameLabelResult city_result = ParseNameAndLabelForCity(context, scanner);
   if (city_result == RESULT_MATCH_NAME_LABEL)
     return true;
   ParseNameLabelResult state_result =
-      ParseNameAndLabelForState(scanner, page_language, pattern_source);
+      ParseNameAndLabelForState(context, scanner);
   if (state_result == RESULT_MATCH_NAME_LABEL)
     return true;
   ParseNameLabelResult country_result =
-      ParseNameAndLabelForCountry(scanner, page_language, pattern_source);
+      ParseNameAndLabelForCountry(context, scanner);
   if (country_result == RESULT_MATCH_NAME_LABEL)
     return true;
   ParseNameLabelResult between_streets_or_landmark_result =
-      ParseNameAndLabelForBetweenStreetsOrLandmark(
-          scanner, client_country, page_language, pattern_source);
+      ParseNameAndLabelForBetweenStreetsOrLandmark(context, scanner);
   if (between_streets_or_landmark_result == RESULT_MATCH_NAME_LABEL) {
     return true;
   }
   ParseNameLabelResult overflow_and_landmark_result =
-      ParseNameAndLabelForOverflowAndLandmark(scanner, client_country,
-                                              page_language, pattern_source);
+      ParseNameAndLabelForOverflowAndLandmark(context, scanner);
   if (overflow_and_landmark_result == RESULT_MATCH_NAME_LABEL) {
     return true;
   }
@@ -822,35 +815,33 @@
   // will match the "overflow" in the label and name. The function would
   // exit here. Instead of later recognizing that "Complemento e referência"
   // points to a different type.
-  ParseNameLabelResult overflow_result = ParseNameAndLabelForOverflow(
-      scanner, client_country, page_language, pattern_source);
+  ParseNameLabelResult overflow_result =
+      ParseNameAndLabelForOverflow(context, scanner);
   if (overflow_result == RESULT_MATCH_NAME_LABEL) {
     return true;
   }
-  ParseNameLabelResult landmark_result = ParseNameAndLabelForLandmark(
-      scanner, client_country, page_language, pattern_source);
+  ParseNameLabelResult landmark_result =
+      ParseNameAndLabelForLandmark(context, scanner);
   if (landmark_result == RESULT_MATCH_NAME_LABEL) {
     return true;
   }
   ParseNameLabelResult between_streets_result =
-      ParseNameAndLabelForBetweenStreets(scanner, client_country, page_language,
-                                         pattern_source);
+      ParseNameAndLabelForBetweenStreets(context, scanner);
   if (between_streets_result == RESULT_MATCH_NAME_LABEL) {
     return true;
   }
   ParseNameLabelResult between_street_lines12_result =
-      ParseNameAndLabelForBetweenStreetsLines12(scanner, client_country,
-                                                page_language, pattern_source);
+      ParseNameAndLabelForBetweenStreetsLines12(context, scanner);
   if (between_street_lines12_result == RESULT_MATCH_NAME_LABEL) {
     return true;
   }
-  ParseNameLabelResult admin_level2_result = ParseNameAndLabelForAdminLevel2(
-      scanner, client_country, page_language, pattern_source);
+  ParseNameLabelResult admin_level2_result =
+      ParseNameAndLabelForAdminLevel2(context, scanner);
   if (admin_level2_result == RESULT_MATCH_NAME_LABEL) {
     return true;
   }
   ParseNameLabelResult zip_result =
-      ParseNameAndLabelForZipCode(scanner, page_language, pattern_source);
+      ParseNameAndLabelForZipCode(context, scanner);
   if (zip_result == RESULT_MATCH_NAME_LABEL)
     return true;
 
@@ -902,7 +893,7 @@
       return SetFieldAndAdvanceCursor(scanner, &admin_level2_);
     }
     if (zip_result != RESULT_MATCH_NONE)
-      return ParseZipCode(scanner, page_language, pattern_source);
+      return ParseZipCode(context, scanner);
   }
 
   // If there is a clash between the country and the state, set the type of
@@ -918,11 +909,11 @@
   // name has a misleading value (e.g. in TR the province field is named "city",
   // in MX the input field for "Municipio/Delegación" is sometimes named "city"
   // even though that should be mapped to a "Cuidad").
-  if (page_language == LanguageCode("tr") &&
+  if (context.page_language == LanguageCode("tr") &&
       base::FeatureList::IsEnabled(
           features::kAutofillEnableLabelPrecedenceForTurkishAddresses)) {
     std::swap(results_to_match[0], results_to_match[1]);
-  } else if (client_country == GeoIpCountryCode("MX") &&
+  } else if (context.client_country == GeoIpCountryCode("MX") &&
              base::FeatureList::IsEnabled(
                  features::kAutofillPreferLabelsInSomeCountries)) {
     // We may want to consider whether we unify this logic with the previous
@@ -969,39 +960,38 @@
       return SetFieldAndAdvanceCursor(scanner, &admin_level2_);
     }
     if (zip_result == result)
-      return ParseZipCode(scanner, page_language, pattern_source);
+      return ParseZipCode(context, scanner);
   }
 
   return false;
 }
 
 AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForZipCode(
-    AutofillScanner* scanner,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
+    ParsingContext& context,
+    AutofillScanner* scanner) {
   if (zip_)
     return RESULT_MATCH_NONE;
 
   base::span<const MatchPatternRef> zip_code_patterns =
-      GetMatchPatterns("ZIP_CODE", page_language, pattern_source);
+      GetMatchPatterns("ZIP_CODE", context);
 
   base::span<const MatchPatternRef> four_digit_zip_code_patterns =
-      GetMatchPatterns("ZIP_4", page_language, pattern_source);
+      GetMatchPatterns("ZIP_4", context);
 
   ParseNameLabelResult result = ParseNameAndLabelSeparately(
-      scanner, kZipCodeRe, kZipCodeMatchType, zip_code_patterns, &zip_,
+      context, scanner, kZipCodeRe, kZipCodeMatchType, zip_code_patterns, &zip_,
       {log_manager_, "kZipCodeRe"});
 
   if (result != RESULT_MATCH_NAME_LABEL || scanner->IsEnd())
     return result;
 
   size_t saved_cursor = scanner->SaveCursor();
-  bool found_non_zip4 = ParseCity(scanner, page_language, pattern_source);
+  bool found_non_zip4 = ParseCity(context, scanner);
   if (found_non_zip4)
     city_ = nullptr;
   scanner->RewindTo(saved_cursor);
   if (!found_non_zip4) {
-    found_non_zip4 = ParseState(scanner, page_language, pattern_source);
+    found_non_zip4 = ParseState(context, scanner);
     if (found_non_zip4)
       state_ = nullptr;
     scanner->RewindTo(saved_cursor);
@@ -1010,7 +1000,7 @@
   if (!found_non_zip4) {
     // Look for a zip+4, whose field name will also often contain
     // the substring "zip".
-    ParseFieldSpecifics(scanner, kZip4Re, kZipCodeMatchType,
+    ParseFieldSpecifics(context, scanner, kZip4Re, kZipCodeMatchType,
                         four_digit_zip_code_patterns, &zip4_,
                         {log_manager_, "kZip4Re"});
   }
@@ -1018,10 +1008,8 @@
 }
 
 AddressField::ParseNameLabelResult
-AddressField::ParseNameAndLabelForDependentLocality(
-    AutofillScanner* scanner,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
+AddressField::ParseNameAndLabelForDependentLocality(ParsingContext& context,
+                                                    AutofillScanner* scanner) {
   const bool is_enabled_dependent_locality_parsing =
       base::FeatureList::IsEnabled(
           features::kAutofillEnableDependentLocalityParsing);
@@ -1030,57 +1018,54 @@
     return RESULT_MATCH_NONE;
 
   base::span<const MatchPatternRef> dependent_locality_patterns =
-      GetMatchPatterns("ADDRESS_HOME_DEPENDENT_LOCALITY", page_language,
-                       pattern_source);
+      GetMatchPatterns("ADDRESS_HOME_DEPENDENT_LOCALITY", context.page_language,
+                       context.pattern_source);
   return ParseNameAndLabelSeparately(
-      scanner, kDependentLocalityRe, kDependentLocalityMatchType,
+      context, scanner, kDependentLocalityRe, kDependentLocalityMatchType,
       dependent_locality_patterns, &dependent_locality_,
       {log_manager_, "kDependentLocalityRe"});
 }
 
 AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForCity(
-    AutofillScanner* scanner,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
+    ParsingContext& context,
+    AutofillScanner* scanner) {
   if (city_)
     return RESULT_MATCH_NONE;
 
   base::span<const MatchPatternRef> city_patterns =
-      GetMatchPatterns("CITY", page_language, pattern_source);
-  return ParseNameAndLabelSeparately(scanner, kCityRe, kCityMatchType,
+      GetMatchPatterns("CITY", context);
+  return ParseNameAndLabelSeparately(context, scanner, kCityRe, kCityMatchType,
                                      city_patterns, &city_,
                                      {log_manager_, "kCityRe"});
 }
 
 AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForState(
-    AutofillScanner* scanner,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
+    ParsingContext& context,
+    AutofillScanner* scanner) {
   if (state_)
     return RESULT_MATCH_NONE;
 
   base::span<const MatchPatternRef> patterns_state =
-      GetMatchPatterns("STATE", page_language, pattern_source);
-  return ParseNameAndLabelSeparately(scanner, kStateRe, kStateMatchType,
-                                     patterns_state, &state_,
+      GetMatchPatterns("STATE", context);
+  return ParseNameAndLabelSeparately(context, scanner, kStateRe,
+                                     kStateMatchType, patterns_state, &state_,
                                      {log_manager_, "kStateRe"});
 }
 
 AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForCountry(
-    AutofillScanner* scanner,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
+    ParsingContext& context,
+    AutofillScanner* scanner) {
   if (country_)
     return RESULT_MATCH_NONE;
 
   base::span<const MatchPatternRef> country_patterns =
-      GetMatchPatterns("COUNTRY", page_language, pattern_source);
+      GetMatchPatterns("COUNTRY", context);
 
   base::span<const MatchPatternRef> country_location_patterns =
-      GetMatchPatterns("COUNTRY_LOCATION", page_language, pattern_source);
+      GetMatchPatterns("COUNTRY_LOCATION", context);
 
   ParseNameLabelResult country_result = ParseNameAndLabelSeparately(
-      scanner, kCountryRe,
+      context, scanner, kCountryRe,
       kDefaultMatchParamsWith<MatchFieldType::kSelect, MatchFieldType::kSearch>,
       country_patterns, &country_, {log_manager_, "kCountryRe"});
   if (country_result != RESULT_MATCH_NONE)
@@ -1089,7 +1074,7 @@
   // The occasional page (e.g. google account registration page) calls this a
   // "location". However, this only makes sense for select tags.
   return ParseNameAndLabelSeparately(
-      scanner, kCountryLocationRe,
+      context, scanner, kCountryLocationRe,
       MatchParams({MatchAttribute::kLabel, MatchAttribute::kName},
                   {MatchFieldType::kSelect, MatchFieldType::kSearch}),
       country_location_patterns, &country_,
@@ -1098,11 +1083,9 @@
 
 AddressField::ParseNameLabelResult
 AddressField::ParseNameAndLabelForBetweenStreetsOrLandmark(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
-  AddressCountryCode country_code = AddressCountryCode(client_country.value());
+    ParsingContext& context,
+    AutofillScanner* scanner) {
+  AddressCountryCode country_code(context.client_country.value());
   if (between_streets_or_landmark_ || landmark_ || between_streets_ ||
       between_streets_line_1_ || between_streets_line_2_ ||
       !base::FeatureList::IsEnabled(
@@ -1113,11 +1096,11 @@
   }
 
   base::span<const MatchPatternRef> between_streets_or_landmark_patterns =
-      GetMatchPatterns("BETWEEN_STREETS_OR_LANDMARK", page_language,
-                       pattern_source);
+      GetMatchPatterns("BETWEEN_STREETS_OR_LANDMARK", context);
   auto result = ParseNameAndLabelSeparately(
-      scanner, kBetweenStreetsOrLandmarkRe, kBetweenStreetsOrLandmarkMatchType,
-      between_streets_or_landmark_patterns, &between_streets_or_landmark_,
+      context, scanner, kBetweenStreetsOrLandmarkRe,
+      kBetweenStreetsOrLandmarkMatchType, between_streets_or_landmark_patterns,
+      &between_streets_or_landmark_,
       {log_manager_, "kBetweenStreetsOrLandmarkRe"});
 
   return result;
@@ -1125,11 +1108,9 @@
 
 AddressField::ParseNameLabelResult
 AddressField::ParseNameAndLabelForOverflowAndLandmark(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
-  AddressCountryCode country_code = AddressCountryCode(client_country.value());
+    ParsingContext& context,
+    AutofillScanner* scanner) {
+  AddressCountryCode country_code(context.client_country.value());
   //  TODO(crbug.com/1441904) Remove feature check when launched.
   if (overflow_and_landmark_ || overflow_ ||
       !base::FeatureList::IsEnabled(
@@ -1140,20 +1121,18 @@
   }
 
   base::span<const MatchPatternRef> overflow_and_landmark_patterns =
-      GetMatchPatterns("OVERFLOW_AND_LANDMARK", page_language, pattern_source);
+      GetMatchPatterns("OVERFLOW_AND_LANDMARK", context);
   auto result = ParseNameAndLabelSeparately(
-      scanner, kOverflowAndLandmarkRe, kOverflowAndLandmarkMatchType,
+      context, scanner, kOverflowAndLandmarkRe, kOverflowAndLandmarkMatchType,
       overflow_and_landmark_patterns, &overflow_and_landmark_,
       {log_manager_, "kOverflowAndLandmarkRe"});
   return result;
 }
 
 AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForOverflow(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
-  AddressCountryCode country_code = AddressCountryCode(client_country.value());
+    ParsingContext& context,
+    AutofillScanner* scanner) {
+  AddressCountryCode country_code(context.client_country.value());
   // TODO(crbug.com/1441904) Remove feature check when launched.
   if (overflow_and_landmark_ || overflow_ ||
       !base::FeatureList::IsEnabled(
@@ -1164,18 +1143,16 @@
   }
 
   base::span<const MatchPatternRef> overflow_patterns =
-      GetMatchPatterns("OVERFLOW", page_language, pattern_source);
-  return ParseNameAndLabelSeparately(scanner, kOverflowRe, kOverflowMatchType,
-                                     overflow_patterns, &overflow_,
-                                     {log_manager_, "kOverflowRe"});
+      GetMatchPatterns("OVERFLOW", context);
+  return ParseNameAndLabelSeparately(context, scanner, kOverflowRe,
+                                     kOverflowMatchType, overflow_patterns,
+                                     &overflow_, {log_manager_, "kOverflowRe"});
 }
 
 AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForLandmark(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
-  AddressCountryCode country_code = AddressCountryCode(client_country.value());
+    ParsingContext& context,
+    AutofillScanner* scanner) {
+  AddressCountryCode country_code(context.client_country.value());
   // TODO(crbug.com/1441904) Remove feature check when launched.
   if (landmark_ ||
       !base::FeatureList::IsEnabled(
@@ -1186,19 +1163,16 @@
   }
 
   base::span<const MatchPatternRef> landmark_patterns =
-      GetMatchPatterns("LANDMARK", page_language, pattern_source);
-  return ParseNameAndLabelSeparately(scanner, kLandmarkRe, kLandmarkMatchType,
-                                     landmark_patterns, &landmark_,
-                                     {log_manager_, "kLandmarkRe"});
+      GetMatchPatterns("LANDMARK", context);
+  return ParseNameAndLabelSeparately(context, scanner, kLandmarkRe,
+                                     kLandmarkMatchType, landmark_patterns,
+                                     &landmark_, {log_manager_, "kLandmarkRe"});
 }
 
 AddressField::ParseNameLabelResult
-AddressField::ParseNameAndLabelForBetweenStreets(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
-  AddressCountryCode country_code = AddressCountryCode(client_country.value());
+AddressField::ParseNameAndLabelForBetweenStreets(ParsingContext& context,
+                                                 AutofillScanner* scanner) {
+  AddressCountryCode country_code(context.client_country.value());
   // TODO(crbug.com/1441904) Remove feature check when launched.
   if (between_streets_ || between_streets_line_1_ ||
       !base::FeatureList::IsEnabled(
@@ -1209,20 +1183,18 @@
   }
 
   base::span<const MatchPatternRef> between_streets_patterns =
-      GetMatchPatterns("BETWEEN_STREETS", page_language, pattern_source);
+      GetMatchPatterns("BETWEEN_STREETS", context);
   return ParseNameAndLabelSeparately(
-      scanner, kBetweenStreetsRe, kBetweenStreetsMatchType,
+      context, scanner, kBetweenStreetsRe, kBetweenStreetsMatchType,
       between_streets_patterns, &between_streets_,
       {log_manager_, "kBetweenStreetsRe"});
 }
 
 AddressField::ParseNameLabelResult
 AddressField::ParseNameAndLabelForBetweenStreetsLines12(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
-  AddressCountryCode country_code = AddressCountryCode(client_country.value());
+    ParsingContext& context,
+    AutofillScanner* scanner) {
+  AddressCountryCode country_code(context.client_country.value());
   // TODO(crbug.com/1441904) Remove feature check when launched.
   if (between_streets_line_2_ ||
       !base::FeatureList::IsEnabled(
@@ -1234,18 +1206,18 @@
 
   if (!between_streets_line_1_) {
     base::span<const MatchPatternRef> between_streets_patterns_line_1 =
-        GetMatchPatterns("BETWEEN_STREETS_LINE_1", page_language,
-                         pattern_source);
+        GetMatchPatterns("BETWEEN_STREETS_LINE_1", context.page_language,
+                         context.pattern_source);
     return ParseNameAndLabelSeparately(
-        scanner, kBetweenStreetsLine1Re, kBetweenStreetsMatchType,
+        context, scanner, kBetweenStreetsLine1Re, kBetweenStreetsMatchType,
         between_streets_patterns_line_1, &between_streets_line_1_,
         {log_manager_, "kBetweenStreetsLine1Re"});
   } else if (!between_streets_line_2_) {
     base::span<const MatchPatternRef> between_streets_patterns_line_2 =
-        GetMatchPatterns("BETWEEN_STREETS_LINE_2", page_language,
-                         pattern_source);
+        GetMatchPatterns("BETWEEN_STREETS_LINE_2", context.page_language,
+                         context.pattern_source);
     return ParseNameAndLabelSeparately(
-        scanner, kBetweenStreetsLine2Re, kBetweenStreetsMatchType,
+        context, scanner, kBetweenStreetsLine2Re, kBetweenStreetsMatchType,
         between_streets_patterns_line_2, &between_streets_line_2_,
         {log_manager_, "kBetweenStreetsLine2Re"});
   }
@@ -1254,12 +1226,9 @@
 }
 
 AddressField::ParseNameLabelResult
-AddressField::ParseNameAndLabelForAdminLevel2(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
-  AddressCountryCode country_code = AddressCountryCode(client_country.value());
+AddressField::ParseNameAndLabelForAdminLevel2(ParsingContext& context,
+                                              AutofillScanner* scanner) {
+  AddressCountryCode country_code(context.client_country.value());
   // TODO(crbug.com/1441904) Remove feature check when launched.
   if (admin_level2_ ||
       !base::FeatureList::IsEnabled(
@@ -1270,10 +1239,10 @@
   }
 
   base::span<const MatchPatternRef> admin_level2_patterns =
-      GetMatchPatterns("ADMIN_LEVEL_2", page_language, pattern_source);
+      GetMatchPatterns("ADMIN_LEVEL_2", context);
   return ParseNameAndLabelSeparately(
-      scanner, kAdminLevel2Re, kAdminLevel2MatchType, admin_level2_patterns,
-      &admin_level2_, {log_manager_, "kAdminLevel2Re"});
+      context, scanner, kAdminLevel2Re, kAdminLevel2MatchType,
+      admin_level2_patterns, &admin_level2_, {log_manager_, "kAdminLevel2Re"});
 }
 
 bool AddressField::PossiblyAStructuredAddressForm() const {
diff --git a/components/autofill/core/browser/form_parsing/address_field.h b/components/autofill/core/browser/form_parsing/address_field.h
index 8e4daab..32107a4 100644
--- a/components/autofill/core/browser/form_parsing/address_field.h
+++ b/components/autofill/core/browser/form_parsing/address_field.h
@@ -26,12 +26,9 @@
 
 class AddressField : public FormField {
  public:
-  static std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                          AutofillScanner* scanner,
+                                          LogManager* log_manager);
 
   // Returns whether a stand-alone zip field is supported for `client_country`.
   // In some countries that's a prevalent UI (the user is first asked to enter
@@ -40,12 +37,9 @@
   // classifications. We may reevaluate that decision in the future.
   static bool IsStandaloneZipSupported(const GeoIpCountryCode& client_country);
 
-  static std::unique_ptr<FormField> ParseStandaloneZip(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FormField> ParseStandaloneZip(ParsingContext& context,
+                                                       AutofillScanner* scanner,
+                                                       LogManager* log_manager);
 
   AddressField(const AddressField&) = delete;
   AddressField& operator=(const AddressField&) = delete;
@@ -64,44 +58,26 @@
 
   explicit AddressField(LogManager* log_manager);
 
-  bool ParseCompany(AutofillScanner* scanner,
-                    const LanguageCode& page_language,
-                    PatternSource pattern_source);
+  bool ParseCompany(ParsingContext& context, AutofillScanner* scanner);
 
-  bool ParseAddress(AutofillScanner* scanner,
-                    const GeoIpCountryCode& client_country,
-                    const LanguageCode& page_language,
-                    PatternSource pattern_source);
+  bool ParseAddress(ParsingContext& context, AutofillScanner* scanner);
 
-  bool ParseAddressFieldSequence(AutofillScanner* scanner,
-                                 const GeoIpCountryCode& client_country,
-                                 const LanguageCode& page_language,
-                                 PatternSource pattern_source);
+  bool ParseAddressFieldSequence(ParsingContext& context,
+                                 AutofillScanner* scanner);
 
-  bool ParseAddressLines(AutofillScanner* scanner,
-                         const LanguageCode& page_language,
-                         PatternSource pattern_source);
+  bool ParseAddressLines(ParsingContext& context, AutofillScanner* scanner);
 
-  bool ParseZipCode(AutofillScanner* scanner,
-                    const LanguageCode& page_language,
-                    PatternSource pattern_source);
+  bool ParseZipCode(ParsingContext& context, AutofillScanner* scanner);
 
-  bool ParseCity(AutofillScanner* scanner,
-                 const LanguageCode& page_language,
-                 PatternSource pattern_source);
+  bool ParseCity(ParsingContext& context, AutofillScanner* scanner);
 
-  bool ParseState(AutofillScanner* scanner,
-                  const LanguageCode& page_language,
-                  PatternSource pattern_source);
+  bool ParseState(ParsingContext& context, AutofillScanner* scanner);
 
   // Parses the current field pointed to by |scanner|, if it exists, and tries
   // to determine if the field's type corresponds to one of the following:
   // dependent locality, city, state, country, zip, landmark, between streets,
   // admin level 2 or none of those.
-  bool ParseAddressField(AutofillScanner* scanner,
-                         const GeoIpCountryCode& client_country,
-                         const LanguageCode& page_language,
-                         PatternSource pattern_source);
+  bool ParseAddressField(ParsingContext& context, AutofillScanner* scanner);
 
   // Like ParseFieldSpecifics(), but applies |pattern| against the name and
   // label of the current field separately. If the return value is
@@ -109,6 +85,7 @@
   // it is non-NULL. Otherwise |scanner| does not advance and |match| does not
   // change.
   static ParseNameLabelResult ParseNameAndLabelSeparately(
+      ParsingContext& context,
       AutofillScanner* scanner,
       const std::u16string& pattern,
       MatchParams match_type,
@@ -119,75 +96,50 @@
   // Run matches on the name and label separately. If the return result is
   // RESULT_MATCH_NAME_LABEL, then |scanner| advances and the field is set.
   // Otherwise |scanner| rewinds and the field is cleared.
-  ParseNameLabelResult ParseNameAndLabelForZipCode(
-      AutofillScanner* scanner,
-      const LanguageCode& page_language,
-      PatternSource pattern_source);
+  ParseNameLabelResult ParseNameAndLabelForZipCode(ParsingContext& context,
+                                                   AutofillScanner* scanner);
 
   ParseNameLabelResult ParseNameAndLabelForDependentLocality(
-      AutofillScanner* scanner,
-      const LanguageCode& page_language,
-      PatternSource pattern_source);
+      ParsingContext& context,
+      AutofillScanner* scanner);
 
-  ParseNameLabelResult ParseNameAndLabelForCity(
-      AutofillScanner* scanner,
-      const LanguageCode& page_language,
-      PatternSource pattern_source);
+  ParseNameLabelResult ParseNameAndLabelForCity(ParsingContext& context,
+                                                AutofillScanner* scanner);
 
-  ParseNameLabelResult ParseNameAndLabelForCountry(
-      AutofillScanner* scanner,
-      const LanguageCode& page_language,
-      PatternSource pattern_source);
+  ParseNameLabelResult ParseNameAndLabelForCountry(ParsingContext& context,
+                                                   AutofillScanner* scanner);
 
-  ParseNameLabelResult ParseNameAndLabelForLandmark(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source);
+  ParseNameLabelResult ParseNameAndLabelForLandmark(ParsingContext& context,
+                                                    AutofillScanner* scanner);
 
   ParseNameLabelResult ParseNameAndLabelForBetweenStreets(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source);
+      ParsingContext& context,
+      AutofillScanner* scanner);
 
   // Run matches on the name and label for a field and sets
   // `between_streets_line_1_` and `between_streets_line_2_` respectively if a
   // match is found.
   ParseNameLabelResult ParseNameAndLabelForBetweenStreetsLines12(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source);
+      ParsingContext& context,
+      AutofillScanner* scanner);
 
   ParseNameLabelResult ParseNameAndLabelForAdminLevel2(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source);
+      ParsingContext& context,
+      AutofillScanner* scanner);
 
   ParseNameLabelResult ParseNameAndLabelForBetweenStreetsOrLandmark(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source);
+      ParsingContext& context,
+      AutofillScanner* scanner);
 
   ParseNameLabelResult ParseNameAndLabelForOverflowAndLandmark(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source);
+      ParsingContext& context,
+      AutofillScanner* scanner);
 
-  ParseNameLabelResult ParseNameAndLabelForOverflow(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source);
+  ParseNameLabelResult ParseNameAndLabelForOverflow(ParsingContext& context,
+                                                    AutofillScanner* scanner);
 
-  ParseNameLabelResult ParseNameAndLabelForState(
-      AutofillScanner* scanner,
-      const LanguageCode& page_language,
-      PatternSource pattern_source);
+  ParseNameLabelResult ParseNameAndLabelForState(ParsingContext& context,
+                                                 AutofillScanner* scanner);
 
   // Return true if the form being parsed shows an indication of being a
   // structured address form.
diff --git a/components/autofill/core/browser/form_parsing/address_field_unittest.cc b/components/autofill/core/browser/form_parsing/address_field_unittest.cc
index 36f1427..81299133 100644
--- a/components/autofill/core/browser/form_parsing/address_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/address_field_unittest.cc
@@ -24,11 +24,9 @@
   AddressFieldTest& operator=(const AddressFieldTest&) = delete;
 
  protected:
-  std::unique_ptr<FormField> Parse(AutofillScanner* scanner,
-                                   const GeoIpCountryCode& client_country,
-                                   const LanguageCode& page_language) override {
-    return AddressField::Parse(scanner, client_country, page_language,
-                               *GetActivePatternSource(),
+  std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                   AutofillScanner* scanner) override {
+    return AddressField::Parse(context, scanner,
                                /*log_manager=*/nullptr);
   }
 };
diff --git a/components/autofill/core/browser/form_parsing/birthdate_field.cc b/components/autofill/core/browser/form_parsing/birthdate_field.cc
index 86c50f5..3f980f8 100644
--- a/components/autofill/core/browser/form_parsing/birthdate_field.cc
+++ b/components/autofill/core/browser/form_parsing/birthdate_field.cc
@@ -35,12 +35,9 @@
     : day_(day), month_(month), year_(year) {}
 
 // static
-std::unique_ptr<FormField> BirthdateField::Parse(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
+std::unique_ptr<FormField> BirthdateField::Parse(ParsingContext& context,
+                                                 AutofillScanner* scanner,
+                                                 LogManager* log_manager) {
   // Currently only <select> elements are considered.
   raw_ptr<AutofillField> day = nullptr;
   raw_ptr<AutofillField> month = nullptr;
diff --git a/components/autofill/core/browser/form_parsing/birthdate_field.h b/components/autofill/core/browser/form_parsing/birthdate_field.h
index 5b393d3..92efb4b 100644
--- a/components/autofill/core/browser/form_parsing/birthdate_field.h
+++ b/components/autofill/core/browser/form_parsing/birthdate_field.h
@@ -18,12 +18,9 @@
 // reduce the number of false positive credit card expiration dates.
 class BirthdateField : public FormField {
  public:
-  static std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                          AutofillScanner* scanner,
+                                          LogManager* log_manager);
 
   BirthdateField(const BirthdateField&) = delete;
   BirthdateField& operator=(const BirthdateField&) = delete;
diff --git a/components/autofill/core/browser/form_parsing/birthdate_field_unittest.cc b/components/autofill/core/browser/form_parsing/birthdate_field_unittest.cc
index 103075b..d9bf004 100644
--- a/components/autofill/core/browser/form_parsing/birthdate_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/birthdate_field_unittest.cc
@@ -48,12 +48,9 @@
   BirthdateFieldTest& operator=(const BirthdateFieldTest&) = delete;
 
  protected:
-  std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language = LanguageCode("en")) override {
-    return BirthdateField::Parse(scanner, client_country, page_language,
-                                 *GetActivePatternSource(),
+  std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                   AutofillScanner* scanner) override {
+    return BirthdateField::Parse(context, scanner,
                                  /*log_manager=*/nullptr);
   }
 };
diff --git a/components/autofill/core/browser/form_parsing/credit_card_field.cc b/components/autofill/core/browser/form_parsing/credit_card_field.cc
index 1d2b13af..cb02bfa 100644
--- a/components/autofill/core/browser/form_parsing/credit_card_field.cc
+++ b/components/autofill/core/browser/form_parsing/credit_card_field.cc
@@ -33,6 +33,16 @@
 
 namespace {
 
+base::span<const MatchPatternRef> GetMatchPatterns(base::StringPiece name,
+                                                   ParsingContext& context) {
+  return GetMatchPatterns(name, context.page_language, context.pattern_source);
+}
+
+base::span<const MatchPatternRef> GetMatchPatterns(ServerFieldType type,
+                                                   ParsingContext& context) {
+  return GetMatchPatterns(type, context.page_language, context.pattern_source);
+}
+
 // Returns true if a field that has |max_length| can fit the data for a field of
 // |type|.
 bool FieldCanFitDataForFieldType(uint64_t max_length, FieldType type) {
@@ -59,14 +69,12 @@
 }  // namespace
 
 // static
-std::unique_ptr<FormField> CreditCardField::Parse(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
-  if (scanner->IsEnd())
+std::unique_ptr<FormField> CreditCardField::Parse(ParsingContext& context,
+                                                  AutofillScanner* scanner,
+                                                  LogManager* log_manager) {
+  if (scanner->IsEnd()) {
     return nullptr;
+  }
 
   auto credit_card_field = std::make_unique<CreditCardField>(log_manager);
   size_t saved_cursor = scanner->SaveCursor();
@@ -74,28 +82,28 @@
   bool cardholder_name_match_has_low_confidence = false;
 
   base::span<const MatchPatternRef> name_on_card_patterns =
-      GetMatchPatterns("NAME_ON_CARD", page_language, pattern_source);
+      GetMatchPatterns("NAME_ON_CARD", context);
 
   base::span<const MatchPatternRef> name_on_card_contextual_patterns =
-      GetMatchPatterns("NAME_ON_CARD_CONTEXTUAL", page_language,
-                       pattern_source);
+      GetMatchPatterns("NAME_ON_CARD_CONTEXTUAL", context);
 
   base::span<const MatchPatternRef> last_name_patterns =
-      GetMatchPatterns("LAST_NAME", page_language, pattern_source);
+      GetMatchPatterns("LAST_NAME", context);
 
-  base::span<const MatchPatternRef> cvc_patterns = GetMatchPatterns(
-      CREDIT_CARD_VERIFICATION_CODE, page_language, pattern_source);
+  base::span<const MatchPatternRef> cvc_patterns =
+      GetMatchPatterns(CREDIT_CARD_VERIFICATION_CODE, context);
 
   // Credit card fields can appear in many different orders.
   // We loop until no more credit card related fields are found, see |break| at
   // the bottom of the loop.
   for (int fields = 0; !scanner->IsEnd(); ++fields) {
     // Ignore gift card fields.
-    if (IsGiftCardField(scanner, log_manager, page_language, pattern_source))
+    if (IsGiftCardField(context, scanner, log_manager)) {
       break;
+    }
 
     if (!credit_card_field->cardholder_) {
-      if (ParseField(scanner, kNameOnCardRe, name_on_card_patterns,
+      if (ParseField(context, scanner, kNameOnCardRe, name_on_card_patterns,
                      &credit_card_field->cardholder_,
                      {log_manager, "kNameOnCardRe"})) {
         continue;
@@ -109,7 +117,7 @@
       // expiration date (which usually appears at the end).
 
       if (fields > 0 && !credit_card_field->expiration_month_ &&
-          ParseField(scanner, kNameOnCardContextualRe,
+          ParseField(context, scanner, kNameOnCardContextualRe,
                      name_on_card_contextual_patterns,
                      &credit_card_field->cardholder_,
                      {log_manager, "kNameOnCardContextualRe"})) {
@@ -122,7 +130,7 @@
       // and haven't yet parsed the expiration date (which usually appears at
       // the end).
       if (!credit_card_field->expiration_month_ &&
-          ParseField(scanner, kLastNameRe, last_name_patterns,
+          ParseField(context, scanner, kLastNameRe, last_name_patterns,
                      &credit_card_field->cardholder_last_,
                      {log_manager, "kLastNameRe"})) {
         continue;
@@ -151,7 +159,7 @@
                                 MatchFieldType::kPassword>;
 
     if (!credit_card_field->verification_ &&
-        ParseFieldSpecifics(scanner, kCardCvcRe, kMatchNumTelAndPwd,
+        ParseFieldSpecifics(context, scanner, kCardCvcRe, kMatchNumTelAndPwd,
                             cvc_patterns, &credit_card_field->verification_,
                             {log_manager, "kCardCvcRe"})) {
       // A couple of sites have multiple verification codes right after another.
@@ -166,8 +174,9 @@
         // Check if the previous field was a verification code.
         scanner->RewindTo(scanner->SaveCursor() - 2);
 
-        if (ParseFieldSpecifics(scanner, kCardCvcRe, kMatchNumTelAndPwd,
-                                cvc_patterns, &credit_card_field->verification_,
+        if (ParseFieldSpecifics(context, scanner, kCardCvcRe,
+                                kMatchNumTelAndPwd, cvc_patterns,
+                                &credit_card_field->verification_,
                                 {log_manager, "kCardCvcRe"})) {
           // Reset the current cvv (The verification parse overwrote it).
           credit_card_field->verification_ = saved_cvv;
@@ -190,8 +199,8 @@
     // doesn't have bad side effects.
     raw_ptr<AutofillField> current_number_field;
     base::span<const MatchPatternRef> patterns =
-        GetMatchPatterns(CREDIT_CARD_NUMBER, page_language, pattern_source);
-    if (ParseFieldSpecifics(scanner, kCardNumberRe, kMatchNumTelAndPwd,
+        GetMatchPatterns(CREDIT_CARD_NUMBER, context);
+    if (ParseFieldSpecifics(context, scanner, kCardNumberRe, kMatchNumTelAndPwd,
                             patterns, &current_number_field,
                             {log_manager, "kCardNumberRe"})) {
       credit_card_field->numbers_.push_back(current_number_field);
@@ -199,8 +208,7 @@
       continue;
     }
 
-    if (credit_card_field->ParseExpirationDate(scanner, log_manager,
-                                               page_language, pattern_source)) {
+    if (credit_card_field->ParseExpirationDate(context, scanner, log_manager)) {
       nb_unknown_fields = 0;
       continue;
     }
@@ -304,11 +312,9 @@
 }
 
 // static
-bool CreditCardField::LikelyCardYearSelectField(
-    AutofillScanner* scanner,
-    LogManager* log_manager,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
+bool CreditCardField::LikelyCardYearSelectField(ParsingContext* context,
+                                                AutofillScanner* scanner,
+                                                LogManager* log_manager) {
   if (scanner->IsEnd())
     return false;
 
@@ -330,10 +336,11 @@
 
   // Another way to eliminate days - filter out 'day' fields.
   base::span<const MatchPatternRef> day_patterns =
-      GetMatchPatterns("DAY", page_language, pattern_source);
+      GetMatchPatterns("DAY", *context);
   if (FormField::ParseFieldSpecifics(
-          scanner, kDayRe, kDefaultMatchParamsWith<MatchFieldType::kSelect>,
-          day_patterns, nullptr, {log_manager, "kDayRe"})) {
+          *context, scanner, kDayRe,
+          kDefaultMatchParamsWith<MatchFieldType::kSelect>, day_patterns,
+          nullptr, {log_manager, "kDayRe"})) {
     return false;
   }
 
@@ -408,10 +415,9 @@
 }
 
 // static
-bool CreditCardField::IsGiftCardField(AutofillScanner* scanner,
-                                      LogManager* log_manager,
-                                      const LanguageCode& page_language,
-                                      PatternSource pattern_source) {
+bool CreditCardField::IsGiftCardField(ParsingContext& context,
+                                      AutofillScanner* scanner,
+                                      LogManager* log_manager) {
   if (scanner->IsEnd())
     return false;
 
@@ -424,28 +430,28 @@
   size_t saved_cursor = scanner->SaveCursor();
 
   base::span<const MatchPatternRef> debit_cards_patterns =
-      GetMatchPatterns("DEBIT_CARD", page_language, pattern_source);
+      GetMatchPatterns("DEBIT_CARD", context);
 
   base::span<const MatchPatternRef> debit_gift_card_patterns =
-      GetMatchPatterns("DEBIT_GIFT_CARD", page_language, pattern_source);
+      GetMatchPatterns("DEBIT_GIFT_CARD", context);
 
   base::span<const MatchPatternRef> gift_card_patterns =
-      GetMatchPatterns("GIFT_CARD", page_language, pattern_source);
+      GetMatchPatterns("GIFT_CARD", context);
 
-  if (ParseFieldSpecifics(scanner, kDebitCardRe, kMatchFieldType,
+  if (ParseFieldSpecifics(context, scanner, kDebitCardRe, kMatchFieldType,
                           debit_cards_patterns, nullptr,
                           {log_manager, "kDebitCardRe"})) {
     scanner->RewindTo(saved_cursor);
     return false;
   }
-  if (ParseFieldSpecifics(scanner, kDebitGiftCardRe, kMatchFieldType,
+  if (ParseFieldSpecifics(context, scanner, kDebitGiftCardRe, kMatchFieldType,
                           debit_gift_card_patterns, nullptr,
                           {log_manager, "kDebitGiftCardRe"})) {
     scanner->RewindTo(saved_cursor);
     return false;
   }
 
-  return ParseFieldSpecifics(scanner, kGiftCardRe, kMatchFieldType,
+  return ParseFieldSpecifics(context, scanner, kGiftCardRe, kMatchFieldType,
                              gift_card_patterns, nullptr,
                              {log_manager, "kGiftCardRe"});
 }
@@ -523,10 +529,9 @@
   }
 }
 
-bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner,
-                                          LogManager* log_manager,
-                                          const LanguageCode& page_language,
-                                          PatternSource pattern_source) {
+bool CreditCardField::ParseExpirationDate(ParsingContext& context,
+                                          AutofillScanner* scanner,
+                                          LogManager* log_manager) {
   if (!expiration_date_ &&
       scanner->Cursor()->form_control_type == FormControlType::kInputMonth) {
     expiration_date_ = scanner->Cursor();
@@ -545,9 +550,8 @@
           scanner, {{&expiration_month_,
                      base::BindRepeating(&LikelyCardMonthSelectField, scanner)},
                     {&expiration_year_,
-                     base::BindRepeating(&LikelyCardYearSelectField, scanner,
-                                         log_manager, page_language,
-                                         pattern_source)}})) {
+                     base::BindRepeating(&LikelyCardYearSelectField, &context,
+                                         scanner, log_manager)}})) {
     return true;
   }
 
@@ -559,23 +563,21 @@
                               MatchFieldType::kSelect, MatchFieldType::kSearch>;
 
   base::span<const MatchPatternRef> cc_exp_month_patterns =
-      GetMatchPatterns(CREDIT_CARD_EXP_MONTH, page_language, pattern_source);
+      GetMatchPatterns(CREDIT_CARD_EXP_MONTH, context);
 
   base::span<const MatchPatternRef> cc_exp_year_patterns =
-      GetMatchPatterns("CREDIT_CARD_EXP_YEAR", page_language, pattern_source);
+      GetMatchPatterns("CREDIT_CARD_EXP_YEAR", context);
 
   base::span<const MatchPatternRef> cc_exp_month_before_year_patterns =
-      GetMatchPatterns("CREDIT_CARD_EXP_MONTH_BEFORE_YEAR", page_language,
-                       pattern_source);
+      GetMatchPatterns("CREDIT_CARD_EXP_MONTH_BEFORE_YEAR", context);
 
   base::span<const MatchPatternRef> cc_exp_year_after_month_patterns =
-      GetMatchPatterns("CREDIT_CARD_EXP_YEAR_AFTER_MONTH", page_language,
-                       pattern_source);
+      GetMatchPatterns("CREDIT_CARD_EXP_YEAR_AFTER_MONTH", context);
 
-  if (ParseFieldSpecifics(scanner, kExpirationMonthRe, kMatchCCType,
+  if (ParseFieldSpecifics(context, scanner, kExpirationMonthRe, kMatchCCType,
                           cc_exp_month_patterns, &expiration_month_,
                           {log_manager_, "kExpirationMonthRe"}) &&
-      ParseFieldSpecifics(scanner, kExpirationYearRe, kMatchCCType,
+      ParseFieldSpecifics(context, scanner, kExpirationYearRe, kMatchCCType,
                           cc_exp_year_patterns, &expiration_year_,
                           {log_manager_, "kExpirationYearRe"})) {
     return true;
@@ -590,12 +592,12 @@
           features::kAutofillEnableExpirationDateImprovements)
           ? u"^(yy|yyyy|aa|aaaa)$"
           : u"^(yy|yyyy)$";
-  if (ParseFieldSpecifics(scanner, u"^mm$", kMatchCCType,
+  if (ParseFieldSpecifics(context, scanner, u"^mm$", kMatchCCType,
                           cc_exp_month_before_year_patterns, &expiration_month_,
                           {log_manager_, "^mm$"}) &&
       ParseFieldSpecifics(
-          scanner, year_pattern, kMatchCCType, cc_exp_year_after_month_patterns,
-          &expiration_year_,
+          context, scanner, year_pattern, kMatchCCType,
+          cc_exp_year_after_month_patterns, &expiration_year_,
           {log_manager_, base::UTF16ToUTF8(year_pattern).c_str()})) {
     return true;
   }
@@ -612,10 +614,10 @@
 
   // Try to look for a 2-digit year expiration date.
   base::span<const MatchPatternRef> cc_exp_2digit_year_patterns =
-      GetMatchPatterns(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, page_language,
-                       pattern_source);
-  if (ParseFieldSpecifics(scanner, kExpirationDate2DigitYearRe, kMatchCCType,
-                          cc_exp_2digit_year_patterns, &expiration_date_,
+      GetMatchPatterns(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, context);
+  if (ParseFieldSpecifics(context, scanner, kExpirationDate2DigitYearRe,
+                          kMatchCCType, cc_exp_2digit_year_patterns,
+                          &expiration_date_,
                           {log_manager_, "kExpirationDate2DigitYearRe"})) {
     exp_year_type_ = CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR;
     expiration_month_ = nullptr;
@@ -624,8 +626,8 @@
 
   // Try to look for a generic expiration date field. (2 or 4 digit year)
   base::span<const MatchPatternRef> cc_exp_date_patterns =
-      GetMatchPatterns("CREDIT_CARD_EXP_DATE", page_language, pattern_source);
-  if (ParseFieldSpecifics(scanner, kExpirationDateRe, kMatchCCType,
+      GetMatchPatterns("CREDIT_CARD_EXP_DATE", context);
+  if (ParseFieldSpecifics(context, scanner, kExpirationDateRe, kMatchCCType,
                           cc_exp_date_patterns, &expiration_date_,
                           {log_manager_, "kExpirationDateRe"})) {
     // If such a field exists, but it cannot fit a 4-digit year expiration
@@ -641,12 +643,12 @@
 
   // Try to look for a 4-digit year expiration date.
   base::span<const MatchPatternRef> cc_exp_date_4_digit_year_patterns =
-      GetMatchPatterns(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, page_language,
-                       pattern_source);
+      GetMatchPatterns(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, context);
   if (FieldCanFitDataForFieldType(current_field_max_length,
                                   CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR) &&
-      ParseFieldSpecifics(scanner, kExpirationDate4DigitYearRe, kMatchCCType,
-                          cc_exp_date_4_digit_year_patterns, &expiration_date_,
+      ParseFieldSpecifics(context, scanner, kExpirationDate4DigitYearRe,
+                          kMatchCCType, cc_exp_date_4_digit_year_patterns,
+                          &expiration_date_,
                           {log_manager_, "kExpirationDate4DigitYearRe"})) {
     expiration_month_ = nullptr;
     return true;
diff --git a/components/autofill/core/browser/form_parsing/credit_card_field.h b/components/autofill/core/browser/form_parsing/credit_card_field.h
index b65e49ba..b57287a 100644
--- a/components/autofill/core/browser/form_parsing/credit_card_field.h
+++ b/components/autofill/core/browser/form_parsing/credit_card_field.h
@@ -30,12 +30,9 @@
   CreditCardField& operator=(const CreditCardField&) = delete;
 
   ~CreditCardField() override;
-  static std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                          AutofillScanner* scanner,
+                                          LogManager* log_manager);
 
   // Instructions for how to format an expiration date for a text field.
   struct ExpirationDateFormat {
@@ -94,10 +91,9 @@
   // <select> for a credit card. i.e. it contains the current year and
   // the next few years. |log_manager| is used to log any parsing details
   // to chrome://autofill-internals
-  static bool LikelyCardYearSelectField(AutofillScanner* scanner,
-                                        LogManager* log_manager,
-                                        const LanguageCode& page_language,
-                                        PatternSource pattern_source);
+  static bool LikelyCardYearSelectField(ParsingContext* context,
+                                        AutofillScanner* scanner,
+                                        LogManager* log_manager);
 
   // Returns true if |scanner| points to a <select> field that contains credit
   // card type options.
@@ -107,17 +103,15 @@
   // |scanner| advances if this returns true.
   // Prepaid debit cards do not count as gift cards, since they can be used like
   // a credit card.
-  static bool IsGiftCardField(AutofillScanner* scanner,
-                              LogManager* log_manager,
-                              const LanguageCode& page_language,
-                              PatternSource pattern_source);
+  static bool IsGiftCardField(ParsingContext& context,
+                              AutofillScanner* scanner,
+                              LogManager* log_manager);
 
   // Parses the expiration month/year/date fields. Returns true if it finds
   // something new.
-  bool ParseExpirationDate(AutofillScanner* scanner,
-                           LogManager* log_manager,
-                           const LanguageCode& page_language,
-                           PatternSource pattern_source);
+  bool ParseExpirationDate(ParsingContext& context,
+                           AutofillScanner* scanner,
+                           LogManager* log_manager);
 
   // For the combined expiration field we return |exp_year_type_|; otherwise if
   // |expiration_year_| is having year with |max_length| of 2-digits we return
diff --git a/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc b/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc
index 96956b63..d748961 100644
--- a/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc
@@ -88,22 +88,21 @@
   CreditCardFieldTestBase& operator=(const CreditCardFieldTestBase&) = delete;
 
  protected:
-  std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language = LanguageCode("us")) override {
-    return CreditCardField::Parse(scanner, client_country, page_language,
-                                  *GetActivePatternSource(), nullptr);
+  std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                   AutofillScanner* scanner) override {
+    return CreditCardField::Parse(context, scanner, nullptr);
   }
 
   // Runs multiple parsing attempts until the end of the form is reached.
   void ClassifyAndVerifyWithMultipleParses(
       const LanguageCode& page_language = LanguageCode("")) {
+    ParsingContext context(GeoIpCountryCode(""), page_language,
+                           *GetActivePatternSource());
     AutofillScanner scanner(list_);
     while (!scanner.IsEnd()) {
       // An empty page_language means the language is unknown and patterns of
       // all languages are used.
-      field_ = Parse(&scanner, GeoIpCountryCode(""), page_language);
+      field_ = Parse(context, &scanner);
       if (field_ == nullptr) {
         scanner.Advance();
       } else {
diff --git a/components/autofill/core/browser/form_parsing/email_field.cc b/components/autofill/core/browser/form_parsing/email_field.cc
index 41ffec0..4925139d 100644
--- a/components/autofill/core/browser/form_parsing/email_field.cc
+++ b/components/autofill/core/browser/form_parsing/email_field.cc
@@ -11,16 +11,13 @@
 namespace autofill {
 
 // static
-std::unique_ptr<FormField> EmailField::Parse(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
+std::unique_ptr<FormField> EmailField::Parse(ParsingContext& context,
+                                             AutofillScanner* scanner,
+                                             LogManager* log_manager) {
   raw_ptr<AutofillField> field;
-  base::span<const MatchPatternRef> email_patterns =
-      GetMatchPatterns("EMAIL_ADDRESS", page_language, pattern_source);
-  if (ParseFieldSpecifics(scanner, kEmailRe,
+  base::span<const MatchPatternRef> email_patterns = GetMatchPatterns(
+      "EMAIL_ADDRESS", context.page_language, context.pattern_source);
+  if (ParseFieldSpecifics(context, scanner, kEmailRe,
                           kDefaultMatchParamsWith<MatchFieldType::kEmail>,
                           email_patterns, &field, {log_manager, "kEmailRe"})) {
     return std::make_unique<EmailField>(field);
diff --git a/components/autofill/core/browser/form_parsing/email_field.h b/components/autofill/core/browser/form_parsing/email_field.h
index c8016c08..8bd9a88dbc 100644
--- a/components/autofill/core/browser/form_parsing/email_field.h
+++ b/components/autofill/core/browser/form_parsing/email_field.h
@@ -19,12 +19,9 @@
 
 class EmailField : public FormField {
  public:
-  static std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                          AutofillScanner* scanner,
+                                          LogManager* log_manager);
   explicit EmailField(const AutofillField* field);
 
   EmailField(const EmailField&) = delete;
diff --git a/components/autofill/core/browser/form_parsing/form_field.cc b/components/autofill/core/browser/form_parsing/form_field.cc
index 29f51b0..4774b05 100644
--- a/components/autofill/core/browser/form_parsing/form_field.cc
+++ b/components/autofill/core/browser/form_parsing/form_field.cc
@@ -54,93 +54,96 @@
   return s == nullptr || s[0] == '\0';
 }
 
-}  // namespace
-
-// static
-bool FormField::MatchesRegexWithCache(base::StringPiece16 input,
-                                      base::StringPiece16 pattern,
-                                      std::vector<std::u16string>* groups) {
+AutofillRegexCache& GetAutofillRegexCache() {
   // TODO(crbug.com/1309848): If ParseForm() is called from the same thread,
   // use a thread-unsafe parser.
   static base::NoDestructor<AutofillRegexCache> cache(ThreadSafe(true));
-  const icu::RegexPattern* regex_pattern = cache->GetRegexPattern(pattern);
+  return *cache;
+}
+
+}  // namespace
+
+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()) {}
+
+ParsingContext::~ParsingContext() = default;
+
+// static
+bool FormField::MatchesRegexWithCache(ParsingContext& context,
+                                      base::StringPiece16 input,
+                                      base::StringPiece16 pattern,
+                                      std::vector<std::u16string>* groups) {
+  const icu::RegexPattern* regex_pattern =
+      context.regex_cache->GetRegexPattern(pattern);
   return autofill::MatchesRegex(input, *regex_pattern, groups);
 }
 
 // static
 void FormField::ParseFormFields(
+    ParsingContext& context,
     const std::vector<std::unique_ptr<AutofillField>>& fields,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
     bool is_form_tag,
-    PatternSource pattern_source,
     FieldCandidatesMap& field_candidates,
     LogManager* log_manager) {
   std::vector<AutofillField*> processed_fields = RemoveCheckableFields(fields);
 
   // Email pass.
-  ParseFormFieldsPass(EmailField::Parse, processed_fields, field_candidates,
-                      client_country, page_language, pattern_source,
-                      log_manager);
+  ParseFormFieldsPass(EmailField::Parse, context, processed_fields,
+                      field_candidates, log_manager);
   bool found_email_field = !field_candidates.empty();
 
   // Phone pass.
-  ParseFormFieldsPass(PhoneField::Parse, processed_fields, field_candidates,
-                      client_country, page_language, pattern_source,
-                      log_manager);
+  ParseFormFieldsPass(PhoneField::Parse, context, processed_fields,
+                      field_candidates, log_manager);
 
   // Travel pass.
-  ParseFormFieldsPass(TravelField::Parse, processed_fields, field_candidates,
-                      client_country, page_language, pattern_source,
-                      log_manager);
+  ParseFormFieldsPass(TravelField::Parse, context, processed_fields,
+                      field_candidates, log_manager);
 
   // Address pass.
-  ParseFormFieldsPass(AddressField::Parse, processed_fields, field_candidates,
-                      client_country, page_language, pattern_source,
-                      log_manager);
+  ParseFormFieldsPass(AddressField::Parse, context, processed_fields,
+                      field_candidates, log_manager);
 
   // Birthdate pass.
   if (base::FeatureList::IsEnabled(features::kAutofillEnableBirthdateParsing)) {
-    ParseFormFieldsPass(BirthdateField::Parse, processed_fields,
-                        field_candidates, client_country, page_language,
-                        pattern_source, log_manager);
+    ParseFormFieldsPass(BirthdateField::Parse, context, processed_fields,
+                        field_candidates, log_manager);
   }
 
   // Numeric quantity pass.
-  ParseFormFieldsPass(NumericQuantityField::Parse, processed_fields,
-                      field_candidates, client_country, page_language,
-                      pattern_source, log_manager);
+  ParseFormFieldsPass(NumericQuantityField::Parse, context, processed_fields,
+                      field_candidates, log_manager);
 
   const size_t candidates_size = field_candidates.size();
   // Credit card pass.
-  ParseFormFieldsPass(CreditCardField::Parse, processed_fields,
-                      field_candidates, client_country, page_language,
-                      pattern_source, log_manager);
+  ParseFormFieldsPass(CreditCardField::Parse, context, processed_fields,
+                      field_candidates, log_manager);
   bool found_cc_fields = candidates_size != field_candidates.size();
   if (base::FeatureList::IsEnabled(
           features::kAutofillParseVcnCardOnFileStandaloneCvcFields) &&
       !found_email_field && !found_cc_fields) {
     // No email or cc fields found. Standalone CVC field pass for the VCN card
     // on file case.
-    ParseStandaloneCVCFields(fields, client_country, page_language,
-                             pattern_source, field_candidates, log_manager);
+    ParseStandaloneCVCFields(context, fields, field_candidates, log_manager);
     // Any detected standalone cvc fields are considered fillable single fields.
   }
 
   // Price pass.
-  ParseFormFieldsPass(PriceField::Parse, processed_fields, field_candidates,
-                      client_country, page_language, pattern_source,
-                      log_manager);
+  ParseFormFieldsPass(PriceField::Parse, context, processed_fields,
+                      field_candidates, log_manager);
 
   // Name pass.
-  ParseFormFieldsPass(NameField::Parse, processed_fields, field_candidates,
-                      client_country, page_language, pattern_source,
-                      log_manager);
+  ParseFormFieldsPass(NameField::Parse, context, processed_fields,
+                      field_candidates, log_manager);
 
   // Search pass.
-  ParseFormFieldsPass(SearchField::Parse, processed_fields, field_candidates,
-                      client_country, page_language, pattern_source,
-                      log_manager);
+  ParseFormFieldsPass(SearchField::Parse, context, processed_fields,
+                      field_candidates, log_manager);
 
   // Deduce `field_candidates` for the `processed_fields` by parsing their
   // `parsable_name()` as an autocomplete attribute.
@@ -150,19 +153,19 @@
   }
 
   // Single fields pass.
-  ParseSingleFieldForms(fields, client_country, page_language, is_form_tag,
-                        pattern_source, field_candidates, log_manager);
+  ParseSingleFieldForms(context, fields, is_form_tag, field_candidates,
+                        log_manager);
 
   ClearCandidatesIfHeuristicsDidNotFindEnoughFields(
-      fields, field_candidates, is_form_tag, client_country, log_manager);
+      context, fields, field_candidates, is_form_tag, log_manager);
 }
 
 // static
 void FormField::ClearCandidatesIfHeuristicsDidNotFindEnoughFields(
+    ParsingContext& context,
     const std::vector<std::unique_ptr<AutofillField>>& fields,
     FieldCandidatesMap& field_candidates,
     bool is_form_tag,
-    const GeoIpCountryCode& client_country,
     LogManager* log_manager) {
   // Set to count distinct field types.
   FieldTypeSet heuristic_types;
@@ -195,7 +198,7 @@
       CREDIT_CARD_STANDALONE_VERIFICATION_CODE};
   if (base::FeatureList::IsEnabled(
           features::kAutofillEnableZipOnlyAddressForms) &&
-      AddressField::IsStandaloneZipSupported(client_country)) {
+      AddressField::IsStandaloneZipSupported(context.client_country)) {
     permitted_single_field_types.insert(ADDRESS_HOME_ZIP);
   }
 
@@ -263,62 +266,52 @@
 }
 
 void FormField::ParseSingleFieldForms(
+    ParsingContext& context,
     const std::vector<std::unique_ptr<AutofillField>>& fields,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
     bool is_form_tag,
-    PatternSource pattern_source,
     FieldCandidatesMap& field_candidates,
     LogManager* log_manager) {
   std::vector<AutofillField*> processed_fields = RemoveCheckableFields(fields);
 
   // Merchant promo code pass.
-  ParseFormFieldsPass(MerchantPromoCodeField::Parse, processed_fields,
-                      field_candidates, client_country, page_language,
-                      pattern_source, log_manager);
+  ParseFormFieldsPass(MerchantPromoCodeField::Parse, context, processed_fields,
+                      field_candidates, log_manager);
 
   // IBAN pass.
-  ParseFormFieldsPass(IbanField::Parse, processed_fields, field_candidates,
-                      client_country, page_language, pattern_source,
-                      log_manager);
+  ParseFormFieldsPass(IbanField::Parse, context, processed_fields,
+                      field_candidates, log_manager);
 
-  if (AddressField::IsStandaloneZipSupported(client_country)) {
+  if (AddressField::IsStandaloneZipSupported(context.client_country)) {
     // In some countries we observe address forms that are particularly small
     // (e.g. only a zip code.)
-    ParseFormFieldsPass(AddressField::ParseStandaloneZip, processed_fields,
-                        field_candidates, client_country, page_language,
-                        pattern_source, log_manager);
+    ParseFormFieldsPass(AddressField::ParseStandaloneZip, context,
+                        processed_fields, field_candidates, log_manager);
   }
 }
 
 void FormField::ParseStandaloneCVCFields(
+    ParsingContext& context,
     const std::vector<std::unique_ptr<AutofillField>>& fields,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
     FieldCandidatesMap& field_candidates,
     LogManager* log_manager) {
   std::vector<AutofillField*> processed_fields = RemoveCheckableFields(fields);
-  ParseFormFieldsPass(StandaloneCvcField::Parse, processed_fields,
-                      field_candidates, client_country, page_language,
-                      pattern_source, log_manager);
+  ParseFormFieldsPass(StandaloneCvcField::Parse, context, processed_fields,
+                      field_candidates, log_manager);
 }
 
 void FormField::ParseStandaloneEmailFields(
+    ParsingContext& context,
     const std::vector<std::unique_ptr<AutofillField>>& fields,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
     FieldCandidatesMap& field_candidates,
     LogManager* log_manager) {
   std::vector<AutofillField*> processed_fields = RemoveCheckableFields(fields);
-  ParseFormFieldsPass(EmailField::Parse, processed_fields, field_candidates,
-                      client_country, page_language, pattern_source,
-                      log_manager);
+  ParseFormFieldsPass(EmailField::Parse, context, processed_fields,
+                      field_candidates, log_manager);
 }
 
 // static
 bool FormField::FieldMatchesMatchPatternRef(
+    ParsingContext& context,
     base::span<const MatchPatternRef> patterns,
     const AutofillField& field,
     const RegExLogging& logging) {
@@ -339,7 +332,7 @@
 
     if (!IsEmpty(pattern.negative_pattern)) {
       for (MatchAttribute attribute : pattern.match_field_attributes) {
-        if (FormField::Match(&field, pattern.negative_pattern,
+        if (FormField::Match(context, &field, pattern.negative_pattern,
                              MatchParams({attribute}, match_type.field_types),
                              logging)) {
           match_type.attributes.erase(attribute);
@@ -352,7 +345,7 @@
     }
 
     if (!IsEmpty(pattern.positive_pattern) &&
-        FormField::Match(&field, pattern.positive_pattern, match_type,
+        FormField::Match(context, &field, pattern.positive_pattern, match_type,
                          logging)) {
       return true;
     }
@@ -361,17 +354,19 @@
 }
 
 // static
-bool FormField::ParseField(AutofillScanner* scanner,
+bool FormField::ParseField(ParsingContext& context,
+                           AutofillScanner* scanner,
                            base::StringPiece16 pattern,
                            base::span<const MatchPatternRef> patterns,
                            raw_ptr<AutofillField>* match,
                            const RegExLogging& logging) {
-  return ParseFieldSpecifics(scanner, pattern, kDefaultMatchParams, patterns,
-                             match, logging);
+  return ParseFieldSpecifics(context, scanner, pattern, kDefaultMatchParams,
+                             patterns, match, logging);
 }
 
 // static
 bool FormField::ParseFieldSpecificsWithLegacyPattern(
+    ParsingContext& context,
     AutofillScanner* scanner,
     base::StringPiece16 pattern,
     MatchParams match_type,
@@ -387,11 +382,12 @@
     return false;
   }
 
-  return MatchAndAdvance(scanner, pattern, match_type, match, logging);
+  return MatchAndAdvance(context, scanner, pattern, match_type, match, logging);
 }
 
 // static
 bool FormField::ParseFieldSpecificsWithNewPatterns(
+    ParsingContext& context,
     AutofillScanner* scanner,
     base::span<const MatchPatternRef> patterns,
     raw_ptr<AutofillField>* match,
@@ -420,7 +416,7 @@
 
     if (!IsEmpty(pattern.negative_pattern)) {
       for (MatchAttribute attribute : pattern.match_field_attributes) {
-        if (FormField::Match(field, pattern.negative_pattern,
+        if (FormField::Match(context, field, pattern.negative_pattern,
                              MatchParams({attribute}, match_type.field_types),
                              logging)) {
           match_type.attributes.erase(attribute);
@@ -433,8 +429,8 @@
 
     // Apply the positive matching against all remaining match field attributes.
     if (!IsEmpty(pattern.positive_pattern) &&
-        MatchAndAdvance(scanner, pattern.positive_pattern, match_type, match,
-                        logging)) {
+        MatchAndAdvance(context, scanner, pattern.positive_pattern, match_type,
+                        match, logging)) {
       return true;
     }
   }
@@ -443,6 +439,7 @@
 
 // static
 bool FormField::ParseFieldSpecifics(
+    ParsingContext& context,
     AutofillScanner* scanner,
     base::StringPiece16 pattern,
     const MatchParams& match_type,
@@ -455,9 +452,9 @@
           // Some patterns may not exist as an old-school regex because they
           // require negative matching.
           pattern == kNoLegacyPattern)
-             ? ParseFieldSpecificsWithNewPatterns(scanner, patterns, match,
-                                                  logging, projection)
-             : ParseFieldSpecificsWithLegacyPattern(scanner, pattern,
+             ? ParseFieldSpecificsWithNewPatterns(context, scanner, patterns,
+                                                  match, logging, projection)
+             : ParseFieldSpecificsWithLegacyPattern(context, scanner, pattern,
                                                     match_type, match, logging);
 }
 
@@ -489,8 +486,9 @@
         break;
       }
     }
-    if (matches)
+    if (matches) {
       return true;
+    }
     scanner->RewindTo(original_pos);
   } while (std::next_permutation(p.begin(), p.end()));
   for (const auto& [field, _] : fields_and_parsers)
@@ -499,10 +497,11 @@
 }
 
 // static
-bool FormField::ParseEmptyLabel(AutofillScanner* scanner,
+bool FormField::ParseEmptyLabel(ParsingContext& context,
+                                AutofillScanner* scanner,
                                 raw_ptr<AutofillField>* match) {
   return ParseFieldSpecificsWithLegacyPattern(
-      scanner, kEmptyLabelRegex,
+      context, scanner, kEmptyLabelRegex,
       MatchParams({MatchAttribute::kLabel}, kAllMatchFieldTypes), match,
       /*logging=*/{});
 }
@@ -542,13 +541,14 @@
 }
 
 // static
-bool FormField::MatchAndAdvance(AutofillScanner* scanner,
+bool FormField::MatchAndAdvance(ParsingContext& context,
+                                AutofillScanner* scanner,
                                 base::StringPiece16 pattern,
                                 MatchParams match_type,
                                 raw_ptr<AutofillField>* match,
                                 const RegExLogging& logging) {
   AutofillField* field = scanner->Cursor();
-  if (FormField::Match(field, pattern, match_type, logging)) {
+  if (FormField::Match(context, field, pattern, match_type, logging)) {
     if (match)
       *match = field;
     scanner->Advance();
@@ -558,7 +558,8 @@
   return false;
 }
 
-bool FormField::Match(const AutofillField* field,
+bool FormField::Match(ParsingContext& context,
+                      const AutofillField* field,
                       base::StringPiece16 pattern,
                       MatchParams match_type,
                       const RegExLogging& logging) {
@@ -581,19 +582,20 @@
   const bool match_label =
       match_type.attributes.contains(MatchAttribute::kLabel);
   if (match_label &&
-      MatchesRegexWithCache(label, pattern, capture_destination)) {
+      MatchesRegexWithCache(context, label, pattern, capture_destination)) {
     found_match = true;
     match_type_string = "Match in label";
     value = label;
   } else if (match_type.attributes.contains(MatchAttribute::kName) &&
-             MatchesRegexWithCache(name, pattern, capture_destination)) {
+             MatchesRegexWithCache(context, name, pattern,
+                                   capture_destination)) {
     found_match = true;
     match_type_string = "Match in name";
     value = name;
   } else if (match_label && pattern != kEmptyLabelRegex &&
              base::FeatureList::IsEnabled(
                  features::kAutofillAlwaysParsePlaceholders) &&
-             MatchesRegexWithCache(field->placeholder, pattern,
+             MatchesRegexWithCache(context, field->placeholder, pattern,
                                    capture_destination)) {
     // Placeholders are matched against the same regexes as labels. However, to
     // prevent false positives in `ParseEmptyLabel()`, matches in placeholders
@@ -626,16 +628,14 @@
 
 // static
 void FormField::ParseFormFieldsPass(ParseFunction parse,
+                                    ParsingContext& context,
                                     const std::vector<AutofillField*>& fields,
                                     FieldCandidatesMap& field_candidates,
-                                    const GeoIpCountryCode& client_country,
-                                    const LanguageCode& page_language,
-                                    PatternSource pattern_source,
                                     LogManager* log_manager) {
   AutofillScanner scanner(fields);
   while (!scanner.IsEnd()) {
-    std::unique_ptr<FormField> form_field = parse(
-        &scanner, client_country, page_language, pattern_source, log_manager);
+    std::unique_ptr<FormField> form_field =
+        parse(context, &scanner, log_manager);
     if (form_field == nullptr) {
       scanner.Advance();
     } else {
diff --git a/components/autofill/core/browser/form_parsing/form_field.h b/components/autofill/core/browser/form_parsing/form_field.h
index 97f0d1ef..0b17466f 100644
--- a/components/autofill/core/browser/form_parsing/form_field.h
+++ b/components/autofill/core/browser/form_parsing/form_field.h
@@ -25,6 +25,7 @@
 namespace autofill {
 
 class AutofillField;
+class AutofillRegexCache;
 class AutofillScanner;
 class LogManager;
 
@@ -42,6 +43,24 @@
   const char* regex_name = "";
 };
 
+// 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.
+struct ParsingContext {
+  ParsingContext(GeoIpCountryCode client_country,
+                 LanguageCode page_language,
+                 PatternSource pattern_source);
+  ParsingContext(const ParsingContext&) = delete;
+  ParsingContext& operator=(const ParsingContext&) = delete;
+  ~ParsingContext();
+
+  const GeoIpCountryCode client_country;
+  const LanguageCode page_language;
+  const PatternSource pattern_source;
+
+  base::raw_ref<AutofillRegexCache> regex_cache;
+};
+
 // Represents a logical form field in a web form. Classes that implement this
 // interface can identify themselves as a particular type of form field, e.g.
 // name, phone number, or address field.
@@ -56,11 +75,9 @@
   // Each field has a derived unique name that is used as the key into
   // |field_candidates|.
   static void ParseFormFields(
+      ParsingContext& context,
       const std::vector<std::unique_ptr<AutofillField>>& fields,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
       bool is_form_tag,
-      PatternSource pattern_source,
       FieldCandidatesMap& field_candidates,
       LogManager* log_manager = nullptr);
 
@@ -68,11 +85,9 @@
   // promo codes) inside |fields|. Each field has a derived unique name that is
   // used as the key into |field_candidates|.
   static void ParseSingleFieldForms(
+      ParsingContext& context,
       const std::vector<std::unique_ptr<AutofillField>>& fields,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
       bool is_form_tag,
-      PatternSource pattern_source,
       FieldCandidatesMap& field_candidates,
       LogManager* log_manager = nullptr);
 
@@ -83,10 +98,8 @@
   // prerequisites in that there shouldn't be other credit card or email fields
   // in the form, which is why its parsing logic is extracted to its own method.
   static void ParseStandaloneCVCFields(
+      ParsingContext& context,
       const std::vector<std::unique_ptr<AutofillField>>& fields,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
       FieldCandidatesMap& field_candidates,
       LogManager* log_manager = nullptr);
 
@@ -95,25 +108,25 @@
   // sites. Currently called only when `kAutofillEnableEmailOnlyAddressForms` is
   // enabled.
   static void ParseStandaloneEmailFields(
+      ParsingContext& context,
       const std::vector<std::unique_ptr<AutofillField>>& fields,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
       FieldCandidatesMap& field_candidates,
       LogManager* log_manager = nullptr);
 
   // Returns true if `field` matches one of the the passed `patterns`.
   static bool FieldMatchesMatchPatternRef(
+      ParsingContext& context,
       base::span<const MatchPatternRef> patterns,
       const AutofillField& field,
       const RegExLogging& logging = {});
 
 #if defined(UNIT_TEST)
-  static bool MatchForTesting(const AutofillField* field,
+  static bool MatchForTesting(ParsingContext& context,
+                              const AutofillField* field,
                               base::StringPiece16 pattern,
                               MatchParams match_type,
                               const RegExLogging& logging = {}) {
-    return FormField::Match(field, pattern, match_type, logging);
+    return FormField::Match(context, field, pattern, match_type, logging);
   }
 
   static bool ParseInAnyOrderForTesting(
@@ -157,6 +170,7 @@
   // Should not be called from the UI thread as it may be blocked on a worker
   // thread.
   static bool MatchesRegexWithCache(
+      ParsingContext& context,
       base::StringPiece16 input,
       base::StringPiece16 pattern,
       std::vector<std::u16string>* groups = nullptr);
@@ -166,7 +180,8 @@
   // When `kNoLegacyPattern` is passed as the `pattern`, the functions always
   // default to `patterns`, regardless of the status of
   // `features::kAutofillParsingPatternProvider`.
-  static bool ParseField(AutofillScanner* scanner,
+  static bool ParseField(ParsingContext& context,
+                         AutofillScanner* scanner,
                          base::StringPiece16 pattern,
                          base::span<const MatchPatternRef> patterns,
                          raw_ptr<AutofillField>* match,
@@ -177,6 +192,7 @@
   // default to `patterns`, regardless of the status of
   // `features::kAutofillParsingPatternProvider`.
   static bool ParseFieldSpecifics(
+      ParsingContext& context,
       AutofillScanner* scanner,
       base::StringPiece16 pattern,
       const MatchParams& match_type,
@@ -187,7 +203,8 @@
 
   // Attempts to parse a field with an empty label. Returns true
   // on success and fills |match| with a pointer to the field.
-  static bool ParseEmptyLabel(AutofillScanner* scanner,
+  static bool ParseEmptyLabel(ParsingContext& context,
+                              AutofillScanner* scanner,
                               raw_ptr<AutofillField>* match);
 
   // Attempts to parse several fields using the specified parsing functions in
@@ -229,12 +246,9 @@
  private:
   // Function pointer type for the parsing function that should be passed to the
   // ParseFormFieldsPass() helper function.
-  typedef std::unique_ptr<FormField> ParseFunction(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  typedef std::unique_ptr<FormField> ParseFunction(ParsingContext& context,
+                                                   AutofillScanner* scanner,
+                                                   LogManager* log_manager);
 
   // Removes entries from `field_candidates` in case
   // - not enough fields were classified by local heuristics.
@@ -242,13 +256,14 @@
   //   contexts that don't contain enough fields (e.g. forms with only an
   //   email address).
   static void ClearCandidatesIfHeuristicsDidNotFindEnoughFields(
+      ParsingContext& context,
       const std::vector<std::unique_ptr<AutofillField>>& fields,
       FieldCandidatesMap& field_candidates,
       bool is_form_tag,
-      const GeoIpCountryCode& client_country,
       LogManager* log_manager);
 
   static bool ParseFieldSpecificsWithNewPatterns(
+      ParsingContext& context,
       AutofillScanner* scanner,
       base::span<const MatchPatternRef> patterns,
       raw_ptr<AutofillField>* match,
@@ -261,6 +276,7 @@
   // advance by one step. A |true| result is returned in the case of a
   // successful match, false otherwise.
   static bool ParseFieldSpecificsWithLegacyPattern(
+      ParsingContext& context,
       AutofillScanner* scanner,
       base::StringPiece16 pattern,
       MatchParams match_type,
@@ -276,7 +292,8 @@
   // |scanner|.
   // Returns |true| if a match is found according to |match_type|, and |false|
   // otherwise.
-  static bool MatchAndAdvance(AutofillScanner* scanner,
+  static bool MatchAndAdvance(ParsingContext& context,
+                              AutofillScanner* scanner,
                               base::StringPiece16 pattern,
                               MatchParams match_type,
                               raw_ptr<AutofillField>* match,
@@ -284,7 +301,8 @@
 
   // Matches the regular expression |pattern| against the components of
   // |field| as specified in |match_type|.
-  static bool Match(const AutofillField* field,
+  static bool Match(ParsingContext& context,
+                    const AutofillField* field,
                     base::StringPiece16 pattern,
                     MatchParams match_type,
                     const RegExLogging& logging = {});
@@ -296,11 +314,9 @@
   // Classification results of the processed fields are stored in
   // |field_candidates|.
   static void ParseFormFieldsPass(ParseFunction parse,
+                                  ParsingContext& context,
                                   const std::vector<AutofillField*>& fields,
                                   FieldCandidatesMap& field_candidates,
-                                  const GeoIpCountryCode& client_country,
-                                  const LanguageCode& page_language,
-                                  PatternSource pattern_source,
                                   LogManager* log_manager);
 
   // Interpret the fields' `parsable_name()` (id or name attribute) as an
diff --git a/components/autofill/core/browser/form_parsing/form_field_unittest.cc b/components/autofill/core/browser/form_parsing/form_field_unittest.cc
index 85d0f68..4b19cf4 100644
--- a/components/autofill/core/browser/form_parsing/form_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/form_field_unittest.cc
@@ -34,43 +34,44 @@
   // Returns the number of fields parsed.
   int ParseFormFields(GeoIpCountryCode client_country = GeoIpCountryCode(""),
                       LanguageCode language = LanguageCode("")) {
-    FormField::ParseFormFields(list_, client_country, language,
-                               /*is_form_tag=*/true,
-                               GetActivePatternSource().value(),
-                               field_candidates_map_,
+    ParsingContext context(client_country, language,
+                           GetActivePatternSource().value());
+    FormField::ParseFormFields(context, list_,
+                               /*is_form_tag=*/true, field_candidates_map_,
                                /*log_manager=*/nullptr);
     return field_candidates_map_.size();
   }
 
   // Like `ParseFormFields()`, but using `ParseSingleFieldForms()` instead.
   int ParseSingleFieldForms() {
-    FormField::ParseSingleFieldForms(
-        list_, GeoIpCountryCode(""), LanguageCode(""),
-        /*is_form_tag=*/true, GetActivePatternSource().value(),
-        field_candidates_map_);
+    ParsingContext context(GeoIpCountryCode(""), LanguageCode(""),
+                           GetActivePatternSource().value());
+    FormField::ParseSingleFieldForms(context, list_,
+                                     /*is_form_tag=*/true,
+                                     field_candidates_map_);
     return field_candidates_map_.size();
   }
 
   int ParseStandaloneCVCFields() {
-    FormField::ParseStandaloneCVCFields(
-        list_, GeoIpCountryCode(""), LanguageCode(""),
-        GetActivePatternSource().value(), field_candidates_map_);
+    ParsingContext context(GeoIpCountryCode(""), LanguageCode(""),
+                           GetActivePatternSource().value());
+    FormField::ParseStandaloneCVCFields(context, list_, field_candidates_map_);
     return field_candidates_map_.size();
   }
 
   int ParseStandaloneEmailFields() {
-    FormField::ParseStandaloneEmailFields(
-        list_, GeoIpCountryCode(""), LanguageCode(""),
-        GetActivePatternSource().value(), field_candidates_map_);
+    ParsingContext context(GeoIpCountryCode(""), LanguageCode(""),
+                           GetActivePatternSource().value());
+    FormField::ParseStandaloneEmailFields(context, list_,
+                                          field_candidates_map_);
     return field_candidates_map_.size();
   }
 
   // FormFieldTestBase:
   // This function is unused in these unit tests, because FormField is not a
   // parser itself, but the infrastructure combining them.
-  std::unique_ptr<FormField> Parse(AutofillScanner* scanner,
-                                   const GeoIpCountryCode& client_country,
-                                   const LanguageCode& page_language) override {
+  std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                   AutofillScanner* scanner) override {
     return nullptr;
   }
 
@@ -128,12 +129,18 @@
   field.label = label;
   field.set_parseable_label(label);
   for (const auto& pattern : positive_patterns) {
+    ParsingContext context(GeoIpCountryCode(""), LanguageCode(""),
+                           PatternSource::kLegacy);
     SCOPED_TRACE("positive_pattern = " + base::UTF16ToUTF8(pattern));
-    EXPECT_TRUE(FormField::MatchForTesting(&field, pattern, kMatchLabel));
+    EXPECT_TRUE(
+        FormField::MatchForTesting(context, &field, pattern, kMatchLabel));
   }
   for (const auto& pattern : negative_patterns) {
+    ParsingContext context(GeoIpCountryCode(""), LanguageCode(""),
+                           PatternSource::kLegacy);
     SCOPED_TRACE("negative_pattern = " + base::UTF16ToUTF8(pattern));
-    EXPECT_FALSE(FormField::MatchForTesting(&field, pattern, kMatchLabel));
+    EXPECT_FALSE(
+        FormField::MatchForTesting(context, &field, pattern, kMatchLabel));
   }
 }
 
@@ -186,15 +193,19 @@
     base::test::ScopedFeatureList feature_list;
     feature_list.InitAndEnableFeature(
         features::kAutofillEnableSupportForParsingWithSharedLabels);
-    EXPECT_TRUE(
-        FormField::MatchForTesting(autofill_field, u"First Name", kMatchLabel));
+    ParsingContext context(GeoIpCountryCode(""), LanguageCode(""),
+                           PatternSource::kLegacy);
+    EXPECT_TRUE(FormField::MatchForTesting(context, autofill_field,
+                                           u"First Name", kMatchLabel));
   }
   {
     base::test::ScopedFeatureList feature_list;
     feature_list.InitAndDisableFeature(
         features::kAutofillEnableSupportForParsingWithSharedLabels);
-    EXPECT_FALSE(
-        FormField::MatchForTesting(autofill_field, u"First Name", kMatchLabel));
+    ParsingContext context(GeoIpCountryCode(""), LanguageCode(""),
+                           PatternSource::kLegacy);
+    EXPECT_FALSE(FormField::MatchForTesting(context, autofill_field,
+                                            u"First Name", kMatchLabel));
   }
 }
 
diff --git a/components/autofill/core/browser/form_parsing/iban_field.cc b/components/autofill/core/browser/form_parsing/iban_field.cc
index 469d3d2..095cc95 100644
--- a/components/autofill/core/browser/form_parsing/iban_field.cc
+++ b/components/autofill/core/browser/form_parsing/iban_field.cc
@@ -12,17 +12,14 @@
 namespace autofill {
 
 // static
-std::unique_ptr<FormField> IbanField::Parse(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
+std::unique_ptr<FormField> IbanField::Parse(ParsingContext& context,
+                                            AutofillScanner* scanner,
+                                            LogManager* log_manager) {
   raw_ptr<AutofillField> field;
-  base::span<const MatchPatternRef> iban_patterns =
-      GetMatchPatterns(IBAN_VALUE, page_language, pattern_source);
+  base::span<const MatchPatternRef> iban_patterns = GetMatchPatterns(
+      IBAN_VALUE, context.page_language, context.pattern_source);
 
-  if (ParseFieldSpecifics(scanner, kIbanRe,
+  if (ParseFieldSpecifics(context, scanner, kIbanRe,
                           kDefaultMatchParamsWith<MatchFieldType::kNumber,
                                                   MatchFieldType::kTextArea>,
                           iban_patterns, &field, {log_manager, "kIbanRe"})) {
diff --git a/components/autofill/core/browser/form_parsing/iban_field.h b/components/autofill/core/browser/form_parsing/iban_field.h
index e6c95d8..948ff6e 100644
--- a/components/autofill/core/browser/form_parsing/iban_field.h
+++ b/components/autofill/core/browser/form_parsing/iban_field.h
@@ -21,12 +21,9 @@
 // A form field that accepts International Bank Account Number (IBAN).
 class IbanField : public FormField {
  public:
-  static std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                          AutofillScanner* scanner,
+                                          LogManager* log_manager);
 
   explicit IbanField(const AutofillField* field);
 
diff --git a/components/autofill/core/browser/form_parsing/iban_field_unittest.cc b/components/autofill/core/browser/form_parsing/iban_field_unittest.cc
index 8fec4a2..831bfb0 100644
--- a/components/autofill/core/browser/form_parsing/iban_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/iban_field_unittest.cc
@@ -17,13 +17,9 @@
   IbanFieldTest& operator=(const IbanFieldTest&) = delete;
 
  protected:
-  std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language = LanguageCode("en")) override {
-    return IbanField::Parse(scanner, client_country, page_language,
-                            *GetActivePatternSource(),
-                            /*log_manager=*/nullptr);
+  std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                   AutofillScanner* scanner) override {
+    return IbanField::Parse(context, scanner, nullptr);
   }
 };
 
diff --git a/components/autofill/core/browser/form_parsing/merchant_promo_code_field.cc b/components/autofill/core/browser/form_parsing/merchant_promo_code_field.cc
index d0fd1e33..9b16037 100644
--- a/components/autofill/core/browser/form_parsing/merchant_promo_code_field.cc
+++ b/components/autofill/core/browser/form_parsing/merchant_promo_code_field.cc
@@ -13,16 +13,15 @@
 
 // static
 std::unique_ptr<FormField> MerchantPromoCodeField::Parse(
+    ParsingContext& context,
     AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
     LogManager* log_manager) {
   raw_ptr<AutofillField> field;
   base::span<const MatchPatternRef> merchant_promo_code_patterns =
-      GetMatchPatterns("MERCHANT_PROMO_CODE", page_language, pattern_source);
+      GetMatchPatterns("MERCHANT_PROMO_CODE", context.page_language,
+                       context.pattern_source);
 
-  if (ParseFieldSpecifics(scanner, kMerchantPromoCodeRe,
+  if (ParseFieldSpecifics(context, scanner, kMerchantPromoCodeRe,
                           kDefaultMatchParamsWith<MatchFieldType::kNumber,
                                                   MatchFieldType::kTextArea>,
                           merchant_promo_code_patterns, &field,
diff --git a/components/autofill/core/browser/form_parsing/merchant_promo_code_field.h b/components/autofill/core/browser/form_parsing/merchant_promo_code_field.h
index 9d44b8e..5170994 100644
--- a/components/autofill/core/browser/form_parsing/merchant_promo_code_field.h
+++ b/components/autofill/core/browser/form_parsing/merchant_promo_code_field.h
@@ -24,12 +24,9 @@
 // merchant's web site.
 class MerchantPromoCodeField : public FormField {
  public:
-  static std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                          AutofillScanner* scanner,
+                                          LogManager* log_manager);
   explicit MerchantPromoCodeField(const AutofillField* field);
 
   MerchantPromoCodeField(const MerchantPromoCodeField&) = delete;
diff --git a/components/autofill/core/browser/form_parsing/merchant_promo_code_field_unittest.cc b/components/autofill/core/browser/form_parsing/merchant_promo_code_field_unittest.cc
index 5b49f69..5a1af80 100644
--- a/components/autofill/core/browser/form_parsing/merchant_promo_code_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/merchant_promo_code_field_unittest.cc
@@ -21,13 +21,9 @@
       delete;
 
  protected:
-  std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language = LanguageCode("en")) override {
-    return MerchantPromoCodeField::Parse(scanner, client_country, page_language,
-                                         *GetActivePatternSource(),
-                                         /*log_manager=*/nullptr);
+  std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                   AutofillScanner* scanner) override {
+    return MerchantPromoCodeField::Parse(context, scanner, nullptr);
   }
 };
 
diff --git a/components/autofill/core/browser/form_parsing/name_field.cc b/components/autofill/core/browser/form_parsing/name_field.cc
index cfe35a6..d06f1fc 100644
--- a/components/autofill/core/browser/form_parsing/name_field.cc
+++ b/components/autofill/core/browser/form_parsing/name_field.cc
@@ -20,12 +20,16 @@
 namespace autofill {
 namespace {
 
+base::span<const MatchPatternRef> GetMatchPatterns(base::StringPiece name,
+                                                   ParsingContext& context) {
+  return GetMatchPatterns(name, context.page_language, context.pattern_source);
+}
+
 // A form field that can parse a full name field.
 class FullNameField : public NameField {
  public:
-  static std::unique_ptr<FullNameField> Parse(AutofillScanner* scanner,
-                                              const LanguageCode& page_language,
-                                              PatternSource pattern_source,
+  static std::unique_ptr<FullNameField> Parse(ParsingContext& context,
+                                              AutofillScanner* scanner,
                                               LogManager* log_manager);
   explicit FullNameField(AutofillField* field);
 
@@ -44,15 +48,12 @@
 class FirstTwoLastNamesField : public NameField {
  public:
   static std::unique_ptr<FirstTwoLastNamesField> ParseComponentNames(
+      ParsingContext& context,
       AutofillScanner* scanner,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
       LogManager* log_manager);
-  static std::unique_ptr<FirstTwoLastNamesField> Parse(
-      AutofillScanner* scanner,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FirstTwoLastNamesField> Parse(ParsingContext& context,
+                                                       AutofillScanner* scanner,
+                                                       LogManager* log_manager);
 
   FirstTwoLastNamesField(const FirstTwoLastNamesField&) = delete;
   FirstTwoLastNamesField& operator=(const FirstTwoLastNamesField&) = delete;
@@ -77,36 +78,31 @@
   // Tries to match a series of name fields that follows the pattern "Name,
   // Surname".
   static std::unique_ptr<FirstLastNameField> ParseNameSurnameLabelSequence(
+      ParsingContext& context,
       AutofillScanner* scanner,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
       LogManager* log_manager);
 
   // Tries to match a series of fields with a shared label: The first field
   // needs to have a unspecific name label followed by up to two fields without
   // a label.
   static std::unique_ptr<FirstLastNameField> ParseSharedNameLabelSequence(
+      ParsingContext& context,
       AutofillScanner* scanner,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
       LogManager* log_manager);
 
   // Tries to match a series of fields with patterns that are specific to the
   // individual components of a name. Note that the order of the components does
   // not matter.
   static std::unique_ptr<FirstLastNameField> ParseSpecificComponentSequence(
+      ParsingContext& context,
       AutofillScanner* scanner,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
       LogManager* log_manager);
 
   // Probes the matching strategies defined above. Returns the result of the
   // first successful match. Returns a nullptr if no matches can be found.
-  static std::unique_ptr<FirstLastNameField> Parse(
-      AutofillScanner* scanner,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FirstLastNameField> Parse(ParsingContext& context,
+                                                   AutofillScanner* scanner,
+                                                   LogManager* log_manager);
 
   FirstLastNameField(const FirstLastNameField&) = delete;
   FirstLastNameField& operator=(const FirstLastNameField&) = delete;
@@ -127,29 +123,24 @@
 }  // namespace
 
 // static
-std::unique_ptr<FormField> NameField::Parse(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
-  if (scanner->IsEnd())
+std::unique_ptr<FormField> NameField::Parse(ParsingContext& context,
+                                            AutofillScanner* scanner,
+                                            LogManager* log_manager) {
+  if (scanner->IsEnd()) {
     return nullptr;
+  }
 
   // Try |FirstLastNameField| and |FirstTwoLastNamesField| first since they are
   // more specific.
   std::unique_ptr<FormField> field;
   if (!field) {
-    field = FirstTwoLastNamesField::Parse(scanner, page_language,
-                                          pattern_source, log_manager);
+    field = FirstTwoLastNamesField::Parse(context, scanner, log_manager);
   }
   if (!field) {
-    field = FirstLastNameField::Parse(scanner, page_language, pattern_source,
-                                      log_manager);
+    field = FirstLastNameField::Parse(context, scanner, log_manager);
   }
   if (!field) {
-    field = FullNameField::Parse(scanner, page_language, pattern_source,
-                                 log_manager);
+    field = FullNameField::Parse(context, scanner, log_manager);
   }
   return field;
 }
@@ -159,27 +150,27 @@
 }
 
 // static
-std::unique_ptr<FullNameField> FullNameField::Parse(
-    AutofillScanner* scanner,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
+std::unique_ptr<FullNameField> FullNameField::Parse(ParsingContext& context,
+                                                    AutofillScanner* scanner,
+                                                    LogManager* log_manager) {
   // Exclude e.g. "username" or "nickname" fields.
   scanner->SaveCursor();
   base::span<const MatchPatternRef> name_ignored_patterns =
-      GetMatchPatterns("NAME_IGNORED", page_language, pattern_source);
+      GetMatchPatterns("NAME_IGNORED", context);
   base::span<const MatchPatternRef> address_name_ignored_patterns =
-      GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language, pattern_source);
+      GetMatchPatterns("ADDRESS_NAME_IGNORED", context);
   bool should_ignore =
-      ParseField(scanner, kNameIgnoredRe, name_ignored_patterns, nullptr,
-                 {log_manager, "kNameIgnoredRe"}) ||
+      ParseField(context, scanner, kNameIgnoredRe, name_ignored_patterns,
+                 nullptr, {log_manager, "kNameIgnoredRe"}) ||
       // This pattern fully migrated to the MatchPattern mechanism. There
       // is no regular expression in autofill_regex_constants.h anymore.
-      ParseField(scanner, kNoLegacyPattern, address_name_ignored_patterns,
-                 nullptr, {log_manager, "kAddressNameIgnoredRe"});
+      ParseField(context, scanner, kNoLegacyPattern,
+                 address_name_ignored_patterns, nullptr,
+                 {log_manager, "kAddressNameIgnoredRe"});
   scanner->Rewind();
-  if (should_ignore)
+  if (should_ignore) {
     return nullptr;
+  }
 
   // Searching for any label containing the word "name" is too general;
   // for example, Travelocity_Edit travel profile.html contains a field
@@ -187,10 +178,11 @@
   raw_ptr<AutofillField> field = nullptr;
 
   base::span<const MatchPatternRef> name_patterns =
-      GetMatchPatterns("FULL_NAME", page_language, pattern_source);
-  if (ParseField(scanner, kFullNameRe, name_patterns, &field,
-                 {log_manager, "kFullNameRe"}))
+      GetMatchPatterns("FULL_NAME", context);
+  if (ParseField(context, scanner, kFullNameRe, name_patterns, &field,
+                 {log_manager, "kFullNameRe"})) {
     return std::make_unique<FullNameField>(field);
+  }
 
   return nullptr;
 }
@@ -206,37 +198,34 @@
 
 // static
 std::unique_ptr<FirstTwoLastNamesField> FirstTwoLastNamesField::Parse(
+    ParsingContext& context,
     AutofillScanner* scanner,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
     LogManager* log_manager) {
-  return ParseComponentNames(scanner, page_language, pattern_source,
-                             log_manager);
+  return ParseComponentNames(context, scanner, log_manager);
 }
 
 // static
 std::unique_ptr<FirstTwoLastNamesField>
-FirstTwoLastNamesField::ParseComponentNames(AutofillScanner* scanner,
-                                            const LanguageCode& page_language,
-                                            PatternSource pattern_source,
+FirstTwoLastNamesField::ParseComponentNames(ParsingContext& context,
+                                            AutofillScanner* scanner,
                                             LogManager* log_manager) {
   auto v = base::WrapUnique(new FirstTwoLastNamesField());
   scanner->SaveCursor();
 
   base::span<const MatchPatternRef> honorific_prefix_patterns =
-      GetMatchPatterns("HONORIFIC_PREFIX", page_language, pattern_source);
+      GetMatchPatterns("HONORIFIC_PREFIX", context);
   base::span<const MatchPatternRef> name_ignored_patterns =
-      GetMatchPatterns("NAME_IGNORED", page_language, pattern_source);
+      GetMatchPatterns("NAME_IGNORED", context);
   base::span<const MatchPatternRef> address_name_ignored_patterns =
-      GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language, pattern_source);
+      GetMatchPatterns("ADDRESS_NAME_IGNORED", context);
   base::span<const MatchPatternRef> first_name_patterns =
-      GetMatchPatterns("FIRST_NAME", page_language, pattern_source);
+      GetMatchPatterns("FIRST_NAME", context);
   base::span<const MatchPatternRef> middle_name_patterns =
-      GetMatchPatterns("MIDDLE_NAME", page_language, pattern_source);
+      GetMatchPatterns("MIDDLE_NAME", context);
   base::span<const MatchPatternRef> first_last_name_patterns =
-      GetMatchPatterns("LAST_NAME_FIRST", page_language, pattern_source);
+      GetMatchPatterns("LAST_NAME_FIRST", context);
   base::span<const MatchPatternRef> second_last_name_patterns =
-      GetMatchPatterns("LAST_NAME_SECOND", page_language, pattern_source);
+      GetMatchPatterns("LAST_NAME_SECOND", context);
 
   // Allow name fields to appear in any order.
   while (!scanner->IsEnd()) {
@@ -244,8 +233,9 @@
     // e.g. "title" or "name".
     // This pattern fully migrated to the MatchPattern mechanism. There is no
     // regular expression in autofill_regex_constants.h anymore.
-    if (ParseField(scanner, kNoLegacyPattern, address_name_ignored_patterns,
-                   nullptr, {log_manager, "kAddressNameIgnoredRe"})) {
+    if (ParseField(context, scanner, kNoLegacyPattern,
+                   address_name_ignored_patterns, nullptr,
+                   {log_manager, "kAddressNameIgnoredRe"})) {
       continue;
     }
 
@@ -255,14 +245,14 @@
     // TODO(crbug.com/1098943): Remove check once feature is launched or
     // removed.
     if (!v->honorific_prefix_ &&
-        ParseField(scanner, kHonorificPrefixRe, honorific_prefix_patterns,
-                   &v->honorific_prefix_,
+        ParseField(context, scanner, kHonorificPrefixRe,
+                   honorific_prefix_patterns, &v->honorific_prefix_,
                    {log_manager, "kHonorificPrefixRe"})) {
       continue;
     }
 
     // Skip over any unrelated fields, e.g. "username" or "nickname".
-    if (ParseFieldSpecifics(scanner, kNameIgnoredRe,
+    if (ParseFieldSpecifics(context, scanner, kNameIgnoredRe,
                             kDefaultMatchParamsWith<MatchFieldType::kSelect,
                                                     MatchFieldType::kSearch>,
                             name_ignored_patterns, nullptr,
@@ -271,26 +261,26 @@
     }
 
     if (!v->first_name_ &&
-        ParseField(scanner, kFirstNameRe, first_name_patterns, &v->first_name_,
-                   {log_manager, "kFirstNameRe"})) {
+        ParseField(context, scanner, kFirstNameRe, first_name_patterns,
+                   &v->first_name_, {log_manager, "kFirstNameRe"})) {
       continue;
     }
 
     if (!v->middle_name_ &&
-        ParseField(scanner, kMiddleNameRe, middle_name_patterns,
+        ParseField(context, scanner, kMiddleNameRe, middle_name_patterns,
                    &v->middle_name_, {log_manager, "kMiddleNameRe"})) {
       continue;
     }
 
     if (!v->first_last_name_ &&
-        ParseField(scanner, kNameLastFirstRe, first_last_name_patterns,
+        ParseField(context, scanner, kNameLastFirstRe, first_last_name_patterns,
                    &v->first_last_name_, {log_manager, "kNameLastFirstRe"})) {
       continue;
     }
 
     if (!v->second_last_name_ &&
-        ParseField(scanner, kNameLastSecondRe, second_last_name_patterns,
-                   &v->second_last_name_,
+        ParseField(context, scanner, kNameLastSecondRe,
+                   second_last_name_patterns, &v->second_last_name_,
                    {log_manager, "kNameLastSecondtRe"})) {
       continue;
     }
@@ -300,8 +290,9 @@
 
   // Consider the match to be successful if we detected both last names and the
   // surname.
-  if (v->first_name_ && v->first_last_name_ && v->second_last_name_)
+  if (v->first_name_ && v->first_last_name_ && v->second_last_name_) {
     return v;
+  }
 
   scanner->Rewind();
   return nullptr;
@@ -322,51 +313,51 @@
 }
 
 std::unique_ptr<FirstLastNameField>
-FirstLastNameField::ParseNameSurnameLabelSequence(
-    AutofillScanner* scanner,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
+FirstLastNameField::ParseNameSurnameLabelSequence(ParsingContext& context,
+                                                  AutofillScanner* scanner,
+                                                  LogManager* log_manager) {
   // Some pages have a generic name label that corresponds to a first name
   // followed by a last name label.
   // Example: Name [      ] Last Name [      ]
   auto v = base::WrapUnique(new FirstLastNameField());
 
   base::span<const MatchPatternRef> name_specific_patterns =
-      GetMatchPatterns("NAME_GENERIC", page_language, pattern_source);
+      GetMatchPatterns("NAME_GENERIC", context);
   base::span<const MatchPatternRef> middle_name_patterns =
-      GetMatchPatterns("MIDDLE_NAME", page_language, pattern_source);
+      GetMatchPatterns("MIDDLE_NAME", context);
   base::span<const MatchPatternRef> last_name_patterns =
-      GetMatchPatterns("LAST_NAME", page_language, pattern_source);
+      GetMatchPatterns("LAST_NAME", context);
   // Check that the field should not be ignored.
 
   base::span<const MatchPatternRef> name_ignored_patterns =
-      GetMatchPatterns("NAME_IGNORED", page_language, pattern_source);
+      GetMatchPatterns("NAME_IGNORED", context);
   base::span<const MatchPatternRef> address_name_ignored_patterns =
-      GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language, pattern_source);
+      GetMatchPatterns("ADDRESS_NAME_IGNORED", context);
   scanner->SaveCursor();
 
   bool should_ignore =
-      ParseField(scanner, kNameIgnoredRe, name_ignored_patterns, nullptr,
-                 {log_manager, "kNameIgnoredRe"}) ||
+      ParseField(context, scanner, kNameIgnoredRe, name_ignored_patterns,
+                 nullptr, {log_manager, "kNameIgnoredRe"}) ||
       // This pattern fully migrated to the MatchPattern mechanism. There is no
       // regular expression in autofill_regex_constants.h anymore.
-      ParseField(scanner, kNoLegacyPattern, address_name_ignored_patterns,
-                 nullptr, {log_manager, "kAddressNameIgnoredRe"});
+      ParseField(context, scanner, kNoLegacyPattern,
+                 address_name_ignored_patterns, nullptr,
+                 {log_manager, "kAddressNameIgnoredRe"});
   scanner->Rewind();
 
   scanner->SaveCursor();
 
-  if (should_ignore)
+  if (should_ignore) {
     return nullptr;
+  }
 
-  if (ParseField(scanner, kNameGenericRe, name_specific_patterns,
+  if (ParseField(context, scanner, kNameGenericRe, name_specific_patterns,
                  &v->first_name_, {log_manager, "kNameGenericRe"})) {
     // Check for an optional middle name field.
-    ParseField(scanner, kMiddleNameRe, middle_name_patterns, &v->middle_name_,
-               {log_manager, "kMiddleNameRe"});
-    if (ParseField(scanner, kLastNameRe, last_name_patterns, &v->last_name_,
-                   {log_manager, "kLastNameRe"})) {
+    ParseField(context, scanner, kMiddleNameRe, middle_name_patterns,
+               &v->middle_name_, {log_manager, "kMiddleNameRe"});
+    if (ParseField(context, scanner, kLastNameRe, last_name_patterns,
+                   &v->last_name_, {log_manager, "kLastNameRe"})) {
       return v;
     }
   }
@@ -376,11 +367,9 @@
 }
 
 std::unique_ptr<FirstLastNameField>
-FirstLastNameField::ParseSharedNameLabelSequence(
-    AutofillScanner* scanner,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
+FirstLastNameField::ParseSharedNameLabelSequence(ParsingContext& context,
+                                                 AutofillScanner* scanner,
+                                                 LogManager* log_manager) {
   // Some pages (e.g. Overstock_comBilling.html, SmithsonianCheckout.html)
   // have the label "Name" followed by two or three text fields.
   auto v = base::WrapUnique(new FirstLastNameField());
@@ -388,12 +377,12 @@
 
   raw_ptr<AutofillField> next = nullptr;
   base::span<const MatchPatternRef> name_specific_patterns =
-      GetMatchPatterns("NAME_GENERIC", page_language, pattern_source);
+      GetMatchPatterns("NAME_GENERIC", context);
 
-  if (ParseField(scanner, kNameGenericRe, name_specific_patterns,
+  if (ParseField(context, scanner, kNameGenericRe, name_specific_patterns,
                  &v->first_name_, {log_manager, "kNameGenericRe"}) &&
-      ParseEmptyLabel(scanner, &next)) {
-    if (ParseEmptyLabel(scanner, &v->last_name_)) {
+      ParseEmptyLabel(context, scanner, &next)) {
+    if (ParseEmptyLabel(context, scanner, &v->last_name_)) {
       // There are three name fields; assume that the middle one is a
       // middle initial (it is, at least, on SmithsonianCheckout.html).
       v->middle_name_ = next;
@@ -411,11 +400,9 @@
 
 // static
 std::unique_ptr<FirstLastNameField>
-FirstLastNameField::ParseSpecificComponentSequence(
-    AutofillScanner* scanner,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
+FirstLastNameField::ParseSpecificComponentSequence(ParsingContext& context,
+                                                   AutofillScanner* scanner,
+                                                   LogManager* log_manager) {
   auto v = base::WrapUnique(new FirstLastNameField());
   scanner->SaveCursor();
 
@@ -432,27 +419,28 @@
   // Allow name fields to appear in any order.
 
   base::span<const MatchPatternRef> honorific_prefix_patterns =
-      GetMatchPatterns("HONORIFIC_PREFIX", page_language, pattern_source);
+      GetMatchPatterns("HONORIFIC_PREFIX", context);
   base::span<const MatchPatternRef> name_ignored_patterns =
-      GetMatchPatterns("NAME_IGNORED", page_language, pattern_source);
+      GetMatchPatterns("NAME_IGNORED", context);
   base::span<const MatchPatternRef> address_name_ignored_patterns =
-      GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language, pattern_source);
+      GetMatchPatterns("ADDRESS_NAME_IGNORED", context);
   base::span<const MatchPatternRef> first_name_patterns =
-      GetMatchPatterns("FIRST_NAME", page_language, pattern_source);
+      GetMatchPatterns("FIRST_NAME", context);
   base::span<const MatchPatternRef> middle_name_initial_patterns =
-      GetMatchPatterns("MIDDLE_INITIAL", page_language, pattern_source);
+      GetMatchPatterns("MIDDLE_INITIAL", context);
   base::span<const MatchPatternRef> middle_name_patterns =
-      GetMatchPatterns("MIDDLE_NAME", page_language, pattern_source);
+      GetMatchPatterns("MIDDLE_NAME", context);
   base::span<const MatchPatternRef> last_name_patterns =
-      GetMatchPatterns("LAST_NAME", page_language, pattern_source);
+      GetMatchPatterns("LAST_NAME", context);
 
   while (!scanner->IsEnd()) {
     // Skip over address label fields, which can have misleading names
     // e.g. "title" or "name".
     // This pattern fully migrated to the MatchPattern mechanism. There is no
     // regular expression in autofill_regex_constants.h anymore.
-    if (ParseField(scanner, kNoLegacyPattern, address_name_ignored_patterns,
-                   nullptr, {log_manager, "kAddressNameIgnoredRe"})) {
+    if (ParseField(context, scanner, kNoLegacyPattern,
+                   address_name_ignored_patterns, nullptr,
+                   {log_manager, "kAddressNameIgnoredRe"})) {
       continue;
     }
 
@@ -460,14 +448,14 @@
     // because a honorific prefix field is expected to have very specific labels
     // including "Title:". The latter is matched with |kNameIgnoredRe|.
     if (!v->honorific_prefix_ &&
-        ParseField(scanner, kHonorificPrefixRe, honorific_prefix_patterns,
-                   &v->honorific_prefix_,
+        ParseField(context, scanner, kHonorificPrefixRe,
+                   honorific_prefix_patterns, &v->honorific_prefix_,
                    {log_manager, "kHonorificPrefixRe"})) {
       continue;
     }
 
     // Skip over any unrelated name fields, e.g. "username" or "nickname".
-    if (ParseFieldSpecifics(scanner, kNameIgnoredRe,
+    if (ParseFieldSpecifics(context, scanner, kNameIgnoredRe,
                             kDefaultMatchParamsWith<MatchFieldType::kSelect,
                                                     MatchFieldType::kSearch>,
                             name_ignored_patterns, nullptr,
@@ -476,8 +464,8 @@
     }
 
     if (!v->first_name_ &&
-        ParseField(scanner, kFirstNameRe, first_name_patterns, &v->first_name_,
-                   {log_manager, "kFirstNameRe"})) {
+        ParseField(context, scanner, kFirstNameRe, first_name_patterns,
+                   &v->first_name_, {log_manager, "kFirstNameRe"})) {
       continue;
     }
 
@@ -487,21 +475,22 @@
     // "txtmiddlename"); such a field probably actually represents a
     // middle initial.
     if (!v->middle_name_ &&
-        ParseField(scanner, kMiddleInitialRe, middle_name_initial_patterns,
-                   &v->middle_name_, {log_manager, "kMiddleInitialRe"})) {
+        ParseField(context, scanner, kMiddleInitialRe,
+                   middle_name_initial_patterns, &v->middle_name_,
+                   {log_manager, "kMiddleInitialRe"})) {
       v->middle_initial_ = true;
       continue;
     }
 
     if (!v->middle_name_ &&
-        ParseField(scanner, kMiddleNameRe, middle_name_patterns,
+        ParseField(context, scanner, kMiddleNameRe, middle_name_patterns,
                    &v->middle_name_, {log_manager, "kMiddleNameRe"})) {
       continue;
     }
 
     if (!v->last_name_ &&
-        ParseField(scanner, kLastNameRe, last_name_patterns, &v->last_name_,
-                   {log_manager, "kLastNameRe"})) {
+        ParseField(context, scanner, kLastNameRe, last_name_patterns,
+                   &v->last_name_, {log_manager, "kLastNameRe"})) {
       continue;
     }
 
@@ -510,8 +499,9 @@
 
   // Consider the match to be successful if we detected both first and last name
   // fields.
-  if (v->first_name_ && v->last_name_)
+  if (v->first_name_ && v->last_name_) {
     return v;
+  }
 
   scanner->Rewind();
   return nullptr;
@@ -519,20 +509,17 @@
 
 // static
 std::unique_ptr<FirstLastNameField> FirstLastNameField::Parse(
+    ParsingContext& context,
     AutofillScanner* scanner,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
     LogManager* log_manager) {
-  std::unique_ptr<FirstLastNameField> field = ParseSharedNameLabelSequence(
-      scanner, page_language, pattern_source, log_manager);
+  std::unique_ptr<FirstLastNameField> field =
+      ParseSharedNameLabelSequence(context, scanner, log_manager);
 
   if (!field) {
-    field = ParseNameSurnameLabelSequence(scanner, page_language,
-                                          pattern_source, log_manager);
+    field = ParseNameSurnameLabelSequence(context, scanner, log_manager);
   }
   if (!field) {
-    field = ParseSpecificComponentSequence(scanner, page_language,
-                                           pattern_source, log_manager);
+    field = ParseSpecificComponentSequence(context, scanner, log_manager);
   }
   return field;
 }
diff --git a/components/autofill/core/browser/form_parsing/name_field.h b/components/autofill/core/browser/form_parsing/name_field.h
index 25ea8f08..0890842 100644
--- a/components/autofill/core/browser/form_parsing/name_field.h
+++ b/components/autofill/core/browser/form_parsing/name_field.h
@@ -22,12 +22,9 @@
 // A form field that can parse either a FullNameField or a FirstLastNameField.
 class NameField : public FormField {
  public:
-  static std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                          AutofillScanner* scanner,
+                                          LogManager* log_manager);
 
   NameField(const NameField&) = delete;
   NameField& operator=(const NameField&) = delete;
diff --git a/components/autofill/core/browser/form_parsing/name_field_unittest.cc b/components/autofill/core/browser/form_parsing/name_field_unittest.cc
index dbf0496..043babe 100644
--- a/components/autofill/core/browser/form_parsing/name_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/name_field_unittest.cc
@@ -25,13 +25,9 @@
   NameFieldTest& operator=(const NameFieldTest&) = delete;
 
  protected:
-  std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language = LanguageCode("us")) override {
-    return NameField::Parse(scanner, client_country, page_language,
-                            *GetActivePatternSource(),
-                            /*log_manager=*/nullptr);
+  std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                   AutofillScanner* scanner) override {
+    return NameField::Parse(context, scanner, nullptr);
   }
 };
 
diff --git a/components/autofill/core/browser/form_parsing/numeric_quantity_field.cc b/components/autofill/core/browser/form_parsing/numeric_quantity_field.cc
index 1460244..6273550 100644
--- a/components/autofill/core/browser/form_parsing/numeric_quantity_field.cc
+++ b/components/autofill/core/browser/form_parsing/numeric_quantity_field.cc
@@ -13,17 +13,15 @@
 
 // static
 std::unique_ptr<FormField> NumericQuantityField::Parse(
+    ParsingContext& context,
     AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
     LogManager* log_manager) {
   raw_ptr<AutofillField> field;
-  base::span<const MatchPatternRef> quantity_patterns =
-      GetMatchPatterns("NUMERIC_QUANTITY", page_language, pattern_source);
+  base::span<const MatchPatternRef> quantity_patterns = GetMatchPatterns(
+      "NUMERIC_QUANTITY", context.page_language, context.pattern_source);
 
   if (ParseFieldSpecifics(
-          scanner, kNumericQuantityRe,
+          context, scanner, kNumericQuantityRe,
           kDefaultMatchParamsWith<
               MatchFieldType::kNumber, MatchFieldType::kSelect,
               MatchFieldType::kTextArea, MatchFieldType::kSearch>,
diff --git a/components/autofill/core/browser/form_parsing/numeric_quantity_field.h b/components/autofill/core/browser/form_parsing/numeric_quantity_field.h
index e26911c5..f76800f 100644
--- a/components/autofill/core/browser/form_parsing/numeric_quantity_field.h
+++ b/components/autofill/core/browser/form_parsing/numeric_quantity_field.h
@@ -21,12 +21,9 @@
 // Numeric quantities that are not eligible to be filled by Autofill.
 class NumericQuantityField : public FormField {
  public:
-  static std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                          AutofillScanner* scanner,
+                                          LogManager* log_manager);
 
   NumericQuantityField(const NumericQuantityField&) = delete;
   NumericQuantityField& operator=(const NumericQuantityField&) = delete;
diff --git a/components/autofill/core/browser/form_parsing/numeric_quantity_field_unittest.cc b/components/autofill/core/browser/form_parsing/numeric_quantity_field_unittest.cc
index 7d52a6bb..88a933eb 100644
--- a/components/autofill/core/browser/form_parsing/numeric_quantity_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/numeric_quantity_field_unittest.cc
@@ -17,13 +17,9 @@
   NumericQuantityFieldTest& operator=(const NumericQuantityFieldTest&) = delete;
 
  protected:
-  std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language = LanguageCode("en")) override {
-    return NumericQuantityField::Parse(scanner, client_country, page_language,
-                                       *GetActivePatternSource(),
-                                       /*log_manager=*/nullptr);
+  std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                   AutofillScanner* scanner) override {
+    return NumericQuantityField::Parse(context, scanner, nullptr);
   }
 };
 
diff --git a/components/autofill/core/browser/form_parsing/parsing_test_utils.cc b/components/autofill/core/browser/form_parsing/parsing_test_utils.cc
index c4d7c13..666f4f75 100644
--- a/components/autofill/core/browser/form_parsing/parsing_test_utils.cc
+++ b/components/autofill/core/browser/form_parsing/parsing_test_utils.cc
@@ -88,7 +88,9 @@
     const GeoIpCountryCode& client_country,
     const LanguageCode& page_language) {
   AutofillScanner scanner(list_);
-  field_ = Parse(&scanner, client_country, page_language);
+  ParsingContext context(client_country, page_language,
+                         *GetActivePatternSource());
+  field_ = Parse(context, &scanner);
 
   if (parse_result == ParseResult::NOT_PARSED) {
     ASSERT_EQ(nullptr, field_.get());
diff --git a/components/autofill/core/browser/form_parsing/parsing_test_utils.h b/components/autofill/core/browser/form_parsing/parsing_test_utils.h
index cb1f7d1..5097f61 100644
--- a/components/autofill/core/browser/form_parsing/parsing_test_utils.h
+++ b/components/autofill/core/browser/form_parsing/parsing_test_utils.h
@@ -93,10 +93,8 @@
   void TestClassificationExpectations();
 
   // Apply the parsing with a specific parser.
-  virtual std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language) = 0;
+  virtual std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                           AutofillScanner* scanner) = 0;
 
   FieldRendererId MakeFieldRendererId();
 
diff --git a/components/autofill/core/browser/form_parsing/phone_field.cc b/components/autofill/core/browser/form_parsing/phone_field.cc
index 2edbd90..24bcf32 100644
--- a/components/autofill/core/browser/form_parsing/phone_field.cc
+++ b/components/autofill/core/browser/form_parsing/phone_field.cc
@@ -188,11 +188,10 @@
 }
 
 // static
-bool PhoneField::ParseGrammar(const PhoneGrammar& grammar,
+bool PhoneField::ParseGrammar(ParsingContext& context,
+                              const PhoneGrammar& grammar,
                               ParsedPhoneFields& parsed_fields,
                               AutofillScanner* scanner,
-                              const LanguageCode& page_language,
-                              PatternSource pattern_source,
                               LogManager* log_manager) {
   for (const auto& rule : grammar) {
     const bool is_country_code_field = rule.phone_part == FIELD_COUNTRY_CODE;
@@ -217,13 +216,12 @@
     // regex of this rule.
     bool parsed =
         is_empty_label
-            ? ParseEmptyLabel(scanner, &parsed_fields[rule.phone_part])
-            : ParsePhoneField(scanner, GetRegExp(rule.regex),
+            ? ParseEmptyLabel(context, scanner, &parsed_fields[rule.phone_part])
+            : ParsePhoneField(context, scanner, GetRegExp(rule.regex),
                               &parsed_fields[rule.phone_part],
                               {log_manager, GetRegExpName(rule.regex)},
                               is_country_code_field,
-                              GetJSONFieldType(rule.regex), page_language,
-                              pattern_source);
+                              GetJSONFieldType(rule.regex));
     if (!parsed)
       return false;
 
@@ -237,12 +235,9 @@
 }
 
 // static
-std::unique_ptr<FormField> PhoneField::Parse(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
+std::unique_ptr<FormField> PhoneField::Parse(ParsingContext& context,
+                                             AutofillScanner* scanner,
+                                             LogManager* log_manager) {
   if (scanner->IsEnd())
     return nullptr;
 
@@ -254,8 +249,7 @@
   int grammar_id = 0;
   for (const PhoneGrammar& grammar : GetPhoneGrammars()) {
     std::fill(parsed_fields.begin(), parsed_fields.end(), nullptr);
-    if (ParseGrammar(grammar, parsed_fields, scanner, page_language,
-                     pattern_source, log_manager)) {
+    if (ParseGrammar(context, grammar, parsed_fields, scanner, log_manager)) {
       found_matching_grammar = true;
       break;
     }
@@ -272,15 +266,15 @@
   bool suffix_matched = false;
   if (!parsed_fields[FIELD_SUFFIX]) {
     suffix_matched =
-        ParsePhoneField(scanner, kPhoneSuffixRe, &parsed_fields[FIELD_SUFFIX],
+        ParsePhoneField(context, scanner, kPhoneSuffixRe,
+                        &parsed_fields[FIELD_SUFFIX],
                         {log_manager, "kPhoneSuffixRe"},
-                        /*is_country_code_field=*/false, "PHONE_SUFFIX",
-                        page_language, pattern_source) ||
-        ParsePhoneField(
-            scanner, kPhoneSuffixSeparatorRe, &parsed_fields[FIELD_SUFFIX],
-            {log_manager, "kPhoneSuffixSeparatorRe"},
-            /*is_country_code_field=*/false, "PHONE_SUFFIX_SEPARATOR",
-            page_language, pattern_source);
+                        /*is_country_code_field=*/false, "PHONE_SUFFIX") ||
+        ParsePhoneField(context, scanner, kPhoneSuffixSeparatorRe,
+                        &parsed_fields[FIELD_SUFFIX],
+                        {log_manager, "kPhoneSuffixSeparatorRe"},
+                        /*is_country_code_field=*/false,
+                        "PHONE_SUFFIX_SEPARATOR");
   }
   AutofillMetrics::LogPhoneNumberGrammarMatched(grammar_id, suffix_matched,
                                                 GetPhoneGrammars().size());
@@ -288,10 +282,10 @@
   // Now look for an extension.
   // The extension is unused, but it is parsed to prevent other parsers from
   // misclassifying it as something else.
-  ParsePhoneField(scanner, kPhoneExtensionRe, &parsed_fields[FIELD_EXTENSION],
+  ParsePhoneField(context, scanner, kPhoneExtensionRe,
+                  &parsed_fields[FIELD_EXTENSION],
                   {log_manager, "kPhoneExtensionRe"},
-                  /*is_country_code_field=*/false, "PHONE_EXTENSION",
-                  page_language, pattern_source);
+                  /*is_country_code_field=*/false, "PHONE_EXTENSION");
 
   return base::WrapUnique(new PhoneField(std::move(parsed_fields)));
 }
@@ -453,14 +447,13 @@
 }
 
 // static
-bool PhoneField::ParsePhoneField(AutofillScanner* scanner,
+bool PhoneField::ParsePhoneField(ParsingContext& context,
+                                 AutofillScanner* scanner,
                                  base::StringPiece16 regex,
                                  raw_ptr<AutofillField>* field,
                                  const RegExLogging& logging,
                                  const bool is_country_code_field,
-                                 const std::string& json_field_type,
-                                 const LanguageCode& page_language,
-                                 PatternSource pattern_source) {
+                                 const std::string& json_field_type) {
   MatchParams match_type = kDefaultMatchParamsWith<MatchFieldType::kTelephone,
                                                    MatchFieldType::kNumber>;
   // Include the selection boxes too for the matching of the phone country code.
@@ -470,11 +463,11 @@
                                          MatchFieldType::kSelect>;
   }
 
-  base::span<const MatchPatternRef> patterns =
-      GetMatchPatterns(json_field_type, page_language, pattern_source);
+  base::span<const MatchPatternRef> patterns = GetMatchPatterns(
+      json_field_type, context.page_language, context.pattern_source);
 
-  return ParseFieldSpecifics(scanner, regex, match_type, patterns, field,
-                             logging);
+  return ParseFieldSpecifics(context, scanner, regex, match_type, patterns,
+                             field, logging);
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/form_parsing/phone_field.h b/components/autofill/core/browser/form_parsing/phone_field.h
index b75df7f4..18fa17f 100644
--- a/components/autofill/core/browser/form_parsing/phone_field.h
+++ b/components/autofill/core/browser/form_parsing/phone_field.h
@@ -34,12 +34,9 @@
   PhoneField(const PhoneField&) = delete;
   PhoneField& operator=(const PhoneField&) = delete;
 
-  static std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                          AutofillScanner* scanner,
+                                          LogManager* log_manager);
 
 #if defined(UNIT_TEST)
   // Assign types to the fields for the testing purposes.
@@ -105,22 +102,20 @@
   static std::string GetJSONFieldType(RegexType phonetype_id);
 
   // Convenient wrapper for ParseFieldSpecifics().
-  static bool ParsePhoneField(AutofillScanner* scanner,
+  static bool ParsePhoneField(ParsingContext& context,
+                              AutofillScanner* scanner,
                               base::StringPiece16 regex,
                               raw_ptr<AutofillField>* field,
                               const RegExLogging& logging,
                               const bool is_country_code_field,
-                              const std::string& json_field_type,
-                              const LanguageCode& page_language,
-                              PatternSource pattern_source);
+                              const std::string& json_field_type);
 
   // Tries parsing the given `grammar` into `parsed_fields` and returns true
   // if it succeeded.
-  static bool ParseGrammar(const PhoneGrammar& grammar,
+  static bool ParseGrammar(ParsingContext& context,
+                           const PhoneGrammar& grammar,
                            ParsedPhoneFields& parsed_fields,
                            AutofillScanner* scanner,
-                           const LanguageCode& page_language,
-                           PatternSource pattern_source,
                            LogManager* log_manager);
 
   // Returns true if |scanner| points to a <select> field that appears to be the
diff --git a/components/autofill/core/browser/form_parsing/phone_field_unittest.cc b/components/autofill/core/browser/form_parsing/phone_field_unittest.cc
index c8b58ff..edcf958 100644
--- a/components/autofill/core/browser/form_parsing/phone_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/phone_field_unittest.cc
@@ -58,13 +58,12 @@
 
  protected:
   // Downcast for tests.
-  static std::unique_ptr<PhoneField> Parse(AutofillScanner* scanner) {
+  static std::unique_ptr<PhoneField> Parse(ParsingContext& context,
+                                           AutofillScanner* scanner) {
     // An empty page_language means the language is unknown and patterns of all
     // languages are used.
     std::unique_ptr<FormField> field =
-        PhoneField::Parse(scanner, GeoIpCountryCode(""), LanguageCode(""),
-                          *GetActivePatternSource(),
-                          /*log_manager=*/nullptr);
+        PhoneField::Parse(context, scanner, nullptr);
     return std::unique_ptr<PhoneField>(
         static_cast<PhoneField*>(field.release()));
   }
@@ -144,7 +143,9 @@
 
   // Parse.
   AutofillScanner scanner(list_);
-  field_ = Parse(&scanner);
+  ParsingContext context(GeoIpCountryCode(""), LanguageCode(""),
+                         PatternSource::kLegacy);
+  field_ = Parse(context, &scanner);
   ASSERT_EQ(expect_success, field_.get() != nullptr);
 
   // Verify expecations.
diff --git a/components/autofill/core/browser/form_parsing/price_field.cc b/components/autofill/core/browser/form_parsing/price_field.cc
index e4e95078..f5a0ab7 100644
--- a/components/autofill/core/browser/form_parsing/price_field.cc
+++ b/components/autofill/core/browser/form_parsing/price_field.cc
@@ -12,18 +12,15 @@
 namespace autofill {
 
 // static
-std::unique_ptr<FormField> PriceField::Parse(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
+std::unique_ptr<FormField> PriceField::Parse(ParsingContext& context,
+                                             AutofillScanner* scanner,
+                                             LogManager* log_manager) {
   raw_ptr<AutofillField> field;
   base::span<const MatchPatternRef> price_patterns =
-      GetMatchPatterns("PRICE", page_language, pattern_source);
+      GetMatchPatterns("PRICE", context.page_language, context.pattern_source);
 
   if (ParseFieldSpecifics(
-          scanner, kPriceRe,
+          context, scanner, kPriceRe,
           kDefaultMatchParamsWith<
               MatchFieldType::kNumber, MatchFieldType::kSelect,
               MatchFieldType::kTextArea, MatchFieldType::kSearch>,
diff --git a/components/autofill/core/browser/form_parsing/price_field.h b/components/autofill/core/browser/form_parsing/price_field.h
index bef2d21..b6aa2d7 100644
--- a/components/autofill/core/browser/form_parsing/price_field.h
+++ b/components/autofill/core/browser/form_parsing/price_field.h
@@ -24,12 +24,9 @@
 // reduce the number of false positives.
 class PriceField : public FormField {
  public:
-  static std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                          AutofillScanner* scanner,
+                                          LogManager* log_manager);
   explicit PriceField(const AutofillField* field);
 
   PriceField(const PriceField&) = delete;
diff --git a/components/autofill/core/browser/form_parsing/price_field_unittest.cc b/components/autofill/core/browser/form_parsing/price_field_unittest.cc
index c606c4d..a0f3005 100644
--- a/components/autofill/core/browser/form_parsing/price_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/price_field_unittest.cc
@@ -19,13 +19,9 @@
   PriceFieldTest& operator=(const PriceFieldTest&) = delete;
 
  protected:
-  std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language = LanguageCode("en")) override {
-    return PriceField::Parse(scanner, client_country, page_language,
-                             *GetActivePatternSource(),
-                             /*log_manager=*/nullptr);
+  std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                   AutofillScanner* scanner) override {
+    return PriceField::Parse(context, scanner, nullptr);
   }
 };
 
diff --git a/components/autofill/core/browser/form_parsing/search_field.cc b/components/autofill/core/browser/form_parsing/search_field.cc
index 578b144..1da8881 100644
--- a/components/autofill/core/browser/form_parsing/search_field.cc
+++ b/components/autofill/core/browser/form_parsing/search_field.cc
@@ -12,17 +12,14 @@
 namespace autofill {
 
 // static
-std::unique_ptr<FormField> SearchField::Parse(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
+std::unique_ptr<FormField> SearchField::Parse(ParsingContext& context,
+                                              AutofillScanner* scanner,
+                                              LogManager* log_manager) {
   raw_ptr<AutofillField> field;
-  base::span<const MatchPatternRef> patterns =
-      GetMatchPatterns(SEARCH_TERM, page_language, pattern_source);
+  base::span<const MatchPatternRef> patterns = GetMatchPatterns(
+      SEARCH_TERM, context.page_language, context.pattern_source);
 
-  if (ParseFieldSpecifics(scanner, kSearchTermRe,
+  if (ParseFieldSpecifics(context, scanner, kSearchTermRe,
                           kDefaultMatchParamsWith<MatchFieldType::kSearch,
                                                   MatchFieldType::kTextArea>,
                           patterns, &field, {log_manager, "kSearchTermRe"})) {
diff --git a/components/autofill/core/browser/form_parsing/search_field.h b/components/autofill/core/browser/form_parsing/search_field.h
index a6981d3..3429f9c 100644
--- a/components/autofill/core/browser/form_parsing/search_field.h
+++ b/components/autofill/core/browser/form_parsing/search_field.h
@@ -24,12 +24,9 @@
 // to reduce the number of false positives.
 class SearchField : public FormField {
  public:
-  static std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                          AutofillScanner* scanner,
+                                          LogManager* log_manager);
   explicit SearchField(const AutofillField* field);
 
   SearchField(const SearchField&) = delete;
diff --git a/components/autofill/core/browser/form_parsing/search_field_unittest.cc b/components/autofill/core/browser/form_parsing/search_field_unittest.cc
index 81e8cfb8..bc52b43 100644
--- a/components/autofill/core/browser/form_parsing/search_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/search_field_unittest.cc
@@ -20,13 +20,9 @@
   SearchFieldTest& operator=(const SearchFieldTest&) = delete;
 
  protected:
-  std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language = LanguageCode("en")) override {
-    return SearchField::Parse(scanner, client_country, page_language,
-                              *GetActivePatternSource(),
-                              /*log_manager=*/nullptr);
+  std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                   AutofillScanner* scanner) override {
+    return SearchField::Parse(context, scanner, nullptr);
   }
 };
 
diff --git a/components/autofill/core/browser/form_parsing/standalone_cvc_field.cc b/components/autofill/core/browser/form_parsing/standalone_cvc_field.cc
index e8b05a5..7574d3e 100644
--- a/components/autofill/core/browser/form_parsing/standalone_cvc_field.cc
+++ b/components/autofill/core/browser/form_parsing/standalone_cvc_field.cc
@@ -13,12 +13,9 @@
 namespace autofill {
 
 // static
-std::unique_ptr<FormField> StandaloneCvcField::Parse(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
+std::unique_ptr<FormField> StandaloneCvcField::Parse(ParsingContext& context,
+                                                     AutofillScanner* scanner,
+                                                     LogManager* log_manager) {
   if (!base::FeatureList::IsEnabled(
           features::kAutofillParseVcnCardOnFileStandaloneCvcFields)) {
     return nullptr;
@@ -27,21 +24,23 @@
   // Ignore gift card fields as both |kGiftCardRe| and |kCardCvcRe| matches
   // "gift card pin" and "gift card code" but it should only match
   // |kGiftCardRe|.
-  if (MatchGiftCard(scanner, log_manager, page_language, pattern_source)) {
+  if (MatchGiftCard(context, scanner, log_manager)) {
     return nullptr;
   }
 
   raw_ptr<AutofillField> field;
-  base::span<const MatchPatternRef> cvc_patterns = GetMatchPatterns(
-      CREDIT_CARD_VERIFICATION_CODE, page_language, pattern_source);
+  base::span<const MatchPatternRef> cvc_patterns =
+      GetMatchPatterns(CREDIT_CARD_VERIFICATION_CODE, context.page_language,
+                       context.pattern_source);
 
   // CVC fields can occur in many different field types so we check for each
   const auto kMatchNumTelAndPwd =
       kDefaultMatchParamsWith<MatchFieldType::kNumber,
                               MatchFieldType::kTelephone,
                               MatchFieldType::kPassword>;
-  if (ParseFieldSpecifics(scanner, kCardCvcRe, kMatchNumTelAndPwd, cvc_patterns,
-                          &field, {log_manager, "kCardCvcRe(standalone)"})) {
+  if (ParseFieldSpecifics(context, scanner, kCardCvcRe, kMatchNumTelAndPwd,
+                          cvc_patterns, &field,
+                          {log_manager, "kCardCvcRe(standalone)"})) {
     return std::make_unique<StandaloneCvcField>(field);
   }
 
@@ -51,23 +50,23 @@
 StandaloneCvcField::~StandaloneCvcField() = default;
 
 // static
-bool StandaloneCvcField::MatchGiftCard(AutofillScanner* scanner,
-                                       LogManager* log_manager,
-                                       const LanguageCode& page_language,
-                                       PatternSource pattern_source) {
-  if (scanner->IsEnd())
+bool StandaloneCvcField::MatchGiftCard(ParsingContext& context,
+                                       AutofillScanner* scanner,
+                                       LogManager* log_manager) {
+  if (scanner->IsEnd()) {
     return false;
+  }
 
   const auto kMatchFieldType = kDefaultMatchParamsWith<
       MatchFieldType::kNumber, MatchFieldType::kTelephone,
       MatchFieldType::kSearch, MatchFieldType::kPassword>;
-  base::span<const MatchPatternRef> gift_card_patterns =
-      GetMatchPatterns("GIFT_CARD", page_language, pattern_source);
+  base::span<const MatchPatternRef> gift_card_patterns = GetMatchPatterns(
+      "GIFT_CARD", context.page_language, context.pattern_source);
 
   size_t saved_cursor = scanner->SaveCursor();
   const bool gift_card_match = ParseFieldSpecifics(
-      scanner, kGiftCardRe, kMatchFieldType, gift_card_patterns, nullptr,
-      {log_manager, "kGiftCardRe"});
+      context, scanner, kGiftCardRe, kMatchFieldType, gift_card_patterns,
+      nullptr, {log_manager, "kGiftCardRe"});
   // MatchGiftCard only wants to test the presence of a gift card but not
   // consume the field.
   scanner->RewindTo(saved_cursor);
diff --git a/components/autofill/core/browser/form_parsing/standalone_cvc_field.h b/components/autofill/core/browser/form_parsing/standalone_cvc_field.h
index 8ed5cad..41649d6 100644
--- a/components/autofill/core/browser/form_parsing/standalone_cvc_field.h
+++ b/components/autofill/core/browser/form_parsing/standalone_cvc_field.h
@@ -21,12 +21,9 @@
 // A form field that accepts a standalone cvc.
 class StandaloneCvcField : public FormField {
  public:
-  static std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                          AutofillScanner* scanner,
+                                          LogManager* log_manager);
 
   explicit StandaloneCvcField(const AutofillField* field);
 
@@ -42,10 +39,9 @@
   raw_ptr<const AutofillField> field_;
 
   // static
-  static bool MatchGiftCard(AutofillScanner* scanner,
-                            LogManager* log_manager,
-                            const LanguageCode& page_language,
-                            PatternSource pattern_source);
+  static bool MatchGiftCard(ParsingContext& context,
+                            AutofillScanner* scanner,
+                            LogManager* log_manager);
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/form_parsing/standalone_cvc_field_unittest.cc b/components/autofill/core/browser/form_parsing/standalone_cvc_field_unittest.cc
index b6db7be..c8b0148 100644
--- a/components/autofill/core/browser/form_parsing/standalone_cvc_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/standalone_cvc_field_unittest.cc
@@ -19,13 +19,9 @@
   StandaloneCvcFieldTest& operator=(const StandaloneCvcFieldTest&) = delete;
 
  protected:
-  std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language = LanguageCode("en")) override {
-    return StandaloneCvcField::Parse(scanner, client_country, page_language,
-                                     *GetActivePatternSource(),
-                                     /*log_manager=*/nullptr);
+  std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                   AutofillScanner* scanner) override {
+    return StandaloneCvcField::Parse(context, scanner, nullptr);
   }
 
   base::test::ScopedFeatureList scoped_feature_list_;
diff --git a/components/autofill/core/browser/form_parsing/travel_field.cc b/components/autofill/core/browser/form_parsing/travel_field.cc
index 7cf1674d..88cc628 100644
--- a/components/autofill/core/browser/form_parsing/travel_field.cc
+++ b/components/autofill/core/browser/form_parsing/travel_field.cc
@@ -12,37 +12,42 @@
 
 namespace autofill {
 
+namespace {
+base::span<const MatchPatternRef> GetMatchPatterns(base::StringPiece name,
+                                                   ParsingContext& context) {
+  return GetMatchPatterns(name, context.page_language, context.pattern_source);
+}
+}  // namespace
+
 TravelField::~TravelField() = default;
 
 // static
-std::unique_ptr<FormField> TravelField::Parse(
-    AutofillScanner* scanner,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    LogManager* log_manager) {
-  if (!scanner || scanner->IsEnd())
+std::unique_ptr<FormField> TravelField::Parse(ParsingContext& context,
+                                              AutofillScanner* scanner,
+                                              LogManager* log_manager) {
+  if (!scanner || scanner->IsEnd()) {
     return nullptr;
+  }
 
   base::span<const MatchPatternRef> passport_patterns =
-      GetMatchPatterns("PASSPORT", page_language, pattern_source);
+      GetMatchPatterns("PASSPORT", context);
   base::span<const MatchPatternRef> travel_origin_patterns =
-      GetMatchPatterns("TRAVEL_ORIGIN", page_language, pattern_source);
+      GetMatchPatterns("TRAVEL_ORIGIN", context);
   base::span<const MatchPatternRef> travel_destination_patterns =
-      GetMatchPatterns("TRAVEL_DESTINATION", page_language, pattern_source);
+      GetMatchPatterns("TRAVEL_DESTINATION", context);
   base::span<const MatchPatternRef> flight_patterns =
-      GetMatchPatterns("FLIGHT", page_language, pattern_source);
+      GetMatchPatterns("FLIGHT", context);
 
   auto travel_field = std::make_unique<TravelField>();
-  if (ParseField(scanner, kPassportRe, passport_patterns,
+  if (ParseField(context, scanner, kPassportRe, passport_patterns,
                  &travel_field->passport_, {log_manager, "kPassportRe"}) ||
-      ParseField(scanner, kTravelOriginRe, travel_origin_patterns,
+      ParseField(context, scanner, kTravelOriginRe, travel_origin_patterns,
                  &travel_field->origin_, {log_manager, "kTravelOriginRe"}) ||
-      ParseField(scanner, kTravelDestinationRe, travel_destination_patterns,
-                 &travel_field->destination_,
+      ParseField(context, scanner, kTravelDestinationRe,
+                 travel_destination_patterns, &travel_field->destination_,
                  {log_manager, "kTravelDestinationRe"}) ||
-      ParseField(scanner, kFlightRe, flight_patterns, &travel_field->flight_,
-                 {log_manager, "kFlightRe"})) {
+      ParseField(context, scanner, kFlightRe, flight_patterns,
+                 &travel_field->flight_, {log_manager, "kFlightRe"})) {
     // If any regex matches, then we found a travel field.
     return std::move(travel_field);
   }
diff --git a/components/autofill/core/browser/form_parsing/travel_field.h b/components/autofill/core/browser/form_parsing/travel_field.h
index 70a8b62..3fc56bc 100644
--- a/components/autofill/core/browser/form_parsing/travel_field.h
+++ b/components/autofill/core/browser/form_parsing/travel_field.h
@@ -21,12 +21,9 @@
  public:
   ~TravelField() override;
 
-  static std::unique_ptr<FormField> Parse(
-      AutofillScanner* scanner,
-      const GeoIpCountryCode& client_country,
-      const LanguageCode& page_language,
-      PatternSource pattern_source,
-      LogManager* log_manager);
+  static std::unique_ptr<FormField> Parse(ParsingContext& context,
+                                          AutofillScanner* scanner,
+                                          LogManager* log_manager);
 
  protected:
   void AddClassifications(FieldCandidatesMap& field_candidates) const override;
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index a9ad919..3c6afdb 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -1302,8 +1302,9 @@
   has_author_specified_upi_vpa_hint_ = false;
   std::map<FieldSignature, size_t> field_rank_map;
   for (const std::unique_ptr<AutofillField>& field : fields_) {
-    if (!field->parsed_autocomplete)
+    if (!field->parsed_autocomplete) {
       continue;
+    }
 
     // A parsable autocomplete value was specified. Even an invalid field_type
     // is considered a type hint. This allows a website's author to specify an
@@ -1350,24 +1351,21 @@
   return has_autocomplete;
 }
 
-void FormStructure::ParseFieldTypesWithPatterns(
-    PatternSource pattern_source,
-    LogManager* log_manager) {
+void FormStructure::ParseFieldTypesWithPatterns(PatternSource pattern_source,
+                                                LogManager* log_manager) {
   FieldCandidatesMap field_type_map;
   const LanguageCode& page_language =
       base::FeatureList::IsEnabled(features::kAutofillPageLanguageDetection)
           ? current_page_language_
           : LanguageCode();
+  ParsingContext context(client_country_, page_language, pattern_source);
   if (ShouldRunHeuristics()) {
-    FormField::ParseFormFields(fields_, client_country_, page_language,
-                               is_form_tag_, pattern_source, field_type_map,
+    FormField::ParseFormFields(context, fields_, is_form_tag_, field_type_map,
                                log_manager);
   } else if (ShouldRunHeuristicsForSingleFieldForms()) {
-    FormField::ParseSingleFieldForms(fields_, client_country_, page_language,
-                                     is_form_tag_, pattern_source,
+    FormField::ParseSingleFieldForms(context, fields_, is_form_tag_,
                                      field_type_map, log_manager);
-    FormField::ParseStandaloneCVCFields(fields_, client_country_, page_language,
-                                        pattern_source, field_type_map,
+    FormField::ParseStandaloneCVCFields(context, fields_, field_type_map,
                                         log_manager);
 
     // For standalone email fields inside a form tag, allow heuristics even
@@ -1376,13 +1374,13 @@
     if (is_form_tag_ &&
         base::FeatureList::IsEnabled(
             features::kAutofillEnableEmailHeuristicOnlyAddressForms)) {
-      FormField::ParseStandaloneEmailFields(fields_, client_country_,
-                                            page_language, pattern_source,
-                                            field_type_map, log_manager);
+      FormField::ParseStandaloneEmailFields(context, fields_, field_type_map,
+                                            log_manager);
     }
   }
-  if (field_type_map.empty())
+  if (field_type_map.empty()) {
     return;
+  }
 
   // Fields can share the same field signature. This map records for each
   // signature how many fields with the same signature have been observed.
@@ -1391,6 +1389,7 @@
     auto iter = field_type_map.find(field->global_id());
     if (iter == field_type_map.end())
       continue;
+
     const FieldCandidates& candidates = iter->second;
     field->set_heuristic_type(PatternSourceToHeuristicSource(pattern_source),
                               candidates.BestHeuristicType());
@@ -1591,16 +1590,18 @@
 }
 
 bool FormStructure::IsMalformed() const {
-  if (!field_count())  // Nothing to add.
+  if (!field_count()) {  // Nothing to add.
     return true;
+  }
 
   // Some badly formatted web sites repeat fields - limit number of fields to
   // 250, which is far larger than any valid form and proto still fits into 10K.
   // Do not send requests for forms with more than this many fields, as they are
   // near certainly not valid/auto-fillable.
   const size_t kMaxFieldsOnTheForm = 250;
-  if (field_count() > kMaxFieldsOnTheForm)
+  if (field_count() > kMaxFieldsOnTheForm) {
     return true;
+  }
   return false;
 }
 
diff --git a/components/autofill/core/browser/form_structure_rationalization_engine.cc b/components/autofill/core/browser/form_structure_rationalization_engine.cc
index 88267ad..98296f2a 100644
--- a/components/autofill/core/browser/form_structure_rationalization_engine.cc
+++ b/components/autofill/core/browser/form_structure_rationalization_engine.cc
@@ -128,10 +128,10 @@
 }
 
 namespace internal {
-bool IsEnvironmentConditionFulfilled(const EnvironmentCondition& env,
-                                     const GeoIpCountryCode& client_country) {
+bool IsEnvironmentConditionFulfilled(ParsingContext& context,
+                                     const EnvironmentCondition& env) {
   if (!env.country_list.empty() &&
-      !base::Contains(env.country_list, client_country)) {
+      !base::Contains(env.country_list, context.client_country)) {
     return false;
   }
 
@@ -142,11 +142,9 @@
   return true;
 }
 
-bool IsFieldConditionFulfilledIgnoringLocation(
-    const FieldCondition& condition,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    const AutofillField& field) {
+bool IsFieldConditionFulfilledIgnoringLocation(ParsingContext& context,
+                                               const FieldCondition& condition,
+                                               const AutofillField& field) {
   if (condition.possible_overall_types.has_value() &&
       !condition.possible_overall_types->contains(
           field.Type().GetStorableType())) {
@@ -154,9 +152,10 @@
   }
 
   if (condition.regex_reference_match.has_value()) {
-    base::span<const MatchPatternRef> patterns = GetMatchPatterns(
-        condition.regex_reference_match.value(), page_language, pattern_source);
-    if (!FormField::FieldMatchesMatchPatternRef(patterns, field)) {
+    base::span<const MatchPatternRef> patterns =
+        GetMatchPatterns(condition.regex_reference_match.value(),
+                         context.page_language, context.pattern_source);
+    if (!FormField::FieldMatchesMatchPatternRef(context, patterns, field)) {
       return false;
     }
   }
@@ -165,12 +164,10 @@
 }
 
 std::optional<size_t> FindFieldMeetingCondition(
+    ParsingContext& context,
     const std::vector<std::unique_ptr<AutofillField>>& fields,
     size_t start_index,
-    const FieldCondition& condition,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
+    const FieldCondition& condition) {
   int direction = [&condition]() {
     switch (condition.location) {
       case FieldLocation::kPredecessor:
@@ -187,8 +184,8 @@
   for (int i = start_index + direction;
        i >= 0 && i < static_cast<int>(fields.size()); i += direction) {
     const AutofillField& candidate_field = *fields[i];
-    if (IsFieldConditionFulfilledIgnoringLocation(
-            condition, page_language, pattern_source, candidate_field)) {
+    if (IsFieldConditionFulfilledIgnoringLocation(context, condition,
+                                                  candidate_field)) {
       return static_cast<size_t>(i);
     }
 
@@ -205,15 +202,13 @@
 }
 
 void ApplyRuleIfApplicable(
+    ParsingContext& context,
     const RationalizationRule& rule,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
     const std::vector<std::unique_ptr<AutofillField>>& fields,
     LogManager* log_manager) {
   if (rule.environment_condition.has_value() &&
-      !IsEnvironmentConditionFulfilled(rule.environment_condition.value(),
-                                       client_country)) {
+      !IsEnvironmentConditionFulfilled(context,
+                                       rule.environment_condition.value())) {
     return;
   }
 
@@ -221,9 +216,8 @@
     const std::unique_ptr<AutofillField>& trigger_field = fields[i];
 
     // Check whether we have found a trigger field at index i.
-    if (!IsFieldConditionFulfilledIgnoringLocation(
-            rule.trigger_field, page_language, pattern_source,
-            *trigger_field)) {
+    if (!IsFieldConditionFulfilledIgnoringLocation(context, rule.trigger_field,
+                                                   *trigger_field)) {
       continue;
     }
 
@@ -233,9 +227,8 @@
     for (const FieldCondition& other_field_condition :
          rule.other_field_conditions) {
       CHECK_NE(other_field_condition.location, FieldLocation::kTriggerField);
-      std::optional<size_t> match_index = FindFieldMeetingCondition(
-          fields, i, other_field_condition, client_country, page_language,
-          pattern_source);
+      std::optional<size_t> match_index =
+          FindFieldMeetingCondition(context, fields, i, other_field_condition);
       if (!match_index.has_value()) {
         break;
       }
@@ -269,9 +262,7 @@
 }  // namespace internal
 
 void ApplyRationalizationEngineRules(
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
+    ParsingContext& context,
     const std::vector<std::unique_ptr<AutofillField>>& fields,
     LogManager* log_manager) {
   auto create_rules = [] {
@@ -326,8 +317,7 @@
       kRationalizationRules(create_rules());
 
   for (const RationalizationRule& rule : *kRationalizationRules) {
-    internal::ApplyRuleIfApplicable(rule, client_country, page_language,
-                                    pattern_source, fields, log_manager);
+    internal::ApplyRuleIfApplicable(context, rule, fields, log_manager);
   }
 }
 
diff --git a/components/autofill/core/browser/form_structure_rationalization_engine.h b/components/autofill/core/browser/form_structure_rationalization_engine.h
index 43cf5ea..3adb980 100644
--- a/components/autofill/core/browser/form_structure_rationalization_engine.h
+++ b/components/autofill/core/browser/form_structure_rationalization_engine.h
@@ -21,7 +21,8 @@
 
 namespace autofill {
 class LogManager;
-}
+struct ParsingContext;
+}  // namespace autofill
 
 namespace autofill::rationalization {
 
@@ -175,35 +176,29 @@
 
 // This is only exposed for testing purposes.
 namespace internal {
-bool IsEnvironmentConditionFulfilled(const EnvironmentCondition& env,
-                                     const GeoIpCountryCode& client_country);
+bool IsEnvironmentConditionFulfilled(ParsingContext& context,
+                                     const EnvironmentCondition& env);
 
-bool IsFieldConditionFulfilledIgnoringLocation(
-    const FieldCondition& condition,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
-    const AutofillField& field);
+bool IsFieldConditionFulfilledIgnoringLocation(ParsingContext& context,
+                                               const FieldCondition& condition,
+                                               const AutofillField& field);
 
 // Returns the first index of a field in `fields` that meets the `condition`
 // when starting at `start_index` and walking in the direction of
 // `condition.location`. Returns std::nullopt if no such field exists.
 std::optional<size_t> FindFieldMeetingCondition(
+    ParsingContext& context,
     const std::vector<std::unique_ptr<AutofillField>>& fields,
     size_t start_index,
-    const FieldCondition& condition,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source);
+    const FieldCondition& condition);
 
 // Performs rationalization according `rule` if the the conditions of the
 // rule are met. The `rule` can be executed multiple times on the `fields`.
 // Note that the `fields` vector is const but the fields are mutable. This
 // constness is inherited from the calling sites.
 void ApplyRuleIfApplicable(
+    ParsingContext& context,
     const RationalizationRule& rule,
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
     const std::vector<std::unique_ptr<AutofillField>>& fields,
     LogManager* log_manager = nullptr);
 
@@ -214,9 +209,7 @@
 // Note that the `fields` vector is const but the fields are mutable. This
 // constness is inherited from the calling sites.
 void ApplyRationalizationEngineRules(
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& page_language,
-    PatternSource pattern_source,
+    ParsingContext& context,
     const std::vector<std::unique_ptr<AutofillField>>& fields,
     LogManager* log_manager = nullptr);
 
diff --git a/components/autofill/core/browser/form_structure_rationalization_engine_unittest.cc b/components/autofill/core/browser/form_structure_rationalization_engine_unittest.cc
index f024b07c..8ccca4c 100644
--- a/components/autofill/core/browser/form_structure_rationalization_engine_unittest.cc
+++ b/components/autofill/core/browser/form_structure_rationalization_engine_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/autofill/core/browser/form_structure_rationalization_engine.h"
 
 #include "base/test/scoped_feature_list.h"
+#include "components/autofill/core/browser/form_parsing/form_field.h"
 #include "components/autofill/core/common/autofill_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -122,21 +123,26 @@
   GeoIpCountryCode kMX = GeoIpCountryCode("MX");
   GeoIpCountryCode kBR = GeoIpCountryCode("BR");
   GeoIpCountryCode kUS = GeoIpCountryCode("US");
+  ParsingContext kMXContext(kMX, LanguageCode("es"), PatternSource::kLegacy);
+  ParsingContext kBRContext(kBR, LanguageCode("pt"), PatternSource::kLegacy);
+  ParsingContext kUSContext(kUS, LanguageCode("en"), PatternSource::kLegacy);
 
   EnvironmentCondition no_country_required =
       EnvironmentConditionBuilder().Build();
-  EXPECT_TRUE(IsEnvironmentConditionFulfilled(no_country_required, kMX));
+  EXPECT_TRUE(IsEnvironmentConditionFulfilled(kMXContext, no_country_required));
 
   EnvironmentCondition specific_country_required =
       EnvironmentConditionBuilder().SetCountryList({kMX}).Build();
-  EXPECT_TRUE(IsEnvironmentConditionFulfilled(specific_country_required, kMX));
-  EXPECT_FALSE(IsEnvironmentConditionFulfilled(specific_country_required, kBR));
+  EXPECT_TRUE(
+      IsEnvironmentConditionFulfilled(kMXContext, specific_country_required));
+  EXPECT_FALSE(
+      IsEnvironmentConditionFulfilled(kBRContext, specific_country_required));
 
   EnvironmentCondition one_of_many =
       EnvironmentConditionBuilder().SetCountryList({kBR, kMX}).Build();
-  EXPECT_TRUE(IsEnvironmentConditionFulfilled(one_of_many, kBR));
-  EXPECT_TRUE(IsEnvironmentConditionFulfilled(one_of_many, kMX));
-  EXPECT_FALSE(IsEnvironmentConditionFulfilled(one_of_many, kUS));
+  EXPECT_TRUE(IsEnvironmentConditionFulfilled(kBRContext, one_of_many));
+  EXPECT_TRUE(IsEnvironmentConditionFulfilled(kMXContext, one_of_many));
+  EXPECT_FALSE(IsEnvironmentConditionFulfilled(kUSContext, one_of_many));
 }
 
 // Verifies that the experiment state is checked.
@@ -144,6 +150,7 @@
      IsEnvironmentConditionFulfilled_CheckExperiment) {
   using internal::IsEnvironmentConditionFulfilled;
   GeoIpCountryCode kMX = GeoIpCountryCode("MX");
+  ParsingContext kMXContext(kMX, LanguageCode("es"), PatternSource::kLegacy);
 
   EnvironmentCondition no_experiment_required =
       EnvironmentConditionBuilder().Build();
@@ -155,15 +162,19 @@
   {
     base::test::ScopedFeatureList enable_feature(
         kTestFeatureForFormStructureRationalizationEngine);
-    EXPECT_TRUE(IsEnvironmentConditionFulfilled(no_experiment_required, kMX));
-    EXPECT_TRUE(IsEnvironmentConditionFulfilled(experiment_required, kMX));
+    EXPECT_TRUE(
+        IsEnvironmentConditionFulfilled(kMXContext, no_experiment_required));
+    EXPECT_TRUE(
+        IsEnvironmentConditionFulfilled(kMXContext, experiment_required));
   }
   {
     base::test::ScopedFeatureList disable_feature;
     disable_feature.InitAndDisableFeature(
         kTestFeatureForFormStructureRationalizationEngine);
-    EXPECT_TRUE(IsEnvironmentConditionFulfilled(no_experiment_required, kMX));
-    EXPECT_FALSE(IsEnvironmentConditionFulfilled(experiment_required, kMX));
+    EXPECT_TRUE(
+        IsEnvironmentConditionFulfilled(kMXContext, no_experiment_required));
+    EXPECT_FALSE(
+        IsEnvironmentConditionFulfilled(kMXContext, experiment_required));
   }
 }
 
@@ -173,39 +184,37 @@
      IsFieldConditionFulfilledIgnoringLocation_CheckPossibleTypes) {
   using internal::IsFieldConditionFulfilledIgnoringLocation;
   GeoIpCountryCode kMX = GeoIpCountryCode("MX");
+  ParsingContext kMXContext(kMX, LanguageCode("es"), PatternSource::kLegacy);
 
   FieldCondition no_possible_types_required = {};
   FieldCondition requires_address_line1_type = {
       .possible_overall_types = FieldTypeSet{ADDRESS_HOME_LINE1},
   };
 
-  LanguageCode page_language = LanguageCode("es");
-  PatternSource pattern_source = PatternSource::kLegacy;
-
   AutofillField field;
 
   // Unknown type.
   ASSERT_EQ(field.Type().GetStorableType(), UNKNOWN_TYPE);
   EXPECT_TRUE(IsFieldConditionFulfilledIgnoringLocation(
-      no_possible_types_required, page_language, pattern_source, field));
+      kMXContext, no_possible_types_required, field));
   EXPECT_FALSE(IsFieldConditionFulfilledIgnoringLocation(
-      requires_address_line1_type, page_language, pattern_source, field));
+      kMXContext, requires_address_line1_type, field));
 
   // Non-matching type.
   field.set_heuristic_type(HeuristicSource::kLegacy, NAME_FIRST);
   ASSERT_EQ(field.Type().GetStorableType(), NAME_FIRST);
   EXPECT_TRUE(IsFieldConditionFulfilledIgnoringLocation(
-      no_possible_types_required, page_language, pattern_source, field));
+      kMXContext, no_possible_types_required, field));
   EXPECT_FALSE(IsFieldConditionFulfilledIgnoringLocation(
-      requires_address_line1_type, page_language, pattern_source, field));
+      kMXContext, requires_address_line1_type, field));
 
   // Matching type.
   field.set_heuristic_type(HeuristicSource::kLegacy, ADDRESS_HOME_LINE1);
   ASSERT_EQ(field.Type().GetStorableType(), ADDRESS_HOME_LINE1);
   EXPECT_TRUE(IsFieldConditionFulfilledIgnoringLocation(
-      no_possible_types_required, page_language, pattern_source, field));
+      kMXContext, no_possible_types_required, field));
   EXPECT_TRUE(IsFieldConditionFulfilledIgnoringLocation(
-      requires_address_line1_type, page_language, pattern_source, field));
+      kMXContext, requires_address_line1_type, field));
 }
 
 // Verifies that the required match for regexes works as expected in
@@ -214,45 +223,43 @@
      IsFieldConditionFulfilledIgnoringLocation_CheckRegex) {
   using internal::IsFieldConditionFulfilledIgnoringLocation;
   GeoIpCountryCode kMX = GeoIpCountryCode("MX");
+  ParsingContext kMXContext(kMX, LanguageCode("es"), PatternSource::kLegacy);
 
   FieldCondition no_regex_match_required = {};
   FieldCondition requires_dependent_locality_match = {
       .regex_reference_match = "ADDRESS_HOME_DEPENDENT_LOCALITY",
   };
 
-  LanguageCode page_language = LanguageCode("es");
-  PatternSource pattern_source = PatternSource::kLegacy;
-
   AutofillField field;
   field.label = u"";
 
   // Empty label.
   EXPECT_TRUE(IsFieldConditionFulfilledIgnoringLocation(
-      no_regex_match_required, page_language, pattern_source, field));
+      kMXContext, no_regex_match_required, field));
   EXPECT_FALSE(IsFieldConditionFulfilledIgnoringLocation(
-      requires_dependent_locality_match, page_language, pattern_source, field));
+      kMXContext, requires_dependent_locality_match, field));
 
   // Non-matching label.
   field.label = u"foobar";
   EXPECT_TRUE(IsFieldConditionFulfilledIgnoringLocation(
-      no_regex_match_required, page_language, pattern_source, field));
+      kMXContext, no_regex_match_required, field));
   EXPECT_FALSE(IsFieldConditionFulfilledIgnoringLocation(
-      requires_dependent_locality_match, page_language, pattern_source, field));
+      kMXContext, requires_dependent_locality_match, field));
 
   // Matching label.
   field.label = u"colonia";
   EXPECT_TRUE(IsFieldConditionFulfilledIgnoringLocation(
-      no_regex_match_required, page_language, pattern_source, field));
+      kMXContext, no_regex_match_required, field));
   EXPECT_TRUE(IsFieldConditionFulfilledIgnoringLocation(
-      requires_dependent_locality_match, page_language, pattern_source, field));
+      kMXContext, requires_dependent_locality_match, field));
 
   // Matching label but incorrect type.
   field.label = u"colonia";
   field.form_control_type = FormControlType::kInputMonth;
   EXPECT_TRUE(IsFieldConditionFulfilledIgnoringLocation(
-      no_regex_match_required, page_language, pattern_source, field));
+      kMXContext, no_regex_match_required, field));
   EXPECT_FALSE(IsFieldConditionFulfilledIgnoringLocation(
-      requires_dependent_locality_match, page_language, pattern_source, field));
+      kMXContext, requires_dependent_locality_match, field));
 
   FieldCondition regex_with_negative_pattern = {
       .regex_reference_match = "ADDRESS_NAME_IGNORED",
@@ -262,7 +269,7 @@
   // be considered fulfilled.
   field.label = u"nombre de usuario/dirección de correo electrónico";
   EXPECT_FALSE(IsFieldConditionFulfilledIgnoringLocation(
-      regex_with_negative_pattern, page_language, pattern_source, field));
+      kMXContext, regex_with_negative_pattern, field));
 }
 
 // Test that the actions are applied if all conditions are met.
@@ -281,9 +288,9 @@
       {u"Estado", u"state", ADDRESS_HOME_STATE},
   });
 
-  internal::ApplyRuleIfApplicable(CreateTestRule(), GeoIpCountryCode("MX"),
-                                  LanguageCode("es"), PatternSource::kLegacy,
-                                  fields);
+  GeoIpCountryCode kMX = GeoIpCountryCode("MX");
+  ParsingContext kMXContext(kMX, LanguageCode("es"), PatternSource::kLegacy);
+  internal::ApplyRuleIfApplicable(kMXContext, CreateTestRule(), fields);
 
   EXPECT_THAT(
       GetTypes(fields),
@@ -310,9 +317,9 @@
       {u"Estado", u"state", ADDRESS_HOME_STATE},
   });
 
-  internal::ApplyRuleIfApplicable(CreateTestRule(), GeoIpCountryCode("MX"),
-                                  LanguageCode("es"), PatternSource::kLegacy,
-                                  fields);
+  GeoIpCountryCode kMX = GeoIpCountryCode("MX");
+  ParsingContext kMXContext(kMX, LanguageCode("es"), PatternSource::kLegacy);
+  internal::ApplyRuleIfApplicable(kMXContext, CreateTestRule(), fields);
 
   EXPECT_THAT(
       GetTypes(fields),
@@ -339,9 +346,9 @@
       {u"Estado", u"state", ADDRESS_HOME_STATE},
   });
 
-  internal::ApplyRuleIfApplicable(CreateTestRule(), GeoIpCountryCode("MX"),
-                                  LanguageCode("es"), PatternSource::kLegacy,
-                                  fields);
+  GeoIpCountryCode kMX = GeoIpCountryCode("MX");
+  ParsingContext kMXContext(kMX, LanguageCode("es"), PatternSource::kLegacy);
+  internal::ApplyRuleIfApplicable(kMXContext, CreateTestRule(), fields);
 
   EXPECT_THAT(
       GetTypes(fields),
@@ -371,9 +378,9 @@
       {u"Estado", u"state", ADDRESS_HOME_STATE},
   });
 
-  internal::ApplyRuleIfApplicable(CreateTestRule(), GeoIpCountryCode("MX"),
-                                  LanguageCode("es"), PatternSource::kLegacy,
-                                  fields);
+  GeoIpCountryCode kMX = GeoIpCountryCode("MX");
+  ParsingContext kMXContext(kMX, LanguageCode("es"), PatternSource::kLegacy);
+  internal::ApplyRuleIfApplicable(kMXContext, CreateTestRule(), fields);
 
   EXPECT_THAT(GetTypes(fields),
               ElementsAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_LINE1,
@@ -401,9 +408,9 @@
       {u"Estado", u"state", ADDRESS_HOME_STATE},
   });
 
-  internal::ApplyRuleIfApplicable(CreateTestRule(), GeoIpCountryCode("MX"),
-                                  LanguageCode("es"), PatternSource::kLegacy,
-                                  fields);
+  GeoIpCountryCode kMX = GeoIpCountryCode("MX");
+  ParsingContext kMXContext(kMX, LanguageCode("es"), PatternSource::kLegacy);
+  internal::ApplyRuleIfApplicable(kMXContext, CreateTestRule(), fields);
 
   EXPECT_THAT(
       GetTypes(fields),
diff --git a/components/autofill/core/browser/form_structure_rationalizer.cc b/components/autofill/core/browser/form_structure_rationalizer.cc
index 10cbb15..a8956172 100644
--- a/components/autofill/core/browser/form_structure_rationalizer.cc
+++ b/components/autofill/core/browser/form_structure_rationalizer.cc
@@ -1023,8 +1023,10 @@
     pattern_source = PatternSource::kLegacy;
   }
 
-  rationalization::ApplyRationalizationEngineRules(
-      client_country, language_code, *pattern_source, *fields_, log_manager);
+  ParsingContext context(client_country, language_code, *pattern_source);
+
+  rationalization::ApplyRationalizationEngineRules(context, *fields_,
+                                                   log_manager);
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/metrics/suggestions_list_metrics.cc b/components/autofill/core/browser/metrics/suggestions_list_metrics.cc
index cb982ea..c1b5837a 100644
--- a/components/autofill/core/browser/metrics/suggestions_list_metrics.cc
+++ b/components/autofill/core/browser/metrics/suggestions_list_metrics.cc
@@ -70,4 +70,11 @@
                                 uma_type);
 }
 
+void LogAutofillShowCardsFromGoogleAccountButtonEventMetric(
+    ShowCardsFromGoogleAccountButtonEvent event) {
+  base::UmaHistogramEnumeration(
+      "Autofill.ButterForPayments.ShowCardsFromGoogleAccountButtonEvents",
+      event);
+}
+
 }  // namespace autofill::autofill_metrics
diff --git a/components/autofill/core/browser/metrics/suggestions_list_metrics.h b/components/autofill/core/browser/metrics/suggestions_list_metrics.h
index 9bb74a7..886aeea 100644
--- a/components/autofill/core/browser/metrics/suggestions_list_metrics.h
+++ b/components/autofill/core/browser/metrics/suggestions_list_metrics.h
@@ -24,6 +24,20 @@
   kMaxValue = kPaymentMethodsIbans,
 };
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+// Used by LogAutofillShowCardsFromGoogleAccountButtonEventMetric().
+enum class ShowCardsFromGoogleAccountButtonEvent {
+  // 'Show Cards from Google Account' button appeared.
+  kButtonAppeared = 0,
+  // 'Show Cards from Google Account' button appeared. Logged once per page
+  // load.
+  kButtonAppearedOnce = 1,
+  // 'Show Cards from Google Account' button clicked.
+  kButtonClicked = 2,
+  kMaxValue = kButtonClicked,
+};
+
 // Log the index of the selected Autofill suggestion in the popup.
 void LogAutofillSuggestionAcceptedIndex(int index,
                                         autofill::PopupType popup_type,
@@ -32,6 +46,10 @@
 // Logs that the user selected 'Manage...' settings entry in the popup.
 void LogAutofillSelectedManageEntry(autofill::PopupType popup_type);
 
+// Logs the 'Show cards from your Google Account" button events.
+void LogAutofillShowCardsFromGoogleAccountButtonEventMetric(
+    ShowCardsFromGoogleAccountButtonEvent event);
+
 }  // namespace autofill_metrics
 }  // namespace autofill
 
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index dede866..bc4fc08 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -667,6 +667,13 @@
              "AutofillUseTypedCreditCardNumber",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// Kill switch for feature which ignores the visibility of formless elements in
+// determining whether a form was submitted. This changes the behavior when a
+// form submission is inferred as a result of the page navigating.
+BASE_FEATURE(kAutofillDontCheckForDisappearingFormlessElementsForSubmission,
+             "AutofillDontCheckForDisappearingFormlessElementsForSubmission",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 // Controls an ablation study in which autofill for addresses and payment data
 // can be suppressed.
 BASE_FEATURE(kAutofillEnableAblationStudy,
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index 41f663c..01d152e 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -218,6 +218,9 @@
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillUseTypedCreditCardNumber);
 COMPONENT_EXPORT(AUTOFILL)
+BASE_DECLARE_FEATURE(
+    kAutofillDontCheckForDisappearingFormlessElementsForSubmission);
+COMPONENT_EXPORT(AUTOFILL)
 extern const base::FeatureParam<bool> kAutofillSectioningModeIgnoreAutocomplete;
 COMPONENT_EXPORT(AUTOFILL)
 extern const base::FeatureParam<bool> kAutofillSectioningModeCreateGaps;
diff --git a/components/browsing_topics/annotator_impl.cc b/components/browsing_topics/annotator_impl.cc
index 28f7645..9a26cd2e 100644
--- a/components/browsing_topics/annotator_impl.cc
+++ b/components/browsing_topics/annotator_impl.cc
@@ -493,15 +493,9 @@
   version_ = model_metadata->version();
 
   // New model, new override list.
-  override_list_file_path_ = absl::nullopt;
+  override_list_file_path_ =
+      model_info->GetAdditionalFileWithBaseName(kOverrideListBasePath);
   override_list_ = absl::nullopt;
-  for (const base::FilePath& path : model_info->GetAdditionalFiles()) {
-    DCHECK(path.IsAbsolute());
-    if (path.BaseName() == base::FilePath(kOverrideListBasePath)) {
-      override_list_file_path_ = path;
-      break;
-    }
-  }
 
   // Run any callbacks that were waiting for an updated model.
   //
diff --git a/components/discardable_memory/service/discardable_shared_memory_manager.cc b/components/discardable_memory/service/discardable_shared_memory_manager.cc
index f0de3b7..b351eddd 100644
--- a/components/discardable_memory/service/discardable_shared_memory_manager.cc
+++ b/components/discardable_memory/service/discardable_shared_memory_manager.cc
@@ -189,11 +189,6 @@
     int64_t shmem_dir_amount_of_free_space_mb =
         shmem_dir_amount_of_free_space / kMegabyte;
 
-    UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.ShmemDir.AmountOfFreeSpace",
-                                shmem_dir_amount_of_free_space_mb, 1,
-                                4 * 1024,  // 4 GB
-                                50);
-
     if (shmem_dir_amount_of_free_space_mb < 64) {
       LOG(WARNING) << "Less than 64MB of free space in temporary directory for "
                       "shared memory files: "
diff --git a/components/exo/OWNERS b/components/exo/OWNERS
index 3ff094f..965e772 100644
--- a/components/exo/OWNERS
+++ b/components/exo/OWNERS
@@ -16,4 +16,6 @@
 
 # For extended-drag.
 per-file extended_drag*=nickdiego@igalia.com
+per-file extended_drag*=tonikitoo@igalia.com
 per-file wayland/zcr_extended_drag*=nickdiego@igalia.com
+per-file wayland/zcr_extended_drag*=tonikitoo@igalia.com
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index efc5198..ec9b7af 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -69,6 +69,7 @@
 #include "ui/views/views_delegate.h"
 #include "ui/views/widget/tooltip_manager.h"
 #include "ui/views/widget/widget.h"
+#include "ui/views/window/client_view.h"
 #include "ui/wm/core/shadow_controller.h"
 #include "ui/wm/core/shadow_types.h"
 #include "ui/wm/core/window_animations.h"
@@ -166,7 +167,7 @@
         ash::WindowState::Get(GetWidget()->GetNativeWindow());
 
     // When un-pipped (window state changed from pip), we must undo the
-    // rounded corners from the host_window.
+    // rounded corners of the host_window.
     const int pip_corner_radius =
         window_state->IsPip() ? chromeos::kPipRoundedCornerRadius : 0;
     const gfx::RoundedCornersF pip_radii(pip_corner_radius);
@@ -196,17 +197,7 @@
       header_view_->SetHeaderCornerRadius(window_radii->upper_left());
     }
 
-    const gfx::RoundedCornersF root_surface_radii = {
-        GetFrameEnabled() ? 0 : window_radii->upper_left(),
-        GetFrameEnabled() ? 0 : window_radii->upper_right(),
-        window_radii->lower_right(), window_radii->lower_left()};
-
-    Surface* root_surface = shell_surface_->root_surface();
-    DCHECK(root_surface);
-
-    shell_surface_->ApplyRoundedCornersToSurfaceTree(
-        gfx::RectF(root_surface->surface_hierarchy_content_bounds()),
-        root_surface_radii);
+    GetWidget()->client_view()->UpdateWindowRoundedCorners();
   }
 
   gfx::Rect GetWindowBoundsForClientBounds(
@@ -266,6 +257,45 @@
   const raw_ptr<ShellSurfaceBase, ExperimentalAsh> shell_surface_;
 };
 
+class CustomClientView : public views::ClientView {
+ public:
+  CustomClientView(views::Widget* widget, ShellSurfaceBase* shell_surface)
+      : views::ClientView(widget, shell_surface),
+        shell_surface_(shell_surface) {}
+
+  CustomClientView(const CustomClientView&) = delete;
+  CustomClientView& operator=(const CustomClientView&) = delete;
+
+  ~CustomClientView() override = default;
+
+  // ClientView:
+  void UpdateWindowRoundedCorners() override {
+    absl::optional<gfx::RoundedCornersF> window_radii =
+        shell_surface_->window_corners_radii();
+
+    DCHECK(GetWidget());
+    const bool frame_enabled = static_cast<CustomFrameView*>(
+                                   GetWidget()->non_client_view()->frame_view())
+                                   ->GetFrameEnabled();
+
+    // TODO(crbug.com/1415486): Support variable window radii.
+    DCHECK(IsRadiiUniform(window_radii.value()));
+    const gfx::RoundedCornersF root_surface_radii = {
+        frame_enabled ? 0 : window_radii->upper_left(),
+        frame_enabled ? 0 : window_radii->upper_right(),
+        window_radii->lower_right(), window_radii->lower_left()};
+
+    const Surface* root_surface = shell_surface_->root_surface();
+
+    shell_surface_->ApplyRoundedCornersToSurfaceTree(
+        gfx::RectF(root_surface->surface_hierarchy_content_bounds()),
+        root_surface_radii);
+  }
+
+ private:
+  raw_ptr<ShellSurfaceBase> shell_surface_;
+};
+
 class CustomWindowTargeter : public aura::WindowTargeter {
  public:
   explicit CustomWindowTargeter(ShellSurfaceBase* shell_surface)
@@ -1297,6 +1327,10 @@
   return this;
 }
 
+views::ClientView* ShellSurfaceBase::CreateClientView(views::Widget* widget) {
+  return new CustomClientView(widget, this);
+}
+
 std::unique_ptr<views::NonClientFrameView>
 ShellSurfaceBase::CreateNonClientFrameView(views::Widget* widget) {
   return CreateNonClientFrameViewInternal(widget);
diff --git a/components/exo/shell_surface_base.h b/components/exo/shell_surface_base.h
index 3b5f4475..57bbb15 100644
--- a/components/exo/shell_surface_base.h
+++ b/components/exo/shell_surface_base.h
@@ -28,6 +28,7 @@
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rounded_corners_f.h"
+#include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 #include "ui/views/widget/widget_observer.h"
@@ -39,6 +40,10 @@
 class WindowState;
 }  // namespace ash
 
+namespace views {
+class ClientView;
+}  // namespace views
+
 namespace base {
 namespace trace_event {
 class TracedValue;
@@ -295,6 +300,7 @@
   bool ShouldSaveWindowPlacement() const override;
   bool WidgetHasHitTestMask() const override;
   void GetWidgetHitTestMask(SkPath* mask) const override;
+  views::ClientView* CreateClientView(views::Widget* widget) override;
 
   // views::WidgetObserver:
   void OnWidgetClosing(views::Widget* widget) override;
diff --git a/components/eye_dropper/eye_dropper_view.cc b/components/eye_dropper/eye_dropper_view.cc
index 9596f76..2c35b33 100644
--- a/components/eye_dropper/eye_dropper_view.cc
+++ b/components/eye_dropper/eye_dropper_view.cc
@@ -4,6 +4,8 @@
 
 #include "components/eye_dropper/eye_dropper_view.h"
 
+#include <utility>
+
 #include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -45,7 +47,8 @@
 
   // Timer used for updating the window location.
   base::RepeatingTimer timer_;
-
+  gfx::Point last_cursor_position_ =
+      display::Screen::GetScreen()->GetCursorScreenPoint();
   raw_ptr<EyeDropperView> owner_;
 };
 
@@ -62,13 +65,20 @@
 }
 
 void EyeDropperView::ViewPositionHandler::UpdateViewPosition() {
-  owner_->UpdatePosition();
+  // The view can be moved by either mouse or touch. Only move it to the cursor
+  // position when cursor changes.
+  gfx::Point cursor_position =
+      display::Screen::GetScreen()->GetCursorScreenPoint();
+  if (std::exchange(last_cursor_position_, cursor_position) !=
+      cursor_position) {
+    owner_->UpdatePosition(cursor_position);
+  }
 }
 
 class EyeDropperView::ScreenCapturer
     : public webrtc::DesktopCapturer::Callback {
  public:
-  ScreenCapturer();
+  explicit ScreenCapturer(EyeDropperView* owner);
   ScreenCapturer(const ScreenCapturer&) = delete;
   ScreenCapturer& operator=(const ScreenCapturer&) = delete;
   ~ScreenCapturer() override = default;
@@ -84,13 +94,15 @@
   int original_offset_y() const;
 
  private:
+  raw_ptr<EyeDropperView> owner_;
   std::unique_ptr<webrtc::DesktopCapturer> capturer_;
   SkBitmap frame_;
   int original_offset_x_;
   int original_offset_y_;
 };
 
-EyeDropperView::ScreenCapturer::ScreenCapturer() {
+EyeDropperView::ScreenCapturer::ScreenCapturer(EyeDropperView* owner)
+    : owner_(owner) {
   static bool allow_wgc_screen_capture =
 #if BUILDFLAG(IS_WIN)
       // Allow WGC screen capture if Windows version is greater or equal
@@ -161,6 +173,7 @@
       original_offset_y_ = scaled_bounds.origin().y();
     }
   }
+  owner_->OnScreenCaptured();
 }
 
 SkBitmap EyeDropperView::ScreenCapturer::GetBitmap() const {
@@ -188,7 +201,7 @@
                                content::EyeDropperListener* listener)
     : listener_(listener),
       view_position_handler_(std::make_unique<ViewPositionHandler>(this)),
-      screen_capturer_(std::make_unique<ScreenCapturer>()) {
+      screen_capturer_(std::make_unique<ScreenCapturer>(this)) {
   SetModalType(ui::MODAL_TYPE_WINDOW);
   // This is owned as a unique_ptr<EyeDropper> elsewhere.
   SetOwnedByWidget(false);
@@ -216,12 +229,21 @@
   views::Widget* widget = new views::Widget();
   widget->Init(std::move(params));
   widget->SetContentsView(this);
-  MoveViewToFront();
   HideCursor();
   pre_dispatch_handler_ =
       std::make_unique<PreEventDispatchHandler>(this, event_handler);
   widget->Show();
   CaptureInputIfNeeded();
+  auto* screen = display::Screen::GetScreen();
+  gfx::Point initial_position = screen->GetCursorScreenPoint();
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (screen->InTabletMode()) {
+    initial_position =
+        screen->GetDisplayForNewWindows().work_area().CenterPoint();
+  }
+#endif
+  UpdatePosition(initial_position);
+
   // The ignore selection time should be long enough to allow the user to see
   // the UI.
   ignore_selection_time_ = base::TimeTicks::Now() + base::Milliseconds(500);
@@ -365,20 +387,14 @@
   screen_capturer_->CaptureScreen(screen);
 }
 
-void EyeDropperView::UpdatePosition() {
-  if (screen_capturer_->GetBitmap().drawsNothing() || !GetWidget()) {
-    return;
-  }
+void EyeDropperView::OnScreenCaptured() {
+  SchedulePaint();
+}
 
-  gfx::Point cursor_position =
-      display::Screen::GetScreen()->GetCursorScreenPoint();
-  if (cursor_position == GetWidget()->GetWindowBoundsInScreen().CenterPoint()) {
-    return;
-  }
-
+void EyeDropperView::UpdatePosition(gfx::Point position) {
   GetWidget()->SetBounds(
-      gfx::Rect(gfx::Point(cursor_position.x() - size().width() / 2,
-                           cursor_position.y() - size().height() / 2),
+      gfx::Rect(gfx::Point(position.x() - size().width() / 2,
+                           position.y() - size().height() / 2),
                 size()));
 }
 
diff --git a/components/eye_dropper/eye_dropper_view.h b/components/eye_dropper/eye_dropper_view.h
index b367c36..4982dac 100644
--- a/components/eye_dropper/eye_dropper_view.h
+++ b/components/eye_dropper/eye_dropper_view.h
@@ -66,6 +66,7 @@
    private:
     void OnMouseEvent(ui::MouseEvent* event) override;
     void OnGestureEvent(ui::GestureEvent* event) override;
+    void OnTouchEvent(ui::TouchEvent* event) override;
 
     raw_ptr<EyeDropperView> view_;
 #if defined(USE_AURA)
@@ -73,15 +74,15 @@
     class FocusObserver;
     std::unique_ptr<KeyboardHandler> keyboard_handler_;
     std::unique_ptr<FocusObserver> focus_observer_;
+    gfx::Vector2d touch_offset_;
 #endif
   };
 
   void CaptureScreen(absl::optional<webrtc::DesktopCapturer::SourceId> screen);
+  void OnScreenCaptured();
 
-  // Moves the view to the cursor position.
-  void UpdatePosition();
-
-  void MoveViewToFront();
+  // Moves the view to the specified position.
+  void UpdatePosition(gfx::Point position);
 
   void CaptureInputIfNeeded();
 
diff --git a/components/eye_dropper/eye_dropper_view_aura.cc b/components/eye_dropper/eye_dropper_view_aura.cc
index 40d64128..67c25c8 100644
--- a/components/eye_dropper/eye_dropper_view_aura.cc
+++ b/components/eye_dropper/eye_dropper_view_aura.cc
@@ -124,8 +124,15 @@
   }
 }
 
-void EyeDropperView::MoveViewToFront() {
-  // The view is already topmost when Aura is used.
+void EyeDropperView::PreEventDispatchHandler::OnTouchEvent(
+    ui::TouchEvent* event) {
+  if (event->type() == ui::ET_TOUCH_PRESSED) {
+    touch_offset_ = event->root_location() -
+                    view_->GetWidget()->GetWindowBoundsInScreen().CenterPoint();
+  }
+  if (event->type() == ui::ET_TOUCH_MOVED) {
+    view_->UpdatePosition(event->root_location() - touch_offset_);
+  }
 }
 
 void EyeDropperView::CaptureInputIfNeeded() {
diff --git a/components/facilitated_payments/README.md b/components/facilitated_payments/README.md
index bf2437f..987a99a 100644
--- a/components/facilitated_payments/README.md
+++ b/components/facilitated_payments/README.md
@@ -1,5 +1,5 @@
 This component contains support for forms of payment ("FOP") that are not based
-on HTML <form>s or JavaScript API, and therefore not covered by
+on HTML `<form>`s or JavaScript API, and therefore not covered by
 //components/autofill or //components/payments.
 
 This component supports the PIX form of payment:
diff --git a/components/feature_engagement/public/feature_configurations.cc b/components/feature_engagement/public/feature_configurations.cc
index 92815c0..97c29355 100644
--- a/components/feature_engagement/public/feature_configurations.cc
+++ b/components/feature_engagement/public/feature_configurations.cc
@@ -473,6 +473,27 @@
     return config;
   }
 
+  if (kIPHDownloadEsbPromoFeature.name == feature->name) {
+    absl::optional<FeatureConfig> config = FeatureConfig();
+    config->valid = true;
+    config->availability = Comparator(ANY, 0);
+    config->session_rate = Comparator(EQUAL, 0);
+    SessionRateImpact session_rate_impact;
+    session_rate_impact.type = SessionRateImpact::Type::ALL;
+    config->session_rate_impact = session_rate_impact;
+    // Don't show if user has already seen an IPH this session.
+    // Show the promo max once a year if the user hasn't interacted with
+    // a dangerous download within the last 21 days.
+    config->trigger = EventConfig("download_bubble_esb_iph_trigger",
+                                  Comparator(EQUAL, 0), 360, 360);
+    config->used = EventConfig("enable_enhanced_protection",
+                               Comparator(EQUAL, 0), 21, 360);
+    config->event_configs.insert(
+        EventConfig("download_bubble_dangerous_download_detected",
+                    Comparator(GREATER_THAN_OR_EQUAL, 1), 21, 360));
+    return config;
+  }
+
   if (kIPHDownloadToolbarButtonFeature.name == feature->name) {
     absl::optional<FeatureConfig> config = FeatureConfig();
     config->valid = true;
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc
index 6a0b6055..1f2de5f 100644
--- a/components/feature_engagement/public/feature_constants.cc
+++ b/components/feature_engagement/public/feature_constants.cc
@@ -52,6 +52,9 @@
 BASE_FEATURE(kIPHDesktopNewTabPageModulesCustomizeFeature,
              "IPH_DesktopNewTabPageModulesCustomize",
              base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kIPHDownloadEsbPromoFeature,
+             "IPH_DownloadEsbPromo",
+             base::FEATURE_ENABLED_BY_DEFAULT);
 BASE_FEATURE(kIPHDownloadToolbarButtonFeature,
              "IPH_DownloadToolbarButton",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h
index 6f7afc2c..8e70f3e 100644
--- a/components/feature_engagement/public/feature_constants.h
+++ b/components/feature_engagement/public/feature_constants.h
@@ -36,6 +36,7 @@
 BASE_DECLARE_FEATURE(kIPHDesktopCustomizeChromeFeature);
 BASE_DECLARE_FEATURE(kIPHDesktopCustomizeChromeRefreshFeature);
 BASE_DECLARE_FEATURE(kIPHDesktopNewTabPageModulesCustomizeFeature);
+BASE_DECLARE_FEATURE(kIPHDownloadEsbPromoFeature);
 BASE_DECLARE_FEATURE(kIPHDownloadToolbarButtonFeature);
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 BASE_DECLARE_FEATURE(kIPHExtensionsMenuFeature);
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc
index 81a2b9a..b7872db1 100644
--- a/components/feature_engagement/public/feature_list.cc
+++ b/components/feature_engagement/public/feature_list.cc
@@ -162,6 +162,7 @@
     &kIPHDesktopCustomizeChromeFeature,
     &kIPHDesktopCustomizeChromeRefreshFeature,
     &kIPHDesktopNewTabPageModulesCustomizeFeature,
+    &kIPHDownloadEsbPromoFeature,
     &kIPHDownloadToolbarButtonFeature,
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     &kIPHExtensionsMenuFeature,
diff --git a/components/feature_engagement/public/feature_list.h b/components/feature_engagement/public/feature_list.h
index 88d97413..82bc5d0 100644
--- a/components/feature_engagement/public/feature_list.h
+++ b/components/feature_engagement/public/feature_list.h
@@ -298,6 +298,7 @@
                        "IPH_DesktopNewTabPageModulesCustomize");
 DEFINE_VARIATION_PARAM(kIPHDesktopTabGroupsNewGroupFeature,
                        "IPH_DesktopTabGroupsNewGroup");
+DEFINE_VARIATION_PARAM(kIPHDownloadEsbPromoFeature, "IPH_DownloadEsbPromo");
 DEFINE_VARIATION_PARAM(kIPHDownloadToolbarButtonFeature,
                        "IPH_DownloadToolbarButton");
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/components/metrics/structured/BUILD.gn b/components/metrics/structured/BUILD.gn
index 468ca39..b835b9a 100644
--- a/components/metrics/structured/BUILD.gn
+++ b/components/metrics/structured/BUILD.gn
@@ -142,8 +142,8 @@
   inputs = [
     "//tools/metrics/structured/codegen.py",
     "//tools/metrics/structured/gen_events.py",
-    "//tools/metrics/structured/model.py",
-    "//tools/metrics/structured/model_util.py",
+    "//tools/metrics/structured/sync/model.py",
+    "//tools/metrics/structured/sync/model_util.py",
     "//tools/metrics/structured/templates_events.py",
   ]
   sources = [ "//tools/metrics/structured/sync/structured.xml" ]
@@ -181,8 +181,8 @@
   inputs = [
     "//tools/metrics/structured/codegen.py",
     "//tools/metrics/structured/gen_validator.py",
-    "//tools/metrics/structured/model.py",
-    "//tools/metrics/structured/model_util.py",
+    "//tools/metrics/structured/sync/model.py",
+    "//tools/metrics/structured/sync/model_util.py",
     "//tools/metrics/structured/templates_validator.py",
   ]
   sources = [ "//tools/metrics/structured/sync/structured.xml" ]
diff --git a/components/navigation_metrics/navigation_metrics.cc b/components/navigation_metrics/navigation_metrics.cc
index 3934eb3b..d94b686b 100644
--- a/components/navigation_metrics/navigation_metrics.cc
+++ b/components/navigation_metrics/navigation_metrics.cc
@@ -27,6 +27,10 @@
 const char kMainFrameScheme[] = "Navigation.MainFrameScheme2";
 const char kMainFrameSchemeDifferentPage[] =
     "Navigation.MainFrameSchemeDifferentPage2";
+// Same as kMainFrameSchemeDifferentPage, but only recorded if the hostname is
+// non-unique (e.g. http://site.test):
+const char kMainFrameSchemeDifferentPageNonUniqueHostname[] =
+    "Navigation.MainFrameSchemeDifferentPage2NonUniqueHostname";
 const char kMainFrameSchemeOTR[] = "Navigation.MainFrameSchemeOTR2";
 const char kMainFrameSchemeDifferentPageOTR[] =
     "Navigation.MainFrameSchemeDifferentPageOTR2";
@@ -143,26 +147,30 @@
   Scheme scheme = GetScheme(url);
   UMA_HISTOGRAM_ENUMERATION(kMainFrameScheme, scheme, Scheme::COUNT);
   if (!is_same_document) {
-    UMA_HISTOGRAM_ENUMERATION("Navigation.MainFrameSchemeDifferentPage2",
-                              scheme, Scheme::COUNT);
-    UMA_HISTOGRAM_BOOLEAN("Navigation.MainFrameHasRTLDomainDifferentPage2",
+    UMA_HISTOGRAM_ENUMERATION(kMainFrameSchemeDifferentPage, scheme,
+                              Scheme::COUNT);
+    UMA_HISTOGRAM_BOOLEAN(kMainFrameHasRTLDomainDifferentPage,
                           base::i18n::StringContainsStrongRTLChars(
                               url_formatter::IDNToUnicode(url.host())));
+
+    if (net::IsHostnameNonUnique(url.host())) {
+      UMA_HISTOGRAM_ENUMERATION(kMainFrameSchemeDifferentPageNonUniqueHostname,
+                                scheme, Scheme::COUNT);
+    }
   }
 
-  UMA_HISTOGRAM_BOOLEAN("Navigation.MainFrameHasRTLDomain2",
+  UMA_HISTOGRAM_BOOLEAN(kMainFrameHasRTLDomain,
                         base::i18n::StringContainsStrongRTLChars(
                             url_formatter::IDNToUnicode(url.host())));
 
   if (is_off_the_record) {
-    UMA_HISTOGRAM_ENUMERATION("Navigation.MainFrameSchemeOTR2", scheme,
-                              Scheme::COUNT);
+    UMA_HISTOGRAM_ENUMERATION(kMainFrameSchemeOTR, scheme, Scheme::COUNT);
     if (!is_same_document) {
-      UMA_HISTOGRAM_ENUMERATION("Navigation.MainFrameSchemeDifferentPageOTR2",
-                                scheme, Scheme::COUNT);
+      UMA_HISTOGRAM_ENUMERATION(kMainFrameSchemeDifferentPageOTR, scheme,
+                                Scheme::COUNT);
     }
   }
-  UMA_HISTOGRAM_ENUMERATION("Navigation.MainFrameProfileType2", profile_type);
+  UMA_HISTOGRAM_ENUMERATION(kMainFrameProfileType, profile_type);
 }
 
 void RecordOmniboxURLNavigation(const GURL& url) {
diff --git a/components/navigation_metrics/navigation_metrics.h b/components/navigation_metrics/navigation_metrics.h
index ba393d2d..0824da1 100644
--- a/components/navigation_metrics/navigation_metrics.h
+++ b/components/navigation_metrics/navigation_metrics.h
@@ -20,6 +20,7 @@
 // Names of the metrics logged by RecordPrimaryMainFrameNavigation() function.
 extern const char kMainFrameScheme[];
 extern const char kMainFrameSchemeDifferentPage[];
+extern const char kMainFrameSchemeDifferentPageNonUniqueHostname[];
 extern const char kMainFrameSchemeOTR[];
 extern const char kMainFrameSchemeDifferentPageOTR[];
 extern const char kMainFrameHasRTLDomain[];
diff --git a/components/navigation_metrics/navigation_metrics_unittest.cc b/components/navigation_metrics/navigation_metrics_unittest.cc
index ff9b3f1..e9c6ee0 100644
--- a/components/navigation_metrics/navigation_metrics_unittest.cc
+++ b/components/navigation_metrics/navigation_metrics_unittest.cc
@@ -28,6 +28,28 @@
   test.ExpectUniqueSample(kMainFrameScheme, 1 /* http */, 1);
   test.ExpectTotalCount(kMainFrameSchemeDifferentPage, 1);
   test.ExpectUniqueSample(kMainFrameSchemeDifferentPage, 1 /* http */, 1);
+  test.ExpectTotalCount(kMainFrameSchemeDifferentPageNonUniqueHostname, 0);
+  test.ExpectTotalCount(kMainFrameSchemeOTR, 0);
+  test.ExpectTotalCount(kMainFrameSchemeDifferentPageOTR, 0);
+  test.ExpectTotalCount(kMainFrameProfileType, 1);
+  test.ExpectUniqueSample(kMainFrameProfileType,
+                          profile_metrics::BrowserProfileType::kRegular, 1);
+}
+
+TEST(NavigationMetrics, MainFrameSchemeDifferentDocument_NonUniqueHostname) {
+  base::HistogramTester test;
+
+  RecordPrimaryMainFrameNavigation(
+      GURL("http://site.test"), false, false,
+      profile_metrics::BrowserProfileType::kRegular);
+
+  test.ExpectTotalCount(kMainFrameScheme, 1);
+  test.ExpectUniqueSample(kMainFrameScheme, 1 /* http */, 1);
+  test.ExpectTotalCount(kMainFrameSchemeDifferentPage, 1);
+  test.ExpectUniqueSample(kMainFrameSchemeDifferentPage, 1 /* http */, 1);
+  test.ExpectTotalCount(kMainFrameSchemeDifferentPageNonUniqueHostname, 1);
+  test.ExpectUniqueSample(kMainFrameSchemeDifferentPageNonUniqueHostname,
+                          1 /* http */, 1);
   test.ExpectTotalCount(kMainFrameSchemeOTR, 0);
   test.ExpectTotalCount(kMainFrameSchemeDifferentPageOTR, 0);
   test.ExpectTotalCount(kMainFrameProfileType, 1);
@@ -45,6 +67,7 @@
   test.ExpectTotalCount(kMainFrameScheme, 1);
   test.ExpectUniqueSample(kMainFrameScheme, 1 /* http */, 1);
   test.ExpectTotalCount(kMainFrameSchemeDifferentPage, 0);
+  test.ExpectTotalCount(kMainFrameSchemeDifferentPageNonUniqueHostname, 0);
   test.ExpectTotalCount(kMainFrameSchemeOTR, 0);
   test.ExpectTotalCount(kMainFrameSchemeDifferentPageOTR, 0);
   test.ExpectTotalCount(kMainFrameProfileType, 1);
@@ -63,6 +86,7 @@
   test.ExpectUniqueSample(kMainFrameScheme, 1 /* http */, 1);
   test.ExpectTotalCount(kMainFrameSchemeDifferentPage, 1);
   test.ExpectUniqueSample(kMainFrameSchemeDifferentPage, 1 /* http */, 1);
+  test.ExpectTotalCount(kMainFrameSchemeDifferentPageNonUniqueHostname, 0);
   test.ExpectTotalCount(kMainFrameSchemeOTR, 1);
   test.ExpectUniqueSample(kMainFrameSchemeOTR, 1 /* http */, 1);
   test.ExpectTotalCount(kMainFrameSchemeDifferentPageOTR, 1);
@@ -82,6 +106,7 @@
   test.ExpectTotalCount(kMainFrameScheme, 1);
   test.ExpectUniqueSample(kMainFrameScheme, 1 /* http */, 1);
   test.ExpectTotalCount(kMainFrameSchemeDifferentPage, 0);
+  test.ExpectTotalCount(kMainFrameSchemeDifferentPageNonUniqueHostname, 0);
   test.ExpectTotalCount(kMainFrameSchemeOTR, 1);
   test.ExpectUniqueSample(kMainFrameSchemeOTR, 1 /* http */, 1);
   test.ExpectTotalCount(kMainFrameSchemeDifferentPageOTR, 0);
diff --git a/components/omnibox/browser/suggestion_group_unittest.cc b/components/omnibox/browser/suggestion_group_unittest.cc
index 89d97b2..ba37d46 100644
--- a/components/omnibox/browser/suggestion_group_unittest.cc
+++ b/components/omnibox/browser/suggestion_group_unittest.cc
@@ -5,9 +5,12 @@
 #include "components/omnibox/browser/suggestion_group_util.h"
 
 #include "base/test/scoped_feature_list.h"
+#include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/common/omnibox_features.h"
+#include "components/strings/grit/components_strings.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/omnibox_proto/groups.pb.h"
+#include "ui/base/l10n/l10n_util.h"
 
 // Ensures that accessing unset fields is safe and verifies the default values.
 // https://developers.google.com/protocol-buffers/docs/reference/cpp-generated
@@ -78,3 +81,43 @@
   ASSERT_EQ(omnibox::GroupConfig_RenderType_DEFAULT_VERTICAL,
             most_visited_group_config->second.render_type());
 }
+
+TEST(SuggestionGroupTest, SectionPopularSearches_VerticalRenderType) {
+  omnibox::ResetDefaultGroupsForTest();
+
+  base::test::ScopedFeatureList features;
+  features.InitWithFeaturesAndParameters({{omnibox::kQueryTilesInZPSOnNTP, {}}},
+                                         {});
+
+  auto default_groups = omnibox::BuildDefaultGroups();
+  auto group_config = default_groups.find(omnibox::GROUP_MOBILE_QUERY_TILES);
+
+  ASSERT_NE(group_config, default_groups.end());
+  ASSERT_EQ(omnibox::GroupConfig_RenderType_DEFAULT_VERTICAL,
+            group_config->second.render_type());
+
+  ASSERT_FALSE(group_config->second.header_text().empty());
+  ASSERT_EQ(l10n_util::GetStringUTF8(IDS_OMNIBOX_HEADER_POPULAR_TOPICS),
+            group_config->second.header_text());
+}
+
+TEST(SuggestionGroupTest, SectionPopularSearches_HorizontalRenderType) {
+  omnibox::ResetDefaultGroupsForTest();
+
+  base::test::ScopedFeatureList features;
+  features.InitWithFeaturesAndParameters(
+      {{omnibox::kQueryTilesInZPSOnNTP,
+        {{OmniboxFieldTrial::kQueryTilesShowAsCarousel.name, "true"}}}},
+      {});
+
+  auto default_groups = omnibox::BuildDefaultGroups();
+  auto group_config = default_groups.find(omnibox::GROUP_MOBILE_QUERY_TILES);
+
+  ASSERT_NE(group_config, default_groups.end());
+  ASSERT_EQ(omnibox::GroupConfig_RenderType_HORIZONTAL,
+            group_config->second.render_type());
+
+  ASSERT_FALSE(group_config->second.header_text().empty());
+  ASSERT_EQ(l10n_util::GetStringUTF8(IDS_OMNIBOX_HEADER_POPULAR_TOPICS),
+            group_config->second.header_text());
+}
diff --git a/components/omnibox/browser/suggestion_group_util.cc b/components/omnibox/browser/suggestion_group_util.cc
index 2e0a1419..5465632 100644
--- a/components/omnibox/browser/suggestion_group_util.cc
+++ b/components/omnibox/browser/suggestion_group_util.cc
@@ -8,16 +8,23 @@
 #include "base/lazy_instance.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/common/omnibox_features.h"
+#include "components/strings/grit/components_strings.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/omnibox_proto/groups.pb.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace omnibox {
 namespace {
 GroupConfig CreateGroup(GroupSection section,
                         GroupConfig::RenderType render_type =
-                            GroupConfig_RenderType_DEFAULT_VERTICAL) {
+                            GroupConfig_RenderType_DEFAULT_VERTICAL,
+                        absl::optional<int32_t> header_text = {}) {
   GroupConfig group;
   group.set_section(section);
   group.set_render_type(render_type);
+  if (header_text) {
+    group.set_header_text(l10n_util::GetStringUTF8(*header_text));
+  }
   return group;
 }
 
@@ -43,7 +50,8 @@
           CreateGroup(SECTION_MOBILE_QUERY_TILES,
               OmniboxFieldTrial::kQueryTilesShowAsCarousel.Get()
               ? GroupConfig_RenderType_HORIZONTAL
-              : GroupConfig_RenderType_DEFAULT_VERTICAL)},
+              : GroupConfig_RenderType_DEFAULT_VERTICAL,
+              IDS_OMNIBOX_HEADER_POPULAR_TOPICS)},
         // clang-format on
     };
   }
diff --git a/components/omnibox_strings.grdp b/components/omnibox_strings.grdp
index 9a0857a9..61b11fb 100644
--- a/components/omnibox_strings.grdp
+++ b/components/omnibox_strings.grdp
@@ -292,4 +292,10 @@
   <message name="IDS_OMNIBOX_ONE_LINE_CALCULATOR_SUGGESTION_TEMPLATE" desc = "The string displayed when a calculator answer is suggested.">
     <ph name="EXPRESSION">$1</ph> = <ph name="ANSWER">$2</ph>
   </message>
+
+  <!-- Supplementary Omnibox header strings, that appear above suggestion groups. -->
+  <message name="IDS_OMNIBOX_HEADER_POPULAR_TOPICS" desc="The text displayed above the popular topic search suggestions section.">
+    Popular topics
+  </message>
+
 </grit-part>
diff --git a/components/omnibox_strings_grdp/IDS_OMNIBOX_HEADER_POPULAR_TOPICS.png.sha1 b/components/omnibox_strings_grdp/IDS_OMNIBOX_HEADER_POPULAR_TOPICS.png.sha1
new file mode 100644
index 0000000..42f1960
--- /dev/null
+++ b/components/omnibox_strings_grdp/IDS_OMNIBOX_HEADER_POPULAR_TOPICS.png.sha1
@@ -0,0 +1 @@
+264b0c739cbc42f923749c3aead40d9cdb304c54
\ No newline at end of file
diff --git a/components/optimization_guide/content/browser/test_page_content_annotator.h b/components/optimization_guide/content/browser/test_page_content_annotator.h
index c9dc7836..81d839b 100644
--- a/components/optimization_guide/content/browser/test_page_content_annotator.h
+++ b/components/optimization_guide/content/browser/test_page_content_annotator.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_OPTIMIZATION_GUIDE_CONTENT_BROWSER_TEST_PAGE_CONTENT_ANNOTATOR_H_
 
 #include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
 #include "components/optimization_guide/content/browser/page_content_annotator.h"
 #include "components/optimization_guide/core/page_content_annotations_common.h"
 
diff --git a/components/optimization_guide/core/entity_annotator_native_library.cc b/components/optimization_guide/core/entity_annotator_native_library.cc
index 261ecba8..c545015 100644
--- a/components/optimization_guide/core/entity_annotator_native_library.cc
+++ b/components/optimization_guide/core/entity_annotator_native_library.cc
@@ -4,12 +4,16 @@
 
 #include "components/optimization_guide/core/entity_annotator_native_library.h"
 
+#include <optional>
+
 #include "base/base_paths.h"
 #include "base/compiler_specific.h"
+#include "base/containers/flat_set.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "components/optimization_guide/core/model_util.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
@@ -27,30 +31,40 @@
 
 namespace {
 
-const char kModelMetadataBaseName[] = "model_metadata.pb";
-const char kWordEmbeddingsBaseName[] = "word_embeddings";
-const char kNameTableBaseName[] = "entities_names";
-const char kMetadataTableBaseName[] = "entities_metadata";
-const char kNameFilterBaseName[] = "entities_names_filter";
-const char kPrefixFilterBaseName[] = "entities_prefixes_filter";
+const base::FilePath::CharType kModelMetadataBaseName[] =
+    FILE_PATH_LITERAL("model_metadata.pb");
+const base::FilePath::CharType kWordEmbeddingsBaseName[] =
+    FILE_PATH_LITERAL("word_embeddings");
+const base::FilePath::CharType kNameTableBaseName[] =
+    FILE_PATH_LITERAL("entities_names");
+const base::FilePath::CharType kMetadataTableBaseName[] =
+    FILE_PATH_LITERAL("entities_metadata");
+const base::FilePath::CharType kNameFilterBaseName[] =
+    FILE_PATH_LITERAL("entities_names_filter");
+const base::FilePath::CharType kPrefixFilterBaseName[] =
+    FILE_PATH_LITERAL("entities_prefixes_filter");
 
-// Sets |field_to_set| with the full file path of |base_name|'s entry in
-// |base_to_full_file_path|. Returns whether |base_name| is in
-// |base_to_full_file_path|.
-absl::optional<std::string> GetFilePathFromMap(
-    const std::string& base_name,
-    const base::flat_map<std::string, base::FilePath>& base_to_full_file_path) {
-  auto it = base_to_full_file_path.find(base_name);
-  return it == base_to_full_file_path.end()
-             ? absl::nullopt
-             : absl::make_optional(FilePathToString(it->second));
+std::optional<std::string> GetStringFilePathForAdditionalFile(
+    const ModelInfo& model_info,
+    const base::FilePath::StringType& base_name) {
+  std::optional<base::FilePath> file_path =
+      model_info.GetAdditionalFileWithBaseName(base_name);
+  if (!file_path) {
+    return std::nullopt;
+  }
+  return FilePathToString(*file_path);
 }
 
 // Returns the expected base name for |slice|. Will be of the form
 // |slice|-|base_name|.
-std::string GetSliceBaseName(const std::string& slice,
-                             const std::string& base_name) {
-  return slice + "-" + base_name;
+base::FilePath::StringType GetSliceBaseName(
+    const std::string& slice,
+    const base::FilePath::StringType& base_name) {
+#if BUILDFLAG(IS_WIN)
+  return base::UTF8ToWide(slice) + FILE_PATH_LITERAL("-") + base_name;
+#else
+  return slice + FILE_PATH_LITERAL("-") + base_name;
+#endif
 }
 
 class ScopedEntityAnnotatorCreationStatusRecorder {
@@ -339,7 +353,7 @@
   }
 
   // // Validate the model metadata.
-  absl::optional<proto::PageEntitiesModelMetadata> entities_model_metadata =
+  std::optional<proto::PageEntitiesModelMetadata> entities_model_metadata =
       ParsedAnyMetadata<proto::PageEntitiesModelMetadata>(
           model_info.GetModelMetadata().value());
   if (!entities_model_metadata) {
@@ -357,13 +371,8 @@
       options, FilePathToString(model_info.GetModelFilePath()).c_str());
 
   // Attach the additional files required by the model.
-  base::flat_map<std::string, base::FilePath> base_to_full_file_path;
-  for (const auto& model_file : model_info.GetAdditionalFiles()) {
-    base_to_full_file_path.insert(
-        {FilePathToString(model_file.BaseName()), model_file});
-  }
-  absl::optional<std::string> model_metadata_file_path =
-      GetFilePathFromMap(kModelMetadataBaseName, base_to_full_file_path);
+  std::optional<std::string> model_metadata_file_path =
+      GetStringFilePathForAdditionalFile(model_info, kModelMetadataBaseName);
   if (!model_metadata_file_path) {
     *status = EntityAnnotatorCreationStatus::
         kMissingAdditionalEntitiesModelMetadataPath;
@@ -371,8 +380,8 @@
   }
   options_set_model_metadata_file_path_func_(options,
                                              model_metadata_file_path->c_str());
-  absl::optional<std::string> word_embeddings_file_path =
-      GetFilePathFromMap(kWordEmbeddingsBaseName, base_to_full_file_path);
+  std::optional<std::string> word_embeddings_file_path =
+      GetStringFilePathForAdditionalFile(model_info, kWordEmbeddingsBaseName);
   if (!word_embeddings_file_path) {
     *status =
         EntityAnnotatorCreationStatus::kMissingAdditionalWordEmbeddingsPath;
@@ -384,37 +393,36 @@
   base::flat_set<std::string> slices(entities_model_metadata->slice().begin(),
                                      entities_model_metadata->slice().end());
   for (const auto& slice_id : slices) {
-    absl::optional<std::string> name_filter_path;
+    std::optional<std::string> name_filter_path;
     if (should_provide_filter_path_) {
-      name_filter_path =
-          GetFilePathFromMap(GetSliceBaseName(slice_id, kNameFilterBaseName),
-                             base_to_full_file_path);
+      name_filter_path = GetStringFilePathForAdditionalFile(
+          model_info, GetSliceBaseName(slice_id, kNameFilterBaseName));
       if (!name_filter_path) {
         *status =
             EntityAnnotatorCreationStatus::kMissingAdditionalNameFilterPath;
         return false;
       }
     }
-    absl::optional<std::string> name_table_path = GetFilePathFromMap(
-        GetSliceBaseName(slice_id, kNameTableBaseName), base_to_full_file_path);
+    std::optional<std::string> name_table_path =
+        GetStringFilePathForAdditionalFile(
+            model_info, GetSliceBaseName(slice_id, kNameTableBaseName));
     if (!name_table_path) {
       *status = EntityAnnotatorCreationStatus::kMissingAdditionalNameTablePath;
       return false;
     }
-    absl::optional<std::string> prefix_filter_path;
+    std::optional<std::string> prefix_filter_path;
     if (should_provide_filter_path_) {
-      prefix_filter_path =
-          GetFilePathFromMap(GetSliceBaseName(slice_id, kPrefixFilterBaseName),
-                             base_to_full_file_path);
+      prefix_filter_path = GetStringFilePathForAdditionalFile(
+          model_info, GetSliceBaseName(slice_id, kPrefixFilterBaseName));
       if (!prefix_filter_path) {
         *status =
             EntityAnnotatorCreationStatus::kMissingAdditionalPrefixFilterPath;
         return false;
       }
     }
-    absl::optional<std::string> metadata_table_path =
-        GetFilePathFromMap(GetSliceBaseName(slice_id, kMetadataTableBaseName),
-                           base_to_full_file_path);
+    std::optional<std::string> metadata_table_path =
+        GetStringFilePathForAdditionalFile(
+            model_info, GetSliceBaseName(slice_id, kMetadataTableBaseName));
     if (!metadata_table_path) {
       *status =
           EntityAnnotatorCreationStatus::kMissingAdditionalMetadataTablePath;
@@ -441,7 +449,7 @@
 }
 
 DISABLE_CFI_DLSYM
-absl::optional<std::vector<ScoredEntityMetadata>>
+std::optional<std::vector<ScoredEntityMetadata>>
 EntityAnnotatorNativeLibrary::AnnotateText(void* annotator,
                                            const std::string& text) {
   DCHECK(IsValid());
@@ -472,7 +480,7 @@
 }
 
 DISABLE_CFI_DLSYM
-absl::optional<EntityMetadata>
+std::optional<EntityMetadata>
 EntityAnnotatorNativeLibrary::GetEntityMetadataForEntityId(
     void* annotator,
     const std::string& entity_id) {
diff --git a/components/optimization_guide/core/entity_annotator_native_library.h b/components/optimization_guide/core/entity_annotator_native_library.h
index db46c58d..875ab4d 100644
--- a/components/optimization_guide/core/entity_annotator_native_library.h
+++ b/components/optimization_guide/core/entity_annotator_native_library.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_OPTIMIZATION_GUIDE_CORE_ENTITY_ANNOTATOR_NATIVE_LIBRARY_H_
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/native_library.h"
@@ -79,12 +80,12 @@
   void DeleteEntityAnnotator(void* entity_annotator);
 
   // Uses |annotator| to annotate entities present in |text|.
-  absl::optional<std::vector<ScoredEntityMetadata>> AnnotateText(
+  std::optional<std::vector<ScoredEntityMetadata>> AnnotateText(
       void* annotator,
       const std::string& text);
 
   // Returns entity metadata from |annotator| for |entity_id|.
-  absl::optional<EntityMetadata> GetEntityMetadataForEntityId(
+  std::optional<EntityMetadata> GetEntityMetadataForEntityId(
       void* annotator,
       const std::string& entity_id);
 
diff --git a/components/optimization_guide/core/model_info.cc b/components/optimization_guide/core/model_info.cc
index 6e9d8035..3bcf253 100644
--- a/components/optimization_guide/core/model_info.cc
+++ b/components/optimization_guide/core/model_info.cc
@@ -4,6 +4,7 @@
 
 #include "components/optimization_guide/core/model_info.h"
 
+#include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
 #include "base/strings/utf_string_conversions.h"
@@ -13,9 +14,10 @@
 namespace optimization_guide {
 
 ModelInfo::ModelInfo(const base::FilePath& model_file_path,
-                     const base::flat_set<base::FilePath>& additional_files,
+                     const base::flat_map<base::FilePath::StringType,
+                                          base::FilePath>& additional_files,
                      const int64_t version,
-                     const absl::optional<proto::Any>& model_metadata)
+                     const std::optional<proto::Any>& model_metadata)
     : model_file_path_(model_file_path),
       additional_files_(additional_files),
       version_(version),
@@ -27,17 +29,17 @@
 // static
 std::unique_ptr<ModelInfo> ModelInfo::Create(
     const proto::PredictionModel& model) {
-  absl::optional<base::FilePath> model_file_path =
+  std::optional<base::FilePath> model_file_path =
       StringToFilePath(model.model().download_url());
   if (!model_file_path)
     return nullptr;
   if (!model.model_info().has_version())
     return nullptr;
 
-  base::flat_set<base::FilePath> additional_files;
+  base::flat_map<base::FilePath::StringType, base::FilePath> additional_files;
   for (const proto::AdditionalModelFile& additional_file :
        model.model_info().additional_files()) {
-    absl::optional<base::FilePath> additional_file_path =
+    std::optional<base::FilePath> additional_file_path =
         StringToFilePath(additional_file.file_path());
     if (!additional_file_path) {
       continue;
@@ -46,10 +48,11 @@
       NOTREACHED() << FilePathToString(*additional_file_path);
       continue;
     }
-    additional_files.insert(*additional_file_path);
+    additional_files[additional_file_path->BaseName().value()] =
+        *additional_file_path;
   }
 
-  absl::optional<proto::Any> model_metadata;
+  std::optional<proto::Any> model_metadata;
   if (model.model_info().has_model_metadata())
     model_metadata = model.model_info().model_metadata();
 
@@ -64,14 +67,27 @@
 }
 
 base::flat_set<base::FilePath> ModelInfo::GetAdditionalFiles() const {
-  return additional_files_;
+  base::flat_set<base::FilePath> files;
+  for (auto it = additional_files_.begin(); it != additional_files_.end();
+       it++) {
+    files.insert(it->second);
+  }
+  return files;
+}
+
+std::optional<base::FilePath> ModelInfo::GetAdditionalFileWithBaseName(
+    const base::FilePath::StringType& base_name) const {
+  if (base::Contains(additional_files_, base_name)) {
+    return additional_files_.at(base_name);
+  }
+  return std::nullopt;
 }
 
 int64_t ModelInfo::GetVersion() const {
   return version_;
 }
 
-absl::optional<proto::Any> ModelInfo::GetModelMetadata() const {
+std::optional<proto::Any> ModelInfo::GetModelMetadata() const {
   return model_metadata_;
 }
 
diff --git a/components/optimization_guide/core/model_info.h b/components/optimization_guide/core/model_info.h
index 7ceb824..edf0ee7 100644
--- a/components/optimization_guide/core/model_info.h
+++ b/components/optimization_guide/core/model_info.h
@@ -6,11 +6,12 @@
 #define COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_INFO_H_
 
 #include <memory>
+#include <optional>
 
+#include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/files/file_path.h"
 #include "components/optimization_guide/proto/models.pb.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace optimization_guide {
 
@@ -38,19 +39,25 @@
   // packaged along with the model.
   base::flat_set<base::FilePath> GetAdditionalFiles() const;
 
+  // Returns the absolute file path of any additional files that were packaged
+  // along with the model based on `base_name`.
+  std::optional<base::FilePath> GetAdditionalFileWithBaseName(
+      const base::FilePath::StringType& base_name) const;
+
   // Returns the metadata that the server provided specific to this model, if
   // applicable.
-  absl::optional<proto::Any> GetModelMetadata() const;
+  std::optional<proto::Any> GetModelMetadata() const;
 
  private:
   ModelInfo(const base::FilePath& model_file_path,
-            const base::flat_set<base::FilePath>& additional_files,
+            const base::flat_map<base::FilePath::StringType, base::FilePath>&
+                additional_files,
             const int64_t version,
-            const absl::optional<proto::Any>& model_metadata);
+            const std::optional<proto::Any>& model_metadata);
   base::FilePath model_file_path_;
-  base::flat_set<base::FilePath> additional_files_;
+  base::flat_map<base::FilePath::StringType, base::FilePath> additional_files_;
   int64_t version_;
-  absl::optional<proto::Any> model_metadata_;
+  std::optional<proto::Any> model_metadata_;
 };
 
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/prediction_manager_unittest.cc b/components/optimization_guide/core/prediction_manager_unittest.cc
index 650efc1..2b56f76 100644
--- a/components/optimization_guide/core/prediction_manager_unittest.cc
+++ b/components/optimization_guide/core/prediction_manager_unittest.cc
@@ -855,8 +855,10 @@
     EXPECT_EQ(received_model->GetModelMetadata()->type_url(), "sometypeurl");
     EXPECT_EQ(base_model_dir.Append(GetBaseFileNameForModels()),
               received_model->GetModelFilePath());
-    EXPECT_EQ(received_model->GetAdditionalFiles(),
-              base::flat_set<base::FilePath>{additional_file_path});
+    auto additional_file = received_model->GetAdditionalFileWithBaseName(
+        base::FilePath::StringType(FILE_PATH_LITERAL("additional_file.txt")));
+    ASSERT_TRUE(additional_file);
+    EXPECT_EQ(*additional_file, additional_file_path);
 
     // Make sure we do not record the model available histogram again.
     model_ready_histogram_tester.ExpectTotalCount(
diff --git a/components/optimization_guide/core/test_model_info_builder.cc b/components/optimization_guide/core/test_model_info_builder.cc
index dd69ce6..161cf38 100644
--- a/components/optimization_guide/core/test_model_info_builder.cc
+++ b/components/optimization_guide/core/test_model_info_builder.cc
@@ -37,7 +37,7 @@
 }
 
 TestModelInfoBuilder& TestModelInfoBuilder::SetModelMetadata(
-    absl::optional<proto::Any> model_metadata) {
+    std::optional<proto::Any> model_metadata) {
   if (!model_metadata) {
     model_.mutable_model_info()->clear_model_metadata();
     return *this;
diff --git a/components/optimization_guide/core/test_model_info_builder.h b/components/optimization_guide/core/test_model_info_builder.h
index fe2831c..1d63515 100644
--- a/components/optimization_guide/core/test_model_info_builder.h
+++ b/components/optimization_guide/core/test_model_info_builder.h
@@ -6,7 +6,9 @@
 #define COMPONENTS_OPTIMIZATION_GUIDE_CORE_TEST_MODEL_INFO_BUILDER_H_
 
 #include <memory>
+#include <optional>
 
+#include "base/containers/flat_set.h"
 #include "components/optimization_guide/core/model_info.h"
 #include "components/optimization_guide/proto/models.pb.h"
 
@@ -31,7 +33,7 @@
   TestModelInfoBuilder& SetVersion(int64_t version);
 
   TestModelInfoBuilder& SetModelMetadata(
-      absl::optional<proto::Any> model_metadata);
+      std::optional<proto::Any> model_metadata);
 
   std::unique_ptr<ModelInfo> Build();
 
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index 82e3e31..3e64fe9 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit 82e3e31f26bc1c980b5616c7845c7fff4df69fa2
+Subproject commit 3e64fe978f46937931741c6842958594b6ed7e9a
diff --git a/components/performance_manager/public/features.h b/components/performance_manager/public/features.h
index ea53371..bea9ddb 100644
--- a/components/performance_manager/public/features.h
+++ b/components/performance_manager/public/features.h
@@ -191,12 +191,12 @@
 // Whether tabs are discarded under high memory pressure.
 BASE_DECLARE_FEATURE(kUrgentPageDiscarding);
 
-// Enable PageTimelineMonitor timer and by extension, PageTimelineState event
+// Enable PageResourceMonitor timer and by extension, PageTimelineState event
 // collection.
 BASE_DECLARE_FEATURE(kPageTimelineMonitor);
 
 // Set the interval in seconds between calls of
-// PageTimelineMonitor::CollectSlice()
+// PageResourceMonitor::CollectSlice()
 extern const base::FeatureParam<base::TimeDelta> kPageTimelineStateIntervalTime;
 
 // Whether to use the resource_attribution::CPUMeasurementMonitor for logging
diff --git a/components/plus_addresses/features.cc b/components/plus_addresses/features.cc
index 6e7cce3..e0f033cc 100644
--- a/components/plus_addresses/features.cc
+++ b/components/plus_addresses/features.cc
@@ -13,7 +13,9 @@
              "PlusAddressesEnabled",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-const char kEnterprisePlusAddressLabelOverrideName[] = "suggestion-label";
+const char kEnterprisePlusAddressSuggestionLabelOverrideName[] =
+    "suggestion-label";
+const char kEnterprisePlusAddressSettingsLabelOverrideName[] = "settings-label";
 const char kEnterprisePlusAddressOAuthScopeName[] = "oauth-scope";
 const char kEnterprisePlusAddressServerUrlName[] = "server-url";
 const char kSyncWithEnterprisePlusAddressServerName[] = "sync-with-server";
@@ -21,8 +23,14 @@
 const char kPlusAddressManagementUrlName[] = "manage-url";
 const char kPlusAddressExcludedSitesName[] = "excluded-sites";
 
-const base::FeatureParam<std::string> kEnterprisePlusAddressLabelOverride{
-    &kFeature, kEnterprisePlusAddressLabelOverrideName, "Lorem Ipsum"};
+const base::FeatureParam<std::string>
+    kEnterprisePlusAddressSuggestionLabelOverride{
+        &kFeature, kEnterprisePlusAddressSuggestionLabelOverrideName,
+        "Lorem Ipsum"};
+const base::FeatureParam<std::string>
+    kEnterprisePlusAddressSettingsLabelOverride{
+        &kFeature, kEnterprisePlusAddressSettingsLabelOverrideName,
+        "Lorem Ipsum"};
 const base::FeatureParam<std::string> kEnterprisePlusAddressOAuthScope{
     &kFeature, kEnterprisePlusAddressOAuthScopeName, ""};
 const base::FeatureParam<std::string> kEnterprisePlusAddressServerUrl{
diff --git a/components/plus_addresses/features.h b/components/plus_addresses/features.h
index e4ff1c75..d3a9f7a 100644
--- a/components/plus_addresses/features.h
+++ b/components/plus_addresses/features.h
@@ -18,7 +18,13 @@
 // label. Defaults to generic Lorem Ipsum as strings are not yet determined.
 COMPONENT_EXPORT(PLUS_ADDRESSES_FEATURES)
 extern const base::FeatureParam<std::string>
-    kEnterprisePlusAddressLabelOverride;
+    kEnterprisePlusAddressSuggestionLabelOverride;
+
+// Used to control the enterprise plus address feature's label in settings.
+// Defaults to generic Lorem Ipsum as strings are not yet determined.
+COMPONENT_EXPORT(PLUS_ADDRESSES_FEATURES)
+extern const base::FeatureParam<std::string>
+    kEnterprisePlusAddressSettingsLabelOverride;
 
 // Used to control the enterprise plus address feature's OAuth scope.
 COMPONENT_EXPORT(PLUS_ADDRESSES_FEATURES)
diff --git a/components/plus_addresses/plus_address_service.cc b/components/plus_addresses/plus_address_service.cc
index 7e762db..dcbe61e 100644
--- a/components/plus_addresses/plus_address_service.cc
+++ b/components/plus_addresses/plus_address_service.cc
@@ -11,6 +11,7 @@
 #include "components/plus_addresses/plus_address_client.h"
 #include "components/plus_addresses/plus_address_prefs.h"
 #include "components/plus_addresses/plus_address_types.h"
+#include "components/prefs/pref_service.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/base/persistent_repeating_timer.h"
 #include "components/signin/public/identity_manager/account_info.h"
@@ -53,8 +54,11 @@
       pref_service_(pref_service),
       plus_address_client_(std::move(plus_address_client)),
       excluded_sites_(GetAndParseExcludedSites()) {
-  // Begin PlusAddress periodic actions at construction.
-  CreateAndStartTimer();
+  if (pref_service) {
+    // Clear the pref to always force a poll on service construction.
+    pref_service->ClearPref(prefs::kPlusAddressLastFetchedTime);
+    CreateAndStartTimer();
+  }
   if (identity_manager) {
     identity_manager_observation_.Observe(identity_manager);
   }
@@ -179,7 +183,7 @@
   // TODO(crbug.com/1467623): once ready, use standard
   // `l10n_util::GetStringUTF16` instead of using feature params.
   return base::UTF8ToUTF16(
-      plus_addresses::kEnterprisePlusAddressLabelOverride.Get());
+      plus_addresses::kEnterprisePlusAddressSuggestionLabelOverride.Get());
 }
 
 absl::optional<std::string> PlusAddressService::GetPrimaryEmail() {
diff --git a/components/plus_addresses/plus_address_service_unittest.cc b/components/plus_addresses/plus_address_service_unittest.cc
index 7a6dd7a..d3b2b55 100644
--- a/components/plus_addresses/plus_address_service_unittest.cc
+++ b/components/plus_addresses/plus_address_service_unittest.cc
@@ -96,14 +96,14 @@
       /*is_off_the_record=*/false));
 }
 
-// Tests for the label overrides. These tests are not in the enabled/disabled
-// fixtures as they vary parameters.
-TEST_F(PlusAddressServiceTest, LabelOverrides) {
+// Tests for the suggestion label overrides. These tests are not in the
+// enabled/disabled fixtures as they vary parameters.
+TEST_F(PlusAddressServiceTest, SuggestionLabelOverride) {
   base::test::ScopedFeatureList scoped_feature_list;
   // Setting the override should result in echoing the override back.
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       plus_addresses::kFeature,
-      {{plus_addresses::kEnterprisePlusAddressLabelOverride.name,
+      {{plus_addresses::kEnterprisePlusAddressSuggestionLabelOverride.name,
         "mattwashere"}});
   PlusAddressService service;
   EXPECT_EQ(service.GetCreateSuggestionLabel(), u"mattwashere");
@@ -114,7 +114,7 @@
   // Setting the override should result in echoing the override back.
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       plus_addresses::kFeature,
-      {{plus_addresses::kEnterprisePlusAddressLabelOverride.name,
+      {{plus_addresses::kEnterprisePlusAddressSuggestionLabelOverride.name,
         "matt was here"}});
   PlusAddressService service;
   EXPECT_EQ(service.GetCreateSuggestionLabel(), u"matt was here");
diff --git a/components/privacy_sandbox/privacy_sandbox_settings_impl.cc b/components/privacy_sandbox/privacy_sandbox_settings_impl.cc
index b3265d4e..834028a 100644
--- a/components/privacy_sandbox/privacy_sandbox_settings_impl.cc
+++ b/components/privacy_sandbox/privacy_sandbox_settings_impl.cc
@@ -733,10 +733,6 @@
 }
 
 void PrivacySandboxSettingsImpl::OnRelatedWebsiteSetsEnabledPrefChanged() {
-  if (!base::FeatureList::IsEnabled(features::kFirstPartySets)) {
-    return;
-  }
-
   for (auto& observer : observers_) {
     observer.OnFirstPartySetsEnabledChanged(AreRelatedWebsiteSetsEnabled());
   }
@@ -848,10 +844,6 @@
 }
 
 void PrivacySandboxSettingsImpl::OnBlockAllThirdPartyCookiesChanged() {
-  if (!base::FeatureList::IsEnabled(features::kFirstPartySets)) {
-    return;
-  }
-
   for (auto& observer : observers_) {
     observer.OnFirstPartySetsEnabledChanged(AreRelatedWebsiteSetsEnabled());
   }
diff --git a/components/privacy_sandbox/privacy_sandbox_settings_impl_unittest.cc b/components/privacy_sandbox/privacy_sandbox_settings_impl_unittest.cc
index e071a7d6..b14a085 100644
--- a/components/privacy_sandbox/privacy_sandbox_settings_impl_unittest.cc
+++ b/components/privacy_sandbox/privacy_sandbox_settings_impl_unittest.cc
@@ -336,9 +336,7 @@
 
 TEST_F(PrivacySandboxSettingsTest, OnRelatedWebsiteSetsEnabledChanged) {
   // OnRelatedWebsiteSetsEnabledChanged() should only call observers when the
-  // base::Feature is enabled and the pref changes.
-  base::test::ScopedFeatureList feature_list_;
-  feature_list_.InitAndEnableFeature(features::kFirstPartySets);
+  // pref changes.
   privacy_sandbox_test_util::MockPrivacySandboxObserver observer;
   privacy_sandbox_settings()->AddObserver(&observer);
   EXPECT_CALL(observer, OnFirstPartySetsEnabledChanged(/*enabled=*/true));
@@ -349,18 +347,9 @@
   EXPECT_CALL(observer, OnFirstPartySetsEnabledChanged(/*enabled=*/false));
   prefs()->SetBoolean(prefs::kPrivacySandboxRelatedWebsiteSetsEnabled, false);
   testing::Mock::VerifyAndClearExpectations(&observer);
-
-  feature_list_.Reset();
-  feature_list_.InitAndDisableFeature(features::kFirstPartySets);
-  EXPECT_CALL(observer, OnFirstPartySetsEnabledChanged(testing::_)).Times(0);
-
-  prefs()->SetBoolean(prefs::kPrivacySandboxRelatedWebsiteSetsEnabled, true);
-  prefs()->SetBoolean(prefs::kPrivacySandboxRelatedWebsiteSetsEnabled, false);
 }
 
 TEST_F(PrivacySandboxSettingsTest, OnFirstPartySetsEnabledChanged3pcd) {
-  base::test::ScopedFeatureList feature_list_;
-  feature_list_.InitAndEnableFeature(features::kFirstPartySets);
   privacy_sandbox_test_util::MockPrivacySandboxObserver observer;
   privacy_sandbox_settings()->AddObserver(&observer);
 
diff --git a/components/push_notification/BUILD.gn b/components/push_notification/BUILD.gn
new file mode 100644
index 0000000..0b0ba5a7
--- /dev/null
+++ b/components/push_notification/BUILD.gn
@@ -0,0 +1,11 @@
+# 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("push_notification") {
+  sources = [
+    "push_notification_service.cc",
+    "push_notification_service.h",
+  ]
+  deps = [ "//base" ]
+}
diff --git a/components/push_notification/DIR_METADATA b/components/push_notification/DIR_METADATA
new file mode 100644
index 0000000..a692f60
--- /dev/null
+++ b/components/push_notification/DIR_METADATA
@@ -0,0 +1,3 @@
+buganizer {
+  component_id: 1108889
+}
diff --git a/components/push_notification/OWNERS b/components/push_notification/OWNERS
new file mode 100644
index 0000000..cedfca3
--- /dev/null
+++ b/components/push_notification/OWNERS
@@ -0,0 +1,3 @@
+hansberry@chromium.org
+julietlevesque@google.com
+akingsb@google.com
diff --git a/components/push_notification/push_notification_service.cc b/components/push_notification/push_notification_service.cc
new file mode 100644
index 0000000..3d846f06
--- /dev/null
+++ b/components/push_notification/push_notification_service.cc
@@ -0,0 +1,12 @@
+// 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/push_notification/push_notification_service.h"
+
+namespace push_notification {
+
+PushNotificationService::PushNotificationService() = default;
+PushNotificationService::~PushNotificationService() = default;
+
+}  // namespace push_notification
diff --git a/components/push_notification/push_notification_service.h b/components/push_notification/push_notification_service.h
new file mode 100644
index 0000000..1644132
--- /dev/null
+++ b/components/push_notification/push_notification_service.h
@@ -0,0 +1,21 @@
+// 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_PUSH_NOTIFICATION_PUSH_NOTIFICATION_SERVICE_H_
+#define COMPONENTS_PUSH_NOTIFICATION_PUSH_NOTIFICATION_SERVICE_H_
+
+namespace push_notification {
+
+// Base class for the PushNotificationService. This class along with other
+// classes in this directory are inherited from to create platform specific push
+// notification services.
+class PushNotificationService {
+ public:
+  PushNotificationService();
+  virtual ~PushNotificationService();
+};
+
+}  // namespace push_notification
+
+#endif  // COMPONENTS_PUSH_NOTIFICATION_PUSH_NOTIFICATION_SERVICE_H_
diff --git a/components/resources/BUILD.gn b/components/resources/BUILD.gn
index 2eef560..ff4f44c 100644
--- a/components/resources/BUILD.gn
+++ b/components/resources/BUILD.gn
@@ -5,7 +5,6 @@
 import("//build/config/android/config.gni")
 import("//build/config/features.gni")
 import("//components/safe_browsing/buildflags.gni")
-import("//components/signin/features.gni")
 import("//components/supervised_user/buildflags.gni")
 import("//printing/buildflags/buildflags.gni")
 import("//tools/grit/grit_rule.gni")
@@ -106,8 +105,6 @@
     "components_resources_300_percent.pak",
   ]
   output_dir = "$root_gen_dir/components"
-
-  defines = [ "enable_search_engine_choice=$enable_search_engine_choice" ]
 }
 
 action("about_credits") {
diff --git a/components/safe_browsing/OWNERS b/components/safe_browsing/OWNERS
index 1c80c5e..593fa2e 100644
--- a/components/safe_browsing/OWNERS
+++ b/components/safe_browsing/OWNERS
@@ -2,5 +2,6 @@
 
 drubery@chromium.org
 nparker@chromium.org
+thefrog@chromium.org
 vakh@chromium.org
 xinghuilu@chromium.org
diff --git a/components/safe_browsing/content/browser/browser_url_loader_throttle.cc b/components/safe_browsing/content/browser/browser_url_loader_throttle.cc
index fc8fcf4..7beac7e 100644
--- a/components/safe_browsing/content/browser/browser_url_loader_throttle.cc
+++ b/components/safe_browsing/content/browser/browser_url_loader_throttle.cc
@@ -188,9 +188,6 @@
         /*complete_callback=*/
         base::BindRepeating(&BrowserURLLoaderThrottle::OnCompleteSyncCheck,
                             weak_factory_.GetWeakPtr()),
-        /*slow_check_callback=*/
-        base::BindRepeating(&BrowserURLLoaderThrottle::NotifySyncSlowCheck,
-                            weak_factory_.GetWeakPtr()),
         /*url_real_time_lookup_enabled=*/false,
         /*can_urt_check_subresource_url=*/false, can_check_db,
         /*can_check_high_confidence_allowlist=*/true,
@@ -200,14 +197,11 @@
         /*is_mechanism_experiment_allowed=*/false,
         /*hash_realtime_selection=*/
         hash_realtime_utils::HashRealTimeSelection::kNone);
-    // The slow_check_callback is set to DoNothing because we don't pause
-    // reading response body for async check.
     async_sb_checker_ = std::make_unique<UrlCheckerOnSB>(
         delegate_getter, frame_tree_node_id, web_contents_getter,
         /*complete_callback=*/
         base::BindRepeating(&BrowserURLLoaderThrottle::OnCompleteAsyncCheck,
                             weak_factory_.GetWeakPtr()),
-        /*slow_check_callback=*/base::DoNothing(),
         url_real_time_lookup_enabled_, can_urt_check_subresource_url,
         can_check_db, can_check_high_confidence_allowlist,
         url_lookup_service_metric_suffix_, url_lookup_service,
@@ -219,9 +213,6 @@
         /*complete_callback=*/
         base::BindRepeating(&BrowserURLLoaderThrottle::OnCompleteSyncCheck,
                             weak_factory_.GetWeakPtr()),
-        /*slow_check_callback=*/
-        base::BindRepeating(&BrowserURLLoaderThrottle::NotifySyncSlowCheck,
-                            weak_factory_.GetWeakPtr()),
         url_real_time_lookup_enabled_, can_urt_check_subresource_url,
         can_check_db, can_check_high_confidence_allowlist,
         url_lookup_service_metric_suffix_, url_lookup_service,
@@ -514,11 +505,6 @@
   DCHECK_LT(0u, pending_sync_checks_);
   pending_sync_checks_--;
 
-  if (result.slow_check) {
-    DCHECK_LT(0u, pending_sync_slow_checks_);
-    pending_sync_slow_checks_--;
-  }
-
   // If the resource load is going to finish (either being cancelled or
   // resumed), record the total delay.
   if (!result.proceed || pending_sync_checks_ == 0) {
@@ -533,10 +519,6 @@
   }
 
   if (result.proceed) {
-    if (pending_sync_slow_checks_ == 0 && result.slow_check) {
-      delegate_->ResumeReadingBodyFromNet();
-    }
-
     if (pending_sync_checks_ == 0 && deferred_) {
       deferred_ = false;
       TRACE_EVENT_NESTABLE_ASYNC_END0("safe_browsing", "Deferred",
@@ -614,26 +596,8 @@
   }
 }
 
-void BrowserURLLoaderThrottle::NotifySyncSlowCheck() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  pending_sync_slow_checks_++;
-
-  // Pending slow checks indicate that the resource may be unsafe. In that case,
-  // pause reading response body from network to minimize the chance of
-  // processing unsafe contents (e.g., writing unsafe contents into cache),
-  // until we get the results. According to the results, we may resume reading
-  // or cancel the resource load.
-  // For real time Safe Browsing checks, we continue reading the response body
-  // but, similar to hash-based checks, do not process it until we know it is
-  // SAFE.
-  if (pending_sync_slow_checks_ == 1) {
-    delegate_->PauseReadingBodyFromNet();
-  }
-}
-
 void BrowserURLLoaderThrottle::DeleteUrlCheckerOnSB() {
   pending_sync_checks_ = 0;
-  pending_sync_slow_checks_ = 0;
   pending_async_checks_ = 0;
   if (base::FeatureList::IsEnabled(kSafeBrowsingOnUIThread)) {
     sync_sb_checker_.reset();
diff --git a/components/safe_browsing/content/browser/browser_url_loader_throttle.h b/components/safe_browsing/content/browser/browser_url_loader_throttle.h
index c11bb011..11f012e 100644
--- a/components/safe_browsing/content/browser/browser_url_loader_throttle.h
+++ b/components/safe_browsing/content/browser/browser_url_loader_throttle.h
@@ -135,9 +135,6 @@
   // necessary.
   void SkipChecks();
 
-  // Called when a slow safe browsing check is ongoing.
-  void NotifySyncSlowCheck();
-
   // Returns the suffixed to be used for the TotalDelay2 metrics that specifies
   // which type of check was performed.
   std::string GetUrlCheckTypeForLogging(
@@ -157,9 +154,6 @@
   size_t pending_sync_checks_ = 0;
   size_t pending_async_checks_ = 0;
 
-  // How many slow checks that haven't received results.
-  size_t pending_sync_slow_checks_ = 0;
-
   // Whether future safe browsing checks should be skipped.
   bool skip_checks_ = false;
 
diff --git a/components/safe_browsing/content/browser/browser_url_loader_throttle_unittest.cc b/components/safe_browsing/content/browser/browser_url_loader_throttle_unittest.cc
index 9d0f08cf..0a5f4a17 100644
--- a/components/safe_browsing/content/browser/browser_url_loader_throttle_unittest.cc
+++ b/components/safe_browsing/content/browser/browser_url_loader_throttle_unittest.cc
@@ -116,7 +116,8 @@
               return ChromeUserPopulation();
             }),
             /*referrer_chain_provider=*/nullptr,
-            /*pref_service=*/nullptr) {}
+            /*pref_service=*/nullptr,
+            /*webui_delegate=*/nullptr) {}
 
   // RealTimeUrlLookupServiceBase:
   bool CanPerformFullURLLookup() const override { return true; }
@@ -131,14 +132,12 @@
       const GURL& url,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       RTLookupResponseCallback response_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override {}
   void SendSampledRequest(
       const GURL& url,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override {}
 
  private:
@@ -153,7 +152,6 @@
       const GURL& url,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       RTLookupResponseCallback response_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override {}
   absl::optional<std::string> GetDMTokenString() const override {
@@ -195,7 +193,6 @@
       GURL last_committed_url,
       scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
       base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui,
-      UrlRealTimeMechanism::WebUIDelegate* webui_delegate,
       base::WeakPtr<HashRealTimeService> hash_realtime_service_on_ui,
       scoped_refptr<SafeBrowsingLookupMechanismExperimenter>
           mechanism_experimenter,
@@ -219,7 +216,6 @@
                                    last_committed_url,
                                    ui_task_runner,
                                    url_lookup_service_on_ui,
-                                   webui_delegate,
                                    hash_realtime_service_on_ui,
                                    mechanism_experimenter,
                                    is_mechanism_experiment_allowed,
@@ -343,7 +339,6 @@
             /*last_committed_url=*/GURL(),
             /*ui_task_runner=*/base::SequencedTaskRunner::GetCurrentDefault(),
             /*url_lookup_service_on_ui=*/nullptr,
-            /*webui_delegate_=*/nullptr,
             /*hash_realtime_service_on_ui=*/nullptr,
             /*mechanism_experimenter=*/nullptr,
             /*is_mechanism_experiment_allowed=*/false,
@@ -368,7 +363,6 @@
               /*last_committed_url=*/GURL(),
               /*ui_task_runner=*/base::SequencedTaskRunner::GetCurrentDefault(),
               /*url_lookup_service_on_ui=*/nullptr,
-              /*webui_delegate_=*/nullptr,
               /*hash_realtime_service_on_ui=*/nullptr,
               /*mechanism_experimenter=*/nullptr,
               /*is_mechanism_experiment_allowed=*/false,
diff --git a/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc b/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc
index 9352d55..67cca79e 100644
--- a/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc
+++ b/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc
@@ -189,7 +189,7 @@
       /*can_check_db=*/true, /*can_check_high_confidence_allowlist=*/true,
       /*url_lookup_service_metric_suffix=*/".None",
       /*last_committed_url=*/GURL(), content::GetUIThreadTaskRunner({}),
-      /*url_lookup_service=*/nullptr, WebUIInfoSingleton::GetInstance(),
+      /*url_lookup_service=*/nullptr,
       /*hash_realtime_service_on_ui=*/nullptr,
       /*mechanism_experimenter=*/nullptr,
       /*is_mechanism_experiment_allowed=*/false,
diff --git a/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc b/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc
index a15152d..39cb4411 100644
--- a/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc
+++ b/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc
@@ -943,16 +943,8 @@
       ElementsAre(base::Bucket(4 /* MATCHED_ALLOWLIST */, 1)));
 }
 
-// crbug.com/1010007: crashes on win
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_TestNoRequestSentIfVerdictAlreadyCached \
-  DISABLED_TestNoRequestSentIfVerdictAlreadyCached
-#else
-#define MAYBE_TestNoRequestSentIfVerdictAlreadyCached \
-  TestNoRequestSentIfVerdictAlreadyCached
-#endif
 TEST_P(PasswordProtectionServiceBaseTest,
-       MAYBE_TestNoRequestSentIfVerdictAlreadyCached) {
+       TestNoRequestSentIfVerdictAlreadyCached) {
   histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
   ReusedPasswordAccountType reused_password_account_type;
   reused_password_account_type.set_account_type(
@@ -970,6 +962,7 @@
   EXPECT_THAT(
       histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
       ElementsAre(base::Bucket(5 /* RESPONSE_ALREADY_CACHED */, 1)));
+  ASSERT_TRUE(password_protection_service_->latest_response());
   EXPECT_EQ(LoginReputationClientResponse::LOW_REPUTATION,
             password_protection_service_->latest_response()->verdict_type());
 }
diff --git a/components/safe_browsing/content/browser/url_checker_on_sb.cc b/components/safe_browsing/content/browser/url_checker_on_sb.cc
index b759573..dfe00dc 100644
--- a/components/safe_browsing/content/browser/url_checker_on_sb.cc
+++ b/components/safe_browsing/content/browser/url_checker_on_sb.cc
@@ -26,12 +26,10 @@
 namespace safe_browsing {
 
 UrlCheckerOnSB::OnCompleteCheckResult::OnCompleteCheckResult(
-    bool slow_check,
     bool proceed,
     bool showed_interstitial,
     SafeBrowsingUrlCheckerImpl::PerformedCheck performed_check)
-    : slow_check(slow_check),
-      proceed(proceed),
+    : proceed(proceed),
       showed_interstitial(showed_interstitial),
       performed_check(performed_check) {}
 
@@ -58,7 +56,6 @@
     int frame_tree_node_id,
     base::RepeatingCallback<content::WebContents*()> web_contents_getter,
     OnCompleteCheckCallback complete_callback,
-    OnNotifySlowCheckCallback slow_check_callback,
     bool url_real_time_lookup_enabled,
     bool can_urt_check_subresource_url,
     bool can_check_db,
@@ -73,7 +70,6 @@
       frame_tree_node_id_(frame_tree_node_id),
       web_contents_getter_(web_contents_getter),
       complete_callback_(std::move(complete_callback)),
-      slow_check_callback_(std::move(slow_check_callback)),
       url_real_time_lookup_enabled_(url_real_time_lookup_enabled),
       can_urt_check_subresource_url_(can_urt_check_subresource_url),
       can_check_db_(can_check_db),
@@ -128,8 +124,7 @@
         can_urt_check_subresource_url_, can_check_db_,
         can_check_high_confidence_allowlist_, url_lookup_service_metric_suffix_,
         last_committed_url_, content::GetUIThreadTaskRunner({}),
-        url_lookup_service_, WebUIInfoSingleton::GetInstance(),
-        hash_realtime_service_, mechanism_experimenter_,
+        url_lookup_service_, hash_realtime_service_, mechanism_experimenter_,
         is_mechanism_experiment_allowed_, hash_realtime_selection_);
   }
 
@@ -169,33 +164,14 @@
     bool proceed,
     bool showed_interstitial,
     SafeBrowsingUrlCheckerImpl::PerformedCheck performed_check) {
-  if (!slow_check_notifier) {
-    OnCompleteCheck(false /* slow_check */, proceed, showed_interstitial,
-                    performed_check);
-    return;
-  }
-
-  if (base::FeatureList::IsEnabled(safe_browsing::kSafeBrowsingOnUIThread)) {
-    slow_check_callback_.Run();
-  } else {
-    content::GetUIThreadTaskRunner({})->PostTask(
-        FROM_HERE, base::BindOnce(slow_check_callback_));
-  }
-
-  // In this case |proceed| and |showed_interstitial| should be ignored. The
-  // result will be returned by calling |*slow_check_notifier| callback.
-  *slow_check_notifier =
-      base::BindOnce(&UrlCheckerOnSB::OnCompleteCheck, base::Unretained(this),
-                     true /* slow_check */);
+  OnCompleteCheck(proceed, showed_interstitial, performed_check);
 }
 
 void UrlCheckerOnSB::OnCompleteCheck(
-    bool slow_check,
     bool proceed,
     bool showed_interstitial,
     SafeBrowsingUrlCheckerImpl::PerformedCheck performed_check) {
-  OnCompleteCheckResult result(slow_check, proceed, showed_interstitial,
-                               performed_check);
+  OnCompleteCheckResult result(proceed, showed_interstitial, performed_check);
   if (base::FeatureList::IsEnabled(safe_browsing::kSafeBrowsingOnUIThread)) {
     complete_callback_.Run(result);
   } else {
diff --git a/components/safe_browsing/content/browser/url_checker_on_sb.h b/components/safe_browsing/content/browser/url_checker_on_sb.h
index b72cdbe..7a25baf 100644
--- a/components/safe_browsing/content/browser/url_checker_on_sb.h
+++ b/components/safe_browsing/content/browser/url_checker_on_sb.h
@@ -58,14 +58,9 @@
 
   struct OnCompleteCheckResult {
     OnCompleteCheckResult(
-        bool slow_check,
         bool proceed,
         bool showed_interstitial,
         SafeBrowsingUrlCheckerImpl::PerformedCheck performed_check);
-    // |slow_check| indicates whether it reports the result of a slow check.
-    // (Please see comments of OnCheckUrlResult() for what slow
-    // check means).
-    bool slow_check;
     bool proceed;
     bool showed_interstitial;
     SafeBrowsingUrlCheckerImpl::PerformedCheck performed_check;
@@ -74,8 +69,6 @@
   using OnCompleteCheckCallback =
       base::RepeatingCallback<void(OnCompleteCheckResult)>;
 
-  using OnNotifySlowCheckCallback = base::RepeatingCallback<void()>;
-
   using GetDelegateCallback =
       base::RepeatingCallback<scoped_refptr<UrlCheckerDelegate>()>;
 
@@ -89,7 +82,6 @@
       int frame_tree_node_id,
       base::RepeatingCallback<content::WebContents*()> web_contents_getter,
       OnCompleteCheckCallback complete_callback,
-      OnNotifySlowCheckCallback slow_check_callback,
       bool url_real_time_lookup_enabled,
       bool can_urt_check_subresource_url,
       bool can_check_db,
@@ -128,10 +120,7 @@
       bool showed_interstitial,
       SafeBrowsingUrlCheckerImpl::PerformedCheck performed_check);
 
-  // |slow_check| indicates whether it reports the result of a slow check.
-  // (Please see comments of OnCheckUrlResult() for what slow check means).
   void OnCompleteCheck(
-      bool slow_check,
       bool proceed,
       bool showed_interstitial,
       SafeBrowsingUrlCheckerImpl::PerformedCheck performed_check);
@@ -146,7 +135,6 @@
       mechanism_experimenter_;
   base::RepeatingCallback<content::WebContents*()> web_contents_getter_;
   OnCompleteCheckCallback complete_callback_;
-  OnNotifySlowCheckCallback slow_check_callback_;
   bool url_real_time_lookup_enabled_ = false;
   bool can_urt_check_subresource_url_ = false;
   bool can_check_db_ = true;
diff --git a/components/safe_browsing/content/browser/web_api_handshake_checker.cc b/components/safe_browsing/content/browser/web_api_handshake_checker.cc
index f24940c..219867e3 100644
--- a/components/safe_browsing/content/browser/web_api_handshake_checker.cc
+++ b/components/safe_browsing/content/browser/web_api_handshake_checker.cc
@@ -81,7 +81,7 @@
         /*can_check_db=*/true, /*can_check_high_confidence_allowlist=*/true,
         /*url_lookup_service_metric_suffix=*/".None", last_committed_url_,
         content::GetUIThreadTaskRunner({}),
-        /*url_lookup_service=*/nullptr, WebUIInfoSingleton::GetInstance(),
+        /*url_lookup_service=*/nullptr,
         /*hash_realtime_service_on_ui=*/nullptr,
         /*mechanism_experimenter=*/nullptr,
         /*is_mechanism_experiment_allowed=*/false,
@@ -110,11 +110,10 @@
 
     *slow_check_notifier =
         base::BindOnce(&WebApiHandshakeChecker::CheckerOnSB::OnCompleteCheck,
-                       base::Unretained(this), /*slow_check=*/true);
+                       base::Unretained(this));
   }
 
   void OnCompleteCheck(
-      bool slow_check,
       bool proceed,
       bool showed_interstitial,
       SafeBrowsingUrlCheckerImpl::PerformedCheck performed_check) {
diff --git a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h
index 19a3351..68cd621 100644
--- a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h
+++ b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h
@@ -329,7 +329,7 @@
   ~SafeBrowsingUI() override;
 };
 
-class WebUIInfoSingleton : public UrlRealTimeMechanism::WebUIDelegate,
+class WebUIInfoSingleton : public RealTimeUrlLookupServiceBase::WebUIDelegate,
                            public PingManager::WebUIDelegate,
                            public HashRealTimeService::WebUIDelegate {
  public:
diff --git a/components/safe_browsing/content/renderer/renderer_url_loader_throttle.cc b/components/safe_browsing/content/renderer/renderer_url_loader_throttle.cc
index 35a7555..95495b3 100644
--- a/components/safe_browsing/content/renderer/renderer_url_loader_throttle.cc
+++ b/components/safe_browsing/content/renderer/renderer_url_loader_throttle.cc
@@ -251,7 +251,7 @@
 
 void RendererURLLoaderThrottle::OnCompleteCheck(bool proceed,
                                                 bool showed_interstitial) {
-  OnCompleteCheckInternal(true /* slow_check */, proceed, showed_interstitial);
+  OnCompleteCheckInternal(proceed, showed_interstitial);
 }
 
 void RendererURLLoaderThrottle::OnCheckUrlResult(
@@ -265,20 +265,10 @@
     return;
 
   if (!slow_check_notifier.is_valid()) {
-    OnCompleteCheckInternal(false /* slow_check */, proceed,
-                            showed_interstitial);
+    OnCompleteCheckInternal(proceed, showed_interstitial);
     return;
   }
 
-  pending_slow_checks_++;
-  // Pending slow checks indicate that the resource may be unsafe. In that case,
-  // pause reading response body from network to minimize the chance of
-  // processing unsafe contents (e.g., writing unsafe contents into cache),
-  // until we get the results. According to the results, we may resume reading
-  // or cancel the resource load.
-  if (pending_slow_checks_ == 1)
-    delegate_->PauseReadingBodyFromNet();
-
   if (!notifier_receivers_) {
     notifier_receivers_ =
         std::make_unique<mojo::ReceiverSet<mojom::UrlCheckNotifier>>();
@@ -287,7 +277,6 @@
 }
 
 void RendererURLLoaderThrottle::OnCompleteCheckInternal(
-    bool slow_check,
     bool proceed,
     bool showed_interstitial) {
   DCHECK(!blocked_);
@@ -296,11 +285,6 @@
   DCHECK_LT(0u, pending_checks_);
   pending_checks_--;
 
-  if (slow_check) {
-    DCHECK_LT(0u, pending_slow_checks_);
-    pending_slow_checks_--;
-  }
-
   // If the resource load is going to finish (either being cancelled or
   // resumed), record the total delay.
   if (!proceed || pending_checks_ == 0) {
@@ -314,9 +298,6 @@
   }
 
   if (proceed) {
-    if (pending_slow_checks_ == 0 && slow_check)
-      delegate_->ResumeReadingBodyFromNet();
-
     if (pending_checks_ == 0 && deferred_) {
       deferred_ = false;
       TRACE_EVENT_NESTABLE_ASYNC_END0("safe_browsing", "Deferred",
@@ -331,7 +312,6 @@
     url_checker_.reset();
     notifier_receivers_.reset();
     pending_checks_ = 0;
-    pending_slow_checks_ = 0;
     // If we didn't show an interstitial, we cancel with ERR_ABORTED to not show
     // an error page either.
     delegate_->CancelWithError(
@@ -349,11 +329,6 @@
 
   pending_checks_ = 0;
 
-  if (pending_slow_checks_ > 0) {
-    pending_slow_checks_ = 0;
-    delegate_->ResumeReadingBodyFromNet();
-  }
-
   if (deferred_) {
     total_delay_ = base::TimeTicks::Now() - defer_start_time_;
 
diff --git a/components/safe_browsing/content/renderer/renderer_url_loader_throttle.h b/components/safe_browsing/content/renderer/renderer_url_loader_throttle.h
index d6e5084..a57809a 100644
--- a/components/safe_browsing/content/renderer/renderer_url_loader_throttle.h
+++ b/components/safe_browsing/content/renderer/renderer_url_loader_throttle.h
@@ -101,11 +101,7 @@
       bool showed_interstitial);
 
   // Called by the two methods above.
-  // |slow_check| indicates whether it reports the result of a slow check.
-  // (Please see comments in safe_browsing.mojom for what slow check means).
-  void OnCompleteCheckInternal(bool slow_check,
-                               bool proceed,
-                               bool showed_interstitial);
+  void OnCompleteCheckInternal(bool proceed, bool showed_interstitial);
 
   void OnMojoDisconnect();
 
@@ -120,7 +116,6 @@
   mojo::Remote<mojom::SafeBrowsingUrlChecker> url_checker_;
 
   size_t pending_checks_ = 0;
-  size_t pending_slow_checks_ = 0;
   bool blocked_ = false;
 
   // The time when |WillStartRequest| is called.
diff --git a/components/safe_browsing/core/browser/db/BUILD.gn b/components/safe_browsing/core/browser/db/BUILD.gn
index 7536ab93..cde314c8 100644
--- a/components/safe_browsing/core/browser/db/BUILD.gn
+++ b/components/safe_browsing/core/browser/db/BUILD.gn
@@ -312,6 +312,7 @@
   sources = [
     "allowlist_checker_client_unittest.cc",
     "database_manager_unittest.cc",
+    "util_unittest.cc",
     "v4_get_hash_protocol_manager_unittest.cc",
     "v4_protocol_manager_util_unittest.cc",
   ]
diff --git a/components/safe_browsing/core/browser/db/util.cc b/components/safe_browsing/core/browser/db/util.cc
index 7d37161..c8fdb4f 100644
--- a/components/safe_browsing/core/browser/db/util.cc
+++ b/components/safe_browsing/core/browser/db/util.cc
@@ -48,7 +48,7 @@
   }
   value->EndDictionary();
 
-  value->SetString("popuplation_id", population_id);
+  value->SetString("population_id", population_id);
   return value;
 }
 
diff --git a/components/safe_browsing/core/browser/db/util_unittest.cc b/components/safe_browsing/core/browser/db/util_unittest.cc
new file mode 100644
index 0000000..87f7ceb
--- /dev/null
+++ b/components/safe_browsing/core/browser/db/util_unittest.cc
@@ -0,0 +1,55 @@
+// 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/safe_browsing/core/browser/db/util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace safe_browsing {
+namespace {
+
+TEST(ThreatMetadataTest, Equality) {
+  ThreatMetadata t1;
+  t1.threat_pattern_type = ThreatPatternType::MALWARE_DISTRIBUTION;
+  t1.api_permissions = {"API_ABUSE"};
+  t1.population_id = "population1";
+  ThreatMetadata t2;
+  t2.threat_pattern_type = ThreatPatternType::MALWARE_DISTRIBUTION;
+  t2.api_permissions = {"API_ABUSE"};
+  t2.population_id = "population1";
+  EXPECT_TRUE(t1 == t2);
+}
+
+TEST(ThreatMetadataTest, Inequality) {
+  ThreatMetadata t1;
+  t1.threat_pattern_type = ThreatPatternType::MALWARE_DISTRIBUTION;
+  t1.api_permissions = {"API_ABUSE"};
+  t1.population_id = "population1";
+  ThreatMetadata t2;
+  t2.threat_pattern_type = ThreatPatternType::MALWARE_DISTRIBUTION;
+  t2.api_permissions = {"API_ABUSE"};
+  t2.population_id = "population2";
+  EXPECT_TRUE(t1 != t2);
+}
+
+TEST(ThreatMetadataTest, ToTracedValue) {
+  ThreatMetadata t1;
+  t1.threat_pattern_type = ThreatPatternType::MALWARE_DISTRIBUTION;
+  t1.api_permissions = {"API_ABUSE"};
+  t1.subresource_filter_match = {
+      {SubresourceFilterType::ABUSIVE, SubresourceFilterLevel::ENFORCE}};
+  t1.population_id = "population1";
+  std::unique_ptr<base::trace_event::TracedValue> v1 = t1.ToTracedValue();
+  std::string json;
+  v1->AppendAsTraceFormat(&json);
+  EXPECT_EQ(
+      "{"
+      "\"threat_pattern_type\":2,"
+      "\"api_permissions\":[\"API_ABUSE\"],"
+      "\"subresource_filter_match\":{\"match_metadata\":[0,1]},"
+      "\"population_id\":\"population1\""
+      "}",
+      json);
+}
+
+}  // namespace
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/browser/realtime/url_lookup_service.cc b/components/safe_browsing/core/browser/realtime/url_lookup_service.cc
index f12ace4e..ee6617b 100644
--- a/components/safe_browsing/core/browser/realtime/url_lookup_service.cc
+++ b/components/safe_browsing/core/browser/realtime/url_lookup_service.cc
@@ -52,12 +52,14 @@
     const ClientConfiguredForTokenFetchesCallback& client_token_config_callback,
     bool is_off_the_record,
     variations::VariationsService* variations_service,
-    ReferrerChainProvider* referrer_chain_provider)
+    ReferrerChainProvider* referrer_chain_provider,
+    WebUIDelegate* delegate)
     : RealTimeUrlLookupServiceBase(url_loader_factory,
                                    cache_manager,
                                    get_user_population_callback,
                                    referrer_chain_provider,
-                                   pref_service),
+                                   pref_service,
+                                   delegate),
       pref_service_(pref_service),
       token_fetcher_(std::move(token_fetcher)),
       client_token_config_callback_(client_token_config_callback),
@@ -78,14 +80,12 @@
     const GURL& url,
     const GURL& last_committed_url,
     bool is_mainframe,
-    RTLookupRequestCallback request_callback,
     RTLookupResponseCallback response_callback,
     scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
   token_fetcher_->Start(base::BindOnce(
       &RealTimeUrlLookupService::OnGetAccessToken, weak_factory_.GetWeakPtr(),
-      url, last_committed_url, is_mainframe, std::move(request_callback),
-      std::move(response_callback), std::move(callback_task_runner),
-      base::TimeTicks::Now()));
+      url, last_committed_url, is_mainframe, std::move(response_callback),
+      std::move(callback_task_runner), base::TimeTicks::Now()));
 }
 
 void RealTimeUrlLookupService::OnPrefChanged() {
@@ -98,7 +98,6 @@
     const GURL& url,
     const GURL& last_committed_url,
     bool is_mainframe,
-    RTLookupRequestCallback request_callback,
     RTLookupResponseCallback response_callback,
     scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
     base::TimeTicks get_token_start_time,
@@ -111,8 +110,8 @@
   base::UmaHistogramBoolean("SafeBrowsing.RT.HasTokenFromFetcher",
                             !access_token.empty());
   SendRequest(url, last_committed_url, is_mainframe, access_token,
-              std::move(request_callback), std::move(response_callback),
-              std::move(callback_task_runner), /* is_sampled_report */ false);
+              std::move(response_callback), std::move(callback_task_runner),
+              /* is_sampled_report */ false);
 }
 
 void RealTimeUrlLookupService::OnResponseUnauthorized(
diff --git a/components/safe_browsing/core/browser/realtime/url_lookup_service.h b/components/safe_browsing/core/browser/realtime/url_lookup_service.h
index ddf0864..c680f48a 100644
--- a/components/safe_browsing/core/browser/realtime/url_lookup_service.h
+++ b/components/safe_browsing/core/browser/realtime/url_lookup_service.h
@@ -66,7 +66,8 @@
           client_token_config_callback,
       bool is_off_the_record,
       variations::VariationsService* variations_service,
-      ReferrerChainProvider* referrer_chain_provider);
+      ReferrerChainProvider* referrer_chain_provider,
+      WebUIDelegate* delegate);
 
   RealTimeUrlLookupService(const RealTimeUrlLookupService&) = delete;
   RealTimeUrlLookupService& operator=(const RealTimeUrlLookupService&) = delete;
@@ -101,7 +102,6 @@
       const GURL& url,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       RTLookupResponseCallback response_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override;
   absl::optional<std::string> GetDMTokenString() const override;
@@ -122,7 +122,6 @@
       const GURL& url,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       RTLookupResponseCallback response_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
       base::TimeTicks get_token_start_time,
diff --git a/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc b/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc
index 43cbf2b4..9b356c50 100644
--- a/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc
+++ b/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc
@@ -128,7 +128,8 @@
     base::RepeatingCallback<ChromeUserPopulation()>
         get_user_population_callback,
     ReferrerChainProvider* referrer_chain_provider,
-    PrefService* pref_service)
+    PrefService* pref_service,
+    WebUIDelegate* delegate)
     : url_loader_factory_(url_loader_factory),
       cache_manager_(cache_manager),
       pref_service_(pref_service),
@@ -139,7 +140,8 @@
           /*min_backoff_reset_duration_in_seconds=*/
           kMinBackOffResetDurationInSeconds,
           /*max_backoff_reset_duration_in_seconds=*/
-          kMaxBackOffResetDurationInSeconds)) {}
+          kMaxBackOffResetDurationInSeconds)),
+      webui_delegate_(delegate) {}
 
 RealTimeUrlLookupServiceBase::~RealTimeUrlLookupServiceBase() = default;
 
@@ -296,14 +298,12 @@
     const GURL& url,
     const GURL& last_committed_url,
     bool is_mainframe,
-    RTLookupRequestCallback request_callback,
     scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(url.is_valid());
 
   SendRequest(url, last_committed_url, is_mainframe,
               /* access_token_string */ std::string(),
-              std::move(request_callback),
               /* response_callback */ base::NullCallback(),
               std::move(callback_task_runner), /* is_sampled_report */ true);
 }
@@ -312,7 +312,6 @@
     const GURL& url,
     const GURL& last_committed_url,
     bool is_mainframe,
-    RTLookupRequestCallback request_callback,
     RTLookupResponseCallback response_callback,
     scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -332,13 +331,13 @@
 
   if (CanPerformFullURLLookupWithToken()) {
     GetAccessToken(url, last_committed_url, is_mainframe,
-                   std::move(request_callback), std::move(response_callback),
+                   std::move(response_callback),
                    std::move(callback_task_runner));
   } else {
     SendRequest(url, last_committed_url, is_mainframe,
                 /* access_token_string */ std::string(),
-                std::move(request_callback), std::move(response_callback),
-                std::move(callback_task_runner), /* is_sampled_report */ false);
+                std::move(response_callback), std::move(callback_task_runner),
+                /* is_sampled_report */ false);
   }
 }
 
@@ -347,7 +346,6 @@
     const GURL& last_committed_url,
     bool is_mainframe,
     const std::string& access_token_string,
-    RTLookupRequestCallback request_callback,
     RTLookupResponseCallback response_callback,
     scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
     bool is_sampled_report) {
@@ -377,17 +375,15 @@
                                     !access_token_string.empty());
 
   MaybeLogLastProtegoPingTimeToPrefs(!access_token_string.empty());
+  absl::optional<int> webui_token =
+      LogLookupRequest(*request, access_token_string);
 
   // NOTE: Pass |callback_task_runner| by copying it here as it's also needed
   // just below.
   SendRequestInternal(
       std::move(resource_request), req_data, access_token_string,
       std::move(response_callback), callback_task_runner,
-      request->population().user_population(), is_sampled_report);
-
-  callback_task_runner->PostTask(
-      FROM_HERE, base::BindOnce(std::move(request_callback), std::move(request),
-                                access_token_string));
+      request->population().user_population(), is_sampled_report, webui_token);
 }
 
 void RealTimeUrlLookupServiceBase::SendRequestInternal(
@@ -397,7 +393,8 @@
     RTLookupResponseCallback response_callback,
     scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
     ChromeUserPopulation::UserPopulation user_population,
-    bool is_sampled_report) {
+    bool is_sampled_report,
+    absl::optional<int> webui_token) {
   std::unique_ptr<network::SimpleURLLoader> owned_loader =
       network::SimpleURLLoader::Create(std::move(resource_request),
                                        GetTrafficAnnotationTag());
@@ -416,7 +413,7 @@
       base::BindOnce(&RealTimeUrlLookupServiceBase::OnURLLoaderComplete,
                      GetWeakPtr(), access_token_string, loader, user_population,
                      start_time, is_sampled_report,
-                     std::move(callback_task_runner)));
+                     std::move(callback_task_runner), webui_token));
 
   pending_requests_[owned_loader.release()] = std::move(response_callback);
 }
@@ -428,6 +425,7 @@
     base::TimeTicks request_start_time,
     bool is_sampled_report,
     scoped_refptr<base::SequencedTaskRunner> response_callback_task_runner,
+    absl::optional<int> webui_token,
     std::unique_ptr<std::string> response_body) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK(first_request_start_time_);
@@ -485,6 +483,10 @@
 
   MayBeCacheRealTimeUrlVerdict(*response);
 
+  if (is_rt_lookup_successful) {
+    LogLookupResponseForToken(webui_token, *response);
+  }
+
   RecordCount100WithAndWithoutSuffix("SafeBrowsing.RT.ThreatInfoSize",
                                      GetMetricSuffix(),
                                      response->threat_info_size());
@@ -587,6 +589,30 @@
   return request;
 }
 
+absl::optional<int> RealTimeUrlLookupServiceBase::LogLookupRequest(
+    const RTLookupRequest& request,
+    const std::string& oauth_token) {
+  if (!webui_delegate_) {
+    return absl::nullopt;
+  }
+
+  return webui_delegate_->AddToURTLookupPings(request, oauth_token);
+}
+
+void RealTimeUrlLookupServiceBase::LogLookupResponseForToken(
+    absl::optional<int> token,
+    const RTLookupResponse& response) {
+  if (!webui_delegate_) {
+    return;
+  }
+
+  if (!token.has_value()) {
+    return;
+  }
+
+  webui_delegate_->AddToURTLookupResponses(token.value(), response);
+}
+
 void RealTimeUrlLookupServiceBase::OnResponseUnauthorized(
     const std::string& invalid_access_token) {}
 
diff --git a/components/safe_browsing/core/browser/realtime/url_lookup_service_base.h b/components/safe_browsing/core/browser/realtime/url_lookup_service_base.h
index e87ad68..e6bde0f 100644
--- a/components/safe_browsing/core/browser/realtime/url_lookup_service_base.h
+++ b/components/safe_browsing/core/browser/realtime/url_lookup_service_base.h
@@ -41,9 +41,6 @@
 // Suffix for metrics when there is no URL lookup service.
 constexpr char kNoRealTimeURLLookupService[] = ".None";
 
-using RTLookupRequestCallback =
-    base::OnceCallback<void(std::unique_ptr<RTLookupRequest>, std::string)>;
-
 using RTLookupResponseCallback =
     base::OnceCallback<void(bool, bool, std::unique_ptr<RTLookupResponse>)>;
 
@@ -57,13 +54,31 @@
 // lookup feature.
 class RealTimeUrlLookupServiceBase : public KeyedService {
  public:
+  // Interface via which a client of this class can surface relevant events in
+  // WebUI. All methods must be called on the UI thread.
+  class WebUIDelegate {
+   public:
+    virtual ~WebUIDelegate() = default;
+
+    // Adds the new ping to the set of URT lookup pings. Returns a token that
+    // can be used in |AddToURTLookupResponses| to correlate a ping and
+    // response.
+    virtual int AddToURTLookupPings(const RTLookupRequest request,
+                                    const std::string oauth_token) = 0;
+
+    // Adds the new response to the set of URT lookup pings.
+    virtual void AddToURTLookupResponses(int webui_token,
+                                         const RTLookupResponse response) = 0;
+  };
+
   explicit RealTimeUrlLookupServiceBase(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       VerdictCacheManager* cache_manager,
       base::RepeatingCallback<ChromeUserPopulation()>
           get_user_population_callback,
       ReferrerChainProvider* referrer_chain_provider,
-      PrefService* pref_service);
+      PrefService* pref_service,
+      WebUIDelegate* webui_delegate);
 
   RealTimeUrlLookupServiceBase(const RealTimeUrlLookupServiceBase&) = delete;
   RealTimeUrlLookupServiceBase& operator=(const RealTimeUrlLookupServiceBase&) =
@@ -86,19 +101,15 @@
   // local hash-based method.
   bool IsInBackoffMode() const;
 
-  // Start the full URL lookup for |url|, call |request_callback| on
-  // |callback_task_runner| when request is sent, call |response_callback| on
-  // |callback_task_runner| when response is received.
-  // Note that |request_callback| is not called if there's a valid entry in the
-  // cache for |url|.
-  // |last_committed_url| and |is_mainframe| are for obtaining page load token
-  // for the request.
+  // Start the full URL lookup for |url| and call |response_callback|
+  // on |callback_task_runner| when response is received.
+  // |last_committed_url| and |is_mainframe| are for obtaining page
+  // load token for the request.
   // This function is overridden in unit tests.
   virtual void StartLookup(
       const GURL& url,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       RTLookupResponseCallback response_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
 
@@ -108,7 +119,6 @@
       const GURL& url,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
 
   // Helper function to return a weak pointer.
@@ -154,7 +164,6 @@
       const GURL& last_committed_url,
       bool is_mainframe,
       const std::string& access_token_string,
-      RTLookupRequestCallback request_callback,
       RTLookupResponseCallback response_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
       bool is_sampled_report);
@@ -192,7 +201,6 @@
       const GURL& url,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       RTLookupResponseCallback response_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner) = 0;
 
@@ -245,7 +253,8 @@
       RTLookupResponseCallback response_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
       ChromeUserPopulation::UserPopulation user_population,
-      bool is_sampled_report);
+      bool is_sampled_report,
+      absl::optional<int> webui_token);
 
   // Called when the response from the real-time lookup remote endpoint is
   // received. |url_loader| is the unowned loader that was used to send the
@@ -260,6 +269,7 @@
       base::TimeTicks request_start_time,
       bool is_sampled_report,
       scoped_refptr<base::SequencedTaskRunner> response_callback_task_runner,
+      absl::optional<int> webui_token,
       std::unique_ptr<std::string> response_body);
 
   // Fills in fields in |RTLookupRequest|.
@@ -269,6 +279,17 @@
       bool is_mainframe,
       bool is_sampled_report);
 
+  // Logs |request| and |oauth_token| on any open
+  // chrome://safe-browsing pages. Returns a token that can be passed
+  // to `LogLookupResponseForToken` to associate a request and
+  // response.
+  absl::optional<int> LogLookupRequest(const RTLookupRequest& request,
+                                       const std::string& oauth_token);
+
+  // Logs |response| on any open chrome://safe-browsing pages.
+  void LogLookupResponseForToken(absl::optional<int> token,
+                                 const RTLookupResponse& response);
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   // The URLLoaderFactory we use to issue network requests.
@@ -296,6 +317,11 @@
   // metrics.
   std::optional<base::TimeTicks> first_request_start_time_ = std::nullopt;
 
+  // May be null on certain platforms that don't support chrome://safe-browsing
+  // and in unit tests. If non-null, guaranteed to outlive this object by
+  // contract.
+  raw_ptr<WebUIDelegate> webui_delegate_ = nullptr;
+
   friend class RealTimeUrlLookupServiceTest;
   friend class ChromeEnterpriseRealTimeUrlLookupServiceTest;
 
diff --git a/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc b/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
index 624f7c4f..40988cc 100644
--- a/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
+++ b/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
@@ -68,6 +68,47 @@
                                  ReferrerChain* out_referrer_chain));
 };
 
+bool GetRequestProto(const network::ResourceRequest& request,
+                     RTLookupRequest* request_proto) {
+  if (!request.request_body || !request.request_body->elements()) {
+    return false;
+  }
+
+  // Supporting one DataElementBytes is sufficient here. If request
+  // protos grow to need data pipes, we would need further test code
+  // to read the contents of the pipe.
+  const std::vector<network::DataElement>* elements =
+      request.request_body->elements();
+  if (elements->size() != 1 ||
+      elements->at(0).type() !=
+          network::mojom::DataElementDataView::Tag::kBytes) {
+    return false;
+  }
+  return request_proto->ParseFromString(std::string(
+      elements->at(0).As<network::DataElementBytes>().AsStringPiece()));
+}
+
+class MustRunInterceptor {
+ public:
+  MustRunInterceptor(network::TestURLLoaderFactory::Interceptor interceptor)
+      : interceptor_(interceptor), has_run_(false) {}
+  ~MustRunInterceptor() { EXPECT_TRUE(has_run_); }
+
+  void Run(const network::ResourceRequest& resource_request) {
+    has_run_ = true;
+    interceptor_.Run(resource_request);
+  }
+
+  network::TestURLLoaderFactory::Interceptor GetCallback() {
+    return base::BindRepeating(&MustRunInterceptor::Run,
+                               base::Unretained(this));
+  }
+
+ private:
+  network::TestURLLoaderFactory::Interceptor interceptor_;
+  bool has_run_;
+};
+
 }  // namespace
 
 class RealTimeUrlLookupServiceTest : public PlatformTest {
@@ -117,7 +158,8 @@
             &RealTimeUrlLookupServiceTest::AreTokenFetchesConfiguredInClient,
             base::Unretained(this)),
         /*is_off_the_record=*/false, /*variations_service=*/nullptr,
-        referrer_chain_provider_.get());
+        referrer_chain_provider_.get(),
+        /*webui_delegate=*/nullptr);
   }
 
   void TearDown() override {
@@ -462,14 +504,16 @@
                                RTLookupResponse::ThreatInfo::COVERING_MATCH);
   task_environment_.RunUntilIdle();
 
-  base::MockCallback<RTLookupRequestCallback> request_callback;
+  base::MockCallback<network::TestURLLoaderFactory::Interceptor>
+      request_callback;
+  test_url_loader_factory_.SetInterceptor(request_callback.Get());
+  EXPECT_CALL(request_callback, Run(_)).Times(0);
+
   base::MockCallback<RTLookupResponseCallback> response_callback;
   rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
-                            request_callback.Get(), response_callback.Get(),
+                            response_callback.Get(),
                             base::SequencedTaskRunner::GetCurrentDefault());
 
-  // |request_callback| should not be called.
-  EXPECT_CALL(request_callback, Run(_, _)).Times(0);
   EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ true,
                                      /* is_cached_response */ true, _));
 
@@ -495,7 +539,7 @@
                         RTLookupResponse::ThreatInfo::COVERING_MATCH);
 
   rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
-                            base::DoNothing(), base::DoNothing(),
+                            base::DoNothing(),
                             base::SequencedTaskRunner::GetCurrentDefault());
 
   FulfillAccessTokenRequest("access_token_string");
@@ -520,7 +564,7 @@
                         RTLookupResponse::ThreatInfo::COVERING_MATCH);
 
   rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
-                            base::DoNothing(), base::DoNothing(),
+                            base::DoNothing(),
                             base::SequencedTaskRunner::GetCurrentDefault());
 
   task_environment_.RunUntilIdle();
@@ -545,7 +589,7 @@
   task_environment_.RunUntilIdle();
 
   rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
-                            base::DoNothing(), base::DoNothing(),
+                            base::DoNothing(),
                             base::SequencedTaskRunner::GetCurrentDefault());
 
   task_environment_.RunUntilIdle();
@@ -571,7 +615,7 @@
   task_environment_.RunUntilIdle();
 
   rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
-                            base::DoNothing(), base::DoNothing(),
+                            base::DoNothing(),
                             base::SequencedTaskRunner::GetCurrentDefault());
 
   task_environment_.RunUntilIdle();
@@ -595,7 +639,7 @@
                         RTLookupResponse::ThreatInfo::COVERING_MATCH);
 
   rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
-                            base::DoNothing(), base::DoNothing(),
+                            base::DoNothing(),
                             base::SequencedTaskRunner::GetCurrentDefault());
 
   FulfillAccessTokenRequest("access_token_string");
@@ -620,7 +664,7 @@
                         RTLookupResponse::ThreatInfo::COVERING_MATCH);
 
   rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
-                            base::DoNothing(), base::DoNothing(),
+                            base::DoNothing(),
                             base::SequencedTaskRunner::GetCurrentDefault());
 
   FulfillAccessTokenRequest("access_token_string");
@@ -643,21 +687,15 @@
                         RTLookupResponse::ThreatInfo::COVERING_MATCH);
 
   base::MockCallback<RTLookupResponseCallback> response_callback;
-  rt_service()->StartLookup(
-      url, last_committed_url_, is_mainframe_,
-      base::BindOnce(
-          [](std::unique_ptr<RTLookupRequest> request, std::string token) {
-            EXPECT_FALSE(request->has_dm_token());
-            // Check token is attached.
-            EXPECT_EQ("access_token_string", token);
-          }),
-      response_callback.Get(), base::SequencedTaskRunner::GetCurrentDefault());
-
   EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ true,
                                      /* is_cached_response */ false, _));
 
-  test_url_loader_factory_.SetInterceptor(
+  MustRunInterceptor interceptor(
       base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        RTLookupRequest request_proto;
+        ASSERT_TRUE(GetRequestProto(request, &request_proto));
+        EXPECT_FALSE(request_proto.has_dm_token());
+
         // Cookies should be removed when token is set.
         EXPECT_EQ(request.credentials_mode,
                   network::mojom::CredentialsMode::kOmit);
@@ -667,6 +705,11 @@
         EXPECT_TRUE(found_header);
         EXPECT_EQ(header_value, "Bearer access_token_string");
       }));
+  test_url_loader_factory_.SetInterceptor(interceptor.GetCallback());
+
+  rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
+                            response_callback.Get(),
+                            base::SequencedTaskRunner::GetCurrentDefault());
 
   EXPECT_TRUE(raw_token_fetcher()->WasStartCalled());
   FulfillAccessTokenRequest("access_token_string");
@@ -695,26 +738,23 @@
                         RTLookupResponse::ThreatInfo::COVERING_MATCH);
 
   base::MockCallback<RTLookupResponseCallback> response_callback;
-  rt_service()->StartLookup(
-      url, last_committed_url_, is_mainframe_,
-      base::BindOnce(
-          [](std::unique_ptr<RTLookupRequest> request, std::string token) {
-            EXPECT_FALSE(request->has_dm_token());
-            // Check token is not attached.
-            EXPECT_EQ("", token);
-          }),
-      response_callback.Get(), base::SequencedTaskRunner::GetCurrentDefault());
-
   EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ true,
                                      /* is_cached_response */ false, _));
 
-  test_url_loader_factory_.SetInterceptor(
+  MustRunInterceptor interceptor(
       base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
-        std::string header_value;
-        bool found_header = request.headers.GetHeader(
-            net::HttpRequestHeaders::kAuthorization, &header_value);
-        EXPECT_FALSE(found_header);
+        RTLookupRequest request_proto;
+        ASSERT_TRUE(GetRequestProto(request, &request_proto));
+        EXPECT_FALSE(request_proto.has_dm_token());
+
+        EXPECT_FALSE(
+            request.headers.HasHeader(net::HttpRequestHeaders::kAuthorization));
       }));
+  test_url_loader_factory_.SetInterceptor(interceptor.GetCallback());
+
+  rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
+                            response_callback.Get(),
+                            base::SequencedTaskRunner::GetCurrentDefault());
 
   EXPECT_TRUE(raw_token_fetcher()->WasStartCalled());
   // Token fetcher returns empty string when the token is unavailable.
@@ -736,25 +776,22 @@
                         RTLookupResponse::ThreatInfo::COVERING_MATCH);
 
   base::MockCallback<RTLookupResponseCallback> response_callback;
-  rt_service()->StartLookup(
-      url, last_committed_url_, is_mainframe_,
-      base::BindOnce(
-          [](std::unique_ptr<RTLookupRequest> request, std::string token) {
-            // Check the token field is empty as the passed-in client callback
-            // indicates that token fetches are not configured in the client.
-            EXPECT_EQ("", token);
-          }),
-      response_callback.Get(), base::SequencedTaskRunner::GetCurrentDefault());
-
   EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ true,
                                      /* is_cached_response */ false, _));
-
-  test_url_loader_factory_.SetInterceptor(
+  MustRunInterceptor interceptor(
       base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
         // Cookies should be attached when token is empty.
         EXPECT_EQ(request.credentials_mode,
                   network::mojom::CredentialsMode::kInclude);
+
+        EXPECT_FALSE(
+            request.headers.HasHeader(net::HttpRequestHeaders::kAuthorization));
       }));
+  test_url_loader_factory_.SetInterceptor(interceptor.GetCallback());
+
+  rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
+                            response_callback.Get(),
+                            base::SequencedTaskRunner::GetCurrentDefault());
 
   task_environment_.RunUntilIdle();
 
@@ -771,16 +808,18 @@
   GURL url(kTestUrl);
   SetUpFailureResponse(net::HTTP_UNAUTHORIZED);
 
-  base::MockCallback<RTLookupRequestCallback> request_callback;
+  base::MockCallback<network::TestURLLoaderFactory::Interceptor>
+      request_callback;
   base::MockCallback<RTLookupResponseCallback> response_callback;
-  rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
-                            request_callback.Get(), response_callback.Get(),
-                            base::SequencedTaskRunner::GetCurrentDefault());
-
-  EXPECT_CALL(request_callback, Run(_, _)).Times(1);
+  test_url_loader_factory_.SetInterceptor(request_callback.Get());
+  EXPECT_CALL(request_callback, Run(_)).Times(1);
   EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ false,
                                      /* is_cached_response */ false, _));
 
+  rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
+                            response_callback.Get(),
+                            base::SequencedTaskRunner::GetCurrentDefault());
+
   FulfillAccessTokenRequest("invalid_token_string");
   EXPECT_CALL(*raw_token_fetcher(),
               OnInvalidAccessToken("invalid_token_string"))
@@ -795,15 +834,14 @@
   GURL url(kTestUrl);
   SetUpFailureResponse(net::HTTP_FORBIDDEN);
 
-  base::MockCallback<RTLookupRequestCallback> request_callback;
   base::MockCallback<RTLookupResponseCallback> response_callback;
-  rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
-                            request_callback.Get(), response_callback.Get(),
-                            base::SequencedTaskRunner::GetCurrentDefault());
-
   EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ false,
                                      /* is_cached_response */ false, _));
 
+  rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
+                            response_callback.Get(),
+                            base::SequencedTaskRunner::GetCurrentDefault());
+
   FulfillAccessTokenRequest("invalid_token_string");
   EXPECT_CALL(*raw_token_fetcher(), OnInvalidAccessToken(_)).Times(0);
   task_environment_.RunUntilIdle();
@@ -840,17 +878,23 @@
   base::MockCallback<RTLookupResponseCallback> response_callback;
   EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ true,
                                      /* is_cached_response */ false, _));
-  rt_service()->StartLookup(
-      url, last_committed_url_, is_mainframe_,
-      base::BindOnce(
-          [](std::unique_ptr<RTLookupRequest> request, std::string token) {
-            EXPECT_EQ(3, request->version());
-            // Check referrer chain is attached.
-            EXPECT_EQ(2, request->referrer_chain().size());
-            EXPECT_EQ(kTestUrl, request->referrer_chain().Get(0).url());
-            EXPECT_EQ(kTestReferrerUrl, request->referrer_chain().Get(1).url());
-          }),
-      response_callback.Get(), base::SequencedTaskRunner::GetCurrentDefault());
+  MustRunInterceptor interceptor(
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        RTLookupRequest request_proto;
+        ASSERT_TRUE(GetRequestProto(request, &request_proto));
+
+        EXPECT_EQ(3, request_proto.version());
+        // Check referrer chain is attached.
+        EXPECT_EQ(2, request_proto.referrer_chain().size());
+        EXPECT_EQ(kTestUrl, request_proto.referrer_chain().Get(0).url());
+        EXPECT_EQ(kTestReferrerUrl,
+                  request_proto.referrer_chain().Get(1).url());
+      }));
+  test_url_loader_factory_.SetInterceptor(interceptor.GetCallback());
+
+  rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
+                            response_callback.Get(),
+                            base::SequencedTaskRunner::GetCurrentDefault());
 
   task_environment_.RunUntilIdle();
 }
@@ -884,38 +928,47 @@
                       Return(ReferrerChainProvider::SUCCESS)));
 
   base::MockCallback<RTLookupResponseCallback> response_callback;
-  rt_service()->StartLookup(
-      url, last_committed_url_, is_mainframe_,
-      base::BindOnce([](std::unique_ptr<RTLookupRequest> request,
-                        std::string token) {
-        EXPECT_EQ(3, request->version());
-        EXPECT_EQ(2, request->referrer_chain().size());
+  MustRunInterceptor interceptor(
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        RTLookupRequest request_proto;
+        ASSERT_TRUE(GetRequestProto(request, &request_proto));
+
+        EXPECT_EQ(3, request_proto.version());
+        EXPECT_EQ(2, request_proto.referrer_chain().size());
         // The first entry is sanitized because it is triggered in a
         // subframe.
-        EXPECT_EQ(kTestUrl, request->referrer_chain().Get(0).url());
-        EXPECT_FALSE(request->referrer_chain().Get(0).has_main_frame_url());
-        EXPECT_TRUE(request->referrer_chain().Get(0).is_subframe_url_removed());
-        EXPECT_EQ(kTestReferrerUrl,
-                  request->referrer_chain().Get(0).referrer_url());
+        EXPECT_EQ(kTestUrl, request_proto.referrer_chain().Get(0).url());
         EXPECT_FALSE(
-            request->referrer_chain().Get(0).has_referrer_main_frame_url());
-        EXPECT_TRUE(request->referrer_chain()
+            request_proto.referrer_chain().Get(0).has_main_frame_url());
+        EXPECT_TRUE(
+            request_proto.referrer_chain().Get(0).is_subframe_url_removed());
+        EXPECT_EQ(kTestReferrerUrl,
+                  request_proto.referrer_chain().Get(0).referrer_url());
+        EXPECT_FALSE(request_proto.referrer_chain()
+                         .Get(0)
+                         .has_referrer_main_frame_url());
+        EXPECT_TRUE(request_proto.referrer_chain()
                         .Get(0)
                         .is_subframe_referrer_url_removed());
         // The second entry is not sanitized because it is triggered in a
         // mainframe.
-        EXPECT_EQ(kTestReferrerUrl, request->referrer_chain().Get(1).url());
+        EXPECT_EQ(kTestReferrerUrl,
+                  request_proto.referrer_chain().Get(1).url());
         EXPECT_FALSE(
-            request->referrer_chain().Get(1).is_subframe_url_removed());
-        EXPECT_FALSE(request->referrer_chain()
+            request_proto.referrer_chain().Get(1).is_subframe_url_removed());
+        EXPECT_FALSE(request_proto.referrer_chain()
                          .Get(1)
                          .is_subframe_referrer_url_removed());
-      }),
-      response_callback.Get(), base::SequencedTaskRunner::GetCurrentDefault());
+      }));
+  test_url_loader_factory_.SetInterceptor(interceptor.GetCallback());
 
   EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ true,
                                      /* is_cached_response */ false, _));
 
+  rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
+                            response_callback.Get(),
+                            base::SequencedTaskRunner::GetCurrentDefault());
+
   task_environment_.RunUntilIdle();
 }
 
@@ -951,34 +1004,41 @@
                       Return(ReferrerChainProvider::SUCCESS)));
 
   base::MockCallback<RTLookupResponseCallback> response_callback;
-  rt_service()->StartLookup(
-      url, last_committed_url_, is_mainframe_,
-      base::BindOnce([](std::unique_ptr<RTLookupRequest> request,
-                        std::string token) {
-        EXPECT_EQ(3, request->version());
-        EXPECT_EQ(2, request->referrer_chain().size());
+  MustRunInterceptor interceptor(
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        RTLookupRequest request_proto;
+        ASSERT_TRUE(GetRequestProto(request, &request_proto));
+        EXPECT_EQ(3, request_proto.version());
+        EXPECT_EQ(2, request_proto.referrer_chain().size());
         // Check referrer chain is not sanitized.
-        EXPECT_EQ(kTestSubframeUrl, request->referrer_chain().Get(0).url());
-        EXPECT_EQ(kTestUrl, request->referrer_chain().Get(0).main_frame_url());
+        EXPECT_EQ(kTestSubframeUrl,
+                  request_proto.referrer_chain().Get(0).url());
+        EXPECT_EQ(kTestUrl,
+                  request_proto.referrer_chain().Get(0).main_frame_url());
         EXPECT_FALSE(
-            request->referrer_chain().Get(0).is_subframe_url_removed());
+            request_proto.referrer_chain().Get(0).is_subframe_url_removed());
         EXPECT_EQ(kTestSubframeReferrerUrl,
-                  request->referrer_chain().Get(0).referrer_url());
-        EXPECT_EQ(kTestReferrerUrl,
-                  request->referrer_chain().Get(0).referrer_main_frame_url());
-        EXPECT_FALSE(request->referrer_chain()
+                  request_proto.referrer_chain().Get(0).referrer_url());
+        EXPECT_EQ(
+            kTestReferrerUrl,
+            request_proto.referrer_chain().Get(0).referrer_main_frame_url());
+        EXPECT_FALSE(request_proto.referrer_chain()
                          .Get(0)
                          .is_subframe_referrer_url_removed());
         EXPECT_EQ(kTestSubframeReferrerUrl,
-                  request->referrer_chain().Get(1).url());
+                  request_proto.referrer_chain().Get(1).url());
         EXPECT_FALSE(
-            request->referrer_chain().Get(1).is_subframe_url_removed());
-      }),
-      response_callback.Get(), base::SequencedTaskRunner::GetCurrentDefault());
+            request_proto.referrer_chain().Get(1).is_subframe_url_removed());
+      }));
+  test_url_loader_factory_.SetInterceptor(interceptor.GetCallback());
 
   EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ true,
                                      /* is_cached_response */ false, _));
 
+  rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
+                            response_callback.Get(),
+                            base::SequencedTaskRunner::GetCurrentDefault());
+
   task_environment_.RunUntilIdle();
 }
 
@@ -1014,38 +1074,46 @@
                       Return(ReferrerChainProvider::SUCCESS)));
 
   base::MockCallback<RTLookupResponseCallback> response_callback;
-  rt_service()->StartLookup(
-      url, last_committed_url_, is_mainframe_,
-      base::BindOnce([](std::unique_ptr<RTLookupRequest> request,
-                        std::string token) {
-        EXPECT_EQ(3, request->version());
-        EXPECT_EQ(2, request->referrer_chain().size());
+  EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ true,
+                                     /* is_cached_response */ false, _));
+
+  MustRunInterceptor interceptor(
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        RTLookupRequest request_proto;
+        ASSERT_TRUE(GetRequestProto(request, &request_proto));
+
+        EXPECT_EQ(3, request_proto.version());
+        EXPECT_EQ(2, request_proto.referrer_chain().size());
         // Check the first referrer chain is sanitized because it's logged
         // before real time URL lookup is enabled.
-        EXPECT_EQ("", request->referrer_chain().Get(0).url());
-        EXPECT_EQ("", request->referrer_chain().Get(0).main_frame_url());
+        EXPECT_EQ("", request_proto.referrer_chain().Get(0).url());
+        EXPECT_EQ("", request_proto.referrer_chain().Get(0).main_frame_url());
         EXPECT_FALSE(
-            request->referrer_chain().Get(0).is_subframe_url_removed());
-        EXPECT_EQ("", request->referrer_chain().Get(0).referrer_url());
-        EXPECT_EQ("",
-                  request->referrer_chain().Get(0).referrer_main_frame_url());
-        EXPECT_FALSE(request->referrer_chain()
+            request_proto.referrer_chain().Get(0).is_subframe_url_removed());
+        EXPECT_EQ("", request_proto.referrer_chain().Get(0).referrer_url());
+        EXPECT_EQ(
+            "",
+            request_proto.referrer_chain().Get(0).referrer_main_frame_url());
+        EXPECT_FALSE(request_proto.referrer_chain()
                          .Get(0)
                          .is_subframe_referrer_url_removed());
         EXPECT_TRUE(
-            request->referrer_chain().Get(0).is_url_removed_by_policy());
+            request_proto.referrer_chain().Get(0).is_url_removed_by_policy());
 
         // The second referrer chain should be sanitized based on the user
         // consent.
-        EXPECT_EQ(kTestReferrerUrl, request->referrer_chain().Get(1).url());
-        EXPECT_TRUE(request->referrer_chain().Get(1).is_subframe_url_removed());
+        EXPECT_EQ(kTestReferrerUrl,
+                  request_proto.referrer_chain().Get(1).url());
+        EXPECT_TRUE(
+            request_proto.referrer_chain().Get(1).is_subframe_url_removed());
         EXPECT_FALSE(
-            request->referrer_chain().Get(1).is_url_removed_by_policy());
-      }),
-      response_callback.Get(), base::SequencedTaskRunner::GetCurrentDefault());
+            request_proto.referrer_chain().Get(1).is_url_removed_by_policy());
+      }));
+  test_url_loader_factory_.SetInterceptor(interceptor.GetCallback());
 
-  EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ true,
-                                     /* is_cached_response */ false, _));
+  rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
+                            response_callback.Get(),
+                            base::SequencedTaskRunner::GetCurrentDefault());
 
   task_environment_.RunUntilIdle();
 }
@@ -1054,14 +1122,19 @@
   EnableMbb();
   GURL url(kTestUrl);
 
-  base::MockCallback<RTLookupRequestCallback> request_callback;
+  base::MockCallback<network::TestURLLoaderFactory::Interceptor>
+      request_callback;
+  test_url_loader_factory_.SetInterceptor(request_callback.Get());
+  EXPECT_CALL(request_callback, Run(_)).Times(1);
+
   base::MockCallback<RTLookupResponseCallback> response_callback;
+
+  EXPECT_CALL(response_callback, Run(_, _, _)).Times(0);
+
   rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
-                            request_callback.Get(), response_callback.Get(),
+                            response_callback.Get(),
                             base::SequencedTaskRunner::GetCurrentDefault());
 
-  EXPECT_CALL(request_callback, Run(_, _)).Times(1);
-  EXPECT_CALL(response_callback, Run(_, _, _)).Times(0);
   rt_service()->Shutdown();
 
   task_environment_.RunUntilIdle();
@@ -1091,16 +1164,19 @@
   EnableTokenFetchesInClient();
   GURL url(kTestUrl);
 
-  testing::StrictMock<base::MockCallback<RTLookupRequestCallback>>
+  base::MockCallback<network::TestURLLoaderFactory::Interceptor>
       request_callback;
   testing::StrictMock<base::MockCallback<RTLookupResponseCallback>>
       response_callback;
+
+  test_url_loader_factory_.SetInterceptor(request_callback.Get());
+  EXPECT_CALL(request_callback, Run(_)).Times(0);
+  EXPECT_CALL(response_callback, Run(_, _, _)).Times(0);
+
   rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
-                            request_callback.Get(), response_callback.Get(),
+                            response_callback.Get(),
                             base::SequencedTaskRunner::GetCurrentDefault());
 
-  EXPECT_CALL(request_callback, Run(_, _)).Times(0);
-  EXPECT_CALL(response_callback, Run(_, _, _)).Times(0);
   rt_service()->Shutdown();
 
   task_environment_.RunUntilIdle();
@@ -1112,14 +1188,19 @@
   EnableTokenFetchesInClient();
   GURL url(kTestUrl);
 
+  MustRunInterceptor interceptor(
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        RTLookupRequest request_proto;
+        ASSERT_TRUE(GetRequestProto(request, &request_proto));
+
+        EXPECT_EQ(3, request_proto.version());
+        EXPECT_EQ(2, request_proto.report_type());
+        EXPECT_EQ(1, request_proto.frame_type());
+      }));
+  test_url_loader_factory_.SetInterceptor(interceptor.GetCallback());
+
   rt_service()->SendSampledRequest(
       url, last_committed_url_, is_mainframe_,
-      base::BindOnce(
-          [](std::unique_ptr<RTLookupRequest> request, std::string token) {
-            EXPECT_EQ(3, request->version());
-            EXPECT_EQ(2, request->report_type());
-            EXPECT_EQ(1, request->frame_type());
-          }),
       base::SequencedTaskRunner::GetCurrentDefault());
   rt_service()->Shutdown();
 
@@ -1143,14 +1224,16 @@
     GURL url(kTestUrl);
     SetUpFailureResponse(make_fail ? net::HTTP_INTERNAL_SERVER_ERROR
                                    : net::HTTP_OK);
-    base::MockCallback<RTLookupRequestCallback> request_callback;
+    base::MockCallback<network::TestURLLoaderFactory::Interceptor>
+        request_callback;
+    test_url_loader_factory_.SetInterceptor(request_callback.Get());
+    EXPECT_CALL(request_callback, Run(_)).Times(1);
     base::MockCallback<RTLookupResponseCallback> response_callback;
-    rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
-                              request_callback.Get(), response_callback.Get(),
-                              base::SequencedTaskRunner::GetCurrentDefault());
-    EXPECT_CALL(request_callback, Run(_, _)).Times(1);
     EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ !make_fail,
                                        /* is_cached_response */ false, _));
+    rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
+                              response_callback.Get(),
+                              base::SequencedTaskRunner::GetCurrentDefault());
     task_environment_.RunUntilIdle();
   };
   auto perform_failing_lookup = [perform_lookup]() { perform_lookup(true); };
@@ -1191,14 +1274,17 @@
     GURL url(kTestUrl);
     test_url_loader_factory_.AddResponse(kRealTimeLookupUrlPrefix,
                                          "unparseable-response");
-    base::MockCallback<RTLookupRequestCallback> request_callback;
+    base::MockCallback<network::TestURLLoaderFactory::Interceptor>
+        request_callback;
+    test_url_loader_factory_.SetInterceptor(request_callback.Get());
+    EXPECT_CALL(request_callback, Run(_)).Times(1);
+
     base::MockCallback<RTLookupResponseCallback> response_callback;
-    rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
-                              request_callback.Get(), response_callback.Get(),
-                              base::SequencedTaskRunner::GetCurrentDefault());
-    EXPECT_CALL(request_callback, Run(_, _)).Times(1);
     EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ false,
                                        /* is_cached_response */ false, _));
+    rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
+                              response_callback.Get(),
+                              base::SequencedTaskRunner::GetCurrentDefault());
     task_environment_.RunUntilIdle();
   };
 
@@ -1215,14 +1301,17 @@
     CHECK_NE(net_error, net::OK);
     GURL url(kTestUrl);
     SetUpFailureResponse(net::HTTP_OK, net_error);
-    base::MockCallback<RTLookupRequestCallback> request_callback;
+    base::MockCallback<network::TestURLLoaderFactory::Interceptor>
+        request_callback;
+    test_url_loader_factory_.SetInterceptor(request_callback.Get());
+    EXPECT_CALL(request_callback, Run(_)).Times(1);
+
     base::MockCallback<RTLookupResponseCallback> response_callback;
-    rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
-                              request_callback.Get(), response_callback.Get(),
-                              base::SequencedTaskRunner::GetCurrentDefault());
-    EXPECT_CALL(request_callback, Run(_, _)).Times(1);
     EXPECT_CALL(response_callback, Run(/* is_rt_lookup_successful */ false,
                                        /* is_cached_response */ false, _));
+    rt_service()->StartLookup(url, last_committed_url_, is_mainframe_,
+                              response_callback.Get(),
+                              base::SequencedTaskRunner::GetCurrentDefault());
     task_environment_.RunUntilIdle();
   };
 
diff --git a/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter.cc b/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter.cc
index ab970b1..902ffb1e 100644
--- a/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter.cc
+++ b/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter.cc
@@ -39,7 +39,6 @@
     std::string url_lookup_service_metric_suffix,
     const GURL& last_committed_url,
     base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui,
-    UrlRealTimeMechanism::WebUIDelegate* webui_delegate,
     base::WeakPtr<HashRealTimeService> hash_real_time_service_on_ui,
     scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
     const base::RepeatingCallback<content::WebContents*()>&
@@ -48,7 +47,7 @@
       url, threat_types, request_destination, database_manager,
       /*can_check_db=*/true, can_check_high_confidence_allowlist,
       url_lookup_service_metric_suffix, last_committed_url, ui_task_runner_,
-      url_lookup_service_on_ui, webui_delegate,
+      url_lookup_service_on_ui,
       MechanismExperimentHashDatabaseCache::kUrlRealTimeOnly,
       url_checker_delegate, web_contents_getter);
   auto hash_database_mechanism = std::make_unique<DatabaseManagerMechanism>(
diff --git a/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter.h b/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter.h
index c827851..2eab3bb 100644
--- a/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter.h
+++ b/components/safe_browsing/core/browser/safe_browsing_lookup_mechanism_experimenter.h
@@ -64,7 +64,6 @@
       std::string url_lookup_service_metric_suffix,
       const GURL& last_committed_url,
       base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui,
-      UrlRealTimeMechanism::WebUIDelegate* webui_delegate,
       base::WeakPtr<HashRealTimeService> hash_real_time_service_on_ui,
       scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
       const base::RepeatingCallback<content::WebContents*()>&
diff --git a/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.cc b/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.cc
index 03ec1cfa..c618b39 100644
--- a/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.cc
+++ b/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.cc
@@ -55,20 +55,6 @@
 SafeBrowsingUrlCheckerImpl::Notifier&
 SafeBrowsingUrlCheckerImpl::Notifier::operator=(Notifier&& other) = default;
 
-void SafeBrowsingUrlCheckerImpl::Notifier::OnStartSlowCheck(
-    PerformedCheck performed_check) {
-  DCHECK(performed_check == PerformedCheck::kHashDatabaseCheck);
-  if (callback_) {
-    std::move(callback_).Run(slow_check_notifier_.BindNewPipeAndPassReceiver(),
-                             false, false);
-    return;
-  }
-
-  DCHECK(native_callback_);
-  std::move(native_callback_)
-      .Run(&native_slow_check_notifier_, false, false, performed_check);
-}
-
 void SafeBrowsingUrlCheckerImpl::Notifier::OnCompleteCheck(
     bool proceed,
     bool showed_interstitial,
@@ -133,7 +119,6 @@
     GURL last_committed_url,
     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
     base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui,
-    UrlRealTimeMechanism::WebUIDelegate* webui_delegate,
     base::WeakPtr<HashRealTimeService> hash_realtime_service_on_ui,
     scoped_refptr<SafeBrowsingLookupMechanismExperimenter>
         mechanism_experimenter,
@@ -158,7 +143,6 @@
       last_committed_url_(last_committed_url),
       ui_task_runner_(ui_task_runner),
       url_lookup_service_on_ui_(url_lookup_service_on_ui),
-      webui_delegate_(webui_delegate),
       hash_realtime_service_on_ui_(hash_realtime_service_on_ui),
       mechanism_experimenter_(mechanism_experimenter),
       is_mechanism_experiment_allowed_(is_mechanism_experiment_allowed),
@@ -528,9 +512,8 @@
               request_destination_, database_manager_,
               can_check_high_confidence_allowlist_,
               url_lookup_service_metric_suffix_, last_committed_url_,
-              url_lookup_service_on_ui_, webui_delegate_,
-              hash_realtime_service_on_ui_, url_checker_delegate_,
-              web_contents_getter_);
+              url_lookup_service_on_ui_, hash_realtime_service_on_ui_,
+              url_checker_delegate_, web_contents_getter_);
       return KickOffLookupMechanismResult(start_check_result, performed_check);
     } else {
       lookup_mechanism = std::make_unique<UrlRealTimeMechanism>(
@@ -538,7 +521,7 @@
           database_manager_, can_check_db_,
           can_check_high_confidence_allowlist_,
           url_lookup_service_metric_suffix_, last_committed_url_,
-          ui_task_runner_, url_lookup_service_on_ui_, webui_delegate_,
+          ui_task_runner_, url_lookup_service_on_ui_,
           MechanismExperimentHashDatabaseCache::kNoExperiment,
           url_checker_delegate_, web_contents_getter_);
     }
diff --git a/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h b/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h
index fb62ec4..5c64f6b 100644
--- a/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h
+++ b/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h
@@ -127,7 +127,6 @@
       GURL last_committed_url,
       scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
       base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui,
-      UrlRealTimeMechanism::WebUIDelegate* webui_delegate,
       base::WeakPtr<HashRealTimeService> hash_realtime_service_on_ui,
       scoped_refptr<SafeBrowsingLookupMechanismExperimenter>
           mechanism_experimenter,
@@ -168,7 +167,6 @@
     Notifier(Notifier&& other);
     Notifier& operator=(Notifier&& other);
 
-    void OnStartSlowCheck(PerformedCheck performed_check);
     void OnCompleteCheck(bool proceed,
                          bool showed_interstitial,
                          PerformedCheck performed_check);
@@ -339,11 +337,6 @@
   // UI thread.
   base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui_;
 
-  // May be null on certain platforms that don't support chrome://safe-browsing
-  // and in unit tests. If non-null, guaranteed to outlive this object by
-  // contract.
-  raw_ptr<UrlRealTimeMechanism::WebUIDelegate> webui_delegate_ = nullptr;
-
   // This object is used to perform the hash-prefix real-time lookup. It can
   // only be accessed on the UI thread.
   base::WeakPtr<HashRealTimeService> hash_realtime_service_on_ui_;
diff --git a/components/safe_browsing/core/browser/safe_browsing_url_checker_impl_unittest.cc b/components/safe_browsing/core/browser/safe_browsing_url_checker_impl_unittest.cc
index e33b555..1e070c56 100644
--- a/components/safe_browsing/core/browser/safe_browsing_url_checker_impl_unittest.cc
+++ b/components/safe_browsing/core/browser/safe_browsing_url_checker_impl_unittest.cc
@@ -290,14 +290,14 @@
               return ChromeUserPopulation();
             }),
             /*referrer_chain_provider=*/nullptr,
-            /*pref_service=*/nullptr) {}
+            /*pref_service=*/nullptr,
+            /*webui_delegate=*/nullptr) {}
   // Returns the threat type previously set by |SetThreatTypeForUrl|. It crashes
   // if the threat type for the |gurl| is not set in advance.
   void StartLookup(
       const GURL& gurl,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       RTLookupResponseCallback response_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override {
     std::string url = gurl.spec();
@@ -346,7 +346,6 @@
       const GURL& gurl,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override {}
 
   // |should_complete_lookup| should generally be true, unless you specifically
@@ -390,7 +389,6 @@
       const GURL& url,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       RTLookupResponseCallback response_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override {}
   absl::optional<std::string> GetDMTokenString() const override {
@@ -514,7 +512,6 @@
         base::SequencedTaskRunner::GetCurrentDefault(),
         url_real_time_lookup_enabled ? url_lookup_service_->GetWeakPtr()
                                      : nullptr,
-        /*webui_delegate_=*/nullptr,
         /*hash_realtime_service=*/hash_realtime_service_->GetWeakPtr(),
         /*mechanism_experimenter=*/mechanism_experimenter,
         optional_args.is_lookup_mechanism_experiment_enabled,
diff --git a/components/safe_browsing/core/browser/url_realtime_mechanism.cc b/components/safe_browsing/core/browser/url_realtime_mechanism.cc
index ec58257d..a5552e9 100644
--- a/components/safe_browsing/core/browser/url_realtime_mechanism.cc
+++ b/components/safe_browsing/core/browser/url_realtime_mechanism.cc
@@ -52,7 +52,6 @@
     const GURL& last_committed_url,
     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
     base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui,
-    WebUIDelegate* webui_delegate,
     MechanismExperimentHashDatabaseCache experiment_cache_selection,
     scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
     const base::RepeatingCallback<content::WebContents*()>& web_contents_getter)
@@ -67,7 +66,6 @@
       last_committed_url_(last_committed_url),
       ui_task_runner_(ui_task_runner),
       url_lookup_service_on_ui_(url_lookup_service_on_ui),
-      webui_delegate_(webui_delegate),
       url_checker_delegate_(url_checker_delegate),
       web_contents_getter_(web_contents_getter) {}
 
@@ -156,15 +154,12 @@
     return;
   }
 
-  RTLookupRequestCallback request_callback =
-      base::BindOnce(&UrlRealTimeMechanism::OnLookupRequest, weak_ptr_on_io);
-
   RTLookupResponseCallback response_callback =
       base::BindOnce(&UrlRealTimeMechanism::OnLookupResponse, weak_ptr_on_io);
 
-  url_lookup_service_on_ui->StartLookup(
-      url, last_committed_url, is_mainframe, std::move(request_callback),
-      std::move(response_callback), std::move(io_task_runner));
+  url_lookup_service_on_ui->StartLookup(url, last_committed_url, is_mainframe,
+                                        std::move(response_callback),
+                                        std::move(io_task_runner));
 }
 
 void UrlRealTimeMechanism::MaybeSendSampleRequest(
@@ -184,22 +179,11 @@
   bool is_lookup_service_available =
       !url_lookup_service_on_ui->IsInBackoffMode();
   if (is_lookup_service_available) {
-    RTLookupRequestCallback request_callback =
-        base::BindOnce(&UrlRealTimeMechanism::OnLookupRequest, weak_ptr_on_io);
     url_lookup_service_on_ui->SendSampledRequest(
-        url, last_committed_url, is_mainframe, std::move(request_callback),
-        std::move(io_task_runner));
+        url, last_committed_url, is_mainframe, std::move(io_task_runner));
   }
 }
 
-void UrlRealTimeMechanism::OnLookupRequest(
-    std::unique_ptr<RTLookupRequest> request,
-    std::string oauth_token) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  LogLookupRequest(*request, oauth_token);
-}
-
 void UrlRealTimeMechanism::OnLookupResponse(
     bool is_lookup_successful,
     bool is_cached_response,
@@ -214,8 +198,6 @@
     return;
   }
 
-  LogLookupResponse(*response);
-
   RTLookupResponse::ThreatInfo::VerdictType rt_verdict_type =
       RTLookupResponse::ThreatInfo::SAFE;
   SBThreatType sb_threat_type = SB_THREAT_TYPE_SAFE;
@@ -247,45 +229,6 @@
   }
 }
 
-void UrlRealTimeMechanism::LogLookupRequest(const RTLookupRequest& request,
-                                            const std::string& oauth_token) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!webui_delegate_) {
-    return;
-  }
-
-  // The following is to log this lookup request on any open
-  // chrome://safe-browsing pages.
-  ui_task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&WebUIDelegate::AddToURTLookupPings,
-                     base::Unretained(webui_delegate_), request, oauth_token),
-      base::BindOnce(&UrlRealTimeMechanism::SetWebUIToken,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void UrlRealTimeMechanism::LogLookupResponse(const RTLookupResponse& response) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!webui_delegate_) {
-    return;
-  }
-
-  if (url_web_ui_token_ != -1) {
-    // The following is to log this lookup response on any open
-    // chrome://safe-browsing pages.
-    ui_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&WebUIDelegate::AddToURTLookupResponses,
-                                  base::Unretained(webui_delegate_),
-                                  url_web_ui_token_, response));
-  }
-}
-
-void UrlRealTimeMechanism::SetWebUIToken(int token) {
-  url_web_ui_token_ = token;
-}
-
 void UrlRealTimeMechanism::PerformHashBasedCheck(
     const GURL& url,
     bool real_time_request_failed) {
diff --git a/components/safe_browsing/core/browser/url_realtime_mechanism.h b/components/safe_browsing/core/browser/url_realtime_mechanism.h
index fa45d22..f65958b9 100644
--- a/components/safe_browsing/core/browser/url_realtime_mechanism.h
+++ b/components/safe_browsing/core/browser/url_realtime_mechanism.h
@@ -24,23 +24,6 @@
 // This performs the real-time URL Safe Browsing check.
 class UrlRealTimeMechanism : public SafeBrowsingLookupMechanism {
  public:
-  // Interface via which a client of this class can surface relevant events in
-  // WebUI. All methods must be called on the UI thread.
-  class WebUIDelegate {
-   public:
-    virtual ~WebUIDelegate() = default;
-
-    // Adds the new ping to the set of URT lookup pings. Returns a token that
-    // can be used in |AddToURTLookupResponses| to correlate a ping and
-    // response.
-    virtual int AddToURTLookupPings(const RTLookupRequest request,
-                                    const std::string oauth_token) = 0;
-
-    // Adds the new response to the set of URT lookup pings.
-    virtual void AddToURTLookupResponses(int token,
-                                         const RTLookupResponse response) = 0;
-  };
-
   UrlRealTimeMechanism(
       const GURL& url,
       const SBThreatTypeSet& threat_types,
@@ -52,7 +35,6 @@
       const GURL& last_committed_url,
       scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
       base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui,
-      WebUIDelegate* webui_delegate,
       MechanismExperimentHashDatabaseCache experiment_cache_selection,
       scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
       const base::RepeatingCallback<content::WebContents*()>&
@@ -91,10 +73,6 @@
       base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui,
       scoped_refptr<base::SequencedTaskRunner> io_task_runner);
 
-  // Called when the |request| from the real-time lookup service is sent.
-  void OnLookupRequest(std::unique_ptr<RTLookupRequest> request,
-                       std::string oauth_token);
-
   // Called when the |response| from the real-time lookup service is received.
   // |is_lookup_successful| is true if the response code is OK and the
   // response body is successfully parsed.
@@ -104,15 +82,6 @@
                         bool is_cached_response,
                         std::unique_ptr<RTLookupResponse> response);
 
-  // Logs |request| and |oauth_token| on any open chrome://safe-browsing pages.
-  void LogLookupRequest(const RTLookupRequest& request,
-                        const std::string& oauth_token);
-
-  // Logs |response| on any open chrome://safe-browsing pages.
-  void LogLookupResponse(const RTLookupResponse& response);
-
-  void SetWebUIToken(int token);
-
   // Perform the hash based check for the url. |real_time_request_failed|
   // specifies whether this was triggered due to the real-time request having
   // failed (e.g. due to backoff, network errors, other service unavailability).
@@ -142,10 +111,6 @@
   // distinguish between mainframe and non-mainframe resources.
   const network::mojom::RequestDestination request_destination_;
 
-  // Token used for displaying url real time lookup pings. A single token is
-  // sufficient since real time check only happens on main frame url.
-  int url_web_ui_token_ = -1;
-
   // Whether safe browsing database can be checked. It is set to false when
   // enterprise real time URL lookup is enabled and safe browsing is disabled
   // for this profile.
@@ -175,11 +140,6 @@
   // accessed in UI thread.
   base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_on_ui_;
 
-  // May be null on certain platforms that don't support
-  // chrome://safe-browsing and in unit tests. If non-null, guaranteed to
-  // outlive this object by contract.
-  raw_ptr<WebUIDelegate> webui_delegate_ = nullptr;
-
   // This object is used to call the |NotifySusiciousSiteDetected| method on
   // URLs with suspicious verdicts.
   scoped_refptr<UrlCheckerDelegate> url_checker_delegate_;
diff --git a/components/safe_browsing/core/browser/url_realtime_mechanism_unittest.cc b/components/safe_browsing/core/browser/url_realtime_mechanism_unittest.cc
index 1242fcf2..6fa6aff 100644
--- a/components/safe_browsing/core/browser/url_realtime_mechanism_unittest.cc
+++ b/components/safe_browsing/core/browser/url_realtime_mechanism_unittest.cc
@@ -42,14 +42,14 @@
               return ChromeUserPopulation();
             }),
             /*referrer_chain_provider=*/nullptr,
-            /*pref_service=*/nullptr) {}
+            /*pref_service=*/nullptr,
+            /*webui_delegate=*/nullptr) {}
   // Returns the threat type previously set by |SetThreatTypeForUrl|. It crashes
   // if the threat type for the |gurl| is not set in advance.
   void StartLookup(
       const GURL& gurl,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       RTLookupResponseCallback response_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override {
     std::string url = gurl.spec();
@@ -107,7 +107,6 @@
       const GURL& gurl,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override {}
 
   // |should_complete_lookup| should generally be true, unless you specifically
@@ -154,7 +153,6 @@
       const GURL& url,
       const GURL& last_committed_url,
       bool is_mainframe,
-      RTLookupRequestCallback request_callback,
       RTLookupResponseCallback response_callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override {}
   absl::optional<std::string> GetDMTokenString() const override {
@@ -372,7 +370,6 @@
         /*last_committed_url=*/GURL(),
         base::SequencedTaskRunner::GetCurrentDefault(),
         url_lookup_service_->GetWeakPtr(),
-        /*webui_delegate_=*/nullptr,
         MechanismExperimentHashDatabaseCache::kNoExperiment,
         url_checker_delegate_, mock_web_contents_getter.Get());
   }
diff --git a/components/safe_browsing/core/common/features.cc b/components/safe_browsing/core/common/features.cc
index c57ea3f..22d0bb3 100644
--- a/components/safe_browsing/core/common/features.cc
+++ b/components/safe_browsing/core/common/features.cc
@@ -88,27 +88,6 @@
              "SafeBrowsingExtensionTelemetryConfiguration",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kExtensionTelemetryFileData,
-             "SafeBrowsingExtensionTelemetryFileData",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-const base::FeatureParam<int> kExtensionTelemetryFileDataMaxFilesToProcess{
-    &kExtensionTelemetryFileData, "MaxFilesToProcess",
-    /*default_value=*/50};
-
-const base::FeatureParam<int> kExtensionTelemetryFileDataMaxFileSizeBytes{
-    &kExtensionTelemetryFileData, "MaxFileSizeBytes",
-    /*default_value=100KB*/ 100 * 1024};
-
-const base::FeatureParam<int>
-    kExtensionTelemetryFileDataCollectionIntervalSeconds{
-        &kExtensionTelemetryFileData, "CollectionIntervalSeconds",
-        /*default_value=*/7200};
-
-const base::FeatureParam<int> kExtensionTelemetryFileDataStartupDelaySeconds{
-    &kExtensionTelemetryFileData, "StartupDelaySeconds",
-    /*default_value=*/300};
-
 BASE_FEATURE(kExtensionTelemetryFileDataForCommandLineExtensions,
              "SafeBrowsingExtensionTelemetryFileDataForCommandLineExtensions",
              base::FEATURE_ENABLED_BY_DEFAULT);
@@ -357,7 +336,6 @@
     {&kDownloadTailoredWarnings, true},
     {&kEvaluateProtectedPasswordLengthMinimum, false},
     {&kExtensionTelemetryDisableOffstoreExtensions, true},
-    {&kExtensionTelemetryFileData, true},
     {&kExtensionTelemetryInterceptRemoteHostsContactedInRenderer, true},
     {&kExtensionTelemetryPotentialPasswordTheft, true},
     {&kExtensionTelemetryReportContactedHosts, true},
diff --git a/components/safe_browsing/core/common/features.h b/components/safe_browsing/core/common/features.h
index 9a2df33..af27244 100644
--- a/components/safe_browsing/core/common/features.h
+++ b/components/safe_browsing/core/common/features.h
@@ -79,30 +79,6 @@
 // sent by the server.
 BASE_DECLARE_FEATURE(kExtensionTelemetryConfiguration);
 
-// Allows the Extension Telemetry Service to process installed extension files
-// and attach file data to reports.
-BASE_DECLARE_FEATURE(kExtensionTelemetryFileData);
-
-// Specifies the max number of files to process per extension in Extension
-// Telemetry's File Processor.
-extern const base::FeatureParam<int>
-    kExtensionTelemetryFileDataMaxFilesToProcess;
-
-// Specifies the max file size to process in Extension Telemetry's File
-// Processor.
-extern const base::FeatureParam<int>
-    kExtensionTelemetryFileDataMaxFileSizeBytes;
-
-// Specifies the interval for extension telemetry to collect offstore extension
-// file data.
-extern const base::FeatureParam<int>
-    kExtensionTelemetryFileDataCollectionIntervalSeconds;
-
-// Specifies the initial delay for extension telemetry to start collecting
-// offstore extension file data.
-extern const base::FeatureParam<int>
-    kExtensionTelemetryFileDataStartupDelaySeconds;
-
 // Allows the Extension Telemetry Service to include file data of extensions
 // specified in the --load-extension commandline switch in telemetry reports.
 BASE_DECLARE_FEATURE(kExtensionTelemetryFileDataForCommandLineExtensions);
diff --git a/components/safe_browsing/core/common/safebrowsing_referral_methods.h b/components/safe_browsing/core/common/safebrowsing_referral_methods.h
index d27bb2b..e4c9591 100644
--- a/components/safe_browsing/core/common/safebrowsing_referral_methods.h
+++ b/components/safe_browsing/core/common/safebrowsing_referral_methods.h
@@ -14,7 +14,8 @@
   kSafetyCheck = 1,
   kPromoSlingerReferral = 2,
   kDownloadBubbleSubpage = 3,
-  kMaxValue = kDownloadBubbleSubpage,
+  kDownloadButtonIphPromo = 4,
+  kMaxValue = kDownloadButtonIphPromo,
 };
 
 }  // namespace safe_browsing
diff --git a/components/signin/features.gni b/components/signin/features.gni
index df5e995..ae1b0ea9 100644
--- a/components/signin/features.gni
+++ b/components/signin/features.gni
@@ -10,10 +10,6 @@
   enable_bound_session_credentials = is_linux || is_mac || is_win
 }
 
-# Warning: The feature is still under development. See b/280753754.
-enable_search_engine_choice = (is_linux || is_mac || is_win || is_chromeos ||
-                               is_ios) && !is_chrome_for_testing
-
 # Dice is supported on the platform (but not necessarily enabled).
 enable_dice_support = is_linux || is_mac || is_win || is_fuchsia
 
diff --git a/components/signin/public/base/BUILD.gn b/components/signin/public/base/BUILD.gn
index b8ba9ee..7e50941 100644
--- a/components/signin/public/base/BUILD.gn
+++ b/components/signin/public/base/BUILD.gn
@@ -16,7 +16,6 @@
     "ENABLE_DICE_SUPPORT=$enable_dice_support",
     "ENABLE_MIRROR=$enable_mirror",
     "ENABLE_BOUND_SESSION_CREDENTIALS=$enable_bound_session_credentials",
-    "ENABLE_SEARCH_ENGINE_CHOICE=$enable_search_engine_choice",
   ]
 }
 
diff --git a/components/soda/constants.cc b/components/soda/constants.cc
index e5dd19add..95bb1ab 100644
--- a/components/soda/constants.cc
+++ b/components/soda/constants.cc
@@ -159,6 +159,20 @@
   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 6d4f5be..af5544eb 100644
--- a/components/soda/constants.h
+++ b/components/soda/constants.h
@@ -230,6 +230,13 @@
 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/strings/BUILD.gn b/components/strings/BUILD.gn
index 04cc00d..b4f6b16 100644
--- a/components/strings/BUILD.gn
+++ b/components/strings/BUILD.gn
@@ -6,7 +6,6 @@
 import("//build/config/locales.gni")
 import("//components/feed/features.gni")
 import("//components/services/screen_ai/buildflags/features.gni")
-import("//components/signin/features.gni")
 import("//device/vr/buildflags/buildflags.gni")
 import("//pdf/features.gni")
 import("//ppapi/buildflags/buildflags.gni")
@@ -40,7 +39,6 @@
     "enable_plugins=$enable_plugins",
     "enable_print_preview=$enable_print_preview",
     "enable_screen_ai_service=$enable_screen_ai_service",
-    "enable_search_engine_choice=$enable_search_engine_choice",
     "enable_vr=$enable_vr",
     "use_blink=$use_blink",
   ]
diff --git a/components/subresource_filter/content/renderer/subresource_filter_agent.cc b/components/subresource_filter/content/renderer/subresource_filter_agent.cc
index d4c770d2..48cfa135 100644
--- a/components/subresource_filter/content/renderer/subresource_filter_agent.cc
+++ b/components/subresource_filter/content/renderer/subresource_filter_agent.cc
@@ -229,8 +229,6 @@
   // Note: mojom::ActivationLevel used to be called mojom::ActivationState, the
   // legacy name is kept for the histogram.
   mojom::ActivationLevel activation_level = activation_state.activation_level;
-  UMA_HISTOGRAM_ENUMERATION("SubresourceFilter.DocumentLoad.ActivationState",
-                            activation_level);
 
   if (!IsSubresourceFilterChild()) {
     UMA_HISTOGRAM_BOOLEAN(
diff --git a/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc b/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc
index 178cb0de..b21252f3 100644
--- a/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc
+++ b/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc
@@ -135,8 +135,7 @@
 // Histogram names.
 constexpr const char kDocumentLoadRulesetIsAvailable[] =
     "SubresourceFilter.DocumentLoad.RulesetIsAvailable";
-constexpr const char kDocumentLoadActivationLevel[] =
-    "SubresourceFilter.DocumentLoad.ActivationState";
+
 constexpr const char kMainFrameLoadRulesetIsAvailableAnyActivationLevel[] =
     "SubresourceFilter.MainFrameLoad.RulesetIsAvailableAnyActivationLevel";
 
@@ -320,9 +319,6 @@
   StartLoadWithoutSettingActivationState();
   FinishLoad();
 
-  histogram_tester.ExpectUniqueSample(
-      kDocumentLoadActivationLevel,
-      static_cast<int>(mojom::ActivationLevel::kDisabled), 1);
   histogram_tester.ExpectTotalCount(kDocumentLoadRulesetIsAvailable, 0);
   histogram_tester.ExpectUniqueSample(
       kMainFrameLoadRulesetIsAvailableAnyActivationLevel, 0, 1);
@@ -336,9 +332,6 @@
   StartLoadWithoutSettingActivationState();
   FinishLoad();
 
-  histogram_tester.ExpectUniqueSample(
-      kDocumentLoadActivationLevel,
-      static_cast<int>(mojom::ActivationLevel::kDisabled), 1);
   histogram_tester.ExpectTotalCount(kDocumentLoadRulesetIsAvailable, 0);
   histogram_tester.ExpectUniqueSample(
       kMainFrameLoadRulesetIsAvailableAnyActivationLevel, 1, 1);
@@ -374,9 +367,6 @@
   StartLoadAndSetActivationState(mojom::ActivationLevel::kEnabled);
   FinishLoad();
 
-  histogram_tester.ExpectUniqueSample(
-      kDocumentLoadActivationLevel,
-      static_cast<int>(mojom::ActivationLevel::kEnabled), 1);
   histogram_tester.ExpectUniqueSample(kDocumentLoadRulesetIsAvailable, 0, 1);
   histogram_tester.ExpectUniqueSample(
       kMainFrameLoadRulesetIsAvailableAnyActivationLevel, 0, 1);
@@ -394,7 +384,6 @@
   StartLoadAndSetActivationState(mojom::ActivationLevel::kEnabled);
   FinishLoad();
 
-  histogram_tester.ExpectTotalCount(kDocumentLoadActivationLevel, 0);
   histogram_tester.ExpectTotalCount(kDocumentLoadRulesetIsAvailable, 0);
   histogram_tester.ExpectTotalCount(
       kMainFrameLoadRulesetIsAvailableAnyActivationLevel, 0);
@@ -432,11 +421,6 @@
   // the figures below, as they came after the original page load event. There
   // should be no samples recorded into subresource count histograms during the
   // final load where there is no activation.
-  EXPECT_THAT(
-      histogram_tester.GetAllSamples(kDocumentLoadActivationLevel),
-      ::testing::ElementsAre(
-          base::Bucket(static_cast<int>(mojom::ActivationLevel::kDisabled), 1),
-          base::Bucket(static_cast<int>(mojom::ActivationLevel::kEnabled), 1)));
   histogram_tester.ExpectUniqueSample(kDocumentLoadRulesetIsAvailable, 1, 1);
   histogram_tester.ExpectUniqueSample(
       kMainFrameLoadRulesetIsAvailableAnyActivationLevel, 1, 2);
@@ -479,9 +463,6 @@
     ExpectDocumentLoadStatisticsSent();
     FinishLoad();
 
-    histogram_tester.ExpectUniqueSample(
-        kDocumentLoadActivationLevel,
-        static_cast<int>(mojom::ActivationLevel::kEnabled), 2);
     histogram_tester.ExpectUniqueSample(kDocumentLoadRulesetIsAvailable, 1, 2);
     histogram_tester.ExpectUniqueSample(
         kMainFrameLoadRulesetIsAvailableAnyActivationLevel, 1, 2);
@@ -558,9 +539,6 @@
   ExpectDocumentLoadStatisticsSent();
   FinishLoad();
 
-  histogram_tester.ExpectUniqueSample(
-      kDocumentLoadActivationLevel,
-      static_cast<int>(mojom::ActivationLevel::kDryRun), 1);
   histogram_tester.ExpectUniqueSample(kDocumentLoadRulesetIsAvailable, 1, 1);
   histogram_tester.ExpectUniqueSample(
       kMainFrameLoadRulesetIsAvailableAnyActivationLevel, 1, 1);
diff --git a/components/vector_icons/BUILD.gn b/components/vector_icons/BUILD.gn
index f4ddc7d..69d20479 100644
--- a/components/vector_icons/BUILD.gn
+++ b/components/vector_icons/BUILD.gn
@@ -212,8 +212,8 @@
     "sync_off_chrome_refresh.icon",
     "sync_problem_chrome_refresh.icon",
     "tenancy.icon",
-    "troubleshoot.icon",
     "touchpad_mouse.icon",
+    "troubleshoot.icon",
     "undo.icon",
     "usb.icon",
     "usb_chrome_refresh.icon",
@@ -264,6 +264,7 @@
       "google_chrome/google_sites.icon",
       "google_chrome/google_slides.icon",
       "google_chrome/google_super_g.icon",
+      "google_chrome/gshield.icon",
       "google_chrome/page_insights.icon",
       "google_chrome/page_insights_color.icon",
       "google_chrome/pen_spark.icon",
diff --git a/components/viz/service/display_embedder/buffer_queue.cc b/components/viz/service/display_embedder/buffer_queue.cc
index be2c7ed..bf06870 100644
--- a/components/viz/service/display_embedder/buffer_queue.cc
+++ b/components/viz/service/display_embedder/buffer_queue.cc
@@ -77,12 +77,10 @@
 void BufferQueue::SwapBuffersComplete() {
   DCHECK(!in_flight_buffers_.empty());
 
-  if (in_flight_buffers_.front()) {
-    if (displayed_buffer_) {
-      available_buffers_.push_back(std::move(displayed_buffer_));
-    }
-    displayed_buffer_ = std::move(in_flight_buffers_.front());
+  if (displayed_buffer_) {
+    available_buffers_.push_back(std::move(displayed_buffer_));
   }
+  displayed_buffer_ = std::move(in_flight_buffers_.front());
 
   in_flight_buffers_.pop_front();
 }
@@ -183,9 +181,9 @@
     return gpu::Mailbox();
   }
 
-  // The last swapped buffer will generally be in displayed_buffer_, as long as
-  // SwapBuffersComplete() has been called at least once for a non-empty swap
-  // since the last Reshape().
+  // The last swapped buffer will generally be in `displayed_buffer_`, unless
+  // the last completed swap was empty or there haven't been any completed swaps
+  // since Reshape() was last called.
   if (displayed_buffer_) {
     return displayed_buffer_->mailbox;
   }
diff --git a/components/viz/service/display_embedder/buffer_queue.h b/components/viz/service/display_embedder/buffer_queue.h
index 1dd0c7f..711393c 100644
--- a/components/viz/service/display_embedder/buffer_queue.h
+++ b/components/viz/service/display_embedder/buffer_queue.h
@@ -78,6 +78,7 @@
   // Called when SwapBuffers is skipped this frame. Damages allocated buffers,
   // but does not advance |in_flight_buffers_| or |current_buffer_|. We don't
   // clear the damage on |current_buffer_| because it hasn't been displayed yet.
+  // SwapBuffersComplete() must not be called for skipped swap.
   void SwapBuffersSkipped(const gfx::Rect& damage);
 
   // If |size| or |color_space| correspond to a change of state, frees all
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2Api.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2Api.java
index 5baea68..23dadf2 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2Api.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2Api.java
@@ -34,8 +34,11 @@
 import org.chromium.blink.mojom.ResidentKeyRequirement;
 import org.chromium.blink.mojom.UserVerificationRequirement;
 import org.chromium.blink.mojom.UvmEntry;
+import org.chromium.device.DeviceFeatureList;
+import org.chromium.device.DeviceFeatureMap;
 import org.chromium.mojo_base.mojom.TimeDelta;
 
+import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -177,6 +180,18 @@
             throws NoSuchAlgorithmException {
         final int a = writeHeader(OBJECT_MAGIC, parcel);
 
+        if (DeviceFeatureMap.isEnabled(DeviceFeatureList.WEBAUTHN_ANDROID_FIDO_JSON)) {
+            // 13: JSON serialisation of the request.
+            //
+            // Recent versions of Play Services will use this field and ignore the
+            // others. In time, we should be able to skip serialising anything else
+            // in this function.
+            int b = writeHeader(13, parcel);
+            parcel.writeString(
+                    Fido2CredentialRequestJni.get().createOptionsToJson(options.serialize()));
+            writeLength(b, parcel);
+        }
+
         // 2: PublicKeyCredentialRpEntity
 
         int b = writeHeader(2, parcel);
@@ -414,6 +429,18 @@
             PublicKeyCredentialRequestOptions options, byte[] tunnelId, Parcel parcel) {
         final int a = writeHeader(OBJECT_MAGIC, parcel);
 
+        if (DeviceFeatureMap.isEnabled(DeviceFeatureList.WEBAUTHN_ANDROID_FIDO_JSON)) {
+            // 11: JSON serialisation of the request.
+            //
+            // Recent versions of Play Services will use this field and ignore the
+            // others. In time, we should be able to skip serialising anything else
+            // in this function.
+            int z = writeHeader(11, parcel);
+            parcel.writeString(
+                    Fido2CredentialRequestJni.get().getOptionsToJson(options.serialize()));
+            writeLength(z, parcel);
+        }
+
         // 2: challenge
         int z = writeHeader(2, parcel);
         parcel.writeByteArray(options.challenge);
@@ -816,6 +843,7 @@
         GetAssertionAuthenticatorResponse assertionResponse = null;
         Extensions extensions = null;
         int attachment = AuthenticatorAttachment.MIN_VALUE - 1;
+        String jsonString = null;
 
         while (parcel.dataPosition() < endPosition) {
             header = readHeader(parcel);
@@ -857,6 +885,10 @@
                     attachment = stringToAttachment(parcel.readString());
                     break;
 
+                case 9:
+                    jsonString = parcel.readString();
+                    break;
+
                 default:
                     // unknown tag. Skip over it.
                     parcel.setDataPosition(addLengthToParcelPosition(header.second, parcel));
@@ -864,6 +896,31 @@
         }
 
         if (creationResponse != null) {
+            if (jsonString != null
+                    && DeviceFeatureMap.isEnabled(DeviceFeatureList.WEBAUTHN_ANDROID_FIDO_JSON)) {
+                // If the JSON form was provided then we use that and ignore the
+                // rest.
+                byte[] responseSerialized =
+                        Fido2CredentialRequestJni.get().makeCredentialResponseFromJson(jsonString);
+                if (responseSerialized == null) {
+                    Log.e(
+                            TAG,
+                            "Failed to convert response from JSON to Mojo object: %s",
+                            jsonString);
+                    throw new IllegalArgumentException();
+                }
+                MakeCredentialAuthenticatorResponse response;
+                try {
+                    response =
+                            MakeCredentialAuthenticatorResponse.deserialize(
+                                    ByteBuffer.wrap(responseSerialized));
+                } catch (org.chromium.mojo.bindings.DeserializationException e) {
+                    throw new IllegalArgumentException(e);
+                }
+
+                return response;
+            }
+
             if (attachment >= AuthenticatorAttachment.MIN_VALUE) {
                 creationResponse.authenticatorAttachment = attachment;
             }
@@ -882,6 +939,31 @@
         }
 
         if (assertionResponse != null) {
+            if (jsonString != null
+                    && DeviceFeatureMap.isEnabled(DeviceFeatureList.WEBAUTHN_ANDROID_FIDO_JSON)) {
+                // If the JSON form was provided then we use that and ignore the
+                // rest.
+                byte[] responseSerialized =
+                        Fido2CredentialRequestJni.get().getCredentialResponseFromJson(jsonString);
+                if (responseSerialized == null) {
+                    Log.e(
+                            TAG,
+                            "Failed to convert response from JSON to Mojo object: %s",
+                            jsonString);
+                    throw new IllegalArgumentException();
+                }
+                GetAssertionAuthenticatorResponse response;
+                try {
+                    response =
+                            GetAssertionAuthenticatorResponse.deserialize(
+                                    ByteBuffer.wrap(responseSerialized));
+                } catch (org.chromium.mojo.bindings.DeserializationException e) {
+                    throw new IllegalArgumentException(e);
+                }
+
+                return response;
+            }
+
             if (extensions != null && extensions.userVerificationMethods != null) {
                 assertionResponse.extensions.echoUserVerificationMethods = true;
                 assertionResponse.extensions.userVerificationMethods = new UvmEntry[0];
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.cc b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
index 83d7503..1fd846c37 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.cc
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
@@ -179,8 +179,7 @@
 
 // DumpAccessibilityTestBase
 DumpAccessibilityTestBase::DumpAccessibilityTestBase()
-    : enable_accessibility_after_navigating_(false),
-      test_helper_(GetParam().first) {}
+    : enable_accessibility_after_navigating_(false), test_helper_(GetParam()) {}
 
 DumpAccessibilityTestBase::~DumpAccessibilityTestBase() {}
 
@@ -232,9 +231,6 @@
   // corresponding code in AXPosition on the browser that collects those
   // markers.
   enabled_features->emplace_back(features::kUseAXPositionForDocumentMarkers);
-
-  auto* vec = GetParam().second ? enabled_features : disabled_features;
-  vec->emplace_back(blink::features::kSerializeAccessibilityPostLifecycle);
 }
 
 std::string DumpAccessibilityTestBase::DumpTreeAsString() const {
@@ -255,16 +251,6 @@
   return formatter->Format(GetRootAccessibilityNode(GetWebContents()));
 }
 
-DumpAccessibilityTestBase::ParamVector DumpAccessibilityTestBase::TestParams(
-    const ApiTypeVector& api_types) {
-  return std::accumulate(api_types.begin(), api_types.end(), ParamVector(),
-                         [](ParamVector&& v, ui::AXApiType::Type api_type) {
-                           v.push_back({api_type, true});
-                           v.push_back({api_type, false});
-                           return v;
-                         });
-}
-
 void DumpAccessibilityTestBase::RunTest(
     ui::AXMode mode,
     const base::FilePath file_path,
@@ -597,7 +583,7 @@
 
 std::unique_ptr<AXTreeFormatter> DumpAccessibilityTestBase::CreateFormatter()
     const {
-  return AXInspectFactory::CreateFormatter(GetParam().first);
+  return AXInspectFactory::CreateFormatter(GetParam());
 }
 
 std::pair<EvalJsResult, std::vector<std::string>>
@@ -608,7 +594,7 @@
   ui::AXTreeSelector selector(manager->GetBrowserAccessibilityRoot()
                                   ->GetTargetForNativeAccessibilityEvent());
   std::unique_ptr<ui::AXEventRecorder> event_recorder =
-      AXInspectFactory::CreateRecorder(GetParam().first, manager,
+      AXInspectFactory::CreateRecorder(GetParam(), manager,
                                        base::GetCurrentProcId(), selector);
   event_recorder->SetOnlyWebEvents(true);
 
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.h b/content/browser/accessibility/dump_accessibility_browsertest_base.h
index 94b17ed..55a2304 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.h
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.h
@@ -36,9 +36,9 @@
 // testing accessibility in Chromium.
 //
 // See content/test/data/accessibility/readme.md for an overview.
-class DumpAccessibilityTestBase : public ContentBrowserTest,
-                                  public ::testing::WithParamInterface<
-                                      std::pair<ui::AXApiType::Type, bool>> {
+class DumpAccessibilityTestBase
+    : public ContentBrowserTest,
+      public ::testing::WithParamInterface<ui::AXApiType::Type> {
  public:
   DumpAccessibilityTestBase();
   ~DumpAccessibilityTestBase() override;
@@ -81,40 +81,36 @@
   }
 
   typedef std::vector<ui::AXApiType::Type> ApiTypeVector;
-  typedef std::vector<std::pair<ui::AXApiType::Type, bool>> ParamVector;
 
-  static ParamVector TestParams(const ApiTypeVector& api_types);
-  static ParamVector TreeTestPasses() {
-    return DumpAccessibilityTestBase::TestParams(
-        ui::AXInspectTestHelper::TreeTestPasses());
+  static ApiTypeVector TreeTestPasses() {
+    return ui::AXInspectTestHelper::TreeTestPasses();
   }
-  static ParamVector EventTestPasses() {
-    return DumpAccessibilityTestBase::TestParams(
-        ui::AXInspectTestHelper::EventTestPasses());
+  static ApiTypeVector EventTestPasses() {
+    return ui::AXInspectTestHelper::EventTestPasses();
   }
 
   template <ApiTypeVector TestPasses(), ui::AXApiType::TypeConstant type>
-  static ParamVector TestPassesExcept() {
+  static ApiTypeVector TestPassesExcept() {
     ApiTypeVector passes = TestPasses();
     base::Erase(passes, type);
-    return TestParams(passes);
+    return passes;
   }
 
   template <ui::AXApiType::TypeConstant type>
-  static ParamVector TreeTestPassesExcept() {
+  static ApiTypeVector TreeTestPassesExcept() {
     return TestPassesExcept<ui::AXInspectTestHelper::TreeTestPasses, type>();
   }
 
   template <ui::AXApiType::TypeConstant type>
-  static ParamVector EventTestPassesExcept() {
+  static ApiTypeVector EventTestPassesExcept() {
     return TestPassesExcept<ui::AXInspectTestHelper::EventTestPasses, type>();
   }
 
-  static ParamVector TreeTestPassesExceptUIA() {
+  static ApiTypeVector TreeTestPassesExceptUIA() {
     return TreeTestPassesExcept<ui::AXApiType::kWinUIA>();
   }
 
-  static ParamVector EventTestPassesExceptUIA() {
+  static ApiTypeVector EventTestPassesExceptUIA() {
     return EventTestPassesExcept<ui::AXApiType::kWinUIA>();
   }
 
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
index 46f26fd..5f52afcf 100644
--- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -174,9 +174,8 @@
 // Parameterize the tests so that each test-pass is run independently.
 struct DumpAccessibilityEventsTestPassToString {
   std::string operator()(
-      const ::testing::TestParamInfo<std::pair<ui::AXApiType::Type, bool>>& i)
-      const {
-    return std::string(i.param.first) + (i.param.second ? "1" : "0");
+      const ::testing::TestParamInfo<ui::AXApiType::Type>& i) const {
+    return std::string(i.param);
   }
 };
 
diff --git a/content/browser/accessibility/dump_accessibility_node_browsertest.cc b/content/browser/accessibility/dump_accessibility_node_browsertest.cc
index d2594e7..afab9522 100644
--- a/content/browser/accessibility/dump_accessibility_node_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_node_browsertest.cc
@@ -32,7 +32,7 @@
 
   std::vector<ui::AXPropertyFilter> DefaultFilters() const override {
     std::vector<AXPropertyFilter> property_filters;
-    if (GetParam().first == ui::AXApiType::kMac) {
+    if (GetParam() == ui::AXApiType::kMac) {
       return property_filters;
     }
 
@@ -97,7 +97,7 @@
  public:
   std::vector<ui::AXPropertyFilter> DefaultFilters() const override {
     std::vector<AXPropertyFilter> property_filters;
-    if (GetParam().first == ui::AXApiType::kMac) {
+    if (GetParam() == ui::AXApiType::kMac) {
       return property_filters;
     }
 
@@ -159,9 +159,8 @@
 // Parameterize the tests so that each test-pass is run independently.
 struct TestPassToString {
   std::string operator()(
-      const ::testing::TestParamInfo<std::pair<ui::AXApiType::Type, bool>>& i)
-      const {
-    return std::string(i.param.first) + (i.param.second ? "1" : "0");
+      const ::testing::TestParamInfo<ui::AXApiType::Type>& i) const {
+    return std::string(i.param);
   }
 };
 
diff --git a/content/browser/accessibility/dump_accessibility_scripts_browsertest.cc b/content/browser/accessibility/dump_accessibility_scripts_browsertest.cc
index 18c1c1b..9b69332 100644
--- a/content/browser/accessibility/dump_accessibility_scripts_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_scripts_browsertest.cc
@@ -180,13 +180,11 @@
   }
 };
 
-typedef std::pair<ui::AXApiType::Type, bool> TestParamType;
-
 // Parameterize the tests so that each test-pass is run independently.
 struct TestPassToString {
   std::string operator()(
-      const ::testing::TestParamInfo<TestParamType>& i) const {
-    return std::string(i.param.first) + (i.param.second ? "1" : "0");
+      const ::testing::TestParamInfo<ui::AXApiType::Type>& i) const {
+    return std::string(i.param);
   }
 };
 
@@ -196,12 +194,10 @@
 
 #if BUILDFLAG(IS_MAC)
 
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    DumpAccessibilityScriptTest,
-    ::testing::Values(TestParamType(ui::AXApiType::kMac, true),
-                      TestParamType(ui::AXApiType::kMac, false)),
-    TestPassToString());
+INSTANTIATE_TEST_SUITE_P(All,
+                         DumpAccessibilityScriptTest,
+                         ::testing::Values(ui::AXApiType::kMac),
+                         TestPassToString());
 
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityScriptTest, AXAccessKey) {
   RunTypedTest<kMacAttributes>("ax-access-key.html");
@@ -614,12 +610,10 @@
 
 #if BUILDFLAG(IS_WIN)
 
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    DumpAccessibilityScriptTest,
-    ::testing::Values(TestParamType(ui::AXApiType::kWinIA2, true),
-                      TestParamType(ui::AXApiType::kWinIA2, false)),
-    TestPassToString());
+INSTANTIATE_TEST_SUITE_P(All,
+                         DumpAccessibilityScriptTest,
+                         ::testing::Values(ui::AXApiType::kWinIA2),
+                         TestPassToString());
 
 // IAccessible
 
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index ea9cb736..be3d582 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -56,7 +56,7 @@
 std::vector<ui::AXPropertyFilter> DumpAccessibilityTreeTest::DefaultFilters()
     const {
   std::vector<AXPropertyFilter> property_filters;
-  if (GetParam().first == ui::AXApiType::kMac) {
+  if (GetParam() == ui::AXApiType::kMac) {
     return property_filters;
   }
 
@@ -146,9 +146,8 @@
 // Parameterize the tests so that each test-pass is run independently.
 struct DumpAccessibilityTreeTestPassToString {
   std::string operator()(
-      const ::testing::TestParamInfo<std::pair<ui::AXApiType::Type, bool>>& i)
-      const {
-    return std::string(i.param.first) + (i.param.second ? "1" : "0");
+      const ::testing::TestParamInfo<ui::AXApiType::Type>& i) const {
+    return std::string(i.param);
   }
 };
 
@@ -3121,18 +3120,9 @@
 }
 
 // Flaky on Android and Fuchsia - crbug.com/1286650, crbug.com/1491059
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
-#define MAYBE_AccessibilitySelectList DISABLED_AccessibilitySelectList
-#else
-#define MAYBE_AccessibilitySelectList AccessibilitySelectList
-#endif
+// crbug.com/1401767
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
-                       MAYBE_AccessibilitySelectList) {
-  // Fails when synchronous a11y serialization is enabled - crbug.com/1401767
-  if (base::FeatureList::IsEnabled(
-          blink::features::kSerializeAccessibilityPostLifecycle)) {
-    return;
-  }
+                       DISABLED_AccessibilitySelectList) {
   RunHtmlTest(FILE_PATH_LITERAL("selectlist.html"));
 }
 
@@ -3491,7 +3481,7 @@
 #if BUILDFLAG(IS_MAC)
   // The /blink test pass is different on macOS than on other platforms. See
   // https://crbug.com/1314896.
-  if (GetParam().first == ui::AXApiType::kBlink) {
+  if (GetParam() == ui::AXApiType::kBlink) {
     return;
   }
 #endif
@@ -3502,7 +3492,7 @@
 #if BUILDFLAG(IS_MAC)
   // The /blink test pass is different on macOS than on other platforms. See
   // https://crbug.com/1314896.
-  if (GetParam().first == ui::AXApiType::kBlink) {
+  if (GetParam() == ui::AXApiType::kBlink) {
     return;
   }
 #endif
diff --git a/content/browser/android/content_feature_map.cc b/content/browser/android/content_feature_map.cc
index c0b4ac1..370d9c4 100644
--- a/content/browser/android/content_feature_map.cc
+++ b/content/browser/android/content_feature_map.cc
@@ -25,7 +25,6 @@
     &features::kAccessibilityPageZoom,
     &features::kAccessibilityPerformanceFiltering,
     &features::kAutoDisableAccessibilityV2,
-    &features::kBackgroundMediaRendererHasModerateBinding,
     &features::kFedCm,
     &features::kMouseAndTrackpadDropdownMenu,
     &features::kOptimizeImmHideCalls,
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc
index cefb0c1..19c7d09 100644
--- a/content/browser/devtools/protocol/storage_handler.cc
+++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -1079,7 +1079,7 @@
   if (group.ads) {
     for (const auto& ad : *group.ads) {
       auto protocol_ad = protocol::Storage::InterestGroupAd::Create()
-                             .SetRenderURL(ad.render_url.spec())
+                             .SetRenderURL(ad.render_url())
                              .Build();
       if (ad.metadata) {
         protocol_ad->SetMetadata(*ad.metadata);
@@ -1092,7 +1092,7 @@
   if (group.ad_components) {
     for (const auto& ad : *group.ad_components) {
       auto protocol_ad = protocol::Storage::InterestGroupAd::Create()
-                             .SetRenderURL(ad.render_url.spec())
+                             .SetRenderURL(ad.render_url())
                              .Build();
       if (ad.metadata) {
         protocol_ad->SetMetadata(*ad.metadata);
diff --git a/content/browser/first_party_sets/first_party_set_parser.cc b/content/browser/first_party_sets/first_party_set_parser.cc
index 5e2d4fb..c1297b8 100644
--- a/content/browser/first_party_sets/first_party_set_parser.cc
+++ b/content/browser/first_party_sets/first_party_set_parser.cc
@@ -54,6 +54,8 @@
 constexpr char kFirstPartySetPolicyReplacementsField[] = "replacements";
 constexpr char kFirstPartySetPolicyAdditionsField[] = "additions";
 
+constexpr int kFirstPartySetsMaxAssociatedSites = 5;
+
 enum class PolicySetType { kReplacement, kAddition };
 
 const char* SetTypeToString(PolicySetType set_type) {
@@ -248,12 +250,10 @@
              SubsetDescriptor{
                  .field_name = kFirstPartySetAssociatedSitesField,
                  .site_type = net::SiteType::kAssociated,
-                 .size_limit =
-                     exempt_from_limits_
-                         ? absl::nullopt
-                         : absl::make_optional(
-                               features::kFirstPartySetsMaxAssociatedSites
-                                   .Get()),
+                 .size_limit = exempt_from_limits_
+                                   ? absl::nullopt
+                                   : absl::make_optional(
+                                         kFirstPartySetsMaxAssociatedSites),
              },
              {
                  .field_name = kFirstPartySetServiceSitesField,
diff --git a/content/browser/first_party_sets/first_party_set_parser.h b/content/browser/first_party_sets/first_party_set_parser.h
index be00b0e7..a98b2ff 100644
--- a/content/browser/first_party_sets/first_party_set_parser.h
+++ b/content/browser/first_party_sets/first_party_set_parser.h
@@ -42,8 +42,7 @@
   // received by Component Updater.
   //
   // Returns an empty GlobalFirstPartySets instance if parsing or validation of
-  // any set failed. Must not be called before field trial state has been
-  // initialized.
+  // any set failed.
   static net::GlobalFirstPartySets ParseSetsFromStream(std::istream& input,
                                                        base::Version version,
                                                        bool emit_errors,
diff --git a/content/browser/first_party_sets/first_party_set_parser_unittest.cc b/content/browser/first_party_sets/first_party_set_parser_unittest.cc
index cb85e3c..0806f7f 100644
--- a/content/browser/first_party_sets/first_party_set_parser_unittest.cc
+++ b/content/browser/first_party_sets/first_party_set_parser_unittest.cc
@@ -8,7 +8,6 @@
 
 #include "base/json/json_reader.h"
 #include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/version.h"
 #include "content/public/browser/first_party_sets_handler.h"
 #include "content/public/common/content_features.h"
@@ -1552,27 +1551,20 @@
           {kReplacementsField, 0, kCctldsField, "https://not_in_set.test"})});
 }
 
-class FirstPartySetParserTest : public ::testing::Test {
- public:
-  FirstPartySetParserTest() {
-    features_.InitWithFeaturesAndParameters(
-        {{features::kFirstPartySets,
-          {{features::kFirstPartySetsMaxAssociatedSites.name, "1"}}}},
-        {});
-  }
-
- private:
-  base::test::ScopedFeatureList features_;
-};
-
-TEST_F(FirstPartySetParserTest, RespectsAssociatedSiteLimit) {
+TEST(FirstPartySetParser, RespectsAssociatedSiteLimit) {
   net::SchemefulSite example(GURL("https://example.test"));
   net::SchemefulSite a(GURL("https://a.test"));
+  net::SchemefulSite b(GURL("https://b.test"));
+  net::SchemefulSite c(GURL("https://c.test"));
+  net::SchemefulSite d(GURL("https://d.test"));
+  net::SchemefulSite e(GURL("https://e.test"));
 
   EXPECT_EQ(
-      ParseSets(R"({"primary": "https://example.test",)"
-                R"("associatedSites": ["https://a.test", "https://b.test"],)"
-                R"(})"),
+      ParseSets(
+          R"({"primary": "https://example.test",)"
+          R"("associatedSites": ["https://a.test", "https://b.test",)"
+          R"("https://c.test", "https://d.test", "https://e.test", "https://f.test"],)"
+          R"(})"),
       net::GlobalFirstPartySets(
           kVersion,
           {
@@ -1580,11 +1572,19 @@
                             example, net::SiteType::kPrimary, absl::nullopt)},
               {a,
                net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
+              {b,
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
+              {c,
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 2)},
+              {d,
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 3)},
+              {e,
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 4)},
           },
           {}));
 }
 
-TEST_F(FirstPartySetParserTest, DetectsErrorsPastAssociatedSiteLimit) {
+TEST(FirstPartySetParser, DetectsErrorsPastAssociatedSiteLimit) {
   EXPECT_EQ(
       ParseSets(R"({"primary": "https://example.test",)"
                 R"("associatedSites": ["https://a.test", "not a domain"],)"
@@ -1592,17 +1592,21 @@
       kEmptySets);
 }
 
-TEST_F(FirstPartySetParserTest,
-       ServiceSitesAreNotCountedAgainstAssociatedSiteLimit) {
+TEST(FirstPartySetParser, ServiceSitesAreNotCountedAgainstAssociatedSiteLimit) {
   net::SchemefulSite example(GURL("https://example.test"));
   net::SchemefulSite a(GURL("https://a.test"));
   net::SchemefulSite b(GURL("https://b.test"));
   net::SchemefulSite c(GURL("https://c.test"));
+  net::SchemefulSite d(GURL("https://d.test"));
+  net::SchemefulSite e(GURL("https://e.test"));
+  net::SchemefulSite f(GURL("https://f.test"));
+  net::SchemefulSite g(GURL("https://g.test"));
 
   EXPECT_EQ(
       ParseSets(R"({)"
                 R"("primary": "https://example.test",)"
-                R"("associatedSites": ["https://a.test"],)"
+                R"("associatedSites": ["https://a.test", "https://d.test",)"
+                R"("https://e.test", "https://f.test", "https://g.test"],)"
                 R"("serviceSites": ["https://b.test", "https://c.test"],)"
                 R"(})"),
       net::GlobalFirstPartySets(
@@ -1612,6 +1616,14 @@
                             example, net::SiteType::kPrimary, absl::nullopt)},
               {a,
                net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
+              {d,
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
+              {e,
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 2)},
+              {f,
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 3)},
+              {g,
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 4)},
               {b, net::FirstPartySetEntry(example, net::SiteType::kService,
                                           absl::nullopt)},
               {c, net::FirstPartySetEntry(example, net::SiteType::kService,
@@ -1620,10 +1632,13 @@
           {}));
 }
 
-TEST_F(FirstPartySetParserTest,
-       AliasesAreNotCountedAgainstAssociatedSiteLimit) {
+TEST(FirstPartySetParser, AliasesAreNotCountedAgainstAssociatedSiteLimit) {
   net::SchemefulSite example(GURL("https://example.test"));
   net::SchemefulSite a(GURL("https://a.test"));
+  net::SchemefulSite b(GURL("https://b.test"));
+  net::SchemefulSite c(GURL("https://c.test"));
+  net::SchemefulSite d(GURL("https://d.test"));
+  net::SchemefulSite e(GURL("https://e.test"));
   net::SchemefulSite a_cctld1(GURL("https://a.cctld1"));
   net::SchemefulSite a_cctld2(GURL("https://a.cctld2"));
 
@@ -1631,7 +1646,8 @@
       ParseSets(
           R"({)"
           R"("primary": "https://example.test",)"
-          R"("associatedSites": ["https://a.test", "https://b.test"],)"
+          R"("associatedSites": ["https://a.test", "https://b.test",)"
+          R"("https://c.test", "https://d.test", "https://e.test"],)"
           R"("ccTLDs": {)"
           R"(  "https://a.test": ["https://a.cctld1", "https://a.cctld2"])"
           R"(})"
@@ -1643,22 +1659,40 @@
                             example, net::SiteType::kPrimary, absl::nullopt)},
               {a,
                net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
+              {b,
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
+              {c,
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 2)},
+              {d,
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 3)},
+              {e,
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 4)},
           },
           {{a_cctld1, a}, {a_cctld2, a}}));
 }
 
-TEST_F(FirstPartySetParserTest,
-       EnterprisePolicies_ExemptFromAssociatedSiteLimit) {
+TEST(FirstPartySetParser, EnterprisePolicies_ExemptFromAssociatedSiteLimit) {
   net::SchemefulSite primary1(GURL("https://primary1.test"));
   net::SchemefulSite associated1(GURL("https://associated1.test"));
   net::SchemefulSite associated2(GURL("https://associated2.test"));
+  net::SchemefulSite associated3(GURL("https://associated3.test"));
+  net::SchemefulSite associated4(GURL("https://associated4.test"));
+  net::SchemefulSite associated5(GURL("https://associated5.test"));
+  net::SchemefulSite associated6(GURL("https://associated6.test"));
 
   base::Value policy_value = base::JSONReader::Read(R"(
              {
                 "replacements": [
                   {
                     "primary": "https://primary1.test",
-                    "associatedSites": ["https://associated1.test", "https://associated2.test"]
+                    "associatedSites": [
+                      "https://associated1.test",
+                      "https://associated2.test",
+                      "https://associated3.test",
+                      "https://associated4.test",
+                      "https://associated5.test",
+                      "https://associated6.test"
+                    ]
                   }
                 ]
               }
@@ -1677,6 +1711,18 @@
               {associated2,
                net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
                                        absl::nullopt)},
+              {associated3,
+               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
+                                       absl::nullopt)},
+              {associated4,
+               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
+                                       absl::nullopt)},
+              {associated5,
+               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
+                                       absl::nullopt)},
+              {associated6,
+               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
+                                       absl::nullopt)},
           }},
           {}));
 }
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl_instance.cc b/content/browser/first_party_sets/first_party_sets_handler_impl_instance.cc
index 8575098a..39efc17 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_impl_instance.cc
+++ b/content/browser/first_party_sets/first_party_sets_handler_impl_instance.cc
@@ -26,7 +26,6 @@
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/first_party_sets_handler.h"
 #include "content/public/common/content_client.h"
-#include "content/public/common/content_features.h"
 #include "net/base/features.h"
 #include "net/first_party_sets/first_party_set_metadata.h"
 #include "net/first_party_sets/first_party_sets_context_config.h"
@@ -325,8 +324,7 @@
     const net::SchemefulSite& site,
     const net::FirstPartySetsContextConfig& config) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!base::FeatureList::IsEnabled(features::kFirstPartySets) ||
-      !global_sets_.has_value()) {
+  if (!global_sets_.has_value()) {
     return absl::nullopt;
   }
   return global_sets_->FindEntry(site, config);
@@ -347,7 +345,7 @@
                             net::FirstPartySetsCacheFilter)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!enabled_ || !features::kFirstPartySetsClearSiteDataOnChangedSets.Get()) {
+  if (!enabled_) {
     std::move(callback).Run(std::move(context_config),
                             net::FirstPartySetsCacheFilter());
     return;
@@ -378,7 +376,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK(global_sets_.has_value());
   CHECK(!browser_context_id.empty());
-  CHECK(enabled_ && features::kFirstPartySetsClearSiteDataOnChangedSets.Get());
+  CHECK(enabled_);
 
   if (db_helper_.is_null()) {
     VLOG(1) << "Invalid First-Party Sets database. Failed to clear site data "
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl_instance_unittest.cc b/content/browser/first_party_sets/first_party_sets_handler_impl_instance_unittest.cc
index 17dd1c6..914a2946 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_impl_instance_unittest.cc
+++ b/content/browser/first_party_sets/first_party_sets_handler_impl_instance_unittest.cc
@@ -18,7 +18,6 @@
 #include "base/test/bind.h"
 #include "base/test/gmock_expected_support.h"
 #include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/version.h"
@@ -349,56 +348,7 @@
 }
 
 TEST_F(FirstPartySetsHandlerImplEnabledTest,
-       ClearSiteDataOnChangedSetsForContext_FeatureNotEnabled) {
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeatureWithParameters(
-      features::kFirstPartySets,
-      {{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "false"}});
-  base::HistogramTester histogram;
-  net::SchemefulSite foo(GURL("https://foo.test"));
-  net::SchemefulSite associated(GURL("https://associatedsite.test"));
-
-  const std::string browser_context_id = "profile";
-  const std::string input =
-      R"({"primary": "https://foo.test", )"
-      R"("associatedSites": ["https://associatedsite.test"]})";
-  ASSERT_TRUE(base::JSONReader::Read(input));
-  handler().SetPublicFirstPartySets(base::Version("0.0.1"),
-                                    WritePublicSetsFile(input));
-
-  handler().Init(scoped_dir_.GetPath(), net::LocalSetDeclaration());
-  ASSERT_THAT(GetSetsAndWait().FindEntries({foo, associated},
-                                           net::FirstPartySetsContextConfig()),
-              UnorderedElementsAre(
-                  Pair(foo, net::FirstPartySetEntry(
-                                foo, net::SiteType::kPrimary, absl::nullopt)),
-                  Pair(associated, net::FirstPartySetEntry(
-                                       foo, net::SiteType::kAssociated, 0))));
-
-  histogram.ExpectTotalCount(kDelayedQueriesCountHistogram, 1);
-  histogram.ExpectTotalCount(kMostDelayedQueryDeltaHistogram, 1);
-
-  ClearSiteDataOnChangedSetsForContextAndWait(
-      context(), browser_context_id, net::FirstPartySetsContextConfig());
-
-  absl::optional<
-      std::pair<net::GlobalFirstPartySets, net::FirstPartySetsContextConfig>>
-      persisted = GetPersistedSetsAndWait(browser_context_id);
-  EXPECT_TRUE(persisted.has_value());
-  EXPECT_THAT(
-      persisted->first.FindEntries({foo, associated}, persisted->second),
-      IsEmpty());
-  // Should not be recorded.
-  histogram.ExpectTotalCount(kFirstPartySetsClearSiteDataOutcomeHistogram, 0);
-}
-
-TEST_F(FirstPartySetsHandlerImplEnabledTest,
        ClearSiteDataOnChangedSetsForContext_ManualSet_Successful) {
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeatureWithParameters(
-      features::kFirstPartySets,
-      {{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "true"}});
-
   net::SchemefulSite foo(GURL("https://foo.test"));
   net::SchemefulSite associated(GURL("https://associatedsite.test"));
   net::SchemefulSite associated2(GURL("https://associatedsite2.test"));
@@ -442,11 +392,6 @@
 
 TEST_F(FirstPartySetsHandlerImplEnabledTest,
        ClearSiteDataOnChangedSetsForContext_PublicSetsWithDiff_Successful) {
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeatureWithParameters(
-      features::kFirstPartySets,
-      {{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "true"}});
-
   net::SchemefulSite foo(GURL("https://foo.test"));
   net::SchemefulSite associated(GURL("https://associatedsite.test"));
   net::SchemefulSite associated2(GURL("https://associatedsite2.test"));
@@ -542,11 +487,6 @@
 
 TEST_F(FirstPartySetsHandlerImplEnabledTest,
        ClearSiteDataOnChangedSetsForContext_EmptyDBPath) {
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeatureWithParameters(
-      features::kFirstPartySets,
-      {{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "true"}});
-
   base::HistogramTester histogram;
   net::SchemefulSite foo(GURL("https://foo.test"));
   net::SchemefulSite associated(GURL("https://associatedsite.test"));
@@ -581,11 +521,6 @@
 
 TEST_F(FirstPartySetsHandlerImplEnabledTest,
        ClearSiteDataOnChangedSetsForContext_BeforeSetsReady) {
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeatureWithParameters(
-      features::kFirstPartySets,
-      {{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "true"}});
-
   base::HistogramTester histogram;
 
   handler().Init(scoped_dir_.GetPath(), net::LocalSetDeclaration());
diff --git a/content/browser/first_party_sets/test/first_party_sets_initialization_browsertest.cc b/content/browser/first_party_sets/test/first_party_sets_initialization_browsertest.cc
index ba3986f..a6f9887 100644
--- a/content/browser/first_party_sets/test/first_party_sets_initialization_browsertest.cc
+++ b/content/browser/first_party_sets/test/first_party_sets_initialization_browsertest.cc
@@ -25,7 +25,7 @@
   FirstPartySetsDeadlockingQueriesBrowserTest()
       : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
     scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{features::kFirstPartySets},
+        /*enabled_features=*/{},
         /*disabled_features=*/{net::features::kWaitForFirstPartySetsInit});
   }
 
diff --git a/content/browser/first_party_sets/test/scoped_mock_first_party_sets_handler.cc b/content/browser/first_party_sets/test/scoped_mock_first_party_sets_handler.cc
index 03b6b43..85d9d2f4 100644
--- a/content/browser/first_party_sets/test/scoped_mock_first_party_sets_handler.cc
+++ b/content/browser/first_party_sets/test/scoped_mock_first_party_sets_handler.cc
@@ -6,12 +6,10 @@
 
 #include <string>
 
-#include "base/feature_list.h"
 #include "base/functional/callback.h"
 #include "base/task/sequenced_task_runner.h"
 #include "content/browser/first_party_sets/first_party_sets_handler_impl.h"
 #include "content/public/browser/first_party_sets_handler.h"
-#include "content/public/common/content_features.h"
 #include "net/first_party_sets/first_party_set_metadata.h"
 #include "net/first_party_sets/first_party_sets_cache_filter.h"
 #include "net/first_party_sets/first_party_sets_context_config.h"
@@ -41,9 +39,6 @@
 ScopedMockFirstPartySetsHandler::FindEntry(
     const net::SchemefulSite& site,
     const net::FirstPartySetsContextConfig& config) const {
-  if (!base::FeatureList::IsEnabled(features::kFirstPartySets)) {
-    return absl::nullopt;
-  }
   return global_sets_.FindEntry(site, config);
 }
 
diff --git a/content/browser/interest_group/ad_auction_service_impl_unittest.cc b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
index 2b9bfdb..dba97672 100644
--- a/content/browser/interest_group/ad_auction_service_impl_unittest.cc
+++ b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
@@ -1563,7 +1563,7 @@
                          kOriginStringA));
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             base::StringPrintf("%s/new_ad_render_url", kOriginStringA));
   EXPECT_EQ(group.ads.value()[0].size_group, "group_new");
   EXPECT_EQ(group.ads.value()[0].metadata, "{\"new_a\":\"b\"}");
@@ -1576,7 +1576,7 @@
               ::testing::UnorderedElementsAre(kOriginF, kOriginG));
   ASSERT_TRUE(group.ad_components.has_value());
   ASSERT_EQ(group.ad_components->size(), 1u);
-  EXPECT_EQ(group.ad_components.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ad_components.value()[0].render_url(),
             "https://example.com/component_url");
   EXPECT_EQ(group.ad_components.value()[0].size_group, "group_new");
   EXPECT_EQ(group.ad_components.value()[0].metadata, "{\"new_c\":\"d\"}");
@@ -1817,7 +1817,7 @@
                 kAllSlotsRequestedSizes);
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             base::StringPrintf("%s/new_ad_render_url", kOriginStringA));
   EXPECT_EQ(group.ads.value()[0].metadata, "{\"new_a\":\"b\"}");
 }
@@ -1862,7 +1862,7 @@
   EXPECT_EQ(group.expiry, kExpirationTime);
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 }
 
@@ -1963,7 +1963,7 @@
   EXPECT_EQ(group.trusted_bidding_signals_keys.value()[0], "key1");
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             base::StringPrintf("%s/new_ad_render_url", kOriginStringA));
 }
 
@@ -2034,10 +2034,10 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             "https://example.com/new_render");
   ASSERT_EQ(group.ad_components->size(), 1u);
-  EXPECT_EQ(group.ad_components.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ad_components.value()[0].render_url(),
             "https://example.com/new_component");
 }
 
@@ -2075,8 +2075,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 }
 
 // Try to set the owner -- for security, name and owner shouldn't be
@@ -2115,8 +2114,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 }
 
 TEST_F(AdAuctionServiceImplTest, UpdatePriorityVector) {
@@ -2312,13 +2310,13 @@
   EXPECT_EQ(first_group.name, kGroupName1);
   ASSERT_TRUE(first_group.ads.has_value());
   ASSERT_EQ(first_group.ads->size(), 1u);
-  EXPECT_EQ(first_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(first_group.ads.value()[0].render_url(),
             "https://example.com/new_render1");
 
   EXPECT_EQ(second_group.name, kGroupName2);
   ASSERT_TRUE(second_group.ads.has_value());
   ASSERT_EQ(second_group.ads->size(), 1u);
-  EXPECT_EQ(second_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(second_group.ads.value()[0].render_url(),
             "https://example.com/new_render2");
 }
 
@@ -3473,7 +3471,7 @@
   EXPECT_EQ(origin_b_group.name, kInterestGroupName);
   ASSERT_TRUE(origin_b_group.ads.has_value());
   ASSERT_EQ(origin_b_group.ads->size(), 1u);
-  EXPECT_EQ(origin_b_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(origin_b_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 
   // ...but the `kOriginA` interest group shouldn't change.
@@ -3484,7 +3482,7 @@
       origin_a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(origin_a_group.ads.has_value());
   ASSERT_EQ(origin_a_group.ads->size(), 1u);
-  EXPECT_EQ(origin_a_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(origin_a_group.ads.value()[0].render_url(),
             "https://example.com/render");
 }
 
@@ -3571,7 +3569,7 @@
   EXPECT_EQ(origin_c_group.name, kInterestGroupName);
   ASSERT_TRUE(origin_c_group.ads.has_value());
   ASSERT_EQ(origin_c_group.ads->size(), 1u);
-  EXPECT_EQ(origin_c_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(origin_c_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 
   // ...but the `kOriginA` interest group shouldn't change.
@@ -3582,7 +3580,7 @@
       origin_a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(origin_a_group.ads.has_value());
   ASSERT_EQ(origin_a_group.ads->size(), 1u);
-  EXPECT_EQ(origin_a_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(origin_a_group.ads.value()[0].render_url(),
             "https://example.com/render");
 
   // Now try on disallowed subframe from originB.
@@ -3602,7 +3600,7 @@
       origin_b_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(origin_b_group.ads.has_value());
   ASSERT_EQ(origin_b_group.ads->size(), 1u);
-  EXPECT_EQ(origin_b_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(origin_b_group.ads.value()[0].render_url(),
             "https://example.com/render");
 }
 
@@ -3657,8 +3655,7 @@
     const auto& group = groups->GetInterestGroups()[0]->interest_group;
     ASSERT_TRUE(group.ads.has_value());
     ASSERT_EQ(group.ads->size(), 1u);
-    EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-              "https://example.com/render");
+    EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
     EXPECT_EQ(group.ads.value()[0].metadata,
               "{\"ad\":\"metadata\",\"here\":[1,2,3]}");
     EXPECT_EQ(group.bidding_url, kBiddingLogicUrlA);
@@ -3704,8 +3701,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
   EXPECT_EQ(group.ads.value()[0].metadata,
             "{\"ad\":\"metadata\",\"here\":[1,2,3]}");
   EXPECT_FALSE(group.ads.value()[0].allowed_reporting_origins.has_value());
@@ -3823,8 +3819,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 }
 
 // UpdateJSONParserCrash fails on Android or with the Rust parser because in
@@ -3872,8 +3867,7 @@
   auto group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Try another IG update, this time with no crash. It should succceed.
   // (We need to advance time since this next attempt is rate-limited).
@@ -3890,7 +3884,7 @@
   group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 }
 
@@ -3925,8 +3919,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 
   // There shouldn't have even been an attempt to update.
   EXPECT_EQ(network_responder_->UpdateCount(), 0u);
@@ -3958,8 +3951,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 }
 
 // The network request for updating interest groups times out, so the update
@@ -3991,8 +3983,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 }
 
 // Start an update, and delay the server response so that the interest group
@@ -4178,8 +4169,7 @@
   const auto& a_group = a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(a_group.ads.has_value());
   ASSERT_EQ(a_group.ads->size(), 1u);
-  EXPECT_EQ(a_group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(a_group.ads.value()[0].render_url(), "https://example.com/render");
 
   // The test ends while the update is in progress. Nothing should crash as we
   // run destructors.
@@ -4216,8 +4206,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 }
 
 // Register a bid and a win, then perform a successful update. The bid and win
@@ -4274,7 +4263,7 @@
   EXPECT_EQ(group.name, kInterestGroupName);
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 }
 
@@ -4313,7 +4302,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 
   // Change the update response and try updating again.
@@ -4331,7 +4320,7 @@
   const auto& group2 = groups2->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group2.ads.has_value());
   ASSERT_EQ(group2.ads->size(), 1u);
-  EXPECT_EQ(group2.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group2.ads.value()[0].render_url(),
             "https://example.com/new_render");
 
   // Advance time to just before end of rate limit period. Update should still
@@ -4349,7 +4338,7 @@
   const auto& group3 = groups3->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group3.ads.has_value());
   ASSERT_EQ(group3.ads->size(), 1u);
-  EXPECT_EQ(group3.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group3.ads.value()[0].render_url(),
             "https://example.com/new_render");
 
   // Advance time to just after end of rate limit period. Update should now
@@ -4366,7 +4355,7 @@
   const auto& group4 = groups4->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group4.ads.has_value());
   ASSERT_EQ(group4.ads->size(), 1u);
-  EXPECT_EQ(group4.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group4.ads.value()[0].render_url(),
             "https://example.com/new_render");
 }
 
@@ -4405,8 +4394,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Change the update response and try updating again.
   network_responder_->RegisterUpdateResponse(kUpdateUrlPath, R"({
@@ -4423,8 +4411,7 @@
   const auto& group2 = groups2->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group2.ads.has_value());
   ASSERT_EQ(group2.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Advance time to just before end of rate limit period. Update should still
   // do nothing due to rate limiting. Invalid responses use the longer
@@ -4442,8 +4429,7 @@
   const auto& group3 = groups3->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group3.ads.has_value());
   ASSERT_EQ(group3.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Advance time to just after end of rate limit period. Update should now
   // succeed.
@@ -4459,7 +4445,7 @@
   const auto& group4 = groups4->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group4.ads.has_value());
   ASSERT_EQ(group4.ads->size(), 1u);
-  EXPECT_EQ(group4.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group4.ads.value()[0].render_url(),
             "https://example.com/new_render");
 }
 
@@ -4508,8 +4494,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
   EXPECT_FALSE(group.ads.value()[0].allowed_reporting_origins.has_value());
 
   // Change the allowedReportingOrigins to attested origins and try updating
@@ -4529,8 +4514,7 @@
   const auto& group2 = groups2->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group2.ads.has_value());
   ASSERT_EQ(group2.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
   EXPECT_FALSE(group.ads.value()[0].allowed_reporting_origins.has_value());
 
   // Advance time to just before end of rate limit period. Update should still
@@ -4549,8 +4533,7 @@
   const auto& group3 = groups3->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group3.ads.has_value());
   ASSERT_EQ(group3.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
   EXPECT_FALSE(group.ads.value()[0].allowed_reporting_origins.has_value());
 
   // Advance time to just after end of rate limit period. Update should now
@@ -4567,7 +4550,7 @@
   const auto& group4 = groups4->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group4.ads.has_value());
   ASSERT_EQ(group4.ads->size(), 1u);
-  EXPECT_EQ(group4.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group4.ads.value()[0].render_url(),
             "https://example.com/new_render");
   std::vector<url::Origin> allowed_reporting_origins = {kOriginB};
   EXPECT_EQ(group4.ads.value()[0].allowed_reporting_origins,
@@ -4607,8 +4590,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Change the update response and try updating again.
   network_responder_->RegisterUpdateResponse(kUpdateUrlPath, R"({
@@ -4625,8 +4607,7 @@
   const auto& group2 = groups2->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group2.ads.has_value());
   ASSERT_EQ(group2.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Advance time to just before end of rate limit period. Update should still
   // do nothing due to rate limiting.
@@ -4643,8 +4624,7 @@
   const auto& group3 = groups3->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group3.ads.has_value());
   ASSERT_EQ(group3.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Advance time to just after end of rate limit period. Update should now
   // succeed.
@@ -4660,7 +4640,7 @@
   const auto& group4 = groups4->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group4.ads.has_value());
   ASSERT_EQ(group4.ads->size(), 1u);
-  EXPECT_EQ(group4.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group4.ads.value()[0].render_url(),
             "https://example.com/new_render");
 }
 
@@ -4698,8 +4678,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Change the update response and try updating again.
   network_responder_->RegisterUpdateResponse(kUpdateUrlPath, R"({
@@ -4716,7 +4695,7 @@
   const auto& group2 = groups2->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group2.ads.has_value());
   ASSERT_EQ(group2.ads->size(), 1u);
-  EXPECT_EQ(group2.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group2.ads.value()[0].render_url(),
             "https://example.com/new_render");
 }
 
@@ -4792,8 +4771,7 @@
           : groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group_2.ads.has_value());
   ASSERT_EQ(group_2.ads->size(), 1u);
-  EXPECT_EQ(group_2.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(group_2.ads.value()[0].render_url(), "https://example.com/render");
 
   // Now, try to update both interest groups. Both should now succeed.
   const std::string kServerResponse2 = R"({
@@ -4819,12 +4797,12 @@
 
   ASSERT_TRUE(group_1.ads.has_value());
   ASSERT_EQ(group_1.ads->size(), 1u);
-  EXPECT_EQ(group_1.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group_1.ads.value()[0].render_url(),
             "https://example.com/new_render");
 
   ASSERT_TRUE(group_2.ads.has_value());
   ASSERT_EQ(group_2.ads->size(), 1u);
-  EXPECT_EQ(group_2.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group_2.ads.value()[0].render_url(),
             "https://example.com/new_render2");
 }
 
@@ -4864,7 +4842,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 }
 
@@ -4949,8 +4927,7 @@
   auto a_group = a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(a_group.ads.has_value());
   ASSERT_EQ(a_group.ads->size(), 1u);
-  EXPECT_EQ(a_group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(a_group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Now, try to update kOriginB's interest groups. The update shouldn't happen
   // yet, because we're still updating kOriginA's interest groups.
@@ -4963,8 +4940,7 @@
   auto b_group = b_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(b_group.ads.has_value());
   ASSERT_EQ(b_group.ads->size(), 1u);
-  EXPECT_EQ(b_group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(b_group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Now, try to update kOriginC's interest groups. The update shouldn't happen
   // yet, because we're still updating kOriginA's interest groups.
@@ -4977,8 +4953,7 @@
   auto c_group = c_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(c_group.ads.has_value());
   ASSERT_EQ(c_group.ads->size(), 1u);
-  EXPECT_EQ(c_group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(c_group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Only one network request should have been made (for the kOriginA update).
   EXPECT_EQ(network_responder_->UpdateCount(), 1u);
@@ -4997,7 +4972,7 @@
   a_group = a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(a_group.ads.has_value());
   ASSERT_EQ(a_group.ads->size(), 1u);
-  EXPECT_EQ(a_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(a_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 
   // kOriginB's groups have updated.
@@ -5006,7 +4981,7 @@
   b_group = b_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(b_group.ads.has_value());
   ASSERT_EQ(b_group.ads->size(), 1u);
-  EXPECT_EQ(b_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(b_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 
   // kOriginC's groups have updated.
@@ -5015,7 +4990,7 @@
   b_group = b_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(b_group.ads.has_value());
   ASSERT_EQ(b_group.ads->size(), 1u);
-  EXPECT_EQ(b_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(b_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 }
 
@@ -5085,7 +5060,7 @@
   for (const SingleStorageInterestGroup& group : groups->GetInterestGroups()) {
     ASSERT_TRUE(group->interest_group.ads.has_value());
     ASSERT_EQ(group->interest_group.ads->size(), 1u);
-    EXPECT_EQ(group->interest_group.ads.value()[0].render_url.spec(),
+    EXPECT_EQ(group->interest_group.ads.value()[0].render_url(),
               "https://example.com/new_render");
   }
 }
@@ -5177,10 +5152,9 @@
     ASSERT_EQ(ads->size(), 1u);
 
     if (group->interest_group.update_url == kUpdateUrlA) {
-      EXPECT_EQ(ads.value()[0].render_url.spec(),
-                "https://example.com/new_render");
+      EXPECT_EQ(ads.value()[0].render_url(), "https://example.com/new_render");
     } else {
-      EXPECT_EQ(ads.value()[0].render_url.spec(), "https://example.com/render");
+      EXPECT_EQ(ads.value()[0].render_url(), "https://example.com/render");
     }
   }
 }
@@ -5261,8 +5235,7 @@
   auto a_group = a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(a_group.ads.has_value());
   ASSERT_EQ(a_group.ads->size(), 1u);
-  EXPECT_EQ(a_group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(a_group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Now, try to update kOriginB's interest groups. The update shouldn't happen
   // yet, because we're still updating kOriginA's interest groups.
@@ -5275,8 +5248,7 @@
   auto b_group = b_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(b_group.ads.has_value());
   ASSERT_EQ(b_group.ads->size(), 1u);
-  EXPECT_EQ(b_group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(b_group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Only one network request should have been made (for the kOriginA update).
   EXPECT_EQ(network_responder_->UpdateCount(), 1u);
@@ -5301,7 +5273,7 @@
   a_group = a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(a_group.ads.has_value());
   ASSERT_EQ(a_group.ads->size(), 1u);
-  EXPECT_EQ(a_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(a_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 
   // But kOriginB's groups have not updated, because they got cancelled.
@@ -5310,8 +5282,7 @@
   b_group = b_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(b_group.ads.has_value());
   ASSERT_EQ(b_group.ads->size(), 1u);
-  EXPECT_EQ(b_group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(b_group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Now, try updating kOriginB. The update should complete successfully.
   network_responder_->RegisterUpdateResponse(kUpdateUrlPathB, R"({
@@ -5329,7 +5300,7 @@
   b_group = b_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(b_group.ads.has_value());
   ASSERT_EQ(b_group.ads->size(), 1u);
-  EXPECT_EQ(b_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(b_group.ads.value()[0].render_url(),
             "https://example.com/newer_render");
 }
 
@@ -5405,8 +5376,7 @@
   auto a_group = a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(a_group.ads.has_value());
   ASSERT_EQ(a_group.ads->size(), 1u);
-  EXPECT_EQ(a_group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(a_group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Now, try to update kOriginB's interest groups. The update shouldn't happen
   // yet, because we're still updating kOriginA's interest groups.
@@ -5419,8 +5389,7 @@
   auto b_group = b_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(b_group.ads.has_value());
   ASSERT_EQ(b_group.ads->size(), 1u);
-  EXPECT_EQ(b_group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(b_group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Only one network request should have been made (for the kOriginA update).
   EXPECT_EQ(network_responder_->UpdateCount(), 1u);
@@ -5445,7 +5414,7 @@
   a_group = a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(a_group.ads.has_value());
   ASSERT_EQ(a_group.ads->size(), 1u);
-  EXPECT_EQ(a_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(a_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 
   // But kOriginB's groups have not updated, because they got cancelled.
@@ -5454,8 +5423,7 @@
   b_group = b_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(b_group.ads.has_value());
   ASSERT_EQ(b_group.ads->size(), 1u);
-  EXPECT_EQ(b_group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(b_group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Now, try updating a new origin, kOriginC. The update should complete
   // successfully.
@@ -5488,7 +5456,7 @@
   auto c_group = c_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(c_group.ads.has_value());
   ASSERT_EQ(c_group.ads->size(), 1u);
-  EXPECT_EQ(c_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(c_group.ads.value()[0].render_url(),
             "https://example.com/newer_render");
 
   // But kOriginB's groups have not updated.
@@ -5497,8 +5465,7 @@
   b_group = b_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(b_group.ads.has_value());
   ASSERT_EQ(b_group.ads->size(), 1u);
-  EXPECT_EQ(b_group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(b_group.ads.value()[0].render_url(), "https://example.com/render");
 }
 
 // After a round of updates completes, the round cancellation timer should reset
@@ -5566,7 +5533,7 @@
   auto a_group = a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(a_group.ads.has_value());
   ASSERT_EQ(a_group.ads->size(), 1u);
-  EXPECT_EQ(a_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(a_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 
   // Only one network request should have been made (for the kOriginA update).
@@ -5587,7 +5554,7 @@
   auto b_group = b_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(b_group.ads.has_value());
   ASSERT_EQ(b_group.ads->size(), 1u);
-  EXPECT_EQ(b_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(b_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 
   // Two network requests should have been made (for the kOriginA and kOriginB
@@ -5691,7 +5658,7 @@
     ASSERT_TRUE(group.ads.has_value());
     ASSERT_EQ(group.ads->size(), 1u);
     if (group.name == kUpdateUrlA.path()) {
-      EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+      EXPECT_EQ(group.ads.value()[0].render_url(),
                 "https://example.com/render2");
       seen_succeeded = true;
       continue;
@@ -5699,8 +5666,7 @@
       seen_failed = true;
     }
     // Failed and deferred interest groups shouldn't have updated.
-    EXPECT_EQ(group.ads.value()[0].render_url.spec(),
-              "https://example.com/render");
+    EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
   }
   EXPECT_TRUE(seen_succeeded);
   EXPECT_TRUE(seen_failed);
@@ -5716,8 +5682,7 @@
   auto b_group = b_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(b_group.ads.has_value());
   ASSERT_EQ(b_group.ads->size(), 1u);
-  EXPECT_EQ(b_group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(b_group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Only 4 network requests should have been made (for the kOriginA updates).
   EXPECT_EQ(network_responder_->UpdateCount(), 4u);
@@ -5737,7 +5702,7 @@
     ASSERT_TRUE(group.ads.has_value());
     ASSERT_EQ(group.ads->size(), 1u);
     if (group.name == kUpdateUrlA3.path()) {
-      EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+      EXPECT_EQ(group.ads.value()[0].render_url(),
                 "https://example.com/render2");
       break;
     }
@@ -5764,7 +5729,7 @@
     ASSERT_TRUE(group.ads.has_value());
     ASSERT_EQ(group.ads->size(), 1u);
     if (group.name == kUpdateUrlA4.path()) {
-      EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+      EXPECT_EQ(group.ads.value()[0].render_url(),
                 "https://example.com/render2");
       break;
     }
@@ -5777,8 +5742,7 @@
   b_group = b_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(b_group.ads.has_value());
   ASSERT_EQ(b_group.ads->size(), 1u);
-  EXPECT_EQ(b_group.ads.value()[0].render_url.spec(),
-            "https://example.com/render");
+  EXPECT_EQ(b_group.ads.value()[0].render_url(), "https://example.com/render");
 
   // Now, try updating kOriginB. The update should complete successfully.
   network_responder_->RegisterUpdateResponse(kUpdateUrlPathB, R"({
@@ -5796,8 +5760,7 @@
   b_group = b_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(b_group.ads.has_value());
   ASSERT_EQ(b_group.ads->size(), 1u);
-  EXPECT_EQ(b_group.ads.value()[0].render_url.spec(),
-            "https://example.com/render3");
+  EXPECT_EQ(b_group.ads.value()[0].render_url(), "https://example.com/render3");
 }
 
 TEST_F(AdAuctionServiceImplTest, CreateAuctionNonce) {
@@ -5967,7 +5930,7 @@
   EXPECT_THAT(GetKAnonJoinedIds(),
               ::testing::UnorderedElementsAre(
                   KAnonKeyForAdBid(interest_group,
-                                   interest_group.ads.value()[0].render_url),
+                                   interest_group.ads.value()[0].render_url()),
                   KAnonKeyForAdNameReporting(interest_group,
                                              interest_group.ads.value()[0])));
 }
@@ -6138,7 +6101,7 @@
   auto a_group = a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(a_group.ads.has_value());
   ASSERT_EQ(a_group.ads->size(), 1u);
-  EXPECT_EQ(a_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(a_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
   ASSERT_TRUE(a_group.ads.value()[0].allowed_reporting_origins.has_value());
   EXPECT_THAT(a_group.ads.value()[0].allowed_reporting_origins.value(),
@@ -6198,7 +6161,7 @@
   auto a_group = a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(a_group.ads.has_value());
   ASSERT_EQ(a_group.ads->size(), 1u);
-  EXPECT_EQ(a_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(a_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 }
 
@@ -6250,7 +6213,7 @@
   auto a_group = a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(a_group.ads.has_value());
   ASSERT_EQ(a_group.ads->size(), 1u);
-  EXPECT_EQ(a_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(a_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 }
 
@@ -6313,7 +6276,7 @@
       no_update_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(no_update_group.ads.has_value());
   ASSERT_EQ(no_update_group.ads->size(), 1u);
-  EXPECT_EQ(no_update_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(no_update_group.ads.value()[0].render_url(),
             "https://example.com/render");
 
   // There shouldn't have even been an attempt to update.
@@ -6427,7 +6390,7 @@
   auto a_group = a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(a_group.ads.has_value());
   ASSERT_EQ(a_group.ads->size(), 1u);
-  EXPECT_EQ(a_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(a_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 
   auto c_groups = GetInterestGroupsForOwner(kOriginC);
@@ -6435,7 +6398,7 @@
   auto c_group = c_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(c_group.ads.has_value());
   ASSERT_EQ(c_group.ads->size(), 1u);
-  EXPECT_EQ(c_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(c_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 }
 
@@ -6539,7 +6502,7 @@
   auto a_group = a_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(a_group.ads.has_value());
   ASSERT_EQ(a_group.ads->size(), 1u);
-  EXPECT_EQ(a_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(a_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 
   auto c_groups = GetInterestGroupsForOwner(kOriginC);
@@ -6547,7 +6510,7 @@
   auto c_group = c_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(c_group.ads.has_value());
   ASSERT_EQ(c_group.ads->size(), 1u);
-  EXPECT_EQ(c_group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(c_group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 }
 
@@ -6624,7 +6587,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             "https://example.com/new_render");
   EXPECT_EQ(group.all_sellers_capabilities,
             blink::SellerCapabilitiesType(
@@ -6667,7 +6630,7 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             "https://example.com/new_render");
 }
 
@@ -9844,10 +9807,10 @@
   const auto& group = groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             "https://example.com/new_render");
   ASSERT_EQ(group.ad_components->size(), 1u);
-  EXPECT_EQ(group.ad_components.value()[0].render_url.spec(),
+  EXPECT_EQ(group.ad_components.value()[0].render_url(),
             "https://example.com/new_component");
   EXPECT_EQ(group.user_bidding_signals.value(), "{\"old\":4}");
 }
@@ -9965,7 +9928,7 @@
           GetKAnonJoinedIds(),
           ::testing::UnorderedElementsAre(
               KAnonKeyForAdBid(interest_group,
-                               interest_group.ads.value()[0].render_url),
+                               interest_group.ads.value()[0].render_url()),
               KAnonKeyForAdNameReporting(interest_group,
                                          interest_group.ads.value()[0])));
       break;
@@ -9979,7 +9942,7 @@
           GetKAnonJoinedIds(),
           ::testing::UnorderedElementsAre(
               KAnonKeyForAdBid(interest_group,
-                               interest_group.ads.value()[0].render_url),
+                               interest_group.ads.value()[0].render_url()),
               KAnonKeyForAdNameReporting(interest_group,
                                          interest_group.ads.value()[0])));
       break;
diff --git a/content/browser/interest_group/additional_bids_util.cc b/content/browser/interest_group/additional_bids_util.cc
index ff52c63..1db2290 100644
--- a/content/browser/interest_group/additional_bids_util.cc
+++ b/content/browser/interest_group/additional_bids_util.cc
@@ -204,8 +204,8 @@
 
   // Create ad vector and its first entry.
   synth_interest_group.interest_group.ads.emplace();
-  synth_interest_group.interest_group.ads.value().emplace_back();
-  synth_interest_group.interest_group.ads.value()[0].render_url = render_url;
+  synth_interest_group.interest_group.ads->emplace_back(
+      render_url, /*metadata=*/absl::nullopt);
 
   absl::optional<double> bid_val = bid_dict->FindDouble("bid");
   if (!bid_val || bid_val.value() <= 0) {
@@ -381,7 +381,7 @@
       *bid_val,
       /*bid_currency=*/bid_currency,
       /*ad_cost=*/ad_cost,
-      /*ad_descriptor=*/blink::AdDescriptor(bid_ad->render_url),
+      /*ad_descriptor=*/blink::AdDescriptor(GURL(bid_ad->render_url())),
       /*ad_component_descriptors=*/std::move(ad_components),
       /*modeling_signals=*/
       static_cast<absl::optional<uint16_t>>(modeling_signals),
diff --git a/content/browser/interest_group/additional_bids_util_unittest.cc b/content/browser/interest_group/additional_bids_util_unittest.cc
index 9a51b32..ef7616c 100644
--- a/content/browser/interest_group/additional_bids_util_unittest.cc
+++ b/content/browser/interest_group/additional_bids_util_unittest.cc
@@ -507,7 +507,7 @@
   ASSERT_TRUE(bid_state->bidder->interest_group.ads.has_value());
   ASSERT_EQ(1u, bid_state->bidder->interest_group.ads->size());
   EXPECT_EQ("https://en.wikipedia.test/wiki/Train",
-            bid_state->bidder->interest_group.ads.value()[0].render_url.spec());
+            bid_state->bidder->interest_group.ads.value()[0].render_url());
 
   EXPECT_EQ(InterestGroupAuction::Bid::BidRole::kBothKAnonModes, bid->bid_role);
   EXPECT_EQ("null", bid->ad_metadata);
@@ -766,10 +766,10 @@
             result->bid_state->bidder->interest_group.ad_components->size());
   EXPECT_EQ("https://en.wikipedia.test/wiki/Locomotive",
             result->bid_state->bidder->interest_group.ad_components.value()[0]
-                .render_url.spec());
+                .render_url());
   EXPECT_EQ("https://en.wikipedia.test/wiki/High-speed_rail",
             result->bid_state->bidder->interest_group.ad_components.value()[1]
-                .render_url.spec());
+                .render_url());
 }
 
 TEST_F(AdditionalBidsUtilTest, ValidAdComponentsEmpty) {
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index f32c744..7420276 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -1272,10 +1272,10 @@
 void AuthorizeKAnonAd(const blink::InterestGroup::Ad& ad,
                       const char* url,
                       StorageInterestGroup& group) {
-  DCHECK_EQ(url, ad.render_url.spec());
+  DCHECK_EQ(url, ad.render_url());
   group.bidding_ads_kanon.emplace_back();
   group.bidding_ads_kanon.back().key =
-      blink::KAnonKeyForAdBid(group.interest_group, ad.render_url);
+      blink::KAnonKeyForAdBid(group.interest_group, ad.render_url());
   group.bidding_ads_kanon.back().is_k_anonymous = true;
   group.bidding_ads_kanon.back().last_updated = base::Time::Now();
 }
@@ -1283,7 +1283,7 @@
 void AuthorizeKAnonReporting(const blink::InterestGroup::Ad& ad,
                              const char* url,
                              StorageInterestGroup& group) {
-  DCHECK_EQ(url, ad.render_url.spec());
+  DCHECK_EQ(url, ad.render_url());
   group.reporting_ads_kanon.emplace_back();
   group.reporting_ads_kanon.back().key =
       blink::KAnonKeyForAdNameReporting(group.interest_group, ad);
@@ -1294,10 +1294,10 @@
 void AuthorizeKAnonAdComponent(const blink::InterestGroup::Ad& ad,
                                const char* url,
                                StorageInterestGroup& group) {
-  DCHECK_EQ(url, ad.render_url.spec());
+  DCHECK_EQ(url, ad.render_url());
   group.component_ads_kanon.emplace_back();
   group.component_ads_kanon.back().key =
-      blink::KAnonKeyForAdComponentBid(ad.render_url);
+      blink::KAnonKeyForAdComponentBid(ad.render_url());
   group.component_ads_kanon.back().is_k_anonymous = true;
   group.component_ads_kanon.back().last_updated = base::Time::Now();
 }
@@ -19216,7 +19216,7 @@
               testing::UnorderedElementsAre(
                   blink::KAnonKeyForAdBid(
                       bidders[0].interest_group,
-                      bidders[0].interest_group.ads.value()[0].render_url),
+                      bidders[0].interest_group.ads.value()[0].render_url()),
                   blink::KAnonKeyForAdNameReporting(
                       bidders[0].interest_group,
                       bidders[0].interest_group.ads.value()[0])));
@@ -19313,7 +19313,7 @@
               testing::UnorderedElementsAre(
                   blink::KAnonKeyForAdBid(
                       bidders[0].interest_group,
-                      bidders[0].interest_group.ads.value()[0].render_url),
+                      bidders[0].interest_group.ads.value()[0].render_url()),
                   blink::KAnonKeyForAdNameReporting(
                       bidders[0].interest_group,
                       bidders[0].interest_group.ads.value()[0])));
@@ -19386,25 +19386,25 @@
   std::vector<std::string> ad1_k_anon_keys = {
       blink::KAnonKeyForAdBid(
           bidders[0].interest_group,
-          bidders[0].interest_group.ads.value()[0].render_url),
+          bidders[0].interest_group.ads.value()[0].render_url()),
       blink::KAnonKeyForAdNameReporting(
           bidders[0].interest_group, bidders[0].interest_group.ads.value()[0]),
       blink::KAnonKeyForAdComponentBid(
-          bidders[0].interest_group.ad_components.value()[0].render_url),
+          bidders[0].interest_group.ad_components.value()[0].render_url()),
       blink::KAnonKeyForAdComponentBid(
-          bidders[0].interest_group.ad_components.value()[1].render_url),
+          bidders[0].interest_group.ad_components.value()[1].render_url()),
   };
 
   std::vector<std::string> ad2_k_anon_keys = {
       blink::KAnonKeyForAdBid(
           bidders[1].interest_group,
-          bidders[1].interest_group.ads.value()[0].render_url),
+          bidders[1].interest_group.ads.value()[0].render_url()),
       blink::KAnonKeyForAdNameReporting(
           bidders[1].interest_group, bidders[1].interest_group.ads.value()[0]),
       blink::KAnonKeyForAdComponentBid(
-          bidders[1].interest_group.ad_components.value()[0].render_url),
+          bidders[1].interest_group.ad_components.value()[0].render_url()),
       blink::KAnonKeyForAdComponentBid(
-          bidders[1].interest_group.ad_components.value()[1].render_url),
+          bidders[1].interest_group.ad_components.value()[1].render_url()),
   };
 
   for (bool run_as_component : {false, true}) {
@@ -19668,14 +19668,14 @@
   std::vector<std::string> ad1_k_anon_keys = {
       blink::KAnonKeyForAdBid(
           bidders[0].interest_group,
-          bidders[0].interest_group.ads.value()[0].render_url),
+          bidders[0].interest_group.ads.value()[0].render_url()),
       blink::KAnonKeyForAdNameReporting(
           bidders[0].interest_group, bidders[0].interest_group.ads.value()[0]),
   };
   std::vector<std::string> ad2_k_anon_keys = {
       blink::KAnonKeyForAdBid(
           bidders[1].interest_group,
-          bidders[1].interest_group.ads.value()[0].render_url),
+          bidders[1].interest_group.ads.value()[0].render_url()),
       blink::KAnonKeyForAdNameReporting(
           bidders[1].interest_group, bidders[1].interest_group.ads.value()[0]),
   };
@@ -19917,7 +19917,7 @@
   std::vector<std::string> ad1_k_anon_keys = {
       blink::KAnonKeyForAdBid(
           bidders[0].interest_group,
-          bidders[0].interest_group.ads.value()[0].render_url),
+          bidders[0].interest_group.ads.value()[0].render_url()),
       blink::KAnonKeyForAdNameReporting(
           bidders[0].interest_group, bidders[0].interest_group.ads.value()[0]),
   };
@@ -20067,14 +20067,14 @@
   std::vector<std::string> ad1_k_anon_keys = {
       blink::KAnonKeyForAdBid(
           bidders[0].interest_group,
-          bidders[0].interest_group.ads.value()[0].render_url),
+          bidders[0].interest_group.ads.value()[0].render_url()),
       blink::KAnonKeyForAdNameReporting(
           bidders[0].interest_group, bidders[0].interest_group.ads.value()[0]),
   };
   std::vector<std::string> ad2_k_anon_keys = {
       blink::KAnonKeyForAdBid(
           bidders[0].interest_group,
-          bidders[0].interest_group.ads.value()[1].render_url),
+          bidders[0].interest_group.ads.value()[1].render_url()),
       blink::KAnonKeyForAdNameReporting(
           bidders[0].interest_group, bidders[0].interest_group.ads.value()[1]),
   };
@@ -20588,7 +20588,7 @@
               testing::UnorderedElementsAre(
                   blink::KAnonKeyForAdBid(
                       bidders[0].interest_group,
-                      bidders[0].interest_group.ads.value()[0].render_url),
+                      bidders[0].interest_group.ads.value()[0].render_url()),
                   blink::KAnonKeyForAdNameReporting(
                       bidders[0].interest_group,
                       bidders[0].interest_group.ads.value()[0])));
diff --git a/content/browser/interest_group/bidding_and_auction_serializer.cc b/content/browser/interest_group/bidding_and_auction_serializer.cc
index 160a53b..bc9979d 100644
--- a/content/browser/interest_group/bidding_and_auction_serializer.cc
+++ b/content/browser/interest_group/bidding_and_auction_serializer.cc
@@ -40,7 +40,7 @@
   for (const auto& ad : ads) {
     if (include_full_ads) {
       cbor::Value::MapValue obj;
-      obj[cbor::Value("renderURL")] = cbor::Value(ad.render_url.spec());
+      obj[cbor::Value("renderURL")] = cbor::Value(ad.render_url());
       if (ad.metadata) {
         obj[cbor::Value("metadata")] = cbor::Value(ad.metadata.value());
       }
diff --git a/content/browser/interest_group/interest_group_auction.cc b/content/browser/interest_group/interest_group_auction.cc
index 69d5401..bd028db 100644
--- a/content/browser/interest_group/interest_group_auction.cc
+++ b/content/browser/interest_group/interest_group_auction.cc
@@ -195,7 +195,7 @@
   }
 
   for (const auto& ad : ads) {
-    if (ad.render_url != ad_descriptor.url) {
+    if (ad.render_url() != ad_descriptor.url) {
       continue;
     }
     if (!ad.size_group && !ad_descriptor.size) {
@@ -3276,7 +3276,7 @@
     const blink::InterestGroup& interest_group =
         *scored_bid->bid->interest_group;
     k_anon_keys_to_join.push_back(blink::KAnonKeyForAdBid(
-        interest_group, scored_bid->bid->bid_ad->render_url));
+        interest_group, scored_bid->bid->bid_ad->render_url()));
     k_anon_keys_to_join.push_back(blink::KAnonKeyForAdNameReporting(
         interest_group, *scored_bid->bid->bid_ad));
     for (const blink::AdDescriptor& ad_component_descriptor :
diff --git a/content/browser/interest_group/interest_group_auction_reporter.cc b/content/browser/interest_group/interest_group_auction_reporter.cc
index 8de35cb..08cebdd 100644
--- a/content/browser/interest_group/interest_group_auction_reporter.cc
+++ b/content/browser/interest_group/interest_group_auction_reporter.cc
@@ -72,7 +72,7 @@
     const GURL& winning_ad_url) {
   auto chosen_ad = base::ranges::find(
       *storage_interest_group->interest_group.ads, winning_ad_url,
-      [](const blink::InterestGroup::Ad& ad) { return ad.render_url; });
+      [](const blink::InterestGroup::Ad& ad) { return ad.render_url(); });
   CHECK(chosen_ad != storage_interest_group->interest_group.ads->end());
   return *chosen_ad;
 }
diff --git a/content/browser/interest_group/interest_group_auction_reporter_unittest.cc b/content/browser/interest_group/interest_group_auction_reporter_unittest.cc
index 9761ede..6a4a61b 100644
--- a/content/browser/interest_group/interest_group_auction_reporter_unittest.cc
+++ b/content/browser/interest_group/interest_group_auction_reporter_unittest.cc
@@ -161,7 +161,7 @@
         losing_interest_group,
         /*joining_url=*/kLosingBidderOrigin.GetURL());
 
-    winning_bid_info_.render_url = (*interest_group.ads)[0].render_url;
+    winning_bid_info_.render_url = GURL((*interest_group.ads)[0].render_url());
     winning_bid_info_.ad_metadata = kWinningAdMetadata;
 
     // The actual value doesn't matter for tests, but need to set some value as
@@ -175,7 +175,7 @@
     // actually use any strings for the sake of these tests, but seems best to
     // use accurate ones.
     std::vector<std::string> k_anon_keys_to_join{
-        KAnonKeyForAdBid(interest_group, (*interest_group.ads)[0].render_url),
+        KAnonKeyForAdBid(interest_group, (*interest_group.ads)[0].render_url()),
         KAnonKeyForAdNameReporting(interest_group, (*interest_group.ads)[0]),
     };
     k_anon_keys_to_join_ =
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index 0aa6804..2a7b942 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -208,7 +208,7 @@
   base::Value::List list;
   for (const auto& ad : ads) {
     base::Value::Dict entry;
-    entry.Set("renderURL", ad.render_url.spec());
+    entry.Set("renderURL", ad.render_url());
     if (ad.size_group) {
       entry.Set("sizeGroup", std::move(ad.size_group.value()));
     }
@@ -3524,12 +3524,12 @@
       groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url,
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             GURL("https://example.com/render"));
   ASSERT_TRUE(group.ads.value()[0].size_group.has_value());
   EXPECT_EQ(group.ads.value()[0].size_group, "group_1");
   ASSERT_EQ(group.ad_components->size(), 1u);
-  EXPECT_EQ(group.ad_components.value()[0].render_url,
+  EXPECT_EQ(group.ad_components.value()[0].render_url(),
             GURL("https://example.com/component"));
   ASSERT_TRUE(group.ad_components.value()[0].size_group.has_value());
   EXPECT_EQ(group.ad_components.value()[0].size_group, "group_1");
@@ -3599,12 +3599,12 @@
       groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 2u);
-  EXPECT_EQ(group.ads.value()[0].render_url,
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             GURL("https://example.com/render"));
   EXPECT_EQ(group.ads.value()[0].buyer_reporting_id, "brid1");
   EXPECT_EQ(group.ads.value()[0].buyer_and_seller_reporting_id, "sh1");
 
-  EXPECT_EQ(group.ads.value()[1].render_url,
+  EXPECT_EQ(group.ads.value()[1].render_url(),
             GURL("https://example.com/render2"));
   EXPECT_FALSE(group.ads.value()[1].buyer_reporting_id.has_value());
   EXPECT_EQ(group.ads.value()[1].buyer_and_seller_reporting_id, "sh2");
@@ -3643,11 +3643,11 @@
       groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url,
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             GURL("https://example.com/render"));
   EXPECT_EQ(group.ads.value()[0].ad_render_id, "123abc");
   ASSERT_EQ(group.ad_components->size(), 1u);
-  EXPECT_EQ(group.ad_components.value()[0].render_url,
+  EXPECT_EQ(group.ad_components.value()[0].render_url(),
             GURL("https://example.com/component"));
   EXPECT_EQ(group.ad_components.value()[0].ad_render_id, "456def");
 }
@@ -3685,7 +3685,7 @@
       groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(group.ads.value()[0].render_url,
+  EXPECT_EQ(group.ads.value()[0].render_url(),
             GURL("https://example.com/render"));
   EXPECT_EQ(group.ads.value()[0].ad_render_id, "123abc");
   EXPECT_EQ(group.ads.value()[0].allowed_reporting_origins,
@@ -3737,7 +3737,8 @@
   ASSERT_TRUE(group.ad_components.has_value());
   ASSERT_EQ(group.ad_components->size(), 1u);
   const auto& ad_component = group.ad_components.value()[0];
-  EXPECT_EQ(ad_component.render_url, GURL("https://ad-components.com/render"));
+  EXPECT_EQ(ad_component.render_url(),
+            GURL("https://ad-components.com/render"));
   EXPECT_EQ(ad_component.ad_render_id, "123abc");
   EXPECT_FALSE(ad_component.buyer_reporting_id.has_value());
   EXPECT_FALSE(ad_component.buyer_and_seller_reporting_id.has_value());
@@ -11247,8 +11248,8 @@
   // first and last ad component URLs, skpping the second.
   RenderFrameHostImpl* ad_frame = GetFencedFrameRenderFrameHost(shell());
   CheckAdComponents(
-      /*expected_ad_component_urls=*/{ad_components[0].render_url,
-                                      ad_components[2].render_url},
+      /*expected_ad_component_urls=*/{GURL(ad_components[0].render_url()),
+                                      GURL(ad_components[2].render_url())},
       ad_frame);
 
   // Get first three URLs from the fenced frame.
@@ -11259,10 +11260,10 @@
 
   // Load each of the ad components in the nested fenced frame, validating the
   // URLs they're mapped to.
-  NavigateFencedFrameAndWait((*components)[0], ad_components[0].render_url,
-                             ad_frame);
-  NavigateFencedFrameAndWait((*components)[1], ad_components[2].render_url,
-                             ad_frame);
+  NavigateFencedFrameAndWait((*components)[0],
+                             GURL(ad_components[0].render_url()), ad_frame);
+  NavigateFencedFrameAndWait((*components)[1],
+                             GURL(ad_components[2].render_url()), ad_frame);
   NavigateFencedFrameAndWait((*components)[2], GURL(url::kAboutBlankURL),
                              ad_frame);
 }
@@ -13313,7 +13314,8 @@
                group.trusted_bidding_signals_keys->size() == 1 &&
                group.trusted_bidding_signals_keys.value()[0] == "new_key" &&
                group.ads.has_value() && group.ads->size() == 1 &&
-               group.ads.value()[0].render_url.path() == "/new_ad_render_url" &&
+               GURL(group.ads.value()[0].render_url()).path() ==
+                   "/new_ad_render_url" &&
                group.ads.value()[0].metadata == "{\"new_a\":\"b\"}";
       }));
 }
@@ -13390,7 +13392,8 @@
                group.trusted_bidding_signals_keys->size() == 1 &&
                group.trusted_bidding_signals_keys.value()[0] == "new_key" &&
                group.ads.has_value() && group.ads->size() == 1 &&
-               group.ads.value()[0].render_url.path() == "/new_ad_render_url" &&
+               GURL(group.ads.value()[0].render_url()).path() ==
+                   "/new_ad_render_url" &&
                group.ads.value()[0].metadata == "{\"new_a\":\"b\"}";
       }));
 }
@@ -13470,7 +13473,8 @@
                group.trusted_bidding_signals_keys->size() == 1 &&
                group.trusted_bidding_signals_keys.value()[0] == "new_key" &&
                group.ads.has_value() && group.ads->size() == 1 &&
-               group.ads.value()[0].render_url.path() == "/new_ad_render_url" &&
+               GURL(group.ads.value()[0].render_url()).path() ==
+                   "/new_ad_render_url" &&
                group.ads.value()[0].metadata == "{\"new_a\":\"b\"}";
       }));
 }
@@ -13538,7 +13542,7 @@
                    group.trusted_bidding_signals_keys->size() == 1 &&
                    group.trusted_bidding_signals_keys.value()[0] == "key1" &&
                    group.ads.has_value() && group.ads->size() == 1 &&
-                   group.ads.value()[0].render_url.path() ==
+                   GURL(group.ads.value()[0].render_url()).path() ==
                        "/new_ad_render_url" &&
                    group.ads.value()[0].metadata == "{\"new_a\":\"b\"}";
           }));
@@ -14513,10 +14517,10 @@
             storage_groups->GetInterestGroups()[0]->interest_group;
         EXPECT_TRUE(group.ads.has_value());
         EXPECT_EQ(group.ads->size(), 1u);
-        if (group.ads.value()[0].render_url == new_ad_url) {
+        if (group.ads.value()[0].render_url() == new_ad_url) {
           return true;
         }
-        EXPECT_EQ(initial_ad_url, group.ads.value()[0].render_url);
+        EXPECT_EQ(initial_ad_url, group.ads.value()[0].render_url());
         return false;
       });
 
@@ -14530,7 +14534,7 @@
       storage_groups->GetInterestGroups()[0]->interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
-  EXPECT_EQ(initial_ad_url, group.ads.value()[0].render_url);
+  EXPECT_EQ(initial_ad_url, group.ads.value()[0].render_url());
 }
 
 // Interest group APIs succeeded (i.e., feature join-ad-interest-group is
diff --git a/content/browser/interest_group/interest_group_k_anonymity_manager_unittest.cc b/content/browser/interest_group/interest_group_k_anonymity_manager_unittest.cc
index 84a65ca..bc9d90b4 100644
--- a/content/browser/interest_group/interest_group_k_anonymity_manager_unittest.cc
+++ b/content/browser/interest_group/interest_group_k_anonymity_manager_unittest.cc
@@ -147,7 +147,7 @@
   manager->JoinInterestGroup(g, top_frame);
   // Set k_anon value to true so that it gets returned with the interest group.
   manager->UpdateKAnonymity(
-      {blink::KAnonKeyForAdBid(g, g.ads->at(0).render_url), true,
+      {blink::KAnonKeyForAdBid(g, g.ads->at(0).render_url()), true,
        base::Time::Min()});
 
   auto maybe_group = GetGroup(manager.get(), owner, name);
diff --git a/content/browser/interest_group/interest_group_storage.cc b/content/browser/interest_group/interest_group_storage.cc
index 67eb136e..1b2cdeff2 100644
--- a/content/browser/interest_group/interest_group_storage.cc
+++ b/content/browser/interest_group/interest_group_storage.cc
@@ -31,6 +31,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
+#include "base/types/pass_key.h"
 #include "content/browser/interest_group/interest_group_ad.pb.h"
 #include "content/browser/interest_group/interest_group_k_anonymity_manager.h"
 #include "content/browser/interest_group/interest_group_update.h"
@@ -55,6 +56,7 @@
 
 namespace {
 
+using PassKey = base::PassKey<InterestGroupStorage>;
 using auction_worklet::mojom::BiddingBrowserSignalsPtr;
 using auction_worklet::mojom::PreviousWinPtr;
 using SellerCapabilitiesType = blink::SellerCapabilitiesType;
@@ -162,13 +164,14 @@
   return result;
 }
 
-blink::InterestGroup::Ad FromInterestGroupAdValue(const base::Value::Dict& dict,
+blink::InterestGroup::Ad FromInterestGroupAdValue(const PassKey& passkey,
+                                                  const base::Value::Dict& dict,
                                                   bool for_components) {
-  blink::InterestGroup::Ad result;
   const std::string* maybe_url = dict.FindString("url");
-  if (maybe_url) {
-    result.render_url = GURL(*maybe_url);
+  if (!maybe_url) {
+    return blink::InterestGroup::Ad();
   }
+  blink::InterestGroup::Ad result(passkey, *maybe_url);
   const std::string* maybe_size_group = dict.FindString("size_group");
   if (maybe_size_group) {
     result.size_group = *maybe_size_group;
@@ -246,7 +249,7 @@
   AdProtos ad_protos;
   for (blink::InterestGroup::Ad ad : ads) {
     AdProtos_AdProto* ad_proto = ad_protos.add_ads();
-    ad_proto->set_render_url(ad.render_url.spec());
+    ad_proto->set_render_url(ad.render_url());
     if (ad.size_group.has_value()) {
       ad_proto->set_size_group(*ad.size_group);
     }
@@ -284,7 +287,8 @@
 }
 
 absl::optional<std::vector<blink::InterestGroup::Ad>>
-DeserializeInterestGroupAdVectorJson(const std::string& serialized_ads,
+DeserializeInterestGroupAdVectorJson(const PassKey& passkey,
+                                     const std::string& serialized_ads,
                                      bool for_components) {
   std::unique_ptr<base::Value> ads_value = DeserializeValue(serialized_ads);
   if (!ads_value || !ads_value->is_list()) {
@@ -294,14 +298,16 @@
   for (const auto& ad_value : ads_value->GetList()) {
     const base::Value::Dict* dict = ad_value.GetIfDict();
     if (dict) {
-      result.emplace_back(FromInterestGroupAdValue(*dict, for_components));
+      result.emplace_back(
+          FromInterestGroupAdValue(passkey, *dict, for_components));
     }
   }
   return result;
 }
 
 absl::optional<std::vector<blink::InterestGroup::Ad>>
-DeserializeInterestGroupAdVectorProto(const std::string& serialized_ads) {
+DeserializeInterestGroupAdVectorProto(const PassKey& passkey,
+                                      const std::string& serialized_ads) {
   AdProtos ad_protos;
 
   bool success = ad_protos.ParseFromString(serialized_ads);
@@ -311,8 +317,7 @@
   }
   std::vector<blink::InterestGroup::Ad> out;
   for (const auto& ad_proto : ad_protos.ads()) {
-    blink::InterestGroup::Ad ad;
-    ad.render_url = GURL(ad_proto.render_url());
+    blink::InterestGroup::Ad ad(passkey, ad_proto.render_url());
     if (ad_proto.has_size_group()) {
       ad.size_group = ad_proto.size_group();
     }
@@ -747,7 +752,7 @@
         return false;
       }
       if (!MaybeCreateKAnonEntry(db, interest_group_key,
-                                 blink::KAnonKeyForAdBid(data, ad.render_url),
+                                 blink::KAnonKeyForAdBid(data, ad.render_url()),
                                  exact_join_time, for_database_update)) {
         return false;
       }
@@ -757,8 +762,8 @@
     for (auto& ad : *data.ad_components) {
       if (!MaybeCreateKAnonEntry(
               db, interest_group_key,
-              blink::KAnonKeyForAdComponentBid(ad.render_url), exact_join_time,
-              for_database_update)) {
+              blink::KAnonKeyForAdComponentBid(ad.render_url()),
+              exact_join_time, for_database_update)) {
         return false;
       }
     }
@@ -1208,7 +1213,9 @@
   return true;
 }
 
-bool UpgradeV16SchemaToV17(sql::Database& db, sql::MetaTable& meta_table) {
+bool UpgradeV16SchemaToV17(sql::Database& db,
+                           sql::MetaTable& meta_table,
+                           const PassKey& passkey) {
   static const char kCreateKAnonTableSql[] =
       "CREATE TABLE k_anon_new("
       "last_referenced_time INTEGER NOT NULL,"
@@ -1258,9 +1265,9 @@
         DeserializeOrigin(select_igs_with_ads_and_bidding_url.ColumnString(0));
     ig.name = select_igs_with_ads_and_bidding_url.ColumnString(1);
     ig.ads = DeserializeInterestGroupAdVectorProto(
-        select_igs_with_ads_and_bidding_url.ColumnString(2));
+        passkey, select_igs_with_ads_and_bidding_url.ColumnString(2));
     ig.ad_components = DeserializeInterestGroupAdVectorProto(
-        select_igs_with_ads_and_bidding_url.ColumnString(3));
+        passkey, select_igs_with_ads_and_bidding_url.ColumnString(3));
     ig.bidding_url =
         DeserializeURL(select_igs_with_ads_and_bidding_url.ColumnString(4));
 
@@ -1285,7 +1292,9 @@
   return CreateKAnonIndices(db);
 }
 
-bool UpgradeV15SchemaToV16(sql::Database& db, sql::MetaTable& meta_table) {
+bool UpgradeV15SchemaToV16(sql::Database& db,
+                           sql::MetaTable& meta_table,
+                           const PassKey& passkey) {
   static const char kInterestGroupTableSql[] =
       // clang-format off
       "CREATE TABLE new_interest_groups("
@@ -1372,10 +1381,12 @@
     std::string owner = kSelectIGsWithAds.ColumnString(0);
     std::string name = kSelectIGsWithAds.ColumnString(1);
     absl::optional<std::vector<blink::InterestGroup::Ad>> ads =
-        DeserializeInterestGroupAdVectorJson(kSelectIGsWithAds.ColumnString(2),
+        DeserializeInterestGroupAdVectorJson(passkey,
+                                             kSelectIGsWithAds.ColumnString(2),
                                              /*for_components=*/false);
     absl::optional<std::vector<blink::InterestGroup::Ad>> ad_components =
-        DeserializeInterestGroupAdVectorJson(kSelectIGsWithAds.ColumnString(3),
+        DeserializeInterestGroupAdVectorJson(passkey,
+                                             kSelectIGsWithAds.ColumnString(3),
                                              /*for_components=*/true);
 
     std::string serialized_ads = Serialize(ads);
@@ -2180,6 +2191,7 @@
 }
 
 bool DoLoadInterestGroup(sql::Database& db,
+                         const PassKey& passkey,
                          const blink::InterestGroupKey& group_key,
                          blink::InterestGroup& group,
                          url::Origin* joining_origin = nullptr,
@@ -2264,9 +2276,10 @@
   if (load.GetColumnType(17) != sql::ColumnType::kNull) {
     group.user_bidding_signals = load.ColumnString(17);
   }
-  group.ads = DeserializeInterestGroupAdVectorProto(load.ColumnString(18));
+  group.ads =
+      DeserializeInterestGroupAdVectorProto(passkey, load.ColumnString(18));
   group.ad_components =
-      DeserializeInterestGroupAdVectorProto(load.ColumnString(19));
+      DeserializeInterestGroupAdVectorProto(passkey, load.ColumnString(19));
   group.ad_sizes = DeserializeStringSizeMap(load.ColumnString(20));
   group.size_groups = DeserializeStringStringVectorMap(load.ColumnString(21));
   group.auction_server_request_flags =
@@ -2335,6 +2348,7 @@
 }
 
 bool DoJoinInterestGroup(sql::Database& db,
+                         const PassKey& passkey,
                          const blink::InterestGroup& data,
                          const GURL& joining_url,
                          base::Time exact_join_time,
@@ -2350,7 +2364,7 @@
   blink::InterestGroup old_group;
   url::Origin old_joining_origin;
   blink::InterestGroupKey interest_group_key(data.owner, data.name);
-  if (DoLoadInterestGroup(db, interest_group_key, old_group,
+  if (DoLoadInterestGroup(db, passkey, interest_group_key, old_group,
                           &old_joining_origin,
                           /*exact_join_time=*/nullptr,
                           /*last_updated=*/nullptr)) {
@@ -2551,6 +2565,7 @@
 }
 
 bool DoUpdateInterestGroup(sql::Database& db,
+                           const PassKey& passkey,
                            const blink::InterestGroupKey& group_key,
                            InterestGroupUpdate update,
                            base::Time now) {
@@ -2569,7 +2584,7 @@
   // verify the interest group is valid before writing it to the database.
 
   blink::InterestGroup stored_group;
-  if (!DoLoadInterestGroup(db, group_key, stored_group,
+  if (!DoLoadInterestGroup(db, passkey, group_key, stored_group,
                            /*joining_origin=*/nullptr,
                            /*exact_join_time=*/nullptr,
                            /*last_updated=*/nullptr)) {
@@ -3342,13 +3357,14 @@
 
 absl::optional<StorageInterestGroup> DoGetStoredInterestGroup(
     sql::Database& db,
+    const PassKey& passkey,
     const blink::InterestGroupKey& group_key,
     base::Time now) {
   StorageInterestGroup db_interest_group;
-  if (!DoLoadInterestGroup(db, group_key, db_interest_group.interest_group,
-                           &db_interest_group.joining_origin,
-                           &db_interest_group.join_time,
-                           &db_interest_group.last_updated)) {
+  if (!DoLoadInterestGroup(
+          db, passkey, group_key, db_interest_group.interest_group,
+          &db_interest_group.joining_origin, &db_interest_group.join_time,
+          &db_interest_group.last_updated)) {
     return absl::nullopt;
   }
 
@@ -3447,6 +3463,7 @@
 
 absl::optional<std::vector<StorageInterestGroup>> DoGetInterestGroupsForOwner(
     sql::Database& db,
+    const PassKey& passkey,
     const url::Origin& owner,
     base::Time now) {
   sql::Transaction transaction(&db);
@@ -3465,7 +3482,8 @@
   std::vector<StorageInterestGroup> result;
   for (const std::string& name : *group_names) {
     absl::optional<StorageInterestGroup> db_interest_group =
-        DoGetStoredInterestGroup(db, blink::InterestGroupKey(owner, name), now);
+        DoGetStoredInterestGroup(db, passkey,
+                                 blink::InterestGroupKey(owner, name), now);
     if (!db_interest_group) {
       return absl::nullopt;
     }
@@ -4122,12 +4140,12 @@
         }
         ABSL_FALLTHROUGH_INTENDED;
       case 15:
-        if (!UpgradeV15SchemaToV16(*db_, meta_table)) {
+        if (!UpgradeV15SchemaToV16(*db_, meta_table, PassKey())) {
           return false;
         }
         ABSL_FALLTHROUGH_INTENDED;
       case 16:
-        if (!UpgradeV16SchemaToV17(*db_, meta_table)) {
+        if (!UpgradeV16SchemaToV17(*db_, meta_table, PassKey())) {
           return false;
         }
         ABSL_FALLTHROUGH_INTENDED;
@@ -4175,7 +4193,7 @@
     return;
   }
   base::Time now = base::Time::Now();
-  if (!DoJoinInterestGroup(*db_, group, main_frame_joining_url,
+  if (!DoJoinInterestGroup(*db_, PassKey(), group, main_frame_joining_url,
                            /*exact_join_time=*/now,
                            /*last_updated=*/now,
                            /*next_update_after=*/base::Time::Min())) {
@@ -4193,7 +4211,8 @@
 
   blink::InterestGroup old_group;
   url::Origin old_joining_origin;
-  if (DoLoadInterestGroup(*db_, group_key, old_group, &old_joining_origin,
+  if (DoLoadInterestGroup(*db_, PassKey(), group_key, old_group,
+                          &old_joining_origin,
                           /*exact_join_time=*/nullptr,
                           /*last_updated=*/nullptr) &&
       old_group.execution_mode ==
@@ -4255,8 +4274,8 @@
     return false;
   }
 
-  bool success =
-      DoUpdateInterestGroup(*db_, group_key, update, base::Time::Now());
+  bool success = DoUpdateInterestGroup(*db_, PassKey(), group_key, update,
+                                       base::Time::Now());
   if (!success) {
     DLOG(ERROR) << "Could not update interest group: "
                 << db_->GetErrorMessage();
@@ -4374,7 +4393,8 @@
     return absl::nullopt;
   }
 
-  return DoGetStoredInterestGroup(*db_, group_key, base::Time::Now());
+  return DoGetStoredInterestGroup(*db_, PassKey(), group_key,
+                                  base::Time::Now());
 }
 
 std::vector<url::Origin> InterestGroupStorage::GetAllInterestGroupOwners() {
@@ -4399,7 +4419,7 @@
   }
 
   absl::optional<std::vector<StorageInterestGroup>> maybe_result =
-      DoGetInterestGroupsForOwner(*db_, owner, base::Time::Now());
+      DoGetInterestGroupsForOwner(*db_, PassKey(), owner, base::Time::Now());
   if (!maybe_result) {
     return {};
   }
@@ -4536,7 +4556,7 @@
   }
 
   blink::InterestGroup group;
-  if (!DoLoadInterestGroup(*db_, group_key, group)) {
+  if (!DoLoadInterestGroup(*db_, PassKey(), group_key, group)) {
     return;
   }
 
@@ -4588,7 +4608,7 @@
   }
   for (const auto& owner : *maybe_owners) {
     absl::optional<std::vector<StorageInterestGroup>> maybe_owner_results =
-        DoGetInterestGroupsForOwner(*db_, owner, distant_past);
+        DoGetInterestGroupsForOwner(*db_, PassKey(), owner, distant_past);
     DCHECK(maybe_owner_results) << owner;
     std::move(maybe_owner_results->begin(), maybe_owner_results->end(),
               std::back_inserter(result));
diff --git a/content/browser/interest_group/interest_group_storage_unittest.cc b/content/browser/interest_group/interest_group_storage_unittest.cc
index 0d6ffa2..42d39d8 100644
--- a/content/browser/interest_group/interest_group_storage_unittest.cc
+++ b/content/browser/interest_group/interest_group_storage_unittest.cc
@@ -42,6 +42,7 @@
 
 using blink::InterestGroup;
 using testing::Field;
+using testing::Property;
 using testing::UnorderedElementsAre;
 using testing::UnorderedElementsAreArray;
 using SellerCapabilities = blink::SellerCapabilities;
@@ -1960,9 +1961,9 @@
                             &absl::optional<
                                 std::vector<blink::InterestGroup::Ad>>::value,
                             testing::ElementsAre(testing::AllOf(
-                                Field("render_url",
-                                      &InterestGroup::Ad::render_url,
-                                      GURL("https://ads.example.com/1")),
+                                Property("render_url",
+                                         &InterestGroup::Ad::render_url,
+                                         GURL("https://ads.example.com/1")),
                                 Field("metadata", &InterestGroup::Ad::metadata,
                                       "[\"4\",\"5\",null,\"6\"]"))))),
                   Field("ad_components", &InterestGroup::ad_components,
@@ -2044,9 +2045,9 @@
                             &absl::optional<
                                 std::vector<blink::InterestGroup::Ad>>::value,
                             testing::ElementsAre(testing::AllOf(
-                                Field("render_url",
-                                      &InterestGroup::Ad::render_url,
-                                      GURL("https://ads.example.com/1")),
+                                Property("render_url",
+                                         &InterestGroup::Ad::render_url,
+                                         GURL("https://ads.example.com/1")),
                                 Field("metadata", &InterestGroup::Ad::metadata,
                                       "[\"4\",\"5\",null,\"6\"]"))))),
                   Field("ad_components", &InterestGroup::ad_components,
@@ -2128,9 +2129,9 @@
                             &absl::optional<
                                 std::vector<blink::InterestGroup::Ad>>::value,
                             testing::ElementsAre(testing::AllOf(
-                                Field("render_url",
-                                      &InterestGroup::Ad::render_url,
-                                      GURL("https://ads.example.com/1")),
+                                Property("render_url",
+                                         &InterestGroup::Ad::render_url,
+                                         GURL("https://ads.example.com/1")),
                                 Field("metadata", &InterestGroup::Ad::metadata,
                                       "[\"4\",\"5\",null,\"6\"]"))))),
                   Field("ad_components", &InterestGroup::ad_components,
@@ -2212,9 +2213,9 @@
                             &absl::optional<
                                 std::vector<blink::InterestGroup::Ad>>::value,
                             testing::ElementsAre(testing::AllOf(
-                                Field("render_url",
-                                      &InterestGroup::Ad::render_url,
-                                      GURL("https://ads.example.com/1")),
+                                Property("render_url",
+                                         &InterestGroup::Ad::render_url,
+                                         GURL("https://ads.example.com/1")),
                                 Field("metadata", &InterestGroup::Ad::metadata,
                                       "[\"4\",\"5\",null,\"6\"]"))))),
                   Field("ad_components", &InterestGroup::ad_components,
diff --git a/content/browser/interest_group/interest_group_update_manager.cc b/content/browser/interest_group/interest_group_update_manager.cc
index f5d7aae..7920fa8d 100644
--- a/content/browser/interest_group/interest_group_update_manager.cc
+++ b/content/browser/interest_group/interest_group_update_manager.cc
@@ -11,6 +11,7 @@
 #include <cstddef>
 #include <map>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -378,8 +379,11 @@
     if (!maybe_render_url) {
       return absl::nullopt;
     }
-    blink::InterestGroup::Ad ad;
-    ad.render_url = GURL(*maybe_render_url);
+    GURL render_gurl = GURL(*maybe_render_url);
+    if (!render_gurl.is_valid()) {
+      return absl::nullopt;
+    }
+    blink::InterestGroup::Ad ad(render_gurl, /*metadata=*/std::nullopt);
     const std::string* maybe_size_group = ads_dict->FindString("sizeGroup");
     if (maybe_size_group) {
       ad.size_group = *maybe_size_group;
diff --git a/content/browser/interest_group/storage_interest_group.h b/content/browser/interest_group/storage_interest_group.h
index c7c9ae3..9800890 100644
--- a/content/browser/interest_group/storage_interest_group.h
+++ b/content/browser/interest_group/storage_interest_group.h
@@ -14,7 +14,6 @@
 #include "mojo/public/cpp/bindings/struct_ptr.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/interest_group/interest_group.h"
-#include "url/gurl.h"
 #include "url/origin.h"
 
 namespace content {
diff --git a/content/browser/network/trust_token_origin_trial_browsertest.cc b/content/browser/network/trust_token_origin_trial_browsertest.cc
index b182665..12cc4aa 100644
--- a/content/browser/network/trust_token_origin_trial_browsertest.cc
+++ b/content/browser/network/trust_token_origin_trial_browsertest.cc
@@ -122,7 +122,6 @@
 }
 
 struct TestDescription {
-  network::mojom::TrustTokenMajorVersion version;
   Op op;
   Outcome outcome;
   TrialType trial_type;
@@ -240,43 +239,28 @@
 };
 
 const TestDescription kTestDescriptions[] = {
-    {network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
-     Op::kIssuance, Outcome::kSuccess,
+    {Op::kIssuance, Outcome::kSuccess,
      TrialType::kOnlyIssuanceRequiresOriginTrial, TrialEnabled::kEnabled},
 
-    {network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
-     Op::kRedemption, Outcome::kSuccess,
+    {Op::kRedemption, Outcome::kSuccess,
      TrialType::kOnlyIssuanceRequiresOriginTrial, TrialEnabled::kEnabled},
 
-    {network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
-     Op::kRedemption, Outcome::kSuccess,
+    {Op::kRedemption, Outcome::kSuccess,
      TrialType::kOnlyIssuanceRequiresOriginTrial, TrialEnabled::kDisabled},
 
-    {network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
-     Op::kIssuance, Outcome::kSuccess,
+    {Op::kIssuance, Outcome::kSuccess,
      TrialType::kAllOperationsRequireOriginTrial, TrialEnabled::kEnabled},
 
-    {network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
-     Op::kIssuance, Outcome::kSuccessWithoutTrustTokenParams,
+    {Op::kIssuance, Outcome::kSuccessWithoutTrustTokenParams,
      TrialType::kAllOperationsRequireOriginTrial, TrialEnabled::kDisabled},
 
-    {network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
-     Op::kRedemption, Outcome::kSuccess,
+    {Op::kRedemption, Outcome::kSuccess,
      TrialType::kAllOperationsRequireOriginTrial, TrialEnabled::kEnabled},
 
-    {network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
-     Op::kRedemption, Outcome::kSuccessWithoutTrustTokenParams,
+    {Op::kRedemption, Outcome::kSuccessWithoutTrustTokenParams,
      TrialType::kAllOperationsRequireOriginTrial, TrialEnabled::kDisabled},
 };
 
-std::string ToString(network::mojom::TrustTokenMajorVersion version) {
-  if (version == network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1) {
-    return "1";
-  }
-  NOTREACHED();
-  return "";
-}
-
 // Prints a string representation to use for generating test names.
 std::string ToString(Op op) {
   switch (op) {
@@ -297,9 +281,9 @@
   const TestDescription& test_description = std::get<1>(info.param);
 
   return base::ReplaceStringPlaceholders(
-      "$1_$2_$3_$4_$5",
-      {ToString(interface), ToString(test_description.version),
-       ToString(test_description.op), ToString(test_description.trial_type),
+      "$1_$2_$3_$4",
+      {ToString(interface), ToString(test_description.op),
+       ToString(test_description.trial_type),
        ToString(test_description.trial_enabled)},
       nullptr);
 }
@@ -349,8 +333,7 @@
   }
 
   network::TrustTokenTestParameters trust_token_params(
-      test_description.version, test_description.op, absl::nullopt,
-      absl::nullopt);
+      1, test_description.op, absl::nullopt, absl::nullopt);
 
   network::TrustTokenParametersAndSerialization
       expected_params_and_serialization =
diff --git a/content/browser/network_service_restart_browsertest.cc b/content/browser/network_service_restart_browsertest.cc
index 90036af..71e0ebd 100644
--- a/content/browser/network_service_restart_browsertest.cc
+++ b/content/browser/network_service_restart_browsertest.cc
@@ -1098,29 +1098,20 @@
 }
 
 class NetworkServiceRestartWithFirstPartySetBrowserTest
-    : public NetworkServiceRestartBrowserTest,
-      public testing::WithParamInterface<bool> {
+    : public NetworkServiceRestartBrowserTest {
  public:
   NetworkServiceRestartWithFirstPartySetBrowserTest()
       : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
-    if (IsFirstPartySetsEnabled()) {
-      scoped_feature_list_.InitWithFeatures(
-          {features::kFirstPartySets,
-           net::features::kWaitForFirstPartySetsInit},
-          {});
-    } else {
-      scoped_feature_list_.InitAndDisableFeature(features::kFirstPartySets);
-    }
+    scoped_feature_list_.InitWithFeatures(
+        {net::features::kWaitForFirstPartySetsInit}, {});
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     NetworkServiceRestartBrowserTest::SetUpCommandLine(command_line);
-    if (IsFirstPartySetsEnabled()) {
-      command_line->AppendSwitchASCII(
-          network::switches::kUseRelatedWebsiteSet,
-          R"({"primary": "https://a.test",)"
-          R"("associatedSites": ["https://b.test","https://c.test"]})");
-    }
+    command_line->AppendSwitchASCII(
+        network::switches::kUseRelatedWebsiteSet,
+        R"({"primary": "https://a.test",)"
+        R"("associatedSites": ["https://b.test","https://c.test"]})");
   }
 
   void SetUpOnMainThread() override {
@@ -1145,14 +1136,12 @@
 
   WebContents* web_contents() { return shell()->web_contents(); }
 
-  bool IsFirstPartySetsEnabled() const { return GetParam(); }
-
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
   net::test_server::EmbeddedTestServer https_server_;
 };
 
-IN_PROC_BROWSER_TEST_P(NetworkServiceRestartWithFirstPartySetBrowserTest,
+IN_PROC_BROWSER_TEST_F(NetworkServiceRestartWithFirstPartySetBrowserTest,
                        GetsUseFirstPartySetSwitch) {
   // Network service is not running out of process, so cannot be crashed.
   if (!content::IsOutOfProcessNetworkService()) {
@@ -1181,8 +1170,4 @@
                   testing::UnorderedElementsAre(testing::Key(kCookieName))));
 }
 
-INSTANTIATE_TEST_SUITE_P(/* no prefix */,
-                         NetworkServiceRestartWithFirstPartySetBrowserTest,
-                         testing::Bool());
-
 }  // namespace content
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 253d9be1..cb0b414 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -8490,8 +8490,8 @@
         "aggregation events.");
     return;
   }
-  // We only care if the event type starts with "reserved." - We allow event
-  // types like "myevent.reserved.name".
+  // Only check if the event type starts with "reserved." - We allow event types
+  // like "myevent.reserved.name".
   if (base::StartsWith(event_type, blink::kFencedFrameReservedPAEventPrefix)) {
     mojo::ReportBadMessage("Reserved events cannot be triggered manually.");
     return;
@@ -8633,6 +8633,13 @@
     return;
   }
 
+  // Only check if the event type starts with "reserved." - We allow event types
+  // like "myevent.reserved.name".
+  if (base::StartsWith(event_type, blink::kFencedFrameReservedPAEventPrefix)) {
+    mojo::ReportBadMessage("Reserved events cannot be triggered manually.");
+    return;
+  }
+
   for (const blink::FencedFrame::ReportingDestination& destination :
        destinations) {
     SendFencedFrameReportingBeaconInternal(
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 904901b..2906d59 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -1753,8 +1753,6 @@
   // Prepare to intercept BeginNavigation mojo IPC. This has to be done before
   // the test creates the RenderFrameHostImpl that is the target of the IPC.
   auto params = network::mojom::TrustTokenParams::New();
-  params->version =
-      network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1;
   params->operation = network::mojom::TrustTokenOperationType::kRedemption;
   BeginNavigationTrustTokenParamsReplacer replacer(web_contents,
                                                    std::move(params));
@@ -1793,8 +1791,6 @@
   // Prepare to intercept BeginNavigation mojo IPC. This has to be done before
   // the test creates the RenderFrameHostImpl that is the target of the IPC.
   auto params = network::mojom::TrustTokenParams::New();
-  params->version =
-      network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1;
   params->operation = network::mojom::TrustTokenOperationType::kSigning;
   BeginNavigationTrustTokenParamsReplacer replacer(web_contents,
                                                    std::move(params));
@@ -1833,8 +1829,6 @@
   // Prepare to intercept BeginNavigation mojo IPC. This has to be done before
   // the test creates the RenderFrameHostImpl that is the target of the IPC.
   auto params = network::mojom::TrustTokenParams::New();
-  params->version =
-      network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1;
   params->operation = network::mojom::TrustTokenOperationType::kIssuance;
   BeginNavigationTrustTokenParamsReplacer replacer(web_contents,
                                                    std::move(params));
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 463c2de..5fc3d65 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -408,7 +408,6 @@
           {"PartitionedCookies", raw_ref(net::features::kPartitionedCookies)},
           {"ReduceAcceptLanguage",
            raw_ref(network::features::kReduceAcceptLanguage)},
-          {"StorageAccessAPI", raw_ref(features::kFirstPartySets)},
           {"TopicsAPI", raw_ref(features::kPrivacySandboxAdsAPIsOverride),
            kSetOnlyIfOverridden},
           {"TopicsAPI", raw_ref(features::kPrivacySandboxAdsAPIsM1Override),
diff --git a/content/common/features.cc b/content/common/features.cc
index 4fdee638..cef23c62 100644
--- a/content/common/features.cc
+++ b/content/common/features.cc
@@ -51,14 +51,6 @@
              "BackForwardCacheTimeToLiveControl",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Sets moderate binding to background renderers playing media, when enabled.
-// Else the renderer will have strong binding.
-#if BUILDFLAG(IS_ANDROID)
-BASE_FEATURE(kBackgroundMediaRendererHasModerateBinding,
-             "BackgroundMediaRendererHasModerateBinding",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-#endif
-
 // When enabled, the browser will schedule before unload tasks that continue
 // navigation network responses in a kHigh priority queue.
 // TODO(b/281094330): Run experiment on ChromeOS. Experiment was not run on
diff --git a/content/common/features.h b/content/common/features.h
index 17f5049..c6e86f7 100644
--- a/content/common/features.h
+++ b/content/common/features.h
@@ -17,9 +17,6 @@
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kAndroidDownloadableFontsMatching);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kAvoidUnnecessaryBeforeUnloadCheckSync);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kBackForwardCacheTimeToLiveControl);
-#if BUILDFLAG(IS_ANDROID)
-CONTENT_EXPORT BASE_DECLARE_FEATURE(kBackgroundMediaRendererHasModerateBinding);
-#endif
 BASE_DECLARE_FEATURE(kBeforeUnloadBrowserResponseQueue);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(
     kBlockInsecurePrivateNetworkRequestsFromUnknown);
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
index 9a27e92..d6e1892 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
@@ -41,7 +41,6 @@
 import org.chromium.content.app.SandboxedProcessService;
 import org.chromium.content.common.ContentSwitchUtils;
 import org.chromium.content_public.browser.ChildProcessImportance;
-import org.chromium.content_public.browser.ContentFeatureList;
 import org.chromium.content_public.browser.ContentFeatureMap;
 import org.chromium.content_public.common.ContentFeatures;
 import org.chromium.content_public.common.ContentSwitches;
@@ -769,19 +768,14 @@
             boostForPendingViews = false;
         }
 
-        boolean mediaRendererHasModerate =
-                ContentFeatureMap.isEnabled(
-                        ContentFeatureList.BACKGROUND_MEDIA_RENDERER_HAS_MODERATE_BINDING);
-
         @ChildProcessImportance int newEffectiveImportance;
         if ((visible && frameDepth == 0)
                 || importance == ChildProcessImportance.IMPORTANT
-                || (hasMediaStream && !mediaRendererHasModerate)) {
+                || hasMediaStream) {
             newEffectiveImportance = ChildProcessImportance.IMPORTANT;
         } else if ((visible && frameDepth > 0 && intersectsViewport)
                 || boostForPendingViews
                 || importance == ChildProcessImportance.MODERATE
-                || (hasMediaStream && mediaRendererHasModerate)
                 || hasForegroundServiceWorker) {
             newEffectiveImportance = ChildProcessImportance.MODERATE;
         } else {
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java b/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
index dd8a74c..862d9e73 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
@@ -24,9 +24,6 @@
 
     public static final String AUTO_DISABLE_ACCESSIBILITY_V2 = "AutoDisableAccessibilityV2";
 
-    public static final String BACKGROUND_MEDIA_RENDERER_HAS_MODERATE_BINDING =
-            "BackgroundMediaRendererHasModerateBinding";
-
     public static final String MOUSE_AND_TRACKPAD_DROPDOWN_MENU = "MouseAndTrackpadDropdownMenu";
 
     public static final String OPTIMIZE_IMM_HIDE_CALLS = "OptimizeImmHideCalls";
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index ed845bf..1c57172 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -1557,7 +1557,7 @@
 }
 
 bool ContentBrowserClient::IsFirstPartySetsEnabled() {
-  return base::FeatureList::IsEnabled(features::kFirstPartySets);
+  return true;
 }
 
 bool ContentBrowserClient::WillProvidePublicFirstPartySets() {
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 7e8a614..6e1d0aaa 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -441,29 +441,6 @@
              "WebIdentityDigitalCredentials",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Enables usage of First Party Sets to determine cookie availability.
-BASE_FEATURE(kFirstPartySets,
-             "FirstPartySets",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-// Controls whether to clear sites data on FPS transitions.
-const base::FeatureParam<bool> kFirstPartySetsClearSiteDataOnChangedSets{
-    &kFirstPartySets, "FirstPartySetsClearSiteDataOnChangedSets", true};
-
-// Controls how many sites are allowed to be in the Associated subset (ignoring
-// ccTLD aliases).
-const base::FeatureParam<int> kFirstPartySetsMaxAssociatedSites{
-    &kFirstPartySets, "FirstPartySetsMaxAssociatedSites", 5};
-
-// Controls the maximum time duration an outermost frame navigation should be
-// deferred by FPS initialization.
-// Using 2s as the starting default timeout. This is based on the UMA metric
-// `History.ClearBrowsingData.Duration.OriginDeletion`.
-const base::FeatureParam<base::TimeDelta>
-    kFirstPartySetsNavigationThrottleTimeout{
-        &kFirstPartySets, "FirstPartySetsNavigationThrottleTimeout",
-        base::Seconds(0)};
-
 // Enables scrollers inside Blink to store scroll offsets in fractional
 // floating-point numbers rather than truncating to integers.
 BASE_FEATURE(kFractionalScrollOffsets,
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 2f54f93..eeaf030c 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -107,13 +107,6 @@
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmWithoutWellKnownEnforcement);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFencedFramesEnforceFocus);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebIdentityDigitalCredentials);
-CONTENT_EXPORT BASE_DECLARE_FEATURE(kFirstPartySets);
-CONTENT_EXPORT extern const base::FeatureParam<bool>
-    kFirstPartySetsClearSiteDataOnChangedSets;
-CONTENT_EXPORT extern const base::FeatureParam<int>
-    kFirstPartySetsMaxAssociatedSites;
-CONTENT_EXPORT extern const base::FeatureParam<base::TimeDelta>
-    kFirstPartySetsNavigationThrottleTimeout;
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFractionalScrollOffsets);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kGreaseUACH);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kInstalledApp);
diff --git a/content/public/common/content_switch_dependent_feature_overrides.cc b/content/public/common/content_switch_dependent_feature_overrides.cc
index 7a36706..d305a83c 100644
--- a/content/public/common/content_switch_dependent_feature_overrides.cc
+++ b/content/public/common/content_switch_dependent_feature_overrides.cc
@@ -138,15 +138,6 @@
      std::cref(network::features::kCrossOriginOpenerPolicyByDefault),
      base::FeatureList::OVERRIDE_ENABLE_FEATURE},
 
-    // Overrides for First-Party Sets, which is enabled when either switch is
-    // present. When both switches are present, `kUseRelatedWebsiteSet` takes
-    // precedence.
-    {network::switches::kUseRelatedWebsiteSet,
-     std::cref(features::kFirstPartySets),
-     base::FeatureList::OVERRIDE_ENABLE_FEATURE},
-    {network::switches::kUseFirstPartySet, std::cref(features::kFirstPartySets),
-     base::FeatureList::OVERRIDE_ENABLE_FEATURE},
-
     {blink::switches::kWebSQLAccess, std::cref(blink::features::kWebSQLAccess),
      base::FeatureList::OVERRIDE_ENABLE_FEATURE},
 
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index 8730892..6bf8ed6 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -97,15 +97,13 @@
 
 RenderAccessibilityImpl::RenderAccessibilityImpl(
     RenderAccessibilityManager* const render_accessibility_manager,
-    RenderFrameImpl* const render_frame,
-    bool serialize_post_lifecycle)
+    RenderFrameImpl* const render_frame)
     : RenderFrameObserver(render_frame),
       render_accessibility_manager_(render_accessibility_manager),
       render_frame_(render_frame),
       plugin_tree_source_(nullptr),
       ukm_timer_(std::make_unique<base::ElapsedTimer>()),
-      last_ukm_source_id_(ukm::kInvalidSourceId),
-      serialize_post_lifecycle_(serialize_post_lifecycle) {
+      last_ukm_source_id_(ukm::kInvalidSourceId) {
   mojo::Remote<ukm::mojom::UkmRecorderFactory> factory;
   content::RenderThread::Get()->BindHostReceiver(
       factory.BindNewPipeAndPassReceiver());
@@ -155,24 +153,13 @@
   const WebDocument& document = GetMainDocument();
   DCHECK(!document.IsNull());
   ax_context_ = std::make_unique<WebAXContext>(document, accessibility_mode_);
-  if (serialize_post_lifecycle_) {
-    ScheduleImmediateAXUpdate();
-  } else {
-    LegacyScheduleSendPendingAccessibilityEvents();
-  }
+  ScheduleImmediateAXUpdate();
 }
 
 void RenderAccessibilityImpl::DidCommitProvisionalLoad(
     ui::PageTransition transition) {
   has_injected_stylesheet_ = false;
 
-  if (!serialize_post_lifecycle_) {
-    // If we have events scheduled, but not sent, cancel them
-    LegacyCancelScheduledEvents();
-    // Defer events during initial page load.
-    legacy_event_schedule_mode_ = LegacyEventScheduleMode::kDeferEvents;
-  }
-
   MaybeSendUKM();
   slowest_serialization_time_ = base::TimeDelta();
   ukm_timer_ = std::make_unique<base::ElapsedTimer>();
@@ -291,10 +278,8 @@
     // request. Instead, the mojo reply should be used directly.
     if (event_to_fire != ax::mojom::Event::kNone) {
       const std::vector<ui::AXEventIntent> intents;
-      if (serialize_post_lifecycle_) {
-        // Marking dirty ensures that a lifecycle update will be scheduled.
-        MarkWebAXObjectDirty(ax_object, /*subtree*/ false);
-      }
+      // Marking dirty ensures that a lifecycle update will be scheduled.
+      MarkWebAXObjectDirty(ax_object, /*subtree*/ false);
       HandleAXEvent(ui::AXEvent(
           ax_object.AxID(), event_to_fire, ax::mojom::EventFrom::kAction,
           ax::mojom::Action::kHitTest, intents, request_id));
@@ -347,10 +332,6 @@
     return;
   }
 
-  // If an action was requested, we no longer want to defer events.
-  legacy_event_schedule_mode_ =
-      LegacyEventScheduleMode::kProcessEventsImmediately;
-
   std::unique_ptr<ui::AXActionTarget> target =
       AXActionTargetFactory::CreateFromNodeId(document, plugin_tree_source_,
                                               data.target_node_id);
@@ -440,13 +421,9 @@
       break;
   }
 
-  if (serialize_post_lifecycle_) {
-    // Ensure the next serialization comes immediately after the action is
-    // complete, even if the document is still loading.
-    ScheduleImmediateAXUpdate();
-  } else {
-    ax_context_->UpdateAXForAllDocuments();
-  }
+  // Ensure the next serialization comes immediately after the action is
+  // complete, even if the document is still loading.
+  ScheduleImmediateAXUpdate();
 }
 
 void RenderAccessibilityImpl::Reset(uint32_t reset_token) {
@@ -458,8 +435,6 @@
   FireLoadCompleteIfLoaded();
 }
 
-// TODO(accessibility): When legacy mode is deleted, calls to this function may
-// be replaced with obj.AddDirtyObjectToSerializationQueue
 void RenderAccessibilityImpl::MarkWebAXObjectDirty(
     const WebAXObject& obj,
     bool subtree,
@@ -472,32 +447,6 @@
 
   obj.AddDirtyObjectToSerializationQueue(subtree, event_from, event_from_action,
                                          event_intents);
-
-  // The logic below here is handled now in AXObjectCache and thus only runs in
-  // legacy mode.
-  if (!serialize_post_lifecycle_) {
-    NotifyWebAXObjectMarkedDirty(obj, event_type);
-  }
-}
-
-// TODO(accessibility): Delete once legacy mode is removed.
-void RenderAccessibilityImpl::NotifyWebAXObjectMarkedDirty(
-    const blink::WebAXObject& obj,
-    ax::mojom::Event event_type) {
-  DCHECK(!serialize_post_lifecycle_);
-
-  // The root is an exception because it often has focus while the page is
-  // loading. In that case the event type is used as the signal (see
-  // HandleAXEvent() and IsImmediateProcessingRequiredForEvent()).
-  if (legacy_event_schedule_mode_ ==
-      LegacyEventScheduleMode::kProcessEventsImmediately) {
-    return;
-  }
-    if (obj != ComputeRoot() && obj.IsFocused()) {
-      legacy_event_schedule_mode_ =
-          LegacyEventScheduleMode::kProcessEventsImmediately;
-    }
-    LegacyScheduleSendPendingAccessibilityEvents();
 }
 
 // TODO(accessibility): Replace all instances of HandleAXEvent with
@@ -512,149 +461,12 @@
     loading_stage_ = LoadingStage::kLoadCompleted;
   }
 
-  if (serialize_post_lifecycle_) {
-    ax_context_->AddEventToSerializationQueue(
-        event, true);  // All events sent to AXObjectCache from RAI need
-                       // immediate serialization!
-    return;
-  }
-
-  // TODO(accessibility): Delete this code when legacy mode is removed.
-  // In lifecycle mode, the below logic is handled in ax_object_cache via
-  // AddEventToSerializationQueue.
-  DCHECK(!serialize_post_lifecycle_);
-  const WebDocument& document = GetMainDocument();
-  DCHECK(!document.IsNull());
-
-  auto obj = WebAXObject::FromWebDocumentByID(document, event.id);
-  DCHECK(!obj.IsDetached());
-
-  if (!ax_context_->AddPendingEvent(event)) {
-    DCHECK(ax_context_);
-    return;
-  }
-
-  MarkWebAXObjectDirty(obj, /* subtree= */ false, event.event_from,
-                       event.event_from_action, event.event_intents,
-                       event.event_type);
-
-  if (IsImmediateProcessingRequiredForEvent(event)) {
-    legacy_event_schedule_mode_ =
-        LegacyEventScheduleMode::kProcessEventsImmediately;
-  }
-
-  LegacyScheduleSendPendingAccessibilityEvents();
-}
-
-// TODO(accessibility): When legacy mode is deleted, this function can go as
-// it's only used in RenderAccessibilityImpl::HandleAXEvent legacy mode.
-bool RenderAccessibilityImpl::IsImmediateProcessingRequiredForEvent(
-    const ui::AXEvent& event) const {
-  DCHECK(!serialize_post_lifecycle_);
-  if (legacy_event_schedule_mode_ ==
-      LegacyEventScheduleMode::kProcessEventsImmediately) {
-    return true;  // Already scheduled for immediate mode.
-  }
-
-  if (event.event_from == ax::mojom::EventFrom::kAction) {
-    return true;  // Actions should result in an immediate response.
-  }
-
-  switch (event.event_type) {
-    case ax::mojom::Event::kActiveDescendantChanged:
-    case ax::mojom::Event::kBlur:
-    case ax::mojom::Event::kCheckedStateChanged:
-    case ax::mojom::Event::kClicked:
-    case ax::mojom::Event::kDocumentSelectionChanged:
-    case ax::mojom::Event::kFocus:
-    case ax::mojom::Event::kHover:
-    case ax::mojom::Event::kLoadComplete:
-    case ax::mojom::Event::kLoadStart:
-    case ax::mojom::Event::kValueChanged:
-      return true;
-
-    case ax::mojom::Event::kEndOfTest:
-    case ax::mojom::Event::kImageFrameUpdated:
-    case ax::mojom::Event::kTreeChanged:
-      return serialize_post_lifecycle_;
-
-    case ax::mojom::Event::kDocumentTitleChanged:
-    case ax::mojom::Event::kExpandedChanged:
-    case ax::mojom::Event::kHide:
-    case ax::mojom::Event::kLayoutComplete:
-    case ax::mojom::Event::kLocationChanged:
-    case ax::mojom::Event::kMenuListValueChanged:
-    case ax::mojom::Event::kRowCollapsed:
-    case ax::mojom::Event::kRowCountChanged:
-    case ax::mojom::Event::kRowExpanded:
-    case ax::mojom::Event::kScrollPositionChanged:
-    case ax::mojom::Event::kScrolledToAnchor:
-    case ax::mojom::Event::kSelectedChildrenChanged:
-    case ax::mojom::Event::kShow:
-    case ax::mojom::Event::kTextChanged:
-      return false;
-
-    // These events are not fired from Blink.
-    // This list is duplicated in WebFrameTestProxy::PostAccessibilityEvent().
-    case ax::mojom::Event::kAlert:
-    case ax::mojom::Event::kAriaAttributeChangedDeprecated:
-    case ax::mojom::Event::kAutocorrectionOccured:
-    case ax::mojom::Event::kChildrenChanged:
-    case ax::mojom::Event::kControlsChanged:
-    case ax::mojom::Event::kFocusAfterMenuClose:
-    case ax::mojom::Event::kFocusContext:
-    case ax::mojom::Event::kHitTestResult:
-    case ax::mojom::Event::kLiveRegionCreated:
-    case ax::mojom::Event::kLiveRegionChanged:
-    case ax::mojom::Event::kMediaStartedPlaying:
-    case ax::mojom::Event::kMediaStoppedPlaying:
-    case ax::mojom::Event::kMenuEnd:
-    case ax::mojom::Event::kMenuPopupEnd:
-    case ax::mojom::Event::kMenuPopupStart:
-    case ax::mojom::Event::kMenuStart:
-    case ax::mojom::Event::kMouseCanceled:
-    case ax::mojom::Event::kMouseDragged:
-    case ax::mojom::Event::kMouseMoved:
-    case ax::mojom::Event::kMousePressed:
-    case ax::mojom::Event::kMouseReleased:
-    case ax::mojom::Event::kNone:
-    case ax::mojom::Event::kSelection:
-    case ax::mojom::Event::kSelectionAdd:
-    case ax::mojom::Event::kSelectionRemove:
-    case ax::mojom::Event::kStateChanged:
-    case ax::mojom::Event::kTextSelectionChanged:
-    case ax::mojom::Event::kTooltipClosed:
-    case ax::mojom::Event::kTooltipOpened:
-    case ax::mojom::Event::kWindowActivated:
-    case ax::mojom::Event::kWindowDeactivated:
-    case ax::mojom::Event::kWindowVisibilityChanged:
-      // Never fired from Blink.
-      NOTREACHED() << "Event not expected from Blink: " << event.event_type;
-      return false;
-  }
-}
-
-// TODO(accessibility): When legacy mode is deleted, this function can go. All
-// deferring is now done in ax_object_cache.
-int RenderAccessibilityImpl::GetDeferredEventsDelay() {
-  // The amount of time, in milliseconds, to wait before sending non-interactive
-  // events that are deferred before the initial page load.
-  constexpr int kDelayForDeferredUpdatesBeforePageLoad = 350;
-
-  // The amount of time, in milliseconds, to wait before sending non-interactive
-  // events that are deferred after the initial page load.
-  // Shync with same constant in CrossPlatformAccessibilityBrowserTest.
-  constexpr int kDelayForDeferredUpdatesAfterPageLoad = 150;
-
-  // Prefer WebDocument::IsLoaded() over WebAXObject::IsLoaded() as the
-  // latter could trigger a layout update while retrieving the root
-  // WebAXObject.
-  return GetMainDocument().IsLoaded() ? kDelayForDeferredUpdatesAfterPageLoad
-                                      : kDelayForDeferredUpdatesBeforePageLoad;
+  ax_context_->AddEventToSerializationQueue(
+      event, true);  // All events sent to AXObjectCache from RAI need
+  // immediate serialization!
 }
 
 void RenderAccessibilityImpl::AXReadyCallback() {
-  DCHECK(serialize_post_lifecycle_);
   DCHECK(ax_context_);
   DCHECK(ax_context_->HasDirtyObjects())
       << "Should not call AXReadyCallback() unless there is something to "
@@ -683,101 +495,8 @@
 // TODO(accessibility): When legacy mode is deleted, calls to this function may
 // be replaced with ax_context_->ScheduleImmediateSerialization()
 void RenderAccessibilityImpl::ScheduleImmediateAXUpdate() {
-  if (serialize_post_lifecycle_) {
-    DCHECK(ax_context_);
-    ax_context_->ScheduleImmediateSerialization();
-  } else {
-    // This method is currently only used for RenderAccessibilityImplLegacyTest
-    // tests, which is expected to change in synchronous a11y implementation.
-    legacy_event_schedule_mode_ =
-        LegacyEventScheduleMode::kProcessEventsImmediately;
-    LegacyScheduleSendPendingAccessibilityEvents(true);
-  }
-}
-
-void RenderAccessibilityImpl::LegacyScheduleSendPendingAccessibilityEvents(
-    bool scheduling_from_task) {
-  DCHECK(!serialize_post_lifecycle_);
-
-  // Don't send accessibility events for frames that are not in the frame tree
-  // yet (i.e., provisional frames used for remote-to-local navigations, which
-  // haven't committed yet).  Doing so might trigger layout, which may not work
-  // correctly for those frames.  The events should be sent once such a frame
-  // commits.
-  if (!render_frame_ || !render_frame_->in_frame_tree()) {
-    return;
-  }
-
-  // Don't send accessibility events for frames that don't yet have an tree id
-  // as doing so will cause the browser to discard that message and all
-  // subsequent ones.
-  // TODO(1231184): There are some cases where no content is currently rendered,
-  // due to an iframe returning 204 or window.stop() being called. In these
-  // cases there will never be an AXTreeID as there is no commit, which will
-  // prevent accessibility updates from ever being sent even if the rendering is
-  // fixed. See also other TODOs related to 1231184.
-  if (!render_frame_->GetWebFrame()->GetAXTreeID().token()) {
-    return;
-  }
-
-  switch (legacy_event_schedule_status_) {
-    case LegacyEventScheduleStatus::kScheduledDeferred:
-      if (legacy_event_schedule_mode_ ==
-          LegacyEventScheduleMode::kProcessEventsImmediately) {
-        // Cancel scheduled deferred events so we can schedule events to be
-        // sent immediately.
-        LegacyCancelScheduledEvents();
-        break;
-      }
-      // We have already scheduled a task to send pending events.
-      return;
-    case LegacyEventScheduleStatus::kScheduledImmediate:
-      // The send pending events task have been scheduled, but has not started.
-      return;
-    case LegacyEventScheduleStatus::kWaitingForAck:
-      // Events have been sent, wait for ack.
-      return;
-    case LegacyEventScheduleStatus::kNotWaiting:
-      // Once the events have been handled, we schedule the pending events from
-      // that task. In this case, there would be a weak ptr still in use.
-      if (!scheduling_from_task &&
-          weak_factory_for_pending_events_.HasWeakPtrs()) {
-        return;
-      }
-      break;
-  }
-
-  base::TimeDelta delay = base::TimeDelta();
-  switch (legacy_event_schedule_mode_) {
-    case LegacyEventScheduleMode::kDeferEvents:
-      legacy_event_schedule_status_ =
-          LegacyEventScheduleStatus::kScheduledDeferred;
-      // Where the user is not currently navigating or typing,
-      // process changes on a delay so that they occur in larger batches,
-      // improving efficiency of repetitive mutations.
-      delay = base::Milliseconds(GetDeferredEventsDelay());
-      break;
-    case LegacyEventScheduleMode::kProcessEventsImmediately:
-      // This set of events needed to be processed immediately because of a
-      // page load or user action.
-      legacy_event_schedule_status_ =
-          LegacyEventScheduleStatus::kScheduledImmediate;
-      delay = base::TimeDelta();
-      break;
-  }
-
-  // When no accessibility events are in-flight post a task to send
-  // the events to the browser. We use PostTask so that we can queue
-  // up additional events.
-  DCHECK(!ax_context_->IsSerializationInFlight());
-  ax_context_->OnSerializationStartSend();
-  render_frame_->GetTaskRunner(blink::TaskType::kInternalDefault)
-      ->PostDelayedTask(
-          FROM_HERE,
-          base::BindOnce(
-              &RenderAccessibilityImpl::SendPendingAccessibilityEvents,
-              weak_factory_for_pending_events_.GetWeakPtr()),
-          delay);
+  DCHECK(ax_context_);
+  ax_context_->ScheduleImmediateSerialization();
 }
 
 bool RenderAccessibilityImpl::HasActiveDocument() const {
@@ -1117,15 +836,9 @@
     if (!WebAXObject::IsDirty(document) && GetMainDocument().IsLoaded()) {
       events.emplace_back(end_of_test);
     } else {
-      DLOG(ERROR) << "Had end of test event, but document is still dirty. "
-                     "Serialize post lifecycle: "
-                  << serialize_post_lifecycle_;
+      DLOG(ERROR) << "Had end of test event, but document is still dirty.";
       // Document is still dirty, queue up another end of test and process
       // immediately.
-      if (!serialize_post_lifecycle_) {
-        legacy_event_schedule_mode_ =
-            LegacyEventScheduleMode::kProcessEventsImmediately;
-      }
       HandleAXEvent(end_of_test);
     }
   }
@@ -1144,19 +857,11 @@
                "RenderAccessibilityImpl::SendPendingAccessibilityEvents");
   base::ElapsedTimer timer;
 
-  // If serialize_post_lifecycle_ is false, then this method is scheduled
-  // asynchronously and serialization_in_flight_ is set at the point of
-  // scheduling. If serialize_post_lifecycle_ is true, then this method is
-  // called synchronously, but should never be called if there's a previous
-  // serialization still in flight.
-  DCHECK(!serialize_post_lifecycle_ || !ax_context_->IsSerializationInFlight());
+  // This method should never be called if there's a previous serialization
+  // still in flight.
+  DCHECK(!ax_context_->IsSerializationInFlight());
 
-  if (!serialize_post_lifecycle_) {
-    // Clear status here in case we return early.
-    legacy_event_schedule_status_ = LegacyEventScheduleStatus::kNotWaiting;
-  }
   WebDocument document = GetMainDocument();
-  DCHECK(serialize_post_lifecycle_ || !document.IsNull());
   if (document.IsNull()) {
     return;
   }
@@ -1171,60 +876,6 @@
   DCHECK(ax_context_);
   ax_context_->OnSerializationStartSend();
 
-  if (!serialize_post_lifecycle_) {
-    DCHECK(document.IsAccessibilityEnabled())
-        << "SendPendingAccessibilityEvents should not do any work when nothing "
-           "has enabled accessibility.";
-
-    // TODO(aleventhal): legacy_needs_initial_ax_tree_root_ and this whole piece
-    // of logic will eventually either go away or move to AXObjectCacheImpl,
-    // where it can be done more simply. Basically we want to fire a page load
-    // event when waking up and the page was already loaded.
-    if (legacy_needs_initial_ax_tree_root_) {
-      // At the very start of accessibility for this document, push a layout
-      // complete for the entire document, in order to initialize the browser's
-      // cached accessibility tree.
-      legacy_needs_initial_ax_tree_root_ = false;
-      ax_context_->UpdateAXForAllDocuments();
-      auto root_obj = WebAXObject::FromWebDocument(document);
-      // Always fire layout complete for a new root object.
-      // TODO(aleventhal): eventually hopefully we can get rid of
-      // insert_at_beginning. We only need that for inserting the fake load
-      // event when we wake up to an already-loaded page. But it would be better
-      // to just insert the kLoadComplete event when creating the root and the
-      // page was already loaded.
-      ax_context_->AddPendingEvent(
-          ui::AXEvent(root_obj.AxID(), ax::mojom::Event::kLayoutComplete),
-          true /* insert_at_beginning*/);
-      MarkWebAXObjectDirty(root_obj, false);
-
-      // If loaded and has some content, insert load complete at the top, so
-      // that screen readers are informed a new document is ready. This is
-      // helpful in the case where the screen reader is launched after the page
-      // was already loaded.
-      if (root_obj.IsLoaded() && !document.Body().IsNull() &&
-          !document.Body().FirstChild().IsNull()) {
-        ax_context_->AddPendingEvent(
-            ui::AXEvent(root_obj.AxID(), ax::mojom::Event::kLoadComplete),
-            true /* insert_at_beginning*/);
-      }
-    }
-
-    if (!ax_context_->HasDirtyObjects()) {
-      // By default, assume the next batch does not have interactive events, and
-      // defer so that the batch of events is larger. If any interactive events
-      // come in, the batch will be processed immediately.
-      legacy_event_schedule_mode_ = LegacyEventScheduleMode::kDeferEvents;
-      ax_context_->OnSerializationCancelled();
-      return;
-    }
-
-    // Update layout before snapshotting the events so that live state read from
-    // the DOM during freezing (e.g. which node currently has focus) is
-    // consistent with the events and node data we're about to send up.
-    ax_context_->UpdateAXForAllDocuments();
-  }
-
   WebAXObject root = ComputeRoot();
 #if DCHECK_IS_ON()
   // Never causes a document lifecycle change during serialization,
@@ -1244,11 +895,9 @@
 #if DCHECK_IS_ON()
   // Protect against lifecycle changes in the popup document, if any.
   WebDocument popup_document = GetPopupDocument();
-  std::unique_ptr<blink::WebDisallowTransitionScope> disallow2;
-  if (!popup_document.IsNull() &&
-      (serialize_post_lifecycle_ || !image_annotation_debugging_)) {
-    disallow2 =
-        std::make_unique<blink::WebDisallowTransitionScope>(&popup_document);
+  absl::optional<blink::WebDisallowTransitionScope> disallow2;
+  if (!popup_document.IsNull()) {
+    disallow2.emplace(&popup_document);
   }
 #endif
 
@@ -1280,31 +929,14 @@
   }
 
   CHECK(reset_token_);
-  if (serialize_post_lifecycle_) {
-    render_accessibility_manager_->HandleAccessibilityEvents(
-        std::move(updates_and_events), *reset_token_,
-        base::BindOnce(&RenderAccessibilityImpl::OnSerializationReceived,
-                       weak_factory_for_pending_events_.GetWeakPtr()));
-  } else {
-    legacy_event_schedule_status_ = LegacyEventScheduleStatus::kWaitingForAck;
-    render_accessibility_manager_->HandleAccessibilityEvents(
-        std::move(updates_and_events), *reset_token_,
-        base::BindOnce(
-            &RenderAccessibilityImpl::LegacyOnAccessibilityEventsHandled,
-            weak_factory_for_pending_events_.GetWeakPtr()));
-  }
+  render_accessibility_manager_->HandleAccessibilityEvents(
+      std::move(updates_and_events), *reset_token_,
+      base::BindOnce(&RenderAccessibilityImpl::OnSerializationReceived,
+                     weak_factory_for_pending_events_.GetWeakPtr()));
   if (need_to_send_location_changes) {
     SendLocationChanges();
   }
 
-  if (!serialize_post_lifecycle_) {
-    // Now that this batch is complete, assume the next batch does not have
-    // interactive events, and defer so that the batch of events is larger.
-    // If any interactive events come in, the batch will be processed
-    // immediately.
-    legacy_event_schedule_mode_ = LegacyEventScheduleMode::kDeferEvents;
-  }
-
   if (features::IsAblateSendPendingAccessibilityEventsEnabled()) {
     // Make the total time equal to 2x the original time.
     auto new_end_time = base::Time::Now() + timer.Elapsed();
@@ -1350,24 +982,7 @@
   ax_context_->SerializeLocationChanges(*reset_token_);
 }
 
-void RenderAccessibilityImpl::LegacyOnAccessibilityEventsHandled() {
-  DCHECK(!serialize_post_lifecycle_);
-  DCHECK_EQ(legacy_event_schedule_status_,
-            LegacyEventScheduleStatus::kWaitingForAck);
-  ax_context_->OnSerializationCancelled();
-  legacy_event_schedule_status_ = LegacyEventScheduleStatus::kNotWaiting;
-  switch (legacy_event_schedule_mode_) {
-    case LegacyEventScheduleMode::kDeferEvents:
-      LegacyScheduleSendPendingAccessibilityEvents(true);
-      break;
-    case LegacyEventScheduleMode::kProcessEventsImmediately:
-      SendPendingAccessibilityEvents();
-      break;
-  }
-}
-
 void RenderAccessibilityImpl::OnSerializationReceived() {
-  DCHECK(serialize_post_lifecycle_);
   DCHECK(ax_context_);
   ax_context_->OnSerializationReceived();
 }
@@ -1381,13 +996,9 @@
   }
   const WebAXObject& obj = blink_target->WebAXObject();
 
-  DCHECK(!serialize_post_lifecycle_ || ax_context_);
+  DCHECK(ax_context_);
   obj.OnLoadInlineTextBoxes();
 
-  if (!serialize_post_lifecycle_) {
-    legacy_event_schedule_mode_ =
-        LegacyEventScheduleMode::kProcessEventsImmediately;
-  }
   // Explicitly send a tree change update event now.
   HandleAXEvent(ui::AXEvent(obj.AxID(), ax::mojom::Event::kTreeChanged));
 }
@@ -1408,28 +1019,9 @@
   }
 
   obj.MarkSerializerSubtreeDirty();
-  if (!serialize_post_lifecycle_) {
-    legacy_event_schedule_mode_ =
-        LegacyEventScheduleMode::kProcessEventsImmediately;
-  }
   HandleAXEvent(ui::AXEvent(obj.AxID(), ax::mojom::Event::kImageFrameUpdated));
 }
 
-void RenderAccessibilityImpl::LegacyCancelScheduledEvents() {
-  DCHECK(!serialize_post_lifecycle_);
-  ax_context_->OnSerializationCancelled();
-  switch (legacy_event_schedule_status_) {
-    case LegacyEventScheduleStatus::kScheduledDeferred:
-    case LegacyEventScheduleStatus::kScheduledImmediate:  // Fallthrough
-      weak_factory_for_pending_events_.InvalidateWeakPtrs();
-      legacy_event_schedule_status_ = LegacyEventScheduleStatus::kNotWaiting;
-      break;
-    case LegacyEventScheduleStatus::kWaitingForAck:
-    case LegacyEventScheduleStatus::kNotWaiting:  // Fallthrough
-      break;
-  }
-}
-
 void RenderAccessibilityImpl::OnDestruct() {
   render_frame_ = nullptr;
   delete this;
@@ -1584,9 +1176,6 @@
 void RenderAccessibilityImpl::ConnectionClosed() {
   // This can happen when a navigation occurs with a serialization is in flight.
   // There is nothing special to do here.
-  if (!serialize_post_lifecycle_) {
-    legacy_event_schedule_status_ = LegacyEventScheduleStatus::kNotWaiting;
-  }
   ax_context_->OnSerializationCancelled();
 }
 
diff --git a/content/renderer/accessibility/render_accessibility_impl.h b/content/renderer/accessibility/render_accessibility_impl.h
index 2d537b2..fb43db4 100644
--- a/content/renderer/accessibility/render_accessibility_impl.h
+++ b/content/renderer/accessibility/render_accessibility_impl.h
@@ -85,8 +85,7 @@
   // start accessibility.
   RenderAccessibilityImpl(
       RenderAccessibilityManager* const render_accessibility_manager,
-      RenderFrameImpl* const render_frame,
-      bool serialize_post_lifecycle);
+      RenderFrameImpl* const render_frame);
 
   RenderAccessibilityImpl(const RenderAccessibilityImpl&) = delete;
   RenderAccessibilityImpl& operator=(const RenderAccessibilityImpl&) = delete;
@@ -162,44 +161,7 @@
   // versions. If any have moved, send an IPC with the new locations.
   void SendLocationChanges();
 
-  // Return true if the event indicates that the current batch of changes
-  // should be processed immediately in order for the user to get fast
-  // feedback, e.g. for navigation or data entry activities.
-  bool IsImmediateProcessingRequiredForEvent(const ui::AXEvent&) const;
-
-  // Get the amount of time, in ms, that event processing should be deferred
-  // in order to more efficiently batch changes.
-  int GetDeferredEventsDelay();
-
  private:
-  enum class LegacyEventScheduleMode {
-    kDeferEvents,
-    kProcessEventsImmediately
-  };
-
-  enum class LegacyEventScheduleStatus {
-    // Events have been scheduled with a delay, but have not been sent.
-    kScheduledDeferred,
-    // Events have been scheduled without a delay, but have not been sent.
-    kScheduledImmediate,
-    // Events have been sent, waiting for callback.
-    kWaitingForAck,
-    // Events are not scheduled and we are not waiting for an ack.
-    kNotWaiting
-  };
-
-  // Callback that will be called from the browser upon handling the message
-  // previously sent to it via SendPendingAccessibilityEvents().
-  void LegacyOnAccessibilityEventsHandled();
-
-  // If we are calling this from a task, scheduling is allowed even if there is
-  // a running task
-  void LegacyScheduleSendPendingAccessibilityEvents(
-      bool scheduling_from_task = false);
-
-  // Cancels scheduled events that are not yet in flight
-  void LegacyCancelScheduledEvents();
-
   // Called whenever the "ack" message is received for a serialization message
   // sent to the browser process, indicating it was received.
   void OnSerializationReceived();
@@ -346,22 +308,6 @@
   // A set of IDs for which we should always load inline text boxes.
   std::set<int32_t> load_inline_text_boxes_ids_;
 
-  // Controls whether serialization should be run synchronously at the end of a
-  // main frame update, or scheduled as an asynchronous task.
-  bool serialize_post_lifecycle_;
-
-  // The initial accessibility tree root still needs to be created. Like other
-  // accessible objects, it must be created when layout is clean.
-  bool legacy_needs_initial_ax_tree_root_ = true;
-
-  // Current event scheduling status
-  LegacyEventScheduleStatus legacy_event_schedule_status_ =
-      LegacyEventScheduleStatus::kNotWaiting;
-
-  // We defer events to improve performance during the initial page load.
-  LegacyEventScheduleMode legacy_event_schedule_mode_ =
-      LegacyEventScheduleMode::kDeferEvents;
-
   // So we can queue up tasks to be executed later.
   base::WeakPtrFactory<RenderAccessibilityImpl>
       weak_factory_for_pending_events_{this};
diff --git a/content/renderer/accessibility/render_accessibility_manager.cc b/content/renderer/accessibility/render_accessibility_manager.cc
index 2e52adc..a79aac1 100644
--- a/content/renderer/accessibility/render_accessibility_manager.cc
+++ b/content/renderer/accessibility/render_accessibility_manager.cc
@@ -61,10 +61,8 @@
 
   if (new_mode.has_mode(ui::AXMode::kWebContents) &&
       !old_mode.has_mode(ui::AXMode::kWebContents)) {
-    render_accessibility_ = std::make_unique<RenderAccessibilityImpl>(
-        this, render_frame_,
-        base::FeatureList::IsEnabled(
-            blink::features::kSerializeAccessibilityPostLifecycle));
+    render_accessibility_ =
+        std::make_unique<RenderAccessibilityImpl>(this, render_frame_);
   } else if (!new_mode.has_mode(ui::AXMode::kWebContents) &&
              old_mode.has_mode(ui::AXMode::kWebContents)) {
     render_accessibility_.reset();
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate.cc b/content/renderer/media/renderer_webmediaplayer_delegate.cc
index bd96be9..fa2dc32d 100644
--- a/content/renderer/media/renderer_webmediaplayer_delegate.cc
+++ b/content/renderer/media/renderer_webmediaplayer_delegate.cc
@@ -44,7 +44,9 @@
   idle_cleanup_interval_ = base::Seconds(5);
   idle_timeout_ = base::Seconds(15);
 
-  is_low_end_ = base::SysInfo::IsLowEndDeviceOrPartialLowEndModeEnabled();
+  // This imposes drastic limits on the number of media players and is only
+  // appropriate for true low end devices.
+  is_low_end_ = base::SysInfo::IsLowEndDevice();
 
   idle_cleanup_timer_.SetTaskRunner(
       render_frame->GetTaskRunner(blink::TaskType::kInternalMedia));
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index c322e3d9..c5d2bb8 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -4655,15 +4655,6 @@
       event);
 }
 
-void RenderFrameImpl::NotifyWebAXObjectMarkedDirty(
-    const blink::WebAXObject& obj) {
-  if (!IsAccessibilityEnabled())
-    return;
-
-  render_accessibility_manager_->GetRenderAccessibilityImpl()
-      ->NotifyWebAXObjectMarkedDirty(obj);
-}
-
 void RenderFrameImpl::AXReadyCallback() {
   if (!IsAccessibilityEnabled())
     return;
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 78ec171..f6a8e125 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -636,7 +636,6 @@
   bool AllowContentInitiatedDataUrlNavigations(
       const blink::WebURL& url) override;
   void PostAccessibilityEvent(const ui::AXEvent& event) override;
-  void NotifyWebAXObjectMarkedDirty(const blink::WebAXObject& object) override;
   void AXReadyCallback() override;
   void CheckIfAudioSinkExistsAndIsAuthorized(
       const blink::WebString& sink_id,
diff --git a/content/services/auction_worklet/bidder_worklet.cc b/content/services/auction_worklet/bidder_worklet.cc
index 914440e..f343c12e 100644
--- a/content/services/auction_worklet/bidder_worklet.cc
+++ b/content/services/auction_worklet/bidder_worklet.cc
@@ -231,23 +231,24 @@
 }
 
 // Converts a vector of blink::InterestGroup::Ads into a v8 object.
-bool CreateAdVector(AuctionV8Helper* v8_helper,
-                    v8::Local<v8::Context> context,
-                    base::RepeatingCallback<bool(const GURL&)> is_ad_excluded,
-                    const std::vector<blink::InterestGroup::Ad>& ads,
-                    v8::Local<v8::Value>& out_value) {
+bool CreateAdVector(
+    AuctionV8Helper* v8_helper,
+    v8::Local<v8::Context> context,
+    base::RepeatingCallback<bool(const std::string&)> is_ad_excluded,
+    const std::vector<blink::InterestGroup::Ad>& ads,
+    v8::Local<v8::Value>& out_value) {
   v8::Isolate* isolate = v8_helper->isolate();
 
   v8::LocalVector<v8::Value> ads_vector(isolate);
   for (const auto& ad : ads) {
-    if (is_ad_excluded.Run(ad.render_url)) {
+    if (is_ad_excluded.Run(ad.render_url())) {
       continue;
     }
     v8::Local<v8::Object> ad_object = v8::Object::New(isolate);
     gin::Dictionary ad_dict(isolate, ad_object);
     if (
         // TODO(crbug.com/1441988): Remove deprecated `renderUrl` alias.
-        !SetRenderUrl(isolate, ad_object, ad.render_url.spec()) ||
+        !SetRenderUrl(isolate, ad_object, ad.render_url()) ||
         (ad.metadata && !v8_helper->InsertJsonValue(context, "metadata",
                                                     *ad.metadata, ad_object))) {
       return false;
@@ -1212,28 +1213,29 @@
     }
   }
 
-  base::RepeatingCallback<bool(const GURL&)> should_exclude_ad_due_to_kanon =
-      base::BindRepeating(
+  base::RepeatingCallback<bool(const std::string&)>
+      should_exclude_ad_due_to_kanon = base::BindRepeating(
           [](bool restrict_to_kanon_ads,
              const mojom::BidderWorkletNonSharedParams* params,
              const url::Origin* owner, const GURL* bidding_url,
-             const GURL& ad_url) {
+             const std::string& ad_url_from_gurl_spec) {
             return restrict_to_kanon_ads &&
                    !BidderWorklet::IsKAnon(
-                       params,
-                       blink::KAnonKeyForAdBid(*owner, *bidding_url, ad_url));
+                       params, blink::KAnonKeyForAdBid(*owner, *bidding_url,
+                                                       ad_url_from_gurl_spec));
           },
           restrict_to_kanon_ads, &bidder_worklet_non_shared_params, &owner_,
           &script_source_url_);
 
-  base::RepeatingCallback<bool(const GURL&)>
+  base::RepeatingCallback<bool(const std::string&)>
       should_exclude_component_ad_due_to_kanon = base::BindRepeating(
           [](bool restrict_to_kanon_ads,
              const mojom::BidderWorkletNonSharedParams* params,
-             const GURL& ad_url) {
+             const std::string& ad_url_from_gurl_spec) {
             return restrict_to_kanon_ads &&
                    !BidderWorklet::IsKAnon(
-                       params, blink::KAnonKeyForAdComponentBid(ad_url));
+                       params,
+                       blink::KAnonKeyForAdComponentBid(ad_url_from_gurl_spec));
           },
           restrict_to_kanon_ads, &bidder_worklet_non_shared_params);
 
diff --git a/content/services/auction_worklet/bidder_worklet_unittest.cc b/content/services/auction_worklet/bidder_worklet_unittest.cc
index 5e6ab5e..0b7a38d6 100644
--- a/content/services/auction_worklet/bidder_worklet_unittest.cc
+++ b/content/services/auction_worklet/bidder_worklet_unittest.cc
@@ -7013,6 +7013,19 @@
       {"https://url.test/:11 Uncaught TypeError: registerAdBeacon(): invalid "
        "reporting url for key 'view': 'http://view.example.com/'."});
 
+  // Error if invalid "reserved.*" reporting event type
+  RunReportWinWithFunctionBodyExpectingResult(
+      R"(registerAdBeacon({
+        'click': "https://click.example.com/",
+        'reserved.bogus': "https://view.example.com/",
+      }))",
+      /*expected_report_url=*/absl::nullopt,
+      /*expected_ad_beacon_map=*/{},
+      /*expected_ad_macro_map=*/{},
+      /*expected_pa_requests=*/{},
+      {"https://url.test/:11 Uncaught TypeError: registerAdBeacon(): Invalid "
+       "reserved type 'reserved.bogus' cannot be used."});
+
   // Special case for error message if the key has mismatched surrogates.
   RunReportWinWithFunctionBodyExpectingResult(
       R"(registerAdBeacon({
diff --git a/content/services/auction_worklet/context_recycler_unittest.cc b/content/services/auction_worklet/context_recycler_unittest.cc
index e7c6a42..e4beb9d 100644
--- a/content/services/auction_worklet/context_recycler_unittest.cc
+++ b/content/services/auction_worklet/context_recycler_unittest.cc
@@ -311,11 +311,12 @@
     ContextRecyclerScope scope(context_recycler);  // Initialize context
     context_recycler.AddSetBidBindings();
   }
-
-  base::RepeatingCallback<bool(const GURL&)> matches_ad1 = base::BindRepeating(
-      [](const GURL& url) { return url == GURL("https://example.com/ad1"); });
-  base::RepeatingCallback<bool(const GURL&)> ignore_arg_return_false =
-      base::BindRepeating([](const GURL& ignored) { return false; });
+  base::RepeatingCallback<bool(const std::string&)> matches_ad1 =
+      base::BindRepeating([](const std::string& url) {
+        return url == "https://example.com/ad1";
+      });
+  base::RepeatingCallback<bool(const std::string&)> ignore_arg_return_false =
+      base::BindRepeating([](const std::string& ignored) { return false; });
 
   {
     mojom::BidderWorkletNonSharedParamsPtr params =
diff --git a/content/services/auction_worklet/register_ad_beacon_bindings.cc b/content/services/auction_worklet/register_ad_beacon_bindings.cc
index d409467..9866a1e 100644
--- a/content/services/auction_worklet/register_ad_beacon_bindings.cc
+++ b/content/services/auction_worklet/register_ad_beacon_bindings.cc
@@ -15,6 +15,7 @@
 #include "base/strings/string_util.h"
 #include "content/services/auction_worklet/auction_v8_helper.h"
 #include "content/services/auction_worklet/webidl_compat.h"
+#include "third_party/blink/public/common/fenced_frame/fenced_frame_utils.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
 #include "v8/include/v8-context.h"
@@ -81,6 +82,18 @@
 
   std::vector<std::pair<std::string, GURL>> ad_beacon_list;
   for (const auto& [key_string, url_string] : idl_map) {
+    if (key_string.starts_with(blink::kFencedFrameReservedPAEventPrefix) &&
+        std::find(std::begin(blink::kFencedFrameAutomaticBeaconTypes),
+                  std::end(blink::kFencedFrameAutomaticBeaconTypes),
+                  key_string) ==
+            std::end(blink::kFencedFrameAutomaticBeaconTypes)) {
+      std::string error_msg =
+          base::StrCat({"registerAdBeacon(): Invalid reserved type '",
+                        key_string, "' cannot be used"});
+      isolate->ThrowException(v8::Exception::TypeError(
+          v8_helper->CreateUtf8String(error_msg).ToLocalChecked()));
+      return;
+    }
     GURL url(url_string);
     if (!url.is_valid() || !url.SchemeIs(url::kHttpsScheme)) {
       std::string error_msg =
diff --git a/content/services/auction_worklet/seller_worklet_unittest.cc b/content/services/auction_worklet/seller_worklet_unittest.cc
index a5e9ff4..a646ec60 100644
--- a/content/services/auction_worklet/seller_worklet_unittest.cc
+++ b/content/services/auction_worklet/seller_worklet_unittest.cc
@@ -2946,6 +2946,20 @@
       {"https://url.test/:10 Uncaught TypeError: registerAdBeacon(): invalid "
        "reporting url for key 'view': 'http://view.example.com/'."});
 
+  // Error if invalid "reserved.*" reporting event type
+  RunReportResultCreatedScriptExpectingResult(
+      R"(5)",
+      R"(registerAdBeacon({
+        'click': "https://127.0.0.1/",
+        'reserved.bogus': "https://view.example.com/",
+      }))",
+      /*expected_signals_for_winner=*/{},
+      /*expected_report_url=*/absl::nullopt,
+      /*expected_ad_beacon_map=*/{},
+      /*expected_pa_requests=*/{},
+      {"https://url.test/:10 Uncaught TypeError: registerAdBeacon(): Invalid "
+       "reserved type 'reserved.bogus' cannot be used."});
+
   // Special case for error message if the key has mismatched surrogates.
   RunReportResultCreatedScriptExpectingResult(
       R"(5)",
diff --git a/content/services/auction_worklet/set_bid_bindings.cc b/content/services/auction_worklet/set_bid_bindings.cc
index 8b441844..3ad9284 100644
--- a/content/services/auction_worklet/set_bid_bindings.cc
+++ b/content/services/auction_worklet/set_bid_bindings.cc
@@ -43,7 +43,7 @@
     const GURL& url,
     std::string& error_prefix,
     const char* argument_name,
-    const base::RepeatingCallback<bool(const GURL&)>& is_excluded,
+    const base::RepeatingCallback<bool(const std::string&)>& is_excluded,
     const std::vector<blink::InterestGroup::Ad>& ads,
     std::string& out_error) {
   if (!url.is_valid() || !url.SchemeIs(url::kHttpsScheme)) {
@@ -54,11 +54,12 @@
   }
 
   for (const auto& ad : ads) {
-    if (is_excluded.Run(ad.render_url)) {
+    if (is_excluded.Run(ad.render_url())) {
       continue;
     }
-    if (url == ad.render_url)
+    if (url.spec() == ad.render_url()) {
       return true;
+    }
   }
   out_error = base::StrCat({error_prefix, "bid ", argument_name, " URL '",
                             url.possibly_invalid_spec(),
@@ -170,8 +171,9 @@
     bool has_top_level_seller_origin,
     const mojom::BidderWorkletNonSharedParams* bidder_worklet_non_shared_params,
     const absl::optional<blink::AdCurrency>& per_buyer_currency,
-    base::RepeatingCallback<bool(const GURL&)> is_ad_excluded,
-    base::RepeatingCallback<bool(const GURL&)> is_component_ad_excluded) {
+    base::RepeatingCallback<bool(const std::string&)> is_ad_excluded,
+    base::RepeatingCallback<bool(const std::string&)>
+        is_component_ad_excluded) {
   DCHECK(bidder_worklet_non_shared_params->ads.has_value());
   start_ = start;
   has_top_level_seller_origin_ = has_top_level_seller_origin;
diff --git a/content/services/auction_worklet/set_bid_bindings.h b/content/services/auction_worklet/set_bid_bindings.h
index c344acf7..ac94c1e 100644
--- a/content/services/auction_worklet/set_bid_bindings.h
+++ b/content/services/auction_worklet/set_bid_bindings.h
@@ -39,8 +39,9 @@
       const mojom::BidderWorkletNonSharedParams*
           bidder_worklet_non_shared_params,
       const absl::optional<blink::AdCurrency>& per_buyer_currency,
-      base::RepeatingCallback<bool(const GURL&)> is_ad_excluded,
-      base::RepeatingCallback<bool(const GURL&)> is_component_ad_excluded);
+      base::RepeatingCallback<bool(const std::string&)> is_ad_excluded,
+      base::RepeatingCallback<bool(const std::string&)>
+          is_component_ad_excluded);
 
   void AttachToContext(v8::Local<v8::Context> context) override;
   void Reset() override;
@@ -72,8 +73,8 @@
 
   // Callbacks set by ReInitialize and cleared by Reset which tell if an ad URL
   // can be used in a valid bid. Used to check the bid for non-k-anonymous ads.
-  base::RepeatingCallback<bool(const GURL&)> is_ad_excluded_;
-  base::RepeatingCallback<bool(const GURL&)> is_component_ad_excluded_;
+  base::RepeatingCallback<bool(const std::string&)> is_ad_excluded_;
+  base::RepeatingCallback<bool(const std::string&)> is_component_ad_excluded_;
 
   mojom::BidderWorkletBidPtr bid_;
 };
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 29d87af9..a19857a 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1077,7 +1077,7 @@
 
     # Generate WebUI bindings in JavaScript instead of TypeScript. This is
     # necessary since this target is intentionally testing JS WebUI bindings.
-    use_typescript_sources = false
+    generate_webui_js_bindings = true
   }
 }
 
diff --git a/content/web_test/renderer/web_frame_test_proxy.cc b/content/web_test/renderer/web_frame_test_proxy.cc
index 056b85d..b6932c1 100644
--- a/content/web_test/renderer/web_frame_test_proxy.cc
+++ b/content/web_test/renderer/web_frame_test_proxy.cc
@@ -551,21 +551,6 @@
   RenderFrameImpl::PostAccessibilityEvent(event);
 }
 
-void WebFrameTestProxy::NotifyWebAXObjectMarkedDirty(
-    const blink::WebAXObject& object) {
-  HandleWebAccessibilityEventForTest(object, "MarkDirty",
-                                     std::vector<ui::AXEventIntent>());
-
-  // Guard against the case where |this| was deleted as a result of an
-  // accessibility listener detaching a frame. If that occurs, the
-  // WebAXObject will be detached.
-  if (object.IsDetached()) {
-    return;  // |this| is invalid.
-  }
-
-  RenderFrameImpl::NotifyWebAXObjectMarkedDirty(object);
-}
-
 void WebFrameTestProxy::HandleWebAccessibilityEventForTest(
     const blink::WebAXObject& object,
     const char* event_name,
diff --git a/content/web_test/renderer/web_frame_test_proxy.h b/content/web_test/renderer/web_frame_test_proxy.h
index 1c5f0fb3..26dac00 100644
--- a/content/web_test/renderer/web_frame_test_proxy.h
+++ b/content/web_test/renderer/web_frame_test_proxy.h
@@ -80,7 +80,6 @@
                        ForRedirect for_redirect) override;
   void BeginNavigation(std::unique_ptr<blink::WebNavigationInfo> info) override;
   void PostAccessibilityEvent(const ui::AXEvent& event) override;
-  void NotifyWebAXObjectMarkedDirty(const blink::WebAXObject& object) override;
   void CheckIfAudioSinkExistsAndIsAuthorized(
       const blink::WebString& sink_id,
       blink::WebSetSinkIdCompleteCallback completion_callback) override;
diff --git a/device/fido/features.cc b/device/fido/features.cc
index 9041707ee..023c28d5 100644
--- a/device/fido/features.cc
+++ b/device/fido/features.cc
@@ -211,4 +211,9 @@
              "AllowExtensionsToSetWebAuthnRpIds",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// Default enabled in M122. Remove in or after M125.
+BASE_FEATURE(kWebAuthnAndroidFidoJson,
+             "WebAuthenticationAndroidFidoJson",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 }  // namespace device
diff --git a/device/fido/features.h b/device/fido/features.h
index d4c7c3e..85b755a2 100644
--- a/device/fido/features.h
+++ b/device/fido/features.h
@@ -178,6 +178,10 @@
 COMPONENT_EXPORT(DEVICE_FIDO)
 BASE_DECLARE_FEATURE(kAllowExtensionsToSetWebAuthnRpIds);
 
+// Send and receive JSON from Play Services.
+COMPONENT_EXPORT(DEVICE_FIDO)
+BASE_DECLARE_FEATURE(kWebAuthnAndroidFidoJson);
+
 }  // namespace device
 
 #endif  // DEVICE_FIDO_FEATURES_H_
diff --git a/infra/config/generated/testing/mixins.pyl b/infra/config/generated/testing/mixins.pyl
index a46890b8..505675d 100644
--- a/infra/config/generated/testing/mixins.pyl
+++ b/infra/config/generated/testing/mixins.pyl
@@ -389,9 +389,6 @@
       '--test-launcher-retry-limit=0',
       '--test-launcher-batch-limit=512',
     ],
-    'android_swarming': {
-      'shards': 2,
-    },
   },
   'disable_field_trial_config_for_earl_grey': {
     'args': [
diff --git a/infra/config/generated/testing/test_suites.pyl b/infra/config/generated/testing/test_suites.pyl
index 51130324..e8b5529 100644
--- a/infra/config/generated/testing/test_suites.pyl
+++ b/infra/config/generated/testing/test_suites.pyl
@@ -2485,6 +2485,9 @@
           '--no-xvfb',
         ],
         'ci_only': True,
+        'android_swarming': {
+          'shards': 2,
+        },
       },
       'dawn_end2end_skip_validation_tests': {
         'test': 'dawn_end2end_tests',
@@ -2497,6 +2500,9 @@
         'linux_args': [
           '--no-xvfb',
         ],
+        'android_swarming': {
+          'shards': 2,
+        },
       },
       'dawn_end2end_tests': {
         'mixins': [
@@ -2505,6 +2511,9 @@
         'linux_args': [
           '--no-xvfb',
         ],
+        'android_swarming': {
+          'shards': 2,
+        },
       },
       'dawn_end2end_wire_tests': {
         'test': 'dawn_end2end_tests',
@@ -2517,6 +2526,9 @@
         'linux_args': [
           '--no-xvfb',
         ],
+        'android_swarming': {
+          'shards': 2,
+        },
       },
     },
 
diff --git a/infra/config/targets/basic_suites.star b/infra/config/targets/basic_suites.star
index 746fcd20..c733154a 100644
--- a/infra/config/targets/basic_suites.star
+++ b/infra/config/targets/basic_suites.star
@@ -2306,21 +2306,33 @@
                 "--no-xvfb",
             ],
             ci_only = True,  # https://crbug.com/dawn/1749
+            android_swarming = targets.swarming(
+                shards = 2,
+            ),
         ),
         "dawn_end2end_skip_validation_tests": targets.legacy_test_config(
             linux_args = [
                 "--no-xvfb",
             ],
+            android_swarming = targets.swarming(
+                shards = 2,
+            ),
         ),
         "dawn_end2end_tests": targets.legacy_test_config(
             linux_args = [
                 "--no-xvfb",
             ],
+            android_swarming = targets.swarming(
+                shards = 2,
+            ),
         ),
         "dawn_end2end_wire_tests": targets.legacy_test_config(
             linux_args = [
                 "--no-xvfb",
             ],
+            android_swarming = targets.swarming(
+                shards = 2,
+            ),
         ),
     },
 )
diff --git a/infra/config/targets/mixins.star b/infra/config/targets/mixins.star
index 6c75a8a..54db652 100644
--- a/infra/config/targets/mixins.star
+++ b/infra/config/targets/mixins.star
@@ -481,9 +481,6 @@
         # Reduces size of stdout of a batch crbug.com/1456415
         "--test-launcher-batch-limit=512",
     ],
-    android_swarming = targets.swarming(
-        shards = 2,
-    ),
 )
 
 targets.mixin(
diff --git a/ios/chrome/browser/commerce/model/push_notification/BUILD.gn b/ios/chrome/browser/commerce/model/push_notification/BUILD.gn
index 75ddd73..f78d6ba 100644
--- a/ios/chrome/browser/commerce/model/push_notification/BUILD.gn
+++ b/ios/chrome/browser/commerce/model/push_notification/BUILD.gn
@@ -16,6 +16,7 @@
     "//components/commerce/core:feature_list",
     "//components/commerce/core:proto",
     "//components/commerce/core:shopping_service",
+    "//components/optimization_guide/core:core",
     "//components/optimization_guide/proto:optimization_guide_proto",
     "//components/variations/service",
     "//components/variations/service:service",
@@ -51,6 +52,7 @@
     "//ios/chrome/browser/bookmarks/model:model",
     "//ios/chrome/browser/commerce/model:session_proto_db",
     "//ios/chrome/browser/commerce/model:shopping_service",
+    "//ios/chrome/browser/optimization_guide/model:model",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
diff --git a/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.h b/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.h
index 9157250..6016a70 100644
--- a/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.h
+++ b/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.h
@@ -5,9 +5,9 @@
 #ifndef IOS_CHROME_BROWSER_COMMERCE_MODEL_PUSH_NOTIFICATION_COMMERCE_PUSH_NOTIFICATION_CLIENT_H_
 #define IOS_CHROME_BROWSER_COMMERCE_MODEL_PUSH_NOTIFICATION_COMMERCE_PUSH_NOTIFICATION_CLIENT_H_
 
+#import "components/optimization_guide/proto/push_notification.pb.h"
 #import "ios/chrome/browser/bookmarks/model/local_or_syncable_bookmark_model_factory.h"
 #import "ios/chrome/browser/commerce/model/shopping_service_factory.h"
-#import "ios/chrome/browser/optimization_guide/model/optimization_guide_push_notification_client.h"
 #import "ios/chrome/browser/push_notification/model/push_notification_client.h"
 #import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
 
diff --git a/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.mm b/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.mm
index 8ae52163..8b9ac65 100644
--- a/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.mm
+++ b/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client.mm
@@ -13,8 +13,11 @@
 #import "components/bookmarks/browser/bookmark_node.h"
 #import "components/commerce/core/price_tracking_utils.h"
 #import "components/commerce/core/proto/price_tracking.pb.h"
+#import "components/optimization_guide/core/hints_manager.h"
 #import "components/optimization_guide/proto/push_notification.pb.h"
 #import "ios/chrome/browser/bookmarks/model/local_or_syncable_bookmark_model_factory.h"
+#import "ios/chrome/browser/optimization_guide/model/optimization_guide_service.h"
+#import "ios/chrome/browser/optimization_guide/model/optimization_guide_service_factory.h"
 #import "ios/chrome/browser/push_notification/model/push_notification_client_id.h"
 #import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
@@ -89,6 +92,19 @@
     NSDictionary<NSString*, id>* notification) {
   base::RecordAction(base::UserMetricsAction(
       "Commerce.PriceTracking.PushNotification.Received"));
+  OptimizationGuideService* optimization_guide_service =
+      OptimizationGuideServiceFactory::GetForBrowserState(
+          GetLastUsedBrowserState());
+  std::unique_ptr<optimization_guide::proto::HintNotificationPayload>
+      hint_notification_payload = ParseHintNotificationPayload(
+          [notification objectForKey:kSerializedPayloadKey]);
+  if (hint_notification_payload) {
+    optimization_guide::PushNotificationManager* push_notification_manager =
+        optimization_guide_service->GetHintsManager()
+            ->push_notification_manager();
+    push_notification_manager->OnNewPushNotification(
+        *hint_notification_payload);
+  }
   return UIBackgroundFetchResultNoData;
 }
 
diff --git a/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client_unittest.mm b/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client_unittest.mm
index b2197393..099ee24 100644
--- a/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client_unittest.mm
+++ b/ios/chrome/browser/commerce/model/push_notification/commerce_push_notification_client_unittest.mm
@@ -17,12 +17,16 @@
 #import "components/commerce/core/proto/commerce_subscription_db_content.pb.h"
 #import "components/commerce/core/proto/price_tracking.pb.h"
 #import "components/commerce/core/test_utils.h"
+#import "components/optimization_guide/core/hints_manager.h"
+#import "components/optimization_guide/core/optimization_guide_features.h"
 #import "components/optimization_guide/proto/push_notification.pb.h"
 #import "components/session_proto_db/session_proto_db.h"
 #import "ios/chrome/app/application_delegate/app_state.h"
 #import "ios/chrome/browser/bookmarks/model/local_or_syncable_bookmark_model_factory.h"
 #import "ios/chrome/browser/commerce/model/session_proto_db_factory.h"
 #import "ios/chrome/browser/commerce/model/shopping_service_factory.h"
+#import "ios/chrome/browser/optimization_guide/model/optimization_guide_service.h"
+#import "ios/chrome/browser/optimization_guide/model/optimization_guide_service_factory.h"
 #import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
 #import "ios/chrome/browser/shared/model/browser/browser_list.h"
 #import "ios/chrome/browser/shared/model/browser/browser_list_factory.h"
@@ -42,6 +46,7 @@
     "Commerce.PriceTracking.Untrack.BookmarkFound";
 std::string kBookmarkTitle = "My product title";
 uint64_t kClusterId = 12345L;
+constexpr char kPayloadValue[] = "value";
 NSString* kSerializedPayloadKey = @"op";
 NSString* kVisitSiteActionId = @"visit_site";
 NSString* kVisitSiteTitle = @"Visit site";
@@ -126,6 +131,17 @@
 
 }  // namespace
 
+class MockDelegate
+    : public optimization_guide::PushNotificationManager::Delegate {
+ public:
+  MOCK_METHOD(void,
+              RemoveFetchedEntriesByHintKeys,
+              (base::OnceClosure,
+               optimization_guide::proto::KeyRepresentation,
+               (const base::flat_set<std::string>&)),
+              (override));
+};
+
 class CommercePushNotificationClientTest : public PlatformTest {
  public:
   CommercePushNotificationClientTest() {}
@@ -150,6 +166,9 @@
         SessionProtoDBFactory<
             commerce_subscription_db::CommerceSubscriptionContentProto>::
             GetDefaultFactory());
+    builder.AddTestingFactory(
+        OptimizationGuideServiceFactory::GetInstance(),
+        OptimizationGuideServiceFactory::GetDefaultFactory());
     chrome_browser_state_ = builder.Build();
     browser_list_ =
         BrowserListFactory::GetForBrowserState(chrome_browser_state_.get());
@@ -183,6 +202,8 @@
 
   Browser* GetBrowser() { return browser_.get(); }
 
+  ChromeBrowserState* GetBrowserState() { return chrome_browser_state_.get(); }
+
   Browser* GetBackgroundBrowser() { return background_browser_.get(); }
 
   void HandleNotificationInteraction(
@@ -206,6 +227,12 @@
         .GetSceneLevelForegroundActiveBrowser();
   }
 
+  void SetLastUsedChromeBrowserStateForTesting(
+      CommercePushNotificationClient& push_notification_client) {
+    push_notification_client.SetLastUsedChromeBrowserStateForTesting(
+        chrome_browser_state_.get());
+  }
+
  protected:
   web::WebTaskEnvironment task_environment_;
   CommercePushNotificationClient commerce_push_notification_client_;
@@ -220,6 +247,84 @@
   AppState* app_state_;
 };
 
+TEST_F(CommercePushNotificationClientTest, TestParsing) {
+  optimization_guide::proto::Any any;
+  optimization_guide::proto::HintNotificationPayload hint_notification_payload;
+  hint_notification_payload.set_hint_key(kHintKey);
+  hint_notification_payload.set_optimization_type(
+      optimization_guide::proto::NOSCRIPT);
+  hint_notification_payload.set_key_representation(
+      optimization_guide::proto::HOST);
+  optimization_guide::proto::Any* payload =
+      hint_notification_payload.mutable_payload();
+  payload->set_type_url(kHintKey);
+  payload->set_value(kPayloadValue);
+
+  std::string serialized_hint_notification_payload;
+  hint_notification_payload.SerializeToString(
+      &serialized_hint_notification_payload);
+  any.set_value(serialized_hint_notification_payload.c_str());
+
+  std::string serialized_any;
+  any.SerializeToString(&serialized_any);
+  std::string serialized_any_escaped;
+  base::Base64Encode(serialized_any, &serialized_any_escaped);
+
+  std::unique_ptr<optimization_guide::proto::HintNotificationPayload> parsed =
+      CommercePushNotificationClient::ParseHintNotificationPayload(
+          base::SysUTF8ToNSString(serialized_any_escaped));
+  EXPECT_EQ(kHintKey, parsed->hint_key());
+  EXPECT_EQ(optimization_guide::proto::NOSCRIPT, parsed->optimization_type());
+  EXPECT_EQ(optimization_guide::proto::HOST, parsed->key_representation());
+  EXPECT_EQ(kHintKey, parsed->payload().type_url());
+  EXPECT_EQ(kPayloadValue, parsed->payload().value());
+}
+
+TEST_F(CommercePushNotificationClientTest, TestHintKeyRemovedUponNotification) {
+  MockDelegate mock_delegate;
+  OptimizationGuideService* optimization_guide_service =
+      OptimizationGuideServiceFactory::GetForBrowserState(GetBrowserState());
+  optimization_guide_service->GetHintsManager()
+      ->push_notification_manager()
+      ->SetDelegate(&mock_delegate);
+
+  optimization_guide::proto::Any any;
+  optimization_guide::proto::HintNotificationPayload hint_notification_payload;
+  hint_notification_payload.set_hint_key(kHintKey);
+  hint_notification_payload.set_key_representation(
+      optimization_guide::proto::HOST);
+  hint_notification_payload.set_optimization_type(
+      optimization_guide::proto::NOSCRIPT);
+
+  optimization_guide::proto::Any* payload =
+      hint_notification_payload.mutable_payload();
+  payload->set_type_url(kHintKey);
+  payload->set_value(kPayloadValue);
+
+  std::string serialized_hint_notification_payload;
+  hint_notification_payload.SerializeToString(
+      &serialized_hint_notification_payload);
+  any.set_value(serialized_hint_notification_payload.c_str());
+
+  std::string serialized_any;
+  any.SerializeToString(&serialized_any);
+  std::string serialized_any_escaped;
+  base::Base64Encode(serialized_any, &serialized_any_escaped);
+
+  NSDictionary* dict = @{
+    kSerializedPayloadKey : base::SysUTF8ToNSString(serialized_any_escaped)
+  };
+
+  CommercePushNotificationClient push_notification_client;
+  SetLastUsedChromeBrowserStateForTesting(push_notification_client);
+
+  EXPECT_CALL(mock_delegate,
+              RemoveFetchedEntriesByHintKeys(
+                  testing::_, testing::Eq(optimization_guide::proto::HOST),
+                  testing::ElementsAreArray({kHintKey})));
+  push_notification_client.HandleNotificationReception(dict);
+}
+
 TEST_F(CommercePushNotificationClientTest, TestNotificationInteraction) {
   NSDictionary* user_info = SerializeOptGuideCommercePayload();
 
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 54b6262..49362b3 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -1367,9 +1367,6 @@
      flag_descriptions::kOnlyAccessClipboardAsyncName,
      flag_descriptions::kOnlyAccessClipboardAsyncDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kOnlyAccessClipboardAsync)},
-    {"omnibox-tail-suggest", flag_descriptions::kOmniboxTailSuggestName,
-     flag_descriptions::kOmniboxTailSuggestDescription, flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(kOmniboxTailSuggest)},
     {"omnibox-improved-rtl-suggestions",
      flag_descriptions::kOmniboxSuggestionsRTLImprovementsName,
      flag_descriptions::kOmniboxSuggestionsRTLImprovementsDescription,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index aa66bc6e..26091b04 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -744,11 +744,6 @@
 extern const char kOmniboxSuggestionsRTLImprovementsDescription[] =
     "Improved layout for suggestions in right-to-left contexts";
 
-const char kOmniboxTailSuggestName[] = "Omnibox Tail suggestions";
-const char kOmniboxTailSuggestDescription[] =
-    "Enables tail search suggestions. Search suggestions only matching the end "
-    "of users input text.";
-
 const char kOmniboxZeroSuggestInMemoryCachingName[] =
     "Omnibox Zero Prefix Suggestion in-memory caching";
 const char kOmniboxZeroSuggestInMemoryCachingDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 165312a..14d33ef 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -647,10 +647,6 @@
 extern const char kOmniboxSuggestionsRTLImprovementsName[];
 extern const char kOmniboxSuggestionsRTLImprovementsDescription[];
 
-// Title and description for tail suggestions in the omnibox.
-extern const char kOmniboxTailSuggestName[];
-extern const char kOmniboxTailSuggestDescription[];
-
 // Title and description for the flag to change the max number of autocomplete
 // matches in the omnibox popup.
 extern const char kOmniboxUIMaxAutocompleteMatchesName[];
diff --git a/ios/chrome/browser/optimization_guide/model/BUILD.gn b/ios/chrome/browser/optimization_guide/model/BUILD.gn
index 0297ecb..83bef6a 100644
--- a/ios/chrome/browser/optimization_guide/model/BUILD.gn
+++ b/ios/chrome/browser/optimization_guide/model/BUILD.gn
@@ -6,8 +6,6 @@
   sources = [
     "ios_chrome_hints_manager.h",
     "ios_chrome_hints_manager.mm",
-    "optimization_guide_push_notification_client.h",
-    "optimization_guide_push_notification_client.mm",
     "optimization_guide_service.h",
     "optimization_guide_service.mm",
     "optimization_guide_service_factory.h",
@@ -35,7 +33,6 @@
     "//components/variations",
     "//ios/chrome/browser/download/model/background_service",
     "//ios/chrome/browser/metrics/model:accessor",
-    "//ios/chrome/browser/push_notification/model:push_notification_client",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/browser_state",
@@ -62,7 +59,6 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "optimization_guide_push_notification_client_unittest.mm",
     "optimization_guide_service_factory_unittest.mm",
     "optimization_guide_service_unittest.mm",
     "optimization_guide_tab_helper_unittest.mm",
diff --git a/ios/chrome/browser/optimization_guide/model/optimization_guide_push_notification_client.h b/ios/chrome/browser/optimization_guide/model/optimization_guide_push_notification_client.h
deleted file mode 100644
index c4cc1b8..0000000
--- a/ios/chrome/browser/optimization_guide/model/optimization_guide_push_notification_client.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_OPTIMIZATION_GUIDE_MODEL_OPTIMIZATION_GUIDE_PUSH_NOTIFICATION_CLIENT_H_
-#define IOS_CHROME_BROWSER_OPTIMIZATION_GUIDE_MODEL_OPTIMIZATION_GUIDE_PUSH_NOTIFICATION_CLIENT_H_
-
-#import "components/optimization_guide/proto/push_notification.pb.h"
-#import "ios/chrome/browser/push_notification/model/push_notification_client.h"
-
-#import <UIKit/UIKit.h>
-
-class ChromeBrowserState;
-
-// Abstract class to be inherited from by push notification clients which
-// utilize the OptimizationGuide push notification infrastructure.
-// TODO(b/315149599): Remove this client and migrate any tests that are not
-// already covered by Commerce client tests.
-class OptimizationGuidePushNotificationClient : public PushNotificationClient {
- public:
-  OptimizationGuidePushNotificationClient(
-      const PushNotificationClientId& push_notification_client_id);
-  ~OptimizationGuidePushNotificationClient() override;
-
-  // PushNotificationClient
-  UIBackgroundFetchResult HandleNotificationReception(
-      NSDictionary<NSString*, id>* notification) override;
-
-  // Convert escaped serialized payload from push notification into
-  // optimization_guide::proto::HintNotificationPayload. This method is used
-  // by OptimizationGuidePushNotificationClient as well as inherited classes
-  // to access and deserialize their payload.
-  static std::unique_ptr<optimization_guide::proto::HintNotificationPayload>
-  ParseHintNotificationPayload(NSString* serialized_payload_escaped);
-
-  // Allows tests to set the last used ChromeBrowserState returned in
-  // GetLastUsedBrowserState().
-  void SetLastUsedChromeBrowserStateForTesting(
-      ChromeBrowserState* chrome_browser_state) {
-    last_used_browser_state_for_testing_ = chrome_browser_state;
-  }
-
- protected:
-  ChromeBrowserState* GetLastUsedBrowserState();
-
- private:
-  // Allows tests to override the last used ChromeBrowserState returned in
-  // GetLastUsedBrowserState().
-  ChromeBrowserState* last_used_browser_state_for_testing_ = nullptr;
-};
-
-#endif  // IOS_CHROME_BROWSER_OPTIMIZATION_GUIDE_MODEL_OPTIMIZATION_GUIDE_PUSH_NOTIFICATION_CLIENT_H_
diff --git a/ios/chrome/browser/optimization_guide/model/optimization_guide_push_notification_client.mm b/ios/chrome/browser/optimization_guide/model/optimization_guide_push_notification_client.mm
deleted file mode 100644
index f3038895..0000000
--- a/ios/chrome/browser/optimization_guide/model/optimization_guide_push_notification_client.mm
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/optimization_guide/model/optimization_guide_push_notification_client.h"
-
-#import "base/base64.h"
-#import "base/logging.h"
-#import "base/strings/sys_string_conversions.h"
-#import "components/optimization_guide/core/hints_manager.h"
-#import "components/optimization_guide/proto/push_notification.pb.h"
-#import "ios/chrome/browser/optimization_guide/model/optimization_guide_service.h"
-#import "ios/chrome/browser/optimization_guide/model/optimization_guide_service_factory.h"
-#import "ios/chrome/browser/push_notification/model/push_notification_client_id.h"
-#import "ios/chrome/browser/shared/model/application_context/application_context.h"
-#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state_manager.h"
-
-namespace {
-
-// Opaque payload key from Chime
-NSString* kSerializedPayloadKey = @"op";
-
-}  // namespace
-
-OptimizationGuidePushNotificationClient::
-    OptimizationGuidePushNotificationClient(
-        const PushNotificationClientId& push_notification_client_id)
-    : PushNotificationClient(push_notification_client_id) {}
-
-OptimizationGuidePushNotificationClient::
-    ~OptimizationGuidePushNotificationClient() = default;
-
-UIBackgroundFetchResult
-OptimizationGuidePushNotificationClient::HandleNotificationReception(
-    NSDictionary<NSString*, id>* notification) {
-  OptimizationGuideService* optimization_guide_service =
-      OptimizationGuideServiceFactory::GetForBrowserState(
-          GetLastUsedBrowserState());
-  std::unique_ptr<optimization_guide::proto::HintNotificationPayload>
-      hint_notification_payload = ParseHintNotificationPayload(
-          [notification objectForKey:kSerializedPayloadKey]);
-  if (hint_notification_payload) {
-    optimization_guide::PushNotificationManager* push_notification_manager =
-        optimization_guide_service->GetHintsManager()
-            ->push_notification_manager();
-    push_notification_manager->OnNewPushNotification(
-        *hint_notification_payload);
-  }
-  return UIBackgroundFetchResultNoData;
-}
-
-// static
-std::unique_ptr<optimization_guide::proto::HintNotificationPayload>
-OptimizationGuidePushNotificationClient::ParseHintNotificationPayload(
-    NSString* serialized_payload_escaped) {
-  std::string serialized_payload_unescaped;
-  if (!base::Base64Decode(base::SysNSStringToUTF8(serialized_payload_escaped),
-                          &serialized_payload_unescaped)) {
-    return nullptr;
-  }
-  optimization_guide::proto::Any any;
-  if (!any.ParseFromString(serialized_payload_unescaped) || !any.has_value()) {
-    return nullptr;
-  }
-  std::unique_ptr<optimization_guide::proto::HintNotificationPayload>
-      hint_notification_payload = std::make_unique<
-          optimization_guide::proto::HintNotificationPayload>();
-  if (!hint_notification_payload->ParseFromString(any.value())) {
-    return nullptr;
-  }
-  return hint_notification_payload;
-}
-
-ChromeBrowserState*
-OptimizationGuidePushNotificationClient::GetLastUsedBrowserState() {
-  if (last_used_browser_state_for_testing_) {
-    return last_used_browser_state_for_testing_;
-  }
-  return GetApplicationContext()
-      ->GetChromeBrowserStateManager()
-      ->GetLastUsedBrowserState();
-}
diff --git a/ios/chrome/browser/optimization_guide/model/optimization_guide_push_notification_client_unittest.mm b/ios/chrome/browser/optimization_guide/model/optimization_guide_push_notification_client_unittest.mm
deleted file mode 100644
index 363f6fdf..0000000
--- a/ios/chrome/browser/optimization_guide/model/optimization_guide_push_notification_client_unittest.mm
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/optimization_guide/model/optimization_guide_push_notification_client.h"
-
-#import "base/base64.h"
-#import "base/functional/callback_helpers.h"
-#import "base/strings/sys_string_conversions.h"
-#import "base/test/scoped_feature_list.h"
-#import "base/test/task_environment.h"
-#import "components/optimization_guide/core/hints_manager.h"
-#import "components/optimization_guide/core/optimization_guide_features.h"
-#import "ios/chrome/browser/optimization_guide/model/optimization_guide_service.h"
-#import "ios/chrome/browser/optimization_guide/model/optimization_guide_service_factory.h"
-#import "ios/chrome/browser/push_notification/model/push_notification_client_id.h"
-#import "ios/chrome/browser/shared/model/browser_state/test_chrome_browser_state.h"
-#import "ios/chrome/browser/shared/model/prefs/browser_prefs.h"
-#import "testing/gmock/include/gmock/gmock.h"
-#import "testing/gtest/include/gtest/gtest.h"
-#import "testing/platform_test.h"
-
-namespace {
-
-constexpr char kHintKey[] = "https://www.foo.com";
-constexpr char kPayloadValue[] = "value";
-NSString* kSerializedPayloadKey = @"op";
-
-}  // namespace
-
-class MockDelegate
-    : public optimization_guide::PushNotificationManager::Delegate {
- public:
-  MOCK_METHOD(void,
-              RemoveFetchedEntriesByHintKeys,
-              (base::OnceClosure,
-               optimization_guide::proto::KeyRepresentation,
-               (const base::flat_set<std::string>&)),
-              (override));
-};
-
-class MockOptimizationGuidePushNotificationClient
-    : public OptimizationGuidePushNotificationClient {
- public:
-  MockOptimizationGuidePushNotificationClient()
-      : OptimizationGuidePushNotificationClient(
-            PushNotificationClientId::kCommerce) {}
-
-  // Override OptimizationGuidePushNotificationClient
-  // Unused at OptimizationGuide level but included to make
-  // MockOptimizationGuidePushNotificationClient non-abstract
-  // and usable as a test class.
-  NSArray<UNNotificationCategory*>* RegisterActionableNotifications() override {
-    return @[];
-  }
-  void HandleNotificationInteraction(
-      UNNotificationResponse* notification) override {}
-  void OnSceneActiveForegroundBrowserReady() override {}
-};
-
-class OptimizationGuidePushNotificationClientTest : public PlatformTest {
- public:
-  OptimizationGuidePushNotificationClientTest() {}
-  ~OptimizationGuidePushNotificationClientTest() override = default;
-
-  void SetUp() override {
-    PlatformTest::SetUp();
-    scoped_feature_list_.InitWithFeatures(
-        {optimization_guide::features::kPushNotifications}, {});
-    TestChromeBrowserState::Builder builder;
-    builder.AddTestingFactory(
-        OptimizationGuideServiceFactory::GetInstance(),
-        OptimizationGuideServiceFactory::GetDefaultFactory());
-    browser_state_ = builder.Build();
-  }
-
-  ChromeBrowserState* GetBrowserState() { return browser_state_.get(); }
-
-  void SetLastUsedChromeBrowserStateForTesting(
-      MockOptimizationGuidePushNotificationClient& push_notification_client) {
-    push_notification_client.SetLastUsedChromeBrowserStateForTesting(
-        browser_state_.get());
-  }
-
- protected:
-  base::test::TaskEnvironment task_environment_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-  OptimizationGuideService* optimization_guide_service_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
-};
-
-TEST_F(OptimizationGuidePushNotificationClientTest, TestParsing) {
-  optimization_guide::proto::Any any;
-  optimization_guide::proto::HintNotificationPayload hint_notification_payload;
-  hint_notification_payload.set_hint_key(kHintKey);
-  hint_notification_payload.set_optimization_type(
-      optimization_guide::proto::NOSCRIPT);
-  hint_notification_payload.set_key_representation(
-      optimization_guide::proto::HOST);
-  optimization_guide::proto::Any* payload =
-      hint_notification_payload.mutable_payload();
-  payload->set_type_url(kHintKey);
-  payload->set_value(kPayloadValue);
-
-  std::string serialized_hint_notification_payload;
-  hint_notification_payload.SerializeToString(
-      &serialized_hint_notification_payload);
-  any.set_value(serialized_hint_notification_payload.c_str());
-
-  std::string serialized_any;
-  any.SerializeToString(&serialized_any);
-  std::string serialized_any_escaped;
-  base::Base64Encode(serialized_any, &serialized_any_escaped);
-
-  std::unique_ptr<optimization_guide::proto::HintNotificationPayload> parsed =
-      OptimizationGuidePushNotificationClient::ParseHintNotificationPayload(
-          base::SysUTF8ToNSString(serialized_any_escaped));
-  EXPECT_EQ(kHintKey, parsed->hint_key());
-  EXPECT_EQ(optimization_guide::proto::NOSCRIPT, parsed->optimization_type());
-  EXPECT_EQ(optimization_guide::proto::HOST, parsed->key_representation());
-  EXPECT_EQ(kHintKey, parsed->payload().type_url());
-  EXPECT_EQ(kPayloadValue, parsed->payload().value());
-}
-
-TEST_F(OptimizationGuidePushNotificationClientTest,
-       TestHintKeyRemovedUponNotification) {
-  MockDelegate mock_delegate;
-  OptimizationGuideService* optimization_guide_service =
-      OptimizationGuideServiceFactory::GetForBrowserState(GetBrowserState());
-  optimization_guide_service->GetHintsManager()
-      ->push_notification_manager()
-      ->SetDelegate(&mock_delegate);
-
-  optimization_guide::proto::Any any;
-  optimization_guide::proto::HintNotificationPayload hint_notification_payload;
-  hint_notification_payload.set_hint_key(kHintKey);
-  hint_notification_payload.set_key_representation(
-      optimization_guide::proto::HOST);
-  hint_notification_payload.set_optimization_type(
-      optimization_guide::proto::NOSCRIPT);
-
-  optimization_guide::proto::Any* payload =
-      hint_notification_payload.mutable_payload();
-  payload->set_type_url(kHintKey);
-  payload->set_value(kPayloadValue);
-
-  std::string serialized_hint_notification_payload;
-  hint_notification_payload.SerializeToString(
-      &serialized_hint_notification_payload);
-  any.set_value(serialized_hint_notification_payload.c_str());
-
-  std::string serialized_any;
-  any.SerializeToString(&serialized_any);
-  std::string serialized_any_escaped;
-  base::Base64Encode(serialized_any, &serialized_any_escaped);
-
-  NSDictionary* dict = @{
-    kSerializedPayloadKey : base::SysUTF8ToNSString(serialized_any_escaped)
-  };
-
-  MockOptimizationGuidePushNotificationClient push_notification_client;
-  SetLastUsedChromeBrowserStateForTesting(push_notification_client);
-
-  EXPECT_CALL(mock_delegate,
-              RemoveFetchedEntriesByHintKeys(
-                  testing::_, testing::Eq(optimization_guide::proto::HOST),
-                  testing::ElementsAreArray({kHintKey})));
-  push_notification_client.HandleNotificationReception(dict);
-}
diff --git a/ios/chrome/browser/safe_browsing/model/real_time_url_lookup_service_factory.mm b/ios/chrome/browser/safe_browsing/model/real_time_url_lookup_service_factory.mm
index c203867..b361a2a 100644
--- a/ios/chrome/browser/safe_browsing/model/real_time_url_lookup_service_factory.mm
+++ b/ios/chrome/browser/safe_browsing/model/real_time_url_lookup_service_factory.mm
@@ -73,5 +73,6 @@
       // Referrer chain provider is currently not available on iOS. Once it
       // is implemented, inject it to enable referrer chain in real time
       // requests.
-      /*referrer_chain_provider=*/nullptr);
+      /*referrer_chain_provider=*/nullptr,
+      /*webui_delegate=*/nullptr);
 }
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.mm
index 487d63e..442e38e 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.mm
@@ -345,6 +345,9 @@
       IOSPasswordManagerDriverFactory::FromWebStateAndWebFrame(webState, frame);
   const std::vector<const password_manager::PasswordForm*>* passwordForms =
       passwordManager->GetBestMatches(driver, formId);
+  if (!passwordForms) {
+    return @[];
+  }
 
   NSMutableArray<ManualFillCredential*>* credentials =
       [[NSMutableArray alloc] initWithCapacity:passwordForms->size()];
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_ui_features.cc b/ios/chrome/browser/ui/omnibox/omnibox_ui_features.cc
index fa2b2410..78ed844 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_ui_features.cc
+++ b/ios/chrome/browser/ui/omnibox/omnibox_ui_features.cc
@@ -19,11 +19,6 @@
              "OmniboxKeyboardPasteButton",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Tail suggest is triggered server side.
-BASE_FEATURE(kOmniboxTailSuggest,
-             "OmniboxTailSuggest",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kOmniboxSuggestionsRTLImprovements,
              "OmniboxSuggestionsRTLImprovements",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_ui_features.h b/ios/chrome/browser/ui/omnibox/omnibox_ui_features.h
index 44817e29..60b512e7 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_ui_features.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_ui_features.h
@@ -17,9 +17,6 @@
 // Feature flag to enable paste button on the omnibox keyboard accessories.
 BASE_DECLARE_FEATURE(kOmniboxKeyboardPasteButton);
 
-// Feature flag to enable tail suggestions in the omnibox.
-BASE_DECLARE_FEATURE(kOmniboxTailSuggest);
-
 // Returns if kEnablePopoutOmniboxIpad feature is enabled.
 bool IsIpadPopoutOmniboxEnabled();
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
index bf78e2c..6de46fd 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
@@ -917,6 +917,14 @@
         selectElementWithMatcher:chrome_test_util::TabGridEditSelectAllButton()]
         assertWithMatcher:grey_sufficientlyVisible()
                     error:&error];
+    if (error == nil) {
+      // Bypass egtest bug on iOS 15 in which the grid cell element briefly
+      // unselectable.
+      [[EarlGrey
+          selectElementWithMatcher:chrome_test_util::TabGridCellAtIndex(0)]
+          assertWithMatcher:grey_sufficientlyVisible()
+                      error:&error];
+    }
     return (error == nil);
   };
   GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
diff --git a/ios/components/security_interstitials/safe_browsing/fake_safe_browsing_service.mm b/ios/components/security_interstitials/safe_browsing/fake_safe_browsing_service.mm
index 9166f846..2ca9a83 100644
--- a/ios/components/security_interstitials/safe_browsing/fake_safe_browsing_service.mm
+++ b/ios/components/security_interstitials/safe_browsing/fake_safe_browsing_service.mm
@@ -43,7 +43,6 @@
             /*last_committed_url=*/GURL::EmptyGURL(),
             web::GetUIThreadTaskRunner({}),
             /*url_lookup_service_on_ui=*/nullptr,
-            /*webui_delegate=*/nullptr,
             /*hash_realtime_service_on_ui=*/nullptr,
             /*mechanism_experimenter=*/nullptr,
             /*is_mechanism_experiment_allowed=*/false,
diff --git a/ios/components/security_interstitials/safe_browsing/safe_browsing_service_impl.mm b/ios/components/security_interstitials/safe_browsing/safe_browsing_service_impl.mm
index 71b5869..a0c453dd 100644
--- a/ios/components/security_interstitials/safe_browsing/safe_browsing_service_impl.mm
+++ b/ios/components/security_interstitials/safe_browsing/safe_browsing_service_impl.mm
@@ -182,7 +182,6 @@
       /*last_committed_url=*/web_state->GetLastCommittedURL(),
       web::GetUIThreadTaskRunner({}),
       url_lookup_service ? url_lookup_service->GetWeakPtr() : nullptr,
-      /*webui_delegate=*/nullptr,
       hash_real_time_service ? hash_real_time_service->GetWeakPtr() : nullptr,
       /*mechanism_experimenter=*/nullptr,
       /*is_mechanism_experiment_allowed=*/false, hash_real_time_selection);
diff --git a/ios/components/security_interstitials/safe_browsing/safe_browsing_service_unittest.mm b/ios/components/security_interstitials/safe_browsing/safe_browsing_service_unittest.mm
index 40642230..35aaa26a 100644
--- a/ios/components/security_interstitials/safe_browsing/safe_browsing_service_unittest.mm
+++ b/ios/components/security_interstitials/safe_browsing/safe_browsing_service_unittest.mm
@@ -305,7 +305,8 @@
         base::BindRepeating([](bool) { return false; }),
         /*is_off_the_record=*/false,
         /*variations_service=*/nullptr,
-        /*referrer_chain_provider=*/nullptr);
+        /*referrer_chain_provider=*/nullptr,
+        /*webui_delegate=*/nullptr);
     safe_browsing_client_.set_real_time_url_lookup_service(
         lookup_service_.get());
   }
diff --git a/ios/third_party/earl_grey2/src b/ios/third_party/earl_grey2/src
index 3b476eb..40fc925 160000
--- a/ios/third_party/earl_grey2/src
+++ b/ios/third_party/earl_grey2/src
@@ -1 +1 @@
-Subproject commit 3b476eb5360c12898c019df2829f2b42d2682738
+Subproject commit 40fc9251e010da0a2a0d6351617a6959e7fe90a8
diff --git a/ios/web_view/internal/passwords/web_view_account_password_store_factory.mm b/ios/web_view/internal/passwords/web_view_account_password_store_factory.mm
index 6872b10..46a50e4 100644
--- a/ios/web_view/internal/passwords/web_view_account_password_store_factory.mm
+++ b/ios/web_view/internal/passwords/web_view_account_password_store_factory.mm
@@ -12,7 +12,6 @@
 #import "base/functional/callback_helpers.h"
 #import "base/no_destructor.h"
 #import "components/keyed_service/ios/browser_state_dependency_manager.h"
-#import "components/password_manager/core/browser/affiliation/affiliations_prefetcher.h"
 #import "components/password_manager/core/browser/features/password_features.h"
 #import "components/password_manager/core/browser/password_manager_constants.h"
 #import "components/password_manager/core/browser/password_manager_util.h"
@@ -21,8 +20,6 @@
 #import "components/password_manager/core/browser/password_store/password_store_built_in_backend.h"
 #import "components/password_manager/core/browser/password_store_factory_util.h"
 #import "components/prefs/pref_service.h"
-#import "ios/web_view/internal/passwords/web_view_affiliation_service_factory.h"
-#import "ios/web_view/internal/passwords/web_view_affiliations_prefetcher_factory.h"
 
 namespace ios_web_view {
 
@@ -56,10 +53,7 @@
 WebViewAccountPasswordStoreFactory::WebViewAccountPasswordStoreFactory()
     : RefcountedBrowserStateKeyedServiceFactory(
           "AccountPasswordStore",
-          BrowserStateDependencyManager::GetInstance()) {
-  DependsOn(WebViewAffiliationServiceFactory::GetInstance());
-  DependsOn(WebViewAffiliationsPrefetcherFactory::GetInstance());
-}
+          BrowserStateDependencyManager::GetInstance()) {}
 
 WebViewAccountPasswordStoreFactory::~WebViewAccountPasswordStoreFactory() {}
 
@@ -82,17 +76,7 @@
               std::move(login_db),
               syncer::WipeModelUponSyncDisabledBehavior::kAlways));
 
-  password_manager::AffiliationService* affiliation_service =
-      WebViewAffiliationServiceFactory::GetForBrowserState(context);
-  std::unique_ptr<password_manager::AffiliatedMatchHelper>
-      affiliated_match_helper =
-          std::make_unique<password_manager::AffiliatedMatchHelper>(
-              affiliation_service);
-
-  ps->Init(browser_state->GetPrefs(), std::move(affiliated_match_helper));
-
-  WebViewAffiliationsPrefetcherFactory::GetForBrowserState(context)
-      ->RegisterPasswordStore(ps.get());
+  ps->Init(browser_state->GetPrefs(), /*affiliated_match_helper=*/nullptr);
 
   return ps;
 }
diff --git a/media/base/demuxer_memory_limit_android.cc b/media/base/demuxer_memory_limit_android.cc
index 4a7bdc5..c2e97b2e 100644
--- a/media/base/demuxer_memory_limit_android.cc
+++ b/media/base/demuxer_memory_limit_android.cc
@@ -14,7 +14,9 @@
 size_t SelectLimit(size_t default_limit,
                    size_t low_limit,
                    size_t very_low_limit) {
-  if (!base::SysInfo::IsLowEndDeviceOrPartialLowEndModeEnabled()) {
+  // This is truly for only for low end devices since it will have impacts on
+  // the ability to buffer and play HD+ content.
+  if (!base::SysInfo::IsLowEndDevice()) {
     return default_limit;
   }
   // Use very low limit on 512MiB Android Go devices only.
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index f36e06c5..5e1b938 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -430,7 +430,7 @@
 // Enables the "Save Video Frame As" context menu item.
 BASE_FEATURE(kContextMenuSaveVideoFrameAs,
              "ContextMenuSaveVideoFrameAs",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables the "Search Video Frame with <Search Provider>" context menu item.
 BASE_FEATURE(kContextMenuSearchForVideoFrame,
diff --git a/media/gpu/vaapi/test/fake_libva_driver/fake_drv_video.cc b/media/gpu/vaapi/test/fake_libva_driver/fake_drv_video.cc
index 538b3a2..386dd27 100644
--- a/media/gpu/vaapi/test/fake_libva_driver/fake_drv_video.cc
+++ b/media/gpu/vaapi/test/fake_libva_driver/fake_drv_video.cc
@@ -591,6 +591,14 @@
 
   const media::internal::FakeSurface& fake_surface = fdrv->GetSurface(surface);
 
+  CHECK(fdrv->ImageExists(image));
+
+  // TODO(b/316609501): Look into replacing this and making this function
+  // operate the same for both testing and non-testing environments.
+  if (!fake_surface.GetMappedBO().IsValid()) {
+    return VA_STATUS_SUCCESS;
+  }
+
   // Chrome should only request images starting at (0, 0).
   CHECK_EQ(x, 0);
   CHECK_EQ(y, 0);
@@ -604,8 +612,6 @@
   const media::internal::ScopedBOMapping::ScopedAccess mapped_bo =
       fake_surface.GetMappedBO().BeginAccess();
 
-  CHECK(fdrv->ImageExists(image));
-
   const media::internal::FakeImage& fake_image = fdrv->GetImage(image);
 
   // Chrome should only ask the fake driver to download NV12 surfaces onto NV12
diff --git a/media/gpu/vaapi/test/fake_libva_driver/fake_drv_video_unittest.cc b/media/gpu/vaapi/test/fake_libva_driver/fake_drv_video_unittest.cc
index 41ce4f5..fcf27a1 100644
--- a/media/gpu/vaapi/test/fake_libva_driver/fake_drv_video_unittest.cc
+++ b/media/gpu/vaapi/test/fake_libva_driver/fake_drv_video_unittest.cc
@@ -334,13 +334,20 @@
                        /*height=*/720, surfaces, kNumSurfaces,
                        /*surface_attribs=*/nullptr, /*num_attribs=*/0));
 
+  VAImageFormat image_format_nv12{.fourcc = VA_FOURCC_NV12,
+                                  .byte_order = VA_LSB_FIRST,
+                                  .bits_per_pixel = 12};
+
   for (unsigned int i = 0; i < kNumSurfaces; i++) {
-    // TODO(b/258275488): Provide a valid Image ID once that functionality is
-    // implemented.
-    const VAStatus va_res =
+    VAImage img;
+    ASSERT_EQ(VA_STATUS_SUCCESS,
+              vaCreateImage(display_, &image_format_nv12, /*width=*/1280,
+                            /*height=*/720, &img));
+    ASSERT_NE(img.image_id, VA_INVALID_ID);
+    ASSERT_EQ(
+        VA_STATUS_SUCCESS,
         vaGetImage(display_, surfaces[i], /*x=*/0, /*y=*/0,
-                   /*width=*/1280, /*height=*/720, /*image=*/0);
-    EXPECT_EQ(VA_STATUS_SUCCESS, va_res);
+                   /*width=*/1280, /*height=*/720, /*image=*/img.image_id));
   }
 }
 
diff --git a/media/gpu/vaapi/test/fake_libva_driver/scoped_bo_mapping_factory.cc b/media/gpu/vaapi/test/fake_libva_driver/scoped_bo_mapping_factory.cc
index d60cea9..4cc47d4 100644
--- a/media/gpu/vaapi/test/fake_libva_driver/scoped_bo_mapping_factory.cc
+++ b/media/gpu/vaapi/test/fake_libva_driver/scoped_bo_mapping_factory.cc
@@ -131,6 +131,7 @@
 ScopedBOMapping::Plane::~Plane() = default;
 
 ScopedBOMapping::ScopedAccess ScopedBOMapping::BeginAccess() const {
+  CHECK(IsValid());
   return ScopedBOMapping::ScopedAccess(*this);
 }
 
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index 542f4db..1e61271 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -345,11 +345,7 @@
   return media::VAImplementation::kOther;
 }
 
-bool UseGlobalVaapiLock(media::VAImplementation implementation_type) {
-  if (!media::VaapiWrapper::allow_disabling_global_lock_) {
-    return true;
-  }
-
+bool IsThreadSafeDriver(media::VAImplementation implementation_type) {
   // Only iHD and Mesa Gallium are known to be thread safe at the moment.
   // * Mesa Gallium: b/144877595
   // * iHD: crbug.com/1123429.
@@ -357,7 +353,15 @@
       base::MakeFixedFlatSet<media::VAImplementation>(
           {media::VAImplementation::kMesaGallium,
            media::VAImplementation::kIntelIHD});
-  return !kNoVaapiLockImplementations.contains(implementation_type) ||
+  return kNoVaapiLockImplementations.contains(implementation_type);
+}
+
+bool UseGlobalVaapiLock(media::VAImplementation implementation_type) {
+  if (!media::VaapiWrapper::allow_disabling_global_lock_) {
+    return true;
+  }
+
+  return !IsThreadSafeDriver(implementation_type) ||
          base::FeatureList::IsEnabled(media::kGlobalVaapiLock);
 }
 
@@ -674,6 +678,15 @@
   return true;
 }
 
+// Creates an AutoLock iff |va_lock_| is not null and the libva backend is
+// thread-safe.
+std::unique_ptr<base::AutoLock> AutoLockOnlyIfNeeded(base::Lock* lock) {
+  if (lock && !IsThreadSafeDriver(VaapiWrapper::GetImplementationType())) {
+    return std::make_unique<base::AutoLock>(*lock);
+  }
+  return nullptr;
+}
+
 // Can't statically initialize the profile map:
 // https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables
 using ProfileCodecMap = std::map<VideoCodecProfile, VAProfile>;
@@ -2725,11 +2738,13 @@
 
 bool VaapiWrapper::UploadVideoFrameToSurface(const VideoFrame& frame,
                                              VASurfaceID va_surface_id,
-                                             const gfx::Size& va_surface_size) {
+                                             const gfx::Size& va_surface_size)
+    NO_THREAD_SAFETY_ANALYSIS {
   CHECK(!enforce_sequence_affinity_ ||
         sequence_checker_.CalledOnValidSequence());
   TRACE_EVENT0("media,gpu", "VaapiWrapper::UploadVideoFrameToSurface");
-  base::AutoLockMaybe auto_lock(va_lock_.get());
+  std::unique_ptr<base::AutoLock> auto_lock =
+      AutoLockOnlyIfNeeded(va_lock_.get());
   TRACE_EVENT0("media,gpu", "VaapiWrapper::UploadVideoFrameToSurfaceLocked");
 
   if (frame.visible_rect().origin() != gfx::Point(0, 0)) {
@@ -2774,9 +2789,11 @@
     return false;
   }
 
-  ScopedVABufferMapping mapping(va_lock_, va_display_, image.buf);
-  if (!mapping.IsValid())
+  ScopedVABufferMapping mapping(auto_lock ? va_lock_ : nullptr, va_display_,
+                                image.buf);
+  if (!mapping.IsValid()) {
     return false;
+  }
   uint8_t* image_ptr = static_cast<uint8_t*>(mapping.data());
 
   if (!ClearNV12Padding(image, visible_size, image_ptr)) {
@@ -2789,8 +2806,10 @@
     TRACE_EVENT0("media,gpu", "VaapiWrapper::UploadVideoFrameToSurface_copy");
 
     std::unique_ptr<base::AutoUnlock> auto_unlock;
-    if (va_lock_)
+    if (auto_lock) {
       auto_unlock = std::make_unique<base::AutoUnlock>(*va_lock_);
+    }
+
     if (frame.format() == PIXEL_FORMAT_I420) {
       ret = libyuv::I420ToNV12(
           frame.data(VideoFrame::kYPlane), frame.stride(VideoFrame::kYPlane),
@@ -2836,13 +2855,14 @@
 }
 
 uint64_t VaapiWrapper::GetEncodedChunkSize(VABufferID buffer_id,
-                                           VASurfaceID sync_surface_id) {
+                                           VASurfaceID sync_surface_id)
+    NO_THREAD_SAFETY_ANALYSIS {
   CHECK(!enforce_sequence_affinity_ ||
         sequence_checker_.CalledOnValidSequence());
   TRACE_EVENT0("media,gpu", "VaapiWrapper::GetEncodedChunkSize");
-  base::AutoLockMaybe auto_lock(va_lock_.get());
+  std::unique_ptr<base::AutoLock> auto_lock =
+      AutoLockOnlyIfNeeded(va_lock_.get());
   TRACE_EVENT0("media,gpu", "VaapiWrapper::GetEncodedChunkSizeLocked");
-
   // vaSyncSurface() is not necessary on Intel platforms as long as there is a
   // vaMapBuffer() like in ScopedVABufferMapping below.
   // vaSyncSurface() synchronizes all active workloads (potentially many, e.g.
@@ -2854,7 +2874,8 @@
     VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVASyncSurface, 0u);
   }
 
-  ScopedVABufferMapping mapping(va_lock_, va_display_, buffer_id);
+  ScopedVABufferMapping mapping(auto_lock ? va_lock_ : nullptr, va_display_,
+                                buffer_id);
   if (!mapping.IsValid())
     return 0u;
 
@@ -2873,14 +2894,14 @@
     absl::optional<VASurfaceID> sync_surface_id,
     uint8_t* target_ptr,
     size_t target_size,
-    size_t* coded_data_size) {
+    size_t* coded_data_size) NO_THREAD_SAFETY_ANALYSIS {
   CHECK(!enforce_sequence_affinity_ ||
         sequence_checker_.CalledOnValidSequence());
   DCHECK(target_ptr);
   TRACE_EVENT0("media,gpu", "VaapiWrapper::DownloadFromVABuffer");
-  base::AutoLockMaybe auto_lock(va_lock_.get());
+  std::unique_ptr<base::AutoLock> auto_lock =
+      AutoLockOnlyIfNeeded(va_lock_.get());
   TRACE_EVENT0("media,gpu", "VaapiWrapper::DownloadFromVABufferLocked");
-
   // vaSyncSurface() is not necessary on Intel platforms as long as there is a
   // vaMapBuffer() like in ScopedVABufferMapping below, see b/184312032.
   // |sync_surface_id| will be nullopt because it has been synced already.
@@ -2893,7 +2914,8 @@
     VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVASyncSurface, false);
   }
 
-  ScopedVABufferMapping mapping(va_lock_, va_display_, buffer_id);
+  ScopedVABufferMapping mapping(auto_lock ? va_lock_ : nullptr, va_display_,
+                                buffer_id);
   if (!mapping.IsValid())
     return false;
   auto* buffer_segment =
diff --git a/mojo/public/rust/BUILD.gn b/mojo/public/rust/BUILD.gn
index d0f24af..61efefb 100644
--- a/mojo/public/rust/BUILD.gn
+++ b/mojo/public/rust/BUILD.gn
@@ -8,8 +8,8 @@
 import("//testing/test.gni")
 
 # Meta target to build everything
-group("rust") {
-  deps = [ ":mojo_rust" ]
+group("mojo_rust") {
+  deps = [ ":mojo" ]
 }
 
 # These tests require real mojo and can't be mixed with test-only mojo. Mojo can
@@ -45,9 +45,8 @@
   visibility = [ ":*" ]
 }
 
-rust_static_library("mojo_rust_system") {
+rust_static_library("mojo_system") {
   crate_root = "system/lib.rs"
-  crate_name = "mojo_system"
   allow_unsafe = true
 
   sources = [
@@ -78,10 +77,9 @@
               rebase_path(bindgen_output[0], get_path_info(crate_root, "dir")) ]
 }
 
-rust_static_library("mojo_rust_system_test_support") {
+rust_static_library("mojo_system_test_support") {
   testonly = true
   crate_root = "system/test_support/lib.rs"
-  crate_name = "mojo_system_test_support"
 
   # Calls Mojo FFI functions.
   allow_unsafe = true
@@ -89,14 +87,13 @@
   sources = [ "system/test_support/lib.rs" ]
 
   deps = [
-    ":mojo_rust_system",
+    ":mojo_system",
     "//mojo/public/c/system",
   ]
 }
 
-rust_static_library("mojo_rust_bindings") {
+rust_static_library("mojo_bindings") {
   crate_root = "bindings/lib.rs"
-  crate_name = "mojo_bindings"
   allow_unsafe = true
 
   sources = [
@@ -108,18 +105,17 @@
     "bindings/mojom.rs",
   ]
 
-  deps = [ ":mojo_rust_system" ]
+  deps = [ ":mojo_system" ]
 }
 
 # Convenience target to link both Mojo Rust components and reexport them under a
 # single `mojo` name.
-rust_static_library("mojo_rust") {
+rust_static_library("mojo") {
   crate_root = "lib.rs"
-  crate_name = "mojo"
 
   sources = [ "lib.rs" ]
   deps = [
-    ":mojo_rust_bindings",
-    ":mojo_rust_system",
+    ":mojo_bindings",
+    ":mojo_system",
   ]
 }
diff --git a/mojo/public/rust/bindings/decoding.rs b/mojo/public/rust/bindings/decoding.rs
index de611d4..b3d207a 100644
--- a/mojo/public/rust/bindings/decoding.rs
+++ b/mojo/public/rust/bindings/decoding.rs
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+chromium::import! {
+    "//mojo/public/rust:mojo_system" as system;
+}
+
 use crate::encoding::{
     Bits, Context, DataHeader, DataHeaderValue, MojomPrimitive, DATA_HEADER_SIZE,
 };
@@ -11,7 +15,7 @@
 use std::ptr;
 use std::vec::Vec;
 
-use system::{self, CastHandle, Handle, UntypedHandle};
+use system::{CastHandle, Handle, UntypedHandle};
 
 #[derive(Debug, Eq, PartialEq)]
 pub enum ValidationError {
diff --git a/mojo/public/rust/bindings/encoding.rs b/mojo/public/rust/bindings/encoding.rs
index 18774e2..541846f4 100644
--- a/mojo/public/rust/bindings/encoding.rs
+++ b/mojo/public/rust/bindings/encoding.rs
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+chromium::import! {
+    "//mojo/public/rust:mojo_system" as system;
+}
+
 use crate::mojom::MOJOM_NULL_POINTER;
 
 use std::mem;
diff --git a/mojo/public/rust/bindings/lib.rs b/mojo/public/rust/bindings/lib.rs
index d1fee52..2b275c3 100644
--- a/mojo/public/rust/bindings/lib.rs
+++ b/mojo/public/rust/bindings/lib.rs
@@ -6,8 +6,10 @@
 // Require unsafe blocks for unsafe operations even in an unsafe fn.
 #![deny(unsafe_op_in_unsafe_fn)]
 
-/// `pub` since a macro refers to `$crate::system`.
-pub extern crate mojo_system as system;
+chromium::import! {
+    // `pub` since a macro refers to `$crate::system`.
+    pub "//mojo/public/rust:mojo_system" as system;
+}
 
 pub mod macros;
 
diff --git a/mojo/public/rust/bindings/mojom.rs b/mojo/public/rust/bindings/mojom.rs
index 495a567..ff09be0 100644
--- a/mojo/public/rust/bindings/mojom.rs
+++ b/mojo/public/rust/bindings/mojom.rs
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+chromium::import! {
+    "//mojo/public/rust:mojo_system" as system;
+}
+
 use crate::decoding::{Decoder, DecodingState, ValidationError};
 use crate::encoding;
 use crate::encoding::{
diff --git a/mojo/public/rust/lib.rs b/mojo/public/rust/lib.rs
index b27b1a8..1609844 100644
--- a/mojo/public/rust/lib.rs
+++ b/mojo/public/rust/lib.rs
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-pub use mojo_bindings as bindings;
-pub use mojo_system as system;
+chromium::import! {
+    pub "//mojo/public/rust:mojo_bindings" as bindings;
+    pub "//mojo/public/rust:mojo_system" as system;
+}
 
 pub use system::MojoResult;
diff --git a/mojo/public/rust/system/test_support/lib.rs b/mojo/public/rust/system/test_support/lib.rs
index a06fcafe..4d38840 100644
--- a/mojo/public/rust/system/test_support/lib.rs
+++ b/mojo/public/rust/system/test_support/lib.rs
@@ -5,7 +5,9 @@
 //! Utilities to support testing Mojo clients and the Mojo system implementation
 //! itself.
 
-extern crate mojo_system as system;
+chromium::import! {
+    "//mojo/public/rust:mojo_system" as system;
+}
 
 macro_rules! gen_panic_stub {
     ($name:ident $(, $arg:ident : $arg_ty:ty)*) => {
diff --git a/mojo/public/rust/tests/BUILD.gn b/mojo/public/rust/tests/BUILD.gn
index 845f1a9ad7..329f5b6 100644
--- a/mojo/public/rust/tests/BUILD.gn
+++ b/mojo/public/rust/tests/BUILD.gn
@@ -18,7 +18,7 @@
   deps = [
     ":c_test_support",
     ":test_util",
-    "//mojo/public/rust:mojo_rust",
+    "//mojo/public/rust:mojo",
     "//testing/rust_gtest_interop",
     "//third_party/rust/lazy_static/v1:lib",
   ]
@@ -40,8 +40,8 @@
     ":c_test_support",
     ":test_util",
     "//mojo/public/c/system",
-    "//mojo/public/rust:mojo_rust",
-    "//mojo/public/rust:mojo_rust_system_test_support",
+    "//mojo/public/rust:mojo",
+    "//mojo/public/rust:mojo_system_test_support",
     "//testing/rust_gtest_interop",
   ]
 
@@ -72,7 +72,7 @@
   deps = [
     ":c_test_support",
     ":test_util",
-    "//mojo/public/rust:mojo_rust",
+    "//mojo/public/rust:mojo",
     "//testing/rust_gtest_interop",
   ]
 }
diff --git a/mojo/public/rust/tests/encoding/encoding.rs b/mojo/public/rust/tests/encoding/encoding.rs
index cf13306..686b0b0 100644
--- a/mojo/public/rust/tests/encoding/encoding.rs
+++ b/mojo/public/rust/tests/encoding/encoding.rs
@@ -8,6 +8,11 @@
 //! and the result being caught in the test! macro. If a test function
 //! returns without panicking, it is assumed to pass.
 
+chromium::import! {
+    "//mojo/public/rust:mojo";
+    "//mojo/public/rust/tests:test_util" as util;
+}
+
 use crate::mojom_validation::*;
 
 use mojo::bindings::encoding::Context;
diff --git a/mojo/public/rust/tests/encoding/lib.rs b/mojo/public/rust/tests/encoding/lib.rs
index 07db13f..e4dc071 100644
--- a/mojo/public/rust/tests/encoding/lib.rs
+++ b/mojo/public/rust/tests/encoding/lib.rs
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-extern crate mojo_system_test_support as test_support;
-extern crate rust_gtest_interop;
-extern crate test_util as util;
+chromium::import! {
+    "//mojo/public/rust:mojo_system_test_support" as test_support;
+}
 
 /// Macro to produce a test which uses the stub Mojo backend. This is used
 /// instead of the macro from `test_util`, which initializes the full Mojo
diff --git a/mojo/public/rust/tests/encoding/mojom_validation.rs b/mojo/public/rust/tests/encoding/mojom_validation.rs
index 664daac3..6750abf 100644
--- a/mojo/public/rust/tests/encoding/mojom_validation.rs
+++ b/mojo/public/rust/tests/encoding/mojom_validation.rs
@@ -8,6 +8,10 @@
 #![allow(unused_variables)]
 #![allow(dead_code)]
 
+chromium::import! {
+    "//mojo/public/rust:mojo";
+}
+
 use mojo::bindings::decoding::{self, Decoder, ValidationError};
 use mojo::bindings::encoding::{
     self, Context, DataHeaderValue, Encoder, EncodingState, DATA_HEADER_SIZE,
diff --git a/mojo/public/rust/tests/encoding/regression.rs b/mojo/public/rust/tests/encoding/regression.rs
index d968fb1..d825ee97 100644
--- a/mojo/public/rust/tests/encoding/regression.rs
+++ b/mojo/public/rust/tests/encoding/regression.rs
@@ -8,6 +8,10 @@
 //! and the result being caught in the test! macro. If a test function
 //! returns without panicking, it is assumed to pass.
 
+chromium::import! {
+    "//mojo/public/rust:mojo";
+}
+
 use mojo::bindings::decoding::{Decoder, ValidationError};
 use mojo::bindings::encoding::{self, Context, DataHeaderValue, Encoder, EncodingState};
 use mojo::bindings::impl_encodable_for_pointer;
diff --git a/mojo/public/rust/tests/encoding/validation.rs b/mojo/public/rust/tests/encoding/validation.rs
index f5a28c8..71b97e6 100644
--- a/mojo/public/rust/tests/encoding/validation.rs
+++ b/mojo/public/rust/tests/encoding/validation.rs
@@ -8,6 +8,11 @@
 //! and the result being caught in the test! macro. If a test function
 //! returns without panicking, it is assumed to pass.
 
+chromium::import! {
+    "//mojo/public/rust:mojo";
+    "//mojo/public/rust/tests:test_util" as util;
+}
+
 use crate::mojom_validation::*;
 
 use mojo::bindings::mojom::MojomMessageOption;
diff --git a/mojo/public/rust/tests/integration/lib.rs b/mojo/public/rust/tests/integration/lib.rs
index 37d789b..22be60f 100644
--- a/mojo/public/rust/tests/integration/lib.rs
+++ b/mojo/public/rust/tests/integration/lib.rs
@@ -4,6 +4,11 @@
 
 //! Tests for system + encoding that use a real Mojo implementation.
 
+chromium::import! {
+    "//mojo/public/rust:mojo";
+    "//mojo/public/rust/tests:test_util";
+}
+
 use mojo::bindings::mojom::{MojomInterface, MojomInterfaceRecv, MojomInterfaceSend};
 use mojo::system::message_pipe;
 use mojo::system::{Handle, HandleSignals};
diff --git a/mojo/public/rust/tests/system/lib.rs b/mojo/public/rust/tests/system/lib.rs
index f56ef13..978619e 100644
--- a/mojo/public/rust/tests/system/lib.rs
+++ b/mojo/public/rust/tests/system/lib.rs
@@ -11,6 +11,11 @@
 #![feature(assert_matches)]
 #![feature(maybe_uninit_write_slice)]
 
+chromium::import! {
+    "//mojo/public/rust:mojo";
+    "//mojo/public/rust/tests:test_util";
+}
+
 mod run_loop;
 
 use mojo::system::shared_buffer::{self, SharedBuffer};
diff --git a/mojo/public/rust/tests/system/run_loop.rs b/mojo/public/rust/tests/system/run_loop.rs
index a7ee54bd..0151fb7 100644
--- a/mojo/public/rust/tests/system/run_loop.rs
+++ b/mojo/public/rust/tests/system/run_loop.rs
@@ -8,6 +8,11 @@
 //! and the result being caught in the test! macro. If a test function
 //! returns without panicking, it is assumed to pass.
 
+chromium::import! {
+    "//mojo/public/rust:mojo";
+    "//mojo/public/rust/tests:test_util";
+}
+
 use mojo::bindings::run_loop::{self, Handler, RunLoop, Token, WaitError};
 use mojo::system::{message_pipe, HandleSignals, MOJO_INDEFINITE};
 use rust_gtest_interop::prelude::*;
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
index 0cf19db..3f6e54e 100644
--- a/mojo/public/tools/bindings/mojom.gni
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -340,24 +340,12 @@
 #
 #   generate_webui_js_bindings (optional)
 #       Generate WebUI bindings in JavaScript rather than TypeScript. Defaults
-#       to false. ChromeOS only parameter. Use instead of
-#       |use_typescript_sources|, which is being removed.
-#
-#   use_typescript_sources (optional)
-#       Generate WebUI bindings in TypeScript. Defaults to "true". When set to
-#       "false", WebUI bindings will be generated in JavaScript instead.
-#       Ignored if |webui_module_path| is not set.
-#
-#       Note: DO NOT USE in new targets. New WebUI code should always be in
-#       TypeScript, so the corresponding WebUI mojo bindings should be in
-#       TypeScript as well. This parameter should only be set to false to
-#       support legacy WebUI code that still uses the Closure Compiler for
-#       typechecking.
+#       to false. ChromeOS only parameter.
 #
 #   generate_legacy_js_bindings (optional)
 #       Generate js_data_deps target containing legacy JavaScript bindings files
 #       for Blink tests and other non-WebUI users when generating TypeScript
-#       bindings for WebUI. Ignored if use_typescript_sources is not set to
+#       bindings for WebUI. Ignored if generate_webui_js_bindings is set to
 #       true.
 #
 #   js_generate_struct_deserializers (optional)
@@ -386,7 +374,7 @@
 #       given URL.
 #
 #       Note: WebUI module bindings are generated in TypeScript by default,
-#       unless |use_typescript_sources| is specified as false.
+#       unless |generate_webui_js_bindings| is specified as true.
 #
 # The following parameters are used to support the component build. They are
 # needed so that bindings which are linked with a component can use the same
@@ -820,10 +808,8 @@
           "--add-module-metadata",
           "webui_module_path=${invoker.webui_module_path}",
         ]
-        if ((defined(invoker.use_typescript_sources) &&
-             !invoker.use_typescript_sources) ||
-            (defined(invoker.generate_webui_js_bindings) &&
-             invoker.generate_webui_js_bindings)) {
+        if (defined(invoker.generate_webui_js_bindings) &&
+            invoker.generate_webui_js_bindings) {
           args += [
             "--add-module-metadata",
             "generate_webui_js=True",
@@ -1861,19 +1847,7 @@
   }
 
   use_typescript_for_target = defined(invoker.webui_module_path) &&
-                              !defined(invoker.generate_webui_js_bindings) &&
-                              (!defined(invoker.use_typescript_sources) ||
-                               (defined(invoker.use_typescript_sources) &&
-                                invoker.use_typescript_sources))
-
-  # Don't allow JavaScript WebUI bindings on non-ChromeOS platforms.
-  # TODO (rbpotter): Remove this check once use_typescript_sources is removed.
-  if (!use_typescript_for_target && defined(invoker.webui_module_path)) {
-    assert(
-        is_chromeos_ash,
-        "WebUI bindings should be in TypeScript on non-ChromeOS platforms. " +
-            "Remove use_typescript_sources = false")
-  }
+                              !defined(invoker.generate_webui_js_bindings)
 
   generate_legacy_js = !use_typescript_for_target ||
                        (defined(invoker.generate_legacy_js_bindings) &&
diff --git a/net/base/features.cc b/net/base/features.cc
index 29f9dde..04f5d1b 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -234,6 +234,14 @@
              "WaitForFirstPartySetsInit",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Controls the maximum time duration an outermost frame navigation should be
+// deferred by RWS initialization.
+extern const base::FeatureParam<base::TimeDelta>
+    kWaitForFirstPartySetsInitNavigationThrottleTimeout{
+        &kWaitForFirstPartySetsInit,
+        "kWaitForFirstPartySetsInitNavigationThrottleTimeout",
+        base::Seconds(0)};
+
 BASE_FEATURE(kPartitionedCookies,
              "PartitionedCookies",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/net/base/features.h b/net/base/features.h
index bfda664..2040267a 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -287,6 +287,11 @@
 // cookies.
 NET_EXPORT BASE_DECLARE_FEATURE(kWaitForFirstPartySetsInit);
 
+// Controls the maximum time duration an outermost frame navigation should be
+// deferred by RWS initialization.
+NET_EXPORT extern const base::FeatureParam<base::TimeDelta>
+    kWaitForFirstPartySetsInitNavigationThrottleTimeout;
+
 // When enabled, sites can opt-in to having their cookies partitioned by
 // top-level site with the Partitioned attribute. Partitioned cookies will only
 // be sent when the browser is on the same top-level site that it was on when
diff --git a/net/cert/cert_verify_proc_builtin_unittest.cc b/net/cert/cert_verify_proc_builtin_unittest.cc
index 079b841..3b65d823 100644
--- a/net/cert/cert_verify_proc_builtin_unittest.cc
+++ b/net/cert/cert_verify_proc_builtin_unittest.cc
@@ -1017,8 +1017,7 @@
                               /*issuer=*/&root_ok);
   // Using the old intermediate as a template does not preserve the subject,
   // SKID, or key.
-  intermediate_ok.SetSubjectTLV(
-      base::as_bytes(base::make_span(builders[1]->GetSubject())));
+  intermediate_ok.SetSubjectTLV(base::as_byte_span(builders[1]->GetSubject()));
   intermediate_ok.SetKey(bssl::UpRef(builders[1]->GetKey()));
   intermediate_ok.SetSubjectKeyIdentifier(
       builders[1]->GetSubjectKeyIdentifier());
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index 77616510..6e365cfa 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -1254,8 +1254,7 @@
 
     // NOTE: The signature is NOT recomputed over TBSCertificate -- for these
     // tests it isn't needed.
-    return X509Certificate::CreateFromBytes(
-        base::as_bytes(base::make_span(cert_der)));
+    return X509Certificate::CreateFromBytes(base::as_byte_span(cert_der));
   }
 
   static scoped_refptr<X509Certificate> CreateChain(
diff --git a/net/cert/ct_objects_extractor_unittest.cc b/net/cert/ct_objects_extractor_unittest.cc
index 3a4b2bbe..2d67c15 100644
--- a/net/cert/ct_objects_extractor_unittest.cc
+++ b/net/cert/ct_objects_extractor_unittest.cc
@@ -26,8 +26,8 @@
     ASSERT_EQ(2u, precert_chain_.size());
 
     std::string der_test_cert(ct::GetDerEncodedX509Cert());
-    test_cert_ = X509Certificate::CreateFromBytes(
-        base::as_bytes(base::make_span(der_test_cert)));
+    test_cert_ =
+        X509Certificate::CreateFromBytes(base::as_byte_span(der_test_cert));
     ASSERT_TRUE(test_cert_);
 
     log_ = CTLogVerifier::Create(ct::GetTestPublicKey(), "testlog");
@@ -138,12 +138,11 @@
 TEST_F(CTObjectsExtractorTest, ExtractSCTListFromOCSPResponse) {
   std::string der_subject_cert(ct::GetDerEncodedFakeOCSPResponseCert());
   scoped_refptr<X509Certificate> subject_cert =
-      X509Certificate::CreateFromBytes(
-          base::as_bytes(base::make_span(der_subject_cert)));
+      X509Certificate::CreateFromBytes(base::as_byte_span(der_subject_cert));
   ASSERT_TRUE(subject_cert);
   std::string der_issuer_cert(ct::GetDerEncodedFakeOCSPResponseIssuerCert());
-  scoped_refptr<X509Certificate> issuer_cert = X509Certificate::CreateFromBytes(
-      base::as_bytes(base::make_span(der_issuer_cert)));
+  scoped_refptr<X509Certificate> issuer_cert =
+      X509Certificate::CreateFromBytes(base::as_byte_span(der_issuer_cert));
   ASSERT_TRUE(issuer_cert);
 
   std::string fake_sct_list = ct::GetFakeOCSPExtensionValue();
@@ -160,8 +159,8 @@
 // Test that the extractor honours serial number.
 TEST_F(CTObjectsExtractorTest, ExtractSCTListFromOCSPResponseMatchesSerial) {
   std::string der_issuer_cert(ct::GetDerEncodedFakeOCSPResponseIssuerCert());
-  scoped_refptr<X509Certificate> issuer_cert = X509Certificate::CreateFromBytes(
-      base::as_bytes(base::make_span(der_issuer_cert)));
+  scoped_refptr<X509Certificate> issuer_cert =
+      X509Certificate::CreateFromBytes(base::as_byte_span(der_issuer_cert));
   ASSERT_TRUE(issuer_cert);
 
   std::string ocsp_response = ct::GetDerEncodedFakeOCSPResponse();
@@ -176,8 +175,7 @@
 TEST_F(CTObjectsExtractorTest, ExtractSCTListFromOCSPResponseMatchesIssuer) {
   std::string der_subject_cert(ct::GetDerEncodedFakeOCSPResponseCert());
   scoped_refptr<X509Certificate> subject_cert =
-      X509Certificate::CreateFromBytes(
-          base::as_bytes(base::make_span(der_subject_cert)));
+      X509Certificate::CreateFromBytes(base::as_byte_span(der_subject_cert));
   ASSERT_TRUE(subject_cert);
 
   std::string ocsp_response = ct::GetDerEncodedFakeOCSPResponse();
diff --git a/net/cert/internal/cert_issuer_source_aia.cc b/net/cert/internal/cert_issuer_source_aia.cc
index 3007f397..e3cbaf5 100644
--- a/net/cert/internal/cert_issuer_source_aia.cc
+++ b/net/cert/internal/cert_issuer_source_aia.cc
@@ -76,8 +76,7 @@
   if (!pem_tokenizer.GetNext())
     return false;
 
-  return ParseCertFromDer(base::as_bytes(base::make_span(pem_tokenizer.data())),
-                          results);
+  return ParseCertFromDer(base::as_byte_span(pem_tokenizer.data()), results);
 }
 
 class AiaRequest : public bssl::CertIssuerSource::Request {
diff --git a/net/cert/internal/system_trust_store.cc b/net/cert/internal/system_trust_store.cc
index 2b0916a9..a87a57c1 100644
--- a/net/cert/internal/system_trust_store.cc
+++ b/net/cert/internal/system_trust_store.cc
@@ -177,8 +177,7 @@
     }
 
     CertificateList certs = X509Certificate::CreateCertificateListFromBytes(
-        base::as_bytes(base::make_span(certs_file)),
-        X509Certificate::FORMAT_AUTO);
+        base::as_byte_span(certs_file), X509Certificate::FORMAT_AUTO);
 
     for (const auto& cert : certs) {
       bssl::CertErrors errors;
diff --git a/net/cert/merkle_tree_leaf_unittest.cc b/net/cert/merkle_tree_leaf_unittest.cc
index 776a0fc9..f798561 100644
--- a/net/cert/merkle_tree_leaf_unittest.cc
+++ b/net/cert/merkle_tree_leaf_unittest.cc
@@ -46,8 +46,8 @@
  public:
   void SetUp() override {
     std::string der_test_cert(ct::GetDerEncodedX509Cert());
-    test_cert_ = X509Certificate::CreateFromBytes(
-        base::as_bytes(base::make_span(der_test_cert)));
+    test_cert_ =
+        X509Certificate::CreateFromBytes(base::as_byte_span(der_test_cert));
     ASSERT_TRUE(test_cert_);
 
     GetX509CertSCT(&x509_sct_);
diff --git a/net/cert/multi_log_ct_verifier_unittest.cc b/net/cert/multi_log_ct_verifier_unittest.cc
index cdebfc0..c6a1959 100644
--- a/net/cert/multi_log_ct_verifier_unittest.cc
+++ b/net/cert/multi_log_ct_verifier_unittest.cc
@@ -49,8 +49,8 @@
 
     verifier_ = std::make_unique<MultiLogCTVerifier>(log_verifiers_);
     std::string der_test_cert(ct::GetDerEncodedX509Cert());
-    chain_ = X509Certificate::CreateFromBytes(
-        base::as_bytes(base::make_span(der_test_cert)));
+    chain_ =
+        X509Certificate::CreateFromBytes(base::as_byte_span(der_test_cert));
     ASSERT_TRUE(chain_.get());
 
     embedded_sct_chain_ =
diff --git a/net/cert/x509_certificate.cc b/net/cert/x509_certificate.cc
index a99c6ce..ef5b2a8 100644
--- a/net/cert/x509_certificate.cc
+++ b/net/cert/x509_certificate.cc
@@ -240,8 +240,8 @@
 
     bssl::UniquePtr<CRYPTO_BUFFER> handle;
     if (format & FORMAT_PEM_CERT_SEQUENCE) {
-      handle = CreateCertBufferFromBytesWithSanityCheck(
-          base::as_bytes(base::make_span(decoded)));
+      handle =
+          CreateCertBufferFromBytesWithSanityCheck(base::as_byte_span(decoded));
     }
     if (handle) {
       // Parsed a DER encoded certificate. All PEM blocks that follow must
@@ -258,9 +258,8 @@
       for (size_t i = 0;
            certificates.empty() && i < std::size(kFormatDecodePriority); ++i) {
         if (format & kFormatDecodePriority[i]) {
-          certificates = CreateCertBuffersFromBytes(
-              base::as_bytes(base::make_span(decoded)),
-              kFormatDecodePriority[i]);
+          certificates = CreateCertBuffersFromBytes(base::as_byte_span(decoded),
+                                                    kFormatDecodePriority[i]);
         }
       }
     }
diff --git a/net/cert/x509_util_unittest.cc b/net/cert/x509_util_unittest.cc
index 6a61fe9..8956ba9 100644
--- a/net/cert/x509_util_unittest.cc
+++ b/net/cert/x509_util_unittest.cc
@@ -33,8 +33,8 @@
 
   ASSERT_TRUE(private_key.get());
 
-  scoped_refptr<X509Certificate> cert(X509Certificate::CreateFromBytes(
-      base::as_bytes(base::make_span(der_cert))));
+  scoped_refptr<X509Certificate> cert(
+      X509Certificate::CreateFromBytes(base::as_byte_span(der_cert)));
   ASSERT_TRUE(cert.get());
 
   EXPECT_EQ("subject", cert->subject().common_name);
@@ -142,8 +142,8 @@
       private_key->key(), x509_util::DIGEST_SHA256, "CN=subject", 1,
       base::Time::Now(), base::Time::Now() + base::Days(1), {}, &der_cert));
 
-  scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromBytes(
-      base::as_bytes(base::make_span(der_cert)));
+  scoped_refptr<X509Certificate> cert =
+      X509Certificate::CreateFromBytes(base::as_byte_span(der_cert));
   ASSERT_TRUE(cert.get());
 
   EXPECT_EQ("subject", cert->subject().GetDisplayName());
diff --git a/net/dns/dns_query_unittest.cc b/net/dns/dns_query_unittest.cc
index 3db9e0e..a53cecff 100644
--- a/net/dns/dns_query_unittest.cc
+++ b/net/dns/dns_query_unittest.cc
@@ -48,8 +48,7 @@
     "example"
     "\x03"
     "com";
-const base::span<const uint8_t> kQName =
-    base::as_bytes(base::make_span(kQNameData));
+const base::span<const uint8_t> kQName = base::as_byte_span(kQNameData);
 
 TEST(DnsQueryTest, Constructor) {
   // This includes \0 at the end.
diff --git a/net/dns/dns_response_unittest.cc b/net/dns/dns_response_unittest.cc
index a9218978..a587a53 100644
--- a/net/dns/dns_response_unittest.cc
+++ b/net/dns/dns_response_unittest.cc
@@ -499,8 +499,8 @@
       "\x03"
       "org";
   // Compilers want to copy when binding temporary to const &, so must use heap.
-  auto query = std::make_unique<DnsQuery>(
-      0xcafe, base::as_bytes(base::make_span(qname)), dns_protocol::kTypeA);
+  auto query = std::make_unique<DnsQuery>(0xcafe, base::as_byte_span(qname),
+                                          dns_protocol::kTypeA);
 
   const uint8_t response_data[] = {
       // Header
@@ -566,7 +566,7 @@
 
   // Reject wrong question.
   auto wrong_query = std::make_unique<DnsQuery>(
-      0xcafe, base::as_bytes(base::make_span(qname)), dns_protocol::kTypeCNAME);
+      0xcafe, base::as_byte_span(qname), dns_protocol::kTypeCNAME);
   EXPECT_FALSE(resp.InitParse(sizeof(response_data), *wrong_query));
   EXPECT_FALSE(resp.IsValid());
   EXPECT_THAT(resp.id(), testing::Optional(0xcafe));
@@ -612,8 +612,8 @@
       "\x03"
       "org";
   // Compilers want to copy when binding temporary to const &, so must use heap.
-  auto query = std::make_unique<DnsQuery>(
-      0xcafe, base::as_bytes(base::make_span(qname)), dns_protocol::kTypeA);
+  auto query = std::make_unique<DnsQuery>(0xcafe, base::as_byte_span(qname),
+                                          dns_protocol::kTypeA);
 
   const uint8_t response_data[] = {
       // Header
@@ -673,8 +673,7 @@
 
   const char kQueryName[] = "\003www\006google\004test";
   DnsQuery query(
-      /*id=*/581, base::as_bytes(base::make_span(kQueryName)),
-      dns_protocol::kTypeA);
+      /*id=*/581, base::as_byte_span(kQueryName), dns_protocol::kTypeA);
   EXPECT_FALSE(resp.InitParse(sizeof(kResponse) - 1, query));
 }
 
@@ -701,8 +700,7 @@
 
   const char kQueryName[] = "\003www\006google\004test";
   DnsQuery query(
-      /*id=*/582, base::as_bytes(base::make_span(kQueryName)),
-      dns_protocol::kTypeA);
+      /*id=*/582, base::as_byte_span(kQueryName), dns_protocol::kTypeA);
   EXPECT_FALSE(resp.InitParse(sizeof(kResponse) - 1, query));
 }
 
@@ -1009,8 +1007,7 @@
   // a too-long name.
   const char kQueryName[] = "\005query\004test";
   DnsQuery query(
-      /*id=*/581, base::as_bytes(base::make_span(kQueryName)),
-      dns_protocol::kTypeA);
+      /*id=*/581, base::as_byte_span(kQueryName), dns_protocol::kTypeA);
   EXPECT_FALSE(resp.InitParse(response_data.size(), query));
 }
 
@@ -1036,8 +1033,7 @@
 
   const char kQueryName[] = "\003www\006google\006testtt";
   DnsQuery query(
-      /*id=*/581, base::as_bytes(base::make_span(kQueryName)),
-      dns_protocol::kTypeA);
+      /*id=*/581, base::as_byte_span(kQueryName), dns_protocol::kTypeA);
   EXPECT_FALSE(resp.InitParse(sizeof(kResponse) - 1, query));
 }
 
@@ -1069,8 +1065,7 @@
 
   const char kQueryName[] = "\003www\006google\004test";
   DnsQuery query(
-      /*id=*/581, base::as_bytes(base::make_span(kQueryName)),
-      dns_protocol::kTypeA);
+      /*id=*/581, base::as_byte_span(kQueryName), dns_protocol::kTypeA);
   EXPECT_FALSE(resp.InitParse(sizeof(kResponse) - 1, query));
 }
 
@@ -1151,8 +1146,7 @@
 
   const char kQueryName[] = "\003www\006google\004test";
   DnsQuery query(
-      /*id=*/581, base::as_bytes(base::make_span(kQueryName)),
-      dns_protocol::kTypeA);
+      /*id=*/581, base::as_byte_span(kQueryName), dns_protocol::kTypeA);
 
   ASSERT_TRUE(resp2.InitParse(sizeof(kResponse) - 1, query));
   DnsRecordParser parser2 = resp2.Parser();
@@ -1705,8 +1699,7 @@
 TEST(DnsResponseWriteTest, CreateEmptyNoDataResponse) {
   DnsResponse response = DnsResponse::CreateEmptyNoDataResponse(
       /*id=*/4,
-      /*is_authoritative=*/true,
-      base::as_bytes(base::make_span("\x04name\x04test\x00")),
+      /*is_authoritative=*/true, base::as_byte_span("\x04name\x04test\x00"),
       dns_protocol::kTypeA);
 
   EXPECT_TRUE(response.IsValid());
diff --git a/net/http/http_auth_ntlm_mechanism.cc b/net/http/http_auth_ntlm_mechanism.cc
index e783e1c..13dee5e 100644
--- a/net/http/http_auth_ntlm_mechanism.cc
+++ b/net/http/http_auth_ntlm_mechanism.cc
@@ -155,7 +155,7 @@
   auto next_token = ntlm_client_.GenerateAuthenticateMessage(
       domain, user, credentials->password(), hostname, channel_bindings, spn,
       g_get_ms_time_proc(), client_challenge,
-      base::as_bytes(base::make_span(challenge_token_)));
+      base::as_byte_span(challenge_token_));
 
   return SetAuthTokenFromBinaryToken(auth_token, next_token);
 }
diff --git a/net/http/http_stream_factory_job.cc b/net/http/http_stream_factory_job.cc
index c03da88..8a0c0055 100644
--- a/net/http/http_stream_factory_job.cc
+++ b/net/http/http_stream_factory_job.cc
@@ -789,12 +789,14 @@
     server_ssl_config_.renego_allowed_for_protos.push_back(kProtoHTTP11);
   }
 
-  SSLConfig base_proxy_ssl_config;
+  // TODO(mmenke): This should be set in ClientSocketFactory. There's a bit of a
+  // problem, though. This is overwritten some ways down with "{kProtoHTTP11}"
+  // in the WebSocket case, and ProxyResolvingClientSocket leaves it empty, so
+  // there are three different values of this we want.
   server_ssl_config_.alpn_protos = session_->GetAlpnProtos();
-  base_proxy_ssl_config.alpn_protos = session_->GetAlpnProtos();
   server_ssl_config_.application_settings = session_->GetApplicationSettings();
-  base_proxy_ssl_config.application_settings =
-      session_->GetApplicationSettings();
+
+  SSLConfig base_proxy_ssl_config;
 
   // TODO(https://crbug.com/964642): Also enable 0-RTT for TLS proxies.
   server_ssl_config_.early_data_enabled = session_->params().enable_early_data;
@@ -870,6 +872,7 @@
         url::SchemeHostPort(request_info_.url),
         request_info_.network_anonymization_key, &server_ssl_config_);
   }
+
   if (job_type_ == PRECONNECT) {
     DCHECK(!is_websocket_);
     DCHECK(request_info_.socket_tag == SocketTag());
diff --git a/net/ntlm/ntlm_buffer_writer.cc b/net/ntlm/ntlm_buffer_writer.cc
index edb31c6..94d255e 100644
--- a/net/ntlm/ntlm_buffer_writer.cc
+++ b/net/ntlm/ntlm_buffer_writer.cc
@@ -104,7 +104,7 @@
 }
 
 bool NtlmBufferWriter::WriteUtf8String(const std::string& str) {
-  return WriteBytes(base::as_bytes(base::make_span(str)));
+  return WriteBytes(base::as_byte_span(str));
 }
 
 bool NtlmBufferWriter::WriteUtf16AsUtf8String(const std::u16string& str) {
diff --git a/net/quic/crypto/proof_source_chromium.cc b/net/quic/crypto/proof_source_chromium.cc
index ab4e2670..5c45ee1a 100644
--- a/net/quic/crypto/proof_source_chromium.cc
+++ b/net/quic/crypto/proof_source_chromium.cc
@@ -32,7 +32,7 @@
   }
 
   certs_in_file_ = X509Certificate::CreateCertificateListFromBytes(
-      base::as_bytes(base::make_span(cert_data)), X509Certificate::FORMAT_AUTO);
+      base::as_byte_span(cert_data), X509Certificate::FORMAT_AUTO);
 
   if (certs_in_file_.empty()) {
     DLOG(FATAL) << "No certificates.";
diff --git a/net/quic/crypto/proof_verifier_chromium.cc b/net/quic/crypto/proof_verifier_chromium.cc
index 7eb14c7..70508bf1 100644
--- a/net/quic/crypto/proof_verifier_chromium.cc
+++ b/net/quic/crypto/proof_verifier_chromium.cc
@@ -475,18 +475,17 @@
 
   crypto::SignatureVerifier verifier;
   if (!x509_util::SignatureVerifierInitWithCertificate(
-          &verifier, algorithm, base::as_bytes(base::make_span(signature)),
+          &verifier, algorithm, base::as_byte_span(signature),
           cert_->cert_buffer())) {
     DLOG(WARNING) << "SignatureVerifierInitWithCertificate failed";
     return false;
   }
 
-  verifier.VerifyUpdate(
-      base::as_bytes(base::make_span(quic::kProofSignatureLabel)));
+  verifier.VerifyUpdate(base::as_byte_span(quic::kProofSignatureLabel));
   uint32_t len = chlo_hash.length();
   verifier.VerifyUpdate(base::as_bytes(base::make_span(&len, 1u)));
-  verifier.VerifyUpdate(base::as_bytes(base::make_span(chlo_hash)));
-  verifier.VerifyUpdate(base::as_bytes(base::make_span(signed_data)));
+  verifier.VerifyUpdate(base::as_byte_span(chlo_hash));
+  verifier.VerifyUpdate(base::as_byte_span(signed_data));
 
   if (!verifier.VerifyFinal()) {
     DLOG(WARNING) << "VerifyFinal failed";
diff --git a/net/quic/quic_event_logger.cc b/net/quic/quic_event_logger.cc
index 4ff3849..400b919 100644
--- a/net/quic/quic_event_logger.cc
+++ b/net/quic/quic_event_logger.cc
@@ -799,8 +799,7 @@
     std::string_view client_hello) {
   net_log_.AddEvent(NetLogEventType::SSL_ENCRYPTED_CLIENT_HELLO, [&] {
     return base::Value::Dict().Set(
-        "bytes",
-        NetLogBinaryValue(base::as_bytes(base::make_span(client_hello))));
+        "bytes", NetLogBinaryValue(base::as_byte_span(client_hello)));
   });
 }
 
diff --git a/net/socket/connect_job_factory.cc b/net/socket/connect_job_factory.cc
index 3311bfe..71222de 100644
--- a/net/socket/connect_job_factory.cc
+++ b/net/socket/connect_job_factory.cc
@@ -85,20 +85,20 @@
                                         NextProtoToString);
 }
 
-void MaybeForceHttp11(const ProxyServer& proxy_server,
-                      const CommonConnectJobParams* common_connect_job_params,
-                      const NetworkAnonymizationKey& network_anonymization_key,
-                      SSLConfig* proxy_server_ssl_config) {
-  HttpServerProperties* http_server_properties =
-      common_connect_job_params->http_server_properties;
-  if (http_server_properties) {
-    if (proxy_server.is_https()) {
-      http_server_properties->MaybeForceHTTP11(
-          url::SchemeHostPort(url::kHttpsScheme,
-                              proxy_server.host_port_pair().host(),
-                              proxy_server.host_port_pair().port()),
-          network_anonymization_key, proxy_server_ssl_config);
-    }
+// Populates `ssl_config.alpn_protos` and `ssl_config.application_settings`,
+// copying them from `common_connect_job_params`. If HTTP/1.1 is required, sets
+// `alpn_protos` to only allow HTTP/1.1 negotiation.
+void ConfigureAlpn(
+    const url::SchemeHostPort& server,
+    const net::NetworkAnonymizationKey& network_anonymization_key,
+    const CommonConnectJobParams& common_connect_job_params,
+    SSLConfig& ssl_config) {
+  ssl_config.alpn_protos = *common_connect_job_params.alpn_protos;
+  ssl_config.application_settings =
+      *common_connect_job_params.application_settings;
+  if (common_connect_job_params.http_server_properties) {
+    common_connect_job_params.http_server_properties->MaybeForceHTTP11(
+        server, network_anonymization_key, &ssl_config);
   }
 }
 
@@ -216,8 +216,11 @@
         //
         proxy_server_ssl_config.disable_cert_verification_network_fetches =
             true;
-        MaybeForceHttp11(proxy_server, common_connect_job_params,
-                         network_anonymization_key, &proxy_server_ssl_config);
+        ConfigureAlpn(url::SchemeHostPort(url::kHttpsScheme,
+                                          proxy_server.host_port_pair().host(),
+                                          proxy_server.host_port_pair().port()),
+                      network_anonymization_key, *common_connect_job_params,
+                      proxy_server_ssl_config);
       }
 
       scoped_refptr<TransportSocketParams> proxy_tcp_params;
diff --git a/net/socket/connect_job_factory_unittest.cc b/net/socket/connect_job_factory_unittest.cc
index ac03d47..f01ca7b 100644
--- a/net/socket/connect_job_factory_unittest.cc
+++ b/net/socket/connect_job_factory_unittest.cc
@@ -169,6 +169,8 @@
            transport_job_factory_->params().size();
   }
 
+  const NextProtoVector alpn_protos_{kProtoHTTP2, kProtoHTTP11};
+  const SSLConfig::ApplicationSettings application_settings_{{kProtoHTTP2, {}}};
   const CommonConnectJobParams common_connect_job_params_{
       /*client_socket_factory=*/nullptr,
       /*host_resolver=*/nullptr,
@@ -185,8 +187,8 @@
       /*net_log=*/nullptr,
       /*websocket_endpoint_lock_manager=*/nullptr,
       /*http_server_properties=*/nullptr,
-      /*alpn_protos=*/nullptr,
-      /*application_settings=*/nullptr,
+      &alpn_protos_,
+      &application_settings_,
       /*ignore_certificate_errors=*/nullptr};
   TestConnectJobDelegate delegate_;
 
diff --git a/net/ssl/client_cert_identity_unittest.cc b/net/ssl/client_cert_identity_unittest.cc
index fa836b5..92a8ee206 100644
--- a/net/ssl/client_cert_identity_unittest.cc
+++ b/net/ssl/client_cert_identity_unittest.cc
@@ -27,8 +27,7 @@
   ASSERT_TRUE(x509_util::CreateSelfSignedCert(
       key->key(), x509_util::DIGEST_SHA256, "CN=expired", 1,
       base::Time::UnixEpoch(), base::Time::UnixEpoch(), {}, &der_cert));
-  cert = X509Certificate::CreateFromBytes(
-      base::as_bytes(base::make_span(der_cert)));
+  cert = X509Certificate::CreateFromBytes(base::as_byte_span(der_cert));
   ASSERT_TRUE(cert);
   certs.push_back(std::make_unique<FakeClientCertIdentity>(cert, nullptr));
 
@@ -37,24 +36,21 @@
   ASSERT_TRUE(x509_util::CreateSelfSignedCert(
       key->key(), x509_util::DIGEST_SHA256, "CN=not yet valid", 2,
       now + base::Days(10), now + base::Days(15), {}, &der_cert));
-  cert = X509Certificate::CreateFromBytes(
-      base::as_bytes(base::make_span(der_cert)));
+  cert = X509Certificate::CreateFromBytes(base::as_byte_span(der_cert));
   ASSERT_TRUE(cert);
   certs.push_back(std::make_unique<FakeClientCertIdentity>(cert, nullptr));
 
   ASSERT_TRUE(x509_util::CreateSelfSignedCert(
       key->key(), x509_util::DIGEST_SHA256, "CN=older cert", 3,
       now - base::Days(5), now + base::Days(5), {}, &der_cert));
-  cert = X509Certificate::CreateFromBytes(
-      base::as_bytes(base::make_span(der_cert)));
+  cert = X509Certificate::CreateFromBytes(base::as_byte_span(der_cert));
   ASSERT_TRUE(cert);
   certs.push_back(std::make_unique<FakeClientCertIdentity>(cert, nullptr));
 
   ASSERT_TRUE(x509_util::CreateSelfSignedCert(
       key->key(), x509_util::DIGEST_SHA256, "CN=newer cert", 2,
       now - base::Days(3), now + base::Days(5), {}, &der_cert));
-  cert = X509Certificate::CreateFromBytes(
-      base::as_bytes(base::make_span(der_cert)));
+  cert = X509Certificate::CreateFromBytes(base::as_byte_span(der_cert));
   ASSERT_TRUE(cert);
   certs.push_back(std::make_unique<FakeClientCertIdentity>(cert, nullptr));
 
diff --git a/net/ssl/client_cert_store_nss_unittest.cc b/net/ssl/client_cert_store_nss_unittest.cc
index b53b11e4..be6b619 100644
--- a/net/ssl/client_cert_store_nss_unittest.cc
+++ b/net/ssl/client_cert_store_nss_unittest.cc
@@ -194,8 +194,8 @@
   std::string cert_der(pem_tokenizer.data());
   ASSERT_FALSE(pem_tokenizer.GetNext());
 
-  ScopedCERTCertificate cert(x509_util::CreateCERTCertificateFromBytes(
-      base::as_bytes(base::make_span(cert_der))));
+  ScopedCERTCertificate cert(
+      x509_util::CreateCERTCertificateFromBytes(base::as_byte_span(cert_der)));
   ASSERT_TRUE(cert);
 
   ASSERT_TRUE(ImportClientCertToSlot(cert.get(), test_db.slot()));
diff --git a/net/test/cert_builder.cc b/net/test/cert_builder.cc
index 899f1b2f..3d1b1ec 100644
--- a/net/test/cert_builder.cc
+++ b/net/test/cert_builder.cc
@@ -1409,8 +1409,7 @@
       SignData(*signature_algorithm, tbs_cert, issuer_->GetKey(), &signature));
 
   auto cert_der = FinishCBB(cbb.get());
-  cert_ =
-      x509_util::CreateCryptoBuffer(base::as_bytes(base::make_span(cert_der)));
+  cert_ = x509_util::CreateCryptoBuffer(base::as_byte_span(cert_der));
 }
 
 }  // namespace net
diff --git a/net/test/cert_test_util.cc b/net/test/cert_test_util.cc
index 820602f5..d90ad83 100644
--- a/net/test/cert_test_util.cc
+++ b/net/test/cert_test_util.cc
@@ -24,7 +24,7 @@
   if (!base::ReadFileToString(cert_path, &cert_data))
     return CertificateList();
   return X509Certificate::CreateCertificateListFromBytes(
-      base::as_bytes(base::make_span(cert_data)), format);
+      base::as_byte_span(cert_data), format);
 }
 
 ::testing::AssertionResult LoadCertificateFiles(
@@ -71,8 +71,7 @@
 
   CertificateList certs_in_file =
       X509Certificate::CreateCertificateListFromBytes(
-          base::as_bytes(base::make_span(cert_data)),
-          X509Certificate::FORMAT_AUTO);
+          base::as_byte_span(cert_data), X509Certificate::FORMAT_AUTO);
   if (certs_in_file.empty())
     return nullptr;
   return certs_in_file[0];
diff --git a/net/test/cert_test_util_nss.cc b/net/test/cert_test_util_nss.cc
index 50b217a..8cd70f5 100644
--- a/net/test/cert_test_util_nss.cc
+++ b/net/test/cert_test_util_nss.cc
@@ -114,9 +114,9 @@
   }
 
   crypto::ScopedSECKEYPrivateKey private_key(
-      crypto::ImportNSSKeyFromPrivateKeyInfo(
-          slot, base::as_bytes(base::make_span(key_pkcs8)),
-          /*permanent=*/true));
+      crypto::ImportNSSKeyFromPrivateKeyInfo(slot,
+                                             base::as_byte_span(key_pkcs8),
+                                             /*permanent=*/true));
   LOG_IF(ERROR, !private_key)
       << "Could not create key from file " << key_path.value();
   return !!private_key;
diff --git a/net/test/spawned_test_server/base_test_server.cc b/net/test/spawned_test_server/base_test_server.cc
index 96e5764..bf79e254 100644
--- a/net/test/spawned_test_server/base_test_server.cc
+++ b/net/test/spawned_test_server/base_test_server.cc
@@ -249,7 +249,7 @@
 
   CertificateList certs_in_file =
       X509Certificate::CreateCertificateListFromBytes(
-          base::as_bytes(base::make_span(cert_data)),
+          base::as_byte_span(cert_data),
           X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
   if (certs_in_file.empty())
     return nullptr;
diff --git a/net/tools/cert_verify_tool/cert_verify_tool_util.cc b/net/tools/cert_verify_tool/cert_verify_tool_util.cc
index def96d0..02aa3b5 100644
--- a/net/tools/cert_verify_tool/cert_verify_tool_util.cc
+++ b/net/tools/cert_verify_tool/cert_verify_tool_util.cc
@@ -45,7 +45,7 @@
 
   std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> pkcs7_cert_buffers;
   if (net::x509_util::CreateCertBuffersFromPKCS7Bytes(
-          base::as_bytes(base::make_span(data_string)), &pkcs7_cert_buffers)) {
+          base::as_byte_span(data_string), &pkcs7_cert_buffers)) {
     int n = 0;
     for (const auto& cert_buffer : pkcs7_cert_buffers) {
       CertInput cert;
diff --git a/sandbox/policy/mojom/BUILD.gn b/sandbox/policy/mojom/BUILD.gn
index 836f5b2..2265876 100644
--- a/sandbox/policy/mojom/BUILD.gn
+++ b/sandbox/policy/mojom/BUILD.gn
@@ -21,7 +21,7 @@
   # bindings in JavaScript rather than TypeScript to support this legacy code.
   if (is_chromeos_ash) {
     webui_module_path = "/"
-    use_typescript_sources = false
+    generate_webui_js_bindings = true
   }
 
   enabled_features = []
diff --git a/services/device/public/cpp/device_feature_map.cc b/services/device/public/cpp/device_feature_map.cc
index 04a4882..f619128 100644
--- a/services/device/public/cpp/device_feature_map.cc
+++ b/services/device/public/cpp/device_feature_map.cc
@@ -20,6 +20,7 @@
 const base::Feature* const kFeaturesExposedToJava[] = {
     &device::kWebAuthnAndroidCredMan,
     &device::kWebAuthnAndroidCredManForHybrid,
+    &device::kWebAuthnAndroidFidoJson,
     &device::kWebAuthnAndroidHybridClientUi,
     &device::kWebAuthnAndroidIncognitoConfirmation,
     &device::kWebAuthnCableViaCredMan,
diff --git a/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java b/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java
index 70679a0f..b7566ae7 100644
--- a/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java
+++ b/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java
@@ -18,6 +18,7 @@
     public static final String WEBAUTHN_ANDROID_CRED_MAN = "WebAuthenticationAndroidCredMan";
     public static final String WEBAUTHN_ANDROID_CRED_MAN_FOR_HYBRID =
             "WebAuthenticationAndroidCredManForHybrid";
+    public static final String WEBAUTHN_ANDROID_FIDO_JSON = "WebAuthenticationAndroidFidoJson";
     public static final String WEBAUTHN_ANDROID_HYBRID_CLIENT_UI =
             "WebAuthenticationAndroidHybridClientUi";
     public static final String WEBAUTHN_ANDROID_INCOGNITO_CONFIRMATION =
diff --git a/services/device/public/mojom/BUILD.gn b/services/device/public/mojom/BUILD.gn
index 7ed4926e..1468b8ed 100644
--- a/services/device/public/mojom/BUILD.gn
+++ b/services/device/public/mojom/BUILD.gn
@@ -233,7 +233,6 @@
 mojom("geolocation_internals") {
   sources = [ "geolocation_internals.mojom" ]
   webui_module_path = "/"
-  use_typescript_sources = true
   public_deps = [
     ":geoposition",
     "//mojo/public/mojom/base",
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index 9f32ef0d..e853ff8 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -7257,8 +7257,6 @@
   ResourceRequest my_request;
   my_request.trust_token_params =
       OptionalTrustTokenParams(mojom::TrustTokenParams::New());
-  my_request.trust_token_params->version =
-      mojom::TrustTokenMajorVersion::kPrivateStateTokenV1;
   my_request.trust_token_params->operation =
       mojom::TrustTokenOperationType::kRedemption;
 
@@ -7319,8 +7317,6 @@
   ResourceRequest my_request;
   my_request.trust_token_params =
       OptionalTrustTokenParams(mojom::TrustTokenParams::New());
-  my_request.trust_token_params->version =
-      mojom::TrustTokenMajorVersion::kPrivateStateTokenV1;
   my_request.trust_token_params->operation =
       mojom::TrustTokenOperationType::kIssuance;
 
@@ -7747,8 +7743,6 @@
   my_request.url = test_server.GetURL("a.test", "/empty.html");
   my_request.trust_token_params =
       OptionalTrustTokenParams(mojom::TrustTokenParams::New());
-  my_request.trust_token_params->version =
-      mojom::TrustTokenMajorVersion::kPrivateStateTokenV1;
   my_request.trust_token_params->operation =
       mojom::TrustTokenOperationType::kIssuance;
 
@@ -7827,8 +7821,6 @@
   my_request.url = test_server.GetURL("a.test", "/empty.html");
   my_request.trust_token_params =
       OptionalTrustTokenParams(mojom::TrustTokenParams::New());
-  my_request.trust_token_params->version =
-      mojom::TrustTokenMajorVersion::kPrivateStateTokenV1;
   my_request.trust_token_params->operation =
       mojom::TrustTokenOperationType::kIssuance;
 
diff --git a/services/network/proxy_resolving_client_socket_factory.cc b/services/network/proxy_resolving_client_socket_factory.cc
index 3fd1584a..a68a856 100644
--- a/services/network/proxy_resolving_client_socket_factory.cc
+++ b/services/network/proxy_resolving_client_socket_factory.cc
@@ -52,9 +52,17 @@
         reference_params->testing_fixed_http_port;
     session_params.testing_fixed_https_port =
         reference_params->testing_fixed_https_port;
-    session_params.enable_http2 = reference_params->enable_http2;
-    session_params.enable_http2_alternative_service =
-        reference_params->enable_http2_alternative_service;
+
+    // Disable H2 negotiation via ALPN.
+    //
+    // TODO(https://crbug.com/1505550): Should this be allowed for proxies, but
+    // not for direct connections?
+    session_params.enable_http2 = false;
+
+    // Disable H2 alternative service as well. It's not supported for proxies,
+    // unlike ALPN, so no concerns with completely disabling it.
+    session_params.enable_http2_alternative_service = false;
+
     // Note that ProxyResolvingClientSocket does not use QUIC, so enabling QUIC
     // won't do anything here.
   }
diff --git a/services/network/proxy_resolving_client_socket_unittest.cc b/services/network/proxy_resolving_client_socket_unittest.cc
index b8fa684..090bada 100644
--- a/services/network/proxy_resolving_client_socket_unittest.cc
+++ b/services/network/proxy_resolving_client_socket_unittest.cc
@@ -160,6 +160,10 @@
 // creates a ProxyResolvingClientSocket instead of using the factory class,
 // because it uses SpdySessionDependencies to create a NetworkSession configured
 // to test H2.
+//
+// TODO(https://crbug.com/1505550): SPDY isn't currently supported, even through
+// proxies, by ProxyResolvingClientSocket. Change that or switch to using an H1
+// proxy.
 TEST_P(ProxyResolvingClientSocketTest, NetworkIsolationKeyWithH2Proxy) {
   // Don't bother running this test in the SSL case - it's complicated enough
   // without it, and testing HTTPS on top of H2 provides minimal value, since
@@ -363,7 +367,9 @@
   }
 }
 
-// Tests that the connection is established to the proxy.
+// Tests that the connection is established to the proxy. Also verifies that
+// the proxy SSL connection will only negotiate H1 via ALPN, and SSL connections
+// to the server receive no ALPN data.
 TEST_P(ProxyResolvingClientSocketTest, ConnectToProxy) {
   const GURL kDestination("https://example.com:443");
   // Use a different port than that of |kDestination|.
@@ -375,24 +381,45 @@
     if (is_direct) {
       pac_result = "DIRECT";
     } else {
-      pac_result = base::StringPrintf("PROXY myproxy.com:%d", kProxyPort);
+      pac_result = base::StringPrintf("HTTPS myproxy.com:%d", kProxyPort);
     }
     auto context = CreateBuilder(pac_result)->Build();
+
+    // Use same StaticSocketDataProvider for the direct and proxy cases. In the
+    // direct case, the data won't actually be read/written.
     net::MockRead reads[] = {net::MockRead("HTTP/1.1 200 Success\r\n\r\n")};
     net::MockWrite writes[] = {
         net::MockWrite("CONNECT example.com:443 HTTP/1.1\r\n"
                        "Host: example.com:443\r\n"
                        "Proxy-Connection: keep-alive\r\n\r\n")};
-    net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
-    mock_client_socket_factory_.AddSSLSocketDataProvider(&ssl_socket);
-
-    net::StaticSocketDataProvider socket_data(reads, writes);
     net::IPEndPoint remote_addr(net::IPAddress(127, 0, 0, 1),
                                 is_direct ? kDirectPort : kProxyPort);
+    net::StaticSocketDataProvider socket_data(reads, writes);
     socket_data.set_connect_data(
         net::MockConnect(net::ASYNC, net::OK, remote_addr));
     mock_client_socket_factory_.AddSocketDataProvider(&socket_data);
 
+    // SSL data for the proxy case.
+    net::SSLSocketDataProvider proxy_ssl_data(net::ASYNC, net::OK);
+    // Only H1 be allowed for the proxy.
+    //
+    // TODO(https://crbug.com/1505550): Investigate changing that.
+    proxy_ssl_data.next_protos_expected_in_ssl_config =
+        net::NextProtoVector{net::kProtoHTTP11};
+
+    if (!is_direct) {
+      mock_client_socket_factory_.AddSSLSocketDataProvider(&proxy_ssl_data);
+    }
+
+    // If using TLS to talk to the server, set up the SSL data for that. ALPN
+    // should not be enabled for the destination server at all, as this may not
+    // even be an HTTP connection.
+    net::SSLSocketDataProvider server_ssl_data(net::ASYNC, net::OK);
+    server_ssl_data.next_protos_expected_in_ssl_config = net::NextProtoVector();
+    if (use_tls_) {
+      mock_client_socket_factory_.AddSSLSocketDataProvider(&server_ssl_data);
+    }
+
     ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
         context.get());
     std::unique_ptr<ProxyResolvingClientSocket> socket =
@@ -413,7 +440,12 @@
     }
     EXPECT_EQ(net::OK, status);
     EXPECT_EQ(remote_addr.ToString(), actual_remote_addr.ToString());
-    EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
+    if (!is_direct) {
+      EXPECT_TRUE(proxy_ssl_data.ConnectDataConsumed());
+    }
+    if (use_tls_) {
+      EXPECT_TRUE(server_ssl_data.ConnectDataConsumed());
+    }
   }
 }
 
diff --git a/services/network/public/cpp/optional_trust_token_params_unittest.cc b/services/network/public/cpp/optional_trust_token_params_unittest.cc
index 637f0dd..6165b55 100644
--- a/services/network/public/cpp/optional_trust_token_params_unittest.cc
+++ b/services/network/public/cpp/optional_trust_token_params_unittest.cc
@@ -25,7 +25,6 @@
 // changes.
 OptionalTrustTokenParams NonemptyTrustTokenParams() {
   return mojom::TrustTokenParams(
-      mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
       mojom::TrustTokenOperationType::kRedemption,
       mojom::TrustTokenRefreshPolicy::kRefresh, "custom_key_commitment",
       url::Origin::Create(GURL("https://custom-issuer.com")),
@@ -81,11 +80,6 @@
 
 TEST(OptionalTrustTokenParams, Dereference) {
   OptionalTrustTokenParams in = NonemptyTrustTokenParams();
-  EXPECT_EQ(in->version, mojom::TrustTokenMajorVersion::kPrivateStateTokenV1);
-  EXPECT_EQ(in.as_ptr()->version,
-            mojom::TrustTokenMajorVersion::kPrivateStateTokenV1);
-  EXPECT_EQ(in.value().version,
-            mojom::TrustTokenMajorVersion::kPrivateStateTokenV1);
   EXPECT_EQ(in->operation, mojom::TrustTokenOperationType::kRedemption);
   EXPECT_EQ(in.as_ptr()->operation,
             mojom::TrustTokenOperationType::kRedemption);
diff --git a/services/network/public/cpp/url_request_mojom_traits_unittest.cc b/services/network/public/cpp/url_request_mojom_traits_unittest.cc
index 2f53aa8..90740ec 100644
--- a/services/network/public/cpp/url_request_mojom_traits_unittest.cc
+++ b/services/network/public/cpp/url_request_mojom_traits_unittest.cc
@@ -124,8 +124,6 @@
   original.trust_token_params = network::mojom::TrustTokenParams();
   original.trust_token_params->issuers.push_back(
       url::Origin::Create(GURL("https://issuer.com")));
-  original.trust_token_params->version =
-      mojom::TrustTokenMajorVersion::kPrivateStateTokenV1;
   original.trust_token_params->operation =
       mojom::TrustTokenOperationType::kRedemption;
   original.trust_token_params->include_timestamp_header = true;
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn
index 2bd74aa1..bc29d50 100644
--- a/services/network/public/mojom/BUILD.gn
+++ b/services/network/public/mojom/BUILD.gn
@@ -29,7 +29,7 @@
 
     # Used by ash/webui/resources/common/network_health, which is still using
     # Closure Compiler, so generate WebUI bindings in JavaScript.
-    use_typescript_sources = false
+    generate_webui_js_bindings = true
   }
 
   public_deps = [ "//url/mojom:url_mojom_gurl" ]
diff --git a/services/network/public/mojom/trust_tokens.mojom b/services/network/public/mojom/trust_tokens.mojom
index c7995c8..987c3199 100644
--- a/services/network/public/mojom/trust_tokens.mojom
+++ b/services/network/public/mojom/trust_tokens.mojom
@@ -7,13 +7,6 @@
 import "url/mojom/origin.mojom";
 import "mojo/public/mojom/base/time.mojom";
 
-// Supported Trust Token versions. A new item is added to this enum when a new
-// version is available. Version resolution will be carried based on the
-// version specified in TrustTokenParams.
-enum TrustTokenMajorVersion {
-  kPrivateStateTokenV1,
-};
-
 // TrustTokenProtocolVersion enumerates the versions of Trust Token that the
 // client knows about. Different versions represent different configuration
 // flows, data structure meanings, etc and may require clearing the database
@@ -132,9 +125,6 @@
 // operation.
 struct TrustTokenParams {
   // Required.
-  TrustTokenMajorVersion version;
-
-  // Required.
   TrustTokenOperationType operation;
 
   // Required exactly when "operation" is "kRedemption"; specifies whether the
@@ -224,8 +214,6 @@
 };
 
 struct TrustTokenKeyCommitmentResult {
-  TrustTokenMajorVersion version;
-
   // |protocol_version| is the Trust Token version that this key commitment is
   // for.
   TrustTokenProtocolVersion protocol_version;
diff --git a/services/network/test/trust_token_test_util.cc b/services/network/test/trust_token_test_util.cc
index ba47f4d..d836790 100644
--- a/services/network/test/trust_token_test_util.cc
+++ b/services/network/test/trust_token_test_util.cc
@@ -65,13 +65,6 @@
   return future.Get();
 }
 
-int TrustTokenEnumToInt(mojom::TrustTokenMajorVersion version) {
-  if (version == mojom::TrustTokenMajorVersion::kPrivateStateTokenV1) {
-    return 1;
-  }
-  return 0;
-}
-
 std::string TrustTokenEnumToString(mojom::TrustTokenOperationType operation) {
   switch (operation) {
     case mojom::TrustTokenOperationType::kIssuance:
@@ -125,7 +118,7 @@
     const TrustTokenTestParameters&) = default;
 
 TrustTokenTestParameters::TrustTokenTestParameters(
-    network::mojom::TrustTokenMajorVersion version,
+    int version,
     network::mojom::TrustTokenOperationType operation,
     absl::optional<network::mojom::TrustTokenRefreshPolicy> refresh_policy,
     absl::optional<std::vector<std::string>> issuer_specs)
@@ -141,9 +134,8 @@
 
   auto parameters =
       base::Value::Dict()
-          .Set("version", TrustTokenEnumToInt(input.version))
+          .Set("version", input.version)
           .Set("operation", TrustTokenEnumToString(input.operation));
-  trust_token_params->version = input.version;
   trust_token_params->operation = input.operation;
 
   if (input.refresh_policy.has_value()) {
diff --git a/services/network/test/trust_token_test_util.h b/services/network/test/trust_token_test_util.h
index 5b06510..90388b9 100644
--- a/services/network/test/trust_token_test_util.h
+++ b/services/network/test/trust_token_test_util.h
@@ -106,7 +106,7 @@
   // TrustTokenTestParameters (when serialized, nullopt in an optional field
   // will be omitted from the parameter's value):
   TrustTokenTestParameters(
-      mojom::TrustTokenMajorVersion version,
+      int version,
       mojom::TrustTokenOperationType operation,
       absl::optional<mojom::TrustTokenRefreshPolicy> refresh_policy,
       absl::optional<std::vector<std::string>> issuer_specs);
@@ -116,7 +116,7 @@
   TrustTokenTestParameters(const TrustTokenTestParameters&);
   TrustTokenTestParameters& operator=(const TrustTokenTestParameters&);
 
-  mojom::TrustTokenMajorVersion version;
+  int version;
   mojom::TrustTokenOperationType operation;
   absl::optional<mojom::TrustTokenRefreshPolicy> refresh_policy;
   // Because static initialization of GURLs/Origins isn't allowed in tests, use
@@ -128,7 +128,6 @@
 // Serializes the value of a Trust Tokens enum parameter to its JS string
 // representation. Must be kept in sync with the corresponding IDL enum
 // definition.
-std::string TrustTokenEnumToString(mojom::TrustTokenMajorVersion version);
 std::string TrustTokenEnumToString(mojom::TrustTokenOperationType operation);
 std::string TrustTokenEnumToString(mojom::TrustTokenRefreshPolicy policy);
 std::string TrustTokenEnumToString(
@@ -166,41 +165,37 @@
 // parameters; see above for a more detailed description of the intended use.
 const TrustTokenTestParameters kIssuanceTrustTokenTestParameters[]{
     // For issuance, there are no additional parameters to specify.
-    TrustTokenTestParameters(
-        mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
-        mojom::TrustTokenOperationType::kIssuance,
-        absl::nullopt,
-        absl::nullopt)};
+    TrustTokenTestParameters(1,
+                             mojom::TrustTokenOperationType::kIssuance,
+                             absl::nullopt,
+                             absl::nullopt)};
 
 const TrustTokenTestParameters kRedemptionTrustTokenTestParameters[]{
     // For redemption, there is one free parameter, refreshPolicy, with two
     // values (and a default).
-    TrustTokenTestParameters(
-        mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
-        mojom::TrustTokenOperationType::kRedemption,
-        mojom::TrustTokenRefreshPolicy::kRefresh,
-        absl::nullopt),
-    TrustTokenTestParameters(
-        mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
-        mojom::TrustTokenOperationType::kRedemption,
-        mojom::TrustTokenRefreshPolicy::kUseCached,
-        absl::nullopt),
-    TrustTokenTestParameters(
-        mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
-        mojom::TrustTokenOperationType::kRedemption,
-        absl::nullopt,
-        absl::nullopt)};
+    TrustTokenTestParameters(1,
+                             mojom::TrustTokenOperationType::kRedemption,
+                             mojom::TrustTokenRefreshPolicy::kRefresh,
+                             absl::nullopt),
+    TrustTokenTestParameters(1,
+                             mojom::TrustTokenOperationType::kRedemption,
+                             mojom::TrustTokenRefreshPolicy::kUseCached,
+                             absl::nullopt),
+    TrustTokenTestParameters(1,
+                             mojom::TrustTokenOperationType::kRedemption,
+                             absl::nullopt,
+                             absl::nullopt)};
 
 const TrustTokenTestParameters kSigningTrustTokenTestParameters[]{
     // Signing's inputs are issuers; "issuers" must be nonempty and must only
     // contain secure origins.
     TrustTokenTestParameters(
-        mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
+        1,
         mojom::TrustTokenOperationType::kSigning,
         absl::nullopt,
         std::vector<std::string>{"https://issuer.example"}),
     TrustTokenTestParameters(
-        mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
+        1,
         mojom::TrustTokenOperationType::kSigning,
         absl::nullopt,
         std::vector<std::string>{"https://issuer.example",
diff --git a/services/on_device_model/ml/chrome_ml.cc b/services/on_device_model/ml/chrome_ml.cc
index b8f7ccc..d1d6ae85 100644
--- a/services/on_device_model/ml/chrome_ml.cc
+++ b/services/on_device_model/ml/chrome_ml.cc
@@ -39,7 +39,8 @@
     &optimization_guide::features::kOptimizationGuideOnDeviceModel,
     "on_device_model_gpu_block_list",
     // These devices are nearly always crashing or have very low performance.
-    "8086:412|8086:a16|8086:41e|8086:416|8086:402|8086:166|1414:8c"};
+    "8086:412|8086:a16|8086:41e|8086:416|8086:402|8086:166|8086:1616|1414:8c|"
+    "8086:*:*31.0.101.4824*|8086:*:*31.0.101.4676*"};
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
diff --git a/services/webnn/dml/graph_impl_test.cc b/services/webnn/dml/graph_impl_test.cc
index a222cac4..96f5ea0 100644
--- a/services/webnn/dml/graph_impl_test.cc
+++ b/services/webnn/dml/graph_impl_test.cc
@@ -640,6 +640,48 @@
                        }}}
         .Test();
   }
+  {
+    // Test throws error when scale operand is missing.
+    BatchNormalizationTester<float>{
+        .input = {.type = mojom::Operand::DataType::kFloat32,
+                  .dimensions = {2},
+                  .values = {-1, 1}},
+        .mean = {.type = mojom::Operand::DataType::kFloat32,
+                 .dimensions = {2},
+                 .values = {-1, 1}},
+        .variance = {.type = mojom::Operand::DataType::kFloat32,
+                     .dimensions = {2},
+                     .values = {1.0, 1.5}},
+        .bias = OperandInfo<float>{.type = mojom::Operand::DataType::kFloat32,
+                                   .dimensions = {2},
+                                   .values = {0, 1}},
+        .attributes = {.axis = 0},
+        .output = {.type = mojom::Operand::DataType::kFloat32,
+                   .dimensions = {2},
+                   .values = {0, 1}}}
+        .Test(BuildAndComputeExpectation::kCreateGraphFailure);
+  }
+  {
+    // Test throws error when bias operand is missing.
+    BatchNormalizationTester<float>{
+        .input = {.type = mojom::Operand::DataType::kFloat32,
+                  .dimensions = {2},
+                  .values = {-1, 1}},
+        .mean = {.type = mojom::Operand::DataType::kFloat32,
+                 .dimensions = {2},
+                 .values = {-1, 1}},
+        .variance = {.type = mojom::Operand::DataType::kFloat32,
+                     .dimensions = {2},
+                     .values = {1.0, 1.5}},
+        .scale = OperandInfo<float>{.type = mojom::Operand::DataType::kFloat32,
+                                    .dimensions = {2},
+                                    .values = {0.5, 1, 0}},
+        .attributes = {.axis = 0},
+        .output = {.type = mojom::Operand::DataType::kFloat32,
+                   .dimensions = {2},
+                   .values = {0, 1}}}
+        .Test(BuildAndComputeExpectation::kCreateGraphFailure);
+  }
 }
 
 template <typename T>
diff --git a/sql/recover_module/payload.cc b/sql/recover_module/payload.cc
index 3e5e2d40..a114b75 100644
--- a/sql/recover_module/payload.cc
+++ b/sql/recover_module/payload.cc
@@ -11,6 +11,7 @@
 #include <type_traits>
 
 #include "base/check_op.h"
+#include "base/logging.h"
 #include "sql/recover_module/btree.h"
 #include "sql/recover_module/integers.h"
 #include "sql/recover_module/pager.h"
@@ -148,7 +149,7 @@
     // The payload size is bigger than the maximum inline payload size, so it
     // must be bigger than the minimum payload size. This check verifies that
     // the subtractions below have non-negative results.
-    DCHECK_GT(payload_size, min_inline_payload_size);
+    CHECK_GT(payload_size, min_inline_payload_size);
 
     // Payload sizes are upper-bounded by the page size.
     static_assert(
@@ -176,15 +177,18 @@
       overflow_page_count_ = efficient_overflow_page_count + 1;
     }
 
-    DCHECK_LE(inline_payload_size_, max_inline_payload_size);
-    DCHECK_EQ(overflow_page_count_, (payload_size - inline_payload_size_ +
-                                     (max_overflow_payload_size_ - 1)) /
-                                        max_overflow_payload_size_)
-        << "Incorect overflow page count calculation";
+    CHECK_LE(inline_payload_size_, max_inline_payload_size);
+    if (overflow_page_count_ != (payload_size - inline_payload_size_ +
+                                 (max_overflow_payload_size_ - 1)) /
+                                    max_overflow_payload_size_) {
+      LOG(ERROR) << "Incorrect overflow page count calculation";
+      page_id_ = DatabasePageReader::kHighestInvalidPageId;
+      return false;
+    }
   }
 
-  DCHECK_LE(inline_payload_size_, payload_size);
-  DCHECK_LE(inline_payload_size_, page_size);
+  CHECK_LE(inline_payload_size_, payload_size);
+  CHECK_LE(inline_payload_size_, page_size);
 
   const int first_overflow_page_id_size =
       (overflow_page_count_ == 0) ? 0 : kPageIdSize;
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index c8ca1af1..f822b78 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -2161,7 +2161,6 @@
       {
         "autotest_name": "chromium",
         "bucket": "chromiumos-image-archive",
-        "ci_only": true,
         "cros_board": "volteer",
         "cros_img": "volteer-public/R121-15684.0.0",
         "cros_model": "voxel",
@@ -2176,7 +2175,6 @@
       {
         "autotest_name": "tast.lacros-from-gcs",
         "bucket": "chromiumos-image-archive",
-        "ci_only": true,
         "cros_board": "volteer",
         "cros_img": "volteer-public/R121-15684.0.0",
         "cros_model": "voxel",
diff --git a/testing/buildbot/chromium.dawn.json b/testing/buildbot/chromium.dawn.json
index 7738dae..ab4d059 100644
--- a/testing/buildbot/chromium.dawn.json
+++ b/testing/buildbot/chromium.dawn.json
@@ -43,9 +43,6 @@
   "Dawn Android arm DEPS Release (Pixel 4)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--gs-results-bucket=chromium-result-details",
@@ -90,15 +87,13 @@
               "name": "shard #${SHARD_INDEX} logcats"
             }
           ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "dawn_end2end_tests",
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -141,7 +136,8 @@
               "name": "shard #${SHARD_INDEX} logcats"
             }
           ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "dawn_end2end_tests",
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
@@ -271,9 +267,6 @@
   "Dawn Android arm Release (Pixel 4)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--gs-results-bucket=chromium-result-details",
@@ -318,15 +311,13 @@
               "name": "shard #${SHARD_INDEX} logcats"
             }
           ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "dawn_end2end_tests",
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -369,7 +360,8 @@
               "name": "shard #${SHARD_INDEX} logcats"
             }
           ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "dawn_end2end_tests",
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
@@ -460,9 +452,6 @@
   "Dawn Android arm64 DEPS Release (Pixel 6)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--gs-results-bucket=chromium-result-details",
@@ -507,15 +496,13 @@
               "name": "shard #${SHARD_INDEX} logcats"
             }
           ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "dawn_end2end_tests",
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -558,7 +545,8 @@
               "name": "shard #${SHARD_INDEX} logcats"
             }
           ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "dawn_end2end_tests",
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
@@ -649,9 +637,6 @@
   "Dawn Android arm64 Release (Pixel 6)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--gs-results-bucket=chromium-result-details",
@@ -696,15 +681,13 @@
               "name": "shard #${SHARD_INDEX} logcats"
             }
           ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "dawn_end2end_tests",
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -747,7 +730,8 @@
               "name": "shard #${SHARD_INDEX} logcats"
             }
           ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "dawn_end2end_tests",
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
@@ -838,9 +822,6 @@
   "Dawn Linux TSAN Release": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--no-xvfb",
@@ -868,9 +849,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--no-xvfb",
@@ -897,9 +875,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--no-xvfb",
           "--use-gpu-in-tests",
@@ -925,9 +900,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--no-xvfb",
@@ -960,9 +932,6 @@
   "Dawn Linux x64 DEPS Release (Intel UHD 630)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--no-xvfb",
@@ -990,9 +959,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--no-xvfb",
@@ -1019,9 +985,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--no-xvfb",
           "--use-gpu-in-tests",
@@ -1047,9 +1010,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--no-xvfb",
@@ -1076,9 +1036,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--no-xvfb",
@@ -1524,9 +1481,6 @@
   "Dawn Linux x64 DEPS Release (NVIDIA)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--no-xvfb",
@@ -1554,9 +1508,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--no-xvfb",
@@ -1583,9 +1534,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--no-xvfb",
           "--use-gpu-in-tests",
@@ -1611,9 +1559,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--no-xvfb",
@@ -1640,9 +1585,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--no-xvfb",
@@ -2084,9 +2026,6 @@
   "Dawn Linux x64 Release (Intel UHD 630)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--no-xvfb",
@@ -2114,9 +2053,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--no-xvfb",
@@ -2143,9 +2079,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--no-xvfb",
           "--use-gpu-in-tests",
@@ -2171,9 +2104,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--no-xvfb",
@@ -2200,9 +2130,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--no-xvfb",
@@ -2648,9 +2575,6 @@
   "Dawn Linux x64 Release (NVIDIA)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--no-xvfb",
@@ -2678,9 +2602,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--no-xvfb",
@@ -2707,9 +2628,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--no-xvfb",
           "--use-gpu-in-tests",
@@ -2735,9 +2653,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--no-xvfb",
@@ -2764,9 +2679,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--no-xvfb",
@@ -3208,9 +3120,6 @@
   "Dawn Mac arm64 DEPS Release (Apple M2)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -3241,9 +3150,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -3273,9 +3179,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -3304,9 +3207,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--use-gpu-in-tests",
@@ -3336,9 +3236,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -3789,9 +3686,6 @@
   "Dawn Mac arm64 Release (Apple M2)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -3822,9 +3716,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -3854,9 +3745,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -3885,9 +3773,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--use-gpu-in-tests",
@@ -3917,9 +3802,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -4372,9 +4254,6 @@
   "Dawn Mac x64 DEPS Release (AMD)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -4404,9 +4283,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -4435,9 +4311,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -4465,9 +4338,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--use-gpu-in-tests",
@@ -4496,9 +4366,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -4936,9 +4803,6 @@
   "Dawn Mac x64 DEPS Release (Intel)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -4966,9 +4830,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -4995,9 +4856,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -5023,9 +4881,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--use-gpu-in-tests",
@@ -5052,9 +4907,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -5538,9 +5390,6 @@
   "Dawn Mac x64 Release (AMD)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -5570,9 +5419,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -5601,9 +5447,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -5631,9 +5474,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--use-gpu-in-tests",
@@ -5662,9 +5502,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -6102,9 +5939,6 @@
   "Dawn Mac x64 Release (Intel)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -6132,9 +5966,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -6161,9 +5992,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -6189,9 +6017,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--use-gpu-in-tests",
@@ -6218,9 +6043,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -6633,9 +6455,6 @@
   "Dawn Win10 x64 ASAN Release (Intel)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -6663,9 +6482,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--disable-toggles=use_dxc",
           "--use-gpu-in-tests",
@@ -6692,9 +6508,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -6721,9 +6534,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -6749,9 +6559,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -7046,9 +6853,6 @@
   "Dawn Win10 x64 ASAN Release (NVIDIA)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -7076,9 +6880,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--disable-toggles=use_dxc",
           "--use-gpu-in-tests",
@@ -7105,9 +6906,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -7134,9 +6932,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -7162,9 +6957,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -7461,9 +7253,6 @@
   "Dawn Win10 x64 DEPS Release (Intel)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -7491,9 +7280,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--disable-toggles=use_dxc",
           "--use-gpu-in-tests",
@@ -7520,9 +7306,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--disable-toggles=use_dxc",
           "--enable-backend-validation",
@@ -7550,9 +7333,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -7579,9 +7359,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -7607,9 +7384,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--use-gpu-in-tests",
@@ -7636,9 +7410,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -8020,9 +7791,6 @@
   "Dawn Win10 x64 DEPS Release (NVIDIA)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -8050,9 +7818,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--disable-toggles=use_dxc",
           "--use-gpu-in-tests",
@@ -8079,9 +7844,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--disable-toggles=use_dxc",
           "--enable-backend-validation",
@@ -8109,9 +7871,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -8138,9 +7897,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -8166,9 +7922,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--use-gpu-in-tests",
@@ -8195,9 +7948,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -8723,9 +8473,6 @@
   "Dawn Win10 x64 Release (Intel)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -8753,9 +8500,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--disable-toggles=use_dxc",
           "--use-gpu-in-tests",
@@ -8782,9 +8526,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--disable-toggles=use_dxc",
           "--enable-backend-validation",
@@ -8812,9 +8553,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -8841,9 +8579,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -8869,9 +8604,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--use-gpu-in-tests",
@@ -8898,9 +8630,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -9282,9 +9011,6 @@
   "Dawn Win10 x64 Release (NVIDIA)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -9312,9 +9038,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--disable-toggles=use_dxc",
           "--use-gpu-in-tests",
@@ -9341,9 +9064,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--disable-toggles=use_dxc",
           "--enable-backend-validation",
@@ -9371,9 +9091,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -9400,9 +9117,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -9428,9 +9142,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--use-gpu-in-tests",
@@ -9457,9 +9168,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -9952,9 +9660,6 @@
   "Dawn Win10 x86 DEPS Release (Intel)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -9982,9 +9687,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -10011,9 +9713,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -10039,9 +9738,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--use-gpu-in-tests",
@@ -10068,9 +9764,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -10400,9 +10093,6 @@
   "Dawn Win10 x86 DEPS Release (NVIDIA)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -10430,9 +10120,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -10459,9 +10146,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -10487,9 +10171,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--use-gpu-in-tests",
@@ -10516,9 +10197,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -10923,9 +10601,6 @@
   "Dawn Win10 x86 Release (Intel)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -10953,9 +10628,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -10982,9 +10654,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -11010,9 +10679,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--use-gpu-in-tests",
@@ -11039,9 +10705,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
@@ -11371,9 +11034,6 @@
   "Dawn Win10 x86 Release (NVIDIA)": {
     "gtest_tests": [
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-implicit-device-sync",
           "--use-gpu-in-tests",
@@ -11401,9 +11061,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-toggles=skip_validation",
           "--use-gpu-in-tests",
@@ -11430,9 +11087,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-gpu-in-tests",
           "--exclusive-device-type-preference=discrete,integrated",
@@ -11458,9 +11112,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--enable-backend-validation",
           "--use-gpu-in-tests",
@@ -11487,9 +11138,6 @@
         "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
       },
       {
-        "android_swarming": {
-          "shards": 2
-        },
         "args": [
           "--use-wire",
           "--use-gpu-in-tests",
diff --git a/testing/buildbot/filters/accessibility-linux.browser_tests.filter b/testing/buildbot/filters/accessibility-linux.browser_tests.filter
index 15d2a586..c2da0ace 100644
--- a/testing/buildbot/filters/accessibility-linux.browser_tests.filter
+++ b/testing/buildbot/filters/accessibility-linux.browser_tests.filter
@@ -34,8 +34,10 @@
 -All/SubresourceFilterWebSocketBrowserTest.BlockWebSocket/1
 -All/TabSharingUIViewsBrowserTest.CloseTabInIncognitoBrowser/0
 -All/TabSharingUIViewsBrowserTest.CloseTabInIncognitoBrowser/1
+-AutofillAcrossIframesTest_DeletedFrame.DeletingFrameDuringFillDoesNotCrash
 -BackgroundFetchBrowserTest.FetchRejectedWithoutPermission
 -BackgroundPage/ExtensionPreferenceApiTest.*
+-BackForwardCachePageLoadMetricsObserverBrowserTest.CumulativeLayoutShiftAfterBackForwardCacheRestore
 -BlockedAppApiTest.OpenAppFromIframe
 -Browser/SubresourceFilterDnsAliasFilteringThrottleBrowserTest.CheckDnsAliasesFromBrowser/ActivationEnabled
 -BrowserNavigatorTest.Disposition_IncompatibleWindow_NoExisting
@@ -106,6 +108,8 @@
 -PermissionManagerBrowserTest.ServiceWorkerPermissionAfterRendererCrash
 -PermissionManagerBrowserTest.ServiceWorkerPermissionQueryIncognitoClose
 -PersistentBackground/ExtensionApiNewTabTest.Tabs/0
+-PersistentBackground/AutomationApiTestWithContextType.ImageLabels/0
+-PersistentBackground/AutomationApiTestWithContextType.TestRendererAccessibilityEnabled/0
 -PopupTrackerBrowserTest.AllowlistedPopup_HasTracker
 -PolicyTest.IncognitoEnabled
 -PrefsInternalsTest.TestPrefsAreServed
@@ -130,6 +134,7 @@
 -SafeBrowsingTriggeredPopupBlockerFencedFrameBrowserTest.ShouldTriggerPopupBlocker
 -SafeBrowsingTriggeredPopupBlockerPrerenderingBrowserTest.PopupBlockedAfterActivation
 -SafetyTipPageInfoBubbleViewBrowserTest.NoBubbleOnErrorEvenAfterVisible
+-SearchEngineChoiceJsBrowserTest.SearchEngineChoiceTest
 -ServiceWorker/CookiesApiTest.CookiesEventsSpanning/0
 -ServiceWorker/CookiesApiTest.CookiesEventsSpanning/1
 -ServiceWorker/ExtensionApiNewTabTest.Tabs/0
diff --git a/testing/buildbot/filters/accessibility-linux.interactive_ui_tests.filter b/testing/buildbot/filters/accessibility-linux.interactive_ui_tests.filter
index ba406cc8..0e56ac4 100644
--- a/testing/buildbot/filters/accessibility-linux.interactive_ui_tests.filter
+++ b/testing/buildbot/filters/accessibility-linux.interactive_ui_tests.filter
@@ -5,8 +5,11 @@
 -AutofillInteractiveTest.*
 -AutofillInteractiveTestBase.*
 -AutofillInteractiveTestDynamicForm.*
+-AutofillInteractiveFormSubmissionClearFormTest.*
+-AutofillInteractiveFormSubmissionTest.*
 -AutofillInteractiveTest/AutofillInteractiveTestShadowDom.*
 -AutofillInteractiveTest_UndoAutofill.BasicUndoAutofill
+-BookmarksFocusTest.DNDManager
 -CaptionBubbleControllerViewsTest.AccessibleTextIsSometimesFocusable
 -CookieControlsInteractiveUiTest.*
 -ExtensionsToolbarContainerUITest.ClickingOnASecondActionClosesTheFirst
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index a46890b8..505675d 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -389,9 +389,6 @@
       '--test-launcher-retry-limit=0',
       '--test-launcher-batch-limit=512',
     ],
-    'android_swarming': {
-      'shards': 2,
-    },
   },
   'disable_field_trial_config_for_earl_grey': {
     'args': [
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 51130324..e8b5529 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -2485,6 +2485,9 @@
           '--no-xvfb',
         ],
         'ci_only': True,
+        'android_swarming': {
+          'shards': 2,
+        },
       },
       'dawn_end2end_skip_validation_tests': {
         'test': 'dawn_end2end_tests',
@@ -2497,6 +2500,9 @@
         'linux_args': [
           '--no-xvfb',
         ],
+        'android_swarming': {
+          'shards': 2,
+        },
       },
       'dawn_end2end_tests': {
         'mixins': [
@@ -2505,6 +2511,9 @@
         'linux_args': [
           '--no-xvfb',
         ],
+        'android_swarming': {
+          'shards': 2,
+        },
       },
       'dawn_end2end_wire_tests': {
         'test': 'dawn_end2end_tests',
@@ -2517,6 +2526,9 @@
         'linux_args': [
           '--no-xvfb',
         ],
+        'android_swarming': {
+          'shards': 2,
+        },
       },
     },
 
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 1a8e97b..df3164a 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1635,9 +1635,6 @@
         'additional_compile_targets': [
           'chrome',
         ],
-        'mixins': [
-            'ci_only',
-        ],
         'test_suites': {
           'skylab_tests': 'lacros_skylab_tests_amd64_generic_rel',
         },
diff --git a/testing/rust_gtest_interop/rust_gtest_interop.rs b/testing/rust_gtest_interop/rust_gtest_interop.rs
index adab2b1..fa8e3da8 100644
--- a/testing/rust_gtest_interop/rust_gtest_interop.rs
+++ b/testing/rust_gtest_interop/rust_gtest_interop.rs
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+chromium::import! {
+    "//testing/rust_gtest_interop:gtest_attribute";
+}
+
 use std::pin::Pin;
 
 /// Use `prelude:::*` to get access to all macros defined in this crate.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 6221569..eff7cc9b 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -8605,9 +8605,6 @@
             ],
             "experiments": [
                 {
-                    "name": "Default"
-                },
-                {
                     "name": "Enabled",
                     "enable_features": [
                         "HitTestOpaqueness"
@@ -12005,7 +12002,6 @@
     "OmniboxPrefBasedConsentHelper": [
         {
             "platforms": [
-                "android",
                 "chromeos",
                 "chromeos_lacros",
                 "ios",
@@ -12152,22 +12148,17 @@
             ]
         }
     ],
-    "OmniboxTailSuggestIOS": [
+    "OmniboxTextAndAnimationOptimizations": [
         {
             "platforms": [
-                "ios"
+                "android"
             ],
             "experiments": [
                 {
-                    "name": "Enabled",
+                    "name": "Enabled_20230811",
                     "enable_features": [
-                        "OmniboxTailSuggest"
-                    ]
-                },
-                {
-                    "name": "Enabled_WithSuggestEmbed",
-                    "enable_features": [
-                        "OmniboxTailSuggest"
+                        "AvoidRelayoutDuringFocusAnimation",
+                        "ScrollToTLDOptimization"
                     ]
                 }
             ]
diff --git a/third_party/angle b/third_party/angle
index dea00ff..a950f00 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit dea00fff8ae1f6fadba47474826c9465568a551e
+Subproject commit a950f0057dbfc4a4924a0dcfa3970ec1e807c3fa
diff --git a/third_party/beto-core/src b/third_party/beto-core/src
index 1aeda0e..4d202da 160000
--- a/third_party/beto-core/src
+++ b/third_party/beto-core/src
@@ -1 +1 @@
-Subproject commit 1aeda0e62218efbaf48e7b8d839dc2a2d8a2bbf1
+Subproject commit 4d202dab960a0b6a6e4757ab4393945aca5a09db
diff --git a/third_party/blink/common/interest_group/interest_group.cc b/third_party/blink/common/interest_group/interest_group.cc
index f5d1647..d35f80ef 100644
--- a/third_party/blink/common/interest_group/interest_group.cc
+++ b/third_party/blink/common/interest_group/interest_group.cc
@@ -34,8 +34,9 @@
 // be cross origin, unlike other interest group URLs, but are still restricted
 // to HTTPS with no embedded credentials.
 bool IsUrlAllowedForRenderUrls(const GURL& url) {
-  if (!url.is_valid() || !url.SchemeIs(url::kHttpsScheme))
+  if (!url.is_valid() || !url.SchemeIs(url::kHttpsScheme)) {
     return false;
+  }
 
   return !url.has_username() && !url.has_password();
 }
@@ -45,8 +46,9 @@
 // checked with IsUrlAllowedForRenderUrls(), which doesn't have the same-origin
 // check, and allows references.
 bool IsUrlAllowed(const GURL& url, const InterestGroup& group) {
-  if (url::Origin::Create(url) != group.owner)
+  if (url::Origin::Create(url) != group.owner) {
     return false;
+  }
 
   return IsUrlAllowedForRenderUrls(url) && !url.has_ref();
 }
@@ -64,27 +66,33 @@
 
 InterestGroup::Ad::Ad() = default;
 
+InterestGroup::Ad::Ad(base::PassKey<content::InterestGroupStorage>,
+                      std::string render_url)
+    : render_url_(render_url) {}
 InterestGroup::Ad::Ad(
-    GURL render_url,
+    GURL render_gurl,
     absl::optional<std::string> metadata,
     absl::optional<std::string> size_group,
     absl::optional<std::string> buyer_reporting_id,
     absl::optional<std::string> buyer_and_seller_reporting_id,
     absl::optional<std::string> ad_render_id,
     absl::optional<std::vector<url::Origin>> allowed_reporting_origins)
-    : render_url(std::move(render_url)),
-      size_group(std::move(size_group)),
+    : size_group(std::move(size_group)),
       metadata(std::move(metadata)),
       buyer_reporting_id(std::move(buyer_reporting_id)),
       buyer_and_seller_reporting_id(std::move(buyer_and_seller_reporting_id)),
       ad_render_id(std::move(ad_render_id)),
-      allowed_reporting_origins(std::move(allowed_reporting_origins)) {}
+      allowed_reporting_origins(std::move(allowed_reporting_origins)) {
+  if (render_gurl.is_valid()) {
+    render_url_ = render_gurl.spec();
+  }
+}
 
 InterestGroup::Ad::~Ad() = default;
 
 size_t InterestGroup::Ad::EstimateSize() const {
   size_t size = 0u;
-  size += render_url.spec().length();
+  size += render_url_.length();
   if (size_group) {
     size += size_group->size();
   }
@@ -109,10 +117,10 @@
 }
 
 bool InterestGroup::Ad::operator==(const Ad& other) const {
-  return std::tie(render_url, size_group, metadata, buyer_reporting_id,
+  return std::tie(render_url_, size_group, metadata, buyer_reporting_id,
                   buyer_and_seller_reporting_id, ad_render_id,
                   allowed_reporting_origins) ==
-         std::tie(other.render_url, other.size_group, other.metadata,
+         std::tie(other.render_url_, other.size_group, other.metadata,
                   other.buyer_reporting_id, other.buyer_and_seller_reporting_id,
                   other.ad_render_id, other.allowed_reporting_origins);
 }
@@ -124,16 +132,19 @@
 // in blink/renderer/modules/ad_auction/. The tests for this logic are also
 // there, so they can be compared against each other.
 bool InterestGroup::IsValid() const {
-  if (owner.scheme() != url::kHttpsScheme)
+  if (owner.scheme() != url::kHttpsScheme) {
     return false;
+  }
 
-  if (!std::isfinite(priority))
+  if (!std::isfinite(priority)) {
     return false;
+  }
 
   if (seller_capabilities) {
     for (const auto& [seller_origin, flags] : *seller_capabilities) {
-      if (seller_origin.scheme() != url::kHttpsScheme)
+      if (seller_origin.scheme() != url::kHttpsScheme) {
         return false;
+      }
     }
   }
 
@@ -146,8 +157,9 @@
     return false;
   }
 
-  if (bidding_url && !IsUrlAllowed(*bidding_url, *this))
+  if (bidding_url && !IsUrlAllowed(*bidding_url, *this)) {
     return false;
+  }
 
   if (bidding_wasm_helper_url &&
       !IsUrlAllowed(*bidding_wasm_helper_url, *this)) {
@@ -159,13 +171,15 @@
   }
 
   if (trusted_bidding_signals_url) {
-    if (!IsUrlAllowed(*trusted_bidding_signals_url, *this))
+    if (!IsUrlAllowed(*trusted_bidding_signals_url, *this)) {
       return false;
+    }
 
     // `trusted_bidding_signals_url` must not have a query string, since the
     // query parameter needs to be set as part of running an auction.
-    if (trusted_bidding_signals_url->has_query())
+    if (trusted_bidding_signals_url->has_query()) {
       return false;
+    }
   }
 
   if (trusted_bidding_signals_slot_size_mode !=
@@ -182,7 +196,7 @@
 
   if (ads) {
     for (const auto& ad : ads.value()) {
-      if (!IsUrlAllowedForRenderUrls(ad.render_url)) {
+      if (!IsUrlAllowedForRenderUrls(GURL(ad.render_url()))) {
         return false;
       }
       if (ad.size_group) {
@@ -212,7 +226,7 @@
 
   if (ad_components) {
     for (const auto& ad : ad_components.value()) {
-      if (!IsUrlAllowedForRenderUrls(ad.render_url)) {
+      if (!IsUrlAllowedForRenderUrls(GURL(ad.render_url()))) {
         return false;
       }
       if (ad.size_group) {
@@ -291,10 +305,12 @@
   size += sizeof(execution_mode);
   size += sizeof(enable_bidding_signals_prioritization);
 
-  if (priority_vector)
+  if (priority_vector) {
     size += EstimateFlatMapSize(*priority_vector);
-  if (priority_signals_overrides)
+  }
+  if (priority_signals_overrides) {
     size += EstimateFlatMapSize(*priority_signals_overrides);
+  }
   if (seller_capabilities) {
     for (const auto& [seller_origin, flags] : *seller_capabilities) {
       size +=
@@ -302,29 +318,36 @@
     }
   }
   size += sizeof(decltype(all_sellers_capabilities)::EnumType);
-  if (bidding_url)
+  if (bidding_url) {
     size += bidding_url->spec().length();
-  if (bidding_wasm_helper_url)
+  }
+  if (bidding_wasm_helper_url) {
     size += bidding_wasm_helper_url->spec().length();
+  }
   if (update_url) {
     size += update_url->spec().length();
   }
-  if (trusted_bidding_signals_url)
+  if (trusted_bidding_signals_url) {
     size += trusted_bidding_signals_url->spec().length();
+  }
   if (trusted_bidding_signals_keys) {
-    for (const std::string& key : *trusted_bidding_signals_keys)
+    for (const std::string& key : *trusted_bidding_signals_keys) {
       size += key.size();
+    }
   }
   size += sizeof(trusted_bidding_signals_slot_size_mode);
-  if (user_bidding_signals)
+  if (user_bidding_signals) {
     size += user_bidding_signals->size();
+  }
   if (ads) {
-    for (const Ad& ad : *ads)
+    for (const Ad& ad : *ads) {
       size += ad.EstimateSize();
+    }
   }
   if (ad_components) {
-    for (const Ad& ad : *ad_components)
+    for (const Ad& ad : *ad_components) {
       size += ad.EstimateSize();
+    }
   }
   if (ad_sizes) {
     for (const auto& [size_name, size_obj] : *ad_sizes) {
@@ -395,12 +418,18 @@
   return KAnonKeyForAdBid(group, blink::AdDescriptor(ad_url));
 }
 
+std::string KAnonKeyForAdBid(const InterestGroup& group,
+                             const std::string& ad_url_from_gurl_spec) {
+  return KAnonKeyForAdBid(group.owner, group.bidding_url.value_or(GURL()),
+                          ad_url_from_gurl_spec);
+}
+
 std::string KAnonKeyForAdBid(const blink::InterestGroup& group,
                              const blink::AdDescriptor& ad_descriptor) {
   DCHECK(group.ads);
   DCHECK(base::Contains(
       *group.ads, ad_descriptor.url,
-      [](const blink::InterestGroup::Ad& ad) { return ad.render_url; }))
+      [](const blink::InterestGroup::Ad& ad) { return ad.render_url(); }))
       << "No such ad: " << ad_descriptor.url;
   DCHECK(group.bidding_url);
   return KAnonKeyForAdBid(group.owner, group.bidding_url.value_or(GURL()),
@@ -410,36 +439,47 @@
 std::string KAnonKeyForAdBid(const url::Origin& owner,
                              const GURL& bidding_url,
                              const GURL& ad_url) {
-  return KAnonKeyForAdBid(owner, bidding_url, blink::AdDescriptor(ad_url));
+  return KAnonKeyForAdBid(owner, bidding_url, ad_url.spec());
 }
 
 std::string KAnonKeyForAdBid(const url::Origin& owner,
                              const GURL& bidding_url,
                              const blink::AdDescriptor& ad_descriptor) {
   // TODO(crbug.com/1442242): Add size back to this check.
+  return KAnonKeyForAdBid(owner, bidding_url, ad_descriptor.url.spec());
+}
+
+std::string KAnonKeyForAdBid(const url::Origin& owner,
+                             const GURL& bidding_url,
+                             const std::string& ad_url_from_gurl_spec) {
   return base::StrCat({kKAnonKeyForAdBidPrefix, owner.GetURL().spec(), "\n",
-                       bidding_url.spec(), "\n", ad_descriptor.url.spec()});
+                       bidding_url.spec(), "\n", ad_url_from_gurl_spec});
 }
 
 std::string KAnonKeyForAdComponentBid(const GURL& ad_url) {
-  return KAnonKeyForAdComponentBid(blink::AdDescriptor(ad_url));
+  return KAnonKeyForAdComponentBid(ad_url.spec());
 }
 
 std::string KAnonKeyForAdComponentBid(
     const blink::AdDescriptor& ad_descriptor) {
   // TODO(crbug.com/1442242): Add size back to this check.
+  return KAnonKeyForAdComponentBid(ad_descriptor.url.spec());
+}
+
+std::string KAnonKeyForAdComponentBid(
+    const std::string& ad_url_from_gurl_spec) {
   return base::StrCat(
-      {kKAnonKeyForAdComponentBidPrefix, ad_descriptor.url.spec()});
+      {kKAnonKeyForAdComponentBidPrefix, ad_url_from_gurl_spec});
 }
 
 std::string KAnonKeyForAdNameReporting(const blink::InterestGroup& group,
                                        const blink::InterestGroup::Ad& ad) {
   DCHECK(group.ads);
-  DCHECK(base::Contains(*group.ads, ad)) << "No such ad: " << ad.render_url;
+  DCHECK(base::Contains(*group.ads, ad)) << "No such ad: " << ad.render_url();
   DCHECK(group.bidding_url);
   std::string middle = base::StrCat({group.owner.GetURL().spec(), "\n",
                                      group.bidding_url.value_or(GURL()).spec(),
-                                     "\n", ad.render_url.spec(), "\n"});
+                                     "\n", ad.render_url(), "\n"});
   if (ad.buyer_and_seller_reporting_id.has_value()) {
     return base::StrCat({kKAnonKeyForAdNameReportingBuyerAndSellerIdPrefix,
                          middle, *ad.buyer_and_seller_reporting_id});
diff --git a/third_party/blink/common/interest_group/interest_group_mojom_traits.cc b/third_party/blink/common/interest_group/interest_group_mojom_traits.cc
index 2bf3d59dc..99cd4753 100644
--- a/third_party/blink/common/interest_group/interest_group_mojom_traits.cc
+++ b/third_party/blink/common/interest_group/interest_group_mojom_traits.cc
@@ -13,7 +13,7 @@
     blink::mojom::InterestGroupAdDataView,
     blink::InterestGroup::Ad>::Read(blink::mojom::InterestGroupAdDataView data,
                                     blink::InterestGroup::Ad* out) {
-  if (!data.ReadRenderUrl(&out->render_url) ||
+  if (!data.ReadRenderUrl(&out->render_url_) ||
       !data.ReadSizeGroup(&out->size_group) ||
       !data.ReadBuyerReportingId(&out->buyer_reporting_id) ||
       !data.ReadBuyerAndSellerReportingId(
diff --git a/third_party/blink/public/common/fenced_frame/fenced_frame_utils.h b/third_party/blink/public/common/fenced_frame/fenced_frame_utils.h
index 41e5508..af38625f 100644
--- a/third_party/blink/public/common/fenced_frame/fenced_frame_utils.h
+++ b/third_party/blink/public/common/fenced_frame/fenced_frame_utils.h
@@ -130,6 +130,11 @@
 inline constexpr char kFencedFrameTopNavigationCommitBeaconType[] =
     "reserved.top_navigation_commit";
 
+inline constexpr const char* kFencedFrameAutomaticBeaconTypes[] = {
+    kDeprecatedFencedFrameTopNavigationBeaconType,
+    kFencedFrameTopNavigationStartBeaconType,
+    kFencedFrameTopNavigationCommitBeaconType};
+
 // Prefix of reserved event types for private aggregation API
 inline constexpr char kFencedFrameReservedPAEventPrefix[] = "reserved.";
 
diff --git a/third_party/blink/public/common/interest_group/interest_group.h b/third_party/blink/public/common/interest_group/interest_group.h
index 0b35295..cc0e4360 100644
--- a/third_party/blink/public/common/interest_group/interest_group.h
+++ b/third_party/blink/public/common/interest_group/interest_group.h
@@ -13,6 +13,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/time/time.h"
+#include "base/types/pass_key.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/common_export.h"
 #include "third_party/blink/public/common/interest_group/ad_display_size.h"
@@ -23,6 +24,9 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
+namespace content {
+class InterestGroupStorage;
+}
 namespace blink {
 
 constexpr char kKAnonKeyForAdComponentBidPrefix[] = "ComponentBid\n";
@@ -48,9 +52,14 @@
   // An advertisement to display for an interest group. Typemapped to
   // blink::mojom::InterestGroupAd.
   // https://github.com/WICG/turtledove/blob/main/FLEDGE.md#12-interest-group-attributes
-  struct BLINK_COMMON_EXPORT Ad {
+  class BLINK_COMMON_EXPORT Ad {
+   public:
     Ad();
-    Ad(GURL render_url,
+    // Must use https. This string must have been the result of GURL().spec().
+    // DO NOT set this to a value that has never passed through GURL.
+    explicit Ad(base::PassKey<content::InterestGroupStorage>,
+                std::string render_url);
+    Ad(GURL render_gurl,
        absl::optional<std::string> metadata,
        absl::optional<std::string> size_group = absl::nullopt,
        absl::optional<std::string> buyer_reporting_id = absl::nullopt,
@@ -65,8 +74,8 @@
     // in bytes.
     size_t EstimateSize() const;
 
-    // Must use https.
-    GURL render_url;
+    const std::string& render_url() const { return render_url_; }
+
     // Optional size group assigned to this Ad.
     absl::optional<std::string> size_group;
     // Opaque JSON data, passed as an object to auction worklet.
@@ -88,6 +97,11 @@
     // IsEqualForTesting() to make it easier to implement InterestGroup's
     // IsEqualForTesting().
     bool operator==(const Ad& other) const;
+
+   private:
+    std::string render_url_;
+    friend struct mojo::StructTraits<blink::mojom::InterestGroupAdDataView,
+                                     blink::InterestGroup::Ad>;
   };
 
   InterestGroup();
@@ -156,7 +170,7 @@
   absl::optional<AdditionalBidKey> additional_bid_key;
   absl::optional<url::Origin> aggregation_coordinator_origin;
 
-  static_assert(__LINE__ == 159, R"(
+  static_assert(__LINE__ == 173, R"(
 If modifying InterestGroup fields, make sure to also modify:
 
 * IsValid(), EstimateSize(), and IsEqualForTesting() in this class
@@ -222,6 +236,9 @@
                                                  const GURL& ad_url);
 std::string BLINK_COMMON_EXPORT
 KAnonKeyForAdBid(const InterestGroup& group,
+                 const std::string& ad_url_from_gurl_spec);
+std::string BLINK_COMMON_EXPORT
+KAnonKeyForAdBid(const InterestGroup& group,
                  const blink::AdDescriptor& ad_descriptor);
 std::string BLINK_COMMON_EXPORT KAnonKeyForAdBid(const url::Origin& owner,
                                                  const GURL& bidding_url,
@@ -230,6 +247,10 @@
 KAnonKeyForAdBid(const url::Origin& owner,
                  const GURL& bidding_url,
                  const blink::AdDescriptor& ad_descriptor);
+std::string BLINK_COMMON_EXPORT
+KAnonKeyForAdBid(const url::Origin& owner,
+                 const GURL& bidding_url,
+                 const std::string& ad_url_from_gurl_spec);
 
 // Calculates the k-anonymity key for an ad component that is used for
 // determining if an ad component is k-anonymous for the purposes of bidding and
@@ -239,6 +260,8 @@
 std::string BLINK_COMMON_EXPORT KAnonKeyForAdComponentBid(const GURL& ad_url);
 std::string BLINK_COMMON_EXPORT
 KAnonKeyForAdComponentBid(const blink::AdDescriptor& ad_descriptor);
+std::string BLINK_COMMON_EXPORT
+KAnonKeyForAdComponentBid(const std::string& ad_url_from_gurl_spec);
 
 // Calculates the k-anonymity key for reporting the interest group name in
 // reportWin along with the given Ad.
diff --git a/third_party/blink/public/common/interest_group/interest_group_mojom_traits.h b/third_party/blink/public/common/interest_group/interest_group_mojom_traits.h
index e24ff65..3db4d28 100644
--- a/third_party/blink/public/common/interest_group/interest_group_mojom_traits.h
+++ b/third_party/blink/public/common/interest_group/interest_group_mojom_traits.h
@@ -24,8 +24,8 @@
 template <>
 struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::InterestGroupAdDataView,
                                         blink::InterestGroup::Ad> {
-  static const GURL& render_url(const blink::InterestGroup::Ad& ad) {
-    return ad.render_url;
+  static const std::string& render_url(const blink::InterestGroup::Ad& ad) {
+    return ad.render_url_;
   }
 
   static const absl::optional<std::string>& size_group(
diff --git a/third_party/blink/public/mojom/interest_group/interest_group_types.mojom b/third_party/blink/public/mojom/interest_group/interest_group_types.mojom
index 448a9d1f..50057c3 100644
--- a/third_party/blink/public/mojom/interest_group/interest_group_types.mojom
+++ b/third_party/blink/public/mojom/interest_group/interest_group_types.mojom
@@ -16,7 +16,7 @@
 // https://github.com/WICG/turtledove/blob/main/FLEDGE.md#12-interest-group-attributes
 struct InterestGroupAd {
   // Must use https.
-  url.mojom.Url render_url;
+  string render_url;
   // Optional size groups assigned to this Ad.
   string? size_group;
   // Optional identifiers that may be passed to reporting functions, if
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
index 98bfe15..3bc8608 100644
--- a/third_party/blink/public/web/web_local_frame_client.h
+++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -677,10 +677,6 @@
   // Notifies the embedder about an accessibility event on a WebAXObject.
   virtual void PostAccessibilityEvent(const ui::AXEvent& event) {}
 
-  // Notifies tests that a WebAXObject is dirty and its state needs
-  // to be serialized again.
-  virtual void NotifyWebAXObjectMarkedDirty(const WebAXObject&) {}
-
   // Called when accessibility is ready to serialize.
   virtual void AXReadyCallback() {}
 
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 471d34b3..e497771 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
@@ -298,7 +298,6 @@
                               window);
   // Mark the handle to be traced by Oilpan, since the global proxy has a
   // reference to the DOMWindow.
-  // global_proxy_.SetWrapperClassId(wrapper_type_info->wrapper_class_id);
   CHECK(global_proxy_ == window->AssociateWithWrapper(GetIsolate(), world_,
                                                       wrapper_type_info,
                                                       global_proxy));
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 011c04d..9720787 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1485,6 +1485,7 @@
   sources += rebase_path(blink_core_tests_clipboard, "", "clipboard")
   sources +=
       rebase_path(blink_core_tests_content_capture, "", "content_capture")
+  sources += rebase_path(blink_core_tests_css, "", "css")
   sources += rebase_path(blink_core_tests_display_lock, "", "display_lock")
   sources += rebase_path(blink_core_tests_dom, "", "dom")
   sources += rebase_path(blink_core_tests_events, "", "events")
diff --git a/third_party/blink/renderer/core/css/css_gradient_value_test.cc b/third_party/blink/renderer/core/css/css_gradient_value_test.cc
index 9ad458d..0527d0f 100644
--- a/third_party/blink/renderer/core/css/css_gradient_value_test.cc
+++ b/third_party/blink/renderer/core/css/css_gradient_value_test.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/graphics/gradient.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
@@ -45,6 +46,7 @@
 }
 
 TEST(CSSGradientValueTest, RadialGradient_Equals) {
+  test::TaskEnvironment task_environment;
   // Trivially identical.
   EXPECT_TRUE(CompareGradients(
       "radial-gradient(circle closest-corner at 100px 60px, blue, red)",
@@ -83,6 +85,7 @@
 }
 
 TEST(CSSGradientValueTest, RepeatingRadialGradientNan) {
+  test::TaskEnvironment task_environment;
   std::unique_ptr<DummyPageHolder> dummy_page_holder =
       std::make_unique<DummyPageHolder>();
   Document& document = dummy_page_holder->GetDocument();
@@ -108,6 +111,7 @@
 }
 
 TEST(CSSGradientValueTest, IsUsingContainerRelativeUnits) {
+  test::TaskEnvironment task_environment;
   EXPECT_TRUE(
       IsUsingContainerRelativeUnits("linear-gradient(green 5cqw, blue 10cqh)"));
   EXPECT_TRUE(
diff --git a/third_party/blink/renderer/core/css/css_page_rule_test.cc b/third_party/blink/renderer/core/css/css_page_rule_test.cc
index 737afb7dc..3a32b9e7 100644
--- a/third_party/blink/renderer/core/css/css_page_rule_test.cc
+++ b/third_party/blink/renderer/core/css/css_page_rule_test.cc
@@ -4,15 +4,16 @@
 
 #include "third_party/blink/renderer/core/css/css_page_rule.h"
 
+#include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/css_rule_list.h"
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
 TEST(CSSPageRule, Serializing) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   const char* css_rule = "@page :left { size: auto; }";
@@ -27,6 +28,7 @@
 }
 
 TEST(CSSPageRule, selectorText) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   const char* css_rule = "@page :left { size: auto; }";
diff --git a/third_party/blink/renderer/core/css/css_selector_test.cc b/third_party/blink/renderer/core/css/css_selector_test.cc
index 5e20028..5cbfb0d 100644
--- a/third_party/blink/renderer/core/css/css_selector_test.cc
+++ b/third_party/blink/renderer/core/css/css_selector_test.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <iostream>
+
+#include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/rule_set.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-#include <iostream>
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
@@ -32,6 +32,7 @@
 }  // namespace
 
 TEST(CSSSelector, Representations) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   const char* css_rules =
@@ -77,6 +78,7 @@
 }
 
 TEST(CSSSelector, OverflowRareDataMatchNth) {
+  test::TaskEnvironment task_environment;
   int max_int = std::numeric_limits<int>::max();
   int min_int = std::numeric_limits<int>::min();
   CSSSelector selector;
@@ -99,6 +101,7 @@
 }
 
 TEST(CSSSelector, Specificity_Is) {
+  test::TaskEnvironment task_environment;
   EXPECT_EQ(Specificity(".a :is(.b, div.c)"), Specificity(".a div.c"));
   EXPECT_EQ(Specificity(".a :is(.c#d, .e)"), Specificity(".a .c#d"));
   EXPECT_EQ(Specificity(":is(.e+.f, .g>.b, .h)"), Specificity(".e+.f"));
@@ -112,6 +115,7 @@
 }
 
 TEST(CSSSelector, Specificity_Where) {
+  test::TaskEnvironment task_environment;
   EXPECT_EQ(Specificity(".a :where(.b, div.c)"), Specificity(".a"));
   EXPECT_EQ(Specificity(".a :where(.c#d, .e)"), Specificity(".a"));
   EXPECT_EQ(Specificity(":where(.e+.f, .g>.b, .h)"), Specificity("*"));
@@ -126,23 +130,27 @@
 }
 
 TEST(CSSSelector, Specificity_Slotted) {
+  test::TaskEnvironment task_environment;
   EXPECT_EQ(Specificity("::slotted(.a)"), Specificity(".a::first-line"));
   EXPECT_EQ(Specificity("::slotted(*)"), Specificity("::first-line"));
 }
 
 TEST(CSSSelector, Specificity_Host) {
+  test::TaskEnvironment task_environment;
   EXPECT_EQ(Specificity(":host"), Specificity(".host"));
   EXPECT_EQ(Specificity(":host(.a)"), Specificity(".host .a"));
   EXPECT_EQ(Specificity(":host(div#a.b)"), Specificity(".host div#a.b"));
 }
 
 TEST(CSSSelector, Specificity_HostContext) {
+  test::TaskEnvironment task_environment;
   EXPECT_EQ(Specificity(":host-context(.a)"), Specificity(".host-context .a"));
   EXPECT_EQ(Specificity(":host-context(div#a.b)"),
             Specificity(".host-context div#a.b"));
 }
 
 TEST(CSSSelector, Specificity_Not) {
+  test::TaskEnvironment task_environment;
   EXPECT_EQ(Specificity(":not(div)"), Specificity(":is(div)"));
   EXPECT_EQ(Specificity(":not(.a)"), Specificity(":is(.a)"));
   EXPECT_EQ(Specificity(":not(div.a)"), Specificity(":is(div.a)"));
@@ -156,6 +164,7 @@
 }
 
 TEST(CSSSelector, Specificity_Has) {
+  test::TaskEnvironment task_environment;
   EXPECT_EQ(Specificity(":has(div)"), Specificity("div"));
   EXPECT_EQ(Specificity(":has(div)"), Specificity("* div"));
   EXPECT_EQ(Specificity(":has(~ div)"), Specificity("* ~ div"));
@@ -172,6 +181,7 @@
 }
 
 TEST(CSSSelector, HasLinkOrVisited) {
+  test::TaskEnvironment task_environment;
   EXPECT_FALSE(HasLinkOrVisited("tag"));
   EXPECT_FALSE(HasLinkOrVisited("visited"));
   EXPECT_FALSE(HasLinkOrVisited("link"));
@@ -197,6 +207,7 @@
 }
 
 TEST(CSSSelector, CueDefaultNamespace) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(R"HTML(
@@ -218,24 +229,28 @@
 }
 
 TEST(CSSSelector, CopyInvalidList) {
+  test::TaskEnvironment task_environment;
   CSSSelectorList* list = CSSSelectorList::Empty();
   EXPECT_FALSE(list->IsValid());
   EXPECT_FALSE(list->Copy()->IsValid());
 }
 
 TEST(CSSSelector, CopyValidList) {
+  test::TaskEnvironment task_environment;
   CSSSelectorList* list = css_test_helpers::ParseSelectorList(".a");
   EXPECT_TRUE(list->IsValid());
   EXPECT_TRUE(list->Copy()->IsValid());
 }
 
 TEST(CSSSelector, FirstInInvalidList) {
+  test::TaskEnvironment task_environment;
   CSSSelectorList* list = CSSSelectorList::Empty();
   EXPECT_FALSE(list->IsValid());
   EXPECT_FALSE(list->First());
 }
 
 TEST(CSSSelector, ImplicitPseudoDescendant) {
+  test::TaskEnvironment task_environment;
   CSSSelector selector[2] = {
       CSSSelector(html_names::kDivTag,
                   /* is_implicit */ false),
@@ -246,6 +261,7 @@
 }
 
 TEST(CSSSelector, ImplicitPseudoChild) {
+  test::TaskEnvironment task_environment;
   CSSSelector selector[2] = {
       CSSSelector(html_names::kDivTag,
                   /* is_implicit */ false),
@@ -256,6 +272,7 @@
 }
 
 TEST(CSSSelector, NonImplicitPseudoChild) {
+  test::TaskEnvironment task_environment;
   CSSSelector selector[2] = {
       CSSSelector(html_names::kDivTag,
                   /* is_implicit */ false),
@@ -266,6 +283,7 @@
 }
 
 TEST(CSSSelector, PseudoTrueBefore) {
+  test::TaskEnvironment task_environment;
   CSSSelector selector[2] = {
       CSSSelector(),
       CSSSelector(AtomicString("hover"), /* is_implicit */ false)};
@@ -276,6 +294,7 @@
 }
 
 TEST(CSSSelector, PseudoTrueAfter) {
+  test::TaskEnvironment task_environment;
   CSSSelector selector[2] = {
       CSSSelector(AtomicString("hover"), /* is_implicit */ false),
       CSSSelector()};
@@ -286,6 +305,7 @@
 }
 
 TEST(CSSSelector, PseudoTrueChild) {
+  test::TaskEnvironment task_environment;
   CSSSelector selector[2] = {CSSSelector(html_names::kDivTag,
                                          /* is_implicit */ false),
                              CSSSelector()};
@@ -296,6 +316,7 @@
 }
 
 TEST(CSSSelector, PseudoTrueSpecificity) {
+  test::TaskEnvironment task_environment;
   CSSSelector selector;
   selector.SetTrue();
   selector.SetLastInComplexSelector(true);
@@ -303,6 +324,7 @@
 }
 
 TEST(CSSSelector, ImplicitScopeSpecificity) {
+  test::TaskEnvironment task_environment;
   CSSSelector selector[2] = {
       CSSSelector(html_names::kDivTag,
                   /* is_implicit */ false),
@@ -314,6 +336,7 @@
 }
 
 TEST(CSSSelector, ExplicitScopeSpecificity) {
+  test::TaskEnvironment task_environment;
   CSSSelector selector[2] = {
       CSSSelector(html_names::kDivTag,
                   /* is_implicit */ false),
diff --git a/third_party/blink/renderer/core/css/css_style_declaration_test.cc b/third_party/blink/renderer/core/css/css_style_declaration_test.cc
index af80db1..de15a77 100644
--- a/third_party/blink/renderer/core/css/css_style_declaration_test.cc
+++ b/third_party/blink/renderer/core/css/css_style_declaration_test.cc
@@ -17,10 +17,12 @@
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/platform/bindings/v8_binding.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
 TEST(CSSStyleDeclarationTest, getPropertyShorthand) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules("div { padding: var(--p); }");
@@ -34,6 +36,7 @@
 }
 
 TEST(CSSStyleDeclarationTest, ParsingRevertWithFeatureEnabled) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
   sheet.AddCSSRules("div { top: revert; --x: revert; }");
   ASSERT_TRUE(sheet.CssRules());
@@ -65,6 +68,7 @@
 //
 // See CssPropertyInfo in css_style_declaration.cc.
 TEST(CSSStyleDeclarationTest, ExposureCacheLeak) {
+  test::TaskEnvironment task_environment;
   V8TestingScope v8_testing_scope;
 
   auto* property_value_set = MakeGarbageCollected<MutableCSSPropertyValueSet>(
diff --git a/third_party/blink/renderer/core/css/cssom/inline_style_property_map_test.cc b/third_party/blink/renderer/core/css/cssom/inline_style_property_map_test.cc
index f914de3..e24b67be 100644
--- a/third_party/blink/renderer/core/css/cssom/inline_style_property_map_test.cc
+++ b/third_party/blink/renderer/core/css/cssom/inline_style_property_map_test.cc
@@ -10,10 +10,12 @@
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/style_property_shorthand.h"
 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
 TEST(InlineStylePropertyMapTest, PendingSubstitutionValueCrash) {
+  test::TaskEnvironment task_environment;
   // Test that trying to reify any longhands with a CSSPendingSubstitutionValue
   // does not cause a crash.
 
diff --git a/third_party/blink/renderer/core/css/drag_update_test.cc b/third_party/blink/renderer/core/css/drag_update_test.cc
index 4738ab3..41cd21f8 100644
--- a/third_party/blink/renderer/core/css/drag_update_test.cc
+++ b/third_party/blink/renderer/core/css/drag_update_test.cc
@@ -3,16 +3,19 @@
 // found in the LICENSE file.
 
 #include <memory>
+
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
 TEST(DragUpdateTest, AffectedByDragUpdate) {
+  test::TaskEnvironment task_environment;
   // Check that when dragging the div in the document below, you only get a
   // single element style recalc.
 
@@ -43,6 +46,7 @@
 }
 
 TEST(DragUpdateTest, ChildAffectedByDragUpdate) {
+  test::TaskEnvironment task_environment;
   // Check that when dragging the div in the document below, you get a
   // single element style recalc.
 
@@ -73,6 +77,7 @@
 }
 
 TEST(DragUpdateTest, SiblingAffectedByDragUpdate) {
+  test::TaskEnvironment task_environment;
   // Check that when dragging the div in the document below, you get a
   // single element style recalc.
 
diff --git a/third_party/blink/renderer/core/css/invalidation/invalidation_set_test.cc b/third_party/blink/renderer/core/css/invalidation/invalidation_set_test.cc
index 82d5684..39989711 100644
--- a/third_party/blink/renderer/core/css/invalidation/invalidation_set_test.cc
+++ b/third_party/blink/renderer/core/css/invalidation/invalidation_set_test.cc
@@ -3,11 +3,12 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/core/css/invalidation/invalidation_set.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 namespace {
@@ -275,6 +276,7 @@
 }
 
 TEST(InvalidationSetTest, ClassInvalidatesElement) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder =
       std::make_unique<DummyPageHolder>(gfx::Size(800, 600));
   auto& document = dummy_page_holder->GetDocument();
@@ -301,6 +303,7 @@
 }
 
 TEST(InvalidationSetTest, AttributeInvalidatesElement) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder =
       std::make_unique<DummyPageHolder>(gfx::Size(800, 600));
   auto& document = dummy_page_holder->GetDocument();
diff --git a/third_party/blink/renderer/core/css/invalidation/pending_invalidations_test.cc b/third_party/blink/renderer/core/css/invalidation/pending_invalidations_test.cc
index 7ae9b69c..95f4300a 100644
--- a/third_party/blink/renderer/core/css/invalidation/pending_invalidations_test.cc
+++ b/third_party/blink/renderer/core/css/invalidation/pending_invalidations_test.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
@@ -23,6 +24,7 @@
   }
 
  private:
+  test::TaskEnvironment task_environment_;
   std::unique_ptr<DummyPageHolder> dummy_page_holder_;
 };
 
diff --git a/third_party/blink/renderer/core/css/invalidation/style_invalidator_test.cc b/third_party/blink/renderer/core/css/invalidation/style_invalidator_test.cc
index 21213457..b044ff3 100644
--- a/third_party/blink/renderer/core/css/invalidation/style_invalidator_test.cc
+++ b/third_party/blink/renderer/core/css/invalidation/style_invalidator_test.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
@@ -22,6 +23,7 @@
   Document& GetDocument() { return dummy_page_holder_->GetDocument(); }
 
  private:
+  test::TaskEnvironment task_environment_;
   std::unique_ptr<DummyPageHolder> dummy_page_holder_;
 };
 
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
index 625cb86..903d7b28 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
@@ -27,6 +27,7 @@
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/gfx/display_color_spaces.h"
@@ -632,6 +633,7 @@
 }
 
 TEST(MediaQueryEvaluatorTest, Dynamic) {
+  test::TaskEnvironment task_environment;
   auto page_holder = std::make_unique<DummyPageHolder>(gfx::Size(500, 500));
   page_holder->GetFrameView().SetMediaType(media_type_names::kScreen);
 
@@ -651,6 +653,7 @@
 }
 
 TEST(MediaQueryEvaluatorTest, DynamicNoView) {
+  test::TaskEnvironment task_environment;
   auto page_holder = std::make_unique<DummyPageHolder>(gfx::Size(500, 500));
   LocalFrame* frame = &page_holder->GetFrame();
   page_holder.reset();
diff --git a/third_party/blink/renderer/core/css/media_query_exp_test.cc b/third_party/blink/renderer/core/css/media_query_exp_test.cc
index ad2f127..11210b4d 100644
--- a/third_party/blink/renderer/core/css/media_query_exp_test.cc
+++ b/third_party/blink/renderer/core/css/media_query_exp_test.cc
@@ -3,12 +3,13 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/core/css/media_query_exp.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
@@ -170,12 +171,14 @@
 }  // namespace
 
 TEST(MediaQueryExpTest, ValuesType) {
+  test::TaskEnvironment task_environment;
   EXPECT_TRUE(IdentValue(CSSValueID::kTop).IsId());
   EXPECT_TRUE(PxValue(10).IsNumeric());
   EXPECT_TRUE(RatioValue(0, 1).IsRatio());
 }
 
 TEST(MediaQueryExpTest, ValueEquality) {
+  test::TaskEnvironment task_environment;
   EXPECT_EQ(PxValue(10), PxValue(10));
   EXPECT_EQ(EmValue(10), EmValue(10));
   EXPECT_EQ(IdentValue(CSSValueID::kTop), IdentValue(CSSValueID::kTop));
@@ -202,6 +205,7 @@
 }
 
 TEST(MediaQueryExpTest, ComparisonEquality) {
+  test::TaskEnvironment task_environment;
   auto px1 = PxValue(10.0);
   auto px2 = PxValue(20.0);
 
@@ -212,6 +216,7 @@
 }
 
 TEST(MediaQueryExpTest, BoundaryEquality) {
+  test::TaskEnvironment task_environment;
   auto px1 = PxValue(10.0);
   auto px2 = PxValue(20.0);
 
@@ -227,6 +232,7 @@
 }
 
 TEST(MediaQueryExpTest, ExpEquality) {
+  test::TaskEnvironment task_environment;
   auto px1 = PxValue(10.0);
   auto px2 = PxValue(20.0);
 
@@ -239,6 +245,7 @@
 }
 
 TEST(MediaQueryExpTest, Serialize) {
+  test::TaskEnvironment task_environment;
   // Boolean feature:
   EXPECT_EQ("color", RightExp("color", NoCmp(InvalidValue())).Serialize());
 
@@ -275,6 +282,7 @@
 }
 
 TEST(MediaQueryExpTest, SerializeNode) {
+  test::TaskEnvironment task_environment;
   EXPECT_EQ("width < 10px",
             FeatureNode(RightExp("width", LtCmp(PxValue(10))))->Serialize());
 
@@ -338,6 +346,7 @@
 }
 
 TEST(MediaQueryExpTest, CollectExpressions) {
+  test::TaskEnvironment task_environment;
   MediaQueryExp width_lt10 = RightExp("width", LtCmp(PxValue(10)));
   MediaQueryExp height_lt10 = RightExp("height", LtCmp(PxValue(10)));
 
@@ -395,6 +404,7 @@
 }
 
 TEST(MediaQueryExpTest, UnitFlags) {
+  test::TaskEnvironment task_environment;
   // width < 10px
   EXPECT_EQ(MediaQueryExpValue::UnitFlags::kNone,
             RightExp("width", LtCmp(PxValue(10.0))).GetUnitFlags());
@@ -443,6 +453,7 @@
 }
 
 TEST(MediaQueryExpTest, UtilsNullptrHandling) {
+  test::TaskEnvironment task_environment;
   MediaQueryExp exp = RightExp("width", LtCmp(PxValue(10)));
 
   EXPECT_FALSE(MediaQueryExpNode::Nested(nullptr));
@@ -457,6 +468,7 @@
 }
 
 TEST(MediaQueryExpTest, ResolutionChecks) {
+  test::TaskEnvironment task_environment;
   EXPECT_TRUE(DppxValue(3).IsResolution());
   EXPECT_TRUE(CalcValue("<resolution>", "calc(96dpi)").IsResolution());
 
diff --git a/third_party/blink/renderer/core/css/media_query_list_test.cc b/third_party/blink/renderer/core/css/media_query_list_test.cc
index 6fc516da..d786f06 100644
--- a/third_party/blink/renderer/core/css/media_query_list_test.cc
+++ b/third_party/blink/renderer/core/css/media_query_list_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
@@ -24,6 +25,7 @@
 }  // anonymous namespace
 
 TEST(MediaQueryListTest, CrashInStop) {
+  test::TaskEnvironment task_environment;
   ScopedNullExecutionContext execution_context;
   auto* document =
       Document::CreateForTest(execution_context.GetExecutionContext());
diff --git a/third_party/blink/renderer/core/css/media_query_matcher_test.cc b/third_party/blink/renderer/core/css/media_query_matcher_test.cc
index aadcbdf..3de6734 100644
--- a/third_party/blink/renderer/core/css/media_query_matcher_test.cc
+++ b/third_party/blink/renderer/core/css/media_query_matcher_test.cc
@@ -5,14 +5,17 @@
 #include "third_party/blink/renderer/core/css/media_query_matcher.h"
 
 #include <memory>
+
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/media_list.h"
 #include "third_party/blink/renderer/core/media_type_names.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
 TEST(MediaQueryMatcherTest, LostFrame) {
+  test::TaskEnvironment task_environment;
   auto page_holder = std::make_unique<DummyPageHolder>(gfx::Size(500, 500));
   auto* matcher =
       MakeGarbageCollected<MediaQueryMatcher>(page_holder->GetDocument());
diff --git a/third_party/blink/renderer/core/css/parser/css_lazy_parsing_test.cc b/third_party/blink/renderer/core/css/parser/css_lazy_parsing_test.cc
index f238680..d185423 100644
--- a/third_party/blink/renderer/core/css/parser/css_lazy_parsing_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_lazy_parsing_test.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
@@ -31,6 +32,7 @@
   }
 
  protected:
+  test::TaskEnvironment task_environment_;
   Persistent<StyleSheetContents> cached_contents_;
 };
 
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl_test.cc b/third_party/blink/renderer/core/css/parser/css_parser_impl_test.cc
index 957ad96..48123314 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl_test.cc
@@ -20,6 +20,7 @@
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
@@ -56,6 +57,7 @@
 };
 
 TEST(CSSParserImplTest, AtImportOffsets) {
+  test::TaskEnvironment task_environment;
   String sheet_text = "@import 'test.css';";
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
@@ -72,6 +74,7 @@
 }
 
 TEST(CSSParserImplTest, AtMediaOffsets) {
+  test::TaskEnvironment task_environment;
   String sheet_text = "@media screen { }";
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
@@ -88,6 +91,7 @@
 }
 
 TEST(CSSParserImplTest, AtSupportsOffsets) {
+  test::TaskEnvironment task_environment;
   String sheet_text = "@supports (display:none) { }";
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
@@ -105,6 +109,7 @@
 }
 
 TEST(CSSParserImplTest, AtFontFaceOffsets) {
+  test::TaskEnvironment task_environment;
   String sheet_text = "@font-face { }";
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
@@ -122,6 +127,7 @@
 }
 
 TEST(CSSParserImplTest, AtKeyframesOffsets) {
+  test::TaskEnvironment task_environment;
   String sheet_text = "@keyframes test { }";
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
@@ -139,6 +145,7 @@
 }
 
 TEST(CSSParserImplTest, AtPageOffsets) {
+  test::TaskEnvironment task_environment;
   String sheet_text = "@page :first { }";
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
@@ -155,6 +162,7 @@
 }
 
 TEST(CSSParserImplTest, AtPropertyOffsets) {
+  test::TaskEnvironment task_environment;
   String sheet_text = "@property --test { syntax: '*'; inherits: false }";
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
@@ -172,6 +180,7 @@
 }
 
 TEST(CSSParserImplTest, AtCounterStyleOffsets) {
+  test::TaskEnvironment task_environment;
   String sheet_text = "@counter-style test { }";
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
@@ -189,6 +198,7 @@
 }
 
 TEST(CSSParserImplTest, AtContainerOffsets) {
+  test::TaskEnvironment task_environment;
   String sheet_text = "@container (max-width: 100px) { }";
 
   auto* context = MakeGarbageCollected<CSSParserContext>(
@@ -207,6 +217,7 @@
 }
 
 TEST(CSSParserImplTest, DirectNesting) {
+  test::TaskEnvironment task_environment;
   String sheet_text =
       ".element { color: green; &.other { color: red; margin-left: 10px; }}";
 
@@ -232,6 +243,7 @@
 }
 
 TEST(CSSParserImplTest, RuleNotStartingWithAmpersand) {
+  test::TaskEnvironment task_environment;
   String sheet_text = ".element { color: green;  .outer & { color: red; }}";
 
   auto* context = MakeGarbageCollected<CSSParserContext>(
@@ -257,6 +269,7 @@
 }
 
 TEST(CSSParserImplTest, ImplicitDescendantSelector) {
+  test::TaskEnvironment task_environment;
   String sheet_text = ".element { color: green; .outer { color: red; }}";
 
   auto* context = MakeGarbageCollected<CSSParserContext>(
@@ -282,6 +295,7 @@
 }
 
 TEST(CSSParserImplTest, NestedRelativeSelector) {
+  test::TaskEnvironment task_environment;
   String sheet_text = ".element { color: green; > .inner { color: red; }}";
 
   auto* context = MakeGarbageCollected<CSSParserContext>(
@@ -307,6 +321,7 @@
 }
 
 TEST(CSSParserImplTest, NestingAtTopLevelIsLegalThoughIsMatchesNothing) {
+  test::TaskEnvironment task_environment;
   String sheet_text = "&.element { color: orchid; }";
 
   auto* context = MakeGarbageCollected<CSSParserContext>(
@@ -323,6 +338,7 @@
 }
 
 TEST(CSSParserImplTest, ErrorRecoveryEatsOnlyFirstDeclaration) {
+  test::TaskEnvironment task_environment;
   // Note the colon after the opening bracket.
   String sheet_text = R"CSS(
     .element {:
@@ -347,6 +363,7 @@
 }
 
 TEST(CSSParserImplTest, NestedEmptySelectorCrash) {
+  test::TaskEnvironment task_environment;
   String sheet_text = "y{ :is() {} }";
 
   auto* context = MakeGarbageCollected<CSSParserContext>(
@@ -360,6 +377,7 @@
 }
 
 TEST(CSSParserImplTest, NestedRulesInsideMediaQueries) {
+  test::TaskEnvironment task_environment;
   String sheet_text = R"CSS(
     .element {
       color: green;
@@ -407,6 +425,7 @@
 }
 
 TEST(CSSParserImplTest, ObserveNestedMediaQuery) {
+  test::TaskEnvironment task_environment;
   String sheet_text = R"CSS(
     .element {
       color: green;
@@ -431,6 +450,7 @@
 }
 
 TEST(CSSParserImplTest, ObserveNestedLayer) {
+  test::TaskEnvironment task_environment;
   String sheet_text = R"CSS(
     .element {
       color: green;
@@ -455,6 +475,7 @@
 }
 
 TEST(CSSParserImplTest, NestedIdent) {
+  test::TaskEnvironment task_environment;
   ScopedCSSNestingIdentForTest enabled(true);
 
   String sheet_text = "div { p:hover { } }";
@@ -471,6 +492,7 @@
 }
 
 TEST(CSSParserImplTest, RemoveImportantAnnotationIfPresent) {
+  test::TaskEnvironment task_environment;
   struct TestCase {
     String input;
     String expected_text;
@@ -504,6 +526,7 @@
 }
 
 TEST(CSSParserImplTest, InvalidLayerRules) {
+  test::TaskEnvironment task_environment;
   using css_test_helpers::ParseRule;
   ScopedNullExecutionContext execution_context;
   Document* document =
@@ -530,6 +553,7 @@
 }
 
 TEST(CSSParserImplTest, ValidLayerBlockRule) {
+  test::TaskEnvironment task_environment;
   using css_test_helpers::ParseRule;
   ScopedNullExecutionContext execution_context;
   Document* document =
@@ -565,6 +589,7 @@
 }
 
 TEST(CSSParserImplTest, ValidLayerStatementRule) {
+  test::TaskEnvironment task_environment;
   using css_test_helpers::ParseRule;
   ScopedNullExecutionContext execution_context;
   Document* document =
@@ -607,6 +632,7 @@
 }
 
 TEST(CSSParserImplTest, NestedLayerRules) {
+  test::TaskEnvironment task_environment;
   using css_test_helpers::ParseRule;
   ScopedNullExecutionContext execution_context;
   Document* document =
@@ -670,6 +696,7 @@
 }
 
 TEST(CSSParserImplTest, LayeredImportRules) {
+  test::TaskEnvironment task_environment;
   using css_test_helpers::ParseRule;
   ScopedNullExecutionContext execution_context;
   Document* document =
@@ -705,6 +732,7 @@
 }
 
 TEST(CSSParserImplTest, LayeredImportRulesInvalid) {
+  test::TaskEnvironment task_environment;
   using css_test_helpers::ParseRule;
   ScopedNullExecutionContext execution_context;
   Document* document =
@@ -736,6 +764,7 @@
 }
 
 TEST(CSSParserImplTest, ImportRulesWithSupports) {
+  test::TaskEnvironment task_environment;
   using css_test_helpers::ParseRule;
   ScopedNullExecutionContext execution_context;
   Document* document =
@@ -791,6 +820,7 @@
 }
 
 TEST(CSSParserImplTest, LayeredImportRulesMultipleLayers) {
+  test::TaskEnvironment task_environment;
   using css_test_helpers::ParseRule;
   ScopedNullExecutionContext execution_context;
   Document* document =
@@ -831,6 +861,7 @@
 }
 
 TEST(CSSParserImplTest, CorrectAtRuleOrderingWithLayers) {
+  test::TaskEnvironment task_environment;
   String sheet_text = R"CSS(
     @layer foo;
     @import url(bar.css) layer(bar);
@@ -851,6 +882,7 @@
 }
 
 TEST(CSSParserImplTest, EmptyLayerStatementsAtWrongPositions) {
+  test::TaskEnvironment task_environment;
   {
     // @layer interleaving with @import rules
     String sheet_text = R"CSS(
@@ -896,6 +928,7 @@
 }
 
 TEST(CSSParserImplTest, EmptyLayerStatementAfterRegularRule) {
+  test::TaskEnvironment task_environment;
   // Empty @layer statements after regular rules are parsed as regular rules.
 
   String sheet_text = R"CSS(
@@ -914,6 +947,7 @@
 }
 
 TEST(CSSParserImplTest, FontPaletteValuesDisabled) {
+  test::TaskEnvironment task_environment;
   // @font-palette-values rules should be ignored when the feature is disabled.
 
   using css_test_helpers::ParseRule;
@@ -927,6 +961,7 @@
 }
 
 TEST(CSSParserImplTest, FontPaletteValuesBasicRuleParsing) {
+  test::TaskEnvironment task_environment;
   using css_test_helpers::ParseRule;
   ScopedNullExecutionContext execution_context;
   Document* document =
@@ -948,6 +983,7 @@
 }
 
 TEST(CSSParserImplTest, FontPaletteValuesMultipleFamiliesParsing) {
+  test::TaskEnvironment task_environment;
   using css_test_helpers::ParseRule;
   ScopedNullExecutionContext execution_context;
   Document* document =
@@ -969,6 +1005,7 @@
 // families, compare:
 // https://drafts.csswg.org/css-fonts/#descdef-font-palette-values-font-family.
 TEST(CSSParserImplTest, FontPaletteValuesGenericFamiliesNotParsing) {
+  test::TaskEnvironment task_environment;
   using css_test_helpers::ParseRule;
   ScopedNullExecutionContext execution_context;
   Document* document =
@@ -987,6 +1024,7 @@
 }
 
 TEST(CSSParserImplTest, FontFeatureValuesRuleParsing) {
+  test::TaskEnvironment task_environment;
   using css_test_helpers::ParseRule;
   ScopedNullExecutionContext execution_context;
   Document* document =
@@ -1009,6 +1047,7 @@
 }
 
 TEST(CSSParserImplTest, FontFeatureValuesOffsets) {
+  test::TaskEnvironment task_environment;
   String sheet_text = "@font-feature-values myFam { @styleset { curly: 1; } }";
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kHTMLStandardMode, SecureContextMode::kInsecureContext);
@@ -1026,6 +1065,7 @@
 }
 
 TEST(CSSParserImplTest, PositionFallbackRuleMaxLength) {
+  test::TaskEnvironment task_environment;
   ScopedCSSAnchorPositioningForTest enabled(true);
 
   String sheet_text = R"CSS(
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
index 7e589ff..06c46c15 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
@@ -21,6 +21,7 @@
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
@@ -313,6 +314,7 @@
 }
 
 TEST(CSSPropertyParserTest, ClipPathEllipse) {
+  test::TaskEnvironment task_environment;
   auto dummy_holder = std::make_unique<DummyPageHolder>(gfx::Size(500, 500));
   Document* doc = &dummy_holder->GetDocument();
   Page::InsertOrdinaryPageForTesting(&dummy_holder->GetPage());
@@ -333,6 +335,7 @@
 }
 
 TEST(CSSPropertyParserTest, GradientUseCount) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder =
       std::make_unique<DummyPageHolder>(gfx::Size(800, 600));
   Document& document = dummy_page_holder->GetDocument();
@@ -345,6 +348,7 @@
 }
 
 TEST(CSSPropertyParserTest, PaintUseCount) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder =
       std::make_unique<DummyPageHolder>(gfx::Size(800, 600));
   dummy_page_holder->GetFrame().Loader().CommitNavigation(
@@ -361,6 +365,7 @@
 }
 
 TEST(CSSPropertyParserTest, CrossFadeUseCount) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder =
       std::make_unique<DummyPageHolder>(gfx::Size(800, 600));
   Document& document = dummy_page_holder->GetDocument();
@@ -374,6 +379,7 @@
 }
 
 TEST(CSSPropertyParserTest, TwoValueOverflowOverlayCount) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder =
       std::make_unique<DummyPageHolder>(gfx::Size(800, 600));
   Document& document = dummy_page_holder->GetDocument();
@@ -390,6 +396,7 @@
 }
 
 TEST(CSSPropertyParserTest, OneValueOverflowOverlayCount) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder =
       std::make_unique<DummyPageHolder>(gfx::Size(800, 600));
   Document& document = dummy_page_holder->GetDocument();
@@ -406,6 +413,7 @@
 }
 
 TEST(CSSPropertyParserTest, OverflowXOverlayCount) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder =
       std::make_unique<DummyPageHolder>(gfx::Size(800, 600));
   Document& document = dummy_page_holder->GetDocument();
@@ -422,6 +430,7 @@
 }
 
 TEST(CSSPropertyParserTest, OverflowYOverlayCount) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder =
       std::make_unique<DummyPageHolder>(gfx::Size(800, 600));
   Document& document = dummy_page_holder->GetDocument();
@@ -438,6 +447,7 @@
 }
 
 TEST(CSSPropertyParserTest, OverflowFirstValueOverlayCount) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder =
       std::make_unique<DummyPageHolder>(gfx::Size(800, 600));
   Document& document = dummy_page_holder->GetDocument();
@@ -454,6 +464,7 @@
 }
 
 TEST(CSSPropertyParserTest, OverflowSecondValueOverlayCount) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder =
       std::make_unique<DummyPageHolder>(gfx::Size(800, 600));
   Document& document = dummy_page_holder->GetDocument();
@@ -470,6 +481,7 @@
 }
 
 TEST(CSSPropertyParserTest, DropFontfaceDescriptor) {
+  test::TaskEnvironment task_environment;
   EXPECT_FALSE(
       IsValidPropertyValueForStyleRule(CSSPropertyID::kSrc, "url(blah)"));
   EXPECT_FALSE(
@@ -502,6 +514,7 @@
   Document& GetDocument() { return dummy_page_holder_->GetDocument(); }
 
  private:
+  test::TaskEnvironment task_environment_;
   std::unique_ptr<DummyPageHolder> dummy_page_holder_;
 };
 
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
index 2f13131f..e1c0776 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
@@ -48,6 +49,7 @@
 }
 
 TEST(CSSSelectorParserTest, ValidANPlusB) {
+  test::TaskEnvironment task_environment;
   ANPlusBTestCase test_cases[] = {
       {"odd", 2, 1},
       {"OdD", 2, 1},
@@ -121,6 +123,7 @@
 }
 
 TEST(CSSSelectorParserTest, InvalidANPlusB) {
+  test::TaskEnvironment task_environment;
   // Some of these have token range prefixes which are valid <an+b> and could
   // in theory be valid in consumeANPlusB, but this behaviour isn't needed
   // anywhere and not implemented.
@@ -142,6 +145,7 @@
 }
 
 TEST(CSSSelectorParserTest, PseudoElementsInCompoundLists) {
+  test::TaskEnvironment task_environment;
   const char* test_cases[] = {":not(::before)",
                               ":not(::content)",
                               ":host(::before)",
@@ -167,6 +171,7 @@
 }
 
 TEST(CSSSelectorParserTest, ValidSimpleAfterPseudoElementInCompound) {
+  test::TaskEnvironment task_environment;
   const char* test_cases[] = {"::-webkit-volume-slider:hover",
                               "::selection:window-inactive",
                               "::-webkit-scrollbar:disabled",
@@ -191,6 +196,7 @@
 }
 
 TEST(CSSSelectorParserTest, InvalidSimpleAfterPseudoElementInCompound) {
+  test::TaskEnvironment task_environment;
   const char* test_cases[] = {
       "::before#id",
       "::after:hover",
@@ -227,6 +233,7 @@
 }
 
 TEST(CSSSelectorParserTest, TransitionPseudoStyles) {
+  test::TaskEnvironment task_environment;
   struct TestCase {
     const char* selector;
     bool valid;
@@ -286,6 +293,7 @@
 }
 
 TEST(CSSSelectorParserTest, WorkaroundForInvalidCustomPseudoInUAStyle) {
+  test::TaskEnvironment task_environment;
   // See crbug.com/578131
   const char* test_cases[] = {
       "video::-webkit-media-text-track-region-container.scrolling",
@@ -307,6 +315,7 @@
 }
 
 TEST(CSSSelectorParserTest, InvalidPseudoElementInNonRightmostCompound) {
+  test::TaskEnvironment task_environment;
   const char* test_cases[] = {"::-webkit-volume-slider *", "::before *",
                               "::-webkit-scrollbar *", "::cue *",
                               "::selection *"};
@@ -327,6 +336,7 @@
 }
 
 TEST(CSSSelectorParserTest, UnresolvedNamespacePrefix) {
+  test::TaskEnvironment task_environment;
   const char* test_cases[] = {"ns|div", "div ns|div", "div ns|div "};
 
   auto* context = MakeGarbageCollected<CSSParserContext>(
@@ -347,6 +357,7 @@
 }
 
 TEST(CSSSelectorParserTest, UnexpectedPipe) {
+  test::TaskEnvironment task_environment;
   const char* test_cases[] = {"div | .c", "| div", " | div"};
 
   auto* context = MakeGarbageCollected<CSSParserContext>(
@@ -367,6 +378,7 @@
 }
 
 TEST(CSSSelectorParserTest, SerializedUniversal) {
+  test::TaskEnvironment task_environment;
   const char* test_cases[][2] = {
       {"*::-webkit-volume-slider", "::-webkit-volume-slider"},
       {"*::cue(i)", "::cue(i)"},
@@ -401,6 +413,7 @@
 }
 
 TEST(CSSSelectorParserTest, AttributeSelectorUniversalInvalid) {
+  test::TaskEnvironment task_environment;
   const char* test_cases[] = {"[*]", "[*|*]"};
 
   auto* context = MakeGarbageCollected<CSSParserContext>(
@@ -422,6 +435,7 @@
 }
 
 TEST(CSSSelectorParserTest, InternalPseudo) {
+  test::TaskEnvironment task_environment;
   const char* test_cases[] = {"::-internal-whatever",
                               "::-internal-media-controls-text-track-list",
                               ":-internal-is-html",
@@ -626,6 +640,7 @@
 }  // namespace
 
 TEST(CSSSelectorParserTest, ASCIILowerHTMLStrict) {
+  test::TaskEnvironment task_environment;
   const ASCIILowerTestCase test_cases[] = {
       {"\\212a bd", u"\u212abd", TagLocalName},
       {"[\\212alass]", u"\u212alass", AttributeLocalName},
@@ -656,6 +671,7 @@
 }
 
 TEST(CSSSelectorParserTest, ASCIILowerHTMLQuirks) {
+  test::TaskEnvironment task_environment;
   const ASCIILowerTestCase test_cases[] = {
       {"\\212a bd", u"\u212abd", TagLocalName},
       {"[\\212alass]", u"\u212alass", AttributeLocalName},
@@ -686,6 +702,7 @@
 }
 
 TEST(CSSSelectorParserTest, ShadowPartPseudoElementValid) {
+  test::TaskEnvironment task_environment;
   const char* test_cases[] = {"::part(ident)", "host::part(ident)",
                               "host::part(ident):hover"};
 
@@ -707,6 +724,7 @@
 }
 
 TEST(CSSSelectorParserTest, ShadowPartAndBeforeAfterPseudoElementValid) {
+  test::TaskEnvironment task_environment;
   const char* test_cases[] = {
       "::part(ident)::before",       "::part(ident)::after",
       "::part(ident)::placeholder",  "::part(ident)::first-line",
@@ -756,6 +774,7 @@
 }
 
 TEST(CSSSelectorParserTest, UseCountShadowPseudo) {
+  test::TaskEnvironment task_environment;
   auto ExpectCount = [](const char* selector, WebFeature feature) {
     SCOPED_TRACE(selector);
     EXPECT_TRUE(IsCounted(selector, kHTMLStandardMode, feature));
@@ -875,6 +894,7 @@
 }
 
 TEST(CSSSelectorParserTest, IsWhereUseCount) {
+  test::TaskEnvironment task_environment;
   const auto is_feature = WebFeature::kCSSSelectorPseudoIs;
   EXPECT_FALSE(IsCounted(".a", kHTMLStandardMode, is_feature));
   EXPECT_FALSE(IsCounted(":not(.a)", kHTMLStandardMode, is_feature));
@@ -899,6 +919,7 @@
 }
 
 TEST(CSSSelectorParserTest, ImplicitShadowCrossingCombinators) {
+  test::TaskEnvironment task_environment;
   struct ShadowCombinatorTest {
     const char* input;
     Vector<std::pair<AtomicString, CSSSelector::RelationType>> expectation;
@@ -974,6 +995,7 @@
 }
 
 TEST(CSSSelectorParserTest, WebKitScrollbarPseudoParsing) {
+  test::TaskEnvironment task_environment;
   const char* test_cases[] = {"::-webkit-resizer",
                               "::-webkit-scrollbar",
                               "::-webkit-scrollbar-button",
@@ -1137,6 +1159,7 @@
 }
 
 TEST(CSSSelectorParserTest, NestingTypeImpliedDescendant) {
+  test::TaskEnvironment task_environment;
   // Nesting selector (&)
   EXPECT_EQ(CSSSelector::kPseudoParent,
             GetImplicitlyAddedPseudo(".foo", CSSNestingType::kNesting));
@@ -1282,6 +1305,7 @@
                          testing::ValuesIn(scope_activation_data));
 
 TEST_P(ScopeActivationTest, All) {
+  test::TaskEnvironment task_environment;
   ScopeActivationData param = GetParam();
   SCOPED_TRACE(param.inner_rule);
 
@@ -1355,6 +1379,7 @@
 }
 
 TEST(CSSSelectorParserTest, CountMatchesSelfTest) {
+  test::TaskEnvironment task_environment;
   auto is_focus = [](const CSSSelector& selector) {
     return selector.GetPseudoType() == CSSSelector::kPseudoFocus;
   };
@@ -1416,7 +1441,10 @@
 };
 
 class ScopeActivationCountTest
-    : public ::testing::TestWithParam<ScopeActivationCountData> {};
+    : public ::testing::TestWithParam<ScopeActivationCountData> {
+ private:
+  test::TaskEnvironment task_environment_;
+};
 
 INSTANTIATE_TEST_SUITE_P(CSSSelectorParserTest,
                          ScopeActivationCountTest,
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils_test.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils_test.cc
index 64d2bebf..c4b8241 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils_test.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/html/html_html_element.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 namespace {
@@ -28,6 +29,7 @@
 }
 
 TEST(CSSParsingUtilsTest, BasicShapeUseCount) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder =
       std::make_unique<DummyPageHolder>(gfx::Size(800, 600));
   Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
diff --git a/third_party/blink/renderer/core/css/resolver/font_builder_test.cc b/third_party/blink/renderer/core/css/resolver/font_builder_test.cc
index e002e91..1f253a3 100644
--- a/third_party/blink/renderer/core/css/resolver/font_builder_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/font_builder_test.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
@@ -27,6 +28,7 @@
   Settings& GetSettings() { return *GetDocument().GetSettings(); }
 
  private:
+  test::TaskEnvironment task_environment_;
   std::unique_ptr<DummyPageHolder> dummy_;
 };
 
diff --git a/third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope_test.cc b/third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope_test.cc
index a59c427..da3a1b0 100644
--- a/third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope_test.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
@@ -23,6 +24,7 @@
   Document& GetDocument() { return dummy_page_holder_->GetDocument(); }
 
  private:
+  test::TaskEnvironment task_environment_;
   std::unique_ptr<DummyPageHolder> dummy_page_holder_;
 };
 
diff --git a/third_party/blink/renderer/core/css/rule_feature_set_test.cc b/third_party/blink/renderer/core/css/rule_feature_set_test.cc
index 6bb0cbfa..624d21f 100644
--- a/third_party/blink/renderer/core/css/rule_feature_set_test.cc
+++ b/third_party/blink/renderer/core/css/rule_feature_set_test.cc
@@ -23,6 +23,7 @@
 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 using testing::AssertionFailure;
@@ -698,6 +699,7 @@
   }
 
  protected:
+  test::TaskEnvironment task_environment_;
   ScopedNullExecutionContext execution_context_;
 
  private:
diff --git a/third_party/blink/renderer/core/css/rule_set_test.cc b/third_party/blink/renderer/core/css/rule_set_test.cc
index 4878ea1..5aa440b 100644
--- a/third_party/blink/renderer/core/css/rule_set_test.cc
+++ b/third_party/blink/renderer/core/css/rule_set_test.cc
@@ -62,6 +62,7 @@
 }  // namespace
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_CustomPseudoElements) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules("summary::-webkit-details-marker { }");
@@ -73,6 +74,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_Id) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules("#id { }");
@@ -84,6 +86,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_NthChild) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules("div:nth-child(2) { }");
@@ -95,6 +98,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_ClassThenId) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(".class#id { }");
@@ -108,6 +112,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_IdThenClass) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules("#id.class { }");
@@ -119,6 +124,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_AttrThenId) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules("[attr]#id { }");
@@ -131,6 +137,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_TagThenAttrThenId) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules("div[attr]#id { }");
@@ -143,6 +150,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_TagThenAttr) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules("div[attr] { }");
@@ -154,6 +162,7 @@
 // It's arbitrary which of these we choose, but it needs to match
 // the behavior in IsCoveredByBucketing().
 TEST(RuleSetTest, findBestRuleSetAndAdd_ThreeClasses) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(".a.b.c { }");
@@ -164,6 +173,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_AttrThenClass) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules("[attr].class { }");
@@ -173,6 +183,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_Host) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(":host { }");
@@ -182,6 +193,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_HostWithId) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(":host(#x) { }");
@@ -191,6 +203,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_HostContext) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(":host-context(*) { }");
@@ -200,6 +213,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_HostContextWithId) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(":host-context(#x) { }");
@@ -209,6 +223,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_HostAndHostContextNotInRightmost) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(":host-context(#x) .y, :host(.a) > #b  { }");
@@ -223,6 +238,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_HostAndClass) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(".foo:host { }");
@@ -232,6 +248,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_HostContextAndClass) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(".foo:host-context(*) { }");
@@ -241,6 +258,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_Focus) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(":focus { }");
@@ -251,6 +269,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_LinkVisited) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(":link { }");
@@ -267,6 +286,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_Cue) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules("::cue(b) { }");
@@ -277,6 +297,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_PlaceholderPseudo) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules("::placeholder { }");
@@ -288,6 +309,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_PartPseudoElements) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules("::part(dummy):focus, #id::part(dummy) { }");
@@ -297,6 +319,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_IsSingleArg) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(":is(.a) { }");
@@ -307,6 +330,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_WhereSingleArg) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(":where(.a) { }");
@@ -317,6 +341,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_WhereSingleArgNested) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(":where(:is(.a)) { }");
@@ -327,6 +352,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_IsMultiArg) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(":is(.a, .b) { }");
@@ -336,6 +362,7 @@
 }
 
 TEST(RuleSetTest, findBestRuleSetAndAdd_WhereMultiArg) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(":where(.a, .b) { }");
@@ -356,6 +383,7 @@
 }
 
 TEST(RuleSetTest, LargeNumberOfAttributeRules) {
+  test::TaskEnvironment task_environment;
   base::test::ScopedFeatureList feature_list;
   css_test_helpers::TestStyleSheet sheet;
   AddManyAttributeRules(feature_list, sheet);
@@ -387,6 +415,7 @@
 }
 
 TEST(RuleSetTest, LargeNumberOfAttributeRulesWithEmpty) {
+  test::TaskEnvironment task_environment;
   base::test::ScopedFeatureList feature_list;
   css_test_helpers::TestStyleSheet sheet;
   AddManyAttributeRules(feature_list, sheet);
@@ -403,6 +432,7 @@
 }
 
 TEST(RuleSetTest, LargeNumberOfAttributeRulesWithCatchAll) {
+  test::TaskEnvironment task_environment;
   base::test::ScopedFeatureList feature_list;
   css_test_helpers::TestStyleSheet sheet;
   AddManyAttributeRules(feature_list, sheet);
@@ -421,6 +451,7 @@
 }
 
 TEST(RuleSetTest, LargeNumberOfAttributeRulesWithCatchAll2) {
+  test::TaskEnvironment task_environment;
   base::test::ScopedFeatureList feature_list;
   css_test_helpers::TestStyleSheet sheet;
   AddManyAttributeRules(feature_list, sheet);
@@ -479,6 +510,7 @@
 }
 
 TEST(RuleSetTest, IsCoveredByBucketing) {
+  test::TaskEnvironment task_environment;
   // Base cases.
   EXPECT_THAT(CoveredByBucketing(".c"), ElementsAreArray({true}));
   EXPECT_THAT(CoveredByBucketing("#id.c"), ElementsAreArray({true, false}));
@@ -529,6 +561,7 @@
 }
 
 TEST(RuleSetTest, VisitedDependentRuleCount) {
+  test::TaskEnvironment task_environment;
   EXPECT_EQ(2u, RuleCount(":link"));
   EXPECT_EQ(2u, RuleCount(":visited"));
   // Not visited-dependent:
@@ -539,6 +572,7 @@
 #endif  // DCHECK_IS_ON()
 
 TEST(RuleSetTest, SelectorIndexLimit) {
+  test::TaskEnvironment task_environment;
   // It's not feasible to run this test for a large number of bits. If the
   // number of bits have increased to a large number, consider removing this
   // test and making do with RuleSetTest.RuleDataSelectorIndexLimit.
@@ -567,6 +601,7 @@
 }
 
 TEST(RuleSetTest, RuleDataPositionLimit) {
+  test::TaskEnvironment task_environment;
   StyleRule* rule = CreateDummyStyleRule();
   AddRuleFlags flags = kRuleHasNoSpecialState;
   const unsigned selector_index = 0;
@@ -583,6 +618,7 @@
 }
 
 TEST(RuleSetTest, RuleCountNotIncreasedByInvalidRuleData) {
+  test::TaskEnvironment task_environment;
   auto* rule_set = MakeGarbageCollected<RuleSet>();
   EXPECT_EQ(0u, rule_set->RuleCount());
 
@@ -602,6 +638,7 @@
 }
 
 TEST(RuleSetTest, NoStyleScope) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules("#b {}");
@@ -612,6 +649,7 @@
 }
 
 TEST(RuleSetTest, StyleScope) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules("@scope (.a) { #b {} }");
@@ -622,6 +660,7 @@
 }
 
 TEST(RuleSetTest, NestedStyleScope) {
+  test::TaskEnvironment task_environment;
   css_test_helpers::TestStyleSheet sheet;
 
   sheet.AddCSSRules(R"CSS(
@@ -660,6 +699,7 @@
 }
 
 TEST(RuleSetTest, SingleScope) {
+  test::TaskEnvironment task_environment;
   {
     css_test_helpers::TestStyleSheet sheet;
     sheet.AddCSSRules(R"CSS(
diff --git a/third_party/blink/renderer/core/css/selector_query_test.cc b/third_party/blink/renderer/core/css/selector_query_test.cc
index 2c6b3ae..d953d70a 100644
--- a/third_party/blink/renderer/core/css/selector_query_test.cc
+++ b/third_party/blink/renderer/core/css/selector_query_test.cc
@@ -18,6 +18,7 @@
 #include "third_party/blink/renderer/core/html/html_html_element.h"
 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 // Uncomment to run the SelectorQueryTests for stats in a release build.
 // #define RELEASE_QUERY_STATS
@@ -66,6 +67,7 @@
 }  // namespace
 
 TEST(SelectorQueryTest, NotMatchingPseudoElement) {
+  test::TaskEnvironment task_environment;
   ScopedNullExecutionContext execution_context;
   auto* document =
       Document::CreateForTest(execution_context.GetExecutionContext());
@@ -98,6 +100,7 @@
 }
 
 TEST(SelectorQueryTest, LastOfTypeNotFinishedParsing) {
+  test::TaskEnvironment task_environment;
   ScopedNullExecutionContext execution_context;
   auto* document =
       HTMLDocument::CreateForTest(execution_context.GetExecutionContext());
@@ -123,6 +126,7 @@
 }
 
 TEST(SelectorQueryTest, StandardsModeFastPaths) {
+  test::TaskEnvironment task_environment;
   ScopedNullExecutionContext execution_context;
   auto* document =
       HTMLDocument::CreateForTest(execution_context.GetExecutionContext());
@@ -229,6 +233,7 @@
 }
 
 TEST(SelectorQueryTest, FastPathScoped) {
+  test::TaskEnvironment task_environment;
   ScopedNullExecutionContext execution_context;
   auto* document =
       HTMLDocument::CreateForTest(execution_context.GetExecutionContext());
@@ -297,6 +302,7 @@
 }
 
 TEST(SelectorQueryTest, QuirksModeSlowPath) {
+  test::TaskEnvironment task_environment;
   ScopedNullExecutionContext execution_context;
   auto* document =
       HTMLDocument::CreateForTest(execution_context.GetExecutionContext());
@@ -335,6 +341,7 @@
 }
 
 TEST(SelectorQueryTest, DisconnectedSubtree) {
+  test::TaskEnvironment task_environment;
   ScopedNullExecutionContext execution_context;
   auto* document =
       HTMLDocument::CreateForTest(execution_context.GetExecutionContext());
@@ -364,6 +371,7 @@
 }
 
 TEST(SelectorQueryTest, DisconnectedTreeScope) {
+  test::TaskEnvironment task_environment;
   ScopedNullExecutionContext execution_context;
   auto* document =
       HTMLDocument::CreateForTest(execution_context.GetExecutionContext());
@@ -395,6 +403,7 @@
 }
 
 TEST(SelectorQueryTest, QueryHasPseudoClass) {
+  test::TaskEnvironment task_environment;
   ScopedNullExecutionContext execution_context;
   auto* document =
       HTMLDocument::CreateForTest(execution_context.GetExecutionContext());
diff --git a/third_party/blink/renderer/core/css/style_element_test.cc b/third_party/blink/renderer/core/css/style_element_test.cc
index 7235e15..3f77198 100644
--- a/third_party/blink/renderer/core/css/style_element_test.cc
+++ b/third_party/blink/renderer/core/css/style_element_test.cc
@@ -5,15 +5,18 @@
 #include "third_party/blink/renderer/core/css/style_element.h"
 
 #include <memory>
+
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
 #include "third_party/blink/renderer/core/dom/comment.h"
 #include "third_party/blink/renderer/core/html/html_style_element.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
 TEST(StyleElementTest, CreateSheetUsesCache) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder =
       std::make_unique<DummyPageHolder>(gfx::Size(800, 600));
   Document& document = dummy_page_holder->GetDocument();
diff --git a/third_party/blink/renderer/core/css/style_traversal_root_test.cc b/third_party/blink/renderer/core/css/style_traversal_root_test.cc
index 7a01532..dd3491e 100644
--- a/third_party/blink/renderer/core/css/style_traversal_root_test.cc
+++ b/third_party/blink/renderer/core/css/style_traversal_root_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
@@ -92,6 +93,7 @@
   Element* DivElement(ElementIndex index) { return elements_->at(index).Get(); }
 
  private:
+  test::TaskEnvironment task_environment_;
   ScopedNullExecutionContext execution_context_;
   Persistent<Document> document_;
   Persistent<HeapVector<Member<Element>, 7>> elements_;
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 408b8df..7941bd9 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -4999,14 +4999,13 @@
   // into its own stacking context during rendering.
   rare_data.SetRestrictionTargetId(std::move(id));
 
-  // By forcing reattachment, we ensure that the element would now be
-  // picked up as in its own stacking context.
-  // There is no corresponding style change.
-  SetForceReattachLayoutTree();
-
   // If a LayoutObject does not yet exist, this full paint invalidation
   // will occur automatically after it is created.
   if (LayoutObject* layout_object = GetLayoutObject()) {
+    // The paint properties need to updated, even though the style hasn't
+    // changed.
+    layout_object->SetNeedsPaintPropertyUpdate();
+
     // The SubCaptureTarget ID needs to be propagated to the paint system.
     layout_object->SetShouldDoFullPaintInvalidation();
   }
diff --git a/third_party/blink/renderer/core/editing/caret_display_item_client.cc b/third_party/blink/renderer/core/editing/caret_display_item_client.cc
index cd9035b..9c093074 100644
--- a/third_party/blink/renderer/core/editing/caret_display_item_client.cc
+++ b/third_party/blink/renderer/core/editing/caret_display_item_client.cc
@@ -118,8 +118,25 @@
 
   // Get the layoutObject that will be responsible for painting the caret
   // (which is either the layoutObject we just found, or one of its containers).
-  LayoutBlock* caret_block =
-      CaretLayoutBlock(caret_position.AnchorNode(), caret_rect.layout_object);
+  LayoutBlock* caret_block;
+  if (caret_rect.root_box_fragment) {
+    caret_block =
+        To<LayoutBlock>(caret_rect.root_box_fragment->GetMutableLayoutObject());
+    // The root box fragment's layout object should always match the one we'd
+    // get from CaretLayoutBlock, except for atomic inline-level LayoutBlocks
+    // (i.e. display: inline-block). In those cases, the layout object should be
+    // either the caret rect's layout block, or its containing block.
+    if (!caret_rect.layout_object->IsLayoutBlock() &&
+        !caret_rect.layout_object->IsAtomicInlineLevel()) {
+      DCHECK_EQ(caret_block, CaretLayoutBlock(caret_position.AnchorNode(),
+                                              caret_rect.layout_object));
+    } else if (caret_block != caret_rect.layout_object) {
+      DCHECK_EQ(caret_block, caret_rect.layout_object->ContainingBlock());
+    }
+  } else {
+    caret_block =
+        CaretLayoutBlock(caret_position.AnchorNode(), caret_rect.layout_object);
+  }
   return {MapCaretRectToCaretPainter(caret_block, caret_rect), caret_block,
           caret_rect.root_box_fragment};
 }
diff --git a/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc b/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc
index 59d3c5f..adf20e3 100644
--- a/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc
+++ b/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc
@@ -74,10 +74,18 @@
     return GetCaretDisplayItemClient().previous_layout_block_.Get();
   }
 
+  const PhysicalBoxFragment* CaretBoxFragment() {
+    return GetCaretDisplayItemClient().box_fragment_.Get();
+  }
+
   bool ShouldPaintCursorCaret(const LayoutBlock& block) {
     return Selection().ShouldPaintCaret(block);
   }
 
+  bool ShouldPaintCursorCaret(const PhysicalBoxFragment& fragment) {
+    return Selection().ShouldPaintCaret(fragment);
+  }
+
   Text* AppendTextNode(const String& data) {
     Text* text = GetDocument().createTextNode(data);
     GetDocument().body()->AppendChild(text);
@@ -690,6 +698,49 @@
   EXPECT_EQ(94, rect.X());
 }
 
+// https://crbug.com/1499405
+TEST_P(CaretDisplayItemClientTest, CaretAtEdgeOfInlineBlock) {
+  LoadAhem();
+  InsertStyleElement(
+      "body { margin: 0; padding: 0; font: 10px/10px Ahem; }"
+      "div#editable { width: 80px; padding: 0px 10px; text-align: center; }"
+      "span { padding: 0px 15px; display: inline-block }");
+  SetBodyContent(
+      "<div id=editable contenteditable>"
+      "<span contenteditable=false>foo</span>"
+      "</div>");
+
+  const Element& editable =
+      *GetDocument().QuerySelector(AtomicString("#editable"));
+  const LayoutBlock* editable_block =
+      To<LayoutBlock>(editable.GetLayoutObject());
+
+  GetDocument().GetPage()->GetFocusController().SetActive(true);
+  GetDocument().GetPage()->GetFocusController().SetFocused(true);
+
+  auto test = [this, editable_block](const Position& position,
+                                     const PhysicalRect& expected_rect) {
+    Selection().SetSelectionAndEndTyping(
+        SelectionInDOMTree::Builder().Collapse(position).Build());
+
+    UpdateAllLifecyclePhasesExceptPaint();
+    EXPECT_FALSE(GetCaretDisplayItemClient().IsValid());
+    UpdateAllLifecyclePhasesForCaretTest();
+
+    EXPECT_TRUE(ShouldPaintCursorCaret(*editable_block));
+    EXPECT_EQ(editable_block, CaretLayoutBlock());
+    EXPECT_EQ(expected_rect, CaretLocalRect());
+
+    DCHECK_EQ(editable_block->PhysicalFragmentCount(), 1u);
+    auto* editable_fragment = editable_block->GetPhysicalFragment(0);
+    EXPECT_TRUE(ShouldPaintCursorCaret(*editable_fragment));
+    EXPECT_EQ(editable_fragment, CaretBoxFragment());
+  };
+
+  test(Position::FirstPositionInNode(editable), PhysicalRect(20, 0, 1, 10));
+  test(Position::LastPositionInNode(editable), PhysicalRect(79, 0, 1, 10));
+}
+
 class ComputeCaretRectTest : public EditingTestBase {
  public:
   ComputeCaretRectTest() = default;
diff --git a/third_party/blink/renderer/core/events/input_event.cc b/third_party/blink/renderer/core/events/input_event.cc
index ed2be15..d05262e 100644
--- a/third_party/blink/renderer/core/events/input_event.cc
+++ b/third_party/blink/renderer/core/events/input_event.cc
@@ -110,25 +110,37 @@
     ranges_.push_back(range->toRange());
 }
 
+InputEvent::InputEvent(const AtomicString& type,
+                       const UIEventInit& init,
+                       InputType input_type,
+                       const String& data,
+                       DataTransfer* data_transfer,
+                       EventIsComposing is_composing,
+                       const StaticRangeVector* ranges)
+    : UIEvent(type, &init),
+      input_type_(input_type),
+      data_(data),
+      data_transfer_(data_transfer),
+      is_composing_(is_composing == kIsComposing) {
+  if (ranges) {
+    for (const auto& range : *ranges) {
+      ranges_.push_back(range->toRange());
+    }
+  }
+}
+
 /* static */
 InputEvent* InputEvent::CreateBeforeInput(InputType input_type,
                                           const String& data,
                                           EventIsComposing is_composing,
                                           const StaticRangeVector* ranges) {
-  InputEventInit* input_event_init = InputEventInit::Create();
-
-  input_event_init->setBubbles(true);
-  input_event_init->setCancelable(InputTypeIsCancelable(input_type));
-  // TODO(ojan): We should find a way to prevent conversion like
-  // String->enum->String just in order to use initializer.
-  // See InputEvent::InputEvent() for the second conversion.
-  input_event_init->setInputType(ConvertInputTypeToString(input_type));
-  input_event_init->setData(data);
-  input_event_init->setIsComposing(is_composing == kIsComposing);
-  if (ranges)
-    input_event_init->setTargetRanges(*ranges);
-  input_event_init->setComposed(true);
-  return InputEvent::Create(event_type_names::kBeforeinput, input_event_init);
+  auto* event_init = UIEventInit::Create();
+  event_init->setBubbles(true);
+  event_init->setCancelable(InputTypeIsCancelable(input_type));
+  event_init->setComposed(true);
+  return MakeGarbageCollected<InputEvent>(event_type_names::kBeforeinput,
+                                          *event_init, input_type, data,
+                                          nullptr, is_composing, ranges);
 }
 
 /* static */
@@ -136,17 +148,13 @@
                                           DataTransfer* data_transfer,
                                           EventIsComposing is_composing,
                                           const StaticRangeVector* ranges) {
-  InputEventInit* input_event_init = InputEventInit::Create();
-
-  input_event_init->setBubbles(true);
-  input_event_init->setCancelable(InputTypeIsCancelable(input_type));
-  input_event_init->setInputType(ConvertInputTypeToString(input_type));
-  input_event_init->setDataTransfer(data_transfer);
-  input_event_init->setIsComposing(is_composing == kIsComposing);
-  if (ranges)
-    input_event_init->setTargetRanges(*ranges);
-  input_event_init->setComposed(true);
-  return InputEvent::Create(event_type_names::kBeforeinput, input_event_init);
+  auto* event_init = UIEventInit::Create();
+  event_init->setBubbles(true);
+  event_init->setCancelable(InputTypeIsCancelable(input_type));
+  event_init->setComposed(true);
+  return MakeGarbageCollected<InputEvent>(event_type_names::kBeforeinput,
+                                          *event_init, input_type, String(),
+                                          data_transfer, is_composing, ranges);
 }
 
 /* static */
@@ -154,20 +162,13 @@
                                     const String& data,
                                     EventIsComposing is_composing,
                                     const StaticRangeVector* ranges) {
-  InputEventInit* input_event_init = InputEventInit::Create();
-
-  input_event_init->setBubbles(true);
-  input_event_init->setCancelable(false);
-  // TODO(ojan): We should find a way to prevent conversion like
-  // String->enum->String just in order to use initializer.
-  // See InputEvent::InputEvent() for the second conversion.
-  input_event_init->setInputType(ConvertInputTypeToString(input_type));
-  input_event_init->setData(data);
-  input_event_init->setIsComposing(is_composing == kIsComposing);
-  if (ranges)
-    input_event_init->setTargetRanges(*ranges);
-  input_event_init->setComposed(true);
-  return InputEvent::Create(event_type_names::kInput, input_event_init);
+  auto* event_init = UIEventInit::Create();
+  event_init->setBubbles(true);
+  event_init->setCancelable(false);
+  event_init->setComposed(true);
+  return MakeGarbageCollected<InputEvent>(event_type_names::kInput, *event_init,
+                                          input_type, data, nullptr,
+                                          is_composing, ranges);
 }
 
 String InputEvent::inputType() const {
diff --git a/third_party/blink/renderer/core/events/input_event.h b/third_party/blink/renderer/core/events/input_event.h
index 11fc2605..0c33453 100644
--- a/third_party/blink/renderer/core/events/input_event.h
+++ b/third_party/blink/renderer/core/events/input_event.h
@@ -92,6 +92,15 @@
                                  const StaticRangeVector*);
 
   InputEvent(const AtomicString&, const InputEventInit*);
+  // This variant of the constructor is more efficient than the InputEventInit
+  // variant.
+  InputEvent(const AtomicString& type,
+             const UIEventInit& init,
+             InputType input_type,
+             const String& data,
+             DataTransfer* data_transfer,
+             EventIsComposing is_composing,
+             const StaticRangeVector* ranges);
 
   String inputType() const;
   const String& data() const { return data_; }
diff --git a/third_party/blink/renderer/core/fetch/build.gni b/third_party/blink/renderer/core/fetch/build.gni
index ce886f1..7fbe765 100644
--- a/third_party/blink/renderer/core/fetch/build.gni
+++ b/third_party/blink/renderer/core/fetch/build.gni
@@ -72,4 +72,5 @@
   "readable_stream_bytes_consumer_test.cc",
   "request_test.cc",
   "response_test.cc",
+  "trust_token_to_mojom_test.cc",
 ]
diff --git a/third_party/blink/renderer/core/fetch/fetch_request_data_test.cc b/third_party/blink/renderer/core/fetch/fetch_request_data_test.cc
index 498621d..a1811cd 100644
--- a/third_party/blink/renderer/core/fetch/fetch_request_data_test.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_request_data_test.cc
@@ -60,7 +60,6 @@
       ::blink::SecurityOrigin::CreateFromString("https://bbb.example"));
   WTF::Vector<WTF::String> additional_signed_headers = {"aaa", "bbb"};
   auto trust_token_params = network::mojom::blink::TrustTokenParams::New(
-      network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1,
       network::mojom::TrustTokenOperationType::kRedemption,
       network::mojom::TrustTokenRefreshPolicy::kUseCached,
       /* custom_key_commitment=*/"custom_key_commitment",
diff --git a/third_party/blink/renderer/core/fetch/request.cc b/third_party/blink/renderer/core/fetch/request.cc
index bc80eadd..8c1fdd2 100644
--- a/third_party/blink/renderer/core/fetch/request.cc
+++ b/third_party/blink/renderer/core/fetch/request.cc
@@ -624,8 +624,8 @@
 
     network::mojom::blink::TrustTokenParams params;
     if (!ConvertTrustTokenToMojomAndCheckPermissions(
-            *init->privateToken(), execution_context, &exception_state,
-            &params)) {
+            *init->privateToken(), GetPSTFeatures(*execution_context),
+            &exception_state, &params)) {
       // Whenever parsing the trustToken argument fails, we expect a suitable
       // exception to be thrown.
       DCHECK(exception_state.HadException());
diff --git a/third_party/blink/renderer/core/fetch/trust_token_to_mojom.cc b/third_party/blink/renderer/core/fetch/trust_token_to_mojom.cc
index 9dc9eae..44d3b30 100644
--- a/third_party/blink/renderer/core/fetch/trust_token_to_mojom.cc
+++ b/third_party/blink/renderer/core/fetch/trust_token_to_mojom.cc
@@ -15,28 +15,28 @@
 using RefreshPolicy = V8RefreshPolicy::Enum;
 using network::mojom::blink::TrustTokenOperationType;
 
+PSTFeatures GetPSTFeatures(const ExecutionContext& execution_context) {
+  PSTFeatures features;
+  features.issuance_enabled = execution_context.IsFeatureEnabled(
+      mojom::blink::PermissionsPolicyFeature::kPrivateStateTokenIssuance);
+  features.redemption_enabled = execution_context.IsFeatureEnabled(
+      mojom::blink::PermissionsPolicyFeature::kTrustTokenRedemption);
+  return features;
+}
+
 bool ConvertTrustTokenToMojomAndCheckPermissions(
     const PrivateToken& in,
-    const ExecutionContext* execution_context,
+    const PSTFeatures& pst_features,
     ExceptionState* exception_state,
     network::mojom::blink::TrustTokenParams* out) {
-  DCHECK(in.hasOperation());  // field is required in IDL
+  // The current implementation always has these fields; the implementation
+  // always initializes them, and the hasFoo functions always return true. These
+  // DCHECKs serve as canaries for implementation changes.
+  DCHECK(in.hasOperation());
+  DCHECK(in.hasVersion());
 
-  // get token version
-  if (in.hasVersion()) {
-    // only version 1 is supported
-    if (in.version().AsEnum() == VersionType::k1) {
-      out->version =
-          network::mojom::blink::TrustTokenMajorVersion::kPrivateStateTokenV1;
-    } else {
-      exception_state->ThrowTypeError("privateToken: unknown token version.");
-      return false;
-    }
-  } else {
-    exception_state->ThrowTypeError(
-        "trustToken: token version is not specified.");
-    return false;
-  }
+  // only version 1 exists at this time
+  DCHECK_EQ(in.version().AsEnum(), VersionType::k1);
 
   if (in.operation().AsEnum() == OperationType::kTokenRequest) {
     out->operation = network::mojom::blink::TrustTokenOperationType::kIssuance;
@@ -100,8 +100,7 @@
   switch (out->operation) {
     case TrustTokenOperationType::kRedemption:
     case TrustTokenOperationType::kSigning:
-      if (!execution_context->IsFeatureEnabled(
-              mojom::blink::PermissionsPolicyFeature::kTrustTokenRedemption)) {
+      if (!pst_features.redemption_enabled) {
         exception_state->ThrowDOMException(
             DOMExceptionCode::kNotAllowedError,
             "Private State Token Redemption ('token-redemption') and signing "
@@ -112,9 +111,7 @@
       }
       break;
     case TrustTokenOperationType::kIssuance:
-      if (!execution_context->IsFeatureEnabled(
-              mojom::blink::PermissionsPolicyFeature::
-                  kPrivateStateTokenIssuance)) {
+      if (!pst_features.issuance_enabled) {
         exception_state->ThrowDOMException(
             DOMExceptionCode::kNotAllowedError,
             "Private State Token Issuance ('token-request') operation "
diff --git a/third_party/blink/renderer/core/fetch/trust_token_to_mojom.h b/third_party/blink/renderer/core/fetch/trust_token_to_mojom.h
index 237b208..eaf5e5b 100644
--- a/third_party/blink/renderer/core/fetch/trust_token_to_mojom.h
+++ b/third_party/blink/renderer/core/fetch/trust_token_to_mojom.h
@@ -6,13 +6,25 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_TRUST_TOKEN_TO_MOJOM_H_
 
 #include "services/network/public/mojom/trust_tokens.mojom-blink.h"
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 
 namespace blink {
 
 class DOMException;
 class PrivateToken;
-class ExecutionContext;
+
+// A representation of the features enabled for Private State Tokens (PSTs)
+// in the current environment.
+struct PSTFeatures {
+  bool issuance_enabled;
+  bool redemption_enabled;
+};
+
+// Returns a PSTFeatures struct representing the features enabled for PSTs in
+// the given execution context.
+PSTFeatures GetPSTFeatures(const ExecutionContext& execution_context);
 
 // Converts an IDL trustToken object to its Mojo counterpart.
 // The elements of trustToken (and of TrustTokenParams) comprise:
@@ -22,7 +34,7 @@
 // - remaining elements partitioned into groups of parameters used for specific
 // operations.
 //
-// The method sets |type|, |version|, |operation| and the fields corresponding
+// The method sets |type|, |operation| and the fields corresponding
 // to the operation specified by |operation|, namely
 // - for issuance, no additional fields;
 // - for redemption, |refresh_policy|;
@@ -33,9 +45,11 @@
 // - for signing, |issuer| must be provided and must be a valid HTTP(S) URL.
 // If this validation fails, throws a TypeError against |exception_state| and
 // returns false.
-bool ConvertTrustTokenToMojomAndCheckPermissions(
+//
+// Exported for unit testing.
+CORE_EXPORT bool ConvertTrustTokenToMojomAndCheckPermissions(
     const PrivateToken& in,
-    const ExecutionContext* execution_context,
+    const PSTFeatures& pst_features,
     ExceptionState* exception_state,
     network::mojom::blink::TrustTokenParams* out);
 
diff --git a/third_party/blink/renderer/core/fetch/trust_token_to_mojom_test.cc b/third_party/blink/renderer/core/fetch/trust_token_to_mojom_test.cc
new file mode 100644
index 0000000..f0d23d56
--- /dev/null
+++ b/third_party/blink/renderer/core/fetch/trust_token_to_mojom_test.cc
@@ -0,0 +1,81 @@
+// 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 "third_party/blink/renderer/core/fetch/trust_token_to_mojom.h"
+
+#include <memory>
+
+#include "services/network/public/mojom/trust_tokens.mojom-blink.h"
+#include "services/network/public/mojom/trust_tokens.mojom-forward.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/permissions_policy/permissions_policy_features.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_private_token.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_private_token_version.h"
+#include "third_party/blink/renderer/platform/bindings/exception_code.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
+
+namespace blink {
+namespace {
+
+TEST(TrustTokenToMojomTest, Issuance) {
+  PrivateToken* pt = PrivateToken::Create();
+  pt->setOperation(V8OperationType::Enum::kTokenRequest);
+  pt->setVersion(V8PrivateTokenVersion::Enum::k1);
+
+  network::mojom::blink::TrustTokenParamsPtr params =
+      network::mojom::blink::TrustTokenParams::New();
+  DummyExceptionStateForTesting e;
+  EXPECT_TRUE(ConvertTrustTokenToMojomAndCheckPermissions(
+      *pt, {.issuance_enabled = true, .redemption_enabled = true}, &e,
+      params.get()));
+  EXPECT_EQ(params->operation,
+            network::mojom::blink::TrustTokenOperationType::kIssuance);
+}
+
+TEST(TrustTokenToMojomTest, IssuanceDenied) {
+  PrivateToken* pt = PrivateToken::Create();
+  pt->setOperation(V8OperationType::Enum::kTokenRequest);
+  pt->setVersion(V8PrivateTokenVersion::Enum::k1);
+
+  auto params = network::mojom::blink::TrustTokenParams::New();
+  DummyExceptionStateForTesting e;
+  EXPECT_FALSE(ConvertTrustTokenToMojomAndCheckPermissions(
+      *pt, {.issuance_enabled = false, .redemption_enabled = true}, &e,
+      params.get()));
+  EXPECT_TRUE(e.HadException());
+  EXPECT_EQ(e.CodeAs<DOMExceptionCode>(), DOMExceptionCode::kNotAllowedError);
+}
+
+TEST(TrustTokenToMojomTest, Redemption) {
+  PrivateToken* pt = PrivateToken::Create();
+  pt->setOperation(V8OperationType::Enum::kTokenRedemption);
+  pt->setVersion(V8PrivateTokenVersion::Enum::k1);
+
+  network::mojom::blink::TrustTokenParamsPtr params =
+      network::mojom::blink::TrustTokenParams::New();
+  DummyExceptionStateForTesting e;
+  EXPECT_TRUE(ConvertTrustTokenToMojomAndCheckPermissions(
+      *pt, {.issuance_enabled = true, .redemption_enabled = true}, &e,
+      params.get()));
+  EXPECT_EQ(params->operation,
+            network::mojom::blink::TrustTokenOperationType::kRedemption);
+}
+
+TEST(TrustTokenToMojomTest, RedemptionDenied) {
+  PrivateToken* pt = PrivateToken::Create();
+  pt->setOperation(V8OperationType::Enum::kTokenRedemption);
+  pt->setVersion(V8PrivateTokenVersion::Enum::k1);
+
+  auto params = network::mojom::blink::TrustTokenParams::New();
+  DummyExceptionStateForTesting e;
+  EXPECT_FALSE(ConvertTrustTokenToMojomAndCheckPermissions(
+      *pt, {.issuance_enabled = true, .redemption_enabled = false}, &e,
+      params.get()));
+  EXPECT_TRUE(e.HadException());
+  EXPECT_EQ(e.CodeAs<DOMExceptionCode>(), DOMExceptionCode::kNotAllowedError);
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 3016bc66..838d4df 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -3102,9 +3102,8 @@
     return;
   }
 
-  VisualViewport& visual_viewport = frame_->GetPage()->GetVisualViewport();
-  gfx::SizeF visual_viewport_size(visual_viewport.VisibleWidthCSSPx(),
-                                  visual_viewport.VisibleHeightCSSPx());
+  gfx::Size visual_viewport_size =
+      GetScrollableArea()->VisibleContentRect().size();
 
   bool did_layout = false;
   {
@@ -3145,16 +3144,10 @@
   frame_->GetDocument()->ClearFocusedElementIfNeeded();
 
   if (did_layout) {
-    bool visual_viewport_size_changed = false;
-    if (frame_->IsMainFrame()) {
-      // Scrollbars changing state can cause a visual viewport size change.
-      gfx::SizeF new_viewport_size(visual_viewport.VisibleWidthCSSPx(),
-                                   visual_viewport.VisibleHeightCSSPx());
-      visual_viewport_size_changed =
-          (new_viewport_size != visual_viewport_size);
-      DCHECK(!visual_viewport_size_changed ||
-             visual_viewport.IsActiveViewport());
-    }
+    gfx::Size new_visual_viewport_size =
+        GetScrollableArea()->VisibleContentRect().size();
+    bool visual_viewport_size_changed =
+        (new_visual_viewport_size != visual_viewport_size);
     SetNeedsUpdateGeometries();
     PerformPostLayoutTasks(visual_viewport_size_changed);
     GetFrame().GetDocument()->LayoutUpdated();
diff --git a/third_party/blink/renderer/core/html/fenced_frame/fence.cc b/third_party/blink/renderer/core/html/fenced_frame/fence.cc
index e40bdf4..b81237bc 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/fence.cc
+++ b/third_party/blink/renderer/core/html/fenced_frame/fence.cc
@@ -95,6 +95,12 @@
     return;
   }
 
+  if (event->getEventTypeOr("").StartsWith(
+          blink::kFencedFrameReservedPAEventPrefix)) {
+    AddConsoleMessage("Reserved events cannot be triggered manually.");
+    return;
+  }
+
   if (event->hasDestinationURL() &&
       base::FeatureList::IsEnabled(
           blink::features::kAdAuctionReportingWithMacroApi)) {
diff --git a/third_party/blink/renderer/core/html/fenced_frame/fence_test.cc b/third_party/blink/renderer/core/html/fenced_frame/fence_test.cc
index 29284994..f05b3ba 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/fence_test.cc
+++ b/third_party/blink/renderer/core/html/fenced_frame/fence_test.cc
@@ -8,6 +8,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_fence_event.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_union_fenceevent_string.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
@@ -59,4 +61,23 @@
             "Reserved events cannot be triggered manually.");
 }
 
+TEST_F(FenceTest, ReportReservedEvent) {
+  const KURL base_url("https://www.example.com/");
+  V8TestingScope scope(base_url);
+  Fence* fence =
+      MakeGarbageCollected<Fence>(*(GetDocument().GetFrame()->DomWindow()));
+  FenceEvent* event = FenceEvent::Create();
+  event->setEventType("reserved.top_navigation");
+  V8UnionFenceEventOrString* event_union =
+      MakeGarbageCollected<V8UnionFenceEventOrString>(event);
+  fence->reportEvent(scope.GetScriptState(), event_union,
+                     scope.GetExceptionState());
+
+  // There should be a "Reserved events cannot be triggered manually." console
+  // warning.
+  EXPECT_EQ(ConsoleMessages().size(), 1u);
+  EXPECT_EQ(ConsoleMessages().front(),
+            "Reserved events cannot be triggered manually.");
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
index d851151..8423d60 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -29,6 +29,7 @@
 #include <utility>
 
 #include "base/feature_list.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/strcat.h"
@@ -66,6 +67,7 @@
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/core/script/html_parser_script_runner.h"
 #include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
+#include "third_party/blink/renderer/platform/heap/cross_thread_handle.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
@@ -78,6 +80,7 @@
 #include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/wtf.h"
 
 namespace blink {
@@ -331,6 +334,37 @@
       preload_processing_mode_(GetPreloadProcessingMode()),
       budget_(budget) {}
 
+// Wrap pending preloads in a thread safe and ref-counted object since the
+// vector is added to from a background thread and taken from from the main
+// thread.
+class HTMLDocumentParser::PendingPreloads
+    : public ThreadSafeRefCounted<PendingPreloads> {
+ public:
+  PendingPreloads() = default;
+
+  std::unique_ptr<PendingPreloadData> TakeFirst() {
+    base::AutoLock auto_lock(lock_);
+    return preloads_.empty() ? nullptr : preloads_.TakeFirst();
+  }
+
+  // Returns the number of items pending preload after `preload_data` has been
+  // added.
+  size_t Add(std::unique_ptr<PendingPreloadData> preload_data) {
+    base::AutoLock auto_lock(lock_);
+    preloads_.push_back(std::move(preload_data));
+    return preloads_.size();
+  }
+
+  bool IsEmpty() {
+    base::AutoLock auto_lock(lock_);
+    return preloads_.empty();
+  }
+
+ private:
+  base::Lock lock_;
+  Deque<std::unique_ptr<PendingPreloadData>> preloads_ GUARDED_BY(lock_);
+};
+
 HTMLDocumentParser::HTMLDocumentParser(HTMLDocument& document,
                                        ParserSynchronizationPolicy sync_policy,
                                        ParserPrefetchPolicy prefetch_policy)
@@ -394,6 +428,7 @@
            document.Url().IsLocalFile())
               ? kInfiniteTokenizationBudget
               : kDefaultMaxTokenizationBudget)),
+      pending_preloads_(base::MakeRefCounted<PendingPreloads>()),
       scheduler_(sync_policy == kAllowDeferredParsing
                      ? Thread::Current()->Scheduler()
                      : nullptr) {
@@ -1501,7 +1536,7 @@
           this, options_, GetPreloadScannerThread()->GetTaskRunner(),
           CrossThreadBindRepeating(
               &HTMLDocumentParser::AddPreloadDataOnBackgroundThread,
-              WrapCrossThreadWeakPersistent(this),
+              MakeCrossThreadWeakHandle(this), this->pending_preloads_,
               GetDocument()->GetTaskRunner(TaskType::kInternalLoading)));
 
       background_scan_fn_ = CrossThreadBindRepeating(
@@ -1538,31 +1573,29 @@
 
 // static
 void HTMLDocumentParser::AddPreloadDataOnBackgroundThread(
-    CrossThreadWeakPersistent<HTMLDocumentParser> weak_parser,
+    CrossThreadWeakHandle<HTMLDocumentParser> parser_handle,
+    scoped_refptr<PendingPreloads> pending_preloads,
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     std::unique_ptr<PendingPreloadData> preload_data) {
   DCHECK(!IsMainThread());
-  auto parser = weak_parser.Lock();
-  if (!parser)
-    return;
 
-  bool should_post_task = false;
-  {
-    base::AutoLock lock(parser->pending_preload_lock_);
-    // Only post a task if the preload data is empty. Otherwise, a task has
-    // already been posted and will consume the new data.
-    should_post_task = parser->pending_preload_data_.empty();
-    parser->pending_preload_data_.push_back(std::move(preload_data));
-  }
+  size_t num_pending_preloads = pending_preloads->Add(std::move(preload_data));
 
-  if (should_post_task) {
+  // Only post a task if the preload data was empty before we added this data.
+  // Otherwise, a task has already been posted and will consume the new data.
+  if (num_pending_preloads == 1) {
     PostCrossThreadTask(
         *task_runner, FROM_HERE,
-        CrossThreadBindOnce(&HTMLDocumentParser::FlushPendingPreloads,
-                            std::move(parser)));
+        CrossThreadBindOnce(
+            &HTMLDocumentParser::FlushPendingPreloads,
+            MakeUnwrappingCrossThreadWeakHandle(std::move(parser_handle))));
   }
 }
 
+bool HTMLDocumentParser::HasPendingPreloads() {
+  return pending_preloads_->IsEmpty();
+}
+
 void HTMLDocumentParser::FlushPendingPreloads() {
   DCHECK(IsMainThread());
   if (!ThreadedPreloadScannerEnabled())
@@ -1572,15 +1605,9 @@
     return;
 
   // Do this in a loop in case more preloads are added in the background.
-  while (HasPendingPreloads()) {
-    Vector<std::unique_ptr<PendingPreloadData>> preload_data;
-    {
-      base::AutoLock lock(pending_preload_lock_);
-      preload_data = std::move(pending_preload_data_);
-    }
-
-    for (auto& preload : preload_data)
-      ProcessPreloadData(std::move(preload));
+  std::unique_ptr<PendingPreloadData> preload_data;
+  while ((preload_data = pending_preloads_->TakeFirst())) {
+    ProcessPreloadData(std::move(preload_data));
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.h b/third_party/blink/renderer/core/html/parser/html_document_parser.h
index c196588..ee22777 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.h
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.h
@@ -50,6 +50,7 @@
 #include "third_party/blink/renderer/core/html/parser/text_resource_decoder.h"
 #include "third_party/blink/renderer/core/page/viewport_description.h"
 #include "third_party/blink/renderer/core/script/html_parser_script_runner_host.h"
+#include "third_party/blink/renderer/platform/heap/cross_thread_handle.h"
 #include "third_party/blink/renderer/platform/heap/prefinalizer.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/deque.h"
@@ -140,13 +141,14 @@
   void ForcePlaintextForTextDocument();
 
  private:
+  enum NextTokenStatus { kNoTokens, kHaveTokens, kHaveTokensAfterScript };
+  class PendingPreloads;
+
   HTMLDocumentParser(Document&,
                      ParserContentPolicy,
                      ParserSynchronizationPolicy,
                      ParserPrefetchPolicy);
 
-  enum NextTokenStatus { kNoTokens, kHaveTokens, kHaveTokensAfterScript };
-
   // DocumentParser
   void Detach() final;
   bool HasInsertionPoint() final;
@@ -234,14 +236,12 @@
 
   // Called on the background thread by |background_scanner_|.
   static void AddPreloadDataOnBackgroundThread(
-      CrossThreadWeakPersistent<HTMLDocumentParser> weak_parser,
+      CrossThreadWeakHandle<HTMLDocumentParser> parser_handle,
+      scoped_refptr<PendingPreloads> pending_preloads,
       scoped_refptr<base::SequencedTaskRunner> task_runner,
       std::unique_ptr<PendingPreloadData> preload_data);
 
-  bool HasPendingPreloads() {
-    base::AutoLock lock(pending_preload_lock_);
-    return !pending_preload_data_.empty();
-  }
+  bool HasPendingPreloads();
 
   // Returns true if the data should be processed (tokenizer pumped) now. If
   // this returns false, SchedulePumpTokenizer() should be called. This is
@@ -285,12 +285,10 @@
   // A timer for how long we are inactive after yielding
   std::unique_ptr<base::ElapsedTimer> yield_timer_;
 
-  // If ThreadedPreloadScanner is enabled, preload data will be added to this
-  // vector from a background thread. The main thread will take this preload
-  // data and send out the requests.
-  base::Lock pending_preload_lock_;
-  Vector<std::unique_ptr<PendingPreloadData>> pending_preload_data_
-      GUARDED_BY(pending_preload_lock_);
+  // If ThreadedPreloadScanner is enabled, preload data will be added to
+  // `pending_preloads_` from a background thread. The main thread will
+  // take this preload data and send out the requests.
+  scoped_refptr<PendingPreloads> pending_preloads_;
 
   ThreadScheduler* scheduler_;
 
diff --git a/third_party/blink/renderer/core/html/trust_token_attribute_parsing.cc b/third_party/blink/renderer/core/html/trust_token_attribute_parsing.cc
index 5c684984..b90b1f08 100644
--- a/third_party/blink/renderer/core/html/trust_token_attribute_parsing.cc
+++ b/third_party/blink/renderer/core/html/trust_token_attribute_parsing.cc
@@ -3,25 +3,16 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/core/html/trust_token_attribute_parsing.h"
+#include "base/logging.h"
 #include "services/network/public/mojom/trust_tokens.mojom-blink.h"
 #include "services/network/public/mojom/trust_tokens.mojom-shared.h"
 #include "third_party/blink/renderer/core/fetch/trust_token_to_mojom.h"
 #include "third_party/blink/renderer/platform/json/json_values.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
-namespace blink {
-
-namespace internal {
+namespace blink::internal {
 
 namespace {
-bool ParseMajorVersion(int in, network::mojom::TrustTokenMajorVersion* out) {
-  if (in == 1) {
-    *out = network::mojom::TrustTokenMajorVersion::kPrivateStateTokenV1;
-    return true;
-  } else {
-    return false;
-  }
-}
 bool ParseOperation(const String& in,
                     network::mojom::TrustTokenOperationType* out) {
   if (in == "token-request") {
@@ -62,12 +53,16 @@
 
   auto ret = network::mojom::blink::TrustTokenParams::New();
 
-  // |version| is required.
+  // |version| is required, though unused.
   int version;
   if (!object->GetInteger("version", &version)) {
+    LOG(WARNING) << "expected integer trust token version, got none";
     return nullptr;
   }
-  if (!ParseMajorVersion(version, &ret->version)) {
+  // Although we don't use the version number internally, it's still the case
+  // that we only understand version 1.
+  if (version != 1) {
+    LOG(WARNING) << "expected trust token version 1, got " << version;
     return nullptr;
   }
 
@@ -117,5 +112,4 @@
   return ret;
 }
 
-}  // namespace internal
-}  // namespace blink
+}  // namespace blink::internal
diff --git a/third_party/blink/renderer/core/html/trust_token_attribute_parsing_test.cc b/third_party/blink/renderer/core/html/trust_token_attribute_parsing_test.cc
index 921cddf..0f9fbe9 100644
--- a/third_party/blink/renderer/core/html/trust_token_attribute_parsing_test.cc
+++ b/third_party/blink/renderer/core/html/trust_token_attribute_parsing_test.cc
@@ -13,14 +13,12 @@
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
-namespace blink {
-namespace internal {
+namespace blink::internal {
 namespace {
 
 network::mojom::blink::TrustTokenParamsPtr NetworkParamsToBlinkParams(
     network::mojom::TrustTokenParamsPtr params) {
   auto ret = network::mojom::blink::TrustTokenParams::New();
-  ret->version = params->version;
   ret->operation = params->operation;
   ret->refresh_policy = params->refresh_policy;
   for (const url::Origin& issuer : params->issuers) {
@@ -74,7 +72,6 @@
   // well with the "issuers" field's members' type of
   // scoped_refptr<blink::SecurityOrigin>: in particular, the method does an
   // address-to-address comparison of the pointers.
-  EXPECT_EQ(result->version, expectation->version);
   EXPECT_EQ(result->operation, expectation->operation);
   EXPECT_EQ(result->refresh_policy, expectation->refresh_policy);
 
@@ -263,5 +260,4 @@
   ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
 }
 
-}  // namespace internal
-}  // namespace blink
+}  // namespace blink::internal
diff --git a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc
index d26e1f9..2c7455c 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc
@@ -421,17 +421,24 @@
                      depends_on_block_constraints};
 }
 
-MinMaxSizesResult GridLayoutAlgorithm::ComputeMinMaxSizes(
-    const GridSizingSubtree& sizing_subtree) {
+MinMaxSizesResult GridLayoutAlgorithm::ComputeSubgridMinMaxSizes(
+    const GridSizingSubtree& sizing_subtree) const {
   DCHECK(sizing_subtree);
 
-  return {{ComputeSubgridContributionSize(sizing_subtree, kForColumns,
-                                          SizingConstraint::kMinContent),
-           ComputeSubgridContributionSize(sizing_subtree, kForColumns,
-                                          SizingConstraint::kMaxContent)},
+  return {{ComputeSubgridIntrinsicSize(sizing_subtree, kForColumns,
+                                       SizingConstraint::kMinContent),
+           ComputeSubgridIntrinsicSize(sizing_subtree, kForColumns,
+                                       SizingConstraint::kMaxContent)},
           /* depends_on_block_constraints */ false};
 }
 
+LayoutUnit GridLayoutAlgorithm::ComputeSubgridIntrinsicBlockSize(
+    const GridSizingSubtree& sizing_subtree) const {
+  DCHECK(sizing_subtree);
+  return ComputeSubgridIntrinsicSize(sizing_subtree, kForRows,
+                                     SizingConstraint::kMaxContent);
+}
+
 namespace {
 
 GridArea SubgriddedAreaInParent(const SubgriddedItemData& opt_subgrid_data) {
@@ -468,7 +475,34 @@
                                        /* is_intrinsic */ true);
 
   return GridLayoutAlgorithm({subgrid_data.node, fragment_geometry, space})
-      .ComputeMinMaxSizes(sizing_subtree);
+      .ComputeSubgridMinMaxSizes(sizing_subtree);
+}
+
+LayoutUnit ComputeBlockSizeForSubgrid(const GridSizingSubtree& sizing_subtree,
+                                      const GridItemData& subgrid_data,
+                                      const ConstraintSpace& space) {
+  DCHECK(sizing_subtree);
+  DCHECK(subgrid_data.IsSubgrid());
+
+  const auto& node = subgrid_data.node;
+  const auto& style = node.Style();
+
+  const auto border_padding =
+      ComputeBorders(space, node) + ComputePadding(space, style);
+  const auto fragment_geometry =
+      CalculateInitialFragmentGeometry(space, node,
+                                       /* break_token */ nullptr,
+                                       /* is_intrinsic */ true);
+
+  const absl::optional<LayoutUnit> available_inline_size =
+      space.AvailableSize().inline_size;
+
+  return ComputeBlockSizeForFragment(
+      space, style, border_padding,
+      GridLayoutAlgorithm({node, fragment_geometry, space})
+          .ComputeSubgridIntrinsicBlockSize(sizing_subtree),
+      (*available_inline_size == kIndefiniteSize) ? absl::nullopt
+                                                  : available_inline_size);
 }
 
 FragmentGeometry CalculateInitialFragmentGeometryForSubgrid(
@@ -1129,23 +1163,6 @@
     baseline_shim = track_baseline - baseline - extra_margin;
   };
 
-  auto SubgridContributionSize = [&](bool is_min_content) -> LayoutUnit {
-    DCHECK(grid_item->IsSubgrid());
-
-    const auto fragment_geometry = CalculateInitialFragmentGeometry(
-        space, grid_item->node, /* break_token */ nullptr,
-        /* is_intrinsic */ true);
-
-    const GridLayoutAlgorithm subgrid_algorithm(
-        {node, fragment_geometry, space});
-
-    return subgrid_algorithm.ComputeSubgridContributionSize(
-        sizing_subtree.SubgridSizingSubtree(*grid_item),
-        RelativeDirectionInSubgrid(track_direction, *grid_item),
-        is_min_content ? SizingConstraint::kMinContent
-                       : SizingConstraint::kMaxContent);
-  };
-
   auto MinMaxSizesFunc = [&](MinMaxSizesType type) -> MinMaxSizesResult {
     if (grid_item->IsSubgrid()) {
       return ComputeMinMaxSizesForSubgrid(
@@ -1203,7 +1220,8 @@
     DCHECK(!is_parallel_with_track_direction);
 
     if (grid_item->IsSubgrid()) {
-      return SubgridContributionSize(/* is_min_content */ false);
+      return ComputeBlockSizeForSubgrid(
+          sizing_subtree.SubgridSizingSubtree(*grid_item), *grid_item, space);
     }
 
     // TODO(ikilpatrick): This check is potentially too broad, i.e. a fixed
@@ -2093,7 +2111,7 @@
   }
 }
 
-LayoutUnit GridLayoutAlgorithm::ComputeSubgridContributionSize(
+LayoutUnit GridLayoutAlgorithm::ComputeSubgridIntrinsicSize(
     const GridSizingSubtree& sizing_subtree,
     GridTrackSizingDirection track_direction,
     SizingConstraint sizing_constraint) const {
diff --git a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.h b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.h
index de9bd997..9cb8581f 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.h
@@ -41,7 +41,10 @@
   const LayoutResult* Layout() override;
   MinMaxSizesResult ComputeMinMaxSizes(const MinMaxSizesFloatInput&) override;
 
-  MinMaxSizesResult ComputeMinMaxSizes(const GridSizingSubtree& sizing_subtree);
+  LayoutUnit ComputeSubgridIntrinsicBlockSize(
+      const GridSizingSubtree& sizing_subtree) const;
+  MinMaxSizesResult ComputeSubgridMinMaxSizes(
+      const GridSizingSubtree& sizing_subtree) const;
 
   // Computes the containing block rect of out of flow items from stored data in
   // |GridLayoutData|.
@@ -170,7 +173,7 @@
                       const CallbackFunc& callback_func,
                       bool should_compute_min_max_sizes = true) const;
 
-  LayoutUnit ComputeSubgridContributionSize(
+  LayoutUnit ComputeSubgridIntrinsicSize(
       const GridSizingSubtree& sizing_subtree,
       GridTrackSizingDirection track_direction,
       SizingConstraint sizing_constraint) const;
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 3a696de..c83e810 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -720,13 +720,6 @@
     NOT_DESTROYED();
     return IsStackingContext(StyleRef());
   }
-  inline bool IsElementCaptureParticipant() const {
-    NOT_DESTROYED();
-    if (Element* element = DynamicTo<Element>(GetNode())) {
-      return element->GetRestrictionTargetId();
-    }
-    return false;
-  }
   inline bool IsStackingContext(const ComputedStyle& style) const {
     NOT_DESTROYED();
     // This is an inlined version of the following:
@@ -739,8 +732,7 @@
            ((style.ContainsLayout() || style.ContainsPaint()) &&
             (!IsInline() || IsAtomicInlineLevel()) && !IsRubyText() &&
             (!IsTablePart() || IsLayoutBlockFlow())) ||
-           ViewTransitionUtils::IsViewTransitionParticipant(*this) ||
-           IsElementCaptureParticipant();
+           ViewTransitionUtils::IsViewTransitionParticipant(*this);
   }
 
   inline bool IsStacked() const {
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 757f221..908da65 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -43,6 +43,7 @@
 #include "base/time/default_tick_clock.h"
 #include "base/types/optional_util.h"
 #include "build/chromeos_buildflags.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/network/public/cpp/client_hints.h"
 #include "services/network/public/cpp/header_util.h"
 #include "services/network/public/cpp/web_sandbox_flags.h"
@@ -3126,6 +3127,12 @@
       response_.HttpHeaderField(http_names::kContentEncoding);
   if (content_encoding.LowerASCII() == "zstd") {
     CountUse(WebFeature::kZstdContentEncoding);
+    if (frame_->IsOutermostMainFrame()) {
+      ukm::builders::MainFrameNavigation_ZstdContentEncoding builder(
+          ukm_source_id_);
+      builder.SetUsedZstd(true);
+      builder.Record(frame_->GetDocument()->UkmRecorder());
+    }
   }
   if (response_.DidUseSharedDictionary()) {
     CountUse(WebFeature::kSharedDictionaryUsed);
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
index cb241f4..ff24ca8 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
@@ -285,6 +285,34 @@
   return object.IsBox();
 }
 
+// Defined by the Element Capture specification:
+// https://screen-share.github.io/element-capture/#elements-eligible-for-restriction
+bool IsEligibleForElementCapture(const LayoutObject& object) {
+  // The element forms a stacking context.
+  if (!object.IsStackingContext()) {
+    return false;
+  }
+
+  // The element is flattened in 3D.
+  if (!object.CreatesGroup()) {
+    return false;
+  }
+
+  // The element forms a backdrop root.
+  // See ViewTransitionUtils::IsViewTransitionParticipant and
+  // NeedsEffectIgnoringClipPath for how View Transitions meets this
+  // requirement.
+  // TODO(https://issuetracker.google.com/291602746): handle backdrop root case.
+
+  // The element has exactly one box fragment.
+  if (object.IsBox() && To<LayoutBox>(object).PhysicalFragmentCount() > 1) {
+    return false;
+  }
+
+  // Meets all of the conditions for element capture.
+  return true;
+}
+
 }  // anonymous namespace
 
 CompositingReasons CompositingReasonFinder::DirectReasonsForPaintProperties(
@@ -359,7 +387,9 @@
 
   auto* element = DynamicTo<Element>(object.GetNode());
   if (element && element->GetRestrictionTargetId()) {
-    reasons |= CompositingReason::kElementCapture;
+    if (IsEligibleForElementCapture(object)) {
+      reasons |= CompositingReason::kElementCapture;
+    }
   }
 
   return reasons;
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 695f621..86d9c9ba 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -1716,7 +1716,7 @@
 
   if (!(full_context_.direct_compositing_reasons &
         CompositingReason::kElementCapture)) {
-    OnClearEffect(properties_->ElementCaptureEffect());
+    OnClearEffect(properties_->ClearElementCaptureEffect());
     return;
   }
 
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index 39e9e10..6a33eee 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -5577,6 +5577,11 @@
   // This test makes sure that an ElementCaptureEffect node is properly added
   // when an element has a restriction ID.
   SetBodyInnerHTML(R"HTML(
+     <style>
+      .stacking {
+        opacity: 0.9;
+      }
+    </style>
     <body id="body1">
       <div id="div1" width="640" height="480"/>
     </body>
@@ -5592,15 +5597,19 @@
       std::make_unique<RestrictionTargetId>(base::Token::CreateRandom()));
   UpdateAllLifecyclePhasesForTest();
 
-  // The element should now have a proper stacking context.
-  EXPECT_TRUE(element->GetLayoutObject()->HasLayer());
-  EXPECT_TRUE(element->GetLayoutObject()->IsStackingContext());
+  // The element should still not have a proper stacking context.
+  EXPECT_FALSE(element->GetLayoutObject()->HasLayer());
+  EXPECT_FALSE(element->GetLayoutObject()->IsStackingContext());
 
-  // Now that the div has a restriction ID, it should have an element capture
-  // effect node.
+  // Now that the div has a restriction ID and a stacking context, it should
+  // have an element capture effect node.
+  element->setAttribute(html_names::kClassAttr, AtomicString("stacking"));
+  UpdateAllLifecyclePhasesForTest();
   const ObjectPaintProperties* paint_properties =
       element->GetLayoutObject()->FirstFragment().PaintProperties();
   EXPECT_TRUE(paint_properties && paint_properties->ElementCaptureEffect());
+  EXPECT_TRUE(element->GetLayoutObject()->HasLayer());
+  EXPECT_TRUE(element->GetLayoutObject()->IsStackingContext());
 
   // NOTE: we don't currently have a teardown path for element capture. Once an
   // element is marked for capture it is marked for the rest of its lifetime.
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
index 1a5b9870..5994b455 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
@@ -2168,4 +2168,79 @@
   EXPECT_FALSE(GetFrame().View()->GetPaintArtifactCompositor()->NeedsUpdate());
 }
 
+TEST_P(PaintPropertyTreeUpdateTest, ElementCaptureUpdate) {
+  ScopedElementCaptureForTest scoped_element_capture(true);
+
+  SetBodyInnerHTML(R"HTML(
+   <style>
+      div {
+        height: 100px;
+      }
+      .stacking {
+        opacity: 0.9;
+      }
+      #container {
+        columns:4;
+        column-fill:auto;
+      }
+      .fragmentize {
+        height: 50px;
+      }
+      #target {
+        background: linear-gradient(red, blue);
+      }
+    </style>
+
+    <div id='container'>
+      <div id='target' class='stacking'></div>
+    </div>
+  )HTML");
+
+  /// Does not have an effect without a restriction target.
+  Element* element = GetDocument().getElementById(AtomicString("target"));
+  const ObjectPaintProperties* paint_properties =
+      element->GetLayoutObject()->FirstFragment().PaintProperties();
+  EXPECT_FALSE(paint_properties && paint_properties->ElementCaptureEffect());
+
+  // Ensure we have an effect once we have a restriction target token.
+  element->SetRestrictionTargetId(
+      std::make_unique<RestrictionTargetId>(base::Token::CreateRandom()));
+  EXPECT_TRUE(element->GetLayoutObject()->NeedsPaintPropertyUpdate());
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(element->GetLayoutObject()->NeedsPaintPropertyUpdate());
+  paint_properties =
+      element->GetLayoutObject()->FirstFragment().PaintProperties();
+  EXPECT_TRUE(paint_properties && paint_properties->ElementCaptureEffect());
+
+  // Should not have an effect if `#target`'s stacking context is removed.
+  element->setAttribute(html_names::kClassAttr, AtomicString(""));
+  UpdateAllLifecyclePhasesForTest();
+  paint_properties =
+      element->GetLayoutObject()->FirstFragment().PaintProperties();
+  EXPECT_FALSE(paint_properties && paint_properties->ElementCaptureEffect());
+
+  // Should have an effect if `#target` gets a stacking context.
+  element->setAttribute(html_names::kClassAttr, AtomicString("stacking"));
+  UpdateAllLifecyclePhasesForTest();
+  paint_properties =
+      element->GetLayoutObject()->FirstFragment().PaintProperties();
+  EXPECT_TRUE(paint_properties && paint_properties->ElementCaptureEffect());
+
+  // Should not have an effect if `#target` becomes fragmented. This is done
+  // indirectly by resizing the parent.
+  Element* container = GetDocument().getElementById(AtomicString("container"));
+  container->setAttribute(html_names::kClassAttr, AtomicString("fragmentize"));
+  UpdateAllLifecyclePhasesForTest();
+  paint_properties =
+      element->GetLayoutObject()->FirstFragment().PaintProperties();
+  EXPECT_FALSE(paint_properties && paint_properties->ElementCaptureEffect());
+
+  // Should have an effect if `#target`'s becomes unfragmented again.
+  container->setAttribute(html_names::kClassAttr, AtomicString(""));
+  UpdateAllLifecyclePhasesForTest();
+  paint_properties =
+      element->GetLayoutObject()->FirstFragment().PaintProperties();
+  EXPECT_TRUE(paint_properties && paint_properties->ElementCaptureEffect());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
index a090b4cf..c0c7251c 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -1470,8 +1470,8 @@
 
   auto params = network::mojom::blink::TrustTokenParams::New();
   if (!ConvertTrustTokenToMojomAndCheckPermissions(
-          *trust_token, GetExecutionContext(), &exception_state,
-          params.get())) {
+          *trust_token, GetPSTFeatures(*GetExecutionContext()),
+          &exception_state, params.get())) {
     DCHECK(exception_state.HadException());
     return;
   }
diff --git a/third_party/blink/renderer/modules/accessibility/accessibility_object_model_test.cc b/third_party/blink/renderer/modules/accessibility/accessibility_object_model_test.cc
index bfd7848b..5e31a35 100644
--- a/third_party/blink/renderer/modules/accessibility/accessibility_object_model_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/accessibility_object_model_test.cc
@@ -63,7 +63,7 @@
   auto* button = GetDocument().getElementById(AtomicString("button"));
   ASSERT_NE(nullptr, button);
 
-  auto* axButton = cache->GetOrCreate(button);
+  auto* axButton = cache->Get(button);
   EXPECT_EQ(ax::mojom::Role::kButton, axButton->RoleValue());
 
   button->accessibleNode()->setRole(AtomicString("slider"));
@@ -71,7 +71,7 @@
 
   GetDocument().View()->UpdateLifecycleToLayoutClean(
       DocumentUpdateReason::kTest);
-  axButton = cache->GetOrCreate(button);
+  axButton = cache->Get(button);
 
   // No change in the AXObject role should be observed.
   EXPECT_EQ(ax::mojom::Role::kButton, axButton->RoleValue());
@@ -94,7 +94,7 @@
   auto* cache = AXObjectCache();
   ASSERT_NE(nullptr, cache);
   cache->UpdateAXForAllDocuments();
-  auto* axTextBox = cache->GetOrCreate(textbox);
+  auto* axTextBox = cache->Get(textbox);
   EXPECT_EQ(ax::mojom::Role::kTextFieldWithComboBox, axTextBox->RoleValue());
   ax::mojom::NameFrom name_from;
   AXObject::AXObjectVector name_objects;
@@ -124,7 +124,7 @@
   auto* cache = AXObjectCache();
   ASSERT_NE(nullptr, cache);
   cache->UpdateAXForAllDocuments();
-  auto* axButton = cache->GetOrCreate(button);
+  auto* axButton = cache->Get(button);
   EXPECT_EQ(ax::mojom::Role::kCheckBox, axButton->RoleValue());
   ax::mojom::NameFrom name_from;
   AXObject::AXObjectVector name_objects;
@@ -171,7 +171,7 @@
   auto* cache = AXObjectCache();
   ASSERT_NE(nullptr, cache);
   cache->UpdateAXForAllDocuments();
-  auto* ax_slider = cache->GetOrCreate(slider);
+  auto* ax_slider = cache->Get(slider);
   float value = 0.0f;
   EXPECT_TRUE(ax_slider->MinValueForRange(&value));
   EXPECT_EQ(0.0f, value);
@@ -194,7 +194,7 @@
   auto* cache = AXObjectCache();
   ASSERT_NE(nullptr, cache);
   cache->UpdateAXForAllDocuments();
-  auto* ax_heading = cache->GetOrCreate(heading);
+  auto* ax_heading = cache->Get(heading);
   EXPECT_EQ(2, ax_heading->HeadingLevel());
 }
 
@@ -213,7 +213,7 @@
   auto* cache = AXObjectCache();
   ASSERT_NE(nullptr, cache);
   cache->UpdateAXForAllDocuments();
-  auto* ax_listitem = cache->GetOrCreate(listitem);
+  auto* ax_listitem = cache->Get(listitem);
   EXPECT_EQ(0, ax_listitem->PosInSet());
   EXPECT_EQ(0, ax_listitem->SetSize());
 }
@@ -252,16 +252,16 @@
   ASSERT_NE(nullptr, cache);
   cache->UpdateAXForAllDocuments();
 
-  auto* ax_grid = cache->GetOrCreate(grid);
+  auto* ax_grid = cache->Get(grid);
   EXPECT_EQ(0, ax_grid->AriaColumnCount());
   EXPECT_EQ(0, ax_grid->AriaRowCount());
 
-  auto* ax_cell = cache->GetOrCreate(cell);
+  auto* ax_cell = cache->Get(cell);
   EXPECT_TRUE(ax_cell->IsTableCellLikeRole());
   EXPECT_EQ(0U, ax_cell->AriaColumnIndex());
   EXPECT_EQ(0U, ax_cell->AriaRowIndex());
 
-  auto* ax_cell2 = cache->GetOrCreate(cell2);
+  auto* ax_cell2 = cache->Get(cell2);
   EXPECT_TRUE(ax_cell2->IsTableCellLikeRole());
   EXPECT_EQ(0U, ax_cell2->AriaColumnIndex());
   EXPECT_EQ(0U, ax_cell2->AriaRowIndex());
diff --git a/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc b/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc
index 0dfb3069..37130de 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc
@@ -61,7 +61,7 @@
   if (!map)
     return nullptr;
 
-  return cache.GetOrCreate(static_cast<Node*>(map->ImageElement()));
+  return cache.Get(static_cast<Node*>(map->ImageElement()));
 }
 
 ax::mojom::blink::Role AXImageMapLink::NativeRoleIgnoringAria() const {
@@ -118,7 +118,7 @@
     return;
 
   out_bounds_in_container = area->GetPath(layout_object).BoundingRect();
-  *out_container = AXObjectCache().GetOrCreate(layout_object);
+  *out_container = AXObjectCache().Get(layout_object);
 }
 
 bool AXImageMapLink::IsImageMapLink() const {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
index 1f9e872..3f530dbd 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
@@ -221,7 +221,7 @@
     return ParentObject()->NextOnLine();
 
   if (AbstractInlineTextBox* next_on_line = inline_text_box_->NextOnLine()) {
-    return AXObjectCache().GetOrCreate(next_on_line, nullptr);
+    return AXObjectCache().Get(next_on_line);
   }
   return nullptr;
 }
@@ -235,7 +235,7 @@
 
   AbstractInlineTextBox* previous_on_line = inline_text_box_->PreviousOnLine();
   if (previous_on_line)
-    return AXObjectCache().GetOrCreate(previous_on_line, nullptr);
+    return AXObjectCache().Get(previous_on_line);
 
   return nullptr;
 }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index 303e11b..193a2b6 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -682,8 +682,7 @@
     if (!current_node)
       break;
 
-    AXObject* tentative_child =
-        start_object->AXObjectCache().GetOrCreate(current_node);
+    AXObject* tentative_child = start_object->AXObjectCache().Get(current_node);
 
     if (tentative_child && tentative_child->AccessibilityIsIncludedInTree())
       result = tentative_child;
@@ -760,7 +759,7 @@
     while (cursor) {
       LayoutObject* runner_layout_object = cursor.CurrentMutableLayoutObject();
       DCHECK(runner_layout_object);
-      AXObject* result = AXObjectCache().GetOrCreate(runner_layout_object);
+      AXObject* result = AXObjectCache().Get(runner_layout_object);
 
       bool is_inert = result ? result->IsInert() : false;
 
@@ -855,7 +854,7 @@
     while (cursor) {
       LayoutObject* runner_layout_object = cursor.CurrentMutableLayoutObject();
       DCHECK(runner_layout_object);
-      AXObject* result = AXObjectCache().GetOrCreate(runner_layout_object);
+      AXObject* result = AXObjectCache().Get(runner_layout_object);
 
       bool is_inert = result ? result->IsInert() : false;
 
@@ -1019,7 +1018,7 @@
   }
 
   LayoutObject* obj = node->GetLayoutObject();
-  AXObject* result = AXObjectCache().GetOrCreate(obj);
+  AXObject* result = AXObjectCache().Get(obj);
   if (!result)
     return nullptr;
   result->UpdateChildrenIfNecessary();
@@ -1033,8 +1032,9 @@
     // control. The label is ignored because it's already reflected in the name.
     if (auto* label = DynamicTo<HTMLLabelElement>(result->GetNode())) {
       if (HTMLElement* control = label->control()) {
-        if (AXObject* ax_control = AXObjectCache().GetOrCreate(control))
+        if (AXObject* ax_control = AXObjectCache().Get(control)) {
           return ax_control;
+        }
       }
     }
 
@@ -1205,7 +1205,7 @@
             target_column_index <= effective_last_col &&
             target_row_index >= row_index &&
             target_row_index < row_index + row_span) {
-          return AXObjectCache().GetOrCreate(cell);
+          return AXObjectCache().Get(cell);
         }
       }
     }
@@ -1230,7 +1230,7 @@
          row = row->NextRow()) {
       for (LayoutTableCell* cell = row->FirstCell(); cell;
            cell = cell->NextCell()) {
-        AXObject* ax_cell = AXObjectCache().GetOrCreate(cell);
+        AXObject* ax_cell = AXObjectCache().Get(cell);
         if (ax_cell && ax_cell->RoleValue() == role)
           cells.push_back(ax_cell);
       }
@@ -1262,7 +1262,7 @@
 
   for (LayoutTableCell* cell = row->FirstCell(); cell;
        cell = cell->NextCell()) {
-    AXObject* ax_cell = cell ? AXObjectCache().GetOrCreate(cell) : nullptr;
+    AXObject* ax_cell = cell ? AXObjectCache().Get(cell) : nullptr;
     if (ax_cell && ax_cell->RoleValue() == ax::mojom::blink::Role::kRowHeader)
       return ax_cell;
   }
@@ -1303,7 +1303,7 @@
   if (!area)
     return nullptr;
 
-  AXObject* parent = AXObjectCache().GetOrCreate(area->ImageElement());
+  AXObject* parent = AXObjectCache().Get(area->ImageElement());
   if (!parent)
     return nullptr;
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_list_box.cc b/third_party/blink/renderer/modules/accessibility/ax_list_box.cc
index 5dc3141a..7f51676 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_list_box.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_list_box.cc
@@ -56,7 +56,7 @@
   // It's also more accurate, because active_index includes optgroup lines,
   // whereas select->item() assumes an index that does not include them.
   HTMLOptionElement* option = select->ActiveSelectionEnd();
-  return AXObjectCache().GetOrCreate(option);
+  return AXObjectCache().Get(option);
 }
 
 void AXListBox::ActiveIndexChanged() {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.cc b/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.cc
index 68cf46a..2014b10 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.cc
@@ -61,15 +61,22 @@
   }
 
   // If there is a <select> ancestor, return the popup for it, if rendered.
-  if (AXObject* select_ax_object = cache.GetOrCreate(select)) {
-    if (auto* menu_list = DynamicTo<AXMenuList>(select_ax_object))
-      return menu_list->GetOrCreateMockPopupChild();
+  AXObject* select_ax_object = cache.Get(select);
+  if (!select_ax_object) {
+    return nullptr;
   }
 
-  // Otherwise, just return an AXObject for the parent node.
-  // This could be the <select> if it was not rendered.
-  // Or, any parent node if the <option> was not inside an AXMenuList.
-  return cache.GetOrCreate(select);
+  if (auto* menu_list = DynamicTo<AXMenuList>(select_ax_object)) {
+    // Return the popup.
+    return menu_list->GetOrCreateMockPopupChild();
+  }
+
+  DUMP_WILL_BE_NOTREACHED_NORETURN();
+
+  // Otherwise, just return the AXObject for the parent <select>.
+  // We should not reach here, because if an AXObject existed for the
+  // select element, then it must be an AXMenuList.
+  return select_ax_object;
 }
 
 bool AXMenuListOption::IsVisible() const {
@@ -180,7 +187,7 @@
   // need to expose the bounds of options on those platforms.
 
   auto* select = To<HTMLOptionElement>(GetNode())->OwnerSelectElement();
-  AXObject* ax_menu_list = AXObjectCache().GetOrCreate(select);
+  AXObject* ax_menu_list = AXObjectCache().Get(select);
   if (!ax_menu_list)
     return;
   DCHECK(ax_menu_list->IsMenuList());
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index f56f4641..ec246bd 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -224,7 +224,7 @@
     if (!node)
       return nullptr;
 
-    blink::AXObject* ax_object = cache.GetOrCreate(node);
+    blink::AXObject* ax_object = cache.Get(node);
     if (ax_object && !IsNeutralWithinTable(ax_object))
       return ax_object;
   }
@@ -504,7 +504,7 @@
   if (!descendant)
     return nullptr;
 
-  AXObject* ax_descendant = AXObjectCache().GetOrCreate(descendant);
+  AXObject* ax_descendant = AXObjectCache().Get(descendant);
   return ax_descendant && ax_descendant->IsVisible() ? ax_descendant : nullptr;
 }
 
@@ -1693,7 +1693,7 @@
     return;
   AXObjectCacheImpl& cache = AXObjectCache();
   for (const auto& element : elements) {
-    if (AXObject* child = cache.GetOrCreate(element)) {
+    if (AXObject* child = cache.Get(element)) {
       // Only aria-labelledby and aria-describedby can target hidden elements.
       if (!child)
         continue;
@@ -2115,7 +2115,7 @@
     return false;
 
   for (const auto& element : elements) {
-    AXObject* tab_panel = AXObjectCache().GetOrCreate(element);
+    AXObject* tab_panel = AXObjectCache().Get(element);
 
     // A tab item should only control tab panels.
     if (!tab_panel ||
@@ -2477,7 +2477,7 @@
   String fragment = link_url.FragmentIdentifier();
   TreeScope& tree_scope = anchor->GetTreeScope();
   Node* target = tree_scope.FindAnchor(fragment);
-  AXObject* ax_target = AXObjectCache().GetOrCreate(target);
+  AXObject* ax_target = AXObjectCache().Get(target);
   if (!ax_target || !IsPotentialInPageLinkTarget(*ax_target->GetNode()))
     return AXObject::InPageLinkTarget();
 
@@ -2583,7 +2583,7 @@
     HeapVector<Member<HTMLInputElement>> html_radio_buttons =
         FindAllRadioButtonsWithSameName(node_radio_button);
     for (HTMLInputElement* radio_button : html_radio_buttons) {
-      AXObject* ax_radio_button = AXObjectCache().GetOrCreate(radio_button);
+      AXObject* ax_radio_button = AXObjectCache().Get(radio_button);
       if (ax_radio_button)
         radio_buttons.push_back(ax_radio_button);
     }
@@ -5148,7 +5148,7 @@
 void AXNodeObject::SelectedOptions(AXObjectVector& options) const {
   if (auto* select = DynamicTo<HTMLSelectElement>(GetNode())) {
     for (auto* const option : *select->selectedOptions()) {
-      AXObject* ax_option = AXObjectCache().GetOrCreate(option);
+      AXObject* ax_option = AXObjectCache().Get(option);
       if (ax_option)
         options.push_back(ax_option);
     }
@@ -5278,7 +5278,7 @@
 
   AXObjectVector error_messages;
   for (Element* element : elements_from_attribute) {
-    AXObject* obj = AXObjectCache().GetOrCreate(element);
+    AXObject* obj = AXObjectCache().Get(element);
     if (obj && !obj->AccessibilityIsIgnored()) {
       error_messages.push_back(obj);
     }
@@ -5337,8 +5337,7 @@
         NameSource(*found_text_alternative, html_names::kPopovertargetAttr));
     name_sources->back().type = name_from;
   }
-  AXObject* popover_ax_object =
-      AXObjectCache().GetOrCreate(popover_target.popover);
+  AXObject* popover_ax_object = AXObjectCache().Get(popover_target.popover);
 
   // Hint popovers are used for text if and only if all of the contents are
   // plain, e.g. have no interesting semantic or interactive elements.
@@ -5678,7 +5677,7 @@
     }
     HTMLTableCaptionElement* caption = table_element->caption();
     if (caption) {
-      AXObject* caption_ax_object = AXObjectCache().GetOrCreate(caption);
+      AXObject* caption_ax_object = AXObjectCache().Get(caption);
       if (caption_ax_object) {
         text_alternative =
             RecursiveTextAlternative(*caption_ax_object, nullptr, visited);
@@ -5796,7 +5795,7 @@
     }
     HTMLElement* legend = html_field_set_element->Legend();
     if (legend) {
-      AXObject* legend_ax_object = AXObjectCache().GetOrCreate(legend);
+      AXObject* legend_ax_object = AXObjectCache().Get(legend);
       // Avoid an infinite loop
       if (legend_ax_object && !visited.Contains(legend_ax_object)) {
         text_alternative =
@@ -6097,7 +6096,7 @@
     }
     HTMLTableCaptionElement* caption = table_element->caption();
     if (caption) {
-      AXObject* caption_ax_object = AXObjectCache().GetOrCreate(caption);
+      AXObject* caption_ax_object = AXObjectCache().Get(caption);
       if (caption_ax_object) {
         AXObjectSet visited;
         description =
@@ -6177,7 +6176,7 @@
           description_sources->back().type = description_from;
         }
         AXObject* popover_ax_object =
-            AXObjectCache().GetOrCreate(popover_target.popover);
+            AXObjectCache().Get(popover_target.popover);
         if (popover_ax_object && popover_ax_object->IsPlainContent()) {
           AXObjectSet visited;
           description = RecursiveTextAlternative(*popover_ax_object,
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index c47ef16..7ece47426 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -966,8 +966,8 @@
         << "AXInlineTextBox box tried to compute a new parent, but they are "
            "not allowed to exist even temporarily without a parent, as their "
            "existence depends on the parent text object. Parent text = "
-        << (AXObjectCache().SafeGet(GetNode())
-                ? AXObjectCache().SafeGet(GetNode())->ToString(true, true)
+        << (AXObjectCache().Get(GetNode())
+                ? AXObjectCache().Get(GetNode())->ToString(true, true)
                 : "");
   } else if (AXObjectCache().IsAriaOwned(this)) {
     ax_parent = AXObjectCache().ValidatedAriaOwner(this);
@@ -1194,9 +1194,19 @@
 
   Node* parent_node = GetParentNodeForComputeParent(cache, current_node);
 
+  // If the tree is not currently mutable, then new AXObjects cannot be created.
+  // Return the AXObject for the parent only if it is already part of the tree.
+  if (!cache.IsProcessingDeferredEvents()) {
+    return cache.Get(parent_node);
+  }
+
+  // Get the existing AXObject for the parent or create one if necessary.
   // Will not create an object if no valid parent node is found. This occurs
   // when a DOM child isn't visited by LayoutTreeBuilderTraversal, such as an
   // element child of a <textarea>, which only supports plain text.
+  // TODO(acessibility) Covert to NOTREACHED(), and eventually try to remove
+  // parent repairs and this method entirely, as AXObjects will only be created
+  // by building from the top down.
   return cache.GetOrCreate(parent_node);
 }
 
@@ -2420,7 +2430,7 @@
     // The next element is already the popover.
     return nullptr;
   }
-  return AXObjectCache().GetOrCreate(target_popover);
+  return AXObjectCache().Get(target_popover);
 }
 
 // Try to get an aria-controls for an <input role="combobox">, because it
@@ -2448,7 +2458,7 @@
                             html_names::kAriaOwnsAttr) &&
       owned_elements.size() > 0) {
     DCHECK(owned_elements[0]);
-    listbox_candidate = AXObjectCache().GetOrCreate(owned_elements[0]);
+    listbox_candidate = AXObjectCache().Get(owned_elements[0]);
   }
 
   // Combobox grouping <div role="combobox"><input><div role="listbox"></div>.
@@ -3314,15 +3324,14 @@
         // dialog or a fullscreen element (see AdjustStyleForInert).
         Document& document = GetNode()->GetDocument();
         if (HTMLDialogElement* dialog = document.ActiveModalDialog()) {
-          if (AXObject* dialog_object = AXObjectCache().GetOrCreate(dialog)) {
+          if (AXObject* dialog_object = AXObjectCache().Get(dialog)) {
             ignored_reasons->push_back(
                 IgnoredReason(kAXActiveModalDialog, dialog_object));
             return true;
           }
         } else if (Element* fullscreen =
                        Fullscreen::FullscreenElementFrom(document)) {
-          if (AXObject* fullscreen_object =
-                  AXObjectCache().GetOrCreate(fullscreen)) {
+          if (AXObject* fullscreen_object = AXObjectCache().Get(fullscreen)) {
             ignored_reasons->push_back(
                 IgnoredReason(kAXActiveFullscreenElement, fullscreen_object));
             return true;
@@ -3492,9 +3501,8 @@
   }
 
   if (ignored_reasons) {
-    ignored_reasons->push_back(
-        IgnoredReason(kAXAriaModalDialog,
-                      AXObjectCache().GetOrCreate(active_aria_modal_dialog)));
+    ignored_reasons->push_back(IgnoredReason(
+        kAXAriaModalDialog, AXObjectCache().Get(active_aria_modal_dialog)));
   }
   return true;
 }
@@ -3529,7 +3537,7 @@
 
   while (element) {
     if (element->IsInertRoot())
-      return AXObjectCache().GetOrCreate(element);
+      return AXObjectCache().Get(element);
     element = FlatTreeTraversal::ParentElement(*element);
   }
 
@@ -3976,7 +3984,7 @@
       type != FormControlType::kInputWeek) {
     return nullptr;
   }
-  return AXObjectCache().GetOrCreate(input);
+  return AXObjectCache().Get(input);
 }
 
 bool AXObject::LastKnownIsIgnoredValue() const {
@@ -4593,7 +4601,7 @@
   AXRelatedObjectVector local_related_objects;
 
   for (const auto& element : elements) {
-    AXObject* ax_element = AXObjectCache().GetOrCreate(element);
+    AXObject* ax_element = AXObjectCache().Get(element);
     if (ax_element) {
       found_valid_element = true;
       AXObject* aria_labelled_by_node = nullptr;
@@ -5993,7 +6001,7 @@
   if (global_root_scroller->GetDocument() != GetDocument())
     return nullptr;
 
-  return AXObjectCache().GetOrCreate(global_root_scroller);
+  return AXObjectCache().Get(global_root_scroller);
 }
 
 LocalFrameView* AXObject::DocumentFrameView() const {
@@ -6450,7 +6458,7 @@
   if (layout_object->IsFixedPositioned()) {
     // If it's a fixed position element, the container should simply be the
     // root web area.
-    container = AXObjectCache().GetOrCreate(GetDocument());
+    container = AXObjectCache().Get(GetDocument());
   } else {
     while (container) {
       container_layout_object = container->GetLayoutObject();
@@ -6566,7 +6574,7 @@
   // Updating style and layout for the node can cause it to gain layout,
   // detaching an AXNodeObject to make room for an AXLayoutObject.
   if (IsDetached()) {
-    AXObject* new_object = cache.GetOrCreate(node);
+    AXObject* new_object = cache.Get(node);
     return new_object ? new_object->PerformAction(action_data) : false;
   }
 
@@ -6770,7 +6778,7 @@
   // Updating style and layout for the node can cause it to gain layout,
   // detaching an AXNodeObject to make room for an AXLayoutObject.
   if (IsDetached()) {
-    AXObject* new_object = cache.GetOrCreate(node);
+    AXObject* new_object = cache.Get(node);
     return new_object
                ? new_object->OnNativeScrollToMakeVisibleWithSubFocusAction(
                      subfocus, horizontal_scroll_alignment,
@@ -6901,9 +6909,8 @@
           ScrollAlignment::CenterIfNeeded(), ScrollAlignment::CenterIfNeeded(),
           mojom::blink::ScrollType::kProgrammatic, false,
           mojom::blink::ScrollBehavior::kAuto));
-  AXObjectCache().PostNotification(
-      AXObjectCache().GetOrCreate(GetDocument()->GetLayoutView()),
-      ax::mojom::blink::Event::kLocationChanged);
+  AXObjectCache().PostNotification(GetDocument(),
+                                   ax::mojom::blink::Event::kLocationChanged);
   return true;
 }
 
@@ -6924,9 +6931,8 @@
           mojom::blink::ScrollType::kProgrammatic,
           false /* make_visible_in_visual_viewport */,
           mojom::blink::ScrollBehavior::kAuto));
-  AXObjectCache().PostNotification(
-      AXObjectCache().GetOrCreate(GetDocument()->GetLayoutView()),
-      ax::mojom::blink::Event::kLocationChanged);
+  AXObjectCache().PostNotification(GetDocument(),
+                                   ax::mojom::blink::Event::kLocationChanged);
   return true;
 }
 
@@ -6944,9 +6950,8 @@
           ScrollAlignment::LeftAlways(), ScrollAlignment::TopAlways(),
           mojom::blink::ScrollType::kProgrammatic, false,
           mojom::blink::ScrollBehavior::kAuto));
-  AXObjectCache().PostNotification(
-      AXObjectCache().GetOrCreate(GetDocument()->GetLayoutView()),
-      ax::mojom::blink::Event::kLocationChanged);
+  AXObjectCache().PostNotification(GetDocument(),
+                                   ax::mojom::blink::Event::kLocationChanged);
   return true;
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 542be01..238a2d5 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -769,8 +769,6 @@
                                      const ui::AXMode& ax_mode)
     : document_(document),
       ax_mode_(ax_mode),
-      serialize_post_lifecycle_(base::FeatureList::IsEnabled(
-          blink::features::kSerializeAccessibilityPostLifecycle)),
       validation_message_axid_(0),
       active_aria_modal_dialog_(nullptr),
       accessibility_event_permission_(mojom::blink::PermissionStatus::ASK),
@@ -832,11 +830,12 @@
 }
 
 AXObject* AXObjectCacheImpl::Root() {
-  if (AXObject* root = SafeGet(document_))
+  if (AXObject* root = Get(document_)) {
     return root;
+  }
 
   ProcessDeferredAccessibilityEvents(GetDocument(), /*force*/ true);
-  return SafeGet(document_.Get());
+  return Get(document_.Get());
 }
 
 AXObject* AXObjectCacheImpl::ObjectFromAXID(AXID id) const {
@@ -903,16 +902,7 @@
   Node* focused_node = FocusedNode();
   CHECK(focused_node);
 
-  AXObject* obj = nullptr;
-  if (IsProcessingDeferredEvents()) {
-    obj = GetOrCreate(focused_node);
-  } else {
-    // The focus is on a node that has been pruned from the a11y tree, such as
-    // on an area in an image map that is not being used. Handle this rare
-    // edge case without crashing (do not try to create an accessible while
-    // being frozen).
-    obj = Get(focused_node);
-  }
+  AXObject* obj = Get(focused_node);
   if (!obj) {
     // In rare cases it's possible for the focus to not exist in the tree.
     // An example would be a focused element inside of an image map that
@@ -988,7 +978,7 @@
   return result;
 }
 
-AXObject* AXObjectCacheImpl::SafeGet(const Node* node) {
+AXObject* AXObjectCacheImpl::Get(const Node* node) {
   if (!node)
     return nullptr;
 
@@ -1029,11 +1019,6 @@
   return result;
 }
 
-// TODO(accessibility) Remove SafeGet() and make all calls Get().
-AXObject* AXObjectCacheImpl::Get(const Node* node) {
-  return SafeGet(node);
-}
-
 AXObject* AXObjectCacheImpl::Get(AbstractInlineTextBox* inline_text_box) {
   if (!inline_text_box)
     return nullptr;
@@ -1068,7 +1053,7 @@
 }
 
 AXID AXObjectCacheImpl::GetExistingAXID(Node* node) {
-  AXObject* ax_object = SafeGet(node);
+  AXObject* ax_object = Get(node);
   if (!ax_object)
     return ui::AXNodeData::kInvalidAXID;
   return ax_object->AXObjectID();
@@ -1085,7 +1070,7 @@
         << accessible_node->element();
     // When the AccessibleNode is attached to an element, return the element's
     // accessible object instead.
-    return SafeGet(accessible_node->element());
+    return Get(accessible_node->element());
   }
 
   auto it_ax = accessible_node_mapping_.find(accessible_node);
@@ -1112,7 +1097,7 @@
   // parent, which should be a native image.
   Node* child = LayoutTreeBuilderTraversal::FirstChild(map);
   while (child) {
-    if (AXObject* ax_child = SafeGet(child)) {
+    if (AXObject* ax_child = Get(child)) {
       if (AXObject* ax_image = ax_child->CachedParentObject()) {
         if (ax_image->IsDetached()) {
           return nullptr;
@@ -1366,6 +1351,9 @@
 
 AXObject* AXObjectCacheImpl::GetOrCreate(Node* node,
                                          AXObject* parent_if_known) {
+  CHECK(IsProcessingDeferredEvents())
+      << "Only create AXObjects while processing AX events and tree: " << node;
+
   if (!node)
     return nullptr;
 
@@ -1399,7 +1387,7 @@
   // These will be used to repair the local included subtree top down.
   HeapDeque<Member<Node>> ancestors_to_repair;
   Node* ancestor = child;
-  AXObject* ax_ancestor = SafeGet(ancestor);
+  AXObject* ax_ancestor = Get(ancestor);
   AXObject* ax_included_ancestor = nullptr;
 
   while (true) {
@@ -1467,7 +1455,7 @@
 
   // Starting from the highest ancestor, add children.
   for (Node* ancestor_to_repair : ancestors_to_repair) {
-    ax_ancestor = SafeGet(ancestor_to_repair);
+    ax_ancestor = Get(ancestor_to_repair);
     if (!ax_ancestor || ax_ancestor->IsDetached()) {
       RemoveSubtreeWhenSafe(ancestor_to_repair);
       return nullptr;
@@ -1475,7 +1463,7 @@
     ax_ancestor->UpdateChildrenIfNecessary();
   }
 
-  AXObject* result = SafeGet(child);
+  AXObject* result = Get(child);
   if (!result || result->IsDetached()) {
     RemoveSubtreeWhenSafe(child);
     return nullptr;
@@ -1491,9 +1479,9 @@
   // New AXObjects cannot be created when the tree is frozen.
   // In this state, the tree should already be complete because
   // of UpdateTreeIfNeeded().
-  if (IsFrozen()) {
-    return nullptr;
-  }
+  CHECK(IsProcessingDeferredEvents())
+      << "Only create AXObjects while processing AX events and tree: " << node
+      << " " << layout_object;
 
 #if DCHECK_IS_ON()
   DCHECK(node || layout_object);
@@ -1573,10 +1561,6 @@
     return nullptr;
   }
 
-  CHECK(IsProcessingDeferredEvents())
-      << "Only create AXObjects while processing AX events and tree: " << node
-      << " " << layout_object;
-
   AXID axid = GenerateAXID();
   DCHECK(!base::Contains(objects_, axid));
 
@@ -1628,6 +1612,10 @@
 
 AXObject* AXObjectCacheImpl::GetOrCreate(LayoutObject* layout_object,
                                          AXObject* parent_if_known) {
+  CHECK(IsProcessingDeferredEvents())
+      << "Only create AXObjects while processing AX events and tree: "
+      << layout_object;
+
   if (!layout_object)
     return nullptr;
 
@@ -1641,6 +1629,9 @@
 
 AXObject* AXObjectCacheImpl::GetOrCreate(AbstractInlineTextBox* inline_text_box,
                                          AXObject* parent) {
+  CHECK(IsProcessingDeferredEvents())
+      << "Only create AXObjects while processing AX events and tree";
+
   if (!inline_text_box)
     return nullptr;
 
@@ -1999,7 +1990,7 @@
   // failed because document had pending slot assignment in
   // external/wpt/dom/nodes/node-appendchild-crash.html.
   DCHECK(!node->GetDocument().IsFlatTreeTraversalForbidden());
-  AXObject* object = SafeGet(node);
+  AXObject* object = Get(node);
   if (!object && !remove_root) {
     // Nothing remaining to do for this subtree. Already removed.
     return;
@@ -2205,7 +2196,7 @@
       node, 0u, ComputeEventFrom(), active_event_from_action_,
       ActiveEventIntents(), update_reason, event));
 
-  if (AXObject* obj = SafeGet(node)) {
+  if (AXObject* obj = Get(node)) {
     obj->InvalidateCachedValues();
   }
 
@@ -2277,7 +2268,7 @@
                                      bool visibility_or_inertness_changed) {
   DCHECK(layout_object);
   SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
-  AXObject* ax_object = SafeGet(layout_object->GetNode());
+  AXObject* ax_object = Get(layout_object->GetNode());
   if (!ax_object) {
     // No object exists to mark dirty yet -- there can sometimes be a layout in
     // the initial empty document, or style has changed before the object cache
@@ -2380,13 +2371,13 @@
     return;
 
   DCHECK(!node->GetDocument().NeedsLayoutTreeUpdateForNode(*node));
-  TextChangedWithCleanLayout(node, GetOrCreate(node));
+  TextChangedWithCleanLayout(node, Get(node));
 }
 
 void AXObjectCacheImpl::FocusableChangedWithCleanLayout(Node* node) {
   Element* element = To<Element>(node);
   DCHECK(!element->GetDocument().NeedsLayoutTreeUpdateForNode(*element));
-  AXObject* obj = GetOrCreate(element);
+  AXObject* obj = Get(element);
   if (!obj)
     return;
 
@@ -2462,7 +2453,7 @@
   // Force computation of aria-owns, so that original parents that already
   // computed their children get the aria-owned children removed.
   if (AXObject::HasARIAOwns(To<Element>(node))) {
-    if (AXObject* obj = GetOrCreate(node)) {
+    if (AXObject* obj = Get(node)) {
       CHECK(relation_cache_);
       relation_cache_->UpdateAriaOwnsWithCleanLayout(obj);
     }
@@ -2529,7 +2520,7 @@
 
   // Handle subtree that was previously display:none gaining layout.
   if (node->GetLayoutObject()) {
-    if (AXObject* obj = SafeGet(node); obj && !IsA<AXLayoutObject>(obj)) {
+    if (AXObject* obj = Get(node); obj && !IsA<AXLayoutObject>(obj)) {
       // Had a previous AXObject, but wasn't an AXLayoutObject, even though
       // there is a layout object available.
       RemoveSubtreeWithFlatTraversal(node);
@@ -2578,7 +2569,7 @@
     if (HTMLMapElement* map = AXObject::GetMapForImage(node)) {
       HTMLImageElement* primary_image_element = map->ImageElement();
       if (node != primary_image_element) {
-        ChildrenChangedWithCleanLayout(SafeGet(primary_image_element));
+        ChildrenChangedWithCleanLayout(Get(primary_image_element));
       } else if (AXObject* ax_previous_parent = GetAXImageForMap(*map)) {
         if (ax_previous_parent->GetNode() != node) {
           ChildrenChangedWithCleanLayout(ax_previous_parent->GetNode(),
@@ -2709,19 +2700,11 @@
 }
 
 void AXObjectCacheImpl::SlotAssignmentWillChange(Node* node) {
-  // Use SafeGet(), because right before slot assignment is a dangerous time to
-  // test whether the slot must be invalidated, because this currently requires
-  // looking at the <slot> children in
-  // IsShadowContentRelevantForAccessibility(), resulting in an infinite loop
-  // as looking at the children causes slot assignment to be recalculated.
-  // TODO(accessibility) In the future this may be simplified.
-  // See crbug.com/1219311.
-  ChildrenChanged(SafeGet(node));
+  ChildrenChanged(node);
 }
 
 void AXObjectCacheImpl::ChildrenChanged(Node* node) {
-  // Use SafeGet() because there is no guarantee that layout is clean right now.
-  ChildrenChanged(SafeGet(node));
+  ChildrenChanged(Get(node));
 }
 
 // ChildrenChanged gets called a lot. For the accessibility tests that
@@ -2760,9 +2743,12 @@
 }
 
 void AXObjectCacheImpl::ChildrenChangedWithCleanLayout(Node* node) {
-  ChildrenChangedWithCleanLayout(node, Get(node));
+  if (AXObject* obj = Get(node)) {
+    ChildrenChangedWithCleanLayout(node, obj);
+  }
 }
 
+// TODO can node be non-optional?
 void AXObjectCacheImpl::ChildrenChangedWithCleanLayout(Node* optional_node,
                                                        AXObject* obj) {
   CHECK(obj);
@@ -2771,7 +2757,7 @@
 #if DCHECK_IS_ON()
   if (optional_node) {
     DCHECK_EQ(obj->GetNode(), optional_node);
-    DCHECK_EQ(obj, SafeGet(optional_node));
+    DCHECK_EQ(obj, Get(optional_node));
   }
   Document* document = obj->GetDocument();
   DCHECK(document);
@@ -3086,36 +3072,34 @@
     UpdateTreeIfNeeded();
   }
 
-  if (serialize_post_lifecycle_) {
-    if (IsSerializationInFlight()) {
-      // Another serialization is in flight. When it's finished, a new
-      // serialization will be triggered if necessary.
-      return;
-    }
-
-    const auto& now = base::Time::Now();
-    const auto& delay_between_serializations =
-        base::Milliseconds(GetDeferredEventsDelay());
-    const auto& elapsed_since_last_serialization =
-        now - last_serialization_timestamp_;
-    const auto& delay_until_next_serialization =
-        delay_between_serializations - elapsed_since_last_serialization;
-    if (delay_until_next_serialization.is_positive()) {
-      if (!weak_factory_for_serialization_pipeline_.HasWeakPtrs()) {
-        document.GetTaskRunner(blink::TaskType::kInternalDefault)
-            ->PostDelayedTask(
-                FROM_HERE,
-                WTF::BindOnce(
-                    &AXObjectCacheImpl::ScheduleAXUpdate,
-                    weak_factory_for_serialization_pipeline_.GetWeakPtr()),
-                delay_until_next_serialization);
-      }
-      return;  // No serialization needed yet.
-    }
-
-    weak_factory_for_serialization_pipeline_.InvalidateWeakPtrs();
+  if (IsSerializationInFlight()) {
+    // Another serialization is in flight. When it's finished, a new
+    // serialization will be triggered if necessary.
+    return;
   }
 
+  const auto& now = base::Time::Now();
+  const auto& delay_between_serializations =
+      base::Milliseconds(GetDeferredEventsDelay());
+  const auto& elapsed_since_last_serialization =
+      now - last_serialization_timestamp_;
+  const auto& delay_until_next_serialization =
+      delay_between_serializations - elapsed_since_last_serialization;
+  if (delay_until_next_serialization.is_positive()) {
+    if (!weak_factory_for_serialization_pipeline_.HasWeakPtrs()) {
+      document.GetTaskRunner(blink::TaskType::kInternalDefault)
+          ->PostDelayedTask(
+              FROM_HERE,
+              WTF::BindOnce(
+                  &AXObjectCacheImpl::ScheduleAXUpdate,
+                  weak_factory_for_serialization_pipeline_.GetWeakPtr()),
+              delay_until_next_serialization);
+    }
+    return;  // No serialization needed yet.
+  }
+
+  weak_factory_for_serialization_pipeline_.InvalidateWeakPtrs();
+
   // ------------------------ Freeze and serialize ---------------------------
   {
     // The frozen state begins immediately after processing deferred events.
@@ -3126,7 +3110,7 @@
     // TODO(accessibility) It's a bit confusing that this can be true when the
     // IsDirty() is false, but this is the case for objects marked dirty from
     // RenderAccessibilityImpl, e.g. for the kEndOfTest event.
-    if (serialize_post_lifecycle_ && HasDirtyObjects()) {
+    if (HasDirtyObjects()) {
       if (auto* client = GetWebLocalFrameClient()) {
         client->AXReadyCallback();
       }
@@ -3248,6 +3232,8 @@
 
   UpdateNumTreeUpdatesQueuedBeforeLayoutHistogram();
 
+  CHECK(!IsFrozen());
+
   TreeUpdateCallbackQueue old_tree_update_callback_queue;
   GetTreeUpdateCallbackQueue(document).swap(old_tree_update_callback_queue);
   nodes_with_pending_children_changed_.clear();
@@ -3300,14 +3286,6 @@
   PostNotification(Get(node), notification);
 }
 
-void AXObjectCacheImpl::EnsurePostNotification(
-    Node* node,
-    ax::mojom::blink::Event notification) {
-  if (!node)
-    return;
-  PostNotification(GetOrCreate(node), notification);
-}
-
 void AXObjectCacheImpl::PostNotification(AXObject* object,
                                          ax::mojom::blink::Event event_type) {
   if (!object || !object->AXObjectID() || object->IsDetached())
@@ -3508,7 +3486,7 @@
     case TreeUpdateReason::kMarkDirtyFromHandleLayout:
     case TreeUpdateReason::kMarkDirtyFromHandleScroll:
     case TreeUpdateReason::kMarkDirtyFromRemove:
-      EnsureMarkDirtyWithCleanLayout(node);
+      MarkAXObjectDirtyWithCleanLayout(Get(node));
       break;
     case TreeUpdateReason::kNodeGainedFocus:
       HandleNodeGainedFocusWithCleanLayout(node);
@@ -3519,7 +3497,7 @@
     case TreeUpdateReason::kPostNotificationFromHandleLoadComplete:
     case TreeUpdateReason::kPostNotificationFromHandleLoadStart:
     case TreeUpdateReason::kPostNotificationFromHandleScrolledToAnchor:
-      EnsurePostNotification(node, tree_update->event);
+      PostNotification(node, tree_update->event);
       break;
     case TreeUpdateReason::kRemoveValidationMessageObjectFromFocusedUIElement:
       RemoveValidationMessageObjectWithCleanLayout(node);
@@ -3720,8 +3698,9 @@
   SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
 
   DCHECK(!node->GetDocument().NeedsLayoutTreeUpdateForNode(*node));
-  if (AXObject* obj = GetOrCreate(node))
+  if (AXObject* obj = Get(node)) {
     obj->HandleAriaExpandedChanged();
+  }
 }
 
 void AXObjectCacheImpl::HandleAriaPressedChangedWithCleanLayout(Node* node) {
@@ -3846,8 +3825,9 @@
   DCHECK(node);
   DCHECK(!node->GetDocument().NeedsLayoutTreeUpdateForNode(*node));
 
-  if (AXObject* obj = GetOrCreate(node))
+  if (AXObject* obj = Get(node)) {
     obj->HandleActiveDescendantChanged();
+  }
 }
 
 // A <section> or role=region uses the region role if and only if it has a name.
@@ -4009,7 +3989,7 @@
                attr_name == html_names::kAriaFlowtoAttr) {
       MarkElementDirty(element);
       if (relation_cache_) {
-        if (AXObject* obj = SafeGet(element)) {
+        if (AXObject* obj = Get(element)) {
           relation_cache_->RegisterIncompleteRelation(obj, attr_name);
         }
       }
@@ -4265,7 +4245,7 @@
 
   MarkElementDirty(&node);
   // If the ignored state changes, the parent's children may have changed.
-  if (AXObject* obj = SafeGet(&node)) {
+  if (AXObject* obj = Get(&node)) {
     if (!obj->IsDetached()) {
       if (obj->CachedParentObject()) {
         ChildrenChanged(obj->CachedParentObject());
@@ -4301,7 +4281,7 @@
 
 void AXObjectCacheImpl::AriaOwnsChangedWithCleanLayout(Node* node) {
   CHECK(relation_cache_);
-  if (AXObject* obj = GetOrCreate(node)) {
+  if (AXObject* obj = Get(node)) {
     relation_cache_->UpdateAriaOwnsWithCleanLayout(obj);
   }
 }
@@ -4341,7 +4321,7 @@
   if (!node)
     return false;
 
-  const AXObject* ax_object = GetOrCreate(const_cast<Node*>(node));
+  const AXObject* ax_object = Get(const_cast<Node*>(node));
   return ax_object && ax_object->IsTextField();
 }
 
@@ -4369,7 +4349,6 @@
 
 bool AXObjectCacheImpl::IsImmediateProcessingRequiredForEvent(
     const ui::AXEvent& event) const {
-  DCHECK(serialize_post_lifecycle_);
   if (last_serialization_timestamp_ == kSerializeAtNextOpportunity) {
     return true;  // Already scheduled for immediate mode.
   }
@@ -4511,8 +4490,6 @@
 }
 
 void AXObjectCacheImpl::ScheduleImmediateSerialization() {
-  DCHECK(serialize_post_lifecycle_);
-
   // This makes sure that we'll serialize at the next available opportunity.
   last_serialization_timestamp_ = kSerializeAtNextOpportunity;
 
@@ -4549,28 +4526,17 @@
   for (auto agent : agents_)
     agent->AXEventFired(obj, event_type);
 
-  if (serialize_post_lifecycle_) {
-    AddEventToSerializationQueue(event,
-                                 IsImmediateProcessingRequiredForEvent(event));
+  AddEventToSerializationQueue(event,
+                               IsImmediateProcessingRequiredForEvent(event));
 
-    // TODO(aleventhal) This is for web tests only, in order to record MarkDirty
-    // events. Is there a way to avoid these calls for normal browsing?
-    // Maybe we should use dependency injection from AccessibilityController.
-    if (auto* client = GetWebLocalFrameClient()) {
-      client->HandleWebAccessibilityEventForTest(event);
-    }
-  } else {
-    // legacy mode, go through RAI again!
-    if (auto* client = GetWebLocalFrameClient()) {
-      client->PostAccessibilityEvent(event);
-    }
+  // TODO(aleventhal) This is for web tests only, in order to record MarkDirty
+  // events. Is there a way to avoid these calls for normal browsing?
+  // Maybe we should use dependency injection from AccessibilityController.
+  if (auto* client = GetWebLocalFrameClient()) {
+    client->HandleWebAccessibilityEventForTest(event);
   }
 }
 
-void AXObjectCacheImpl::EnsureMarkDirtyWithCleanLayout(Node* node) {
-  MarkAXObjectDirtyWithCleanLayout(GetOrCreate(node));
-}
-
 void AXObjectCacheImpl::MarkAXObjectDirtyWithCleanLayoutHelper(
     AXObject* obj,
     bool subtree,
@@ -4597,23 +4563,17 @@
   // events. Is there a way to avoid these calls for normal browsing?
   // Maybe we should use dependency injection from AccessibilityController.
   if (auto* client = GetWebLocalFrameClient()) {
-    if (serialize_post_lifecycle_) {
-      client->HandleWebAccessibilityEventForTest(
-          WebAXObject(obj), "MarkDirty", std::vector<ui::AXEventIntent>());
-    } else {
-      client->NotifyWebAXObjectMarkedDirty(WebAXObject(obj));
-    }
+    client->HandleWebAccessibilityEventForTest(
+        WebAXObject(obj), "MarkDirty", std::vector<ui::AXEventIntent>());
   }
 
-  if (serialize_post_lifecycle_) {
-    // It's important for the user to have access to any changes to the
-    // currently focused object, so schedule serializations immediately if that
-    // object changes. The root is an exception because it often has focus while
-    // the page is loading. In that case the event type is used as the signal
-    // (see IsImmediateProcessingRequiredForEvent()).
-    if (obj != Root() && obj->IsFocused()) {
-      ScheduleImmediateSerialization();
-    }
+  // It's important for the user to have access to any changes to the
+  // currently focused object, so schedule serializations immediately if that
+  // object changes. The root is an exception because it often has focus while
+  // the page is loading. In that case the event type is used as the signal
+  // (see IsImmediateProcessingRequiredForEvent()).
+  if (obj != Root() && obj->IsFocused()) {
+    ScheduleImmediateSerialization();
   }
 
   std::vector<ui::AXEventIntent> event_intents;
@@ -4773,8 +4733,7 @@
 // partial subtrees and mid-tree object removal directly when they occur.
 AXObject* AXObjectCacheImpl::RestoreParentOrPrune(AXObject* child) {
   CHECK(child->GetNode()) << "Cannot restore parents of nodeless objects.";
-  AXObject* parent =
-      SafeGet(LayoutTreeBuilderTraversal::Parent(*child->GetNode()));
+  AXObject* parent = Get(LayoutTreeBuilderTraversal::Parent(*child->GetNode()));
   if (parent) {
     child->SetParent(parent);
   } else {
@@ -4979,12 +4938,6 @@
     bool& had_end_of_test_event,
     bool& had_load_complete_messages,
     bool& need_to_send_location_changes) {
-  // TODO(accessibility) Remove this once non-postlifecycle serialization code
-  // is completely removed, as it is redundant with other calls.
-  if (!serialize_post_lifecycle_) {
-    CheckTreeIsUpdated();
-  }
-
   // Make a copy of the events, because it's possible that
   // actions inside this loop will cause more events to be
   // queued up.
@@ -5228,8 +5181,8 @@
   int start_offset = start_pos.ComputeOffsetInContainerNode();
   int end_offset = end_pos.ComputeOffsetInContainerNode();
 
-  AXObject* start_obj = SafeGet(start_pos.ComputeContainerNode());
-  AXObject* end_obj = SafeGet(end_pos.ComputeContainerNode());
+  AXObject* start_obj = Get(start_pos.ComputeContainerNode());
+  AXObject* end_obj = Get(end_pos.ComputeContainerNode());
   if (!start_obj || !end_obj) {
     return;
   }
@@ -5257,7 +5210,7 @@
 
 void AXObjectCacheImpl::HandleEditableTextContentChangedWithCleanLayout(
     Node* node) {
-  AXObject* obj = GetOrCreate(node);
+  AXObject* obj = Get(node);
   if (obj) {
     obj = obj->GetTextFieldAncestor();
   }
@@ -5352,8 +5305,9 @@
 
 void AXObjectCacheImpl::HandleUpdateActiveMenuOptionWithCleanLayout(
     Node* menu_list) {
-  if (AXMenuList* ax_menu_list = DynamicTo<AXMenuList>(GetOrCreate(menu_list)))
+  if (AXMenuList* ax_menu_list = DynamicTo<AXMenuList>(Get(menu_list))) {
     ax_menu_list->DidUpdateActiveOption();
+  }
 }
 
 void AXObjectCacheImpl::DidShowMenuListPopup(LayoutObject* menu_list) {
@@ -5489,7 +5443,7 @@
   SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
   ProcessDeferredAccessibilityEvents(GetDocument(), /*force*/ true);
   ScopedFreezeAXCache scoped_freeze_cache(*this);
-  AXObject* obj = SafeGet(node);
+  AXObject* obj = Get(node);
   return AXObject::ARIARoleName(obj ? obj->RoleValue()
                                     : ax::mojom::blink::Role::kUnknown);
 }
@@ -5499,7 +5453,7 @@
   SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
   ProcessDeferredAccessibilityEvents(GetDocument(), /*force*/ true);
   ScopedFreezeAXCache scoped_freeze_cache(*this);
-  AXObject* obj = SafeGet(node);
+  AXObject* obj = Get(node);
   return obj ? obj->ComputedName() : "";
 }
 
@@ -5523,11 +5477,11 @@
                                               const PhysicalRect& rect) {
   SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
 
-  AXObject* obj = GetOrCreate(element);
+  AXObject* obj = Get(element);
   if (!obj)
     return;
 
-  AXObject* ax_canvas = GetOrCreate(canvas);
+  AXObject* ax_canvas = Get(canvas);
   if (!ax_canvas)
     return;
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
index 7b42ad9..54a5b5a1 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
@@ -165,14 +165,6 @@
       // Already frozen.
       return;
     }
-    // TODO(crbug.com/1477047): Remove this case once post lifecycle
-    // serialization is the only remaining code path. It's unclear why the
-    // document lifecycle check is necessary but this is short-lived code.
-    if (!serialize_post_lifecycle_ && frozen_count_ == 1 &&
-        GetDocument().Lifecycle().GetState() <
-            DocumentLifecycle::kPrePaintClean) {
-      UpdateAXForAllDocuments();
-    }
     ax_tree_source_->Freeze();
     CHECK(FocusedObject());
   }
@@ -386,10 +378,6 @@
   // its parent, it will be set as the new parent.
   AXObject* Get(const LayoutObject*, AXObject* parent_for_repair = nullptr);
 
-  // Get an AXObject* for a node.
-  // TODO(accessibility) This is the same as Get(Node*), and should be removed.
-  AXObject* SafeGet(const Node* node);
-
   // Return true if the object is still part of the tree, meaning that ancestors
   // exist or can be repaired all the way to the root.
   bool IsStillInTree(AXObject*);
@@ -442,11 +430,6 @@
                            const AriaNotificationOptions*) override;
 
   void PostNotification(const LayoutObject*, ax::mojom::blink::Event);
-  // Creates object if necessary.
-  void EnsurePostNotification(Node*, ax::mojom::blink::Event);
-  void EnsureMarkDirtyWithCleanLayout(Node*);
-  // Does not create object.
-  // TODO(accessibility) Find out if we can merge with EnsurePostNotification().
   void PostNotification(Node*, ax::mojom::blink::Event);
   void PostNotification(AXObject*, ax::mojom::blink::Event);
 
@@ -869,7 +852,6 @@
   Member<Document> popup_document_;
 
   ui::AXMode ax_mode_;
-  const bool serialize_post_lifecycle_;
 
   HeapHashMap<AXID, Member<AXObject>> objects_;
   HeapHashMap<Member<AccessibleNode>, AXID> accessible_node_mapping_;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
index c4f9cd6..e316433 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
@@ -1186,7 +1186,7 @@
 
   AXObject* ax_select = GetAXObjectByElementId("sel");
   ax_select->SetNeedsToUpdateChildren();
-  ax_select->UpdateChildrenIfNecessary();
+  GetAXObjectCache().UpdateAXForAllDocuments();
 
   ASSERT_EQ(
       ax_select->FirstChildIncludingIgnored()->ChildCountIncludingIgnored(), 1);
@@ -1346,7 +1346,7 @@
   Node* p2_text = p2->firstChild();
 
   auto AssertInertReasons = [&](Node* node, AXIgnoredReason expectation) {
-    AXObject* object = GetAXObjectCache().GetOrCreate(node);
+    AXObject* object = GetAXObjectCache().Get(node);
     ASSERT_NE(object, nullptr);
     AXObject::IgnoredReasons reasons;
     ASSERT_TRUE(object->ComputeIsInert(&reasons));
@@ -1354,7 +1354,7 @@
     ASSERT_EQ(reasons[0].reason, expectation);
   };
   auto AssertNotInert = [&](Node* node) {
-    AXObject* object = GetAXObjectCache().GetOrCreate(node);
+    AXObject* object = GetAXObjectCache().Get(node);
     ASSERT_NE(object, nullptr);
     AXObject::IgnoredReasons reasons;
     ASSERT_FALSE(object->ComputeIsInert(&reasons));
@@ -1503,13 +1503,13 @@
   Element* element = document.QuerySelector(AtomicString("main"));
   while (element) {
     Node* node = element->firstChild();
-    AXObject* ax_node = GetAXObjectCache().GetOrCreate(node);
+    AXObject* ax_node = GetAXObjectCache().Get(node);
 
     // The text indicates the expected inert root, which is the nearest HTML
     // element ancestor with the 'inert' attribute.
     AtomicString selector(node->textContent().Impl());
     Element* inert_root = document.QuerySelector(selector);
-    AXObject* ax_inert_root = GetAXObjectCache().GetOrCreate(inert_root);
+    AXObject* ax_inert_root = GetAXObjectCache().Get(inert_root);
 
     AXObject::IgnoredReasons reasons;
     ASSERT_TRUE(ax_node->ComputeIsInert(&reasons));
diff --git a/third_party/blink/renderer/modules/accessibility/ax_position.cc b/third_party/blink/renderer/modules/accessibility/ax_position.cc
index f04949ac..1c8905c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_position.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_position.cc
@@ -190,7 +190,7 @@
   const Position& parent_anchored_position = position.ToOffsetInAnchor();
   const Node* container_node = parent_anchored_position.AnchorNode();
   DCHECK(container_node);
-  const AXObject* container = ax_object_cache_impl->GetOrCreate(container_node);
+  const AXObject* container = ax_object_cache_impl->Get(container_node);
   if (!container)
     return {};
 
@@ -310,8 +310,7 @@
         container->ChildCountIncludingIgnored();
 
     } else {
-      const AXObject* ax_child =
-          ax_object_cache_impl->GetOrCreate(node_after_position);
+      const AXObject* ax_child = ax_object_cache_impl->Get(node_after_position);
       // |ax_child| might be nullptr because not all DOM nodes can have AX
       // objects. For example, the "head" element has no corresponding AX
       // object.
@@ -812,8 +811,7 @@
     return {};
 
   auto& ax_object_cache_impl = container->AXObjectCache();
-  const AXObject* new_container =
-      ax_object_cache_impl.GetOrCreate(container_node);
+  const AXObject* new_container = ax_object_cache_impl.Get(container_node);
   DCHECK(new_container);
   if (!new_container)
     return {};
@@ -1070,8 +1068,7 @@
       const Node* next_node = &child_node;
       while ((next_node = NodeTraversal::NextIncludingPseudo(*next_node,
                                                              container_node))) {
-        const AXObject* next_object =
-            ax_object_cache_impl->GetOrCreate(next_node);
+        const AXObject* next_object = ax_object_cache_impl->Get(next_node);
         if (next_object && next_object->AccessibilityIsIncludedInTree())
           return next_object;
       }
@@ -1089,7 +1086,7 @@
                   *previous_node, container_node)) &&
              previous_node != container_node) {
         const AXObject* previous_object =
-            ax_object_cache_impl->GetOrCreate(previous_node);
+            ax_object_cache_impl->Get(previous_node);
         if (previous_object && previous_object->AccessibilityIsIncludedInTree())
           return previous_object;
       }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
index a08bb5c..0b17c3f8 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
@@ -723,7 +723,7 @@
 #if DCHECK_IS_ON()
   if (obj)
     DCHECK(!obj->IsDetached());
-  AXObject* obj_for_node = object_cache_->SafeGet(node);
+  AXObject* obj_for_node = object_cache_->Get(node);
   DCHECK(!obj || obj_for_node == obj)
       << "Object and node did not match:"
       << "\n* node = " << node << "\n* obj = " << obj->ToString(true, true)
diff --git a/third_party/blink/renderer/modules/accessibility/ax_selection.cc b/third_party/blink/renderer/modules/accessibility/ax_selection.cc
index a04f02b..fafda14 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_selection.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_selection.cc
@@ -143,8 +143,7 @@
     return {};
 
   auto* ax_object_cache_impl = static_cast<AXObjectCacheImpl*>(ax_object_cache);
-  const AXObject* ax_text_control =
-      ax_object_cache_impl->GetOrCreate(&text_control);
+  const AXObject* ax_text_control = ax_object_cache_impl->Get(&text_control);
   DCHECK(ax_text_control);
 
   // We can't directly use "text_control.Selection()" because the selection it
diff --git a/third_party/blink/renderer/modules/accessibility/ax_sparse_attribute_setter.cc b/third_party/blink/renderer/modules/accessibility/ax_sparse_attribute_setter.cc
index 65f16e5..09a97390c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_sparse_attribute_setter.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_sparse_attribute_setter.cc
@@ -61,7 +61,7 @@
   if (!target)
     return;
 
-  AXObject* ax_target = object->AXObjectCache().GetOrCreate(target);
+  AXObject* ax_target = object->AXObjectCache().Get(target);
   if (!ax_target)
     return;
   if (attribute == ax::mojom::blink::IntAttribute::kActivedescendantId &&
@@ -91,8 +91,7 @@
   std::vector<int32_t> ax_ids;
 
   for (const auto& associated_element : *attr_associated_elements) {
-    AXObject* ax_element =
-        object->AXObjectCache().GetOrCreate(associated_element);
+    AXObject* ax_element = object->AXObjectCache().Get(associated_element);
     if (!ax_element)
       continue;
     if (!ax_element->AccessibilityIsIgnored())
@@ -248,7 +247,7 @@
   }
 
   Element* target = value.element();
-  AXObject* ax_target = ax_object_cache_->GetOrCreate(target);
+  AXObject* ax_target = ax_object_cache_->Get(target);
   if (!ax_target)
     return;
 
@@ -281,7 +280,7 @@
     AccessibleNode* accessible_node = relations.item(i);
     if (accessible_node) {
       Element* element = accessible_node->element();
-      AXObject* ax_element = ax_object_cache_->GetOrCreate(element);
+      AXObject* ax_element = ax_object_cache_->Get(element);
       if (ax_element && !ax_element->AccessibilityIsIgnored())
         ax_ids.push_back(ax_element->AXObjectID());
     }
diff --git a/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc b/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc
index 116ed658..397168d 100644
--- a/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc
+++ b/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc
@@ -549,7 +549,7 @@
   DocumentLifecycle::DisallowTransitionScope disallow_transition(
       document.Lifecycle());
 
-  AXObject* inspected_ax_object = cache.GetOrCreate(dom_node);
+  AXObject* inspected_ax_object = cache.Get(dom_node);
   *nodes = std::make_unique<protocol::Array<protocol::Accessibility::AXNode>>();
   if (inspected_ax_object) {
     (*nodes)->emplace_back(
@@ -574,12 +574,12 @@
     auto* shadow_root = DynamicTo<ShadowRoot>(dom_node);
     Node* parent_node = shadow_root ? &shadow_root->host()
                                     : FlatTreeTraversal::Parent(*dom_node);
-    parent_ax_object = cache.GetOrCreate(parent_node);
+    parent_ax_object = cache.Get(parent_node);
     while (parent_node && !parent_ax_object) {
       shadow_root = DynamicTo<ShadowRoot>(parent_node);
       parent_node = shadow_root ? &shadow_root->host()
                                 : FlatTreeTraversal::Parent(*parent_node);
-      parent_ax_object = cache.GetOrCreate(parent_node);
+      parent_ax_object = cache.Get(parent_node);
     }
   }
   if (!parent_ax_object)
@@ -884,7 +884,7 @@
   DocumentLifecycle::DisallowTransitionScope disallow_transition(
       document.Lifecycle());
 
-  AXObject* ax_object = cache.GetOrCreate(dom_node);
+  AXObject* ax_object = cache.Get(dom_node);
 
   ScopedFreezeAXCache freeze(cache);
 
@@ -1084,7 +1084,7 @@
 
   std::unique_ptr<protocol::Array<AXNode>> nodes =
       std::make_unique<protocol::Array<protocol::Accessibility::AXNode>>();
-  AXObject* root_ax_node = cache.GetOrCreate(root_dom_node);
+  AXObject* root_ax_node = cache.Get(root_dom_node);
 
   HeapVector<Member<AXObject>> reachable;
   if (root_ax_node)
diff --git a/third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.cc b/third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.cc
index f3e63d2..f721ce3d 100644
--- a/third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.cc
@@ -207,7 +207,7 @@
                                         HTMLElement& element) {
     element.setInnerHTML(String::FromUTF8(html_snippet));
     element.GetDocument().View()->UpdateAllLifecyclePhasesForTest();
-    AXObject* root = ax_object_cache_->GetOrCreate(&element);
+    AXObject* root = ax_object_cache_->Get(&element);
     if (!root || root->IsDetached())
       return {};
 
diff --git a/third_party/blink/renderer/modules/accessibility/testing/accessibility_test.cc b/third_party/blink/renderer/modules/accessibility/testing/accessibility_test.cc
index 75619d0c9..bbcc7d73 100644
--- a/third_party/blink/renderer/modules/accessibility/testing/accessibility_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/testing/accessibility_test.cc
@@ -33,19 +33,20 @@
 }
 
 AXObject* AccessibilityTest::GetAXObject(LayoutObject* layout_object) const {
-  return GetAXObjectCache().GetOrCreate(layout_object);
+  return GetAXObjectCache().Get(layout_object);
 }
 
 AXObject* AccessibilityTest::GetAXObject(const Node& node) const {
-  return GetAXObjectCache().GetOrCreate(&node);
+  return GetAXObjectCache().Get(&node);
 }
 
 AXObject* AccessibilityTest::GetAXRootObject() const {
-  return GetAXObjectCache().GetOrCreate(&GetLayoutView());
+  GetAXObjectCache().UpdateAXForAllDocuments();
+  return GetAXObjectCache().Root();
 }
 
 AXObject* AccessibilityTest::GetAXBodyObject() const {
-  return GetAXObjectCache().GetOrCreate(GetDocument().body());
+  return GetAXObjectCache().Get(GetDocument().body());
 }
 
 AXObject* AccessibilityTest::GetAXFocusedObject() const {
diff --git a/third_party/blink/renderer/modules/ad_auction/validate_blink_interest_group.cc b/third_party/blink/renderer/modules/ad_auction/validate_blink_interest_group.cc
index 9c608f3..2f8fe07e 100644
--- a/third_party/blink/renderer/modules/ad_auction/validate_blink_interest_group.cc
+++ b/third_party/blink/renderer/modules/ad_auction/validate_blink_interest_group.cc
@@ -96,7 +96,7 @@
 
   if (group.ads) {
     for (const auto& ad : group.ads.value()) {
-      size += ad->render_url.GetString().length();
+      size += ad->render_url.length();
       size += ad->size_group.length();
       size += ad->buyer_reporting_id.length();
       size += ad->buyer_and_seller_reporting_id.length();
@@ -112,7 +112,7 @@
 
   if (group.ad_components) {
     for (const auto& ad : group.ad_components.value()) {
-      size += ad->render_url.GetString().length();
+      size += ad->render_url.length();
       size += ad->size_group.length();
       size += ad->metadata.length();
       size += ad->ad_render_id.length();
@@ -267,7 +267,7 @@
 
   if (group.ads) {
     for (WTF::wtf_size_t i = 0; i < group.ads.value().size(); ++i) {
-      const KURL& render_url = group.ads.value()[i]->render_url;
+      const KURL& render_url = KURL(group.ads.value()[i]->render_url);
       if (!IsUrlAllowedForRenderUrls(render_url)) {
         error_field_name = String::Format("ads[%u].renderURL", i);
         error_field_value = render_url.GetString();
@@ -326,7 +326,7 @@
 
   if (group.ad_components) {
     for (WTF::wtf_size_t i = 0; i < group.ad_components.value().size(); ++i) {
-      const KURL& render_url = group.ad_components.value()[i]->render_url;
+      const KURL& render_url = KURL(group.ad_components.value()[i]->render_url);
       if (!IsUrlAllowedForRenderUrls(render_url)) {
         error_field_name = String::Format("adComponents[%u].renderURL", i);
         error_field_value = render_url.GetString();
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc
index 676d202..6ae5e8b 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -1085,7 +1085,7 @@
   DCHECK(cache);
   if (!cache->Root())
     return WebAXObject();  // Accessibility not yet active in this cache.
-  return WebAXObject(cache->GetOrCreate(document));
+  return WebAXObject(cache->Get(document));
 }
 
 // static
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
index a864d48..2d6f1f74 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -794,9 +794,20 @@
 
   AtomicString content_encoding =
       response.HttpHeaderField(http_names::kContentEncoding);
+  bool used_zstd = false;
   if (content_encoding.LowerASCII() == "zstd") {
     fetcher_->GetUseCounter().CountUse(WebFeature::kZstdContentEncoding);
+    used_zstd = true;
   }
+  // We need a current default task runner to obtain a UKM recorder, so if there
+  // is not one, do not record UKMs.
+  if (base::SequencedTaskRunner::HasCurrentDefault()) {
+    ukm::builders::SubresourceLoad_ZstdContentEncoding builder(
+        request.GetUkmSourceId());
+    builder.SetUsedZstd(used_zstd);
+    builder.Record(fetcher_->UkmRecorder());
+  }
+
   if (response.DidUseSharedDictionary()) {
     fetcher_->GetUseCounter().CountUse(WebFeature::kSharedDictionaryUsed);
     fetcher_->GetUseCounter().CountUse(
diff --git a/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.cc b/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.cc
index 9f13ec1..0d6c7f11 100644
--- a/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.cc
@@ -16,7 +16,6 @@
 
   network::mojom::TrustTokenParamsPtr out =
       network::mojom::TrustTokenParams::New();
-  out->version = in.version;
   out->operation = in.operation;
   out->refresh_policy = in.refresh_policy;
   out->sign_request_data = in.sign_request_data;
diff --git a/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.h b/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.h
index f5846141..93a5fea 100644
--- a/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.h
+++ b/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.h
@@ -9,14 +9,9 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/platform/web_common.h"
 
-namespace network {
-namespace mojom {
-namespace blink {
+namespace network::mojom::blink {
 class TrustTokenParams;
-
-}  // namespace blink
-}  // namespace mojom
-}  // namespace network
+}  // namespace network::mojom::blink
 
 namespace blink {
 
diff --git a/third_party/blink/web_tests/ChromeTestExpectations b/third_party/blink/web_tests/ChromeTestExpectations
index 861265e0..d5c23ea 100644
--- a/third_party/blink/web_tests/ChromeTestExpectations
+++ b/third_party/blink/web_tests/ChromeTestExpectations
@@ -583,6 +583,9 @@
 crbug.com/1507050 external/wpt/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-success-inline-classic.html [ Failure Pass ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/webdriver/tests/bidi/network/continue_response/invalid.py [ Timeout ]
+crbug.com/626703 external/wpt/webdriver/tests/bidi/network/continue_with_auth/invalid.py [ Timeout ]
+crbug.com/626703 external/wpt/webdriver/tests/bidi/network/fail_request/invalid.py [ 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 ]
 crbug.com/626703 external/wpt/css/css-page/page-orientation-on-square-001-print.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index a77a5e7..f537178 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1698,7 +1698,7 @@
       "--enable-features=FencedFramesDefaultMode,AggregationServiceMultipleCloudProviders:aws_cloud/https%3A%2F%2Fweb-platform.test%3A8444,FencedFramesEnforceFocus",
       "--disable-threaded-compositing", "--disable-threaded-animation"
     ],
-    "expires": "Jan 1, 2024"
+    "expires": "Jul 1, 2024"
   },
   {
     "prefix": "private-aggregation-developer-mode",
@@ -1715,7 +1715,7 @@
       "--enable-features=FencedFramesDefaultMode,AggregationServiceMultipleCloudProviders:aws_cloud/https%3A%2F%2Fweb-platform.test%3A8444/gcp_cloud/https%3A%2F%2Fwww1.web-platform.test%3A8444,PrivateAggregationApiBundledEnhancements,PrivateAggregationApiMultipleCloudProviders",
       "--disable-threaded-compositing", "--disable-threaded-animation"
     ],
-    "expires": "Jan 1, 2024"
+    "expires": "Jul 1, 2024"
   },
   {
     "prefix": "shared-storage-fenced-frame-mparch",
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 9c88ec4..58965ae39 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
@@ -4528,8 +4528,8 @@
      ]
     },
     "css-view-transitions": {
-     "document-element-detatched-crash.html": [
-      "3c5419a8102c2160ac82e69bf046408b2c542e41",
+     "document-element-detached-crash.html": [
+      "cfdf769695dc11864229d0f4fe5d1dfc3712aec7",
       [
        null,
        {}
@@ -80072,7 +80072,7 @@
       ]
      ],
      "background-clip-padding-box-with-border-radius.html": [
-      "8595caec7bb2d751dd87aebc777be84de6233af7",
+      "22d7bd9d297d25206834f93c78b27d9bc3b28039",
       [
        null,
        [
@@ -80081,7 +80081,23 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            29
+           ],
+           [
+            0,
+            80
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "background-clip_padding-box.html": [
@@ -106536,6 +106552,19 @@
        {}
       ]
      ],
+     "contain-inline-size-grid-stretches-auto-rows.html": [
+      "c168950929d58c2c7b0b4c4228f2185f8a7b4e88",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square-only.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "contain-inline-size-grid.html": [
       "5cd8bfc96e34e36ff1a340e9e09963df9b22ec89",
       [
@@ -108882,6 +108911,19 @@
        {}
       ]
      ],
+     "contain-size-grid-stretches-auto-rows.html": [
+      "10b84665aa91edc2245fae08c9ee259c8bc1863a",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square-only.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "contain-size-inline-block-001.html": [
       "41458550272f896d47e15210c82f4c22d386d9e0",
       [
@@ -141441,6 +141483,32 @@
         {}
        ]
       ],
+      "standalone-axis-size-008.html": [
+       "6ee4e77405b47c3de23c6fc19a25cec3d576511f",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "standalone-axis-size-009.html": [
+       "bbf585efd1ad7ca4aba112be57cd4e964ace94b6",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "subgrid-baseline-001.html": [
        "95469c0018e0a97786aff086c0cabaf081ced7b4",
        [
@@ -156387,6 +156455,45 @@
         {}
        ]
       ],
+      "clip-path-on-svg-003.svg": [
+       "01c4a2610e8cde9e8400fbc2a023ab936babd3ad",
+       [
+        null,
+        [
+         [
+          "/css/css-masking/clip-path-svg-content/reference/clip-path-square-002-ref.svg",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "clip-path-on-svg-004.svg": [
+       "62a020e558676f533add52c8de80518eb54e6041",
+       [
+        null,
+        [
+         [
+          "/css/css-masking/clip-path-svg-content/reference/clip-path-square-002-ref.svg",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "clip-path-on-svg-005.svg": [
+       "c39d2660280826b6ec2f579d011df3cc2a7f5572",
+       [
+        null,
+        [
+         [
+          "/css/css-masking/clip-path-svg-content/reference/clip-path-square-002-ref.svg",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "clip-path-on-use-001.svg": [
        "8a2cf81c51de6f62d498c3d2e4cfe7a50113a0f1",
        [
@@ -353630,7 +353737,7 @@
        []
       ],
       "trusted-bidding-signals.py": [
-       "a3c50f6ae9b41db2ef5e6cbdf1d04d4d5b165ca4",
+       "5ec487fcc904e876ebdf130b511f4c4780863395",
        []
       ],
       "trusted-scoring-signals.py": [
@@ -394135,15 +394242,39 @@
         ]
        },
        "conftest.py": [
-        "f7420c11a9355beddef50dfd2524c908656717a2",
+        "fb6dcc45dd779a1cc790a95607781b01254fa3b3",
         []
        ],
+       "continue_request": {
+        "__init__.py": [
+         "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+         []
+        ]
+       },
+       "continue_response": {
+        "__init__.py": [
+         "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+         []
+        ]
+       },
+       "continue_with_auth": {
+        "__init__.py": [
+         "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+         []
+        ]
+       },
        "fail_request": {
         "__init__.py": [
          "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
          []
         ]
        },
+       "provide_response": {
+        "__init__.py": [
+         "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+         []
+        ]
+       },
        "remove_intercept": {
         "__init__.py": [
          "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
@@ -394827,7 +394958,7 @@
         []
        ],
        "headers.py": [
-        "71c4901c44522847ec93d5fc5b57c4720fadbce9",
+        "cb8d18d964416a5db9f98bcc273762e47bc10c81",
         []
        ],
        "must-revalidate.py": [
@@ -471477,7 +471608,7 @@
       ]
      ],
      "scroll-behavior-subframe-root.html": [
-      "27f9d2f408ff8cfbd11f2f452fc9e8f92b7b269c",
+      "2687d6eb5cadfbe8c3d47073fd5ae89eff7ac318",
       [
        null,
        {
@@ -471486,7 +471617,7 @@
       ]
      ],
      "scroll-behavior-subframe-window.html": [
-      "74c6fe7510a8169a9e6427e83f0f304921d4046d",
+      "3113cbc88e3a2213de0f85803d16ee6a2c30bdf2",
       [
        null,
        {
@@ -499797,6 +499928,13 @@
       {}
      ]
     ],
+    "report-event-reserved-event.https.html": [
+     "0a541bb0c5c8f3e19c609b7e2411c89fea103764",
+     [
+      null,
+      {}
+     ]
+    ],
     "resize-lock-input.https.html": [
      "261c9a737eff4b85723214b7f4735b8fe9423a65",
      [
@@ -518283,7 +518421,7 @@
       ]
      ],
      "trusted-bidding-signals.https.window.js": [
-      "3a3283ac18e552ae8086ad9e0a32c9ee5ac50756",
+      "9799af6ac1fbe1a3f7db8ce4230137d272cc8307",
       [
        "fledge/tentative/trusted-bidding-signals.https.window.html?1-5",
        {
@@ -701855,7 +701993,7 @@
        },
        "before_request_sent": {
         "before_request_sent.py": [
-         "bf94d58f1b1fede1698f9759649ba0a0c42428e6",
+         "c92337e5075bf0b4eb6fff85dcc550d181f6588e",
          [
           null,
           {}
@@ -701864,7 +702002,34 @@
        },
        "combined": {
         "network_events.py": [
-         "d3f0f7ddd3349d81306e8b3d3c9fa0244f8c4a92",
+         "7b6d99727ae116587737abd0c02fd908a6690de9",
+         [
+          null,
+          {}
+         ]
+        ]
+       },
+       "continue_request": {
+        "invalid.py": [
+         "cdc66cfaabb20dde4bd3e3caa0c8c8d948fe87f6",
+         [
+          null,
+          {}
+         ]
+        ]
+       },
+       "continue_response": {
+        "invalid.py": [
+         "b8e1c08e0125fd16d405a9d07848db8a25adc47a",
+         [
+          null,
+          {}
+         ]
+        ]
+       },
+       "continue_with_auth": {
+        "invalid.py": [
+         "ea188cbde88d1874bf3401a6507ef641144045fc",
          [
           null,
           {}
@@ -701873,7 +702038,16 @@
        },
        "fail_request": {
         "invalid.py": [
-         "60371d58ed3f36fbea051a1ae47764c326e369f2",
+         "ead87c1a376ec403c24ca8cd6b4b04bf2a1de0fa",
+         [
+          null,
+          {}
+         ]
+        ]
+       },
+       "provide_response": {
+        "invalid.py": [
+         "eec118a692df3a27d89236d7e2862dd2aca9f35d",
          [
           null,
           {}
@@ -701900,7 +702074,7 @@
        },
        "response_completed": {
         "response_completed.py": [
-         "b54b9699099eec2466666c4360b5a9e90571f87b",
+         "b9b4ae727e56d5505c3221eaa7800584371f1123",
          [
           null,
           {}
@@ -701923,7 +702097,7 @@
        },
        "response_started": {
         "response_started.py": [
-         "c4e9c653a7867f2f0e1061cd940607665aae6320",
+         "dec743e175559b7068a44f043bce4951a17efcaa",
          [
           null,
           {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-clip-padding-box-with-border-radius.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-clip-padding-box-with-border-radius.html
index 8595cae..22d7bd9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-clip-padding-box-with-border-radius.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-clip-padding-box-with-border-radius.html
@@ -8,6 +8,7 @@
 <link rel="help" href="http://www.w3.org/TR/css3-background/#the-background-clip" />
 <link rel="help" href="http://www.w3.org/TR/css3-background/#corner-shaping" />
 <link rel="match" href="reference/background-clip-padding-box-with-border-radius-ref.html" />
+<meta name="fuzzy" content="maxDifference=0-29;totalPixels=0-80" />
 <meta name="assert" content="Backgrounds clipped to the padding box should follow the padding box curve, which should be equal to the outer border radius minus the corresponding border thickness." />
 <style>
 div {
@@ -76,4 +77,4 @@
 </div>
 
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/contain-inline-size-grid-stretches-auto-rows.html b/third_party/blink/web_tests/external/wpt/css/css-contain/contain-inline-size-grid-stretches-auto-rows.html
new file mode 100644
index 0000000..c1689509
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/contain-inline-size-grid-stretches-auto-rows.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="author" title="Sammy Gill" href="mailto:sammy.gill@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-grid-2/#algo-stretch">
+<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html">
+<meta name="assert" content="grid with inline-size containment, and min-height will still distribute extra space to auto rows">
+<style>
+grid {
+    display: grid;
+    min-height: 100px;
+    grid-template-rows: auto;
+    contain: inline-size;
+}
+.absolute {
+    position: absolute;
+}
+.align-end {
+    align-self: end;
+}
+.item {
+    width: 100px;
+    height: 50px;
+    background-color: green;
+}
+</style>
+</head>
+<body>
+    <p>Test passes if there is a filled green square.</p>
+    <grid>
+        <div class="absolute item"></div>
+        <div class="item align-end"></div>
+    </grid>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/contain-size-grid-stretches-auto-rows.html b/third_party/blink/web_tests/external/wpt/css/css-contain/contain-size-grid-stretches-auto-rows.html
new file mode 100644
index 0000000..10b84665
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/contain-size-grid-stretches-auto-rows.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="author" title="Sammy Gill" href="mailto:sammy.gill@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-contain/#size-containment">
+<link rel="help" href="https://drafts.csswg.org/css-grid-2/#algo-stretch">
+<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html">
+<meta name="assert" content="grid with size containment, and min-height will still distribute extra space to auto rows">
+<style>
+grid {
+    display: grid;
+    min-height: 100px;
+    grid-template-rows: auto;
+    contain: size;
+}
+.absolute {
+    position: absolute;
+}
+.align-end {
+    align-self: end;
+}
+.item {
+    width: 100px;
+    height: 50px;
+    background-color: green;
+}
+</style>
+</head>
+<body>
+    <p>Test passes if there is a filled green square.</p>
+    <grid>
+        <div class="absolute item"></div>
+        <div class="item align-end"></div>
+    </grid>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/standalone-axis-size-008.html b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/standalone-axis-size-008.html
new file mode 100644
index 0000000..6ee4e77
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/standalone-axis-size-008.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Test: Subgrid contribution size on standalone axis</title>
+<link rel="author" title="Ethan Jimenez" href="mailto:ethavar@microsoft.com">
+<link rel="help" href="https://drafts.csswg.org/css-grid-2/#subgrid-box-alignment">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+.grid {
+  background: green;
+  display: inline-grid;
+  grid-template-columns: 100px;
+}
+.subgrid {
+  height: 100px;
+  display: grid;
+  grid-template-columns: subgrid;
+}
+.h200 { height: 200px }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="grid">
+  <div class="subgrid">
+    <div class="h200"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/standalone-axis-size-009.html b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/standalone-axis-size-009.html
new file mode 100644
index 0000000..bbf585e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/standalone-axis-size-009.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Test: Subgrid contribution size on standalone axis</title>
+<link rel="author" title="Ethan Jimenez" href="mailto:ethavar@microsoft.com">
+<link rel="help" href="https://drafts.csswg.org/css-grid-2/#subgrid-box-alignment">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+.grid {
+  display: inline-grid;
+  grid-template: minmax(auto, 100px) / 100px;
+}
+.subgrid {
+  height: 100%;
+  display: grid;
+  background: green;
+  grid-template-columns: subgrid;
+}
+.h200 { height: 200px }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="grid">
+  <div class="subgrid">
+    <div class="h200"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path-svg-content/clip-path-on-svg-003.svg b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path-svg-content/clip-path-on-svg-003.svg
new file mode 100644
index 0000000..01c4a26
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path-svg-content/clip-path-on-svg-003.svg
@@ -0,0 +1,24 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml" clip-path="url(#clip1)">
+<g id="testmeta">
+	<title>CSS Masking: Clipped clipPath on root &lt;svg></title>
+	<html:link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#svg-clipping-paths"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#ClipPathElement"/>
+	<html:link rel="match" href="reference/clip-path-square-002-ref.svg" />
+	<metadata class="flags">svg</metadata>
+	<desc class="assert">A clipPath element can be applied to a root
+	&lt;svg> element. This clipPath element can be clipped itself. You
+	should see a green square.</desc>
+</g>
+<clipPath id="clip2">
+	<rect x="50" y="50" width="100" height="100"/>
+</clipPath>
+<clipPath id="clip1">
+	<rect x="25" y="25" width="150" height="150"/>
+</clipPath>
+<rect width="200" height="200" fill="green"/>
+<script>
+    var svg = document.documentElement;
+    svg.setAttribute('clip-path','url(#clip2)');
+</script>
+</svg>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path-svg-content/clip-path-on-svg-004.svg b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path-svg-content/clip-path-on-svg-004.svg
new file mode 100644
index 0000000..62a020e5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path-svg-content/clip-path-on-svg-004.svg
@@ -0,0 +1,22 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml" clip-path="url(#clip1)">
+<g id="testmeta">
+	<title>CSS Masking: Clipped clipPath on root &lt;svg></title>
+	<html:link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#svg-clipping-paths"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#ClipPathElement"/>
+	<html:link rel="match" href="reference/clip-path-square-002-ref.svg" />
+	<metadata class="flags">svg</metadata>
+	<desc class="assert">A clipPath element can be applied to a root
+	&lt;svg> element. This clipPath element can be clipped itself. You
+	should see a green square.</desc>
+</g>
+<clipPath id="clip2">
+    <rect width="150" height="150"/>
+</clipPath>
+<clipPath id="clip1">
+    <rect x="50" y="50" width="175" height="175"/>
+</clipPath>
+<svg clip-path="url(#clip2)">
+    <rect width="200" height="200" fill="green"/>
+</svg>
+</svg>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path-svg-content/clip-path-on-svg-005.svg b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path-svg-content/clip-path-on-svg-005.svg
new file mode 100644
index 0000000..c39d266
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path-svg-content/clip-path-on-svg-005.svg
@@ -0,0 +1,16 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml" style="-webkit-clip-path: url(#clip1)">
+<g id="testmeta">
+	<title>CSS Masking: clipPath on root &lt;svg></title>
+	<html:link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#svg-clipping-paths"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#ClipPathElement"/>
+	<html:link rel="match" href="reference/clip-path-square-002-ref.svg" />
+	<metadata class="flags">svg</metadata>
+	<desc class="assert">A clipPath element can be applied to a root
+	&lt;svg> element. You should see a green square.</desc>
+</g>
+<clipPath id="clip1">
+	<rect x="50" y="50" width="100" height="100"/>
+</clipPath>
+<rect width="200" height="200" fill="green"/>
+</svg>
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/scroll-behavior-subframe-root.html b/third_party/blink/web_tests/external/wpt/css/cssom-view/scroll-behavior-subframe-root.html
index 27f9d2f..2687d6e 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom-view/scroll-behavior-subframe-root.html
+++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/scroll-behavior-subframe-root.html
@@ -31,15 +31,15 @@
 </html>">
 </iframe>
 <script>
-  promise_test(async () => {
-    await waitForCompositorReady();
-  }, "Make sure the page is ready for animation.");
-
   var iframeLoadTest = async_test("iframe loaded");
   var scrollingElement, styledElement, elementToReveal;
   var elementToRevealLeft = 500;
   var elementToRevealTop = 250;
   iframeNode.addEventListener("load", iframeLoadTest.step_func_done(() => {
+    promise_test(async () => {
+      await waitForCompositorReady();
+    }, "Make sure the page is ready for animation.");
+
     scrollingElement = iframeNode.contentDocument.scrollingElement;
     styledElement = iframeNode.contentDocument.documentElement;
     elementToReveal = iframeNode.contentDocument.getElementById("elementToReveal");
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/scroll-behavior-subframe-window.html b/third_party/blink/web_tests/external/wpt/css/cssom-view/scroll-behavior-subframe-window.html
index 74c6fe75..3113cbc 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom-view/scroll-behavior-subframe-window.html
+++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/scroll-behavior-subframe-window.html
@@ -32,15 +32,15 @@
 </html>">
 </iframe>
 <script>
-  promise_test(async () => {
-    await waitForCompositorReady();
-  }, "Make sure the page is ready for animation.");
-
   var iframeLoadTest = async_test("iframe loaded");
   var scrollingWindow, styledElement, elementToReveal;
   var elementToRevealLeft = 500;
   var elementToRevealTop = 250;
   iframeNode.addEventListener("load", iframeLoadTest.step_func_done(() => {
+    promise_test(async () => {
+      await waitForCompositorReady();
+    }, "Make sure the page is ready for animation.");
+
     scrollingWindow = iframeNode.contentWindow;
     styledElement = iframeNode.contentDocument.documentElement;
     elementToReveal = iframeNode.contentDocument.getElementById("elementToReveal");
diff --git a/third_party/blink/web_tests/external/wpt/fenced-frame/report-event-reserved-event.https.html b/third_party/blink/web_tests/external/wpt/fenced-frame/report-event-reserved-event.https.html
new file mode 100644
index 0000000..0a541bb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fenced-frame/report-event-reserved-event.https.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<title>Test window.fence.reportEvent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/utils.js"></script>
+
+<body>
+<script>
+promise_test(async(t) => {
+  const fencedframe = await attachFencedFrameContext({
+    generator_api: "fledge",
+    automatic_beacon: true,
+  });
+  const new_url = new URL("resources/dummy.html", location.href);
+  const beacon_data = "This is the beacon data!";
+  const beacon_type = "reserved.top_navigation_commit";
+
+  await fencedframe.execute(
+    (new_url, beacon_data, beacon_type) => {
+      let beacon_event = {
+        eventType: beacon_type,
+        eventData: beacon_data,
+        destination: ["buyer", "seller"],
+      };
+      window.fence.reportEvent(beacon_event);
+    },
+    [new_url, beacon_data, beacon_type]
+  );
+
+  const timeout = new Promise(resolve => t.step_timeout(resolve, 1000));
+  const result = await Promise.race(
+      [nextAutomaticBeacon(beacon_type, beacon_data), timeout]);
+  assert_true(typeof result === "undefined",
+      "A beacon should not have been sent.");
+
+}, 'Reserved events should not be callable through reportEvent()');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/visual-viewport/viewport-scrollbars-cause-resize-in-iframe.html b/third_party/blink/web_tests/external/wpt/visual-viewport/viewport-scrollbars-cause-resize-in-iframe.html
new file mode 100644
index 0000000..ce9ec32
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/visual-viewport/viewport-scrollbars-cause-resize-in-iframe.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<title>Viewport: Scrollbars Cause Resize</title>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, minimum-scale=1">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="viewport_support.js"></script>
+<h1>Viewport: Scrollbars Cause Resize In IFrame</h1>
+<h4>
+    Test Description: This test checks that the appearance of classic
+    scrollbars will cause a resize event to be fired at window.visualViewport in iframe.
+</h4>
+<iframe srcdoc="<p>Hello, world!</p>"></iframe>
+<script>
+  async_test(t => {
+    window.onload = () => {
+      const iframe = document.querySelector("iframe");
+
+      let resize_event_count = 0;
+      iframe.contentWindow.visualViewport.addEventListener("resize", () => {
+        resize_event_count++;
+      });
+
+      const originalVisualViewportWidth = iframe.contentWindow.visualViewport.width;
+
+      iframe.contentDocument.body.style.height = "10000px";
+      // Force layout to queue a resize event at this moment.
+      iframe.contentDocument.body.clientWidth;
+
+      requestAnimationFrame(t.step_func_done(() => {
+        // it's uncontrollable whether the test will run
+        // with classic or overlay scrollbars in the case of Mac,
+        // so the check has to be conditional.
+        const width_changed = iframe.contentWindow.visualViewport.width !== originalVisualViewportWidth;
+        assert_equals(resize_event_count, width_changed ? 1 : 0);
+      }));
+    }
+  }, "the appearance of classic scrollbars will fire a resize event" +
+      "at window.visualViewport in iframe");
+</script>
+<div id="log"></div>
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/before_request_sent/before_request_sent.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/before_request_sent/before_request_sent.py
index bf94d58..c92337e 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/before_request_sent/before_request_sent.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/before_request_sent/before_request_sent.py
@@ -61,6 +61,39 @@
 
 
 @pytest.mark.asyncio
+async def test_iframe_load(
+    bidi_session,
+    top_context,
+    setup_network_test,
+    test_page,
+    test_page_same_origin_frame,
+):
+    network_events = await setup_network_test(events=[BEFORE_REQUEST_SENT_EVENT])
+    events = network_events[BEFORE_REQUEST_SENT_EVENT]
+
+    await bidi_session.browsing_context.navigate(
+        context=top_context["context"],
+        url=test_page_same_origin_frame,
+        wait="complete",
+    )
+
+    contexts = await bidi_session.browsing_context.get_tree(root=top_context["context"])
+    frame_context = contexts[0]["children"][0]
+
+    assert len(events) == 2
+    assert_before_request_sent_event(
+        events[0],
+        expected_request={"url": test_page_same_origin_frame},
+        context=top_context["context"],
+    )
+    assert_before_request_sent_event(
+        events[1],
+        expected_request={"url": test_page},
+        context=frame_context["context"],
+    )
+
+
+@pytest.mark.asyncio
 async def test_load_page_twice(
     bidi_session, top_context, wait_for_event, url, setup_network_test, wait_for_future_safe
 ):
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/combined/network_events.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/combined/network_events.py
index d3f0f7dd..7b6d997 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/combined/network_events.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/combined/network_events.py
@@ -14,6 +14,109 @@
 
 
 @pytest.mark.asyncio
+async def test_iframe_navigation_request(
+    bidi_session,
+    top_context,
+    subscribe_events,
+    setup_network_test,
+    inline,
+    test_page,
+    test_page_cross_origin,
+    test_page_same_origin_frame,
+):
+    network_events = await setup_network_test(
+        events=[
+            BEFORE_REQUEST_SENT_EVENT,
+            RESPONSE_STARTED_EVENT,
+            RESPONSE_COMPLETED_EVENT,
+        ],
+        contexts=[top_context["context"]],
+    )
+
+    navigation_events = []
+
+    async def on_event(method, data):
+        navigation_events.append(data)
+
+    remove_listener = bidi_session.add_event_listener(
+        "browsingContext.navigationStarted", on_event
+    )
+    await subscribe_events(events=["browsingContext.navigationStarted"])
+
+    result = await bidi_session.browsing_context.navigate(
+        context=top_context["context"], url=test_page_same_origin_frame, wait="complete"
+    )
+
+    # Get the frame_context loaded in top_context
+    contexts = await bidi_session.browsing_context.get_tree(root=top_context["context"])
+    assert len(contexts[0]["children"]) == 1
+    frame_context = contexts[0]["children"][0]
+
+    assert len(navigation_events) == 2
+    assert len(network_events[BEFORE_REQUEST_SENT_EVENT]) == 2
+    assert len(network_events[RESPONSE_STARTED_EVENT]) == 2
+    assert len(network_events[RESPONSE_COMPLETED_EVENT]) == 2
+
+    # Check that 2 distinct navigations were captured, for the expected contexts
+    assert navigation_events[0]["navigation"] == result["navigation"]
+    assert navigation_events[0]["context"] == top_context["context"]
+    assert navigation_events[1]["navigation"] != result["navigation"]
+    assert navigation_events[1]["context"] == frame_context["context"]
+
+    # Helper to assert the 3 main network events for this test
+    def assert_events(event_index, url, context, navigation):
+        expected_request = {"method": "GET", "url": url}
+        expected_response = {"url": url}
+        assert_before_request_sent_event(
+            network_events[BEFORE_REQUEST_SENT_EVENT][event_index],
+            expected_request=expected_request,
+            context=context,
+            navigation=navigation,
+        )
+        assert_response_event(
+            network_events[RESPONSE_STARTED_EVENT][event_index],
+            expected_response=expected_response,
+            context=context,
+            navigation=navigation,
+        )
+        assert_response_event(
+            network_events[RESPONSE_COMPLETED_EVENT][event_index],
+            expected_response=expected_response,
+            context=context,
+            navigation=navigation,
+        )
+
+    assert_events(
+        0,
+        url=test_page_same_origin_frame,
+        context=top_context["context"],
+        navigation=navigation_events[0]["navigation"],
+    )
+    assert_events(
+        1,
+        url=test_page,
+        context=frame_context["context"],
+        navigation=navigation_events[1]["navigation"],
+    )
+
+    # Navigate the iframe to another url
+    result = await bidi_session.browsing_context.navigate(
+        context=frame_context["context"], url=test_page_cross_origin, wait="complete"
+    )
+
+    assert len(navigation_events) == 3
+    assert len(network_events[BEFORE_REQUEST_SENT_EVENT]) == 3
+    assert len(network_events[RESPONSE_STARTED_EVENT]) == 3
+    assert len(network_events[RESPONSE_COMPLETED_EVENT]) == 3
+    assert_events(
+        2,
+        url=test_page_cross_origin,
+        context=frame_context["context"],
+        navigation=navigation_events[2]["navigation"],
+    )
+
+
+@pytest.mark.asyncio
 async def test_same_navigation_id(
     bidi_session, top_context, wait_for_event, wait_for_future_safe, url, setup_network_test
 ):
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/conftest.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/conftest.py
index f7420c1..fb6dcc45 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/conftest.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/conftest.py
@@ -1,12 +1,13 @@
 import json
 
+import asyncio
 import pytest
 import pytest_asyncio
 
 from webdriver.bidi.error import NoSuchInterceptException
 from webdriver.bidi.modules.script import ContextTarget
 
-from . import PAGE_EMPTY_HTML, RESPONSE_COMPLETED_EVENT
+from . import PAGE_EMPTY_HTML, PAGE_EMPTY_TEXT, RESPONSE_COMPLETED_EVENT
 
 
 @pytest_asyncio.fixture
@@ -124,3 +125,36 @@
     # cleanup
     for remove_listener in listeners:
         remove_listener()
+
+
+@pytest_asyncio.fixture
+async def setup_blocked_request(
+    setup_network_test, url, add_intercept, fetch, wait_for_event
+):
+    async def setup_blocked_request(phase):
+        await setup_network_test(events=[f"network.{phase}"])
+
+        if phase == "authRequired":
+            blocked_url = url(
+                "/webdriver/tests/support/http_handlers/authentication.py?realm=testrealm"
+            )
+        else:
+            blocked_url = url(PAGE_EMPTY_TEXT)
+
+        await add_intercept(
+            phases=[phase],
+            url_patterns=[
+                {
+                    "type": "string",
+                    "pattern": blocked_url,
+                }
+            ],
+        )
+
+        asyncio.ensure_future(fetch(blocked_url))
+        event = await wait_for_event(f"network.{phase}")
+        request = event["request"]["request"]
+
+        return request
+
+    return setup_blocked_request
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_request/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_request/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_request/__init__.py
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_request/invalid.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_request/invalid.py
new file mode 100644
index 0000000..cdc66cf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_request/invalid.py
@@ -0,0 +1,67 @@
+import pytest
+import webdriver.bidi.error as error
+
+from .. import PAGE_EMPTY_TEXT, RESPONSE_COMPLETED_EVENT
+
+pytestmark = pytest.mark.asyncio
+
+
+@pytest.mark.parametrize("value", [False, 42, {}, []])
+async def test_params_method_invalid_type(setup_blocked_request, bidi_session, value):
+    request = await setup_blocked_request("beforeRequestSent")
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_request(request=request, method=value)
+
+
+@pytest.mark.parametrize("value", [None, False, 42, {}, []])
+async def test_params_request_invalid_type(bidi_session, value):
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_request(request=value)
+
+
+@pytest.mark.parametrize("value", ["", "foo"])
+async def test_params_request_invalid_value(bidi_session, value):
+    with pytest.raises(error.NoSuchRequestException):
+        await bidi_session.network.continue_request(request=value)
+
+
+async def test_params_request_no_such_request(
+    bidi_session, setup_network_test, wait_for_event, fetch, url
+):
+    await setup_network_test(
+        events=[
+            RESPONSE_COMPLETED_EVENT,
+        ]
+    )
+    on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT)
+
+    text_url = url(PAGE_EMPTY_TEXT)
+    await fetch(text_url)
+
+    response_completed_event = await on_response_completed
+    request = response_completed_event["request"]["request"]
+
+    with pytest.raises(error.NoSuchRequestException):
+        await bidi_session.network.continue_request(request=request)
+
+
+@pytest.mark.parametrize("value", [False, 42, {}, []])
+async def test_params_url_invalid_type(setup_blocked_request, bidi_session, value):
+    request = await setup_blocked_request("beforeRequestSent")
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_request(request=request, url=value)
+
+
+@pytest.mark.parametrize("protocol", ["http", "https"])
+@pytest.mark.parametrize("value", [":invalid", "#invalid"])
+async def test_params_url_invalid_value(
+    setup_blocked_request, bidi_session, protocol, value
+):
+    request = await setup_blocked_request("beforeRequestSent")
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_request(
+            request=request, url=f"{protocol}://{value}"
+        )
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_response/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_response/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_response/__init__.py
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_response/invalid.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_response/invalid.py
new file mode 100644
index 0000000..b8e1c08
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_response/invalid.py
@@ -0,0 +1,77 @@
+import pytest
+import webdriver.bidi.error as error
+
+from .. import PAGE_EMPTY_TEXT, RESPONSE_COMPLETED_EVENT
+
+pytestmark = pytest.mark.asyncio
+
+
+async def test_params_request_invalid_phase(setup_blocked_request, bidi_session):
+    request = await setup_blocked_request("beforeRequestSent")
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_response(request=request)
+
+
+@pytest.mark.parametrize("value", [None, False, 42, {}, []])
+async def test_params_request_invalid_type(bidi_session, value):
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_response(request=value)
+
+
+@pytest.mark.parametrize("value", ["", "foo"])
+async def test_params_request_invalid_value(bidi_session, value):
+    with pytest.raises(error.NoSuchRequestException):
+        await bidi_session.network.continue_response(request=value)
+
+
+async def test_params_request_no_such_request(
+    bidi_session, setup_network_test, wait_for_event, fetch, url
+):
+    await setup_network_test(
+        events=[
+            RESPONSE_COMPLETED_EVENT,
+        ]
+    )
+    on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT)
+
+    text_url = url(PAGE_EMPTY_TEXT)
+    await fetch(text_url)
+
+    response_completed_event = await on_response_completed
+    request = response_completed_event["request"]["request"]
+
+    with pytest.raises(error.NoSuchRequestException):
+        await bidi_session.network.continue_response(request=request)
+
+
+@pytest.mark.parametrize("value", [False, 42, {}, []])
+async def test_params_reason_phrase_invalid_type(
+    setup_blocked_request, bidi_session, value
+):
+    request = await setup_blocked_request("responseStarted")
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_response(
+            request=request, reason_phrase=value
+        )
+
+
+@pytest.mark.parametrize("value", [False, "foo", {}, []])
+async def test_params_status_code_invalid_type(
+    setup_blocked_request, bidi_session, value
+):
+    request = await setup_blocked_request("responseStarted")
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_response(request=request, status_code=value)
+
+
+@pytest.mark.parametrize("value", [-1, 4.3])
+async def test_params_status_code_invalid_value(
+    setup_blocked_request, bidi_session, value
+):
+    request = await setup_blocked_request("responseStarted")
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_response(request=request, status_code=value)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_with_auth/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_with_auth/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_with_auth/__init__.py
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_with_auth/invalid.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_with_auth/invalid.py
new file mode 100644
index 0000000..ea188cb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/continue_with_auth/invalid.py
@@ -0,0 +1,141 @@
+import pytest
+import webdriver.bidi.error as error
+
+from .. import PAGE_EMPTY_TEXT, RESPONSE_COMPLETED_EVENT
+
+pytestmark = pytest.mark.asyncio
+
+
+@pytest.mark.parametrize("value", ["beforeRequestSent", "responseStarted"])
+async def test_params_request_invalid_phase(setup_blocked_request, bidi_session, value):
+    request = await setup_blocked_request(value)
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_with_auth(request=request, action="cancel")
+
+
+@pytest.mark.parametrize("value", [None, False, 42, {}, []])
+async def test_params_request_invalid_type(bidi_session, value):
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_with_auth(request=value, action="cancel")
+
+
+@pytest.mark.parametrize("value", ["", "foo"])
+async def test_params_request_invalid_value(bidi_session, value):
+    with pytest.raises(error.NoSuchRequestException):
+        await bidi_session.network.continue_with_auth(request=value, action="cancel")
+
+
+async def test_params_request_no_such_request(
+    bidi_session, setup_network_test, wait_for_event, fetch, url
+):
+    await setup_network_test(
+        events=[
+            RESPONSE_COMPLETED_EVENT,
+        ]
+    )
+    on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT)
+
+    text_url = url(PAGE_EMPTY_TEXT)
+    await fetch(text_url)
+
+    response_completed_event = await on_response_completed
+    request = response_completed_event["request"]["request"]
+
+    with pytest.raises(error.NoSuchRequestException):
+        await bidi_session.network.continue_with_auth(request=request, action="cancel")
+
+
+@pytest.mark.parametrize("value", [None, False, 42, {}, []])
+async def test_params_action_invalid_type(setup_blocked_request, bidi_session, value):
+    request = await setup_blocked_request("authRequired")
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_with_auth(request=request, action=value)
+
+
+@pytest.mark.parametrize("value", ["", "foo"])
+async def test_params_action_invalid_value(setup_blocked_request, bidi_session, value):
+    request = await setup_blocked_request("authRequired")
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_with_auth(request=request, action=value)
+
+
+@pytest.mark.parametrize(
+    "value",
+    [
+        {"type": "password", "password": "foo"},
+        {"type": "password", "username": "foo"},
+        {
+            "type": "password",
+        },
+        {
+            "username": "foo",
+            "password": "bar",
+        },
+        None,
+    ],
+    ids=[
+        "missing username",
+        "missing password",
+        "missing username and password",
+        "missing type",
+        "missing credentials",
+    ],
+)
+async def test_params_action_provideCredentials_invalid_credentials(
+    setup_blocked_request, bidi_session, value
+):
+    request = await setup_blocked_request("authRequired")
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_with_auth(
+            request=request, action="provideCredentials", credentials=value
+        )
+
+
+@pytest.mark.parametrize("value", [None, False, 42, {}, []])
+async def test_params_action_provideCredentials_credentials_type_invalid_type(
+    setup_blocked_request, bidi_session, value
+):
+    request = await setup_blocked_request("authRequired")
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_with_auth(
+            request=request, action="provideCredentials", credentials={"type": value,}
+        )
+
+
+@pytest.mark.parametrize("value", ["", "foo"])
+async def test_params_action_provideCredentials_credentials_type_invalid_value(
+    setup_blocked_request, bidi_session, value
+):
+    request = await setup_blocked_request("authRequired")
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_with_auth(
+            request=request, action="provideCredentials", credentials={"type": value,}
+        )
+
+
+@pytest.mark.parametrize("value", [None, False, 42, {}, []])
+async def test_params_action_provideCredentials_credentials_username_invalid_type(
+    setup_blocked_request, bidi_session, value
+):
+    request = await setup_blocked_request("authRequired")
+    credentials = {"type": "password", "username": value, "password": "foo"}
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_with_auth(
+            request=request, action="provideCredentials", credentials=credentials
+        )
+
+
+@pytest.mark.parametrize("value", [None, False, 42, {}, []])
+async def test_params_action_provideCredentials_credentials_password_invalid_type(
+    setup_blocked_request, bidi_session, value
+):
+    request = await setup_blocked_request("authRequired")
+    credentials = {"type": "password", "username": "foo", "password": value}
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.continue_with_auth(
+            request=request, action="provideCredentials", credentials=credentials
+        )
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/fail_request/invalid.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/fail_request/invalid.py
index 60371d5..ead87c1 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/fail_request/invalid.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/fail_request/invalid.py
@@ -1,9 +1,16 @@
 import pytest
 import webdriver.bidi.error as error
 
+from .. import PAGE_EMPTY_TEXT, RESPONSE_COMPLETED_EVENT
+
 pytestmark = pytest.mark.asyncio
 
-from .. import PAGE_EMPTY_TEXT, RESPONSE_COMPLETED_EVENT
+
+async def test_params_request_invalid_phase(setup_blocked_request, bidi_session):
+    request = await setup_blocked_request("authRequired")
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.fail_request(request=request)
 
 
 @pytest.mark.parametrize("value", [None, False, 42, {}, []])
@@ -18,18 +25,20 @@
         await bidi_session.network.fail_request(request=value)
 
 
-async def test_params_request_no_such_request(bidi_session, setup_network_test,
-                                              wait_for_event, wait_for_future_safe,
-                                              fetch, url):
-    await setup_network_test(events=[
-        RESPONSE_COMPLETED_EVENT,
-    ])
+async def test_params_request_no_such_request(
+    bidi_session, setup_network_test, wait_for_event, fetch, url
+):
+    await setup_network_test(
+        events=[
+            RESPONSE_COMPLETED_EVENT,
+        ]
+    )
     on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT)
 
     text_url = url(PAGE_EMPTY_TEXT)
     await fetch(text_url)
 
-    response_completed_event = await wait_for_future_safe(on_response_completed)
+    response_completed_event = await on_response_completed
     request = response_completed_event["request"]["request"]
 
     with pytest.raises(error.NoSuchRequestException):
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/provide_response/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/provide_response/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/provide_response/__init__.py
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/provide_response/invalid.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/provide_response/invalid.py
new file mode 100644
index 0000000..eec118a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/provide_response/invalid.py
@@ -0,0 +1,68 @@
+import pytest
+import webdriver.bidi.error as error
+
+from .. import PAGE_EMPTY_TEXT, RESPONSE_COMPLETED_EVENT
+
+pytestmark = pytest.mark.asyncio
+
+
+@pytest.mark.parametrize("value", [None, False, 42, {}, []])
+async def test_params_request_invalid_type(bidi_session, value):
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.provide_response(request=value)
+
+
+@pytest.mark.parametrize("value", ["", "foo"])
+async def test_params_request_invalid_value(bidi_session, value):
+    with pytest.raises(error.NoSuchRequestException):
+        await bidi_session.network.provide_response(request=value)
+
+
+async def test_params_request_no_such_request(
+    bidi_session, setup_network_test, wait_for_event, fetch, url
+):
+    await setup_network_test(
+        events=[
+            RESPONSE_COMPLETED_EVENT,
+        ]
+    )
+    on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT)
+
+    text_url = url(PAGE_EMPTY_TEXT)
+    await fetch(text_url)
+
+    response_completed_event = await on_response_completed
+    request = response_completed_event["request"]["request"]
+
+    with pytest.raises(error.NoSuchRequestException):
+        await bidi_session.network.provide_response(request=request)
+
+
+@pytest.mark.parametrize("value", [False, 42, {}, []])
+async def test_params_reason_phrase_invalid_type(setup_blocked_request,
+                                                 bidi_session,
+                                                 value):
+    request = await setup_blocked_request("beforeRequestSent")
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.provide_response(request=request,
+                                                    reason_phrase=value)
+
+
+@pytest.mark.parametrize("value", [False, "foo", {}, []])
+async def test_params_status_code_invalid_type(setup_blocked_request, bidi_session,
+                                               value):
+    request = await setup_blocked_request("beforeRequestSent")
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.provide_response(request=request,
+                                                    status_code=value)
+
+
+@pytest.mark.parametrize("value", [-1, 4.3])
+async def test_params_status_code_invalid_value(setup_blocked_request, bidi_session, value):
+    request = await setup_blocked_request("beforeRequestSent")
+
+    with pytest.raises(error.InvalidArgumentException):
+        await bidi_session.network.provide_response(request=request,
+                                                    status_code=value)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/response_completed/response_completed.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/response_completed/response_completed.py
index b54b969..b9b4ae7 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/response_completed/response_completed.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/response_completed/response_completed.py
@@ -89,6 +89,39 @@
 
 
 @pytest.mark.asyncio
+async def test_iframe_load(
+    bidi_session,
+    top_context,
+    setup_network_test,
+    test_page,
+    test_page_same_origin_frame,
+):
+    network_events = await setup_network_test(events=[RESPONSE_COMPLETED_EVENT])
+    events = network_events[RESPONSE_COMPLETED_EVENT]
+
+    await bidi_session.browsing_context.navigate(
+        context=top_context["context"],
+        url=test_page_same_origin_frame,
+        wait="complete",
+    )
+
+    contexts = await bidi_session.browsing_context.get_tree(root=top_context["context"])
+    frame_context = contexts[0]["children"][0]
+
+    assert len(events) == 2
+    assert_response_event(
+        events[0],
+        expected_request={"url": test_page_same_origin_frame},
+        context=top_context["context"],
+    )
+    assert_response_event(
+        events[1],
+        expected_request={"url": test_page},
+        context=frame_context["context"],
+    )
+
+
+@pytest.mark.asyncio
 async def test_load_page_twice(
     bidi_session, top_context, wait_for_event, wait_for_future_safe, url, setup_network_test
 ):
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/response_started/response_started.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/response_started/response_started.py
index c4e9c65..dec743e 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/response_started/response_started.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/network/response_started/response_started.py
@@ -67,6 +67,39 @@
 
 
 @pytest.mark.asyncio
+async def test_iframe_load(
+    bidi_session,
+    top_context,
+    setup_network_test,
+    test_page,
+    test_page_same_origin_frame,
+):
+    network_events = await setup_network_test(events=[RESPONSE_STARTED_EVENT])
+    events = network_events[RESPONSE_STARTED_EVENT]
+
+    await bidi_session.browsing_context.navigate(
+        context=top_context["context"],
+        url=test_page_same_origin_frame,
+        wait="complete",
+    )
+
+    contexts = await bidi_session.browsing_context.get_tree(root=top_context["context"])
+    frame_context = contexts[0]["children"][0]
+
+    assert len(events) == 2
+    assert_response_event(
+        events[0],
+        expected_request={"url": test_page_same_origin_frame},
+        context=top_context["context"],
+    )
+    assert_response_event(
+        events[1],
+        expected_request={"url": test_page},
+        context=frame_context["context"],
+    )
+
+
+@pytest.mark.asyncio
 async def test_load_page_twice(
     bidi_session, top_context, wait_for_event, wait_for_future_safe, url, setup_network_test
 ):
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/http_handlers/headers.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/http_handlers/headers.py
index 71c4901..cb8d18d 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/http_handlers/headers.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/http_handlers/headers.py
@@ -16,4 +16,7 @@
         except ValueError:
             pass
 
+    if b"Content-Type" not in response.headers:
+        response.headers.set(b"Content-Type", "text/plain")
+
     response.content = "HTTP Response Headers"
diff --git a/third_party/blink/web_tests/images/color-jpeg-with-color-profile.html b/third_party/blink/web_tests/images/color-jpeg-with-color-profile.html
index 0f96c04..ea88c4655 100644
--- a/third_party/blink/web_tests/images/color-jpeg-with-color-profile.html
+++ b/third_party/blink/web_tests/images/color-jpeg-with-color-profile.html
@@ -1,3 +1,3 @@
 <!-- crbug.com/1330691: Suppress benign pixel differences. -->
-<meta name=fuzzy content="maxDifference=0-2;totalPixels=0-42401">
+<meta name=fuzzy content="maxDifference=0-2;totalPixels=0-56925">
 <img src="resources/icc-v2-gbr.jpg">
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_request/invalid-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_request/invalid-expected.txt
new file mode 100644
index 0000000..b4cd1484
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/continue_request/invalid-expected.txt
@@ -0,0 +1,42 @@
+This is a wdspec test.
+[FAIL] test_params_method_invalid_type[False]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_method_invalid_type[42]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_method_invalid_type[value2]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_method_invalid_type[value3]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_request_invalid_type[None]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_request_invalid_type[False]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_request_invalid_type[42]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_request_invalid_type[value3]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_request_invalid_type[value4]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_request_invalid_value[]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_request_invalid_value[foo]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_request_no_such_request
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_url_invalid_type[False]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_url_invalid_type[42]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_url_invalid_type[value2]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_url_invalid_type[value3]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_url_invalid_value[:invalid-http]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_url_invalid_value[:invalid-https]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_url_invalid_value[#invalid-http]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+[FAIL] test_params_url_invalid_value[#invalid-https]
+  AttributeError: 'Network' object has no attribute 'continue_request'
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/provide_response/invalid-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/provide_response/invalid-expected.txt
new file mode 100644
index 0000000..9a5752c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/webdriver/tests/bidi/network/provide_response/invalid-expected.txt
@@ -0,0 +1,38 @@
+This is a wdspec test.
+[FAIL] test_params_request_invalid_type[None]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_request_invalid_type[False]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_request_invalid_type[42]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_request_invalid_type[value3]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_request_invalid_type[value4]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_request_invalid_value[]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_request_invalid_value[foo]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_request_no_such_request
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_reason_phrase_invalid_type[False]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_reason_phrase_invalid_type[42]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_reason_phrase_invalid_type[value2]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_reason_phrase_invalid_type[value3]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_status_code_invalid_type[False]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_status_code_invalid_type[foo]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_status_code_invalid_type[value2]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_status_code_invalid_type[value3]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_status_code_invalid_value[-1]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+[FAIL] test_params_status_code_invalid_value[4.3]
+  AttributeError: 'Network' object has no attribute 'provide_response'
+Harness: the test ran to completion.
diff --git a/third_party/chromium-variations b/third_party/chromium-variations
index 68cb7ee..78c4cb4 160000
--- a/third_party/chromium-variations
+++ b/third_party/chromium-variations
@@ -1 +1 @@
-Subproject commit 68cb7ee131434b825dca5e465653e3292abaf116
+Subproject commit 78c4cb498a6ce49194e7e1c140e292eb76a9c630
diff --git a/third_party/dawn b/third_party/dawn
index 762e826..9c54476 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit 762e826397ab42b8379d9bf3063117b6530be6a3
+Subproject commit 9c54476ce64f21d6878ede05af9397dd55973a14
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal
index 1e99db8..7978586 160000
--- a/third_party/devtools-frontend-internal
+++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@
-Subproject commit 1e99db8ac5adeec92e59c8b9f602808af61444a9
+Subproject commit 7978586bb4d2abcb15c816ec7c9e0d7d4b299a9c
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 699358f..87acf6b 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 699358f39b61599f314f72580e9f8ff096785bed
+Subproject commit 87acf6b1ff2cb02f2bd838a821790727a83f0414
diff --git a/third_party/libc++/src b/third_party/libc++/src
index 99f5e4d..6226f31 160000
--- a/third_party/libc++/src
+++ b/third_party/libc++/src
@@ -1 +1 @@
-Subproject commit 99f5e4d09e3014e1b178dfd7ebf451e652461ad6
+Subproject commit 6226f31a8c995612018d53c7accad729a028e9c0
diff --git a/third_party/skia b/third_party/skia
index cf84bab..01aa69c 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit cf84bab13da3201dc3164a5c938fb70cc9458d5a
+Subproject commit 01aa69c38af545e361bcf8d9b0f8688f19d4244a
diff --git a/third_party/sqlite/src b/third_party/sqlite/src
index a7a54e1..cd94868 160000
--- a/third_party/sqlite/src
+++ b/third_party/sqlite/src
@@ -1 +1 @@
-Subproject commit a7a54e1dd9b6124c725a35552d48d6ce0e3c1ef9
+Subproject commit cd9486849ba3c3ec753f556fd29c0aabee122a28
diff --git a/third_party/swiftshader b/third_party/swiftshader
index 77be35e..2fa7e9b 160000
--- a/third_party/swiftshader
+++ b/third_party/swiftshader
@@ -1 +1 @@
-Subproject commit 77be35e337bb80df52c5ea4dd4cf2d2072abad25
+Subproject commit 2fa7e9b99ae4e70ea5ae2cc9c8d3afb43391384f
diff --git a/third_party/webgpu-cts/src b/third_party/webgpu-cts/src
index dac947f..eb1892c 160000
--- a/third_party/webgpu-cts/src
+++ b/third_party/webgpu-cts/src
@@ -1 +1 @@
-Subproject commit dac947fdd70cf6ec088e1c621f58eaf99430175f
+Subproject commit eb1892c3e4ab734a135e8752f5442e270a4738d1
diff --git a/third_party/webrtc b/third_party/webrtc
index ac60ad7..c9d44b3 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit ac60ad7acd95149fa7bbd61a2f71e27b019ff96a
+Subproject commit c9d44b3fb94511e92879f3b6061aee117474cb27
diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn
index 8ed0807..46627bc 100644
--- a/third_party/zlib/BUILD.gn
+++ b/third_party/zlib/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/compiler/compiler.gni")
+import("//build/config/dcheck_always_on.gni")
 
 declare_args() {
   # Expose zlib's symbols, used by Node.js to provide zlib APIs for its native
@@ -33,7 +34,7 @@
     # Build code using -O3, see: crbug.com/1084371.
     configs = [ "//build/config/compiler:optimize_speed" ]
   }
-  if (is_debug || use_fuzzing_engine) {
+  if (is_debug || dcheck_always_on || use_fuzzing_engine) {
     # Enable zlib's asserts in debug and fuzzer builds.
     defines += [ "ZLIB_DEBUG" ]
   }
diff --git a/tools/android/avd/proto/creation/android_34_google_apis_x64.textpb b/tools/android/avd/proto/creation/android_34_google_apis_x64.textpb
index fe80916..3e9ffeb2 100644
--- a/tools/android/avd/proto/creation/android_34_google_apis_x64.textpb
+++ b/tools/android/avd/proto/creation/android_34_google_apis_x64.textpb
@@ -34,3 +34,10 @@
     value: "on"
   }
 }
+
+min_sdk: 34
+additional_apk {
+  package_name: "chrome_internal/third_party/google3/apks/gmscore/x86_64"
+  version: "yPyAJAHojVhJz8dfy28tCLyUidWpD95q2Zzj6JXJRkIC"
+  dest_path: "android_33_google_apis_x64/gmscore_apks"
+}
diff --git a/tools/binary_size/generate_milestone_reports.py b/tools/binary_size/generate_milestone_reports.py
index f00911dd..4247a7c 100755
--- a/tools/binary_size/generate_milestone_reports.py
+++ b/tools/binary_size/generate_milestone_reports.py
@@ -106,6 +106,7 @@
     '118.0.5993.5',
     '119.0.6045.7',
     '120.0.6099.18',
+    '121.0.6167.7',
 ]
 
 
diff --git a/tools/cast3p/runtime.version b/tools/cast3p/runtime.version
index e3a78754..894a692 100644
--- a/tools/cast3p/runtime.version
+++ b/tools/cast3p/runtime.version
@@ -1 +1 @@
-391527
+393515
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index cd15fce..960c936 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -265,6 +265,10 @@
     "META": {"sizes": {"includes": [15]}},
     "includes": [3120],
   },
+  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/device_log/resources.grd": {
+    "META": {"sizes": {"includes": [5],}},
+    "includes": [3130],
+  },
   "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/discards/resources.grd": {
     "META": {"sizes": {"includes": [20],}},
     "includes": [3140],
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 4437c4ea..ea282086 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -37,7 +37,7 @@
       'chromeos-jacuzzi-chrome-skylab': 'chromeos_jacuzzi_include_unwind_tables_official_skylab_reclient',
       'chromeos-octopus-chrome': 'chromeos_octopus_include_unwind_tables_official_dchecks_reclient',
       'chromeos-octopus-chrome-skylab': 'chromeos_octopus_include_unwind_tables_official_skylab',
-      'chromeos-reven-chrome': 'chromeos_reven_include_unwind_tables_official_dchecks_reclient',
+      'chromeos-reven-chrome': 'chromeos_reven_include_unwind_tables_official_reclient',
       'chromeos-volteer-chrome-skylab': 'chromeos_volteer_include_unwind_tables_official_skylab_reclient',
       'lacros-amd64-generic-chrome-skylab': 'chromeos_amd64-generic_lacros_official_skylab_reclient',
       'lacros-arm-generic-chrome': 'chromeos_arm-generic_lacros_official_reclient',
@@ -366,7 +366,7 @@
       'chromeos-octopus-chrome': 'chromeos_octopus_include_unwind_tables_official_dchecks',
       'chromeos-octopus-chrome-skylab': 'chromeos_octopus_include_unwind_tables_official_skylab',
       'chromeos-octopus-compile-chrome': 'chromeos_octopus_include_unwind_tables_official_dchecks',
-      'chromeos-reven-chrome': 'chromeos_reven_include_unwind_tables_official_dchecks',
+      'chromeos-reven-chrome': 'chromeos_reven_include_unwind_tables_official',
       'chromeos-trogdor-chrome-skylab': 'chromeos_trogdor_include_unwind_tables_official_skylab',
       'chromeos-volteer-chrome-skylab': 'chromeos_volteer_include_unwind_tables_official_skylab',
       'lacros-amd64-generic-chrome': 'chromeos_amd64-generic_lacros_official_no_symbols',
@@ -815,12 +815,12 @@
       'also_build_lacros_chrome_for_architecture_amd64',
     ],
 
-    'chromeos_reven_include_unwind_tables_official_dchecks': [
-      'chromeos_device', 'reven', 'include_unwind_tables', 'official', 'dcheck_always_on',
+    'chromeos_reven_include_unwind_tables_official': [
+      'chromeos_device', 'reven', 'include_unwind_tables', 'official',
       'also_build_lacros_chrome_for_architecture_amd64',
     ],
-    'chromeos_reven_include_unwind_tables_official_dchecks_reclient': [
-      'chromeos_device_reclient', 'reven', 'include_unwind_tables', 'official', 'dcheck_always_on',
+    'chromeos_reven_include_unwind_tables_official_reclient': [
+      'chromeos_device_reclient', 'reven', 'include_unwind_tables', 'official',
       'also_build_lacros_chrome_for_architecture_amd64', 'ozone_headless'
     ],
 
diff --git a/tools/mb/mb_config_expectations/chrome.json b/tools/mb/mb_config_expectations/chrome.json
index 018e5c6..661c76e 100644
--- a/tools/mb/mb_config_expectations/chrome.json
+++ b/tools/mb/mb_config_expectations/chrome.json
@@ -157,7 +157,7 @@
     "args_file": "//build/args/chromeos/reven-vmtest.gni",
     "gn_args": {
       "also_build_lacros_chrome_for_architecture": "amd64",
-      "dcheck_always_on": true,
+      "dcheck_always_on": false,
       "exclude_unwind_tables": false,
       "is_chrome_branded": true,
       "is_chromeos_device": true,
diff --git a/tools/mb/mb_config_expectations/tryserver.chrome.json b/tools/mb/mb_config_expectations/tryserver.chrome.json
index 52d91d2..f2b2c30 100644
--- a/tools/mb/mb_config_expectations/tryserver.chrome.json
+++ b/tools/mb/mb_config_expectations/tryserver.chrome.json
@@ -279,7 +279,7 @@
     "args_file": "//build/args/chromeos/reven-vmtest.gni",
     "gn_args": {
       "also_build_lacros_chrome_for_architecture": "amd64",
-      "dcheck_always_on": true,
+      "dcheck_always_on": false,
       "exclude_unwind_tables": false,
       "is_chrome_branded": true,
       "is_chromeos_device": true,
diff --git a/tools/metrics/BUILD.gn b/tools/metrics/BUILD.gn
index 926e868..35d225b 100644
--- a/tools/metrics/BUILD.gn
+++ b/tools/metrics/BUILD.gn
@@ -105,7 +105,7 @@
     "//tools/metrics/histograms/validate_token_test.py",
 
     "//tools/metrics/structured/codegen_unittest.py",
-    "//tools/metrics/structured/model_unittest.py",
+    "//tools/metrics/structured/sync/model_unittest.py",
 
     "//tools/metrics/ukm/ukm.xml",
     "//tools/metrics/ukm/codegen.py",
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index f3e0478..b14d5b56 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -40537,6 +40537,7 @@
   <suffix name="DesktopTabGroupsNewGroup" label="For desktop Tab Groups."/>
   <suffix name="DiscoverFeedHeaderMenu"
       label="For DiscoverFeedHeaderMenu feature."/>
+  <suffix name="DownloadEsbPromo" label="For Download Esb Iph feature promo."/>
   <suffix name="DownloadHome" label="For DownloadHome feature."/>
   <suffix name="DownloadIndicator" label="For DownloadIndicator feature."/>
   <suffix name="DownloadInfoBarDownloadContinuing"
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a8b06b7..a934214 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -30187,6 +30187,7 @@
       label="PolicyBlocklistThrottleRequiresPoliciesLoaded:enabled"/>
   <int value="-1392627070" label="AppShimNotificationAttribution:disabled"/>
   <int value="-1392562498" label="disable-origin-chip"/>
+  <int value="-1392231827" label="kTearOffWebAppTabOpensWebAppWindow:enabled"/>
   <int value="-1391728260" label="ContextualSearchDefinitions:enabled"/>
   <int value="-1391693054"
       label="ContentSuggestionsFaviconsFromNewServer:disabled"/>
@@ -31243,7 +31244,6 @@
   <int value="-891856063" label="MidiManagerAndroid:enabled"/>
   <int value="-889670978" label="AssistantRoutines:disabled"/>
   <int value="-888336510" label="NtpSingleRowShortcuts:enabled"/>
-  <int value="-887101831" label="StoreHoursAndroid:disabled"/>
   <int value="-887094098" label="ForcedColors:enabled"/>
   <int value="-886912558" label="ChromeHomePromo:enabled"/>
   <int value="-886898803" label="CooperativeScheduling:enabled"/>
@@ -31900,6 +31900,7 @@
   <int value="-579192400" label="disable-input-view"/>
   <int value="-577982497" label="CupsPrintersUiOverhaul:enabled"/>
   <int value="-577503348" label="GridTabSwitcherForTablets:disabled"/>
+  <int value="-577443556" label="kTearOffWebAppTabOpensWebAppWindow:disabled"/>
   <int value="-576759065" label="HoldingSpaceInProgressAnimationV2:disabled"/>
   <int value="-576656013" label="WebAppSystemMediaControlsWin:enabled"/>
   <int value="-576475188" label="BorealisForceDoubleScale:disabled"/>
@@ -32141,6 +32142,7 @@
   <int value="-466047052" label="EnableRuntimeCountersTelemetry:disabled"/>
   <int value="-465429170" label="BackGestureRefactorActivityAndroid:enabled"/>
   <int value="-465381408" label="Sharesheet:disabled"/>
+  <int value="-463161052" label="TearOffWebAppTabOpensWebAppWindow:enabled"/>
   <int value="-462554210"
       label="OminboxUIExperimentUseGenericSearchEngineIcon:enabled"/>
   <int value="-462404204" label="CryptAuthV2DeviceSync:disabled"/>
@@ -33023,7 +33025,6 @@
   <int value="-49124128" label="CastEnableStreamingWithHiDPI:enabled"/>
   <int value="-48920737" label="enable-smooth-scrolling"/>
   <int value="-48187847" label="OneTimePermission:enabled"/>
-  <int value="-47034052" label="StoreHoursAndroid:enabled"/>
   <int value="-46847847" label="EstablishGpuChannelAsync:disabled"/>
   <int value="-46622176" label="NtpModulesHeaderIcon:enabled"/>
   <int value="-45849989" label="ModeSpecificPowerButton:disabled"/>
@@ -33500,6 +33501,7 @@
   <int value="180414503"
       label="JourneysOmniboxHistoryClusterProvider:disabled"/>
   <int value="181150000" label="CrosVmCupsProxy:enabled"/>
+  <int value="181908345" label="TearOffWebAppTabOpensWebAppWindow:disabled"/>
   <int value="182358203" label="AddToHomescreenIPH:enabled"/>
   <int value="182788555" label="DecoupleSyncFromAndroidMasterSync:disabled"/>
   <int value="182986882" label="TerminalSSH:disabled"/>
@@ -48651,6 +48653,12 @@
   <int value="12" label="Extension Renderer"/>
 </enum>
 
+<enum name="ShowCardsFromGoogleAccountButtonEvent">
+  <int value="0" label="Button shown"/>
+  <int value="1" label="Button shown (once)"/>
+  <int value="2" label="Button clicked"/>
+</enum>
+
 <enum name="ShutdownReason">
   <summary>
     The reason that the Chrome OS power manager shut down or rebooted the
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 0fcd8efb..2132e9a4 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -1138,6 +1138,18 @@
   </token>
 </histogram>
 
+<histogram
+    name="Autofill.ButterForPayments.ShowCardsFromGoogleAccountButtonEvents"
+    enum="ShowCardsFromGoogleAccountButtonEvent" expires_after="2024-07-01">
+  <owner>averina@google.com</owner>
+  <owner>payments-autofill-team@google.com</owner>
+  <summary>
+    Logs events for the 'Show cards from your Google Account' button, i.e.,
+    button shown, button shown (logged once per page), or clicked. Logged at the
+    time of the event happening.
+  </summary>
+</histogram>
+
 <histogram name="Autofill.CardholderNameFixFlowPrompt.Events"
     enum="AutofillCardholderNameFixFlowPromptEvent" expires_after="2024-07-01">
   <owner>jsaul@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
index 358fa3f..86604cb 100644
--- a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
+++ b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
@@ -138,6 +138,7 @@
       summary="PWA launch due to link capturing being enabled"/>
   <variant name="IPH_DesktopTabGroupsNewGroup"
       summary="creating a new tab group"/>
+  <variant name="IPH_DownloadEsbPromo" summary="download ESB promo"/>
   <variant name="IPH_DownloadHome" summary="download home"/>
   <variant name="IPH_DownloadInfobarDownloadContinuing"
       summary="download infobar informing that downloads are still in
diff --git a/tools/metrics/histograms/metadata/holding_space/enums.xml b/tools/metrics/histograms/metadata/holding_space/enums.xml
index 5a0682f..994d666 100644
--- a/tools/metrics/histograms/metadata/holding_space/enums.xml
+++ b/tools/metrics/histograms/metadata/holding_space/enums.xml
@@ -192,6 +192,7 @@
   <int value="16" label="kCameraAppScanPdf"/>
   <int value="17" label="kCameraAppVideoGif"/>
   <int value="18" label="kCameraAppVideoMp4"/>
+  <int value="19" label="kPhotoshopWeb"/>
 </enum>
 
 <enum name="HoldingSpacePodAction">
diff --git a/tools/metrics/histograms/metadata/holding_space/histograms.xml b/tools/metrics/histograms/metadata/holding_space/histograms.xml
index 585520b7..a9f0288 100644
--- a/tools/metrics/histograms/metadata/holding_space/histograms.xml
+++ b/tools/metrics/histograms/metadata/holding_space/histograms.xml
@@ -63,6 +63,7 @@
   <variant name="NearbyShare" summary="Items backed by a nearby shared file."/>
   <variant name="PhoneHubCameraRoll"
       summary="Recent photos and videos taken on connected Android phone."/>
+  <variant name="PhotoshopWeb" summary="Items backed by a Photoshop Web file."/>
   <variant name="PinnedFile" summary="Items pinned explicitly by the user."/>
   <variant name="PrintedPdf" summary="Items backed by a printed PDF file."/>
   <variant name="Scan" summary="Items backed by a scanned file."/>
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index 21d50cf8..caccd1b 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -1994,15 +1994,6 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.ShmemDir.AmountOfFreeSpace" units="MB"
-    expires_after="M77">
-  <owner>reveman@chromium.org</owner>
-  <summary>
-    The amount of free space in temporary directory for shared memory files.
-    Recorded each time a new discardable memory manager instance is created.
-  </summary>
-</histogram>
-
 <histogram name="Memory.StackSamplingProfiler.StackSampleSize2" units="KB"
     expires_after="2024-05-12">
   <owner>iby@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml
index 2735efb9..db1d9eb 100644
--- a/tools/metrics/histograms/metadata/navigation/histograms.xml
+++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -994,6 +994,19 @@
   </summary>
 </histogram>
 
+<histogram name="Navigation.MainFrameSchemeDifferentPage2NonUniqueHostname"
+    enum="NavigationScheme" expires_after="2024-09-01">
+  <owner>meacer@chromium.org</owner>
+  <owner>trusty-transport@chromium.org</owner>
+  <summary>
+    A subset of MainFrameSchemeDifferentPage2 only recorded for non-unique
+    hostnames. The scheme of the URL for each main-frame navigation that
+    replaces a document object. This is not reported for reference fragment
+    navigations, pushState/replaceState or same page history navigation. This is
+    only recorded for finished navigations and not attempts.
+  </summary>
+</histogram>
+
 <histogram name="Navigation.MainFrameSchemeDifferentPageOTR2"
     enum="NavigationScheme" expires_after="2024-05-19">
   <owner>estark@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/printing/histograms.xml b/tools/metrics/histograms/metadata/printing/histograms.xml
index 35f49d86..2d53d678 100644
--- a/tools/metrics/histograms/metadata/printing/histograms.xml
+++ b/tools/metrics/histograms/metadata/printing/histograms.xml
@@ -498,6 +498,24 @@
   </summary>
 </histogram>
 
+<histogram name="Printing.PrintDuration.Success" units="seconds"
+    expires_after="2024-08-14">
+  <owner>awscreen@chromium.org</owner>
+  <owner>thestig@chromium.org</owner>
+  <summary>
+    Records the time to successfully print a document to a local printer. All
+    print job settings are determined before the start of the measurement. This
+    time represents the period from when a print job is started until a document
+    done completes successfully, after the document has been fully spooled to
+    the platform for transmission to the printer. This includes the time for any
+    required data format conversions, such as from PDF to another format like
+    EMF, PostScript, or XPS. Specifically excluded from collection for this
+    metric are Windows print jobs which use a printer driver that prompts the
+    user with a &quot;Save Print Output As&quot; dialog, since such print jobs
+    could be arbitrarily long waiting for data entry by users.
+  </summary>
+</histogram>
+
 <histogram name="Printing.PrintServers.ServersToQuery" units="servers"
     expires_after="2024-09-01">
   <owner>pawliczek@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index 94bce437..b439feb 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -2676,7 +2676,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4Database.SizeLinear{ThreatTypeKB}"
-    units="100 KB" expires_after="2023-11-11">
+    units="100 KB" expires_after="2024-12-12">
   <owner>kristianm@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/subresource/histograms.xml b/tools/metrics/histograms/metadata/subresource/histograms.xml
index 1601e9dc..0bc0822 100644
--- a/tools/metrics/histograms/metadata/subresource/histograms.xml
+++ b/tools/metrics/histograms/metadata/subresource/histograms.xml
@@ -63,15 +63,6 @@
   </summary>
 </histogram>
 
-<histogram name="SubresourceFilter.DocumentLoad.ActivationState"
-    enum="SubresourceFilterActivationState" expires_after="2020-10-04">
-  <owner>engedy@chromium.org</owner>
-  <summary>
-    Whenever a document load is committed in any frame, records whether
-    subresource filtering should be activated for that load.
-  </summary>
-</histogram>
-
 <histogram name="SubresourceFilter.DocumentLoad.RulesetIsAvailable"
     enum="BooleanAvailable" expires_after="2024-07-10">
   <owner>alexmt@chromium.org</owner>
diff --git a/tools/metrics/structured/events.xml b/tools/metrics/structured/events.xml
deleted file mode 100644
index 6002b864..0000000
--- a/tools/metrics/structured/events.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<structured-metrics>
-
-</structured-metrics>
\ No newline at end of file
diff --git a/tools/metrics/structured/gen_events.py b/tools/metrics/structured/gen_events.py
index 81432c9..10426c03 100755
--- a/tools/metrics/structured/gen_events.py
+++ b/tools/metrics/structured/gen_events.py
@@ -12,7 +12,7 @@
 import sys
 
 import codegen
-import model
+from sync import model
 import templates_events as templates
 
 parser = argparse.ArgumentParser(
diff --git a/tools/metrics/structured/gen_validator.py b/tools/metrics/structured/gen_validator.py
index 1f4eded2..f07c149 100755
--- a/tools/metrics/structured/gen_validator.py
+++ b/tools/metrics/structured/gen_validator.py
@@ -12,7 +12,7 @@
 import sys
 
 import codegen
-import model
+from sync import model
 
 parser = argparse.ArgumentParser(
     description='Generate structured metrics validator')
diff --git a/tools/metrics/structured/pretty_print.py b/tools/metrics/structured/pretty_print.py
index 0480eac..d3e5e15 100755
--- a/tools/metrics/structured/pretty_print.py
+++ b/tools/metrics/structured/pretty_print.py
@@ -6,7 +6,7 @@
 import os
 import sys
 
-import model
+from sync import model
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common'))
 import presubmit_util
diff --git a/tools/metrics/structured/sync/__init__.py b/tools/metrics/structured/sync/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/metrics/structured/sync/__init__.py
diff --git a/tools/metrics/structured/model.py b/tools/metrics/structured/sync/model.py
similarity index 99%
rename from tools/metrics/structured/model.py
rename to tools/metrics/structured/sync/model.py
index d12707f..562a446 100644
--- a/tools/metrics/structured/model.py
+++ b/tools/metrics/structured/sync/model.py
@@ -11,7 +11,7 @@
 
 import xml.etree.ElementTree as ET
 import textwrap as tw
-import model_util as util
+import sync.model_util as util
 import re
 
 # Default key rotation period if not explicitly specified in the XML.
diff --git a/tools/metrics/structured/model_unittest.py b/tools/metrics/structured/sync/model_unittest.py
similarity index 99%
rename from tools/metrics/structured/model_unittest.py
rename to tools/metrics/structured/sync/model_unittest.py
index f20ad5f3..3467971 100755
--- a/tools/metrics/structured/model_unittest.py
+++ b/tools/metrics/structured/sync/model_unittest.py
@@ -7,7 +7,7 @@
 
 # TODO(crbug.com/1148168): Set up these tests to run on the tryjobs.
 
-import model
+import sync.model
 import unittest
 
 from model import Model
diff --git a/tools/metrics/structured/model_util.py b/tools/metrics/structured/sync/model_util.py
similarity index 100%
rename from tools/metrics/structured/model_util.py
rename to tools/metrics/structured/sync/model_util.py
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index a923733..bbf2ec3 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -13330,6 +13330,20 @@
   </metric>
 </event>
 
+<event name="MainFrameNavigation.ZstdContentEncoding">
+  <owner>nidhijaju@chromium.org</owner>
+  <owner>blink-network-stack@google.com</owner>
+  <summary>
+    Event recorded on navigation commit when a page load is content-encoded with
+    zstd and is a main frame navigation.
+  </summary>
+  <metric name="UsedZstd" enum="Boolean">
+    <summary>
+      Set to 1 if the main frame served zstd-compressed content.
+    </summary>
+  </metric>
+</event>
+
 <event name="Media.Autoplay.Attempt">
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
@@ -24374,6 +24388,20 @@
   </metric>
 </event>
 
+<event name="SubresourceLoad.ZstdContentEncoding">
+  <owner>nidhijaju@chromium.org</owner>
+  <owner>blink-network-stack@google.com</owner>
+  <summary>
+    Event recorded when a response is received that is content-encoded with zstd
+    and is a subresource load.
+  </summary>
+  <metric name="UsedZstd" enum="Boolean">
+    <summary>
+      Set to 1 if the subresource served zstd-compressed content.
+    </summary>
+  </metric>
+</event>
+
 <event name="SubresourceRedirect.PublicSrcVideoCompression">
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
diff --git a/tools/typescript/validate_tsconfig.py b/tools/typescript/validate_tsconfig.py
index 8e577c2..03956c6 100644
--- a/tools/typescript/validate_tsconfig.py
+++ b/tools/typescript/validate_tsconfig.py
@@ -143,9 +143,14 @@
       # remove exception.
       'chrome/browser/resources/bluetooth_internals',
       'chrome/browser/resources/chromeos/accessibility',
+      # TODO(crbug.com/1511758): Migrate to TypeScript.
+      'chrome/browser/resources/device_log',
       'chrome/test/data/webui',
       'chrome/test/data/webui/chromeos',
       'chrome/test/data/webui/chromeos/ash_common',
+      # TODO(b/310963279): Migrate os_feedback_ui to TypeScript and remove
+      # exception.
+      'chrome/test/data/webui/chromeos/os_feedback_ui',
       'chrome/test/data/webui/cr_components/chromeos',
       'chrome/test/data/webui/nearby_share',
       'chrome/test/data/webui/settings/chromeos',
diff --git a/tools/visual_debugger/app.html b/tools/visual_debugger/app.html
index c10b847f..1ab2b78 100644
--- a/tools/visual_debugger/app.html
+++ b/tools/visual_debugger/app.html
@@ -456,14 +456,21 @@
     const scrubberMax = document.querySelector('#maxDrawScrubber');
     const drawRange = document.querySelector('#drawRange');
 
+    scrubberMax.max = nDraws;
     scrubberMin.value = minIndex;
     scrubberMax.value = maxIndex;
-    scrubberMax.max = nDraws;
     drawRange.textContent = ` [${minIndex}, ${maxIndex})`;
 
     updateDrawScrubberSizes(minIndex, maxIndex, nDraws);
   }
 
+  function updateFrameScrubber(oldest, newest, current) {
+    const scrubberFrame = document.querySelector('#scrubberframe');
+    scrubberFrame.min = oldest;
+    scrubberFrame.max = newest;
+    scrubberFrame.value = current;
+  }
+
   function setUpPlayer() {
     // First, set up the viewer.
     const canvas = document.querySelector('#canvas');
@@ -471,15 +478,10 @@
     const viewer = new Viewer(canvas, logContainer);
     // Now create the player for the viewer.
     const player = new Player(viewer, (frame) => {
-      // TODO: This feels like a hack. Find a cleaner way to update the scrubbers?
-      const scrubberFrame = document.querySelector('#scrubberframe');
-      scrubberFrame.value = player.currentFrameIndex;
-
-      if (!frame) {
-        // TODO: Not sure why frame is sometimes empty. Fix that.
-        return;
-      }
-
+      updateFrameScrubber(
+          DrawFrame.frameBuffer.oldestIndex(),
+          DrawFrame.frameBuffer.newestIndex(),
+          player.currentFrameIndex);
       setDrawScrubbers(
           frame.minIndex(), frame.maxIndex(), frame.submissionCount());
     });
@@ -503,7 +505,6 @@
     });
 
     document.querySelector('#live').addEventListener('click', () => {
-      player.freezeFrame(DrawFrame.frameBuffer.numFrames - 1);
       player.live();
       has_disconnected = false;
       pause.removeAttribute('style');
diff --git a/tools/visual_debugger/frame.js b/tools/visual_debugger/frame.js
index d02d2c65..fe235f8 100644
--- a/tools/visual_debugger/frame.js
+++ b/tools/visual_debugger/frame.js
@@ -34,6 +34,18 @@
     this.instances[this.numFrames % this.maxSize] = frame;
     this.numFrames++;
   }
+
+  oldestIndex() {
+    if (this.numFrames <= this.maxSize) {
+      return 0;
+    } else {
+      return this.numFrames - this.maxSize;
+    }
+  }
+
+  newestIndex() {
+    return this.numFrames -  1;
+  }
 }
 
 // Represents a single frame, and contains all associated data.
@@ -108,30 +120,6 @@
     this.json_ = json;
 
     DrawFrame.frameBuffer.push(this);
-
-    // Update scrubber as new frames come in
-    const scrubberFrame = document.querySelector('#scrubberframe');
-
-    scrubberFrame.max = DrawFrame.frameBuffer.numFrames - 1;
-
-    // Handle scrubber when # of frames reached cap of circular buffer.
-    if (DrawFrame.frameBuffer.numFrames > DrawFrame.frameBuffer.maxSize) {
-      const oldestFrameId = DrawFrame.frameBuffer.numFrames
-                            - DrawFrame.frameBuffer.maxSize;
-
-      scrubberFrame.min = oldestFrameId;
-      // Once the scrubber reaches the left very (oldest frame),
-      // update scrubber value to match scrubber min value and
-      // update drawing on canvas to match correspondingly.
-      if (scrubberFrame.value <= scrubberFrame.min) {
-        scrubberFrame.value = oldestFrameId;
-        Player.instance.forward();
-      }
-    }
-    // Handle scrubber when # of frames haven't yet reached buffer cap.
-    else {
-      scrubberFrame.min = 0;
-    }
   }
 
   submissionCount() {
@@ -405,30 +393,6 @@
     this.updateLogs_();
   }
 
-  drawNextFrame() {
-    // When we switch to a different frame, we need to unfreeze the current
-    // frame (to make sure the frame draws completely the next time it is drawn
-    // in the player).
-    this.unfreeze();
-    if (DrawFrame.get(this.currentFrameIndex_ + 1)) {
-      ++this.currentFrameIndex_;
-      this.updateCurrentFrame();
-      return true;
-    }
-  }
-
-
-  drawPreviousFrame() {
-    // When we switch to a different frame, we need to unfreeze the current
-    // frame (to make sure the frame draws completely the next time it is drawn
-    // in the player).
-    this.unfreeze();
-    if (DrawFrame.get(this.currentFrameIndex_ - 1)) {
-      --this.currentFrameIndex_;
-      this.updateCurrentFrame();
-    }
-  }
-
   redrawCurrentFrame_() {
     const frame = this.getCurrentFrame();
     if (!frame) return;
@@ -463,7 +427,7 @@
     this.viewOrientation = orientationAsInt;
   }
 
-  freezeFrame(frameIndex, minIndex, maxIndex) {
+  setFrame(frameIndex, minIndex = -1, maxIndex = -1) {
     if (DrawFrame.get(frameIndex)) {
       this.currentFrameIndex_ = frameIndex;
       this.getCurrentFrame().filter(minIndex, maxIndex);
@@ -471,13 +435,6 @@
     }
   }
 
-  unfreeze() {
-    const frame = this.getCurrentFrame();
-    if (frame) {
-      frame.resetFilter();
-    }
-  }
-
   zoomToMouse(currentMouseX, currentMouseY, delta) {
     var factor = 1.1;
     if (delta > 0) {
@@ -499,12 +456,12 @@
 class Player {
   static instances = [];
 
-  constructor(viewer, draw_cb) {
+  constructor(viewer, updateUi) {
     this.viewer_ = viewer;
     this.paused_ = false;
     this.nextFrameScheduled_ = false;
     this.live_ = true;
-    this.drawCb_ = draw_cb;
+    this.updateUi_ = updateUi;
 
     Player.instances[0] = this;
   }
@@ -513,13 +470,17 @@
     this.paused_ = false;
     if (this.nextFrameScheduled_) return;
 
-    const drawn = this.viewer_.drawNextFrame();
-    if(this.live_){
-      while(this.viewer_.drawNextFrame());
+    if (this.viewer_.currentFrameIndex == DrawFrame.frameBuffer.newestIndex()) {
+      return;
+    }
+
+    if (this.live_) {
+      this.drawNewestFrame_();
+    } else {
+      this.drawNextFrame_();
     }
 
     this.didDrawNewFrame_();
-    if (!drawn) return;
 
     this.nextFrameScheduled_ = true;
     requestAnimationFrame(() => {
@@ -529,8 +490,7 @@
     });
   }
 
-  live()
-  {
+  live() {
     this.live_ = true;
     this.play();
   }
@@ -542,13 +502,13 @@
 
   rewind() {
     this.pause();
-    this.viewer_.drawPreviousFrame();
+    this.drawPreviousFrame_();
     this.didDrawNewFrame_();
   }
 
   forward() {
     this.pause();
-    this.viewer_.drawNextFrame();
+    this.drawNextFrame_();
     this.didDrawNewFrame_();
   }
 
@@ -557,7 +517,7 @@
   // Draws all calls if |minIndex| and |maxIndex| are not set.
   freezeFrame(frameIndex, minIndex = -1, maxIndex = -1) {
     this.pause();
-    this.viewer_.freezeFrame(frameIndex, minIndex, maxIndex);
+    this.viewer_.setFrame(frameIndex, minIndex, maxIndex);
     this.didDrawNewFrame_();
   }
 
@@ -582,16 +542,37 @@
     this.viewer_.updateCurrentFrame();
   }
 
+  drawNewestFrame_() {
+    let newest = DrawFrame.frameBuffer.newestIndex();
+    this.viewer_.setFrame(newest);
+  }
+
+  drawNextFrame_() {
+    this.viewer_.setFrame(this.viewer_.currentFrameIndex + 1);
+  }
+
+  drawPreviousFrame_() {
+    this.viewer_.setFrame(this.viewer_.currentFrameIndex - 1);
+  }
+
   didDrawNewFrame_() {
-    this.drawCb_(this.viewer_.getCurrentFrame());
+    this.updateUi_(this.viewer_.getCurrentFrame());
   }
 
   get currentFrameIndex() { return this.viewer_.currentFrameIndex; }
 
   onNewFrame() {
+    let oldest = DrawFrame.frameBuffer.oldestIndex();
+    if (this.currentFrameIndex < oldest) {
+      this.viewer_.setFrame(oldest, -1, -1);
+    }
+    this.didDrawNewFrame_();
+
     // If the player is not paused, and a new frame is received, then make sure
     // the next frame is drawn.
-    if (!this.paused_) this.play();
+    if (!this.paused_) {
+      this.play();
+    }
   }
 
   static get instance() { return Player.instances[0]; }
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index e563c3b..8017592 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -586,6 +586,7 @@
     "junit/src/org/chromium/ui/resources/dynamics/ViewResourceAdapterTest.java",
     "junit/src/org/chromium/ui/shadows/ShadowAppCompatResourcesTest.java",
     "junit/src/org/chromium/ui/text/SpanApplierTest.java",
+    "junit/src/org/chromium/ui/util/ColorUtilsTest.java",
     "junit/src/org/chromium/ui/util/TokenHolderTest.java",
     "junit/src/org/chromium/ui/widget/AnchoredPopupWindowTest.java",
     "junit/src/org/chromium/ui/widget/LoadingViewTest.java",
diff --git a/ui/android/OWNERS b/ui/android/OWNERS
index 45e2234..3314f9f6 100644
--- a/ui/android/OWNERS
+++ b/ui/android/OWNERS
@@ -1,3 +1,4 @@
+sinansahin@google.com
 tedchoc@chromium.org
 twellington@chromium.org
 
diff --git a/ui/android/java/src/org/chromium/ui/util/ColorUtils.java b/ui/android/java/src/org/chromium/ui/util/ColorUtils.java
index 6ce2865..d74036f 100644
--- a/ui/android/java/src/org/chromium/ui/util/ColorUtils.java
+++ b/ui/android/java/src/org/chromium/ui/util/ColorUtils.java
@@ -9,6 +9,7 @@
 import android.graphics.Color;
 
 import androidx.annotation.ColorInt;
+import androidx.annotation.FloatRange;
 import androidx.annotation.IntRange;
 
 import org.chromium.base.MathUtils;
@@ -70,7 +71,9 @@
      * @param overlayAlpha The alpha |overlayColor| should have on the base color.
      */
     public static @ColorInt int getColorWithOverlay(
-            @ColorInt int baseColor, @ColorInt int overlayColor, float overlayAlpha) {
+            @ColorInt int baseColor,
+            @ColorInt int overlayColor,
+            @FloatRange(from = 0f, to = 1f) float overlayAlpha) {
         return getColorWithOverlay(baseColor, overlayColor, overlayAlpha, false);
     }
 
@@ -165,11 +168,14 @@
      * @param overlayAlpha The alpha |overlayColor| should have on the base color.
      * @param considerOpacity indicates whether to take color opacity into consideration when
      *     calculating the new color.
+     * @deprecated Should not directly call this version, as considerOpacity has surprising
+     *     behavior. If you need to handle opacity, consider {@link #blendColorsMultiply} instead.
      */
+    @Deprecated
     public static @ColorInt int getColorWithOverlay(
             @ColorInt int baseColor,
             @ColorInt int overlayColor,
-            float overlayAlpha,
+            @FloatRange(from = 0f, to = 1f) float overlayAlpha,
             boolean considerOpacity) {
         int red =
                 (int)
@@ -196,6 +202,47 @@
     }
 
     /**
+     * Interpolates between two colors, using pre-multiplied alpha values. Tries to not allocate any
+     * new objects or lose any precision unnecessarily.
+     *
+     * @param from The color to start at, when fraction is at zero.
+     * @param to The color to end at, when the fraction is at one.
+     * @param fraction The percent through interpolation that's currently being calculated.
+     * @return The interpolated color value.
+     */
+    public static @ColorInt int blendColorsMultiply(
+            @ColorInt int from, @ColorInt int to, @FloatRange(from = 0f, to = 1f) float fraction) {
+        int fromAlpha = Color.alpha(from);
+        int toAlpha = Color.alpha(to);
+        // Alpha can be linearly interpolated. Keep the result as float to increase precision of
+        // the intermediate math. Lastly, this alpha value can be zero, and we're going to divide by
+        // it below. Surprisingly, no special casing is needed. If resultAlpha is zero, this is
+        // because one or both of the from/to alphas are also zero, and the color channel
+        // interpolation is always going to get zero back. Turns out in Java, 0.0f / 0.0f is NaN,
+        // and Math.round special cases this to return 0, which is what we'd want to do anyway.
+        float resultAlpha = MathUtils.interpolate(fromAlpha, toAlpha, fraction);
+
+        // Each rgb channel value is multiplied by source alpha before interpolation. Then it'll be
+        // divided by the result alpha at the end. This is the pre-multiplied alpha approach as
+        // detailed in https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending.
+        int fromRed = Color.red(from) * fromAlpha;
+        int toRed = Color.red(to) * toAlpha;
+        int resultRed = Math.round(MathUtils.interpolate(fromRed, toRed, fraction) / resultAlpha);
+
+        int fromGreen = Color.green(from) * fromAlpha;
+        int toGreen = Color.green(to) * toAlpha;
+        int resultGreen =
+                Math.round(MathUtils.interpolate(fromGreen, toGreen, fraction) / resultAlpha);
+
+        int fromBlue = Color.blue(from) * fromAlpha;
+        int toBlue = Color.blue(to) * toAlpha;
+        int resultBlue =
+                Math.round(MathUtils.interpolate(fromBlue, toBlue, fraction) / resultAlpha);
+
+        return Color.argb(Math.round(resultAlpha), resultRed, resultGreen, resultBlue);
+    }
+
+    /**
      * Pass through to {@link androidx.core.graphics.ColorUtils#setAlphaComponent(int, int)} so
      * callers that need methods out of this class don't need to bother importing two versions of
      * classes named "ColorUtils".
diff --git a/ui/android/junit/src/org/chromium/ui/util/ColorUtilsTest.java b/ui/android/junit/src/org/chromium/ui/util/ColorUtilsTest.java
new file mode 100644
index 0000000..b36adc5
--- /dev/null
+++ b/ui/android/junit/src/org/chromium/ui/util/ColorUtilsTest.java
@@ -0,0 +1,105 @@
+// 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.
+
+package org.chromium.ui.util;
+
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Color;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.FloatRange;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/** Unit tests for {@link ColorUtils}. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class ColorUtilsTest {
+    // Use fixed fractions to avoid any float addition that might not exactly hit 0f and 1f.
+    private static final float[] FRACTIONS = {0f, .1f, .2f, .3f, .4f, .5f, .6f, .7f, .8f, .9f, 1f};
+
+    // These cannot have any alpha.
+    private static final int[] BACKGROUNDS = {
+        Color.parseColor("#FFFFFF"),
+        Color.parseColor("#000000"),
+        Color.parseColor("#115599"),
+        Color.parseColor("#888888")
+    };
+
+    private static final int[] COLORS = {
+        Color.parseColor("#00000000"),
+        Color.parseColor("#00123456"),
+        Color.parseColor("#00FFFFFF"),
+        Color.parseColor("#FF000000"),
+        Color.parseColor("#FF123456"),
+        Color.parseColor("#FFFFFFFF"),
+        Color.parseColor("#22446688"),
+        Color.parseColor("#88888888"),
+        Color.parseColor("#12345678"),
+    };
+
+    @Test
+    public void testBlendColorsMultiply() {
+        for (@ColorInt int background : BACKGROUNDS) {
+            for (@ColorInt int from : COLORS) {
+                for (@ColorInt int to : COLORS) {
+                    testBlendColorsMultiplyHelper(background, from, to);
+                }
+            }
+        }
+    }
+
+    private void testBlendColorsMultiplyHelper(
+            @ColorInt int background, @ColorInt int from, @ColorInt int to) {
+        String sharedMessage = formatColors("background:%s from:%s to:%s", background, from, to);
+        // Calculates an expected color by pre-flattening everything onto the background and an
+        // actual value by using the pre multiply blend mechanism that combines two colors with
+        // alpha values. These two approaches should return the same color.
+        @ColorInt int fromFlat = flatten(background, from);
+        @ColorInt int toFlat = flatten(background, to);
+        for (@FloatRange(from = 0f, to = 1f) float fraction : FRACTIONS) {
+            @ColorInt int flatBlend = ColorUtils.getColorWithOverlay(fromFlat, toFlat, fraction);
+            @ColorInt int blend = ColorUtils.blendColorsMultiply(from, to, fraction);
+            @ColorInt int blendOnBackground = flatten(background, blend);
+            String fractionMessage = String.format("%s fraction:%s", sharedMessage, fraction);
+            assertColorsEqual(fractionMessage, flatBlend, blendOnBackground);
+        }
+    }
+
+    private @ColorInt int flatten(@ColorInt int background, @ColorInt int overlay) {
+        assert Color.alpha(background) == 255;
+        return ColorUtils.getColorWithOverlay(background, overlay, Color.alpha(overlay) / 255f);
+    }
+
+    private void assertColorsEqual(
+            String testMessage, @ColorInt int expected, @ColorInt int actual) {
+        String compareMessage =
+                String.format(
+                        "%s expected:%s actual:%s",
+                        testMessage, printColor(expected), printColor(actual));
+        // Allow for rounding errors where things are off by 1.
+        assertTrue(compareMessage, Math.abs(Color.red(expected) - Color.red(actual)) <= 1);
+        assertTrue(compareMessage, Math.abs(Color.green(expected) - Color.green(actual)) <= 1);
+        assertTrue(compareMessage, Math.abs(Color.blue(expected) - Color.blue(actual)) <= 1);
+        assertTrue(compareMessage, Math.abs(Color.alpha(expected) - Color.alpha(actual)) <= 1);
+    }
+
+    private String printColor(@ColorInt int color) {
+        return String.format(
+                "{r:%s,g:%s,b:%s,a:%s",
+                Color.red(color), Color.green(color), Color.blue(color), Color.alpha(color));
+    }
+
+    private String formatColors(String formatString, int... colors) {
+        int length = colors.length;
+        Object[] colorStrings = new String[length];
+        for (int i = 0; i < length; ++i) {
+            colorStrings[i] = printColor(colors[i]);
+        }
+        return String.format(formatString, colorStrings);
+    }
+}
diff --git a/ui/file_manager/file_manager/foreground/js/metrics_start.js b/ui/file_manager/file_manager/foreground/js/metrics_start.ts
similarity index 100%
rename from ui/file_manager/file_manager/foreground/js/metrics_start.js
rename to ui/file_manager/file_manager/foreground/js/metrics_start.ts
diff --git a/ui/file_manager/file_names.gni b/ui/file_manager/file_names.gni
index b35899f..18b87de 100644
--- a/ui/file_manager/file_names.gni
+++ b/ui/file_manager/file_names.gni
@@ -27,7 +27,6 @@
   "file_manager/externs/ts/store.js",
 
   # Files app Foreground:
-  "file_manager/foreground/js/metrics_start.js",
   "file_manager/foreground/js/mock_navigation_list_model.js",
   "file_manager/foreground/js/navigation_list_model.js",
 
@@ -291,6 +290,7 @@
   "file_manager/foreground/js/main_window_component.ts",
   "file_manager/foreground/js/metadata_box_controller.ts",
   "file_manager/foreground/js/metadata_update_controller.ts",
+  "file_manager/foreground/js/metrics_start.ts",
   "file_manager/foreground/js/mock_actions_model.ts",
   "file_manager/foreground/js/mock_directory_model.ts",
   "file_manager/foreground/js/mock_folder_shortcut_data_model.ts",
diff --git a/ui/ozone/platform/drm/gpu/drm_display_unittest.cc b/ui/ozone/platform/drm/gpu/drm_display_unittest.cc
index b55b3943..2d3ea3a 100644
--- a/ui/ozone/platform/drm/gpu/drm_display_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/drm_display_unittest.cc
@@ -137,12 +137,7 @@
                     uint32_t crtc_index) const override {
     return false;
   }
-  bool CommitColorMatrix(const CrtcProperties& crtc_props) override {
-    return false;
-  }
-  bool CommitGammaCorrection(const CrtcProperties& crtc_props) override {
-    return false;
-  }
+  bool CommitPendingCrtcState(CrtcState* state) override { return false; }
 };
 
 }  // namespace
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
index 4f12f02..4d58c8d 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
@@ -318,10 +318,10 @@
     return false;
   }
 
-  crtc_state->ctm_blob =
+  crtc_state->pending_ctm_blob =
       drm_->CreatePropertyBlob(ctm_blob_data.get(), sizeof(drm_color_ctm));
-  crtc_state->properties.ctm.value = crtc_state->ctm_blob->id();
-  return CommitColorMatrix(crtc_state->properties);
+
+  return CommitPendingCrtcState(crtc_state);
 }
 
 void HardwareDisplayPlaneManager::SetBackgroundColor(
@@ -368,24 +368,22 @@
       CreateLutBlob(gamma_curve, crtc_props->gamma_lut_size.value);
 
   if (degamma_blob_data) {
-    crtc_state->degamma_lut_blob = drm_->CreatePropertyBlob(
+    crtc_state->pending_degamma_lut_blob = drm_->CreatePropertyBlob(
         degamma_blob_data.get(),
         sizeof(drm_color_lut) * crtc_props->degamma_lut_size.value);
-    crtc_props->degamma_lut.value = crtc_state->degamma_lut_blob->id();
   } else {
-    crtc_props->degamma_lut.value = 0;
+    crtc_state->pending_degamma_lut_blob = nullptr;
   }
 
   if (gamma_blob_data) {
-    crtc_state->gamma_lut_blob = drm_->CreatePropertyBlob(
+    crtc_state->pending_gamma_lut_blob = drm_->CreatePropertyBlob(
         gamma_blob_data.get(),
         sizeof(drm_color_lut) * crtc_props->gamma_lut_size.value);
-    crtc_props->gamma_lut.value = crtc_state->gamma_lut_blob->id();
   } else {
-    crtc_props->gamma_lut.value = 0;
+    crtc_state->pending_gamma_lut_blob = nullptr;
   }
 
-  return CommitGammaCorrection(*crtc_props);
+  return CommitPendingCrtcState(crtc_state);
 }
 
 bool HardwareDisplayPlaneManager::InitializeCrtcState() {
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
index 39acd1c..b15102f 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
@@ -102,11 +102,13 @@
 
     CrtcProperties properties = {};
 
-    // Cached blobs for the properties since the CRTC properties are applied on
-    // the next page flip and we need to keep the properties valid until then.
-    ScopedDrmPropertyBlob ctm_blob;
-    ScopedDrmPropertyBlob gamma_lut_blob;
-    ScopedDrmPropertyBlob degamma_lut_blob;
+    // Cached blobs for the properties to commit in CommitCrtcProperties.
+    // * If a property is `absl::nullopt`, then it should be left unchanged.
+    // * If a property is `nullptr` then it should be set to 0.
+    // * If a property is a blob, then it should be set to that blob.
+    absl::optional<ScopedDrmPropertyBlob> pending_ctm_blob;
+    absl::optional<ScopedDrmPropertyBlob> pending_gamma_lut_blob;
+    absl::optional<ScopedDrmPropertyBlob> pending_degamma_lut_blob;
   };
 
   explicit HardwareDisplayPlaneManager(DrmDevice* drm);
@@ -272,9 +274,7 @@
   // Populates scanout formats supported by all planes.
   void PopulateSupportedFormats();
 
-  virtual bool CommitColorMatrix(const CrtcProperties& crtc_props) = 0;
-
-  virtual bool CommitGammaCorrection(const CrtcProperties& crtc_props) = 0;
+  virtual bool CommitPendingCrtcState(CrtcState* state) = 0;
 
   // Object containing the connection to the graphics device and wraps the API
   // calls to control it. Not owned.
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
index 606a72b..63470a0 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
@@ -62,6 +62,44 @@
   return crtcs;
 }
 
+bool AddPendingCrtcProperty(drmModeAtomicReq* property_set,
+                            uint32_t crtc_id,
+                            DrmWrapper::Property& prop,
+                            absl::optional<ScopedDrmPropertyBlob>& pending_blob,
+                            std::vector<ScopedDrmPropertyBlob>& pending_blobs) {
+  // If `pending_blob` is absl::nullopt then don't change the property.
+  if (!pending_blob.has_value()) {
+    return true;
+  }
+
+  // Take the pending blob. If we successfully set it, we'll add it to
+  // `pending_blobs`.
+  ScopedDrmPropertyBlob blob = std::move(pending_blob.value());
+  pending_blob = absl::nullopt;
+  if (!prop.id) {
+    return true;
+  }
+
+  // Update the CRTC property and add the change to the commit.
+  if (blob) {
+    prop.value = blob->id();
+  } else {
+    // If the blob was nullptr, then un-set the property.
+    prop.value = 0;
+  }
+  int ret =
+      drmModeAtomicAddProperty(property_set, crtc_id, prop.id, prop.value);
+  if (ret < 0) {
+    LOG(ERROR) << "Failed to set CTM property for crtc=" << crtc_id;
+    return false;
+  }
+
+  // Save the pending blob in `pending_blobs` so that it can be freed after
+  // `property_set` is committed.
+  pending_blobs.push_back(std::move(blob));
+  return true;
+}
+
 }  // namespace
 
 HardwareDisplayPlaneManagerAtomic::HardwareDisplayPlaneManagerAtomic(
@@ -412,15 +450,38 @@
   return std::make_unique<HardwareDisplayPlaneAtomic>(plane_id);
 }
 
-bool HardwareDisplayPlaneManagerAtomic::CommitColorMatrix(
-    const CrtcProperties& crtc_props) {
-  DCHECK(crtc_props.ctm.id);
+bool HardwareDisplayPlaneManagerAtomic::CommitPendingCrtcState(
+    CrtcState* crtc_state) {
+  CrtcProperties& crtc_props = crtc_state->properties;
+  std::vector<ScopedDrmPropertyBlob> pending_blobs;
   ScopedDrmAtomicReqPtr property_set(drmModeAtomicAlloc());
-  int ret = drmModeAtomicAddProperty(property_set.get(), crtc_props.id,
-                                     crtc_props.ctm.id, crtc_props.ctm.value);
-  if (ret < 0) {
+  bool result = true;
+
+  if (!AddPendingCrtcProperty(property_set.get(), crtc_props.id, crtc_props.ctm,
+                              crtc_state->pending_ctm_blob, pending_blobs)) {
     LOG(ERROR) << "Failed to set CTM property for crtc=" << crtc_props.id;
-    return false;
+    result = false;
+  }
+
+  if (!AddPendingCrtcProperty(
+          property_set.get(), crtc_props.id, crtc_props.degamma_lut,
+          crtc_state->pending_degamma_lut_blob, pending_blobs)) {
+    LOG(ERROR) << "Failed to set DEGAMMA_LUT property for crtc="
+               << crtc_props.id;
+    result = false;
+  }
+
+  if (!AddPendingCrtcProperty(
+          property_set.get(), crtc_props.id, crtc_props.gamma_lut,
+          crtc_state->pending_gamma_lut_blob, pending_blobs)) {
+    LOG(ERROR) << "Failed to set GAMMA_LUT property for crtc="
+               << crtc_props.id;
+    result = false;
+  }
+
+  // If we aren't committing any new blobs, early-out.
+  if (pending_blobs.empty()) {
+    return result;
   }
 
   // If we try to do this in a non-blocking fashion this can return EBUSY since
@@ -428,41 +489,12 @@
   // API) to ensure the properties are applied.
   // TODO(dnicoara): Should cache these values locally and aggregate them with
   // the page flip event otherwise this "steals" a vsync to apply the property.
-  return drm_->CommitProperties(property_set.get(), 0, 0, nullptr);
-}
-
-bool HardwareDisplayPlaneManagerAtomic::CommitGammaCorrection(
-    const CrtcProperties& crtc_props) {
-  DCHECK(crtc_props.degamma_lut.id || crtc_props.gamma_lut.id);
-  ScopedDrmAtomicReqPtr property_set(drmModeAtomicAlloc());
-  if (crtc_props.degamma_lut.id) {
-    int ret = drmModeAtomicAddProperty(property_set.get(), crtc_props.id,
-                                       crtc_props.degamma_lut.id,
-                                       crtc_props.degamma_lut.value);
-    if (ret < 0) {
-      LOG(ERROR) << "Failed to set DEGAMMA_LUT property for crtc="
-                 << crtc_props.id;
-      return false;
-    }
+  if (!drm_->CommitProperties(property_set.get(), 0, 0, nullptr)) {
+    LOG(ERROR) << "Failed to commit properties for crtc=" << crtc_props.id;
+    result = false;
   }
 
-  if (crtc_props.gamma_lut.id) {
-    int ret = drmModeAtomicAddProperty(property_set.get(), crtc_props.id,
-                                       crtc_props.gamma_lut.id,
-                                       crtc_props.gamma_lut.value);
-    if (ret < 0) {
-      LOG(ERROR) << "Failed to set GAMMA_LUT property for crtc="
-                 << crtc_props.id;
-      return false;
-    }
-  }
-
-  // If we try to do this in a non-blocking fashion this can return EBUSY since
-  // there is a pending page flip. Do a blocking commit (the same as the legacy
-  // API) to ensure the properties are applied.
-  // TODO(dnicoara): Should cache these values locally and aggregate them with
-  // the page flip event otherwise this "steals" a vsync to apply the property.
-  return drm_->CommitProperties(property_set.get(), 0, 0, nullptr);
+  return result;
 }
 
 bool HardwareDisplayPlaneManagerAtomic::AddOutFencePtrProperties(
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h
index 76b34f9..076fcc4 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h
@@ -60,9 +60,7 @@
   bool SetConnectorProps(drmModeAtomicReq* atomic_request,
                          uint32_t connector_id,
                          uint32_t crtc_id);
-
-  bool CommitColorMatrix(const CrtcProperties& crtc_props) override;
-  bool CommitGammaCorrection(const CrtcProperties& crtc_props) override;
+  bool CommitPendingCrtcState(CrtcState* state) override;
   bool AddOutFencePtrProperties(
       drmModeAtomicReq* property_set,
       const std::vector<uint32_t>& crtcs,
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
index e9e8c86..a607a98 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
@@ -38,6 +38,29 @@
   return planes;
 }
 
+bool CommitPendingCrtcProperty(
+    DrmDevice* device,
+    uint32_t crtc_id,
+    DrmWrapper::Property& prop,
+    absl::optional<ScopedDrmPropertyBlob>& pending_blob) {
+  if (!pending_blob.has_value()) {
+    return true;
+  }
+  ScopedDrmPropertyBlob blob = std::move(pending_blob.value());
+  pending_blob = absl::nullopt;
+  if (!prop.id) {
+    return true;
+  }
+
+  prop.value = blob ? blob->id() : 0;
+  int ret = device->SetObjectProperty(crtc_id, DRM_MODE_OBJECT_CRTC, prop.id,
+                                      prop.value);
+  if (ret < 0) {
+    return false;
+  }
+  return true;
+}
+
 }  // namespace
 
 HardwareDisplayPlaneManagerLegacy::HardwareDisplayPlaneManagerLegacy(
@@ -208,39 +231,28 @@
   return plane->IsSupportedFormat(format);
 }
 
-bool HardwareDisplayPlaneManagerLegacy::CommitColorMatrix(
-    const CrtcProperties& crtc_props) {
-  return drm_->SetObjectProperty(crtc_props.id, DRM_MODE_OBJECT_CRTC,
-                                 crtc_props.ctm.id, crtc_props.ctm.value);
-}
+bool HardwareDisplayPlaneManagerLegacy::CommitPendingCrtcState(
+    CrtcState* crtc_state) {
+  CrtcProperties& crtc_props = crtc_state->properties;
+  bool result = true;
 
-bool HardwareDisplayPlaneManagerLegacy::CommitGammaCorrection(
-    const CrtcProperties& crtc_props) {
-  DCHECK(crtc_props.degamma_lut.id || crtc_props.gamma_lut.id);
-
-  if (crtc_props.degamma_lut.id) {
-    int ret = drm_->SetObjectProperty(crtc_props.id, DRM_MODE_OBJECT_CRTC,
-                                      crtc_props.degamma_lut.id,
-                                      crtc_props.degamma_lut.value);
-    if (ret < 0) {
-      LOG(ERROR) << "Failed to set DEGAMMA_LUT property for crtc="
-                 << crtc_props.id;
-      return false;
-    }
+  if (!CommitPendingCrtcProperty(drm_, crtc_props.id, crtc_props.ctm,
+                                 crtc_state->pending_ctm_blob)) {
+    LOG(ERROR) << "Failed to set CTM property for crtc=" << crtc_props.id;
+    result = false;
   }
-
-  if (crtc_props.gamma_lut.id) {
-    int ret = drm_->SetObjectProperty(crtc_props.id, DRM_MODE_OBJECT_CRTC,
-                                      crtc_props.gamma_lut.id,
-                                      crtc_props.gamma_lut.value);
-    if (ret < 0) {
-      LOG(ERROR) << "Failed to set GAMMA_LUT property for crtc="
-                 << crtc_props.id;
-      return false;
-    }
+  if (!CommitPendingCrtcProperty(drm_, crtc_props.id, crtc_props.gamma_lut,
+                                 crtc_state->pending_gamma_lut_blob)) {
+    LOG(ERROR) << "Failed to set GAMMA_LUT property for crtc=" << crtc_props.id;
+    result = false;
   }
-
-  return true;
+  if (!CommitPendingCrtcProperty(drm_, crtc_props.id, crtc_props.degamma_lut,
+                                 crtc_state->pending_degamma_lut_blob)) {
+    LOG(ERROR) << "Failed to set DEGAMMA_LUT property for crtc="
+               << crtc_props.id;
+    result = false;
+  }
+  return result;
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h
index 54e833e..49fb4bb 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h
@@ -51,8 +51,7 @@
   bool IsCompatible(HardwareDisplayPlane* plane,
                     const DrmOverlayPlane& overlay,
                     uint32_t crtc_id) const override;
-  bool CommitColorMatrix(const CrtcProperties& crtc_props) override;
-  bool CommitGammaCorrection(const CrtcProperties& crtc_props) override;
+  bool CommitPendingCrtcState(CrtcState* state) override;
 };
 
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index 71850fc..640f5c3 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -218,6 +218,11 @@
 #endif
   }
 
+  bool IsWindowCompositingSupported() const override {
+    // Wayland always supports compositing.
+    return true;
+  }
+
   bool ShouldUseCustomFrame() override {
     return connection_->xdg_decoration_manager_v1() == nullptr;
   }
diff --git a/ui/ozone/platform/x11/ozone_platform_x11.cc b/ui/ozone/platform/x11/ozone_platform_x11.cc
index 348cf4c..62382a2 100644
--- a/ui/ozone/platform/x11/ozone_platform_x11.cc
+++ b/ui/ozone/platform/x11/ozone_platform_x11.cc
@@ -31,6 +31,7 @@
 #include "ui/gfx/linux/gpu_memory_buffer_support_x11.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/switches.h"
+#include "ui/gfx/x/visual_manager.h"
 #include "ui/linux/linux_ui_delegate.h"
 #include "ui/ozone/common/stub_overlay_manager.h"
 #include "ui/ozone/platform/x11/gl_egl_utility_x11.h"
@@ -226,6 +227,12 @@
     return false;
   }
 
+  bool IsWindowCompositingSupported() const override {
+    return x11::Connection::Get()
+        ->GetOrCreateVisualManager()
+        .ArgbVisualAvailable();
+  }
+
   bool InitializeUI(const InitParams& params) override {
     if (ShouldFailInitializeUIForTest()) {
       LOG(ERROR) << "Failing for test";
diff --git a/ui/ozone/public/ozone_platform.cc b/ui/ozone/public/ozone_platform.cc
index b7c7ec8..4fcb13d4 100644
--- a/ui/ozone/public/ozone_platform.cc
+++ b/ui/ozone/public/ozone_platform.cc
@@ -151,6 +151,10 @@
   return false;
 }
 
+bool OzonePlatform::IsWindowCompositingSupported() const {
+  return false;
+}
+
 bool OzonePlatform::ShouldUseCustomFrame() {
   return GetPlatformProperties().custom_frame_pref_default;
 }
diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h
index 78b46426..29ff7bf 100644
--- a/ui/ozone/public/ozone_platform.h
+++ b/ui/ozone/public/ozone_platform.h
@@ -315,6 +315,9 @@
   virtual bool IsNativePixmapConfigSupported(gfx::BufferFormat format,
                                              gfx::BufferUsage usage) const;
 
+  // Whether the platform supports compositing windows with transparency.
+  virtual bool IsWindowCompositingSupported() const;
+
   // Returns whether a custom frame should be used for windows.
   // The default behaviour is returning what is suggested by the
   // custom_frame_pref_default property of the platform: if the platform
diff --git a/ui/views/controls/menu/menu_config_linux.cc b/ui/views/controls/menu/menu_config_linux.cc
index 4b0482ac..a32b05f 100644
--- a/ui/views/controls/menu/menu_config_linux.cc
+++ b/ui/views/controls/menu/menu_config_linux.cc
@@ -5,6 +5,7 @@
 #include "ui/views/controls/menu/menu_config.h"
 
 #include "ui/base/ui_base_features.h"
+#include "ui/ozone/public/ozone_platform.h"
 
 namespace views {
 
@@ -16,7 +17,8 @@
 
 void MenuConfig::InitPlatformCR2023() {
   context_menu_font_list = font_list;
-  use_bubble_border = true;
+  use_bubble_border =
+      ui::OzonePlatform::GetInstance()->IsWindowCompositingSupported();
 }
 
 }  // namespace views
diff --git a/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html b/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
index 86357bf..5c280bd 100644
--- a/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
+++ b/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
@@ -1,14 +1,14 @@
     <style>
       :host {
         --cr-drawer-width: 256px;
-        --cr-drawer-border-start-end-radius: 0;
       }
 
       :host dialog {
         --transition-timing: 200ms ease;
         background-color: var(--cr-drawer-background-color, #fff);
         border: none;
-        border-start-end-radius: var(--cr-drawer-border-start-end-radius);
+        border-start-end-radius: var(--cr-drawer-border-start-end-radius, 0);
+        border-end-end-radius: var(--cr-drawer-border-end-end-radius, 0);
         bottom: 0;
         left: calc(-1 * var(--cr-drawer-width));
         margin: 0;
@@ -71,6 +71,7 @@
         display: flex;
         font-size: 123.08%;  /* go to 16px from 13px */
         font-weight: var(--cr-drawer-header-font-weight, inherit);
+        font: var(--cr-drawer-header-font, inherit);
         min-height: 56px;
         padding-inline-start: var(--cr-drawer-header-padding, 24px);
       }
diff --git a/v8 b/v8
index 3469157..e6049fb 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 3469157c02160bf1e6035c2a15087e7702fa75a0
+Subproject commit e6049fb98502acb2afb1ec9f5181c3db5e08c9b7