diff --git a/DEPS b/DEPS
index c3ba0b6..79f926e 100644
--- a/DEPS
+++ b/DEPS
@@ -299,19 +299,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': 'b368746d696a72bff291dddf7ab1492f5d72267f',
+  'skia_revision': '215251846d262ed758bc0d95180f02e16abc79f7',
   # 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': '6eadb14bb7a7908c7c0b86a0381260430b6c81fc',
+  'v8_revision': 'a6545c66db9fcecbbe964ca2adb49eb92651d9c4',
   # 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': '0f3aaebf742f0ac16360a1191d75f99843d1b255',
+  'angle_revision': '412dd36859d326204bd2d3f15f12ba94abf3b81e',
   # 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': 'fbcd6095717437609d567fc7eaad5d8575677b19',
+  'swiftshader_revision': 'c70b46423fc435f9b2d0c7c8e7302331320e54ab',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -370,7 +370,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'e28e9f182ed6c8f468cd44c2a908af230e4cdf16',
+  'catapult_revision': '0d416fe68a0e1f6712e75a8f9c7a019e8385a11e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -438,7 +438,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nearby
   # and whatever else without interference from each other.
-  'nearby_revision': 'b3f85342261db10bbeb2e5a07c7e54b616309b9e',
+  'nearby_revision': 'ad9c26ec909292c46e3fd71993d8185463615b26',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling securemessage
   # and whatever else without interference from each other.
@@ -771,7 +771,7 @@
 
   'src/clank': {
     'url': 'https://chrome-internal.googlesource.com/clank/internal/apps.git' + '@' +
-    'd6e61b675e9d5f3064111c6ea37c8c58312e4d60',
+    '61dc76dde856d963d23b1ee183208d4405f339e7',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1205,7 +1205,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '18359bb9a19ee9b31a33fae83aa49ced770a9b81',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9ace9b4dc4f5c4ef83ce916e0537c2ada7bb8e3d',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1640,7 +1640,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'aa7e493dcf9bf77707735b98723b916e08561bbe',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '4ffacbee2eaca86dde29a7a22fcf0ff0455f74b8',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1785,7 +1785,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@65f11fade2bd2b9f23d9c7c1e76087caac3490b7',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@c77b9ed3b121eab0a478257d7adc770ec379f3bb',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1895,7 +1895,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@94562e58e6e22f0f8b5c1dd89ab590f94494bf85',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7fba81cfcc115d968140a7a170abe6336c809717',
     'condition': 'checkout_src_internal',
   },
 
@@ -1947,7 +1947,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'Ob23mvxHPbJ009KdpT2COStZxs0gKpKxPoiKPw5Y1rIC',
+        'version': 'FEgnPkcHkeDWehL6Lgt_7aeLRFMDt0iL7Dkl0xWEaf4C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
index dd2aa9e..c0ed93e4 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
@@ -234,8 +234,6 @@
                 mAwProxyController = new AwProxyController();
             }
 
-            mFactory.getRunQueue().drainQueue();
-
             if (BuildInfo.isAtLeastT()
                             ? CompatChanges.isChangeEnabled(WebSettings.ENABLE_SIMPLIFIED_DARK_MODE)
                             : BuildInfo.targetsAtLeastT()) {
@@ -245,6 +243,10 @@
             if (CommandLine.getInstance().hasSwitch(AwSwitches.WEBVIEW_VERBOSE_LOGGING)) {
                 logCommandLineAndActiveTrials();
             }
+
+            // This runs all the pending tasks queued for after Chromium init is finished,
+            // so should be the last thing that happens in startChromiumLocked.
+            mFactory.getRunQueue().drainQueue();
         }
         RecordHistogram.recordTimesHistogram(
                 "Android.WebView.Startup.CreationTime.StartChromiumLocked",
diff --git a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
index be51a8b..6a9e2ba 100644
--- a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
+++ b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
@@ -52,6 +52,7 @@
 
 import androidx.activity.result.ActivityResultCallback;
 import androidx.activity.result.ActivityResultLauncher;
+import androidx.annotation.RequiresApi;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.appcompat.app.AppCompatDelegate;
 import androidx.appcompat.widget.Toolbar;
@@ -568,6 +569,7 @@
 
     // WebKit permissions which can be granted because either they have no associated Android
     // permission or the associated Android permission has been granted
+    @RequiresApi(Build.VERSION_CODES.M)
     private boolean canGrant(String webkitPermission) {
         String androidPermission = sPermissions.get(webkitPermission);
         if (androidPermission.equals(NO_ANDROID_PERMISSION)) {
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 3809647..e0dba24 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -2279,6 +2279,8 @@
     "wm/tablet_mode/tablet_mode_browser_window_drag_session_windows_hider.h",
     "wm/tablet_mode/tablet_mode_controller.cc",
     "wm/tablet_mode/tablet_mode_controller.h",
+    "wm/tablet_mode/tablet_mode_multitask_cue.cc",
+    "wm/tablet_mode/tablet_mode_multitask_cue.h",
     "wm/tablet_mode/tablet_mode_multitask_menu.cc",
     "wm/tablet_mode/tablet_mode_multitask_menu.h",
     "wm/tablet_mode/tablet_mode_multitask_menu_event_handler.cc",
@@ -2487,6 +2489,7 @@
     "//chromeos/ash/components/peripheral_notification",
     "//chromeos/ash/components/phonehub",
     "//chromeos/ash/components/settings",
+    "//chromeos/ash/components/system",
     "//chromeos/ash/services/assistant/public/cpp",
     "//chromeos/ash/services/assistant/public/mojom",
     "//chromeos/ash/services/bluetooth_config/public/cpp",
@@ -2516,7 +2519,6 @@
     "//chromeos/services/network_config/public/cpp",
     "//chromeos/services/network_config/public/mojom",
     "//chromeos/strings",
-    "//chromeos/system",
     "//chromeos/ui/base",
     "//chromeos/ui/frame",
     "//chromeos/ui/wm",
@@ -3274,6 +3276,7 @@
     "wm/tablet_mode/accelerometer_test_data_literals.cc",
     "wm/tablet_mode/tablet_mode_browser_window_drag_session_windows_hider_unittest.cc",
     "wm/tablet_mode/tablet_mode_controller_unittest.cc",
+    "wm/tablet_mode/tablet_mode_multitask_cue_unittest.cc",
     "wm/tablet_mode/tablet_mode_multitask_menu_event_handler_unittest.cc",
     "wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler_unittest.cc",
     "wm/tablet_mode/tablet_mode_window_manager_unittest.cc",
@@ -3371,6 +3374,7 @@
     "//chromeos/ash/components/network:test_support",
     "//chromeos/ash/components/phonehub:test_support",
     "//chromeos/ash/components/settings",
+    "//chromeos/ash/components/system",
     "//chromeos/ash/services/assistant:test_support",
     "//chromeos/ash/services/assistant/public/cpp",
     "//chromeos/ash/services/assistant/public/mojom",
@@ -3391,7 +3395,6 @@
     "//chromeos/services/network_config/public/cpp",
     "//chromeos/services/network_config/public/mojom",
     "//chromeos/strings:strings_grit",
-    "//chromeos/system",
     "//chromeos/ui/base",
     "//chromeos/ui/frame",
     "//chromeos/ui/frame:test_support",
@@ -3841,6 +3844,7 @@
     "//chromeos/ash/components/geolocation",
     "//chromeos/ash/components/login/login_state",
     "//chromeos/ash/components/network:test_support",
+    "//chromeos/ash/components/system",
     "//chromeos/ash/services/assistant:test_support",
     "//chromeos/ash/services/assistant/public/cpp",
     "//chromeos/ash/services/bluetooth_config:test_support",
@@ -3848,7 +3852,6 @@
     "//chromeos/ash/services/recording/public/mojom",
     "//chromeos/dbus/power",
     "//chromeos/dbus/power:power_manager_proto",
-    "//chromeos/system",
     "//chromeos/ui/frame",
     "//components/account_id",
     "//components/desks_storage",
diff --git a/ash/DEPS b/ash/DEPS
index e8b5fe38..c3f3aac1 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -69,6 +69,7 @@
   "+chromeos/ash/components/multidevice",
   "+chromeos/ash/components/peripheral_notification",
   "+chromeos/ash/components/proximity_auth",
+  "+chromeos/ash/components/system",
   "+chromeos/components/quick_answers",
   "+chromeos/components/security_token_pin",
   "+chromeos/components/sensors",
@@ -101,7 +102,6 @@
   "+chromeos/services/network_config/public",
   "+chromeos/services/power/public",
   "+chromeos/strings",
-  "+chromeos/system",
   "+chromeos/ui",
 
   # ui/base/idle depends on SessionManagerClient so disallow it.
diff --git a/ash/app_list/views/app_list_view_pixeltest.cc b/ash/app_list/views/app_list_view_pixeltest.cc
index 103efa2..e63ffbc 100644
--- a/ash/app_list/views/app_list_view_pixeltest.cc
+++ b/ash/app_list/views/app_list_view_pixeltest.cc
@@ -67,11 +67,45 @@
     // Adding results will schedule Update().
     base::RunLoop().RunUntilIdle();
   }
+
+  void SetUpURLResult(SearchModel::SearchResults* results,
+                      int init_id,
+                      int new_result_count) {
+    auto result = std::make_unique<TestSearchResult>();
+    result->set_result_id(base::NumberToString(init_id));
+    result->set_display_type(ash::SearchResultDisplayType::kList);
+
+    std::vector<SearchResult::TextItem> title_text_vector;
+    SearchResult::TextItem title_text_item_1(
+        ash::SearchResultTextItemType::kString);
+    title_text_item_1.SetText(u"youtube");
+    title_text_item_1.SetTextTags({SearchResult::Tag(
+        SearchResult::Tag::NONE, 0, result->details().length())});
+    title_text_vector.push_back(title_text_item_1);
+    result->SetTitleTextVector(title_text_vector);
+
+    std::vector<SearchResult::TextItem> details_text_vector;
+    SearchResult::TextItem details_text_item_1(
+        ash::SearchResultTextItemType::kString);
+    details_text_item_1.SetText(u"youtube.com");
+    details_text_item_1.SetTextTags({SearchResult::Tag(
+        SearchResult::Tag::URL, 0, result->details().length())});
+    details_text_vector.push_back(details_text_item_1);
+    result->SetDetailsTextVector(details_text_vector);
+
+    result->SetAccessibleName(u"Accessible Name");
+    result->set_result_id("Test Search Result");
+    result->set_best_match(true);
+    results->Add(std::move(result));
+
+    // Adding results will schedule Update().
+    base::RunLoop().RunUntilIdle();
+  }
 };
 
 INSTANTIATE_TEST_SUITE_P(RTL, AppListViewPixelRTLTest, testing::Bool());
 
-// Verifies best match search results under the clamshell mode.
+// Verifies Answer Card search results under the clamshell mode.
 TEST_P(AppListViewPixelRTLTest, AnswerCardSearchResult) {
   ShowAppList();
 
@@ -80,7 +114,7 @@
   // Populate answer card result.
   auto* test_helper = GetAppListTestHelper();
   SearchModel::SearchResults* results = test_helper->GetSearchResults();
-  SetUpAnswerCardResult(results, 1, 1);
+  SetUpAnswerCardResult(results, /*init_id=*/1, /*new_result_count=*/1);
   test_helper->GetProductivityLauncherSearchView()
       ->OnSearchResultContainerResultsChanged();
   // OnSearchResultContainerResultsChanged will schedule show animations().
@@ -93,6 +127,28 @@
       GetPrimaryShelf()->navigation_widget()));
 }
 
+// Verifies URL results under the clamshell mode.
+TEST_P(AppListViewPixelRTLTest, URLSearchResult) {
+  ShowAppList();
+
+  // Press a key to start a search.
+  PressAndReleaseKey(ui::VKEY_Y);
+  // Populate answer card result.
+  auto* test_helper = GetAppListTestHelper();
+  SearchModel::SearchResults* results = test_helper->GetSearchResults();
+  SetUpURLResult(results, /*init_id=*/1, /*new_result_count=*/1);
+  test_helper->GetProductivityLauncherSearchView()
+      ->OnSearchResultContainerResultsChanged();
+  // OnSearchResultContainerResultsChanged will schedule show animations().
+  base::RunLoop().RunUntilIdle();
+
+  HideCursor();
+  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
+      "bubble_launcher_url_search_results.rev_0",
+      GetAppListTestHelper()->GetBubbleView(),
+      GetPrimaryShelf()->navigation_widget()));
+}
+
 // Verifies the app list view under the clamshell mode.
 TEST_P(AppListViewPixelRTLTest, Basics) {
   GetAppListTestHelper()->AddAppItemsWithColorAndName(
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index bc477ea..c67302a 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -927,11 +927,8 @@
       <message name="IDS_ASH_DRAG_HANDLE_NUDGE" desc="The text shown to users above the drag handle when the device is in tablet mode and showing the in-app shelf. Instructs them that they can transition to home screen by swiping up from the shelf.">
         Swipe up to go home
       </message>
-      <message name="IDS_ASH_DRAG_HANDLE_HOTSEAT_SHOW_ACCESSIBLE_NAME" desc="The tooltip text indicate that users can use this as a button to show the hotseat/apps in shelf bar.">
-        Show apps in shelf
-      </message>
-      <message name="IDS_ASH_DRAG_HANDLE_HOTSEAT_HIDE_ACCESSIBLE_NAME" desc="The tooltip text indicate that users can use this as a button to hide the hotseat/apps in shelf bar.">
-        Hide apps in shelf
+      <message name="IDS_ASH_DRAG_HANDLE_HOTSEAT_ACCESSIBLE_NAME" desc="The tooltip text indicates that users can use this as a button to show and hide the hotseat/apps in shelf bar.">
+        Apps in shelf
       </message>
       <message name="IDS_ASH_HOME_TO_OVERVIEW_CONTEXTUAL_NUDGE" desc="Text shown to the user below shelf on the home screen in tablet mode, instructing them that they can transition to overview UI by swiping up from the shelf and holding.">
         Swipe up and hold to see open apps
diff --git a/ash/ash_strings_grd/IDS_ASH_DRAG_HANDLE_HOTSEAT_ACCESSIBLE_NAME.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DRAG_HANDLE_HOTSEAT_ACCESSIBLE_NAME.png.sha1
new file mode 100644
index 0000000..86ce64a
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_DRAG_HANDLE_HOTSEAT_ACCESSIBLE_NAME.png.sha1
@@ -0,0 +1 @@
+d3de135c53eee71fa22e9f07c06f74e32427161e
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_DRAG_HANDLE_HOTSEAT_HIDE_ACCESSIBLE_NAME.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DRAG_HANDLE_HOTSEAT_HIDE_ACCESSIBLE_NAME.png.sha1
deleted file mode 100644
index 8351b13..0000000
--- a/ash/ash_strings_grd/IDS_ASH_DRAG_HANDLE_HOTSEAT_HIDE_ACCESSIBLE_NAME.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-92f122ec19a7711bc4b22e9b932cb50ecda12179
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_DRAG_HANDLE_HOTSEAT_SHOW_ACCESSIBLE_NAME.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DRAG_HANDLE_HOTSEAT_SHOW_ACCESSIBLE_NAME.png.sha1
deleted file mode 100644
index 15fa1f4b..0000000
--- a/ash/ash_strings_grd/IDS_ASH_DRAG_HANDLE_HOTSEAT_SHOW_ACCESSIBLE_NAME.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a96c02783d2106540870bccafaef785cd7d95c99
\ No newline at end of file
diff --git a/ash/components/arc/DEPS b/ash/components/arc/DEPS
index 9c46ae3..41fd610 100644
--- a/ash/components/arc/DEPS
+++ b/ash/components/arc/DEPS
@@ -2,6 +2,7 @@
   "+ash/constants",
   "+ash/public/cpp",
   "+chromeos/ash/components/memory",
+  "+chromeos/ash/components/system",
   "+chromeos/components/sensors",
   "+chromeos/dbus",
   "+chromeos/system",
diff --git a/ash/components/arc/mojom/BUILD.gn b/ash/components/arc/mojom/BUILD.gn
index 96d8c5a3..4829148 100644
--- a/ash/components/arc/mojom/BUILD.gn
+++ b/ash/components/arc/mojom/BUILD.gn
@@ -240,7 +240,7 @@
         },
         {
           mojom = "arc.mojom.MountEvent"
-          cpp = "::chromeos::disks::DiskMountManager::MountEvent"
+          cpp = "::ash::disks::DiskMountManager::MountEvent"
         },
       ]
       traits_headers = [
diff --git a/ash/components/arc/session/BUILD.gn b/ash/components/arc/session/BUILD.gn
index 1b6af7d..b631595 100644
--- a/ash/components/arc/session/BUILD.gn
+++ b/ash/components/arc/session/BUILD.gn
@@ -64,8 +64,9 @@
     "//chromeos/ash/components/dbus/spaced:spaced",
     "//chromeos/ash/components/dbus/upstart",
     "//chromeos/ash/components/memory:memory",
+    "//chromeos/ash/components/system",
     "//chromeos/components/sensors:buildflags",
-    "//chromeos/system:system",
+    "//chromeos/system",
     "//components/prefs:prefs",
     "//components/user_manager",
     "//components/version_info",
@@ -144,7 +145,7 @@
     "//chromeos/ash/components/dbus/dlcservice",
     "//chromeos/ash/components/dbus/session_manager",
     "//chromeos/ash/components/dbus/upstart",
-    "//chromeos/system:system",
+    "//chromeos/ash/components/system",
     "//components/account_id",
     "//components/prefs:test_support",
     "//components/user_manager",
diff --git a/ash/components/arc/session/arc_session_impl.cc b/ash/components/arc/session/arc_session_impl.cc
index 25eedc5..a6e1574 100644
--- a/ash/components/arc/session/arc_session_impl.cc
+++ b/ash/components/arc/session/arc_session_impl.cc
@@ -35,7 +35,7 @@
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
 #include "chromeos/ash/components/dbus/spaced/spaced_client.h"
 #include "chromeos/ash/components/memory/memory.h"
-#include "chromeos/system/scheduler_configuration_manager_base.h"
+#include "chromeos/ash/components/system/scheduler_configuration_manager_base.h"
 #include "components/user_manager/user_manager.h"
 #include "components/version_info/channel.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
diff --git a/ash/components/arc/session/arc_session_impl.h b/ash/components/arc/session/arc_session_impl.h
index 824cd86..5fb25e87 100644
--- a/ash/components/arc/session/arc_session_impl.h
+++ b/ash/components/arc/session/arc_session_impl.h
@@ -15,7 +15,7 @@
 #include "base/files/scoped_file.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
-#include "chromeos/system/scheduler_configuration_manager_base.h"
+#include "chromeos/ash/components/system/scheduler_configuration_manager_base.h"
 
 namespace base {
 struct SystemMemoryInfoKB;
diff --git a/ash/components/arc/session/arc_session_impl_unittest.cc b/ash/components/arc/session/arc_session_impl_unittest.cc
index c0b8bf0..7cbb441 100644
--- a/ash/components/arc/session/arc_session_impl_unittest.cc
+++ b/ash/components/arc/session/arc_session_impl_unittest.cc
@@ -25,7 +25,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
-#include "chromeos/system/scheduler_configuration_manager_base.h"
+#include "chromeos/ash/components/system/scheduler_configuration_manager_base.h"
 #include "components/version_info/channel.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/components/arc/session/arc_vm_client_adapter.cc b/ash/components/arc/session/arc_vm_client_adapter.cc
index ef39c1b..696d6e0f 100644
--- a/ash/components/arc/session/arc_vm_client_adapter.cc
+++ b/ash/components/arc/session/arc_vm_client_adapter.cc
@@ -60,10 +60,10 @@
 #include "chromeos/ash/components/dbus/concierge/concierge_client.h"
 #include "chromeos/ash/components/dbus/debug_daemon/debug_daemon_client.h"
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/components/sensors/buildflags.h"
 #include "chromeos/dbus/common/dbus_method_call_status.h"
 #include "chromeos/system/core_scheduling.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/version_info/version_info.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/display/display.h"
diff --git a/ash/display/display_configuration_controller.cc b/ash/display/display_configuration_controller.cc
index d63a65e..72325495 100644
--- a/ash/display/display_configuration_controller.cc
+++ b/ash/display/display_configuration_controller.cc
@@ -18,7 +18,7 @@
 #include "ash/wallpaper/wallpaper_controller_impl.h"
 #include "base/bind.h"
 #include "base/time/time.h"
-#include "chromeos/system/devicemode.h"
+#include "chromeos/ash/components/system/devicemode.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/display/display_layout.h"
 #include "ui/display/manager/display_manager.h"
diff --git a/ash/metrics/ui_metrics_recorder_unittest.cc b/ash/metrics/ui_metrics_recorder_unittest.cc
index 16681d3..38f26a722 100644
--- a/ash/metrics/ui_metrics_recorder_unittest.cc
+++ b/ash/metrics/ui_metrics_recorder_unittest.cc
@@ -77,5 +77,72 @@
   histogram_tester.ExpectTotalCount("Ash.EventLatency.TotalLatency", 2);
 }
 
+TEST_F(UiMetricsRecorderTest, Gestures) {
+  std::unique_ptr<views::Widget> widget = CreateTestWindowWidget();
+  FakeTestView* view =
+      widget->SetContentsView(std::make_unique<FakeTestView>());
+  const gfx::Rect bounds = view->GetBoundsInScreen();
+
+  {
+    // Tap.
+    base::HistogramTester histogram_tester;
+
+    GestureTapOn(view);
+    EXPECT_TRUE(ui::WaitForNextFrameToBePresented(widget->GetCompositor()));
+
+    histogram_tester.ExpectTotalCount(
+        "Ash.EventLatency.GestureTapDown.TotalLatency", 1);
+    histogram_tester.ExpectTotalCount(
+        "Ash.EventLatency.GestureShowPress.TotalLatency", 1);
+    histogram_tester.ExpectTotalCount(
+        "Ash.EventLatency.GestureTap.TotalLatency", 1);
+  }
+
+  {
+    // Scroll.
+    base::HistogramTester histogram_tester;
+
+    constexpr int kNumOfTouches = 5;
+    GetEventGenerator()->GestureScrollSequence(
+        bounds.top_center(), bounds.bottom_center(), base::Milliseconds(100),
+        kNumOfTouches);
+    EXPECT_TRUE(ui::WaitForNextFrameToBePresented(widget->GetCompositor()));
+
+    histogram_tester.ExpectTotalCount(
+        "Ash.EventLatency.GestureTapDown.TotalLatency", 1);
+    histogram_tester.ExpectTotalCount(
+        "Ash.EventLatency.GestureTapCancel.TotalLatency", 1);
+    histogram_tester.ExpectTotalCount(
+        "Ash.EventLatency.GestureScrollBegin.TotalLatency", 1);
+    histogram_tester.GetBucketCount(
+        "Ash.EventLatency.GestureScrollUpdate.TotalLatency", kNumOfTouches);
+    histogram_tester.ExpectTotalCount(
+        "Ash.EventLatency.GestureScrollEnd.TotalLatency", 1);
+  }
+
+  {
+    // Pinch.
+    base::HistogramTester histogram_tester;
+
+    GetEventGenerator()->PressTouchId(1, bounds.origin());
+    GetEventGenerator()->PressTouchId(2, bounds.bottom_right());
+
+    GetEventGenerator()->MoveTouchId(bounds.CenterPoint(), 1);
+    GetEventGenerator()->MoveTouchId(bounds.CenterPoint(), 2);
+
+    GetEventGenerator()->ReleaseTouchId(1);
+    GetEventGenerator()->ReleaseTouchId(2);
+
+    EXPECT_TRUE(ui::WaitForNextFrameToBePresented(widget->GetCompositor()));
+
+    histogram_tester.ExpectTotalCount(
+        "Ash.EventLatency.GesturePinchBegin.TotalLatency", 1);
+    histogram_tester.ExpectTotalCount(
+        "Ash.EventLatency.GesturePinchUpdate.TotalLatency", 1);
+    histogram_tester.ExpectTotalCount(
+        "Ash.EventLatency.GesturePinchEnd.TotalLatency", 1);
+  }
+}
+
 }  // namespace
 }  // namespace ash
diff --git a/ash/public/cpp/assistant/assistant_state_base.cc b/ash/public/cpp/assistant/assistant_state_base.cc
index bced562..d7a7709 100644
--- a/ash/public/cpp/assistant/assistant_state_base.cc
+++ b/ash/public/cpp/assistant/assistant_state_base.cc
@@ -138,7 +138,7 @@
 bool AssistantStateBase::HasAudioInputDevice() const {
   ash::AudioDeviceList devices;
   ash::CrasAudioHandler::Get()->GetAudioDevices(&devices);
-  for (const chromeos::AudioDevice& device : devices) {
+  for (const AudioDevice& device : devices) {
     if (device.is_input)
       return true;
   }
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 4f20d07..d5d7fcd 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
+++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
@@ -545,6 +545,24 @@
 constexpr char kSubsequentSuccessFunnelMetric[] = "FastPair.SubsequentPairing";
 constexpr char kRetroactiveSuccessFunnelMetric[] =
     "FastPair.RetroactivePairing";
+constexpr char kInitializePairingProcessInitial[] =
+    "FastPair.InitialPairing.Initialization";
+constexpr char kInitializePairingProcessSubsequent[] =
+    "FastPair.SubsequentPairing.Initialization";
+constexpr char kInitializePairingProcessRetroactive[] =
+    "FastPair.RetroactivePairing.Initialization";
+constexpr char kInitializePairingProcessFailureReasonInitial[] =
+    "FastPair.InitialPairing.Initialization.FailureReason";
+constexpr char kInitializePairingProcessFailureReasonSubsequent[] =
+    "FastPair.SubsequentPairing.Initialization.FailureReason";
+constexpr char kInitializePairingProcessFailureReasonRetroactive[] =
+    "FastPair.RetroactivePairing.Initialization.FailureReason";
+constexpr char kInitializePairingProcessRetriesBeforeSuccessInitial[] =
+    "FastPair.InitialPairing.Initialization.RetriesBeforeSuccess";
+constexpr char kInitializePairingProcessRetriesBeforeSuccessSubsequent[] =
+    "FastPair.SubsequentPairing.Initialization.RetriesBeforeSuccess";
+constexpr char kInitializePairingProcessRetriesBeforeSuccessRetroactive[] =
+    "FastPair.RetroactivePairing.Initialization.RetriesBeforeSuccess";
 
 const std::string GetEngagementFlowInitialModelIdMetric(
     const ash::quick_pair::Device& device) {
@@ -614,6 +632,65 @@
   base::UmaHistogramEnumeration(kRetroactiveSuccessFunnelMetric, event);
 }
 
+void RecordFastPairInitializePairingProcessEvent(
+    const Device& device,
+    FastPairInitializePairingProcessEvent event) {
+  switch (device.protocol) {
+    case Protocol::kFastPairInitial:
+      base::UmaHistogramEnumeration(kInitializePairingProcessInitial, event);
+      break;
+    case Protocol::kFastPairRetroactive:
+      base::UmaHistogramEnumeration(kInitializePairingProcessRetroactive,
+                                    event);
+      break;
+    case Protocol::kFastPairSubsequent:
+      base::UmaHistogramEnumeration(kInitializePairingProcessSubsequent, event);
+      break;
+  }
+}
+
+void RecordInitializationFailureReason(const Device& device,
+                                       PairFailure failure_reason) {
+  switch (device.protocol) {
+    case Protocol::kFastPairInitial:
+      base::UmaHistogramEnumeration(
+          kInitializePairingProcessFailureReasonInitial, failure_reason);
+      break;
+    case Protocol::kFastPairRetroactive:
+      base::UmaHistogramEnumeration(
+          kInitializePairingProcessFailureReasonRetroactive, failure_reason);
+      break;
+    case Protocol::kFastPairSubsequent:
+      base::UmaHistogramEnumeration(
+          kInitializePairingProcessFailureReasonSubsequent, failure_reason);
+      break;
+  }
+}
+
+void RecordInitializationRetriesBeforeSuccess(const Device& device,
+                                              int num_retries_before_success) {
+  switch (device.protocol) {
+    case Protocol::kFastPairInitial:
+      base::UmaHistogramExactLinear(
+          kInitializePairingProcessRetriesBeforeSuccessInitial,
+          num_retries_before_success,
+          /*exclusive_max=*/10);
+      break;
+    case Protocol::kFastPairRetroactive:
+      base::UmaHistogramExactLinear(
+          kInitializePairingProcessRetriesBeforeSuccessRetroactive,
+          num_retries_before_success,
+          /*exclusive_max=*/10);
+      break;
+    case Protocol::kFastPairSubsequent:
+      base::UmaHistogramExactLinear(
+          kInitializePairingProcessRetriesBeforeSuccessSubsequent,
+          num_retries_before_success,
+          /*exclusive_max=*/10);
+      break;
+  }
+}
+
 void AttemptRecordingTotalUxPairTime(const Device& device,
                                      base::TimeDelta total_pair_time) {
   switch (device.protocol) {
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 8ed5c9e9..e390c32 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
+++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
@@ -116,6 +116,21 @@
 
 // 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.
+enum class COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+    FastPairInitializePairingProcessEvent {
+      kInitializationStarted = 0,
+      kAlreadyPairingFailure = 1,
+      kPassedToPairingDialog = 2,
+      kExhaustedRetriesFailure = 3,
+      kHandshakeReused = 4,
+      kInitializationComplete = 5,
+      kMaxValue = kInitializationComplete,
+    };
+
+// 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.
 enum class COMPONENT_EXPORT(QUICK_PAIR_COMMON) PairingMethod {
@@ -203,6 +218,19 @@
     FastPairRetroactiveSuccessFunnelEvent event);
 
 COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordFastPairInitializePairingProcessEvent(
+    const Device& device,
+    FastPairInitializePairingProcessEvent event);
+
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordInitializationFailureReason(const Device& device,
+                                       PairFailure failure_reason);
+
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordInitializationRetriesBeforeSuccess(const Device& device,
+                                              int num_retries_before_success);
+
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
 void AttemptRecordingTotalUxPairTime(const Device& device,
                                      base::TimeDelta total_pair_time);
 
diff --git a/ash/quick_pair/fast_pair_handshake/fake_fast_pair_handshake.h b/ash/quick_pair/fast_pair_handshake/fake_fast_pair_handshake.h
index b2750e7..874b1d4c 100644
--- a/ash/quick_pair/fast_pair_handshake/fake_fast_pair_handshake.h
+++ b/ash/quick_pair/fast_pair_handshake/fake_fast_pair_handshake.h
@@ -27,6 +27,10 @@
   ~FakeFastPairHandshake() override;
 
   void InvokeCallback(absl::optional<PairFailure> failure = absl::nullopt);
+
+  void set_completed_successfully(bool completed_successfully) {
+    completed_successfully_ = completed_successfully;
+  }
 };
 
 }  // namespace quick_pair
diff --git a/ash/quick_pair/keyed_service/quick_pair_metrics_logger.cc b/ash/quick_pair/keyed_service/quick_pair_metrics_logger.cc
index 6c5e70f..0b9cd8e 100644
--- a/ash/quick_pair/keyed_service/quick_pair_metrics_logger.cc
+++ b/ash/quick_pair/keyed_service/quick_pair_metrics_logger.cc
@@ -201,6 +201,9 @@
 }
 
 void QuickPairMetricsLogger::OnPairingStart(scoped_refptr<Device> device) {
+  RecordFastPairInitializePairingProcessEvent(
+      *device, FastPairInitializePairingProcessEvent::kInitializationStarted);
+
   switch (device->protocol) {
     case Protocol::kFastPairSubsequent:
       RecordSubsequentSuccessFunnelFlow(
@@ -218,6 +221,9 @@
 }
 
 void QuickPairMetricsLogger::OnHandshakeComplete(scoped_refptr<Device> device) {
+  RecordFastPairInitializePairingProcessEvent(
+      *device, FastPairInitializePairingProcessEvent::kInitializationComplete);
+
   switch (device->protocol) {
     case Protocol::kFastPairSubsequent:
       RecordSubsequentSuccessFunnelFlow(
diff --git a/ash/quick_pair/keyed_service/quick_pair_metrics_logger_unittest.cc b/ash/quick_pair/keyed_service/quick_pair_metrics_logger_unittest.cc
index f745e99f..72da35c 100644
--- a/ash/quick_pair/keyed_service/quick_pair_metrics_logger_unittest.cc
+++ b/ash/quick_pair/keyed_service/quick_pair_metrics_logger_unittest.cc
@@ -81,6 +81,12 @@
     "Bluetooth.ChromeOS.FastPair.AccountKey.Failure.RetroactivePairingProtocol";
 constexpr char kRetroactiveSuccessFunnelMetric[] =
     "FastPair.RetroactivePairing";
+constexpr char kInitializePairingProcessInitial[] =
+    "FastPair.InitialPairing.Initialization";
+constexpr char kInitializePairingProcessSubsequent[] =
+    "FastPair.SubsequentPairing.Initialization";
+constexpr char kInitializePairingProcessRetroactive[] =
+    "FastPair.RetroactivePairing.Initialization";
 
 constexpr char kTestDeviceAddress[] = "11:12:13:14:15:16";
 constexpr char kTestBleDeviceName[] = "Test Device Name";
@@ -1126,6 +1132,15 @@
                 kInitialSuccessFunnelMetric,
                 FastPairInitialSuccessFunnelEvent::kProcessComplete),
             1);
+
+  EXPECT_EQ(histogram_tester().GetBucketCount(
+                kInitializePairingProcessInitial,
+                FastPairInitializePairingProcessEvent::kInitializationStarted),
+            1);
+  EXPECT_EQ(histogram_tester().GetBucketCount(
+                kInitializePairingProcessInitial,
+                FastPairInitializePairingProcessEvent::kInitializationComplete),
+            1);
 }
 
 TEST_F(QuickPairMetricsLoggerTest, LogSuccessFunnel_Retroactive) {
@@ -1153,6 +1168,15 @@
                 kRetroactiveSuccessFunnelMetric,
                 FastPairRetroactiveSuccessFunnelEvent::kSaveRequested),
             1);
+
+  EXPECT_EQ(histogram_tester().GetBucketCount(
+                kInitializePairingProcessRetroactive,
+                FastPairInitializePairingProcessEvent::kInitializationStarted),
+            1);
+  EXPECT_EQ(histogram_tester().GetBucketCount(
+                kInitializePairingProcessRetroactive,
+                FastPairInitializePairingProcessEvent::kInitializationComplete),
+            1);
 }
 
 TEST_F(QuickPairMetricsLoggerTest, LogSuccessFunnel_Subseqent) {
@@ -1175,6 +1199,15 @@
                 kSubsequentSuccessFunnelMetric,
                 FastPairSubsequentSuccessFunnelEvent::kProcessComplete),
             1);
+
+  EXPECT_EQ(histogram_tester().GetBucketCount(
+                kInitializePairingProcessSubsequent,
+                FastPairInitializePairingProcessEvent::kInitializationStarted),
+            1);
+  EXPECT_EQ(histogram_tester().GetBucketCount(
+                kInitializePairingProcessSubsequent,
+                FastPairInitializePairingProcessEvent::kInitializationComplete),
+            1);
 }
 
 TEST_F(QuickPairMetricsLoggerTest, LogErrorUiDismissed_Initial) {
diff --git a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl.cc b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl.cc
index 6cbe14a..da6b4152 100644
--- a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl.cc
+++ b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl.cc
@@ -137,6 +137,9 @@
   if (device_->version().value() == DeviceFastPairVersion::kV1) {
     RecordInitialSuccessFunnelFlow(
         FastPairInitialSuccessFunnelEvent::kV1DeviceDetected);
+    RecordFastPairInitializePairingProcessEvent(
+        *device_,
+        FastPairInitializePairingProcessEvent::kPassedToPairingDialog);
     Shell::Get()->system_tray_model()->client()->ShowBluetoothPairingDialog(
         device_->ble_address);
     return;
@@ -150,6 +153,8 @@
     if (fast_pair_handshake_->completed_successfully()) {
       QP_LOG(VERBOSE) << __func__
                       << ": Reusing handshake for retried pair attempt.";
+      RecordFastPairInitializePairingProcessEvent(
+          *device_, FastPairInitializePairingProcessEvent::kHandshakeReused);
       OnHandshakeComplete(device_, /*failure=*/absl::nullopt);
       return;
     }
@@ -172,9 +177,14 @@
 void FastPairPairerImpl::OnHandshakeComplete(
     scoped_refptr<Device> device,
     absl::optional<PairFailure> failure) {
+  // TODO(b/259429032) : Log with `RecordInitializationRetriesBeforeSuccess`
+  // the number of handshake retries occurred before success. Log with
+  // `FastPairInitializePairingProcessEvent` if we have exhausted the retries.
+
   if (failure.has_value()) {
     QP_LOG(WARNING) << __func__ << ": Handshake failed with " << device
                     << " because: " << failure.value();
+    RecordInitializationFailureReason(*device, failure.value());
     std::move(pair_failed_callback_).Run(device_, failure.value());
     // |this| may be destroyed after this line.
     return;
@@ -183,6 +193,7 @@
   // During handshake, the device address can be set to null.
   if (!device_->classic_address()) {
     QP_LOG(WARNING) << __func__ << ": Device lost during handshake.";
+    RecordInitializationFailureReason(*device, PairFailure::kPairingDeviceLost);
     std::move(pair_failed_callback_)
         .Run(device_, PairFailure::kPairingDeviceLost);
     // |this| may be destroyed after this line.
diff --git a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl_unittest.cc b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl_unittest.cc
index 1ef93c520..7cda4f2d 100644
--- a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl_unittest.cc
+++ b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl_unittest.cc
@@ -105,6 +105,18 @@
     "FastPair.InitialPairing.Pairing";
 constexpr char kProtocolPairingStepSubsequent[] =
     "FastPair.SubsequentPairing.Pairing";
+constexpr char kInitializePairingProcessInitial[] =
+    "FastPair.InitialPairing.Initialization";
+constexpr char kInitializePairingProcessSubsequent[] =
+    "FastPair.SubsequentPairing.Initialization";
+constexpr char kInitializePairingProcessRetroactive[] =
+    "FastPair.RetroactivePairing.Initialization";
+constexpr char kInitializePairingProcessFailureReasonInitial[] =
+    "FastPair.InitialPairing.Initialization.FailureReason";
+constexpr char kInitializePairingProcessFailureReasonSubsequent[] =
+    "FastPair.SubsequentPairing.Initialization.FailureReason";
+constexpr char kInitializePairingProcessFailureReasonRetroactive[] =
+    "FastPair.RetroactivePairing.Initialization.FailureReason";
 
 class FakeBluetoothAdapter
     : public testing::NiceMock<device::MockBluetoothAdapter> {
@@ -310,13 +322,20 @@
       FastPairHandshake::OnCompleteCallback callback) {
     // This is the only place where data_encryptor_unique_ is used. We assume
     // that CreateHandshake is only called once.
-    auto fake = std::make_unique<FakeFastPairHandshake>(
+    auto fake_fast_pair_handshake = std::make_unique<FakeFastPairHandshake>(
         adapter_, device, std::move(callback),
         std::move(data_encryptor_unique_), std::move(gatt_service_client_));
 
-    fake_fast_pair_handshake_ = fake.get();
+    fake_fast_pair_handshake_ = fake_fast_pair_handshake.get();
 
-    return fake;
+    return fake_fast_pair_handshake;
+  }
+
+  void SetReuseHandshake() {
+    FastPairHandshakeLookup::GetInstance()->Create(adapter_, device_,
+                                                   base::DoNothing());
+    fake_fast_pair_handshake_->set_completed_successfully(
+        /*completed_successfully=*/true);
   }
 
   base::HistogramTester& histogram_tester() { return histogram_tester_; }
@@ -448,6 +467,7 @@
     RunWriteAccountKeyCallback();
   }
 
+  bool set_handshake_completed_successfully_ = false;
   absl::optional<PairFailure> failure_ = absl::nullopt;
   std::unique_ptr<FakeBluetoothDevice> fake_bluetooth_device_;
   FakeBluetoothDevice* fake_bluetooth_device_ptr_ = nullptr;
@@ -473,7 +493,7 @@
   base::WeakPtrFactory<FastPairPairerImplTest> weak_ptr_factory_{this};
 };
 
-TEST_F(FastPairPairerImplTest, NoPairingIfHandshakeFailed) {
+TEST_F(FastPairPairerImplTest, NoPairingIfHandshakeFailed_Initial) {
   Login(user_manager::UserType::USER_TYPE_REGULAR);
   base::RunLoop().RunUntilIdle();
   CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
@@ -482,6 +502,40 @@
   fake_fast_pair_handshake_->InvokeCallback(PairFailure::kCreateGattConnection);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), PairFailure::kCreateGattConnection);
+  EXPECT_EQ(histogram_tester_.GetBucketCount(
+                kInitializePairingProcessFailureReasonInitial,
+                PairFailure::kCreateGattConnection),
+            1);
+}
+
+TEST_F(FastPairPairerImplTest, NoPairingIfHandshakeFailed_Subsequent) {
+  Login(user_manager::UserType::USER_TYPE_REGULAR);
+  base::RunLoop().RunUntilIdle();
+  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
+                   /*protocol=*/Protocol::kFastPairSubsequent);
+  CreatePairer();
+  fake_fast_pair_handshake_->InvokeCallback(PairFailure::kCreateGattConnection);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(GetPairFailure(), PairFailure::kCreateGattConnection);
+  EXPECT_EQ(histogram_tester_.GetBucketCount(
+                kInitializePairingProcessFailureReasonSubsequent,
+                PairFailure::kCreateGattConnection),
+            1);
+}
+
+TEST_F(FastPairPairerImplTest, NoPairingIfHandshakeFailed_Retroactive) {
+  Login(user_manager::UserType::USER_TYPE_REGULAR);
+  base::RunLoop().RunUntilIdle();
+  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
+                   /*protocol=*/Protocol::kFastPairRetroactive);
+  CreatePairer();
+  fake_fast_pair_handshake_->InvokeCallback(PairFailure::kCreateGattConnection);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(GetPairFailure(), PairFailure::kCreateGattConnection);
+  EXPECT_EQ(histogram_tester_.GetBucketCount(
+                kInitializePairingProcessFailureReasonRetroactive,
+                PairFailure::kCreateGattConnection),
+            1);
 }
 
 TEST_F(FastPairPairerImplTest, NoCallbackIsInvokedOnGattSuccess_Initial) {
@@ -2066,6 +2120,10 @@
   EXPECT_CALL(pairing_procedure_complete_, Run);
   EXPECT_EQ(DeviceFastPairVersion::kV1, device_->version().value());
   DevicePaired();
+  EXPECT_EQ(histogram_tester().GetBucketCount(
+                kInitializePairingProcessInitial,
+                FastPairInitializePairingProcessEvent::kPassedToPairingDialog),
+            1);
 }
 
 TEST_F(FastPairPairerImplTest, FastPairVersionOne_DeviceUnpaired) {
@@ -2523,5 +2581,59 @@
             0);
 }
 
+TEST_F(FastPairPairerImplTest, HandshakeReused_Initial) {
+  Login(user_manager::UserType::USER_TYPE_REGULAR);
+  base::RunLoop().RunUntilIdle();
+
+  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
+                   /*protocol=*/Protocol::kFastPairInitial);
+
+  // Simulate Handshake already created before attempt
+  SetReuseHandshake();
+  SetGetDeviceNullptr();
+  CreatePairer();
+
+  EXPECT_EQ(histogram_tester().GetBucketCount(
+                kInitializePairingProcessInitial,
+                FastPairInitializePairingProcessEvent::kHandshakeReused),
+            1);
+}
+
+TEST_F(FastPairPairerImplTest, HandshakeReused_Subsequent) {
+  Login(user_manager::UserType::USER_TYPE_REGULAR);
+  base::RunLoop().RunUntilIdle();
+
+  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
+                   /*protocol=*/Protocol::kFastPairSubsequent);
+
+  // Simulate Handshake already created before attempt
+  SetReuseHandshake();
+  SetGetDeviceNullptr();
+  CreatePairer();
+
+  EXPECT_EQ(histogram_tester().GetBucketCount(
+                kInitializePairingProcessSubsequent,
+                FastPairInitializePairingProcessEvent::kHandshakeReused),
+            1);
+}
+
+TEST_F(FastPairPairerImplTest, HandshakeReused_Retroactive) {
+  Login(user_manager::UserType::USER_TYPE_REGULAR);
+  base::RunLoop().RunUntilIdle();
+
+  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
+                   /*protocol=*/Protocol::kFastPairRetroactive);
+
+  // Simulate Handshake already created before attempt
+  SetReuseHandshake();
+  SetGetDeviceNullptr();
+  CreatePairer();
+
+  EXPECT_EQ(histogram_tester().GetBucketCount(
+                kInitializePairingProcessRetroactive,
+                FastPairInitializePairingProcessEvent::kHandshakeReused),
+            1);
+}
+
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/quick_pair/pairing/pairer_broker_impl.cc b/ash/quick_pair/pairing/pairer_broker_impl.cc
index a5b60de..50f698e 100644
--- a/ash/quick_pair/pairing/pairer_broker_impl.cc
+++ b/ash/quick_pair/pairing/pairer_broker_impl.cc
@@ -90,6 +90,8 @@
 void PairerBrokerImpl::PairFastPairDevice(scoped_refptr<Device> device) {
   if (base::Contains(fast_pair_pairers_, device->ble_address)) {
     QP_LOG(WARNING) << __func__ << ": Already pairing device" << device;
+    RecordFastPairInitializePairingProcessEvent(
+        *device, FastPairInitializePairingProcessEvent::kAlreadyPairingFailure);
     return;
   }
 
diff --git a/ash/quick_pair/pairing/pairer_broker_impl_unittest.cc b/ash/quick_pair/pairing/pairer_broker_impl_unittest.cc
index ee7e5623..28f496d8 100644
--- a/ash/quick_pair/pairing/pairer_broker_impl_unittest.cc
+++ b/ash/quick_pair/pairing/pairer_broker_impl_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "ash/quick_pair/common/account_key_failure.h"
 #include "ash/quick_pair/common/device.h"
+#include "ash/quick_pair/common/fast_pair/fast_pair_metrics.h"
 #include "ash/quick_pair/common/pair_failure.h"
 #include "ash/quick_pair/common/protocol.h"
 #include "ash/quick_pair/feature_status_tracker/fake_bluetooth_adapter.h"
@@ -33,6 +34,12 @@
 
 const char kFastPairRetryCountMetricName[] =
     "Bluetooth.ChromeOS.FastPair.PairRetry.Count";
+constexpr char kInitializePairingProcessInitial[] =
+    "FastPair.InitialPairing.Initialization";
+constexpr char kInitializePairingProcessSubsequent[] =
+    "FastPair.SubsequentPairing.Initialization";
+constexpr char kInitializePairingProcessRetroactive[] =
+    "FastPair.RetroactivePairing.Initialization";
 
 constexpr char kProtocolPairingStepInitial[] =
     "FastPair.InitialPairing.Pairing";
@@ -280,7 +287,7 @@
   EXPECT_FALSE(pairer_broker_->IsPairing());
 }
 
-TEST_F(PairerBrokerImplTest, AlreadyPairingDevice) {
+TEST_F(PairerBrokerImplTest, AlreadyPairingDevice_Initial) {
   histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
   auto device = base::MakeRefCounted<Device>(kValidModelId, kTestDeviceAddress,
                                              Protocol::kFastPairInitial);
@@ -294,6 +301,53 @@
   EXPECT_TRUE(pairer_broker_->IsPairing());
   EXPECT_EQ(device_paired_count_, 1);
   histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);
+  EXPECT_EQ(histogram_tester_.GetBucketCount(
+                kInitializePairingProcessInitial,
+                FastPairInitializePairingProcessEvent::kAlreadyPairingFailure),
+            1);
+}
+
+TEST_F(PairerBrokerImplTest, AlreadyPairingDevice_Subsequent) {
+  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
+  auto device = base::MakeRefCounted<Device>(kValidModelId, kTestDeviceAddress,
+                                             Protocol::kFastPairSubsequent);
+
+  pairer_broker_->PairDevice(device);
+  pairer_broker_->PairDevice(device);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(pairer_broker_->IsPairing());
+
+  fast_pair_pairer_factory_->fake_fast_pair_pairer()->TriggerPairedCallback();
+
+  EXPECT_TRUE(pairer_broker_->IsPairing());
+  EXPECT_EQ(device_paired_count_, 1);
+  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);
+  EXPECT_EQ(histogram_tester_.GetBucketCount(
+                kInitializePairingProcessSubsequent,
+                FastPairInitializePairingProcessEvent::kAlreadyPairingFailure),
+            1);
+}
+
+TEST_F(PairerBrokerImplTest, AlreadyPairingDevice_Retroactive) {
+  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
+  auto device = base::MakeRefCounted<Device>(kValidModelId, kTestDeviceAddress,
+                                             Protocol::kFastPairRetroactive);
+
+  pairer_broker_->PairDevice(device);
+  pairer_broker_->PairDevice(device);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(pairer_broker_->IsPairing());
+
+  fast_pair_pairer_factory_->fake_fast_pair_pairer()->TriggerPairedCallback();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(pairer_broker_->IsPairing());
+  EXPECT_EQ(device_paired_count_, 1);
+  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);
+  EXPECT_EQ(histogram_tester_.GetBucketCount(
+                kInitializePairingProcessRetroactive,
+                FastPairInitializePairingProcessEvent::kAlreadyPairingFailure),
+            1);
 }
 
 TEST_F(PairerBrokerImplTest, PairAfterCancelPairing) {
diff --git a/ash/shelf/drag_handle.cc b/ash/shelf/drag_handle.cc
index 54756ae0..11193644 100644
--- a/ash/shelf/drag_handle.cc
+++ b/ash/shelf/drag_handle.cc
@@ -4,6 +4,8 @@
 
 #include "ash/shelf/drag_handle.h"
 
+#include <string>
+
 #include "ash/accessibility/accessibility_controller_impl.h"
 #include "ash/constants/ash_features.h"
 #include "ash/controls/contextual_tooltip.h"
@@ -272,7 +274,9 @@
 }
 
 void DragHandle::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+  // TODO(b/262424972): Remove unwanted ", window" string from the announcement.
   Button::GetAccessibleNodeData(node_data);
+  GetViewAccessibility().OverrideRole(ax::mojom::Role::kPopUpButton);
 
   std::u16string accessible_name = std::u16string();
   switch (shelf_->shelf_layout_manager()->hotseat_state()) {
@@ -282,7 +286,8 @@
       break;
     case HotseatState::kHidden:
       accessible_name = l10n_util::GetStringUTF16(
-          IDS_ASH_DRAG_HANDLE_HOTSEAT_SHOW_ACCESSIBLE_NAME);
+          IDS_ASH_DRAG_HANDLE_HOTSEAT_ACCESSIBLE_NAME);
+      node_data->AddState(ax::mojom::State::kCollapsed);
 
       // When the hotseat is kHidden, the focus traversal should go to the
       // status area as the next focus and the navigation area as the previous
@@ -292,6 +297,8 @@
           shelf_->shelf_widget()->navigation_widget());
       break;
     case HotseatState::kExtended:
+      node_data->AddState(ax::mojom::State::kExpanded);
+
       // When the hotseat is kExtended, the focus traversal should go to the
       // hotseat as both the next and previous focus.
       GetViewAccessibility().OverrideNextFocus(shelf_->hotseat_widget());
@@ -299,9 +306,10 @@
 
       // The name should be empty when the hotseat is extended but we cannot
       // hide it.
-      if (force_show_hotseat_resetter_)
+      if (force_show_hotseat_resetter_) {
         accessible_name = l10n_util::GetStringUTF16(
-            IDS_ASH_DRAG_HANDLE_HOTSEAT_HIDE_ACCESSIBLE_NAME);
+            IDS_ASH_DRAG_HANDLE_HOTSEAT_ACCESSIBLE_NAME);
+      }
       break;
   }
   node_data->SetName(accessible_name);
diff --git a/ash/shell.cc b/ash/shell.cc
index 5a3baa3..dc423ee 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -212,10 +212,10 @@
 #include "chromeos/ash/components/dbus/usb/usbguard_client.h"
 #include "chromeos/ash/components/fwupd/firmware_update_manager.h"
 #include "chromeos/ash/components/peripheral_notification/peripheral_notification_manager.h"
+#include "chromeos/ash/components/system/devicemode.h"
 #include "chromeos/ash/services/assistant/public/cpp/features.h"
 #include "chromeos/dbus/init/initialize_dbus_client.h"
 #include "chromeos/dbus/power/power_policy_controller.h"
-#include "chromeos/system/devicemode.h"
 #include "chromeos/ui/wm/features.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
diff --git a/ash/system/microphone_mute/microphone_mute_notification_controller_unittest.cc b/ash/system/microphone_mute/microphone_mute_notification_controller_unittest.cc
index 8c96c27..7f508d1 100644
--- a/ash/system/microphone_mute/microphone_mute_notification_controller_unittest.cc
+++ b/ash/system/microphone_mute/microphone_mute_notification_controller_unittest.cc
@@ -314,7 +314,7 @@
             0);
   // Clicking the action button should unmute device.
   ClickOnNotificationButton();
-  EXPECT_FALSE(chromeos::CrasAudioHandler::Get()->IsInputMuted());
+  EXPECT_FALSE(CrasAudioHandler::Get()->IsInputMuted());
 
   EXPECT_FALSE(GetNotification());
   EXPECT_EQ(histogram_tester().GetBucketCount(
@@ -342,7 +342,7 @@
   // Clicking the action button should unmute device.
   ClickOnNotificationBody();
   EXPECT_EQ(GetSystemTrayClient()->show_os_settings_privacy_hub_count(), 1);
-  EXPECT_TRUE(chromeos::CrasAudioHandler::Get()->IsInputMuted());
+  EXPECT_TRUE(CrasAudioHandler::Get()->IsInputMuted());
 
   EXPECT_EQ(histogram_tester().GetBucketCount(
                 privacy_hub_metrics::kPrivacyHubOpenedHistogram,
@@ -369,10 +369,10 @@
   EXPECT_CALL(new_window_delegate(), OpenUrl).Times(1);
   ClickOnNotificationButton();
 
-  EXPECT_TRUE(chromeos::CrasAudioHandler::Get()->IsInputMuted());
+  EXPECT_TRUE(CrasAudioHandler::Get()->IsInputMuted());
 
   SetMicrophoneMuteSwitchState(/*muted=*/false);
-  ASSERT_FALSE(chromeos::CrasAudioHandler::Get()->IsInputMuted());
+  ASSERT_FALSE(CrasAudioHandler::Get()->IsInputMuted());
   EXPECT_FALSE(GetNotification());
 }
 
@@ -388,7 +388,7 @@
   ClickOnNotificationBody();
 
   // Check that clicking the body has no effect and notification disappears.
-  EXPECT_TRUE(chromeos::CrasAudioHandler::Get()->IsInputMuted());
+  EXPECT_TRUE(CrasAudioHandler::Get()->IsInputMuted());
   EXPECT_FALSE(GetNotification());
 }
 
@@ -417,7 +417,7 @@
             notification->buttons()[0].title);
 
   SetMicrophoneMuteSwitchState(/*muted=*/false);
-  ASSERT_FALSE(chromeos::CrasAudioHandler::Get()->IsInputMuted());
+  ASSERT_FALSE(CrasAudioHandler::Get()->IsInputMuted());
   EXPECT_FALSE(GetNotification());
 }
 
@@ -440,7 +440,7 @@
   EXPECT_TRUE(GetPopupNotification());
 
   SetMicrophoneMuteSwitchState(/*muted=*/false);
-  ASSERT_FALSE(chromeos::CrasAudioHandler::Get()->IsInputMuted());
+  ASSERT_FALSE(CrasAudioHandler::Get()->IsInputMuted());
   EXPECT_FALSE(GetNotification());
 }
 
diff --git a/ash/test/ash_test_helper.h b/ash/test/ash_test_helper.h
index 64ebb789..b11424c 100644
--- a/ash/test/ash_test_helper.h
+++ b/ash/test/ash_test_helper.h
@@ -18,8 +18,8 @@
 #include "ash/system/message_center/test_notifier_settings_controller.h"
 #include "ash/test/pixel/ash_pixel_test_init_params.h"
 #include "base/test/scoped_command_line.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/ash/services/bluetooth_config/scoped_bluetooth_config_test_helper.h"
-#include "chromeos/system/fake_statistics_provider.h"
 #include "ui/aura/test/aura_test_helper.h"
 
 class PrefService;
diff --git a/ash/webui/common/resources/keyboard_key.html b/ash/webui/common/resources/keyboard_key.html
index 45d7864e..0ad7e4d 100644
--- a/ash/webui/common/resources/keyboard_key.html
+++ b/ash/webui/common/resources/keyboard_key.html
@@ -40,12 +40,12 @@
     }
 
   :host {
-    --background-color-pressed: var(--cros-icon-color-selection);
+    --background-color-disabled: var(--cros-bg-color-dropped-elevation-2);
+    --background-color-pressed: var(--google-blue-800);
     --background-color-unpressed: var(--cros-highlight-color);
     --background-color-tested: rgba(var(--cros-icon-color-selection-rgb),
-        var(--cros-second-tone-opacity));
-    --background-color-disabled: var(--cros-bg-color-dropped-elevation-2);
-    --border-color: var(--cros-icon-color-prominent);
+        .85);
+    --border-color: var(--cros-text-color-selection);
     --border-radius:
         var(--keyboard-key-top-radii, 4px)
         var(--keyboard-key-top-radii, 4px)
@@ -53,15 +53,19 @@
         4px;
     --icon-size: min(100%, var(--keyboard-key-icon-size, 100%));
     --travel: var(--keyboard-key-travel, 4px);
-    --text-color-unpressed: var(--cros-icon-color-prominent);
+    --text-color-unpressed: var(--cros-text-color-selection);
     --text-color-pressed: var(--cros-button-icon-color-primary);
-
+    --text-color-tested: var(--cros-button-icon-color-primary);
     position: relative;
   }
 
   @media (prefers-color-scheme: dark) {
     :host {
       --background-color-disabled: var(--cros-bg-color-elevation-4);
+      --background-color-pressed: var(--cros-icon-color-selection);
+      --background-color-unpressed: var(--cros-button-active-shadow-color-ambient-primary);
+      --background-color-tested: rgba(var(--cros-icon-color-selection-rgb),
+        .65);
     }
   }
 
@@ -167,6 +171,11 @@
 
   :host([state='tested']) #key {
     background-color: var(--background-color-tested);
+    color: var(--text-color-tested);
+  }
+
+  :host([state='tested']) iron-icon {
+    --iron-icon-fill-color: var(--text-color-tested);
   }
 
   #shadow {
diff --git a/ash/webui/diagnostics_ui/backend/BUILD.gn b/ash/webui/diagnostics_ui/backend/BUILD.gn
index d0a3384..72190fc 100644
--- a/ash/webui/diagnostics_ui/backend/BUILD.gn
+++ b/ash/webui/diagnostics_ui/backend/BUILD.gn
@@ -52,6 +52,7 @@
     "//ash/public/cpp",
     "//ash/webui/diagnostics_ui/mojom",
     "//base",
+    "//chromeos/ash/components/system",
     "//chromeos/ash/services/cros_healthd/public/cpp",
     "//chromeos/ash/services/cros_healthd/public/mojom",
     "//chromeos/dbus/power",
@@ -60,7 +61,6 @@
     "//chromeos/services/network_config/public/cpp",
     "//chromeos/services/network_health/public/mojom",
     "//chromeos/strings/",
-    "//chromeos/system",
     "//content/public/browser",
     "//services/data_decoder/public/cpp",
     "//services/device/public/mojom",
@@ -112,6 +112,7 @@
     "//chromeos/ash/components/dbus:test_support",
     "//chromeos/ash/components/dbus/shill",
     "//chromeos/ash/components/login/login_state:login_state",
+    "//chromeos/ash/components/system",
     "//chromeos/ash/services/cros_healthd/public/cpp",
     "//chromeos/ash/services/cros_healthd/public/mojom",
     "//chromeos/dbus/power",
@@ -120,7 +121,6 @@
     "//chromeos/services/network_config:network_config",
     "//chromeos/services/network_config/public/cpp:test_support",
     "//chromeos/services/network_config/public/mojom",
-    "//chromeos/system",
     "//components/onc",
     "//components/prefs:test_support",
     "//components/sync_preferences:test_support",
diff --git a/ash/webui/diagnostics_ui/backend/input/input_data_provider_keyboard.cc b/ash/webui/diagnostics_ui/backend/input/input_data_provider_keyboard.cc
index 8340517..6185170 100644
--- a/ash/webui/diagnostics_ui/backend/input/input_data_provider_keyboard.cc
+++ b/ash/webui/diagnostics_ui/backend/input/input_data_provider_keyboard.cc
@@ -20,7 +20,7 @@
 #include "base/logging.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "ui/events/devices/input_device.h"
 #include "ui/events/keycodes/keyboard_codes_posix.h"
 #include "ui/events/ozone/evdev/event_device_info.h"
diff --git a/ash/webui/diagnostics_ui/backend/input/input_data_provider_keyboard_unittest.cc b/ash/webui/diagnostics_ui/backend/input/input_data_provider_keyboard_unittest.cc
index 4b4ab02..be3d9ad 100644
--- a/ash/webui/diagnostics_ui/backend/input/input_data_provider_keyboard_unittest.cc
+++ b/ash/webui/diagnostics_ui/backend/input/input_data_provider_keyboard_unittest.cc
@@ -17,8 +17,8 @@
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/strings/string_piece_forward.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/ash/components/test/ash_test_suite.h"
-#include "chromeos/system/fake_statistics_provider.h"
 #include "content/public/test/browser_task_environment.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/chromeos/events/event_rewriter_chromeos.h"
diff --git a/ash/webui/diagnostics_ui/backend/input/input_data_provider_unittest.cc b/ash/webui/diagnostics_ui/backend/input/input_data_provider_unittest.cc
index 0216469..970277d 100644
--- a/ash/webui/diagnostics_ui/backend/input/input_data_provider_unittest.cc
+++ b/ash/webui/diagnostics_ui/backend/input/input_data_provider_unittest.cc
@@ -36,11 +36,11 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/time/time.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/ash/components/test/ash_test_suite.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
 #include "chromeos/dbus/power/power_manager_client.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
 #include "content/public/test/browser_task_environment.h"
 #include "device/udev_linux/fake_udev_loader.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
diff --git a/ash/webui/diagnostics_ui/resources/keyboard_tester.html b/ash/webui/diagnostics_ui/resources/keyboard_tester.html
index 7717a3b..bf5284f 100644
--- a/ash/webui/diagnostics_ui/resources/keyboard_tester.html
+++ b/ash/webui/diagnostics_ui/resources/keyboard_tester.html
@@ -55,7 +55,7 @@
   }
 
   #diagram-border {
-    border: 1px solid var(--cros-icon-color-prominent);
+    border: 1px solid var(--cros-text-color-selection);
     border-radius: 8px;
     padding: 6px;
   }
@@ -95,9 +95,9 @@
 
   #shortcutKeyAlt,
   #shortcutKeyEsc {
-    border: 1px solid var(--cros-color-prominent);
+    border: 1px solid var(--cros-text-color-selection);
     border-radius: 10px;
-    color: var(--cros-button-label-color-secondary);
+    color: var(--cros-text-color-selection);
     font-family: var(--diagnostics-roboto-font-family);
     font-size: 13px;
     font-weight: var(--diagnostics-medium-font-weight);
diff --git a/ash/webui/help_app_ui/BUILD.gn b/ash/webui/help_app_ui/BUILD.gn
index 7b71708..f5e5eb9 100644
--- a/ash/webui/help_app_ui/BUILD.gn
+++ b/ash/webui/help_app_ui/BUILD.gn
@@ -50,9 +50,9 @@
     "//chromeos/ash/components/local_search_service/public/cpp",
     "//chromeos/ash/components/local_search_service/public/mojom",
     "//chromeos/ash/components/local_search_service/public/mojom:mojom_js",
+    "//chromeos/ash/components/system",
     "//chromeos/constants",
     "//chromeos/strings",
-    "//chromeos/system",
     "//components/keyed_service/content:content",
     "//content/public/browser",
     "//content/public/common",
diff --git a/ash/webui/personalization_app/mojom/personalization_app.mojom b/ash/webui/personalization_app/mojom/personalization_app.mojom
index b508bb0..55ed1582 100644
--- a/ash/webui/personalization_app/mojom/personalization_app.mojom
+++ b/ash/webui/personalization_app/mojom/personalization_app.mojom
@@ -342,6 +342,9 @@
   // Notifies the JS side the current state of color mode auto scheduler.
   OnColorModeAutoScheduleChanged(bool enabled);
 
+  //Notifies the JS side about the current state of the color scheme.
+  OnColorSchemeChanged(ColorScheme color_scheme);
+
   // Notifies the JS side about the current state of the static color.
   OnStaticColorChanged(skia.mojom.SkColor? color);
 };
@@ -365,6 +368,9 @@
   // @see //ash/style/color_palette_controller.h
   SetStaticColor(skia.mojom.SkColor static_color);
 
+  // Retrieves the color scheme for dynamic color.
+  GetColorScheme() => (ColorScheme color_scheme);
+
   // Retrieves the static seed color for dynamic color.
   GetStaticColor() => (skia.mojom.SkColor? static_color);
 
diff --git a/ash/webui/personalization_app/resources/js/ambient/ambient_preview_element.html b/ash/webui/personalization_app/resources/js/ambient/ambient_preview_element.html
index 5b4b01ab..85347e6 100644
--- a/ash/webui/personalization_app/resources/js/ambient/ambient_preview_element.html
+++ b/ash/webui/personalization_app/resources/js/ambient/ambient_preview_element.html
@@ -337,7 +337,9 @@
   <slot></slot>
   <template is="dom-if" if="[[loading_]]">
     <div id="imagePlaceholder" class="placeholder"></div>
-    <div id="thumbnailPlaceholder" class="placeholder"></div>
+    <template is="dom-if" if="[[isAmbientSubpageUiChangeEnabled_()]]">
+      <div id="thumbnailPlaceholder" class="placeholder"></div>
+    </template>
     <div id="textPlaceholder" class="preview-text-placeholder album-info-mainpage album-info-subpage">
       <div class="placeholder currently-set-text"></div>
       <div class="placeholder"></div>
diff --git a/ash/webui/personalization_app/resources/js/theme/dynamic_color_element.html b/ash/webui/personalization_app/resources/js/theme/dynamic_color_element.html
index a3f45578..32d0632 100644
--- a/ash/webui/personalization_app/resources/js/theme/dynamic_color_element.html
+++ b/ash/webui/personalization_app/resources/js/theme/dynamic_color_element.html
@@ -64,7 +64,7 @@
     <!-- TODO(b/253089237): Add the final translated text. -->
     <div id="dynamicColorToggleDescription">[temp]Auto</div>
     <!-- TODO(b/253089237): Add the translated aria label. -->
-    <cr-toggle checked="{{automaticSeedColorEnabled}}" on-click="onClickToggle_">
+    <cr-toggle checked="{{automaticSeedColorEnabled}}" on-change="onToggleChanged_">
     </cr-toggle>
   </div>
   <iron-a11y-keys id="colorSchemeKeys" keys="left right" on-keys-pressed="onColorSchemeKeysPress_">
@@ -75,12 +75,13 @@
       id="colorSchemeSelector"
       selected="0"
       selected-item="{{colorSchemeHighlightedButton_}}"
-      hidden="[[!automaticSeedColorEnabled]]">
+      hidden$="[[!automaticSeedColorEnabled]]">
     <template is="dom-repeat" items="[[colorSchemes_]]" as="colorScheme">
-      <cr-button 
+      <cr-button
           tabindex$="[[getTabIndex_(colorScheme.id)]]"
           on-click="onClickColorSchemeButton_"
-          data-color-scheme-id$="[[colorScheme.id]]">
+          data-color-scheme-id$="[[colorScheme.id]]"
+          aria-checked$="[[getColorSchemeAriaChecked_(colorScheme.id, colorSchemeSelected_)]]">
         <color-scheme-icon-svg scheme="[[colorScheme]]"></color-scheme-icon-svg>
       </cr-button>
     </template>
@@ -89,7 +90,7 @@
       id="staticColorSelector"
       selected="0"
       selected-item="{{staticColorHighlightedButton_}}"
-      hidden="[[automaticSeedColorEnabled]]">
+      hidden$="[[automaticSeedColorEnabled]]">
     <template is="dom-repeat" items="[[staticColors_]]" as="staticColor">
       <cr-button
           tabindex$="[[getTabIndex_(staticColor)]]"
diff --git a/ash/webui/personalization_app/resources/js/theme/dynamic_color_element.ts b/ash/webui/personalization_app/resources/js/theme/dynamic_color_element.ts
index 5e8a5cc..07f0745 100644
--- a/ash/webui/personalization_app/resources/js/theme/dynamic_color_element.ts
+++ b/ash/webui/personalization_app/resources/js/theme/dynamic_color_element.ts
@@ -60,9 +60,7 @@
       // Whether or not to use the wallpaper to calculate the seed color.
       automaticSeedColorEnabled: {
         type: Boolean,
-        value: true,
-        notify: true,
-        reflectToAttribute: true,
+        computed: 'isAutomaticSeedColorEnabled_(colorSchemeSelected_)',
       },
       // The static color stored in the backend.
       staticColorSelected_: Object,
@@ -145,6 +143,8 @@
     ThemeObserver.initThemeObserverIfNeeded();
     this.watch<DynamicColorElement['staticColorSelected_']>(
         'staticColorSelected_', state => state.theme.staticColorSelected);
+    this.watch<DynamicColorElement['colorSchemeSelected_']>(
+        'colorSchemeSelected_', state => state.theme.colorSchemeSelected);
     this.updateFromStore();
     initializeDynamicColorData(getThemeProvider(), this.getStore());
   }
@@ -152,7 +152,6 @@
   private onClickColorSchemeButton_(event: Event) {
     const eventTarget = event.currentTarget as HTMLElement;
     const colorScheme = Number(eventTarget.dataset['colorSchemeId']);
-    this.colorSchemeSelected_ = colorScheme;
     setColorSchemePref(colorScheme, getThemeProvider(), this.getStore());
   }
 
@@ -163,7 +162,7 @@
     setStaticColorPref(staticColor, getThemeProvider(), this.getStore());
   }
 
-  private onClickToggle_() {
+  private onToggleChanged_() {
     if (this.automaticSeedColorEnabled) {
       const staticColor = this.staticColorSelected_ || DEFAULT_STATIC_COLOR;
       setStaticColorPref(staticColor, getThemeProvider(), this.getStore());
@@ -173,13 +172,26 @@
     }
   }
 
+  private isAutomaticSeedColorEnabled_(colorScheme: ColorScheme|null) {
+    return colorScheme === null || colorScheme !== ColorScheme.kStatic;
+  }
+
+  private getColorSchemeAriaChecked_(
+      colorScheme: string, colorSchemeSelected: string): 'true'|'false' {
+    if (!colorSchemeSelected) {
+      return 'false';
+    }
+    return colorSchemeSelected === colorScheme ? 'true' : 'false';
+  }
+
   private getStaticColorAriaChecked_(
-      staticColor: string, staticColorSelected: SkColor|null): string {
+      staticColor: string, staticColorSelected: SkColor|null): 'true'|'false' {
     if (!staticColorSelected) {
       return 'false';
     }
-    return (staticColor === convertToRgbHexStr(staticColorSelected.value))
-        .toString();
+    return staticColor === convertToRgbHexStr(staticColorSelected.value) ?
+        'true' :
+        'false';
   }
 
   private onStaticColorKeysPress_(
diff --git a/ash/webui/personalization_app/resources/js/theme/theme_controller.ts b/ash/webui/personalization_app/resources/js/theme/theme_controller.ts
index 9878edbcc..e9ea45b 100644
--- a/ash/webui/personalization_app/resources/js/theme/theme_controller.ts
+++ b/ash/webui/personalization_app/resources/js/theme/theme_controller.ts
@@ -29,8 +29,12 @@
 export async function initializeDynamicColorData(
     provider: ThemeProviderInterface,
     store: PersonalizationStore): Promise<void> {
+  store.beginBatchUpdate();
   const {staticColor} = await provider.getStaticColor();
   store.dispatch(setStaticColorAction(staticColor));
+  const {colorScheme} = await provider.getColorScheme();
+  store.dispatch(setColorSchemeAction(colorScheme));
+  store.endBatchUpdate();
 }
 
 // Disables or enables dark color mode.
@@ -63,4 +67,5 @@
     store: PersonalizationStore) {
   provider.setStaticColor(staticColor);
   store.dispatch(setStaticColorAction(staticColor));
+  store.dispatch(setColorSchemeAction(ColorScheme.kStatic));
 }
diff --git a/ash/webui/personalization_app/resources/js/theme/theme_observer.ts b/ash/webui/personalization_app/resources/js/theme/theme_observer.ts
index ab1eb19..66c259d9 100644
--- a/ash/webui/personalization_app/resources/js/theme/theme_observer.ts
+++ b/ash/webui/personalization_app/resources/js/theme/theme_observer.ts
@@ -4,10 +4,10 @@
 
 import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
 
-import {ThemeObserverInterface, ThemeObserverReceiver, ThemeProviderInterface} from '../personalization_app.mojom-webui.js';
+import {ColorScheme, ThemeObserverInterface, ThemeObserverReceiver, ThemeProviderInterface} from '../personalization_app.mojom-webui.js';
 import {PersonalizationStore} from '../personalization_store.js';
 
-import {setColorModeAutoScheduleEnabledAction, setDarkModeEnabledAction, setStaticColorAction} from './theme_actions.js';
+import {setColorModeAutoScheduleEnabledAction, setColorSchemeAction, setDarkModeEnabledAction, setStaticColorAction} from './theme_actions.js';
 import {getThemeProvider} from './theme_interface_provider.js';
 
 /** @fileoverview listens for updates on color mode changes. */
@@ -51,6 +51,11 @@
     store.dispatch(setColorModeAutoScheduleEnabledAction(enabled));
   }
 
+  onColorSchemeChanged(scheme: ColorScheme): void {
+    const store = PersonalizationStore.getInstance();
+    store.dispatch(setColorSchemeAction(scheme));
+  }
+
   onStaticColorChanged(staticColor: SkColor): void {
     const store = PersonalizationStore.getInstance();
     store.dispatch(setStaticColorAction(staticColor));
diff --git a/ash/webui/personalization_app/resources/js/theme/theme_reducers.ts b/ash/webui/personalization_app/resources/js/theme/theme_reducers.ts
index 53c6de3..9a9364e 100644
--- a/ash/webui/personalization_app/resources/js/theme/theme_reducers.ts
+++ b/ash/webui/personalization_app/resources/js/theme/theme_reducers.ts
@@ -31,6 +31,17 @@
   }
 }
 
+export function colorSchemeSelectedReducer(
+    state: ThemeState['colorSchemeSelected'], action: Actions,
+    _: PersonalizationState): ThemeState['colorSchemeSelected'] {
+  switch (action.name) {
+    case ThemeActionName.SET_COLOR_SCHEME:
+      return action.colorScheme;
+    default:
+      return state;
+  }
+}
+
 export function staticColorSelectedReducer(
     state: ThemeState['staticColorSelected'], action: Actions,
     _: PersonalizationState): ThemeState['staticColorSelected'] {
@@ -46,5 +57,6 @@
     {[K in keyof ThemeState]: ReducerFunction<ThemeState[K]>} = {
       colorModeAutoScheduleEnabled: colorModeAutoScheduleEnabledReducer,
       darkModeEnabled: darkModeEnabledReducer,
+      colorSchemeSelected: colorSchemeSelectedReducer,
       staticColorSelected: staticColorSelectedReducer,
     };
diff --git a/ash/webui/personalization_app/resources/js/theme/theme_state.ts b/ash/webui/personalization_app/resources/js/theme/theme_state.ts
index 3b27765..51bad29 100644
--- a/ash/webui/personalization_app/resources/js/theme/theme_state.ts
+++ b/ash/webui/personalization_app/resources/js/theme/theme_state.ts
@@ -4,11 +4,14 @@
 
 import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
 
+import {ColorScheme} from '../personalization_app.mojom-webui.js';
+
 /**
  * Stores theme related states.
  */
 export interface ThemeState {
   colorModeAutoScheduleEnabled: boolean|null;
+  colorSchemeSelected: ColorScheme|null;
   darkModeEnabled: boolean|null;
   staticColorSelected: SkColor|null;
 }
@@ -16,6 +19,7 @@
 export function emptyState(): ThemeState {
   return {
     colorModeAutoScheduleEnabled: null,
+    colorSchemeSelected: null,
     darkModeEnabled: null,
     staticColorSelected: null,
   };
diff --git a/ash/webui/personalization_app/test/fake_personalization_app_theme_provider.cc b/ash/webui/personalization_app/test/fake_personalization_app_theme_provider.cc
index e954d75..5dc740e 100644
--- a/ash/webui/personalization_app/test/fake_personalization_app_theme_provider.cc
+++ b/ash/webui/personalization_app/test/fake_personalization_app_theme_provider.cc
@@ -55,6 +55,11 @@
   return;
 }
 
+void FakePersonalizationAppThemeProvider::GetColorScheme(
+    GetColorSchemeCallback callback) {
+  std::move(callback).Run(ash::ColorScheme::kTonalSpot);
+}
+
 void FakePersonalizationAppThemeProvider::GetStaticColor(
     GetStaticColorCallback callback) {
   std::move(callback).Run(SK_ColorBLUE);
diff --git a/ash/webui/personalization_app/test/fake_personalization_app_theme_provider.h b/ash/webui/personalization_app/test/fake_personalization_app_theme_provider.h
index 5e34a49..78969ca 100644
--- a/ash/webui/personalization_app/test/fake_personalization_app_theme_provider.h
+++ b/ash/webui/personalization_app/test/fake_personalization_app_theme_provider.h
@@ -49,6 +49,8 @@
 
   void SetStaticColor(::SkColor static_color) override;
 
+  void GetColorScheme(GetColorSchemeCallback callback) override;
+
   void GetStaticColor(GetStaticColorCallback callback) override;
 
   void IsDarkModeEnabled(IsDarkModeEnabledCallback callback) override;
diff --git a/ash/webui/personalization_app/test/personalization_app_mojom_banned_browsertest_fixture.cc b/ash/webui/personalization_app/test/personalization_app_mojom_banned_browsertest_fixture.cc
index fcc3fde..ad57b2f 100644
--- a/ash/webui/personalization_app/test/personalization_app_mojom_banned_browsertest_fixture.cc
+++ b/ash/webui/personalization_app/test/personalization_app_mojom_banned_browsertest_fixture.cc
@@ -107,6 +107,10 @@
               (bool enabled),
               (override));
   MOCK_METHOD(void,
+              GetColorScheme,
+              (GetColorSchemeCallback callback),
+              (override));
+  MOCK_METHOD(void,
               GetStaticColor,
               (GetStaticColorCallback callback),
               (override));
diff --git a/ash/wm/float/tablet_mode_float_window_resizer.cc b/ash/wm/float/tablet_mode_float_window_resizer.cc
index 3e6ab9c..31f7ed2 100644
--- a/ash/wm/float/tablet_mode_float_window_resizer.cc
+++ b/ash/wm/float/tablet_mode_float_window_resizer.cc
@@ -30,6 +30,9 @@
 // The minimum distance that will be considered as a drag event.
 constexpr float kMinimumDragDistance = 5.f;
 
+// Minimum fling velocity required to tuck the window.
+const int kFlingToTuckVelocityThresholdSquared = 800 * 800;
+
 }  // namespace
 
 TabletModeFloatWindowResizer::TabletModeFloatWindowResizer(
@@ -131,9 +134,19 @@
   absl::optional<bool> left;
   bool up;
   if (event->type() == ui::ET_SCROLL_FLING_START) {
-    if (details.velocity_x() != 0.f)
-      left.emplace(details.velocity_x() < 0.f);
-    up = details.velocity_y() < 0.f;
+    float velocity_x = details.velocity_x();
+    float velocity_y = details.velocity_y();
+    float fling_amount = velocity_x * velocity_x + velocity_y * velocity_y;
+    // If the fling wasn't large enough, update the window position based on its
+    // drag location.
+    if (fling_amount <= kFlingToTuckVelocityThresholdSquared) {
+      CompleteDrag();
+      return;
+    }
+    if (velocity_x != 0.f) {
+      left.emplace(velocity_x < 0.f);
+    }
+    up = velocity_y < 0.f;
   } else {
     DCHECK_EQ(ui::ET_GESTURE_SWIPE, event->type());
     if (details.swipe_left() || details.swipe_right())
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc
index 37b5113..74d0d38 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -39,8 +39,8 @@
 #include "base/notreached.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/tick_clock.h"
+#include "chromeos/ash/components/system/devicemode.h"
 #include "chromeos/dbus/power/power_manager_client.h"
-#include "chromeos/system/devicemode.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window_observer.h"
 #include "ui/base/accelerators/accelerator.h"
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_cue.cc b/ash/wm/tablet_mode/tablet_mode_multitask_cue.cc
new file mode 100644
index 0000000..9b26a826
--- /dev/null
+++ b/ash/wm/tablet_mode/tablet_mode_multitask_cue.cc
@@ -0,0 +1,118 @@
+// 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.
+
+#include "ash/wm/tablet_mode/tablet_mode_multitask_cue.h"
+
+#include "ash/constants/app_types.h"
+#include "ash/shell.h"
+#include "ash/wm/window_state.h"
+#include "ash/wm/window_util.h"
+#include "chromeos/ui/wm/features.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/wm/public/activation_client.h"
+
+namespace ash {
+
+namespace {
+
+// Cue layout values.
+constexpr float kCornerRadius = 2;
+constexpr int kCueYOffset = 6;
+constexpr int kCueWidth = 48;
+constexpr int kCueHeight = 4;
+
+}  // namespace
+
+TabletModeMultitaskCue::TabletModeMultitaskCue() {
+  DCHECK(chromeos::wm::features::IsFloatWindowEnabled());
+  Shell::Get()->activation_client()->AddObserver(this);
+}
+
+TabletModeMultitaskCue::~TabletModeMultitaskCue() {
+  DismissCueInternal();
+  Shell::Get()->activation_client()->RemoveObserver(this);
+}
+
+void TabletModeMultitaskCue::OnWindowActivated(ActivationReason reason,
+                                               aura::Window* gained_active,
+                                               aura::Window* lost_active) {
+  if (!gained_active)
+    return;
+
+  // Only show the cue on app windows.
+  // TODO(hewer): Review and update logic when `gained_active` is a NON_APP
+  // window and `lost_active` is an app.
+  if (static_cast<AppType>(gained_active->GetProperty(
+          aura::client::kAppType)) == AppType::NON_APP) {
+    return;
+  }
+
+  // `UpdateCueInternal()` does not currently re-parent the layer, so it must be
+  // dismissed before it can be shown again. May change when animations are
+  // implemented.
+  DismissCueInternal();
+
+  // Floated windows do not have the multitask menu.
+  // TODO(hewer): Consolidate checks with ones for multitask menu in a helper.
+  WindowState* state = WindowState::Get(gained_active);
+  if (state->IsFloated() || !state->CanMaximize())
+    return;
+
+  window_ = gained_active;
+
+  cue_layer_ = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR);
+  cue_layer_->SetColor(SK_ColorGRAY);
+  cue_layer_->SetRoundedCornerRadius(gfx::RoundedCornersF(kCornerRadius));
+
+  window_->layer()->Add(cue_layer_.get());
+
+  UpdateCueBounds();
+
+  // Observe `window_` to update the cue if the window gets destroyed, its
+  // bounds change, or its state type changes (e.g., is floated).
+  window_observation_.Observe(window_);
+  state->AddObserver(this);
+}
+
+void TabletModeMultitaskCue::OnWindowDestroying(aura::Window* window) {
+  DismissCueInternal();
+}
+
+void TabletModeMultitaskCue::OnWindowBoundsChanged(
+    aura::Window* window,
+    const gfx::Rect& old_bounds,
+    const gfx::Rect& new_bounds,
+    ui::PropertyChangeReason reason) {
+  UpdateCueBounds();
+}
+
+void TabletModeMultitaskCue::OnPostWindowStateTypeChange(
+    WindowState* window_state,
+    chromeos::WindowStateType old_type) {
+  if (window_state->IsFloated())
+    DismissCueInternal();
+}
+
+void TabletModeMultitaskCue::DismissCueInternal() {
+  window_observation_.Reset();
+
+  if (window_) {
+    WindowState::Get(window_)->RemoveObserver(this);
+    window_ = nullptr;
+  }
+
+  cue_layer_.reset();
+}
+
+void TabletModeMultitaskCue::UpdateCueBounds() {
+  // Needed for some edge cases where the cue is dismissed while it is being
+  // updated.
+  if (!window_)
+    return;
+
+  cue_layer_->SetBounds(gfx::Rect((window_->bounds().width() - kCueWidth) / 2,
+                                  kCueYOffset, kCueWidth, kCueHeight));
+}
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_cue.h b/ash/wm/tablet_mode/tablet_mode_multitask_cue.h
new file mode 100644
index 0000000..933378c
--- /dev/null
+++ b/ash/wm/tablet_mode/tablet_mode_multitask_cue.h
@@ -0,0 +1,71 @@
+// 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 ASH_WM_TABLET_MODE_TABLET_MODE_MULTITASK_CUE_H_
+#define ASH_WM_TABLET_MODE_TABLET_MODE_MULTITASK_CUE_H_
+
+#include <memory>
+
+#include "ash/ash_export.h"
+#include "ash/wm/window_state_observer.h"
+#include "base/scoped_observation.h"
+#include "ui/aura/window.h"
+#include "ui/compositor/layer.h"
+#include "ui/wm/public/activation_change_observer.h"
+
+namespace ash {
+
+// Creates a cue (drag bar) when app windows are activated in tablet mode.
+class ASH_EXPORT TabletModeMultitaskCue : aura::WindowObserver,
+                                          wm::ActivationChangeObserver,
+                                          WindowStateObserver {
+ public:
+  TabletModeMultitaskCue();
+
+  TabletModeMultitaskCue(const TabletModeMultitaskCue&) = delete;
+  TabletModeMultitaskCue& operator=(const TabletModeMultitaskCue&) = delete;
+
+  ~TabletModeMultitaskCue() override;
+
+  // aura::WindowObserver:
+  void OnWindowDestroying(aura::Window* window) override;
+  void OnWindowBoundsChanged(aura::Window* window,
+                             const gfx::Rect& old_bounds,
+                             const gfx::Rect& new_bounds,
+                             ui::PropertyChangeReason reason) override;
+
+  // wm::ActivationChangeObserver:
+  void OnWindowActivated(ActivationReason reason,
+                         aura::Window* gained_active,
+                         aura::Window* lost_active) override;
+
+  // WindowStateObserver:
+  void OnPostWindowStateTypeChange(WindowState* window_state,
+                                   chromeos::WindowStateType old_type) override;
+
+  ui::Layer* cue_layer_for_testing() { return cue_layer_.get(); }
+
+ private:
+  // Dismisses the cue from the screen and cleans up the pointers and
+  // observers related to its parent window.
+  void DismissCueInternal();
+
+  // Updates the bounds of the cue relative to the window if the window is
+  // still available.
+  void UpdateCueBounds();
+
+  // The app window that the cue is associated with.
+  aura::Window* window_ = nullptr;
+
+  // The solid color layer that represents the cue (drag bar).
+  std::unique_ptr<ui::Layer> cue_layer_;
+
+  // Observes for window destruction or bounds changes.
+  base::ScopedObservation<aura::Window, aura::WindowObserver>
+      window_observation_{this};
+};
+
+}  // namespace ash
+
+#endif  // ASH_WM_TABLET_MODE_TABLET_MODE_MULTITASK_CUE_H_
\ No newline at end of file
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_cue_unittest.cc b/ash/wm/tablet_mode/tablet_mode_multitask_cue_unittest.cc
new file mode 100644
index 0000000..b5d50d5
--- /dev/null
+++ b/ash/wm/tablet_mode/tablet_mode_multitask_cue_unittest.cc
@@ -0,0 +1,61 @@
+// 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.
+
+#include "ash/wm/tablet_mode/tablet_mode_multitask_cue.h"
+
+#include "ash/test/ash_test_base.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
+#include "ash/wm/tablet_mode/tablet_mode_window_manager.h"
+#include "base/test/scoped_feature_list.h"
+#include "chromeos/ui/wm/features.h"
+
+namespace ash {
+
+namespace {
+
+constexpr int kCueYOffset = 6;
+constexpr int kCueWidth = 48;
+constexpr int kCueHeight = 4;
+
+}  // namespace
+
+class TabletModeMultitaskCueTest : public AshTestBase {
+ public:
+  TabletModeMultitaskCueTest()
+      : scoped_feature_list_(chromeos::wm::features::kFloatWindow) {}
+  TabletModeMultitaskCueTest(const TabletModeMultitaskCueTest&) = delete;
+  TabletModeMultitaskCueTest& operator=(const TabletModeMultitaskCueTest&) =
+      delete;
+  ~TabletModeMultitaskCueTest() override = default;
+
+  TabletModeMultitaskCue* GetMultitaskCue() {
+    return TabletModeControllerTestApi()
+        .tablet_mode_window_manager()
+        ->tablet_mode_multitask_cue_for_testing();
+  }
+
+  // AshTestBase:
+  void SetUp() override {
+    AshTestBase::SetUp();
+    TabletModeControllerTestApi().EnterTabletMode();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(TabletModeMultitaskCueTest, BasicShowCue) {
+  auto window = CreateAppWindow();
+
+  auto* multitask_cue = GetMultitaskCue();
+  ASSERT_TRUE(multitask_cue);
+
+  ui::Layer* cue_layer = multitask_cue->cue_layer_for_testing();
+  ASSERT_TRUE(cue_layer);
+  EXPECT_EQ(
+      gfx::Rect((800 - kCueWidth) / 2, kCueYOffset, kCueWidth, kCueHeight),
+      cue_layer->bounds());
+}
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.cc b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
index c71482dc..d1afb548 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
@@ -24,6 +24,7 @@
 #include "ash/wm/splitview/split_view_utils.h"
 #include "ash/wm/tablet_mode/scoped_skip_user_session_blocked_check.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "ash/wm/tablet_mode/tablet_mode_multitask_cue.h"
 #include "ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.h"
 #include "ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.h"
 #include "ash/wm/tablet_mode/tablet_mode_window_state.h"
@@ -179,6 +180,7 @@
   if (chromeos::wm::features::IsFloatWindowEnabled()) {
     tablet_mode_multitask_menu_event_handler_ =
         std::make_unique<TabletModeMultitaskMenuEventHandler>();
+    tablet_mode_multitask_cue_ = std::make_unique<TabletModeMultitaskCue>();
   }
 }
 
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.h b/ash/wm/tablet_mode/tablet_mode_window_manager.h
index 9dd0692..b1fcfbd 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.h
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.h
@@ -28,6 +28,7 @@
 
 namespace ash {
 class TabletModeController;
+class TabletModeMultitaskCue;
 class TabletModeMultitaskMenuEventHandler;
 class TabletModeToggleFullscreenEventHandler;
 class TabletModeWindowState;
@@ -112,6 +113,10 @@
     return tablet_mode_multitask_menu_event_handler_.get();
   }
 
+  TabletModeMultitaskCue* tablet_mode_multitask_cue_for_testing() {
+    return tablet_mode_multitask_cue_.get();
+  }
+
  private:
   using WindowToState = std::map<aura::Window*, TabletModeWindowState*>;
   using WindowAndStateTypeList =
@@ -201,6 +206,10 @@
   std::unique_ptr<TabletModeMultitaskMenuEventHandler>
       tablet_mode_multitask_menu_event_handler_;
 
+  // A visual cue (drag bar) that indicates the gesture to activate the
+  // multitask menu.
+  std::unique_ptr<TabletModeMultitaskCue> tablet_mode_multitask_cue_;
+
   absl::optional<display::ScopedDisplayObserver> display_observer_;
 
   // True when tablet mode is about to end.
diff --git a/build/fuchsia/test/common.py b/build/fuchsia/test/common.py
index 448b1df..2e1b1f56 100644
--- a/build/fuchsia/test/common.py
+++ b/build/fuchsia/test/common.py
@@ -13,7 +13,7 @@
 from argparse import ArgumentParser
 from typing import Iterable, List, Optional
 
-from compatible_utils import get_ssh_prefix, get_host_arch
+from compatible_utils import get_ssh_prefix, get_host_arch, running_unattended
 
 DIR_SRC_ROOT = os.path.abspath(
     os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir))
@@ -250,14 +250,33 @@
 def resolve_packages(packages: List[str], target_id: Optional[str]) -> None:
     """Ensure that all |packages| are installed on a device."""
 
+    ssh_prefix = get_ssh_prefix(get_ssh_address(target_id))
+
+    # Garbage collection for swarming bots.
+    if running_unattended():
+        subprocess.run(ssh_prefix + ['--', 'pkgctl', 'gc'], check=False)
+
     for package in packages:
         resolve_cmd = [
             '--', 'pkgctl', 'resolve',
             'fuchsia-pkg://%s/%s' % (REPO_ALIAS, package)
         ]
-        subprocess.run(get_ssh_prefix(get_ssh_address(target_id)) +
-                       resolve_cmd,
-                       check=True)
+        retry_command(ssh_prefix + resolve_cmd)
+
+
+def retry_command(cmd: List[str], retries: int = 2,
+                  **kwargs) -> Optional[subprocess.CompletedProcess]:
+    """Helper function for retrying a subprocess.run command."""
+
+    for i in range(retries):
+        if i == retries - 1:
+            proc = subprocess.run(cmd, **kwargs, check=True)
+            return proc
+        proc = subprocess.run(cmd, **kwargs, check=False)
+        if proc.returncode == 0:
+            return proc
+        time.sleep(3)
+    return None
 
 
 def get_ssh_address(target_id: Optional[str]) -> str:
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index e1cfd7c..39c6026 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -158,11 +158,7 @@
   "java/res/drawable-nodpi/bookmark_widget_preview.png",
   "java/res/drawable-nodpi/widget_preview.png",
   "java/res/drawable-sw600dp/window_background.xml",
-  "java/res/drawable-v21/button_borderless_compat.xml",
-  "java/res/drawable-v21/text_cursor_lowend.xml",
   "java/res/drawable-v21/tile_view_highlight.xml",
-  "java/res/drawable-v21/web_notification_button_background.xml",
-  "java/res/drawable-v21/web_notification_small_icon_background.xml",
   "java/res/drawable-v23/distilled_page_pref_background.xml",
   "java/res/drawable-v23/section_tab_background.xml",
   "java/res/drawable-v31/distilled_page_pref_background.xml",
@@ -378,6 +374,7 @@
   "java/res/drawable/btn_bg_holo_active.xml",
   "java/res/drawable/btn_forward.xml",
   "java/res/drawable/btn_reload_stop.xml",
+  "java/res/drawable/button_borderless_compat.xml",
   "java/res/drawable/button_compat_shape.xml",
   "java/res/drawable/capture_overlay_border.xml",
   "java/res/drawable/card_container_background.xml",
@@ -449,8 +446,11 @@
   "java/res/drawable/signin_header_animation.xml",
   "java/res/drawable/tab_indicator.xml",
   "java/res/drawable/tab_layout_background.xml",
+  "java/res/drawable/text_cursor_lowend.xml",
   "java/res/drawable/tile_view_hairline_border_background.xml",
   "java/res/drawable/virtual_card_enrollment_illustration.xml",
+  "java/res/drawable/web_notification_button_background.xml",
+  "java/res/drawable/web_notification_small_icon_background.xml",
   "java/res/layout-sw600dp/find_toolbar.xml",
   "java/res/layout/accessibility_tab_switcher.xml",
   "java/res/layout/accessibility_tab_switcher_list_item.xml",
diff --git a/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
index d084cb6..f2a9775b 100644
--- a/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
@@ -1008,7 +1008,7 @@
     <receiver  # DIFF-ANCHOR: 226134db
         android:name="org.chromium.chrome.browser.quickactionsearchwidget.QuickActionSearchWidgetProvider$QuickActionSearchWidgetProviderDino"
         android:exported="true"
-        android:label="@string/dino_widget_title">
+        android:label="@string/dino_widget_text">
       <intent-filter>  # DIFF-ANCHOR: 4ed161a4
         <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
       </intent-filter>  # DIFF-ANCHOR: 4ed161a4
diff --git a/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected
index d77066a..0dc02b3 100644
--- a/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected
@@ -777,7 +777,7 @@
     <receiver  # DIFF-ANCHOR: 226134db
         android:name="org.chromium.chrome.browser.quickactionsearchwidget.QuickActionSearchWidgetProvider$QuickActionSearchWidgetProviderDino"
         android:exported="true"
-        android:label="@string/dino_widget_title">
+        android:label="@string/dino_widget_text">
       <intent-filter>  # DIFF-ANCHOR: 4ed161a4
         <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
       </intent-filter>  # DIFF-ANCHOR: 4ed161a4
diff --git a/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
index c4bfc7c..f5bf5c8 100644
--- a/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
@@ -934,7 +934,7 @@
     <receiver  # DIFF-ANCHOR: 226134db
         android:name="org.chromium.chrome.browser.quickactionsearchwidget.QuickActionSearchWidgetProvider$QuickActionSearchWidgetProviderDino"
         android:exported="true"
-        android:label="@string/dino_widget_title">
+        android:label="@string/dino_widget_text">
       <intent-filter>  # DIFF-ANCHOR: 4ed161a4
         <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
       </intent-filter>  # DIFF-ANCHOR: 4ed161a4
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 39c57cc7..e861de3a 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -899,7 +899,7 @@
 
         <receiver
         android:name="org.chromium.chrome.browser.quickactionsearchwidget.QuickActionSearchWidgetProvider$QuickActionSearchWidgetProviderDino"
-        android:label="@string/dino_widget_title"
+        android:label="@string/dino_widget_text"
             android:exported="true">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
diff --git a/chrome/android/java/res/drawable-v21/button_borderless_compat.xml b/chrome/android/java/res/drawable/button_borderless_compat.xml
similarity index 100%
rename from chrome/android/java/res/drawable-v21/button_borderless_compat.xml
rename to chrome/android/java/res/drawable/button_borderless_compat.xml
diff --git a/chrome/android/java/res/drawable-v21/text_cursor_lowend.xml b/chrome/android/java/res/drawable/text_cursor_lowend.xml
similarity index 100%
rename from chrome/android/java/res/drawable-v21/text_cursor_lowend.xml
rename to chrome/android/java/res/drawable/text_cursor_lowend.xml
diff --git a/chrome/android/java/res/drawable-v21/web_notification_button_background.xml b/chrome/android/java/res/drawable/web_notification_button_background.xml
similarity index 100%
rename from chrome/android/java/res/drawable-v21/web_notification_button_background.xml
rename to chrome/android/java/res/drawable/web_notification_button_background.xml
diff --git a/chrome/android/java/res/drawable-v21/web_notification_small_icon_background.xml b/chrome/android/java/res/drawable/web_notification_small_icon_background.xml
similarity index 100%
rename from chrome/android/java/res/drawable-v21/web_notification_small_icon_background.xml
rename to chrome/android/java/res/drawable/web_notification_small_icon_background.xml
diff --git a/chrome/android/java/res/xml/search_widget_info.xml b/chrome/android/java/res/xml/search_widget_info.xml
index 5fb7c9c8..6b19bfe 100644
--- a/chrome/android/java/res/xml/search_widget_info.xml
+++ b/chrome/android/java/res/xml/search_widget_info.xml
@@ -11,6 +11,7 @@
     android:minWidth="240dp"
     android:minHeight="48dp"
     android:initialLayout="@layout/search_widget_template"
+    android:previewLayout="@layout/search_widget_template"
     android:previewImage="@drawable/widget_preview"
     android:resizeMode="horizontal"
     android:updatePeriodMillis="86400000"
diff --git a/chrome/android/java/res_chromium_base/values/channel_constants.xml b/chrome/android/java/res_chromium_base/values/channel_constants.xml
index b475689..cc1f2a30 100644
--- a/chrome/android/java/res_chromium_base/values/channel_constants.xml
+++ b/chrome/android/java/res_chromium_base/values/channel_constants.xml
@@ -11,5 +11,4 @@
     <string name="bookmark_widget_title" translatable="false">Chromium bookmarks</string>
     <string name="search_widget_title" translatable="false">Chromium search</string>
     <string name="quick_action_search_widget_title" translatable="false">Chromium quick action search</string>
-    <string name="dino_widget_title" translatable="false">Chromium dino</string>
 </resources>
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index af547e2..01c53bb 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -4243,7 +4243,7 @@
   </message>
 
   <!-- Lacros data migration screen. -->
-  <message translateable="false" name="IDS_LACROS_DATA_MIGRATION_SCREEN_TITLE" desc="Title of the data migration screen.">
+  <message name="IDS_LACROS_DATA_MIGRATION_SCREEN_TITLE" desc="Title of the data migration screen.">
     Updating Chrome browser
   </message>
   <message name="IDS_LACROS_DATA_MIGRATION_SCREEN_SUBTITLE" desc="Shows the % of progress.">
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LACROS_DATA_MIGRATION_SCREEN_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LACROS_DATA_MIGRATION_SCREEN_TITLE.png.sha1
new file mode 100644
index 0000000..e76dff7
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_LACROS_DATA_MIGRATION_SCREEN_TITLE.png.sha1
@@ -0,0 +1 @@
+d1d569f958465c135f750e912d2955f49fac5801
\ No newline at end of file
diff --git a/chrome/app/extensions_strings.grdp b/chrome/app/extensions_strings.grdp
index 6e9e8d1..e98044a7 100644
--- a/chrome/app/extensions_strings.grdp
+++ b/chrome/app/extensions_strings.grdp
@@ -421,6 +421,9 @@
   <message name="IDS_EXTENSIONS_SITE_PERMISSIONS_ALWAYS_ON_THIS_SITE" desc="The option shown for an extension that can automatically access the site in the edit permissions dialog.">
     Always on this site
   </message>
+  <message name="IDS_EXTENSIONS_SITE_SETTINGS" desc="The text for the link to manage site settings for the extension.">
+    Site settings
+  </message>
   <message name="IDS_EXTENSIONS_PERMITTED_SITES" desc="The label above the list of sites that extensions are always permitted to run in.">
     All extensions allowed
   </message>
diff --git a/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_SITE_SETTINGS.png.sha1 b/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_SITE_SETTINGS.png.sha1
new file mode 100644
index 0000000..f0e11f0
--- /dev/null
+++ b/chrome/app/extensions_strings_grdp/IDS_EXTENSIONS_SITE_SETTINGS.png.sha1
@@ -0,0 +1 @@
+0d79b5fc5cf034c7f6e91f79401faae3f1ed82cf
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index a6f5f77d..3c1b52ab 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3945,6 +3945,8 @@
       "new_tab_page/modules/drive/drive_service_factory.h",
       "new_tab_page/modules/feed/feed_handler.cc",
       "new_tab_page/modules/feed/feed_handler.h",
+      "new_tab_page/modules/new_tab_page_modules.cc",
+      "new_tab_page/modules/new_tab_page_modules.h",
       "new_tab_page/modules/photos/photos_handler.cc",
       "new_tab_page/modules/photos/photos_handler.h",
       "new_tab_page/modules/photos/photos_service.cc",
@@ -5223,6 +5225,7 @@
       "//chromeos/ash/components/network",
       "//chromeos/ash/components/settings",
       "//chromeos/ash/components/sync_wifi",
+      "//chromeos/ash/components/system",
       "//chromeos/ash/components/timezone",
       "//chromeos/ash/components/tpm",
       "//chromeos/ash/services/assistant/public/cpp",
@@ -5260,7 +5263,6 @@
       "//chromeos/services/network_health/public/cpp",
       "//chromeos/services/network_health/public/mojom:mojom",
       "//chromeos/strings",
-      "//chromeos/system",
       "//chromeos/ui/base",
       "//chromeos/ui/vector_icons",
       "//chromeos/version",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 0810df9..d9e0275 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -634,16 +634,6 @@
          nullptr},
 };
 
-const FeatureEntry::FeatureParam
-    kOmniboxRemoveSuggestionHeaderChevron_DisallowCollapse[] = {
-        {"allow_group_collapsed_state", "false"}};
-const FeatureEntry::FeatureVariation
-    kOmniboxRemoveSuggestionHeaderChevronVariations[] = {
-        {"DisallowCollapse",
-         kOmniboxRemoveSuggestionHeaderChevron_DisallowCollapse,
-         std::size(kOmniboxRemoveSuggestionHeaderChevron_DisallowCollapse),
-         nullptr},
-};
 #endif  // BUILDFLAG(IS_ANDROID)
 
 const FeatureEntry::FeatureParam
@@ -3571,6 +3561,10 @@
     {"enable-webassembly-baseline", flag_descriptions::kEnableWasmBaselineName,
      flag_descriptions::kEnableWasmBaselineDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kWebAssemblyBaseline)},
+    {"enable-webassembly-garbage-collection",
+     flag_descriptions::kEnableWasmGarbageCollectionName,
+     flag_descriptions::kEnableWasmGarbageCollectionDescription, kOsAll,
+     FEATURE_VALUE_TYPE(features::kWebAssemblyGarbageCollection)},
     {"enable-webassembly-lazy-compilation",
      flag_descriptions::kEnableWasmLazyCompilationName,
      flag_descriptions::kEnableWasmLazyCompilationDescription, kOsAll,
@@ -5283,15 +5277,6 @@
      kOsAndroid,
      FEATURE_VALUE_TYPE(omnibox::kOmniboxMostVisitedTilesAddRecycledViewPool)},
 
-    {"omnibox-remove-suggestion-header-chevron",
-     flag_descriptions::kOmniboxRemoveSuggestionHeaderChevronName,
-     flag_descriptions::kOmniboxRemoveSuggestionHeaderChevronDescription,
-     kOsAndroid,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(
-         omnibox::kOmniboxRemoveSuggestionHeaderChevron,
-         kOmniboxRemoveSuggestionHeaderChevronVariations,
-         "OmniboxRemoveSuggestionHeaderChevron")},
-
     {"omnibox-most-visited-tiles-title-wrap-around",
      flag_descriptions::kOmniboxMostVisitedTilesTitleWrapAroundName,
      flag_descriptions::kOmniboxMostVisitedTilesTitleWrapAroundDescription,
@@ -9567,6 +9552,11 @@
      flag_descriptions::kStorageBucketsDescription, kOsAll,
      FEATURE_VALUE_TYPE(blink::features::kStorageBuckets)},
 
+    {"service-worker-bypass-fetch-handler-for-main-resource",
+     flag_descriptions::kServiceWorkerBypassFetchHandlerForMainResourceName,
+     flag_descriptions::
+         kServiceWorkerBypassFetchHandlerForMainResourceDescription,
+     kOsAll, FEATURE_VALUE_TYPE(features::kServiceWorkerBypassFetchHandler)}
     // 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/apps/app_preload_service/app_preload_server_connector.cc b/chrome/browser/apps/app_preload_service/app_preload_server_connector.cc
index 562e929..2f5c618 100644
--- a/chrome/browser/apps/app_preload_service/app_preload_server_connector.cc
+++ b/chrome/browser/apps/app_preload_service/app_preload_server_connector.cc
@@ -48,22 +48,22 @@
       }
     )");
 
-apps::proto::AppProvisioningRequest::UserType ConvertStringUserTypeToProto(
-    const std::string& user_type) {
+apps::proto::AppProvisioningListAppsRequest::UserType
+ConvertStringUserTypeToProto(const std::string& user_type) {
   if (user_type == apps::kUserTypeUnmanaged) {
-    return apps::proto::AppProvisioningRequest::USERTYPE_UNMANAGED;
+    return apps::proto::AppProvisioningListAppsRequest::USERTYPE_UNMANAGED;
   } else if (user_type == apps::kUserTypeManaged) {
-    return apps::proto::AppProvisioningRequest::USERTYPE_MANAGED;
+    return apps::proto::AppProvisioningListAppsRequest::USERTYPE_MANAGED;
   } else if (user_type == apps::kUserTypeChild) {
-    return apps::proto::AppProvisioningRequest::USERTYPE_CHILD;
+    return apps::proto::AppProvisioningListAppsRequest::USERTYPE_CHILD;
   } else if (user_type == apps::kUserTypeGuest) {
-    return apps::proto::AppProvisioningRequest::USERTYPE_GUEST;
+    return apps::proto::AppProvisioningListAppsRequest::USERTYPE_GUEST;
   }
-  return apps::proto::AppProvisioningRequest::USERTYPE_UNKNOWN;
+  return apps::proto::AppProvisioningListAppsRequest::USERTYPE_UNKNOWN;
 }
 
 std::string BuildGetAppsForFirstLoginRequestBody(const apps::DeviceInfo& info) {
-  apps::proto::AppProvisioningRequest request_proto;
+  apps::proto::AppProvisioningListAppsRequest request_proto;
   request_proto.set_board(info.board);
   request_proto.set_model(info.model);
   request_proto.set_language(info.locale);
@@ -146,7 +146,7 @@
     return;
   }
 
-  proto::AppProvisioningResponse response;
+  proto::AppProvisioningListAppsResponse response;
 
   if (!response.ParseFromString(*response_body)) {
     LOG(ERROR) << "Parsing failed";
diff --git a/chrome/browser/apps/app_preload_service/app_preload_server_connector_unittest.cc b/chrome/browser/apps/app_preload_service/app_preload_server_connector_unittest.cc
index 28c5c4d..872d339 100644
--- a/chrome/browser/apps/app_preload_service/app_preload_server_connector_unittest.cc
+++ b/chrome/browser/apps/app_preload_service/app_preload_server_connector_unittest.cc
@@ -74,20 +74,20 @@
   EXPECT_EQ(method_override_header, "GET");
   EXPECT_EQ(content_type, "application/x-protobuf");
 
-  proto::AppProvisioningRequest request;
+  proto::AppProvisioningListAppsRequest request;
   ASSERT_TRUE(request.ParseFromString(body));
 
   EXPECT_EQ(request.board(), "brya");
   EXPECT_EQ(request.language(), "en-US");
   EXPECT_EQ(request.model(), "taniks");
   EXPECT_EQ(request.user_type(),
-            apps::proto::AppProvisioningRequest::USERTYPE_UNMANAGED);
+            apps::proto::AppProvisioningListAppsRequest::USERTYPE_UNMANAGED);
   EXPECT_EQ(request.chrome_os_version().ash_chrome(), "10.10.10");
   EXPECT_EQ(request.chrome_os_version().platform(), "12345.0.0");
 }
 
 TEST_F(AppPreloadServerConnectorTest, GetAppsForFirstLoginSuccessfulResponse) {
-  proto::AppProvisioningResponse response;
+  proto::AppProvisioningListAppsResponse response;
   auto* app = response.add_apps_to_install();
   app->set_name("Peanut Types");
 
diff --git a/chrome/browser/apps/app_preload_service/app_preload_service_unittest.cc b/chrome/browser/apps/app_preload_service/app_preload_service_unittest.cc
index 50259f5..d5c5904 100644
--- a/chrome/browser/apps/app_preload_service/app_preload_service_unittest.cc
+++ b/chrome/browser/apps/app_preload_service/app_preload_service_unittest.cc
@@ -45,13 +45,12 @@
   return profile->GetPrefs()->GetDict(kApsStateManager);
 }
 
-void FillWebExtras(apps::proto::AppProvisioningResponse_WebExtras* extras,
-                   const std::string& start_url) {
-  extras->set_manifest_id(start_url);
-  extras->set_start_url(start_url);
-  extras->set_scope(start_url);
-  extras->set_display_mode(
-      apps::proto::AppProvisioningResponse::DISPLAY_MODE_STANDALONE);
+void FillWebExtras(
+    apps::proto::AppProvisioningListAppsResponse_WebExtras* extras,
+    const std::string& manifest_id,
+    const std::string& manifest_url) {
+  extras->set_manifest_id(manifest_id);
+  extras->set_manifest_url(manifest_url);
 }
 
 }  // namespace
@@ -160,7 +159,7 @@
 TEST_F(AppPreloadServiceTest, FirstLoginCompletedPrefSetAfterSuccess) {
   // An empty response indicates that the request completed successfully, but
   // there are no apps to install.
-  proto::AppProvisioningResponse response;
+  proto::AppProvisioningListAppsResponse response;
 
   url_loader_factory_.AddResponse(
       AppPreloadServerConnector::GetServerUrl().spec(),
@@ -192,13 +191,16 @@
   EXPECT_FALSE(flow_started.has_value());
 }
 
-TEST_F(AppPreloadServiceTest, WebAppInstall) {
-  proto::AppProvisioningResponse response;
+// TODO(b/261632289): temporarily disabled while refactoring is in progress.
+TEST_F(AppPreloadServiceTest, DISABLED_WebAppInstall) {
+  proto::AppProvisioningListAppsResponse response;
   auto* app = response.add_apps_to_install();
   app->set_name("Peanut Types");
-  app->set_platform(proto::AppProvisioningResponse::PLATFORM_WEB);
-  app->set_install_reason(proto::AppProvisioningResponse::INSTALL_REASON_OEM);
-  FillWebExtras(app->mutable_web_extras(), "https://peanuttypes.com/app");
+  app->set_platform(proto::AppProvisioningListAppsResponse::PLATFORM_WEB);
+  app->set_install_reason(
+      proto::AppProvisioningListAppsResponse::INSTALL_REASON_OEM);
+  FillWebExtras(app->mutable_web_extras(), "https://peanuttypes.com/app",
+                "https://meltingpot.googleusercontent.com/manifest.json");
 
   url_loader_factory_.AddResponse(
       AppPreloadServerConnector::GetServerUrl().spec(),
@@ -223,13 +225,14 @@
 }
 
 TEST_F(AppPreloadServiceTest, IgnoreDefaultAppInstall) {
-  proto::AppProvisioningResponse response;
+  proto::AppProvisioningListAppsResponse response;
   auto* app = response.add_apps_to_install();
   app->set_name("Peanut Types");
-  app->set_platform(proto::AppProvisioningResponse::PLATFORM_WEB);
+  app->set_platform(proto::AppProvisioningListAppsResponse::PLATFORM_WEB);
   app->set_install_reason(
-      proto::AppProvisioningResponse::INSTALL_REASON_DEFAULT);
-  FillWebExtras(app->mutable_web_extras(), "https://peanuttypes.com/app");
+      proto::AppProvisioningListAppsResponse::INSTALL_REASON_DEFAULT);
+  FillWebExtras(app->mutable_web_extras(), "https://peanuttypes.com/app",
+                "https://meltingpot.googleusercontent.com/manifest.json");
 
   url_loader_factory_.AddResponse(
       AppPreloadServerConnector::GetServerUrl().spec(),
@@ -252,11 +255,12 @@
   constexpr char kPackageName[] = "com.peanuttypes";
   constexpr char kActivityName[] = "com.peanuttypes.PeanutTypesActivity";
 
-  proto::AppProvisioningResponse response;
+  proto::AppProvisioningListAppsResponse response;
   auto* app = response.add_apps_to_install();
   app->set_name("Peanut Types");
-  app->set_platform(proto::AppProvisioningResponse::PLATFORM_ANDROID);
-  app->set_install_reason(proto::AppProvisioningResponse::INSTALL_REASON_OEM);
+  app->set_platform(proto::AppProvisioningListAppsResponse::PLATFORM_ANDROID);
+  app->set_install_reason(
+      proto::AppProvisioningListAppsResponse::INSTALL_REASON_OEM);
   app->mutable_android_extras()->set_package_name(kPackageName);
   app->mutable_android_extras()->set_activity_name(kActivityName);
 
@@ -278,20 +282,24 @@
   ASSERT_FALSE(found);
 }
 
-TEST_F(AppPreloadServiceTest, InstallOverUserApp) {
-  constexpr char kStartUrl[] = "https://www.example.com/";
+// TODO(b/261632289): temporarily disabled while refactoring is in progress.
+TEST_F(AppPreloadServiceTest, DISABLED_InstallOverUserApp) {
+  constexpr char kManifestId[] = "https://www.example.com/";
+  constexpr char kManifestUrl[] =
+      "https://meltingpot.googleusercontent.com/manifest.json";
   constexpr char kUserAppName[] = "User Installed App";
 
   auto app_id = web_app::test::InstallDummyWebApp(GetProfile(), kUserAppName,
-                                                  GURL(kStartUrl));
+                                                  GURL(kManifestId));
 
-  proto::AppProvisioningResponse response;
+  proto::AppProvisioningListAppsResponse response;
   auto* app = response.add_apps_to_install();
 
   app->set_name("OEM Installed app");
-  app->set_platform(proto::AppProvisioningResponse::PLATFORM_WEB);
-  app->set_install_reason(proto::AppProvisioningResponse::INSTALL_REASON_OEM);
-  FillWebExtras(app->mutable_web_extras(), kStartUrl);
+  app->set_platform(proto::AppProvisioningListAppsResponse::PLATFORM_WEB);
+  app->set_install_reason(
+      proto::AppProvisioningListAppsResponse::INSTALL_REASON_OEM);
+  FillWebExtras(app->mutable_web_extras(), kManifestId, kManifestUrl);
 
   url_loader_factory_.AddResponse(
       AppPreloadServerConnector::GetServerUrl().spec(),
diff --git a/chrome/browser/apps/app_preload_service/preload_app_definition.cc b/chrome/browser/apps/app_preload_service/preload_app_definition.cc
index aeff373..95bb4bb 100644
--- a/chrome/browser/apps/app_preload_service/preload_app_definition.cc
+++ b/chrome/browser/apps/app_preload_service/preload_app_definition.cc
@@ -4,40 +4,6 @@
 
 #include "chrome/browser/apps/app_preload_service/preload_app_definition.h"
 
-#include <memory>
-
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/web_applications/user_display_mode.h"
-#include "chrome/browser/web_applications/web_app_install_info.h"
-#include "components/services/app_service/public/cpp/app_types.h"
-#include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
-
-namespace {
-blink::mojom::DisplayMode GetDisplayMode(
-    apps::proto::AppProvisioningResponse_DisplayMode mode) {
-  switch (mode) {
-    case apps::proto::AppProvisioningResponse::DISPLAY_MODE_UNKNOWN:
-      // If the server sends a value we don't recognise, default to "standalone"
-      // display.
-      return blink::mojom::DisplayMode::kStandalone;
-
-    case apps::proto::AppProvisioningResponse::DISPLAY_MODE_FULLSCREEN:
-      return blink::mojom::DisplayMode::kFullscreen;
-
-    case apps::proto::AppProvisioningResponse::DISPLAY_MODE_STANDALONE:
-      return blink::mojom::DisplayMode::kStandalone;
-
-    case apps::proto::AppProvisioningResponse::DISPLAY_MODE_MINIMAL_UI:
-      return blink::mojom::DisplayMode::kMinimalUi;
-
-    case apps::proto::AppProvisioningResponse::DISPLAY_MODE_BROWSER:
-      return blink::mojom::DisplayMode::kBrowser;
-  }
-}
-
-}  // namespace
-
 namespace apps {
 
 std::string PreloadAppDefinition::GetName() const {
@@ -46,99 +12,18 @@
 
 AppType PreloadAppDefinition::GetPlatform() const {
   switch (app_proto_.platform()) {
-    case proto::AppProvisioningResponse::PLATFORM_UNKNOWN:
+    case proto::AppProvisioningListAppsResponse::PLATFORM_UNKNOWN:
       return AppType::kUnknown;
-    case proto::AppProvisioningResponse::PLATFORM_WEB:
+    case proto::AppProvisioningListAppsResponse::PLATFORM_WEB:
       return AppType::kWeb;
-    case proto::AppProvisioningResponse::PLATFORM_ANDROID:
+    case proto::AppProvisioningListAppsResponse::PLATFORM_ANDROID:
       return AppType::kArc;
   }
 }
 
 bool PreloadAppDefinition::IsOemApp() const {
   return app_proto_.install_reason() ==
-         proto::AppProvisioningResponse_InstallReason::
-             AppProvisioningResponse_InstallReason_INSTALL_REASON_OEM;
-}
-
-std::unique_ptr<WebAppInstallInfo>
-PreloadAppDefinition::CreateWebAppInstallInfo() const {
-  DCHECK_EQ(GetPlatform(), AppType::kWeb);
-
-  auto install_info = std::make_unique<WebAppInstallInfo>();
-
-  install_info->title = base::UTF8ToUTF16(GetName());
-
-  if (!app_proto_.has_web_extras()) {
-    LOG(ERROR) << "Failed to convert app " << GetName()
-               << " into WebAppInstallInfo. Missing required web_extras.";
-    return nullptr;
-  }
-
-  auto web_extras = app_proto_.web_extras();
-
-  const std::string& start_url = web_extras.start_url();
-  install_info->start_url = GURL(start_url);
-
-  if (!install_info->start_url.is_valid()) {
-    LOG(ERROR) << "Failed to convert app " << GetName()
-               << " into WebAppInstallInfo. Start URL is invalid: "
-               << start_url;
-    return nullptr;
-  }
-
-  // The server returns a manifest ID which has already been resolved against
-  // the Start URL to make the processed manifest ID. WebAppInstallInfo requires
-  // the opposite, an ID string which can be added to the base URL to create the
-  // processed ID. So we need to 'unresolve' the URL by removing the base URL
-  // from it.
-  const std::string& manifest_id = web_extras.manifest_id();
-  if (!GURL(manifest_id).is_valid()) {
-    LOG(ERROR) << "Failed to convert app " << GetName()
-               << " into WebAppInstallInfo. Manifest ID is invalid: "
-               << manifest_id;
-    return nullptr;
-  }
-
-  std::string manifest_id_base_url =
-      GURL(manifest_id).GetWithEmptyPath().spec();
-  DCHECK(base::StartsWith(manifest_id, manifest_id_base_url));
-
-  if (manifest_id_base_url !=
-      install_info->start_url.GetWithEmptyPath().spec()) {
-    LOG(ERROR) << "Failed to convert app " << GetName()
-               << " into WebAppInstallInfo. Manifest ID (" << manifest_id
-               << ") does not have same origin as Start URL (" << start_url
-               << ")";
-    return nullptr;
-  }
-
-  install_info->manifest_id = manifest_id.substr(manifest_id_base_url.size());
-
-  const std::string& scope = app_proto_.web_extras().scope();
-  install_info->scope = GURL(scope);
-  if (!install_info->scope.is_valid()) {
-    LOG(ERROR) << "Failed to convert app " << GetName()
-               << " into WebAppInstallInfo. Scope is invalid: " << scope;
-    return nullptr;
-  }
-  if (!base::StartsWith(install_info->start_url.path(),
-                        install_info->scope.path(),
-                        base::CompareCase::SENSITIVE)) {
-    LOG(ERROR) << "Failed to convert app " << GetName()
-               << " into WebAppInstallInfo. Start URL ("
-               << install_info->start_url << ") is not within scope ("
-               << install_info->scope.spec();
-    return nullptr;
-  }
-
-  install_info->display_mode =
-      GetDisplayMode(app_proto_.web_extras().display_mode());
-
-  // Always install the app as a web app in a window.
-  install_info->user_display_mode = web_app::UserDisplayMode::kStandalone;
-
-  return install_info;
+         proto::AppProvisioningListAppsResponse::INSTALL_REASON_OEM;
 }
 
 std::string PreloadAppDefinition::GetWebAppManifestId() const {
@@ -148,9 +33,17 @@
 }
 
 std::ostream& operator<<(std::ostream& os, const PreloadAppDefinition& app) {
-  os << "- Name: " << app.GetName();
-  os << "- Platform: " << EnumToString(app.GetPlatform());
-  os << "- OEM: " << app.IsOemApp();
+  os << std::boolalpha;
+  os << "- Name: " << app.GetName() << std::endl;
+  os << "- Platform: " << EnumToString(app.GetPlatform()) << std::endl;
+  os << "- OEM: " << app.IsOemApp() << std::endl;
+
+  if (app.GetPlatform() == AppType::kWeb) {
+    os << "- Web Extras:" << std::endl;
+    os << "  - Manifest ID: " << app.GetWebAppManifestId() << std::endl;
+  }
+
+  os << std::noboolalpha;
   return os;
 }
 
diff --git a/chrome/browser/apps/app_preload_service/preload_app_definition.h b/chrome/browser/apps/app_preload_service/preload_app_definition.h
index f8010c17..2089a26d 100644
--- a/chrome/browser/apps/app_preload_service/preload_app_definition.h
+++ b/chrome/browser/apps/app_preload_service/preload_app_definition.h
@@ -5,18 +5,20 @@
 #ifndef CHROME_BROWSER_APPS_APP_PRELOAD_SERVICE_PRELOAD_APP_DEFINITION_H_
 #define CHROME_BROWSER_APPS_APP_PRELOAD_SERVICE_PRELOAD_APP_DEFINITION_H_
 
+#include <ostream>
+#include <string>
+
 #include "chrome/browser/apps/app_preload_service/proto/app_provisioning.pb.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 
-struct WebAppInstallInfo;
-
 namespace apps {
 
 // A wrapper class around an App Preload Server proto to allow for easier
 // extraction and conversion of information.
 class PreloadAppDefinition {
  public:
-  explicit PreloadAppDefinition(proto::AppProvisioningResponse_App app_proto)
+  explicit PreloadAppDefinition(
+      proto::AppProvisioningListAppsResponse_App app_proto)
       : app_proto_(app_proto) {}
   PreloadAppDefinition(const PreloadAppDefinition&) = default;
   PreloadAppDefinition& operator=(const PreloadAppDefinition&) = default;
@@ -26,11 +28,6 @@
   AppType GetPlatform() const;
   bool IsOemApp() const;
 
-  // Creates a `WebAppInstallInfo` to install this preloaded app as a web app.
-  // Returns `nullptr` if there was an error converting the app. Must only be
-  // called if `GetPlatform()` returns `AppType::kWeb`.
-  std::unique_ptr<WebAppInstallInfo> CreateWebAppInstallInfo() const;
-
   // Returns the Web App manifest ID for the app, which is the canonical
   // identifier for this app, as specified by
   // https://www.w3.org/TR/appmanifest/#id-member. Does not attempt to validate
@@ -39,7 +36,7 @@
   std::string GetWebAppManifestId() const;
 
  private:
-  proto::AppProvisioningResponse_App app_proto_;
+  proto::AppProvisioningListAppsResponse_App app_proto_;
 };
 
 std::ostream& operator<<(std::ostream& os, const PreloadAppDefinition& app);
diff --git a/chrome/browser/apps/app_preload_service/preload_app_definition_unittest.cc b/chrome/browser/apps/app_preload_service/preload_app_definition_unittest.cc
index 038ab9696..f908479 100644
--- a/chrome/browser/apps/app_preload_service/preload_app_definition_unittest.cc
+++ b/chrome/browser/apps/app_preload_service/preload_app_definition_unittest.cc
@@ -17,16 +17,13 @@
 
 // Returns a sample valid web App response proto. Tests should overwrite the
 // individual fields that they need to verify.
-proto::AppProvisioningResponse_App CreateTestWebApp() {
-  proto::AppProvisioningResponse_App app;
+proto::AppProvisioningListAppsResponse_App CreateTestWebApp() {
+  proto::AppProvisioningListAppsResponse_App app;
   app.set_name("Test app");
-  app.set_platform(proto::AppProvisioningResponse::PLATFORM_WEB);
+  app.set_platform(proto::AppProvisioningListAppsResponse::PLATFORM_WEB);
   auto* web_extras = app.mutable_web_extras();
   web_extras->set_manifest_id("https://www.example.com/home");
-  web_extras->set_start_url("https://www.example.com/home");
-  web_extras->set_scope("https://www.example.com/");
-  web_extras->set_display_mode(
-      proto::AppProvisioningResponse::DISPLAY_MODE_STANDALONE);
+  web_extras->set_manifest_url("https://www.example.com/home/manifest.json");
   return app;
 }
 }  // namespace
@@ -37,7 +34,7 @@
 };
 
 TEST_F(PreloadAppDefinitionTest, GetNameWhenNotSet) {
-  proto::AppProvisioningResponse_App app;
+  proto::AppProvisioningListAppsResponse_App app;
 
   auto app_def = PreloadAppDefinition(app);
   ASSERT_EQ(app_def.GetName(), "");
@@ -45,7 +42,7 @@
 
 TEST_F(PreloadAppDefinitionTest, GetName) {
   const std::string test_name = "test_app_name";
-  proto::AppProvisioningResponse_App app;
+  proto::AppProvisioningListAppsResponse_App app;
 
   app.set_name(test_name);
   auto app_def = PreloadAppDefinition(app);
@@ -53,169 +50,50 @@
 }
 
 TEST_F(PreloadAppDefinitionTest, GetPlatformWhenNotSet) {
-  proto::AppProvisioningResponse_App app;
+  proto::AppProvisioningListAppsResponse_App app;
 
   auto app_def = PreloadAppDefinition(app);
   ASSERT_EQ(app_def.GetPlatform(), AppType::kUnknown);
 }
 
 TEST_F(PreloadAppDefinitionTest, GetPlatform) {
-  proto::AppProvisioningResponse_App app;
+  proto::AppProvisioningListAppsResponse_App app;
 
-  app.set_platform(proto::AppProvisioningResponse_Platform::
-                       AppProvisioningResponse_Platform_PLATFORM_WEB);
+  app.set_platform(proto::AppProvisioningListAppsResponse_Platform::
+                       AppProvisioningListAppsResponse_Platform_PLATFORM_WEB);
   auto app_def = PreloadAppDefinition(app);
   ASSERT_EQ(app_def.GetPlatform(), AppType::kWeb);
 }
 
 TEST_F(PreloadAppDefinitionTest, IsOemAppWhenNotSet) {
-  proto::AppProvisioningResponse_App app;
+  proto::AppProvisioningListAppsResponse_App app;
 
   auto app_def = PreloadAppDefinition(app);
   ASSERT_FALSE(app_def.IsOemApp());
 }
 
 TEST_F(PreloadAppDefinitionTest, IsOemApp) {
-  proto::AppProvisioningResponse_App app;
+  proto::AppProvisioningListAppsResponse_App app;
 
   app.set_install_reason(
-      proto::AppProvisioningResponse_InstallReason::
-          AppProvisioningResponse_InstallReason_INSTALL_REASON_OEM);
+      proto::AppProvisioningListAppsResponse_InstallReason::
+          AppProvisioningListAppsResponse_InstallReason_INSTALL_REASON_OEM);
   auto app_def = PreloadAppDefinition(app);
   ASSERT_TRUE(app_def.IsOemApp());
 }
 
 TEST_F(PreloadAppDefinitionTest, IsNotOemApp) {
-  proto::AppProvisioningResponse_App app;
+  proto::AppProvisioningListAppsResponse_App app;
 
   app.set_install_reason(
-      proto::AppProvisioningResponse_InstallReason::
-          AppProvisioningResponse_InstallReason_INSTALL_REASON_DEFAULT);
+      proto::AppProvisioningListAppsResponse_InstallReason::
+          AppProvisioningListAppsResponse_InstallReason_INSTALL_REASON_DEFAULT);
   auto app_def = PreloadAppDefinition(app);
   ASSERT_FALSE(app_def.IsOemApp());
 }
 
-TEST_F(PreloadAppDefinitionTest, CreateWebAppInstallInfo) {
-  proto::AppProvisioningResponse_App app;
-  app.set_name("Test app");
-  app.set_platform(proto::AppProvisioningResponse::PLATFORM_WEB);
-  auto* web_extras = app.mutable_web_extras();
-  web_extras->set_manifest_id("https://www.example.com/app_id");
-  web_extras->set_start_url("https://www.example.com/home");
-  web_extras->set_scope("https://www.example.com/");
-  web_extras->set_display_mode(
-      proto::AppProvisioningResponse::DISPLAY_MODE_STANDALONE);
-
-  PreloadAppDefinition app_def(app);
-  std::unique_ptr<WebAppInstallInfo> install_info =
-      app_def.CreateWebAppInstallInfo();
-
-  ASSERT_TRUE(install_info);
-  EXPECT_EQ(u"Test app", install_info->title);
-  EXPECT_EQ(GURL("https://www.example.com/home"), install_info->start_url);
-  EXPECT_EQ("app_id", install_info->manifest_id);
-  EXPECT_EQ(GURL("https://www.example.com/"), install_info->scope);
-  EXPECT_EQ(blink::mojom::DisplayMode::kStandalone, install_info->display_mode);
-}
-
-TEST_F(PreloadAppDefinitionTest, CreateWebAppInstallInfoNoExtras) {
-  proto::AppProvisioningResponse_App app;
-  app.set_name("Foo bar");
-  app.set_platform(proto::AppProvisioningResponse::PLATFORM_WEB);
-
-  PreloadAppDefinition app_def(app);
-  std::unique_ptr<WebAppInstallInfo> install_info =
-      app_def.CreateWebAppInstallInfo();
-
-  ASSERT_FALSE(install_info);
-}
-
-TEST_F(PreloadAppDefinitionTest, CreateWebAppInstallInfoInvalidStartUrl) {
-  proto::AppProvisioningResponse_App app = CreateTestWebApp();
-  // Start URL has no scheme and so cannot parse as a valid URL.
-  app.mutable_web_extras()->set_start_url("www.foo.com");
-
-  PreloadAppDefinition app_def(app);
-  std::unique_ptr<WebAppInstallInfo> install_info =
-      app_def.CreateWebAppInstallInfo();
-
-  ASSERT_FALSE(install_info);
-}
-
-TEST_F(PreloadAppDefinitionTest, CreateWebAppInstallInfoInvalidScope) {
-  proto::AppProvisioningResponse_App app = CreateTestWebApp();
-  // Scope has no scheme and so cannot parse as a valid URL.
-  app.mutable_web_extras()->set_scope("www.foo.com");
-
-  PreloadAppDefinition app_def(app);
-  std::unique_ptr<WebAppInstallInfo> install_info =
-      app_def.CreateWebAppInstallInfo();
-
-  ASSERT_FALSE(install_info);
-}
-
-TEST_F(PreloadAppDefinitionTest, CreateWebAppInstallInfoStartUrlOutsideScope) {
-  proto::AppProvisioningResponse_App app = CreateTestWebApp();
-  app.mutable_web_extras()->set_start_url("https://www.foo.com/");
-  app.mutable_web_extras()->set_scope("https://www.foo.com/app/");
-
-  PreloadAppDefinition app_def(app);
-  std::unique_ptr<WebAppInstallInfo> install_info =
-      app_def.CreateWebAppInstallInfo();
-
-  ASSERT_FALSE(install_info);
-}
-
-TEST_F(PreloadAppDefinitionTest, CreateWebAppInstallInfoWithNoManifestId) {
-  proto::AppProvisioningResponse_App app = CreateTestWebApp();
-  app.mutable_web_extras()->clear_manifest_id();
-
-  PreloadAppDefinition app_def(app);
-  std::unique_ptr<WebAppInstallInfo> install_info =
-      app_def.CreateWebAppInstallInfo();
-
-  ASSERT_FALSE(install_info);
-}
-
-TEST_F(PreloadAppDefinitionTest, CreateWebAppInstallInfoWithInvalidManifestId) {
-  proto::AppProvisioningResponse_App app = CreateTestWebApp();
-  app.mutable_web_extras()->set_manifest_id("/app_id");
-
-  PreloadAppDefinition app_def(app);
-  std::unique_ptr<WebAppInstallInfo> install_info =
-      app_def.CreateWebAppInstallInfo();
-
-  ASSERT_FALSE(install_info);
-}
-
-TEST_F(PreloadAppDefinitionTest,
-       CreateWebAppInstallInfoWithDifferentOriginManifestId) {
-  proto::AppProvisioningResponse_App app = CreateTestWebApp();
-  app.mutable_web_extras()->set_manifest_id("https://www.foo.com/bar");
-  app.mutable_web_extras()->set_start_url("https://www.bar.com/foo");
-
-  PreloadAppDefinition app_def(app);
-  std::unique_ptr<WebAppInstallInfo> install_info =
-      app_def.CreateWebAppInstallInfo();
-
-  ASSERT_FALSE(install_info);
-}
-
-TEST_F(PreloadAppDefinitionTest,
-       CreateWebAppInstallInfoWithUnknownDisplayMode) {
-  proto::AppProvisioningResponse_App app = CreateTestWebApp();
-  app.mutable_web_extras()->set_display_mode(
-      proto::AppProvisioningResponse::DISPLAY_MODE_UNKNOWN);
-
-  PreloadAppDefinition app_def(app);
-  std::unique_ptr<WebAppInstallInfo> install_info =
-      app_def.CreateWebAppInstallInfo();
-
-  EXPECT_EQ(blink::mojom::DisplayMode::kStandalone, install_info->display_mode);
-}
-
 TEST_F(PreloadAppDefinitionTest, GetWebAppManifestId) {
-  proto::AppProvisioningResponse_App app = CreateTestWebApp();
+  proto::AppProvisioningListAppsResponse_App app = CreateTestWebApp();
   app.mutable_web_extras()->set_manifest_id(
       "https://www.example.com/manifest_id/");
 
@@ -226,8 +104,8 @@
 }
 
 TEST_F(PreloadAppDefinitionTest, GetWebAppManifestIdNotSpecified) {
-  proto::AppProvisioningResponse_App app;
-  app.set_platform(proto::AppProvisioningResponse::PLATFORM_WEB);
+  proto::AppProvisioningListAppsResponse_App app;
+  app.set_platform(proto::AppProvisioningListAppsResponse::PLATFORM_WEB);
 
   PreloadAppDefinition app_def(app);
 
diff --git a/chrome/browser/apps/app_preload_service/proto/app_provisioning.proto b/chrome/browser/apps/app_preload_service/proto/app_provisioning.proto
index ea52ec4..6990035 100644
--- a/chrome/browser/apps/app_preload_service/proto/app_provisioning.proto
+++ b/chrome/browser/apps/app_preload_service/proto/app_provisioning.proto
@@ -16,7 +16,7 @@
 
 // TODO(b/243338003): Review and finalise API. >> THIS IS A DRAFT API ONLY <<
 
-message AppProvisioningRequest {
+message AppProvisioningListAppsRequest {
   enum UserType {
     // Default for deserialization when an unexpected value is encountered.
     // Indicates to the client that the server has a new platform and needs
@@ -70,7 +70,7 @@
   optional UserType user_type = 6;
 }
 
-message AppProvisioningResponse {
+message AppProvisioningListAppsResponse {
   // A list of zero or more apps for APS to install.
   repeated App apps_to_install = 1;
 
@@ -100,25 +100,6 @@
     INSTALL_REASON_OEM = 2;
   }
 
-  enum DisplayMode {
-    // Default for deserialization when an unexpected value is encountered.
-    // Indicates to the client that the server has a new mode and needs the
-    // proto file updated.
-    DISPLAY_MODE_UNKNOWN = 0;
-
-    // Full Screen.
-    DISPLAY_MODE_FULLSCREEN = 1;
-
-    // Standalone.
-    DISPLAY_MODE_STANDALONE = 2;
-
-    // Minimal UI.
-    DISPLAY_MODE_MINIMAL_UI = 3;
-
-    // Browser.
-    DISPLAY_MODE_BROWSER = 4;
-  }
-
   message Icon {
     // Url to query to get the icon. This will always be from the host
     // meltingpot.googleusercontent.com.
@@ -149,14 +130,9 @@
     // |manifest_id| uniquely identifies a web app.
     optional string manifest_id = 1;
 
-    // The navigation scope of the web app.
-    optional string scope = 2;
-
-    // The start url that is loaded into the web app when it opens up.
-    optional string start_url = 3;
-
-    // The display mode of the web app.
-    optional DisplayMode display_mode = 4;
+    // A URL to the web app's manifest in json format. This will always be from
+    // the host meltingpot.googleusercontent.com.
+    optional string manifest_url = 2;
   }
 
   message App {
@@ -183,11 +159,5 @@
       AndroidExtras android_extras = 6;
       WebExtras web_extras = 7;
     }
-
-    // The shelf order for this app.
-    optional uint32 shelf_order = 8;
-
-    // The launcher order for this app.
-    optional uint32 launcher_order = 9;
   }
 }
diff --git a/chrome/browser/apps/app_preload_service/web_app_preload_installer.cc b/chrome/browser/apps/app_preload_service/web_app_preload_installer.cc
index a1b70fe6..59b2736 100644
--- a/chrome/browser/apps/app_preload_service/web_app_preload_installer.cc
+++ b/chrome/browser/apps/app_preload_service/web_app_preload_installer.cc
@@ -50,7 +50,7 @@
 
   auto* provider = web_app::WebAppProvider::GetForWebApps(profile_);
 
-  std::unique_ptr<WebAppInstallInfo> info = app.CreateWebAppInstallInfo();
+  std::unique_ptr<WebAppInstallInfo> info = nullptr;
   if (!info) {
     std::move(callback).Run(/*success=*/false);
     return;
diff --git a/chrome/browser/apps/app_preload_service/web_app_preload_installer_unittest.cc b/chrome/browser/apps/app_preload_service/web_app_preload_installer_unittest.cc
index 655029f..12d4ef7 100644
--- a/chrome/browser/apps/app_preload_service/web_app_preload_installer_unittest.cc
+++ b/chrome/browser/apps/app_preload_service/web_app_preload_installer_unittest.cc
@@ -39,20 +39,19 @@
   TestingProfile profile_;
 };
 
-TEST_F(WebAppPreloadInstallerTest, InstallOemApp) {
+// TODO(b/261632289): temporarily disabled while refactoring is in progress.
+TEST_F(WebAppPreloadInstallerTest, DISABLED_InstallOemApp) {
   WebAppPreloadInstaller installer(profile());
 
-  proto::AppProvisioningResponse_App app;
+  proto::AppProvisioningListAppsResponse_App app;
   app.set_name("Test app");
-  app.set_platform(proto::AppProvisioningResponse::PLATFORM_WEB);
-  app.set_install_reason(proto::AppProvisioningResponse::INSTALL_REASON_OEM);
+  app.set_platform(proto::AppProvisioningListAppsResponse::PLATFORM_WEB);
+  app.set_install_reason(
+      proto::AppProvisioningListAppsResponse::INSTALL_REASON_OEM);
 
   auto* web_extras = app.mutable_web_extras();
   web_extras->set_manifest_id("https://www.example.com/home");
-  web_extras->set_start_url("https://www.example.com/home");
-  web_extras->set_scope("https://www.example.com/");
-  web_extras->set_display_mode(
-      proto::AppProvisioningResponse::DISPLAY_MODE_STANDALONE);
+  web_extras->set_manifest_url("https://www.example.com/home");
 
   base::test::TestFuture<bool> result;
   installer.InstallApp(PreloadAppDefinition(app), result.GetCallback());
@@ -71,10 +70,11 @@
 TEST_F(WebAppPreloadInstallerTest, InstallFailure) {
   WebAppPreloadInstaller installer(profile());
 
-  proto::AppProvisioningResponse_App app;
+  proto::AppProvisioningListAppsResponse_App app;
   app.set_name("Test app");
-  app.set_platform(proto::AppProvisioningResponse::PLATFORM_WEB);
-  app.set_install_reason(proto::AppProvisioningResponse::INSTALL_REASON_OEM);
+  app.set_platform(proto::AppProvisioningListAppsResponse::PLATFORM_WEB);
+  app.set_install_reason(
+      proto::AppProvisioningListAppsResponse::INSTALL_REASON_OEM);
 
   // Installation should fail due to missing web_extras field.
   base::test::TestFuture<bool> result;
@@ -82,20 +82,19 @@
   ASSERT_FALSE(result.Get());
 }
 
-TEST_F(WebAppPreloadInstallerTest, InstallWithManifestId) {
+// TODO(b/261632289): temporarily disabled while refactoring is in progress.
+TEST_F(WebAppPreloadInstallerTest, DISABLED_InstallWithManifestId) {
   WebAppPreloadInstaller installer(profile());
 
-  proto::AppProvisioningResponse_App app;
+  proto::AppProvisioningListAppsResponse_App app;
   app.set_name("Test app");
-  app.set_platform(proto::AppProvisioningResponse::PLATFORM_WEB);
-  app.set_install_reason(proto::AppProvisioningResponse::INSTALL_REASON_OEM);
+  app.set_platform(proto::AppProvisioningListAppsResponse::PLATFORM_WEB);
+  app.set_install_reason(
+      proto::AppProvisioningListAppsResponse::INSTALL_REASON_OEM);
 
   auto* web_extras = app.mutable_web_extras();
   web_extras->set_manifest_id("https://www.example.com/app");
-  web_extras->set_start_url("https://www.example.com/home");
-  web_extras->set_scope("https://www.example.com/");
-  web_extras->set_display_mode(
-      proto::AppProvisioningResponse::DISPLAY_MODE_STANDALONE);
+  web_extras->set_manifest_url("https://www.example.com/manifest.json");
 
   base::test::TestFuture<bool> result;
   installer.InstallApp(PreloadAppDefinition(app), result.GetCallback());
@@ -110,8 +109,11 @@
 
 // Reinstalling an existing user-installed app should not overwrite manifest
 // data, but will add the OEM install reason.
-TEST_F(WebAppPreloadInstallerTest, InstallOverUserApp) {
+// TODO(b/261632289): temporarily disabled while refactoring is in progress.
+TEST_F(WebAppPreloadInstallerTest, DISABLED_InstallOverUserApp) {
   constexpr char kStartUrl[] = "https://www.example.com/";
+  constexpr char kManifestUrl[] =
+      "https://meltingpot.googleusercontent.com/manifest.json";
   constexpr char kUserAppName[] = "User Installed App";
 
   WebAppPreloadInstaller installer(profile());
@@ -119,17 +121,15 @@
   auto app_id = web_app::test::InstallDummyWebApp(profile(), kUserAppName,
                                                   GURL(kStartUrl));
 
-  proto::AppProvisioningResponse_App app;
+  proto::AppProvisioningListAppsResponse_App app;
   app.set_name("OEM Installed app");
-  app.set_platform(proto::AppProvisioningResponse::PLATFORM_WEB);
-  app.set_install_reason(proto::AppProvisioningResponse::INSTALL_REASON_OEM);
+  app.set_platform(proto::AppProvisioningListAppsResponse::PLATFORM_WEB);
+  app.set_install_reason(
+      proto::AppProvisioningListAppsResponse::INSTALL_REASON_OEM);
 
   auto* web_extras = app.mutable_web_extras();
   web_extras->set_manifest_id(kStartUrl);
-  web_extras->set_start_url(kStartUrl);
-  web_extras->set_scope(kStartUrl);
-  web_extras->set_display_mode(
-      proto::AppProvisioningResponse::DISPLAY_MODE_STANDALONE);
+  web_extras->set_manifest_url(kManifestUrl);
 
   base::test::TestFuture<bool> result;
   installer.InstallApp(PreloadAppDefinition(app), result.GetCallback());
@@ -146,8 +146,8 @@
 TEST_F(WebAppPreloadInstallerTest, GetAppId) {
   WebAppPreloadInstaller installer(profile());
 
-  proto::AppProvisioningResponse_App app;
-  app.set_platform(proto::AppProvisioningResponse::PLATFORM_WEB);
+  proto::AppProvisioningListAppsResponse_App app;
+  app.set_platform(proto::AppProvisioningListAppsResponse::PLATFORM_WEB);
   app.mutable_web_extras()->set_manifest_id("https://cursive.apps.chrome/");
 
   ASSERT_EQ(installer.GetAppId(PreloadAppDefinition(app)),
diff --git a/chrome/browser/apps/app_service/app_launch_params.h b/chrome/browser/apps/app_service/app_launch_params.h
index 9223b16..679fe11 100644
--- a/chrome/browser/apps/app_service/app_launch_params.h
+++ b/chrome/browser/apps/app_service/app_launch_params.h
@@ -12,7 +12,6 @@
 #include "base/files/file_path.h"
 #include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "components/services/app_service/public/cpp/intent.h"
-#include "components/services/app_service/public/mojom/types.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/display/types/display_constants.h"
diff --git a/chrome/browser/apps/app_service/app_notifications.cc b/chrome/browser/apps/app_service/app_notifications.cc
index dc9eb0a5..4dbba17 100644
--- a/chrome/browser/apps/app_service/app_notifications.cc
+++ b/chrome/browser/apps/app_service/app_notifications.cc
@@ -69,16 +69,4 @@
   return app;
 }
 
-apps::mojom::AppPtr AppNotifications::GetAppWithHasBadgeStatus(
-    apps::mojom::AppType app_type,
-    const std::string& app_id) {
-  apps::mojom::AppPtr app = apps::mojom::App::New();
-  app->app_type = app_type;
-  app->app_id = app_id;
-  app->has_badge = (HasNotification(app_id))
-                       ? apps::mojom::OptionalBool::kTrue
-                       : apps::mojom::OptionalBool::kFalse;
-  return app;
-}
-
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/app_notifications.h b/chrome/browser/apps/app_service/app_notifications.h
index ad31e03..00c2b26 100644
--- a/chrome/browser/apps/app_service/app_notifications.h
+++ b/chrome/browser/apps/app_service/app_notifications.h
@@ -11,7 +11,6 @@
 #include <utility>
 
 #include "components/services/app_service/public/cpp/app_types.h"
-#include "components/services/app_service/public/mojom/types.mojom.h"
 
 namespace apps {
 
@@ -47,9 +46,6 @@
   AppPtr CreateAppWithHasBadgeStatus(AppType app_type,
                                      const std::string& app_id);
 
-  apps::mojom::AppPtr GetAppWithHasBadgeStatus(apps::mojom::AppType app_type,
-                                               const std::string& app_id);
-
  private:
   // Maps one app id to a set of all matching notification ids.
   std::map<std::string, std::set<std::string>> app_id_to_notification_ids_;
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
index b0beb946..e3a7fa1 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
@@ -44,7 +44,6 @@
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/cpp/instance_registry.h"
-#include "components/services/app_service/public/mojom/types.mojom.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync/test/test_sync_service.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
diff --git a/chrome/browser/apps/app_service/paused_apps.cc b/chrome/browser/apps/app_service/paused_apps.cc
index 1f0eac83..8d66d82 100644
--- a/chrome/browser/apps/app_service/paused_apps.cc
+++ b/chrome/browser/apps/app_service/paused_apps.cc
@@ -12,19 +12,6 @@
 
 PausedApps::~PausedApps() = default;
 
-// static
-apps::mojom::AppPtr PausedApps::GetAppWithPauseStatus(
-    apps::mojom::AppType app_type,
-    const std::string& app_id,
-    bool paused) {
-  apps::mojom::AppPtr app = apps::mojom::App::New();
-  app->app_type = app_type;
-  app->app_id = app_id;
-  app->paused = (paused) ? apps::mojom::OptionalBool::kTrue
-                         : apps::mojom::OptionalBool::kFalse;
-  return app;
-}
-
 AppPtr PausedApps::CreateAppWithPauseStatus(AppType app_type,
                                             const std::string& app_id,
                                             bool paused) {
diff --git a/chrome/browser/apps/app_service/paused_apps.h b/chrome/browser/apps/app_service/paused_apps.h
index d5003820..f3ad609f 100644
--- a/chrome/browser/apps/app_service/paused_apps.h
+++ b/chrome/browser/apps/app_service/paused_apps.h
@@ -10,7 +10,6 @@
 #include <utility>
 
 #include "components/services/app_service/public/cpp/app_types.h"
-#include "components/services/app_service/public/mojom/types.mojom.h"
 
 namespace apps {
 
@@ -24,11 +23,6 @@
   PausedApps(const PausedApps&) = delete;
   PausedApps& operator=(const PausedApps&) = delete;
 
-  static apps::mojom::AppPtr GetAppWithPauseStatus(
-      apps::mojom::AppType app_type,
-      const std::string& app_id,
-      bool paused);
-
   AppPtr CreateAppWithPauseStatus(AppType app_type,
                                   const std::string& app_id,
                                   bool paused);
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps.cc b/chrome/browser/apps/app_service/publishers/arc_apps.cc
index 2665db8..a2e85b4a 100644
--- a/chrome/browser/apps/app_service/publishers/arc_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/arc_apps.cc
@@ -1095,10 +1095,6 @@
     SetIconEffect(app_id);
   }
 
-  constexpr bool kPaused = true;
-  PublisherBase::Publish(paused_apps_.GetAppWithPauseStatus(
-                             apps::mojom::AppType::kArc, app_id, kPaused),
-                         subscribers_);
   AppPublisher::Publish(paused_apps_.CreateAppWithPauseStatus(
       AppType::kArc, app_id, /*paused=*/true));
   CloseTasks(app_id);
@@ -1109,10 +1105,6 @@
     SetIconEffect(app_id);
   }
 
-  constexpr bool kPaused = false;
-  PublisherBase::Publish(paused_apps_.GetAppWithPauseStatus(
-                             apps::mojom::AppType::kArc, app_id, kPaused),
-                         subscribers_);
   AppPublisher::Publish(paused_apps_.CreateAppWithPauseStatus(
       AppType::kArc, app_id, /*paused=*/false));
 }
@@ -1418,9 +1410,6 @@
   }
 
   app_notifications_.AddNotification(app_id, notification_id);
-  PublisherBase::Publish(app_notifications_.GetAppWithHasBadgeStatus(
-                             apps::mojom::AppType::kArc, app_id),
-                         subscribers_);
   AppPublisher::Publish(
       app_notifications_.CreateAppWithHasBadgeStatus(AppType::kArc, app_id));
 }
@@ -1435,9 +1424,6 @@
   app_notifications_.RemoveNotification(notification_id);
 
   for (const auto& app_id : app_ids) {
-    PublisherBase::Publish(app_notifications_.GetAppWithHasBadgeStatus(
-                               apps::mojom::AppType::kArc, app_id),
-                           subscribers_);
     AppPublisher::Publish(
         app_notifications_.CreateAppWithHasBadgeStatus(AppType::kArc, app_id));
   }
diff --git a/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc b/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
index 43b36a5d..cadffde 100644
--- a/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
+++ b/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
@@ -350,11 +350,6 @@
   if (paused_apps_.MaybeAddApp(app_id)) {
     SetIconEffect(app_id);
   }
-
-  constexpr bool kPaused = true;
-  PublisherBase::Publish(
-      paused_apps_.GetAppWithPauseStatus(mojom_app_type(), app_id, kPaused),
-      subscribers());
   AppPublisher::Publish(paused_apps_.CreateAppWithPauseStatus(
       app_type(), app_id, /*paused=*/true));
 
@@ -373,10 +368,6 @@
     SetIconEffect(app_id);
   }
 
-  constexpr bool kPaused = false;
-  PublisherBase::Publish(
-      paused_apps_.GetAppWithPauseStatus(mojom_app_type(), app_id, kPaused),
-      subscribers());
   AppPublisher::Publish(paused_apps_.CreateAppWithPauseStatus(
       app_type(), app_id, /*paused=*/false));
 
@@ -586,9 +577,6 @@
   app_notifications_.RemoveNotification(notification_id);
 
   for (const auto& app_id : app_ids) {
-    PublisherBase::Publish(
-        app_notifications_.GetAppWithHasBadgeStatus(mojom_app_type(), app_id),
-        subscribers());
     AppPublisher::Publish(
         app_notifications_.CreateAppWithHasBadgeStatus(app_type(), app_id));
   }
@@ -608,9 +596,6 @@
   }
 
   app_notifications_.AddNotification(app_id, notification_id);
-  PublisherBase::Publish(
-      app_notifications_.GetAppWithHasBadgeStatus(mojom_app_type(), app_id),
-      subscribers());
   AppPublisher::Publish(
       app_notifications_.CreateAppWithHasBadgeStatus(app_type(), app_id));
   return true;
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index 5de5844..27a44b211 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -3296,6 +3296,7 @@
     "//chromeos/ash/components/scanning",
     "//chromeos/ash/components/settings",
     "//chromeos/ash/components/smbfs",
+    "//chromeos/ash/components/system",
     "//chromeos/ash/components/tether",
     "//chromeos/ash/components/timezone",
     "//chromeos/ash/components/trash_service/public/cpp",
@@ -3337,7 +3338,6 @@
     "//chromeos/services/network_health",
     "//chromeos/services/network_health/public/cpp",
     "//chromeos/services/network_health/public/mojom",
-    "//chromeos/system",
     "//chromeos/ui/base",
     "//components/account_id",
     "//components/account_manager_core",
@@ -5254,6 +5254,7 @@
     "//chromeos/ash/components/settings",
     "//chromeos/ash/components/smbfs",
     "//chromeos/ash/components/sync_wifi:test_support",
+    "//chromeos/ash/components/system",
     "//chromeos/ash/components/tether",
     "//chromeos/ash/components/tether:test_support",
     "//chromeos/ash/components/tpm",
@@ -5304,7 +5305,6 @@
     "//chromeos/services/network_config/public/mojom",
     "//chromeos/services/network_health/public/mojom",
     "//chromeos/strings",
-    "//chromeos/system",
     "//chromeos/ui/base",
     "//chromeos/ui/frame:test_support",
     "//chromeos/ui/vector_icons",
diff --git a/chrome/browser/ash/app_list/search/ranking/removed_results_ranker.cc b/chrome/browser/ash/app_list/search/ranking/removed_results_ranker.cc
index 7ef8e46..b7c3e33 100644
--- a/chrome/browser/ash/app_list/search/ranking/removed_results_ranker.cc
+++ b/chrome/browser/ash/app_list/search/ranking/removed_results_ranker.cc
@@ -37,12 +37,16 @@
   const auto it = results.find(provider);
   DCHECK(it != results.end());
 
-  // If `proto_` is not initialized, filter all results; otherwise, filter any
-  // results whose IDs have been recorded as for removal.
+  // If `proto_` is not initialized, filter all results except for recent apps.
+  // Otherwise, filter any results whose IDs have been recorded as for removal.
   const bool proto_initialized = initialized();
   for (const auto& result : it->second) {
-    if (!proto_initialized || (*proto_)->removed_ids().contains(result->id())) {
-      result->scoring().filter = true;
+    if (!proto_initialized) {
+      result->scoring().filter =
+          result->display_type() != DisplayType::kRecentApps;
+    } else {
+      result->scoring().filter =
+          (*proto_)->removed_ids().contains(result->id());
     }
   }
 }
diff --git a/chrome/browser/ash/app_list/search/ranking/removed_results_ranker_unittest.cc b/chrome/browser/ash/app_list/search/ranking/removed_results_ranker_unittest.cc
index c171fe7..7fec639 100644
--- a/chrome/browser/ash/app_list/search/ranking/removed_results_ranker_unittest.cc
+++ b/chrome/browser/ash/app_list/search/ranking/removed_results_ranker_unittest.cc
@@ -32,11 +32,11 @@
 }
 
 Results MakeResults(std::vector<std::string> ids) {
-  Results res;
+  Results results;
   for (const std::string& id : ids) {
-    res.push_back(MakeResult(id));
+    results.push_back(MakeResult(id));
   }
-  return res;
+  return results;
 }
 
 }  // namespace
@@ -55,13 +55,18 @@
           base::BindRepeating(
               &MockFileSuggestKeyedService::BuildMockFileSuggestKeyedService,
               temp_dir_.GetPath().Append("proto"))}});
-    WaitUntilFileSuggestServiceReady(
-        FileSuggestKeyedServiceFactory::GetInstance()->GetService(profile_));
     ranker_ = std::make_unique<RemovedResultsRanker>(profile_);
   }
 
   void Wait() { task_environment_.RunUntilIdle(); }
 
+  // Initialize the file suggest service, which holds the underlying proto for
+  // all removed results.
+  void InitFileService() {
+    WaitUntilFileSuggestServiceReady(
+        FileSuggestKeyedServiceFactory::GetInstance()->GetService(profile_));
+  }
+
   content::BrowserTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   std::unique_ptr<TestingProfileManager> testing_profile_manager_;
@@ -71,6 +76,8 @@
 };
 
 TEST_F(RemovedResultsRankerTest, UpdateResultRanks) {
+  InitFileService();
+
   // Request to remove some results.
   ranker_->Remove(MakeResult("A").get());
   ranker_->Remove(MakeResult("C").get());
@@ -102,7 +109,7 @@
 }
 
 TEST_F(RemovedResultsRankerTest, RankEmptyResults) {
-  Wait();
+  InitFileService();
 
   ResultsMap results_map;
   results_map[ResultType::kInstalledApp] =
@@ -113,7 +120,7 @@
 }
 
 TEST_F(RemovedResultsRankerTest, RankDuplicateResults) {
-  Wait();
+  InitFileService();
 
   // Request to remove some results.
   ranker_->Remove(MakeResult("A").get());
@@ -140,7 +147,7 @@
 // Verifies that the ranker removes a result through the file suggest keyed
 // service if the result is a file suggestion.
 TEST_F(RemovedResultsRankerTest, RemoveFileSuggestions) {
-  Wait();
+  InitFileService();
 
   const base::FilePath drive_file_result_path("file_A");
   FileResult drive_file_result(
@@ -175,4 +182,23 @@
   ranker_->Remove(&local_file_result);
 }
 
+TEST_F(RemovedResultsRankerTest, RemoveBeforeInit) {
+  // Don't fully initialize the file service for this test.
+
+  ResultsMap results_map;
+  auto apps = MakeResults({"A", "B"});
+  apps[0]->SetDisplayType(DisplayType::kRecentApps);
+  results_map[ResultType::kInstalledApp] = std::move(apps);
+  results_map[ResultType::kOmnibox] = MakeResults({"C", "D"});
+
+  ranker_->UpdateResultRanks(results_map, ResultType::kInstalledApp);
+  ranker_->UpdateResultRanks(results_map, ResultType::kOmnibox);
+
+  // All results should be filtered out except for the recent app.
+  EXPECT_FALSE(results_map[ResultType::kInstalledApp][0]->scoring().filter);
+  EXPECT_TRUE(results_map[ResultType::kInstalledApp][1]->scoring().filter);
+  EXPECT_TRUE(results_map[ResultType::kOmnibox][0]->scoring().filter);
+  EXPECT_TRUE(results_map[ResultType::kOmnibox][1]->scoring().filter);
+}
+
 }  // namespace app_list::test
diff --git a/chrome/browser/ash/app_restore/arc_app_queue_restore_handler.cc b/chrome/browser/ash/app_restore/arc_app_queue_restore_handler.cc
index 85fc5ef..af24396 100644
--- a/chrome/browser/ash/app_restore/arc_app_queue_restore_handler.cc
+++ b/chrome/browser/ash/app_restore/arc_app_queue_restore_handler.cc
@@ -35,9 +35,9 @@
 #include "chrome/browser/ui/ash/shelf/arc_shelf_spinner_item_controller.h"
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
+#include "chromeos/ash/components/system/scheduler_configuration_manager_base.h"
 #include "chromeos/ash/services/cros_healthd/public/cpp/service_connection.h"
 #include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_probe.mojom.h"
-#include "chromeos/system/scheduler_configuration_manager_base.h"
 #include "components/app_restore/app_launch_info.h"
 #include "components/app_restore/app_restore_utils.h"
 #include "components/app_restore/features.h"
diff --git a/chrome/browser/ash/arc/adbd/arc_adbd_monitor_bridge.cc b/chrome/browser/ash/arc/adbd/arc_adbd_monitor_bridge.cc
index 91a29a7..379ed348 100644
--- a/chrome/browser/ash/arc/adbd/arc_adbd_monitor_bridge.cc
+++ b/chrome/browser/ash/arc/adbd/arc_adbd_monitor_bridge.cc
@@ -22,7 +22,7 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/thread_restrictions.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 // Enable VLOG level 1.
diff --git a/chrome/browser/ash/arc/policy/arc_policy_bridge_unittest.cc b/chrome/browser/ash/arc/policy/arc_policy_bridge_unittest.cc
index 12305f8..0493cfe 100644
--- a/chrome/browser/ash/arc/policy/arc_policy_bridge_unittest.cc
+++ b/chrome/browser/ash/arc/policy/arc_policy_bridge_unittest.cc
@@ -39,7 +39,7 @@
 #include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/ash/components/dbus/concierge/concierge_client.h"
 #include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/policy/core/common/mock_policy_service.h"
diff --git a/chrome/browser/ash/arc/policy/managed_configuration_variables.cc b/chrome/browser/ash/arc/policy/managed_configuration_variables.cc
index 539d5821..b54e5d2 100644
--- a/chrome/browser/ash/arc/policy/managed_configuration_variables.cc
+++ b/chrome/browser/ash/arc/policy/managed_configuration_variables.cc
@@ -23,7 +23,7 @@
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "third_party/re2/src/re2/re2.h"
 #include "third_party/re2/src/re2/stringpiece.h"
diff --git a/chrome/browser/ash/arc/policy/managed_configuration_variables_unittest.cc b/chrome/browser/ash/arc/policy/managed_configuration_variables_unittest.cc
index df644c4..12839ae 100644
--- a/chrome/browser/ash/arc/policy/managed_configuration_variables_unittest.cc
+++ b/chrome/browser/ash/arc/policy/managed_configuration_variables_unittest.cc
@@ -19,8 +19,8 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/account_id/account_id.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/ash/arc/session/arc_session_manager.cc b/chrome/browser/ash/arc/session/arc_session_manager.cc
index 5385f13..caa8261 100644
--- a/chrome/browser/ash/arc/session/arc_session_manager.cc
+++ b/chrome/browser/ash/arc/session/arc_session_manager.cc
@@ -64,7 +64,7 @@
 #include "chrome/browser/ui/webui/ash/diagnostics_dialog.h"
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/account_id/account_id.h"
 #include "components/exo/wm_helper_chromeos.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc b/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc
index 71414bd..8154f3c 100644
--- a/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc
+++ b/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc
@@ -101,8 +101,8 @@
         arc_service_manager_.arc_bridge_service()->wallpaper());
 
     // Salt
-    chromeos::SystemSaltGetter::Initialize();
-    chromeos::SystemSaltGetter::Get()->SetRawSaltForTesting({0x01, 0x02, 0x03});
+    ash::SystemSaltGetter::Initialize();
+    ash::SystemSaltGetter::Get()->SetRawSaltForTesting({0x01, 0x02, 0x03});
   }
 
   void TearDown() override {
@@ -112,7 +112,7 @@
 
     wallpaper_controller_client_.reset();
     TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
-    chromeos::SystemSaltGetter::Shutdown();
+    ash::SystemSaltGetter::Shutdown();
   }
 
  protected:
diff --git a/chrome/browser/ash/audio/audio_survey_handler.cc b/chrome/browser/ash/audio/audio_survey_handler.cc
index b57dc15..4c7c855f 100644
--- a/chrome/browser/ash/audio/audio_survey_handler.cc
+++ b/chrome/browser/ash/audio/audio_survey_handler.cc
@@ -16,11 +16,11 @@
     return;
   }
 
-  chromeos::CrasAudioHandler::Get()->AddAudioObserver(this);
+  CrasAudioHandler::Get()->AddAudioObserver(this);
 }
 AudioSurveyHandler::~AudioSurveyHandler() {
-  if (chromeos::CrasAudioHandler::Get()) {
-    chromeos::CrasAudioHandler::Get()->RemoveAudioObserver(this);
+  if (CrasAudioHandler::Get()) {
+    CrasAudioHandler::Get()->RemoveAudioObserver(this);
   }
 }
 
diff --git a/chrome/browser/ash/borealis/borealis_features_unittest.cc b/chrome/browser/ash/borealis/borealis_features_unittest.cc
index 7e7159b..d3c8af0 100644
--- a/chrome/browser/ash/borealis/borealis_features_unittest.cc
+++ b/chrome/browser/ash/borealis/borealis_features_unittest.cc
@@ -21,8 +21,8 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/prefs/pref_service.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/user_manager/scoped_user_manager.h"
diff --git a/chrome/browser/ash/borealis/borealis_features_util.cc b/chrome/browser/ash/borealis/borealis_features_util.cc
index 9d6808b..facd10e 100644
--- a/chrome/browser/ash/borealis/borealis_features_util.cc
+++ b/chrome/browser/ash/borealis/borealis_features_util.cc
@@ -13,7 +13,7 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "crypto/sha2.h"
 #include "third_party/re2/src/re2/re2.h"
 
diff --git a/chrome/browser/ash/child_accounts/child_status_reporting_service.cc b/chrome/browser/ash/child_accounts/child_status_reporting_service.cc
index aa41367..034bf94 100644
--- a/chrome/browser/ash/child_accounts/child_status_reporting_service.cc
+++ b/chrome/browser/ash/child_accounts/child_status_reporting_service.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/ash/policy/uploading/status_uploader.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
 #include "components/prefs/pref_change_registrar.h"
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
index c204134..46afe83 100644
--- a/chrome/browser/ash/chrome_browser_main_parts_ash.cc
+++ b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
@@ -217,6 +217,7 @@
 #include "chromeos/ash/components/peripheral_notification/peripheral_notification_manager.h"
 #include "chromeos/ash/components/power/dark_resume_controller.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/ash/components/tpm/tpm_token_loader.h"
 #include "chromeos/ash/services/cros_healthd/private/cpp/data_collector.h"
 #include "chromeos/ash/services/cros_healthd/public/cpp/service_connection.h"
@@ -225,7 +226,6 @@
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/power/power_policy_controller.h"
 #include "chromeos/services/machine_learning/public/cpp/service_connection.h"
-#include "chromeos/system/statistics_provider.h"
 #include "chromeos/version/version_loader.h"
 #include "components/account_id/account_id.h"
 #include "components/device_event_log/device_event_log.h"
diff --git a/chrome/browser/ash/chromebox_for_meetings/device_info/device_info_service.cc b/chrome/browser/ash/chromebox_for_meetings/device_info/device_info_service.cc
index 04cd9872..fd23b41a 100644
--- a/chrome/browser/ash/chromebox_for_meetings/device_info/device_info_service.cc
+++ b/chrome/browser/ash/chromebox_for_meetings/device_info/device_info_service.cc
@@ -15,7 +15,7 @@
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/common/channel_info.h"
 #include "chromeos/ash/components/dbus/chromebox_for_meetings/cfm_hotline_client.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/version_info/version_info.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/chrome/browser/ash/chromebox_for_meetings/device_info/device_info_service_unittest.cc b/chrome/browser/ash/chromebox_for_meetings/device_info/device_info_service_unittest.cc
index 73041ba..bb7fce3b 100644
--- a/chrome/browser/ash/chromebox_for_meetings/device_info/device_info_service_unittest.cc
+++ b/chrome/browser/ash/chromebox_for_meetings/device_info/device_info_service_unittest.cc
@@ -20,12 +20,12 @@
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chromeos/ash/components/dbus/chromebox_for_meetings/fake_cfm_hotline_client.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/ash/services/chromebox_for_meetings/public/cpp/fake_service_connection.h"
 #include "chromeos/ash/services/chromebox_for_meetings/public/cpp/fake_service_context.h"
 #include "chromeos/ash/services/chromebox_for_meetings/public/cpp/service_connection.h"
 #include "chromeos/ash/services/chromebox_for_meetings/public/mojom/cfm_service_manager.mojom.h"
 #include "chromeos/ash/services/chromebox_for_meetings/public/mojom/meet_devices_info.mojom.h"
-#include "chromeos/system/fake_statistics_provider.h"
 #include "components/ownership/mock_owner_key_util.h"
 #include "content/public/test/test_utils.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn
index e16647c..36e2b839 100644
--- a/chrome/browser/ash/crosapi/BUILD.gn
+++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -296,6 +296,7 @@
     "//chromeos/ash/components/login/login_state",
     "//chromeos/ash/components/network",
     "//chromeos/ash/components/settings",
+    "//chromeos/ash/components/system",
     "//chromeos/ash/components/tpm",
     "//chromeos/components/cdm_factory_daemon:cdm_factory_daemon_browser",
     "//chromeos/components/certificate_provider:certificate_provider",
@@ -311,7 +312,6 @@
     "//chromeos/services/network_config/public/cpp",
     "//chromeos/startup",
     "//chromeos/startup:constants",
-    "//chromeos/system",
     "//chromeos/ui/wm:wm",
     "//chromeos/version",
     "//components/arc",
@@ -468,11 +468,11 @@
     "//chromeos/ash/components/dbus/shill",
     "//chromeos/ash/components/dbus/upstart",
     "//chromeos/ash/components/login/login_state",
+    "//chromeos/ash/components/system",
     "//chromeos/crosapi/cpp:cpp",
     "//chromeos/crosapi/cpp:crosapi_constants",
     "//chromeos/printing",
     "//chromeos/startup:constants",
-    "//chromeos/system",
     "//components/component_updater:test_support",
     "//components/session_manager/core",
     "//printing/backend",
diff --git a/chrome/browser/ash/crosapi/browser_loader.cc b/chrome/browser/ash/crosapi/browser_loader.cc
index 839ccdb..2330ff4 100644
--- a/chrome/browser/ash/crosapi/browser_loader.cc
+++ b/chrome/browser/ash/crosapi/browser_loader.cc
@@ -486,12 +486,12 @@
   // assumes that system salt is available. This isn't always true when chrome
   // restarts to apply non-owner flags. It's hard to make MetadataTable async.
   // Ensure salt is available before unloading. https://crbug.com/1122674
-  chromeos::SystemSaltGetter::Get()->GetSystemSalt(base::BindOnce(
+  ash::SystemSaltGetter::Get()->GetSystemSalt(base::BindOnce(
       &BrowserLoader::UnloadAfterCleanUp, weak_factory_.GetWeakPtr()));
 }
 
 void BrowserLoader::UnloadAfterCleanUp(const std::string& ignored_salt) {
-  CHECK(chromeos::SystemSaltGetter::Get()->GetRawSalt());
+  CHECK(ash::SystemSaltGetter::Get()->GetRawSalt());
   component_manager_->Unload(GetLacrosComponentName());
 }
 
diff --git a/chrome/browser/ash/crosapi/browser_util_unittest.cc b/chrome/browser/ash/crosapi/browser_util_unittest.cc
index 0cd2a067..521d786 100644
--- a/chrome/browser/ash/crosapi/browser_util_unittest.cc
+++ b/chrome/browser/ash/crosapi/browser_util_unittest.cc
@@ -23,8 +23,8 @@
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/crosapi/mojom/crosapi.mojom.h"
-#include "chromeos/system/fake_statistics_provider.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/policy_constants.h"
 #include "components/user_manager/scoped_user_manager.h"
diff --git a/chrome/browser/ash/crosapi/content_protection_ash.cc b/chrome/browser/ash/crosapi/content_protection_ash.cc
index 35cb42ab..39ac2cd 100644
--- a/chrome/browser/ash/crosapi/content_protection_ash.cc
+++ b/chrome/browser/ash/crosapi/content_protection_ash.cc
@@ -73,7 +73,7 @@
 }
 
 void ContentProtectionAsh::GetSystemSalt(GetSystemSaltCallback callback) {
-  chromeos::SystemSaltGetter::Get()->GetSystemSalt(std::move(callback));
+  ash::SystemSaltGetter::Get()->GetSystemSalt(std::move(callback));
 }
 
 void ContentProtectionAsh::ChallengePlatform(
diff --git a/chrome/browser/ash/crosapi/crosapi_util.cc b/chrome/browser/ash/crosapi/crosapi_util.cc
index fb5f3d9..9ef2f4a9 100644
--- a/chrome/browser/ash/crosapi/crosapi_util.cc
+++ b/chrome/browser/ash/crosapi/crosapi_util.cc
@@ -36,6 +36,7 @@
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/cros_settings_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/components/cdm_factory_daemon/mojom/browser_cdm_factory.mojom.h"
 #include "chromeos/components/remote_apps/mojom/remote_apps.mojom.h"
 #include "chromeos/components/sensors/mojom/cros_sensor_service.mojom.h"
@@ -125,7 +126,6 @@
 #include "chromeos/crosapi/mojom/web_page_info.mojom.h"
 #include "chromeos/services/machine_learning/public/mojom/machine_learning_service.mojom.h"
 #include "chromeos/startup/startup.h"
-#include "chromeos/system/statistics_provider.h"
 #include "chromeos/ui/wm/features.h"
 #include "chromeos/version/version_loader.h"
 #include "components/account_manager_core/account_manager_util.h"
diff --git a/chrome/browser/ash/crosapi/echo_private_ash.cc b/chrome/browser/ash/crosapi/echo_private_ash.cc
index 4d79362..1287869 100644
--- a/chrome/browser/ash/crosapi/echo_private_ash.cc
+++ b/chrome/browser/ash/crosapi/echo_private_ash.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/common/url_constants.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/base/window_open_disposition.h"
 
diff --git a/chrome/browser/ash/crosapi/extension_info_private_ash.cc b/chrome/browser/ash/crosapi/extension_info_private_ash.cc
index 208d018b..212f331b 100644
--- a/chrome/browser/ash/crosapi/extension_info_private_ash.cc
+++ b/chrome/browser/ash/crosapi/extension_info_private_ash.cc
@@ -30,8 +30,8 @@
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/constants/devicetype.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/metrics/metrics_service.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc b/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc
index af0e105..ab1e0ce 100644
--- a/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc
+++ b/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc
@@ -36,10 +36,10 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/ash/components/login/login_state/login_state.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/crosapi/cpp/crosapi_constants.h"
 #include "chromeos/crosapi/mojom/crosapi.mojom.h"
 #include "chromeos/startup/startup_switches.h"
-#include "chromeos/system/fake_statistics_provider.h"
 #include "components/account_id/account_id.h"
 #include "components/user_manager/fake_user_manager.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/ash/customization/customization_document.cc b/chrome/browser/ash/customization/customization_document.cc
index 0b077ee..34611e2 100644
--- a/chrome/browser/ash/customization/customization_document.cc
+++ b/chrome/browser/ash/customization/customization_document.cc
@@ -40,7 +40,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/customization/customization_document_browsertest.cc b/chrome/browser/ash/customization/customization_document_browsertest.cc
index ca8d6b8..9957987 100644
--- a/chrome/browser/ash/customization/customization_document_browsertest.cc
+++ b/chrome/browser/ash/customization/customization_document_browsertest.cc
@@ -15,8 +15,8 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/webui/ash/login/l10n_util.h"
 #include "chrome/test/base/in_process_browser_test.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/ash/customization/customization_document_unittest.cc b/chrome/browser/ash/customization/customization_document_unittest.cc
index c608cee..051ff57 100644
--- a/chrome/browser/ash/customization/customization_document_unittest.cc
+++ b/chrome/browser/ash/customization/customization_document_unittest.cc
@@ -22,7 +22,7 @@
 #include "chromeos/ash/components/network/network_handler_test_helper.h"
 #include "chromeos/ash/components/network/network_state.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/sync_preferences/pref_service_mock_factory.h"
diff --git a/chrome/browser/ash/dbus/ash_dbus_helper.cc b/chrome/browser/ash/dbus/ash_dbus_helper.cc
index c60fac7..c94a24f6d 100644
--- a/chrome/browser/ash/dbus/ash_dbus_helper.cc
+++ b/chrome/browser/ash/dbus/ash_dbus_helper.cc
@@ -124,7 +124,7 @@
 
   OverrideStubPathsIfNeeded();
 
-  chromeos::SystemSaltGetter::Initialize();
+  SystemSaltGetter::Initialize();
 
   // Initialize DBusThreadManager for the browser.
   DBusThreadManager::Initialize();
@@ -345,7 +345,7 @@
 
   shill_clients::Shutdown();
   DBusThreadManager::Shutdown();
-  chromeos::SystemSaltGetter::Shutdown();
+  SystemSaltGetter::Shutdown();
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/extensions/autotest_private/autotest_private_apitest.cc b/chrome/browser/ash/extensions/autotest_private/autotest_private_apitest.cc
index b04c7177..1795cde 100644
--- a/chrome/browser/ash/extensions/autotest_private/autotest_private_apitest.cc
+++ b/chrome/browser/ash/extensions/autotest_private/autotest_private_apitest.cc
@@ -602,7 +602,16 @@
   results_changed_waiter.Wait();
   results_waiter.Wait();
 
-  const auto results = PublishedResults();
+  std::vector<ChromeSearchResult*> results;
+  for (auto* result : PublishedResults()) {
+    // There may be zero state results that are also published, but not visible
+    // in the UI. This test should only check search list results.
+    if (result->display_type() != ash::SearchResultDisplayType::kList)
+      continue;
+
+    results.push_back(result);
+  }
+
   ASSERT_EQ(results.size(), 1u);
   ASSERT_TRUE(results[0]);
   EXPECT_EQ(base::UTF16ToASCII(results[0]->title()), "youtube");
diff --git a/chrome/browser/ash/extensions/login_screen_ui/ui_handler.cc b/chrome/browser/ash/extensions/login_screen_ui/ui_handler.cc
index e92b819..fd9d862 100644
--- a/chrome/browser/ash/extensions/login_screen_ui/ui_handler.cc
+++ b/chrome/browser/ash/extensions/login_screen_ui/ui_handler.cc
@@ -28,6 +28,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+using ::ash::InstallAttributes;
+
 const char kErrorWindowAlreadyExists[] =
     "Login screen extension UI already in use.";
 const char kErrorNoExistingWindow[] = "No open window to close.";
diff --git a/chrome/browser/ash/extensions/login_screen_ui/ui_handler_unittest.cc b/chrome/browser/ash/extensions/login_screen_ui/ui_handler_unittest.cc
index 638c431..13fce64 100644
--- a/chrome/browser/ash/extensions/login_screen_ui/ui_handler_unittest.cc
+++ b/chrome/browser/ash/extensions/login_screen_ui/ui_handler_unittest.cc
@@ -30,6 +30,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+using ::ash::StubInstallAttributes;
+
 const char kErrorWindowAlreadyExists[] =
     "Login screen extension UI already in use.";
 const char kErrorNoExistingWindow[] = "No open window to close.";
diff --git a/chrome/browser/ash/input_method/input_method_manager_impl.cc b/chrome/browser/ash/input_method/input_method_manager_impl.cc
index ee097be9..b75c9546 100644
--- a/chrome/browser/ash/input_method/input_method_manager_impl.cc
+++ b/chrome/browser/ash/input_method/input_method_manager_impl.cc
@@ -42,7 +42,7 @@
 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/system/devicemode.h"
+#include "chromeos/ash/components/system/devicemode.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
 #include "third_party/icu/source/common/unicode/uloc.h"
diff --git a/chrome/browser/ash/login/demo_mode/demo_session.cc b/chrome/browser/ash/login/demo_mode/demo_session.cc
index 11538cd..508f3850 100644
--- a/chrome/browser/ash/login/demo_mode/demo_session.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_session.cc
@@ -47,8 +47,8 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/constants/chromeos_features.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/login/demo_mode/demo_setup_browsertest.cc b/chrome/browser/ash/login/demo_mode/demo_setup_browsertest.cc
index 519e91572..294dcb7 100644
--- a/chrome/browser/ash/login/demo_mode/demo_setup_browsertest.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_setup_browsertest.cc
@@ -58,9 +58,9 @@
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_state.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/constants/chromeos_features.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ash/login/demo_mode/demo_setup_controller_unittest.cc b/chrome/browser/ash/login/demo_mode/demo_setup_controller_unittest.cc
index 8affdb9..6c8d074 100644
--- a/chrome/browser/ash/login/demo_mode/demo_setup_controller_unittest.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_setup_controller_unittest.cc
@@ -28,8 +28,8 @@
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/constants/chromeos_features.h"
-#include "chromeos/system/fake_statistics_provider.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/ash/login/enrollment/auto_enrollment_controller.cc b/chrome/browser/ash/login/enrollment/auto_enrollment_controller.cc
index 52841fc..d1cbacb 100644
--- a/chrome/browser/ash/login/enrollment/auto_enrollment_controller.cc
+++ b/chrome/browser/ash/login/enrollment/auto_enrollment_controller.cc
@@ -34,7 +34,7 @@
 #include "chromeos/ash/components/dbus/system_clock/system_clock_sync_observation.h"
 #include "chromeos/ash/components/dbus/userdataauth/install_attributes_client.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/device_event_log/device_event_log.h"
 #include "components/policy/core/common/cloud/device_management_service.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc b/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
index 5192be3..38dd2038 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
@@ -55,7 +55,7 @@
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/policy/core/common/cloud/device_management_service.h"
 #include "components/policy/core/common/policy_switches.h"
 #include "components/policy/proto/device_management_backend.pb.h"
diff --git a/chrome/browser/ash/login/enrollment/enrollment_screen_unittest.cc b/chrome/browser/ash/login/enrollment/enrollment_screen_unittest.cc
index 823202cd..1d5113c 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_screen_unittest.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_screen_unittest.cc
@@ -22,7 +22,7 @@
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/ash/login/enrollment/enterprise_enrollment_helper_impl.cc b/chrome/browser/ash/login/enrollment/enterprise_enrollment_helper_impl.cc
index 2066522b..465655d 100644
--- a/chrome/browser/ash/login/enrollment/enterprise_enrollment_helper_impl.cc
+++ b/chrome/browser/ash/login/enrollment/enterprise_enrollment_helper_impl.cc
@@ -25,9 +25,9 @@
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/dbus/tpm_manager/tpm_manager.pb.h"
 #include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/dm_auth.h"
 #include "components/policy/proto/device_management_backend.pb.h"
diff --git a/chrome/browser/ash/login/hwid_checker.cc b/chrome/browser/ash/login/hwid_checker.cc
index 537bd3bae..891d918 100644
--- a/chrome/browser/ash/login/hwid_checker.cc
+++ b/chrome/browser/ash/login/hwid_checker.cc
@@ -15,7 +15,7 @@
 #include "base/system/sys_info.h"
 #include "build/branding_buildflags.h"
 #include "chrome/common/chrome_switches.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "content/public/common/content_switches.h"
 #include "third_party/re2/src/re2/re2.h"
 #include "third_party/zlib/zlib.h"
diff --git a/chrome/browser/ash/login/hwid_checker_unittest.cc b/chrome/browser/ash/login/hwid_checker_unittest.cc
index 00910692..a7e1228 100644
--- a/chrome/browser/ash/login/hwid_checker_unittest.cc
+++ b/chrome/browser/ash/login/hwid_checker_unittest.cc
@@ -9,7 +9,7 @@
 #include "base/test/scoped_running_on_chromeos.h"
 #include "base/time/time.h"
 #include "build/branding_buildflags.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "content/public/common/content_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/ash/login/lock/screen_locker_unittest.cc b/chrome/browser/ash/login/lock/screen_locker_unittest.cc
index bb35d797..d713340 100644
--- a/chrome/browser/ash/login/lock/screen_locker_unittest.cc
+++ b/chrome/browser/ash/login/lock/screen_locker_unittest.cc
@@ -41,8 +41,8 @@
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
 #include "chromeos/ash/components/login/login_state/login_state.h"
 #include "chromeos/ash/components/login/session/session_termination_manager.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
-#include "chromeos/system/fake_statistics_provider.h"
 #include "components/account_id/account_id.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/session_manager/core/session_manager.h"
diff --git a/chrome/browser/ash/login/login_utils_browsertest.cc b/chrome/browser/ash/login/login_utils_browsertest.cc
index 13e41a4..2771a160 100644
--- a/chrome/browser/ash/login/login_utils_browsertest.cc
+++ b/chrome/browser/ash/login/login_utils_browsertest.cc
@@ -13,7 +13,7 @@
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_test.h"
 #include "rlz/buildflags/buildflags.h"
diff --git a/chrome/browser/ash/login/oobe_interactive_ui_test.cc b/chrome/browser/ash/login/oobe_interactive_ui_test.cc
index d7c1ba5..5cb84a9 100644
--- a/chrome/browser/ash/login/oobe_interactive_ui_test.cc
+++ b/chrome/browser/ash/login/oobe_interactive_ui_test.cc
@@ -66,8 +66,8 @@
 #include "chromeos/ash/components/assistant/buildflags.h"
 #include "chromeos/ash/components/dbus/constants/attestation_constants.h"
 #include "chromeos/ash/components/dbus/update_engine/update_engine_client.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/constants/chromeos_features.h"
-#include "chromeos/system/fake_statistics_provider.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
diff --git a/chrome/browser/ash/login/oobe_localization_browsertest.cc b/chrome/browser/ash/login/oobe_localization_browsertest.cc
index 683ea68f..23249ff 100644
--- a/chrome/browser/ash/login/oobe_localization_browsertest.cc
+++ b/chrome/browser/ash/login/oobe_localization_browsertest.cc
@@ -23,8 +23,8 @@
 #include "chrome/browser/ui/webui/ash/login/welcome_screen_handler.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/notification_service.h"
diff --git a/chrome/browser/ash/login/reset_browsertest.cc b/chrome/browser/ash/login/reset_browsertest.cc
index 47df8fe4..25b52b4 100644
--- a/chrome/browser/ash/login/reset_browsertest.cc
+++ b/chrome/browser/ash/login/reset_browsertest.cc
@@ -26,8 +26,8 @@
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
 #include "chromeos/ash/components/dbus/update_engine/fake_update_engine_client.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
-#include "chromeos/system/fake_statistics_provider.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_launcher.h"
diff --git a/chrome/browser/ash/login/screens/reset_screen.cc b/chrome/browser/ash/login/screens/reset_screen.cc
index f4cdcc9..4bbb456 100644
--- a/chrome/browser/ash/login/screens/reset_screen.cc
+++ b/chrome/browser/ash/login/screens/reset_screen.cc
@@ -31,8 +31,8 @@
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
 #include "chromeos/ash/components/dbus/update_engine/update_engine_client.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/dbus/power/power_manager_client.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ash/login/screens/reset_screen_unittest.cc b/chrome/browser/ash/login/screens/reset_screen_unittest.cc
index aedbb4ec..01c22a90 100644
--- a/chrome/browser/ash/login/screens/reset_screen_unittest.cc
+++ b/chrome/browser/ash/login/screens/reset_screen_unittest.cc
@@ -8,7 +8,7 @@
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc b/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
index 3adad761..4f70fa8 100644
--- a/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
@@ -42,8 +42,8 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/dbus/constants/dbus_switches.h"
-#include "chromeos/system/fake_statistics_provider.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ash/login/session/chrome_session_manager_browsertest.cc b/chrome/browser/ash/login/session/chrome_session_manager_browsertest.cc
index 7d80b22b..24b265a 100644
--- a/chrome/browser/ash/login/session/chrome_session_manager_browsertest.cc
+++ b/chrome/browser/ash/login/session/chrome_session_manager_browsertest.cc
@@ -24,8 +24,8 @@
 #include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "content/public/test/browser_test.h"
 #include "google_apis/gaia/fake_gaia.h"
 #include "rlz/buildflags/buildflags.h"
diff --git a/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.cc b/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.cc
index 46d2964..d6e91a4 100644
--- a/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.cc
+++ b/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.cc
@@ -19,7 +19,7 @@
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chromeos/ash/components/attestation/fake_attestation_flow.h"
 #include "chromeos/ash/components/attestation/fake_certificate.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
 #include "components/policy/core/common/policy_switches.h"
diff --git a/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.h b/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.h
index bd5bd45..d873487 100644
--- a/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.h
+++ b/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.h
@@ -13,7 +13,7 @@
 #include "base/containers/flat_set.h"
 #include "chrome/browser/ash/policy/server_backed_state/server_backed_state_keys_broker.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/policy/proto/cloud_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
diff --git a/chrome/browser/ash/login/users/wallpaper_policy_browsertest.cc b/chrome/browser/ash/login/users/wallpaper_policy_browsertest.cc
index bd83c55..2acf3d4 100644
--- a/chrome/browser/ash/login/users/wallpaper_policy_browsertest.cc
+++ b/chrome/browser/ash/login/users/wallpaper_policy_browsertest.cc
@@ -121,8 +121,8 @@
 
 // Initialize system salt to calculate wallpaper file names.
 void SetSystemSalt() {
-  chromeos::SystemSaltGetter::Get()->SetRawSaltForTesting(
-      chromeos::SystemSaltGetter::RawSalt({1, 2, 3, 4, 5, 6, 7, 8}));
+  SystemSaltGetter::Get()->SetRawSaltForTesting(
+      SystemSaltGetter::RawSalt({1, 2, 3, 4, 5, 6, 7, 8}));
 }
 
 }  // namespace
diff --git a/chrome/browser/ash/login/version_info_updater.cc b/chrome/browser/ash/login/version_info_updater.cc
index 550da568..c175a45a 100644
--- a/chrome/browser/ash/login/version_info_updater.cc
+++ b/chrome/browser/ash/login/version_info_updater.cc
@@ -21,8 +21,8 @@
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
-#include "chromeos/system/statistics_provider.h"
 #include "chromeos/version/version_loader.h"
 #include "components/version_info/version_info.h"
 #include "device/bluetooth/bluetooth_adapter.h"
diff --git a/chrome/browser/ash/login/wizard_controller_browsertest.cc b/chrome/browser/ash/login/wizard_controller_browsertest.cc
index d037e07..1a80e41e 100644
--- a/chrome/browser/ash/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/ash/login/wizard_controller_browsertest.cc
@@ -102,11 +102,11 @@
 #include "chromeos/ash/components/network/network_state.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
 #include "chromeos/ash/components/settings/timezone_settings.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/ash/components/timezone/timezone_request.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/dbus/constants/dbus_switches.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
 #include "chromeos/test/chromeos_test_utils.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/prefs/pref_registry_simple.h"
diff --git a/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl.cc b/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl.cc
index 5d64acf..23e24b8 100644
--- a/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl.cc
+++ b/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ash/policy/core/device_attributes_impl.h"
 #include "chrome/browser/net/secure_dns_config.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl_unittest.cc b/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl_unittest.cc
index 0f98db68..640f6383 100644
--- a/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl_unittest.cc
+++ b/chrome/browser/ash/net/dns_over_https/templates_uri_resolver_impl_unittest.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/net/secure_dns_config.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/user_manager/fake_user_manager.h"
diff --git a/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc b/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc
index 1ac5b47..b564808 100644
--- a/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc
+++ b/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc
@@ -77,7 +77,7 @@
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/cros_settings_provider.h"
 #include "chromeos/ash/components/settings/timezone_settings.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h"
 #include "components/policy/core/common/cloud/resource_cache.h"
diff --git a/chrome/browser/ash/policy/core/device_attributes_browsertest.cc b/chrome/browser/ash/policy/core/device_attributes_browsertest.cc
index 1db0ba7..20040f2f 100644
--- a/chrome/browser/ash/policy/core/device_attributes_browsertest.cc
+++ b/chrome/browser/ash/policy/core/device_attributes_browsertest.cc
@@ -16,8 +16,8 @@
 #include "chrome/browser/browser_process_platform_part_ash.h"
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
diff --git a/chrome/browser/ash/policy/core/device_attributes_impl.cc b/chrome/browser/ash/policy/core/device_attributes_impl.cc
index 2e64ab7..2200226 100644
--- a/chrome/browser/ash/policy/core/device_attributes_impl.cc
+++ b/chrome/browser/ash/policy/core/device_attributes_impl.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/ash/policy/handlers/device_name_policy_handler.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_ash.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 
 namespace policy {
diff --git a/chrome/browser/ash/policy/core/device_cloud_policy_client_factory_ash.cc b/chrome/browser/ash/policy/core/device_cloud_policy_client_factory_ash.cc
index 8a004a3..191a951 100644
--- a/chrome/browser/ash/policy/core/device_cloud_policy_client_factory_ash.cc
+++ b/chrome/browser/ash/policy/core/device_cloud_policy_client_factory_ash.cc
@@ -11,7 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/chrome/browser/ash/policy/core/device_cloud_policy_client_factory_ash_unittest.cc b/chrome/browser/ash/policy/core/device_cloud_policy_client_factory_ash_unittest.cc
index c59da8e..573e606 100644
--- a/chrome/browser/ash/policy/core/device_cloud_policy_client_factory_ash_unittest.cc
+++ b/chrome/browser/ash/policy/core/device_cloud_policy_client_factory_ash_unittest.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/ash/policy/core/device_cloud_policy_client_factory_ash.h"
 
 #include "base/test/task_environment.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/policy/core/common/cloud/mock_device_management_service.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
diff --git a/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.cc b/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.cc
index fb14f35..2a141b4 100644
--- a/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.cc
+++ b/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.cc
@@ -44,7 +44,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_external_data_manager.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
 #include "components/policy/core/common/cloud/cloud_policy_service.h"
diff --git a/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc b/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc
index 1f57ddb2..ebb4b322 100644
--- a/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc
+++ b/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc
@@ -43,8 +43,8 @@
 #include "chromeos/ash/components/dbus/userdataauth/fake_cryptohome_misc_client.h"
 #include "chromeos/ash/components/dbus/userdataauth/fake_install_attributes_client.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
@@ -225,7 +225,7 @@
     TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_);
 
     // SystemSaltGetter is used in DeviceOAuth2TokenService.
-    chromeos::SystemSaltGetter::Initialize();
+    ash::SystemSaltGetter::Initialize();
     DeviceOAuth2TokenServiceFactory::Initialize(
         test_url_loader_factory_.GetSafeWeakWrapper(), &local_state_);
 
@@ -246,7 +246,7 @@
     install_attributes_.reset();
 
     DeviceOAuth2TokenServiceFactory::Shutdown();
-    chromeos::SystemSaltGetter::Shutdown();
+    ash::SystemSaltGetter::Shutdown();
     ash::InstallAttributesClient::Shutdown();
     TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
 
diff --git a/chrome/browser/ash/policy/core/dm_token_storage.cc b/chrome/browser/ash/policy/core/dm_token_storage.cc
index 752d4ad5..1c32d94 100644
--- a/chrome/browser/ash/policy/core/dm_token_storage.cc
+++ b/chrome/browser/ash/policy/core/dm_token_storage.cc
@@ -37,7 +37,7 @@
 DMTokenStorage::DMTokenStorage(PrefService* local_state)
     : local_state_(local_state) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  chromeos::SystemSaltGetter::Get()->GetSystemSalt(base::BindOnce(
+  ash::SystemSaltGetter::Get()->GetSystemSalt(base::BindOnce(
       &DMTokenStorage::OnSystemSaltReceived, weak_ptr_factory_.GetWeakPtr()));
 }
 
diff --git a/chrome/browser/ash/policy/core/dm_token_storage_unittest.cc b/chrome/browser/ash/policy/core/dm_token_storage_unittest.cc
index e759b5ac..0280f2f 100644
--- a/chrome/browser/ash/policy/core/dm_token_storage_unittest.cc
+++ b/chrome/browser/ash/policy/core/dm_token_storage_unittest.cc
@@ -28,10 +28,10 @@
 
   void SetSaltPending() {
     // Clear the cached salt.
-    chromeos::SystemSaltGetter::Shutdown();
+    ash::SystemSaltGetter::Shutdown();
     FakeCryptohomeMiscClient::Get()->set_system_salt(std::vector<uint8_t>());
     FakeCryptohomeMiscClient::Get()->SetServiceIsAvailable(false);
-    chromeos::SystemSaltGetter::Initialize();
+    ash::SystemSaltGetter::Initialize();
   }
 
   void SetSaltAvailable() {
@@ -49,12 +49,12 @@
     ash::CryptohomeMiscClient::InitializeFake();
     SetSaltAvailable();
 
-    chromeos::SystemSaltGetter::Initialize();
+    ash::SystemSaltGetter::Initialize();
   }
 
   void TearDown() override {
     dm_token_storage_.reset();
-    chromeos::SystemSaltGetter::Shutdown();
+    ash::SystemSaltGetter::Shutdown();
     ash::CryptohomeMiscClient::Shutdown();
     base::RunLoop().RunUntilIdle();
   }
diff --git a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.cc b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.cc
index 9935f05..2d04289 100644
--- a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.cc
+++ b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.cc
@@ -10,8 +10,8 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "build/branding_buildflags.h"
-#include "chromeos/system/factory_ping_embargo_check.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/factory_ping_embargo_check.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 
 namespace ash::system {
 // TODO(https://crbug.com/1164001): remove when migrated to ash::
diff --git a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc
index c5dea28..5be2377 100644
--- a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc
+++ b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc
@@ -11,8 +11,8 @@
 #include "base/test/scoped_command_line.h"
 #include "base/time/time.h"
 #include "build/branding_buildflags.h"
-#include "chromeos/system/factory_ping_embargo_check.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/factory_ping_embargo_check.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash::system {
diff --git a/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer.cc b/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer.cc
index 759b2afe..8b2eafd 100644
--- a/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer.cc
+++ b/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer.cc
@@ -18,7 +18,7 @@
 #include "chrome/common/chrome_content_client.h"
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
 #include "components/policy/core/common/cloud/device_management_service.h"
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_config.cc b/chrome/browser/ash/policy/enrollment/enrollment_config.cc
index ce6758aa..0ea18a2 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_config.cc
+++ b/chrome/browser/ash/policy/enrollment/enrollment_config.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/prefs/pref_service.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_config_unittest.cc b/chrome/browser/ash/policy/enrollment/enrollment_config_unittest.cc
index fc56442..7bbfa8f4 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_config_unittest.cc
+++ b/chrome/browser/ash/policy/enrollment/enrollment_config_unittest.cc
@@ -14,8 +14,8 @@
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.cc b/chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.cc
index a0ff525..80dd9d0 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.cc
+++ b/chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.cc
@@ -11,7 +11,7 @@
 #include "chrome/browser/ash/login/startup_utils.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/chrome/browser/ash/policy/enrollment/psm/construct_rlwe_id.cc b/chrome/browser/ash/policy/enrollment/psm/construct_rlwe_id.cc
index d4eecaa..8779807 100644
--- a/chrome/browser/ash/policy/enrollment/psm/construct_rlwe_id.cc
+++ b/chrome/browser/ash/policy/enrollment/psm/construct_rlwe_id.cc
@@ -8,7 +8,7 @@
 
 #include "base/check.h"
 #include "base/strings/string_number_conversions.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "third_party/private_membership/src/private_membership_rlwe.pb.h"
 
 namespace psm_rlwe = private_membership::rlwe;
diff --git a/chrome/browser/ash/policy/enrollment/psm/construct_rlwe_id_unittest.cc b/chrome/browser/ash/policy/enrollment/psm/construct_rlwe_id_unittest.cc
index 94d978e..449dd2a 100644
--- a/chrome/browser/ash/policy/enrollment/psm/construct_rlwe_id_unittest.cc
+++ b/chrome/browser/ash/policy/enrollment/psm/construct_rlwe_id_unittest.cc
@@ -4,8 +4,8 @@
 
 #include "chrome/browser/ash/policy/enrollment/psm/construct_rlwe_id.h"
 
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/private_membership/src/private_membership_rlwe.pb.h"
 
diff --git a/chrome/browser/ash/policy/handlers/device_name_policy_handler_impl.h b/chrome/browser/ash/policy/handlers/device_name_policy_handler_impl.h
index 6d54511..1b3ff589 100644
--- a/chrome/browser/ash/policy/handlers/device_name_policy_handler_impl.h
+++ b/chrome/browser/ash/policy/handlers/device_name_policy_handler_impl.h
@@ -13,7 +13,7 @@
 #include "chrome/browser/ash/policy/handlers/device_name_policy_handler.h"
 #include "chrome/browser/ash/settings/cros_settings.h"
 #include "chromeos/ash/components/network/network_state_handler_observer.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 
 namespace ash {
 class NetworkState;
diff --git a/chrome/browser/ash/policy/handlers/device_name_policy_handler_impl_unittest.cc b/chrome/browser/ash/policy/handlers/device_name_policy_handler_impl_unittest.cc
index 7884f89..109f247 100644
--- a/chrome/browser/ash/policy/handlers/device_name_policy_handler_impl_unittest.cc
+++ b/chrome/browser/ash/policy/handlers/device_name_policy_handler_impl_unittest.cc
@@ -13,7 +13,7 @@
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_handler_test_helper.h"
 #include "chromeos/ash/components/network/network_state.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/ash/policy/invalidation/affiliated_invalidation_service_provider_impl_unittest.cc b/chrome/browser/ash/policy/invalidation/affiliated_invalidation_service_provider_impl_unittest.cc
index 415ef66..aab3b2c 100644
--- a/chrome/browser/ash/policy/invalidation/affiliated_invalidation_service_provider_impl_unittest.cc
+++ b/chrome/browser/ash/policy/invalidation/affiliated_invalidation_service_provider_impl_unittest.cc
@@ -219,7 +219,7 @@
 
 void AffiliatedInvalidationServiceProviderImplTest::SetUp() {
   ash::CryptohomeMiscClient::InitializeFake();
-  chromeos::SystemSaltGetter::Initialize();
+  ash::SystemSaltGetter::Initialize();
   ASSERT_TRUE(profile_manager_.SetUp());
 
   DeviceOAuth2TokenServiceFactory::Initialize(
@@ -242,7 +242,7 @@
       ->RegisterTestingFactory(
           BrowserContextKeyedServiceFactory::TestingFactory());
   DeviceOAuth2TokenServiceFactory::Shutdown();
-  chromeos::SystemSaltGetter::Shutdown();
+  ash::SystemSaltGetter::Shutdown();
   ash::CryptohomeMiscClient::Shutdown();
 }
 
diff --git a/chrome/browser/ash/policy/networking/network_policy_application_browsertest.cc b/chrome/browser/ash/policy/networking/network_policy_application_browsertest.cc
index b9d6b8e..37385db3 100644
--- a/chrome/browser/ash/policy/networking/network_policy_application_browsertest.cc
+++ b/chrome/browser/ash/policy/networking/network_policy_application_browsertest.cc
@@ -30,9 +30,9 @@
 #include "chromeos/ash/components/network/network_cert_loader.h"
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_policy_observer.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/services/network_config/cros_network_config.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
-#include "chromeos/system/fake_statistics_provider.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
diff --git a/chrome/browser/ash/policy/remote_commands/device_command_set_volume_job.cc b/chrome/browser/ash/policy/remote_commands/device_command_set_volume_job.cc
index ad1ad0e..405ebe1 100644
--- a/chrome/browser/ash/policy/remote_commands/device_command_set_volume_job.cc
+++ b/chrome/browser/ash/policy/remote_commands/device_command_set_volume_job.cc
@@ -51,7 +51,7 @@
 void DeviceCommandSetVolumeJob::RunImpl(CallbackWithResult succeeded_callback,
                                         CallbackWithResult failed_callback) {
   SYSLOG(INFO) << "Running set volume command, volume = " << volume_;
-  auto* audio_handler = chromeos::CrasAudioHandler::Get();
+  auto* audio_handler = ash::CrasAudioHandler::Get();
   audio_handler->SetOutputVolumePercent(volume_);
   bool mute = audio_handler->IsOutputVolumeBelowDefaultMuteLevel();
   audio_handler->SetOutputMute(mute);
diff --git a/chrome/browser/ash/policy/remote_commands/device_command_set_volume_job_unittest.cc b/chrome/browser/ash/policy/remote_commands/device_command_set_volume_job_unittest.cc
index f4a157df..e27fb4ec 100644
--- a/chrome/browser/ash/policy/remote_commands/device_command_set_volume_job_unittest.cc
+++ b/chrome/browser/ash/policy/remote_commands/device_command_set_volume_job_unittest.cc
@@ -86,8 +86,8 @@
                    int expected_volume,
                    bool expected_muted) {
   EXPECT_EQ(RemoteCommandJob::SUCCEEDED, job->status());
-  int volume = chromeos::CrasAudioHandler::Get()->GetOutputVolumePercent();
-  bool muted = chromeos::CrasAudioHandler::Get()->IsOutputMuted();
+  int volume = ash::CrasAudioHandler::Get()->GetOutputVolumePercent();
+  bool muted = ash::CrasAudioHandler::Get()->IsOutputMuted();
   EXPECT_EQ(expected_volume, volume);
   EXPECT_EQ(expected_muted, muted);
   run_loop->Quit();
diff --git a/chrome/browser/ash/policy/remote_commands/device_command_start_crd_session_unittest.cc b/chrome/browser/ash/policy/remote_commands/device_command_start_crd_session_unittest.cc
index 3d27229..c2d3677 100644
--- a/chrome/browser/ash/policy/remote_commands/device_command_start_crd_session_unittest.cc
+++ b/chrome/browser/ash/policy/remote_commands/device_command_start_crd_session_unittest.cc
@@ -190,7 +190,7 @@
     web_kiosk_app_manager_ = std::make_unique<ash::WebKioskAppManager>();
 
     // SystemSaltGetter is used by the token service.
-    chromeos::SystemSaltGetter::Initialize();
+    ash::SystemSaltGetter::Initialize();
     DeviceOAuth2TokenServiceFactory::Initialize(
         test_url_loader_factory_.GetSafeWeakWrapper(), &local_state_);
     // The token service also requires local state.
@@ -199,7 +199,7 @@
 
   void TearDown() override {
     DeviceOAuth2TokenServiceFactory::Shutdown();
-    chromeos::SystemSaltGetter::Shutdown();
+    ash::SystemSaltGetter::Shutdown();
 
     web_kiosk_app_manager_.reset();
     arc_kiosk_app_manager_.reset();
diff --git a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager_unittest.cc b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager_unittest.cc
index 7ff8a2e11..9d43806 100644
--- a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager_unittest.cc
@@ -24,7 +24,7 @@
 #include "chrome/browser/ash/policy/reporting/install_event_log_util.h"
 #include "chrome/browser/profiles/reporting_util.h"
 #include "chrome/test/base/testing_profile.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
 #include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h"
diff --git a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_uploader_unittest.cc b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_uploader_unittest.cc
index 46ede89..f254126 100644
--- a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_uploader_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_uploader_unittest.cc
@@ -19,7 +19,7 @@
 #include "base/values.h"
 #include "chrome/browser/ash/policy/reporting/install_event_log_util.h"
 #include "chrome/browser/profiles/reporting_util.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
 #include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h"
 #include "components/policy/proto/device_management_backend.pb.h"
diff --git a/chrome/browser/ash/policy/reporting/install_event_log_util.cc b/chrome/browser/ash/policy/reporting/install_event_log_util.cc
index b3e91a8..9d312db 100644
--- a/chrome/browser/ash/policy/reporting/install_event_log_util.cc
+++ b/chrome/browser/ash/policy/reporting/install_event_log_util.cc
@@ -13,7 +13,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
 #include "base/values.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 
 namespace em = enterprise_management;
 
diff --git a/chrome/browser/ash/policy/status_collector/child_status_collector.cc b/chrome/browser/ash/policy/status_collector/child_status_collector.cc
index 506c82e..eb16a50 100644
--- a/chrome/browser/ash/policy/status_collector/child_status_collector.cc
+++ b/chrome/browser/ash/policy/status_collector/child_status_collector.cc
@@ -40,7 +40,7 @@
 #include "chromeos/ash/components/login/login_state/login_state.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/timezone_settings.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/version/version_loader.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/cloud_policy_util.h"
diff --git a/chrome/browser/ash/policy/status_collector/child_status_collector_browsertest.cc b/chrome/browser/ash/policy/status_collector/child_status_collector_browsertest.cc
index b4e72ad..c9f36f60 100644
--- a/chrome/browser/ash/policy/status_collector/child_status_collector_browsertest.cc
+++ b/chrome/browser/ash/policy/status_collector/child_status_collector_browsertest.cc
@@ -50,9 +50,9 @@
 #include "chromeos/ash/components/login/login_state/login_state.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/timezone_settings.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
 #include "chromeos/dbus/power_manager/idle.pb.h"
-#include "chromeos/system/fake_statistics_provider.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/policy/status_collector/device_status_collector.cc b/chrome/browser/ash/policy/status_collector/device_status_collector.cc
index 43f2781c..5be3a1a 100644
--- a/chrome/browser/ash/policy/status_collector/device_status_collector.cc
+++ b/chrome/browser/ash/policy/status_collector/device_status_collector.cc
@@ -79,11 +79,11 @@
 #include "chromeos/ash/components/network/network_state_handler.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/timezone_settings.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_probe.mojom.h"
 #include "chromeos/dbus/power_manager/idle.pb.h"
 #include "chromeos/dbus/tpm_manager/tpm_manager.pb.h"
 #include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
-#include "chromeos/system/statistics_provider.h"
 #include "chromeos/version/version_loader.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
@@ -2513,7 +2513,7 @@
 
 bool DeviceStatusCollector::GetAudioStatus(
     em::DeviceStatusReportRequest* status) {
-  chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
+  ash::CrasAudioHandler* audio_handler = ash::CrasAudioHandler::Get();
   status->set_sound_volume(audio_handler->GetOutputVolumePercent());
   return true;
 }
diff --git a/chrome/browser/ash/policy/status_collector/device_status_collector_browsertest.cc b/chrome/browser/ash/policy/status_collector/device_status_collector_browsertest.cc
index ee06dd5b..ce78565a 100644
--- a/chrome/browser/ash/policy/status_collector/device_status_collector_browsertest.cc
+++ b/chrome/browser/ash/policy/status_collector/device_status_collector_browsertest.cc
@@ -85,12 +85,12 @@
 #include "chromeos/ash/components/network/network_state_handler.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/timezone_settings.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.h"
 #include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd.mojom.h"
 #include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_probe.mojom.h"
 #include "chromeos/dbus/power_manager/idle.pb.h"
 #include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
-#include "chromeos/system/fake_statistics_provider.h"
 #include "components/account_id/account_id.h"
 #include "components/ownership/mock_owner_key_util.h"
 #include "components/policy/proto/device_management_backend.pb.h"
@@ -894,7 +894,7 @@
     // Use FakeUpdateEngineClient.
     update_engine_client_ = ash::UpdateEngineClient::InitializeFakeForTest();
 
-    chromeos::CrasAudioHandler::InitializeForTesting();
+    ash::CrasAudioHandler::InitializeForTesting();
     ash::UserDataAuthClient::InitializeFake();
     chromeos::PowerManagerClient::InitializeFake();
     ash::AttestationClient::InitializeFake();
@@ -923,7 +923,7 @@
     ash::AttestationClient::Shutdown();
     chromeos::PowerManagerClient::Shutdown();
     ash::UserDataAuthClient::Shutdown();
-    chromeos::CrasAudioHandler::Shutdown();
+    ash::CrasAudioHandler::Shutdown();
     ash::UpdateEngineClient::Shutdown();
     ash::KioskAppManager::Shutdown();
     ash::cros_healthd::FakeCrosHealthd::Shutdown();
@@ -2913,7 +2913,7 @@
   const int kCustomVolume = 42;
   scoped_testing_cros_settings_.device_settings()->SetBoolean(
       ash::kReportDeviceAudioStatus, true);
-  chromeos::CrasAudioHandler::Get()->SetOutputVolumePercent(kCustomVolume);
+  ash::CrasAudioHandler::Get()->SetOutputVolumePercent(kCustomVolume);
   GetStatus();
   EXPECT_EQ(kCustomVolume, device_status_.sound_volume());
 }
diff --git a/chrome/browser/ash/policy/status_collector/status_collector.cc b/chrome/browser/ash/policy/status_collector/status_collector.cc
index 342e4ed..45c5ddd 100644
--- a/chrome/browser/ash/policy/status_collector/status_collector.cc
+++ b/chrome/browser/ash/policy/status_collector/status_collector.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/ash/policy/status_collector/app_info_generator.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/user_manager/user_manager.h"
 
diff --git a/chrome/browser/ash/preferences.cc b/chrome/browser/ash/preferences.cc
index 5b32656..98034d8 100644
--- a/chrome/browser/ash/preferences.cc
+++ b/chrome/browser/ash/preferences.cc
@@ -51,10 +51,10 @@
 #include "chromeos/ash/components/dbus/update_engine/update_engine_client.h"
 #include "chromeos/ash/components/peripheral_notification/peripheral_notification_manager.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
+#include "chromeos/ash/components/system/devicemode.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/ash/components/timezone/timezone_resolver.h"
 #include "chromeos/components/disks/disks_prefs.h"
-#include "chromeos/system/devicemode.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/drive/drive_pref_names.h"
 #include "components/feedback/content/content_tracing_manager.h"
 #include "components/language/core/browser/pref_names.h"
diff --git a/chrome/browser/ash/scheduler_configuration_manager.h b/chrome/browser/ash/scheduler_configuration_manager.h
index a00224d..656aaa3 100644
--- a/chrome/browser/ash/scheduler_configuration_manager.h
+++ b/chrome/browser/ash/scheduler_configuration_manager.h
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "base/memory/weak_ptr.h"
-#include "chromeos/system/scheduler_configuration_manager_base.h"
+#include "chromeos/ash/components/system/scheduler_configuration_manager_base.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/chrome/browser/ash/system/device_disabling_manager.cc b/chrome/browser/ash/system/device_disabling_manager.cc
index 5c73c31..77e2d8a 100644
--- a/chrome/browser/ash/system/device_disabling_manager.cc
+++ b/chrome/browser/ash/system/device_disabling_manager.cc
@@ -19,7 +19,7 @@
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/cros_settings_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ash/system/device_disabling_manager_unittest.cc b/chrome/browser/ash/system/device_disabling_manager_unittest.cc
index 5d6b368..5865ebe 100644
--- a/chrome/browser/ash/system/device_disabling_manager_unittest.cc
+++ b/chrome/browser/ash/system/device_disabling_manager_unittest.cc
@@ -24,7 +24,7 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/ownership/mock_owner_key_util.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/proto/device_management_backend.pb.h"
diff --git a/chrome/browser/ash/system/input_device_settings.cc b/chrome/browser/ash/system/input_device_settings.cc
index 09e05ca..630e2687 100644
--- a/chrome/browser/ash/system/input_device_settings.cc
+++ b/chrome/browser/ash/system/input_device_settings.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/ash/system/input_device_settings.h"
 
 #include "chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/prefs/pref_service.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/system/input_device_settings_impl_ozone.cc b/chrome/browser/ash/system/input_device_settings_impl_ozone.cc
index df0956a..04be22a 100644
--- a/chrome/browser/ash/system/input_device_settings_impl_ozone.cc
+++ b/chrome/browser/ash/system/input_device_settings_impl_ozone.cc
@@ -8,7 +8,7 @@
 #include "chrome/browser/ash/system/fake_input_device_settings.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_ash.h"
-#include "chromeos/system/devicemode.h"
+#include "chromeos/ash/components/system/devicemode.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/ozone/public/input_controller.h"
 #include "ui/ozone/public/ozone_platform.h"
diff --git a/chrome/browser/ash/tpm_firmware_update.cc b/chrome/browser/ash/tpm_firmware_update.cc
index 8738ab80..7e37d93f 100644
--- a/chrome/browser/ash/tpm_firmware_update.cc
+++ b/chrome/browser/ash/tpm_firmware_update.cc
@@ -26,7 +26,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/tpm_firmware_update_unittest.cc b/chrome/browser/ash/tpm_firmware_update_unittest.cc
index c94b950e..5b11028f 100644
--- a/chrome/browser/ash/tpm_firmware_update_unittest.cc
+++ b/chrome/browser/ash/tpm_firmware_update_unittest.cc
@@ -22,7 +22,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/ash/web_applications/help_app/help_app_untrusted_ui_config.cc b/chrome/browser/ash/web_applications/help_app/help_app_untrusted_ui_config.cc
index ca848171..8944f061 100644
--- a/chrome/browser/ash/web_applications/help_app/help_app_untrusted_ui_config.cc
+++ b/chrome/browser/ash/web_applications/help_app/help_app_untrusted_ui_config.cc
@@ -29,9 +29,9 @@
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/webui_url_constants.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/ash/services/multidevice_setup/public/cpp/prefs.h"
 #include "chromeos/constants/chromeos_features.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/web_ui.h"
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_theme_provider_impl.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_theme_provider_impl.cc
index 19e1a1b..74db0182 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_theme_provider_impl.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_theme_provider_impl.cc
@@ -57,8 +57,9 @@
   // Call once to get the initial status.
   NotifyColorModeAutoScheduleChanged();
   if (ash::features::IsJellyEnabled()) {
-    // TODO(b/261505637): Observe changes to the static color pref.
+    // TODO(b/261505637): Observe changes to the color prefs.
     OnStaticColorChanged(color_palette_controller_->static_color());
+    OnColorSchemeChanged(color_palette_controller_->color_scheme());
   }
 }
 
@@ -101,6 +102,12 @@
   theme_observer_remote_->OnColorModeChanged(dark_mode_enabled);
 }
 
+void PersonalizationAppThemeProviderImpl::OnColorSchemeChanged(
+    ColorScheme color_scheme) {
+  DCHECK(theme_observer_remote_.is_bound());
+  theme_observer_remote_->OnColorSchemeChanged(color_scheme);
+}
+
 void PersonalizationAppThemeProviderImpl::OnStaticColorChanged(
     absl::optional<SkColor> color) {
   DCHECK(theme_observer_remote_.is_bound());
@@ -121,6 +128,16 @@
       IsColorModeAutoScheduleEnabled());
 }
 
+void PersonalizationAppThemeProviderImpl::GetColorScheme(
+    GetColorSchemeCallback callback) {
+  if (!ash::features::IsJellyEnabled()) {
+    theme_receiver_.ReportBadMessage(
+        "Cannot call GetColorScheme without Jelly enabled.");
+    return;
+  }
+  std::move(callback).Run(color_palette_controller_->color_scheme());
+}
+
 void PersonalizationAppThemeProviderImpl::SetColorScheme(
     ColorScheme color_scheme) {
   if (!ash::features::IsJellyEnabled()) {
@@ -129,16 +146,7 @@
     return;
   }
   color_palette_controller_->SetColorScheme(color_scheme, base::DoNothing());
-}
-
-void PersonalizationAppThemeProviderImpl::SetStaticColor(SkColor static_color) {
-  if (!ash::features::IsJellyEnabled()) {
-    theme_receiver_.ReportBadMessage(
-        "Cannot call SetStaticColor without Jelly enabled.");
-    return;
-  }
-  color_palette_controller_->SetStaticColor(static_color, base::DoNothing());
-  OnStaticColorChanged(static_color);
+  OnColorSchemeChanged(color_scheme);
 }
 
 void PersonalizationAppThemeProviderImpl::GetStaticColor(
@@ -150,4 +158,17 @@
   }
   std::move(callback).Run(color_palette_controller_->static_color());
 }
+
+void PersonalizationAppThemeProviderImpl::SetStaticColor(SkColor static_color) {
+  if (!ash::features::IsJellyEnabled()) {
+    theme_receiver_.ReportBadMessage(
+        "Cannot call SetStaticColor without Jelly enabled.");
+    return;
+  }
+  color_palette_controller_->SetStaticColor(static_color, base::DoNothing());
+  // TODO(b/261505637): Remove and use pref listeners once the prefs are
+  // available.
+  OnStaticColorChanged(static_color);
+  OnColorSchemeChanged(color_palette_controller_->color_scheme());
+}
 }  // namespace ash::personalization_app
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_theme_provider_impl.h b/chrome/browser/ash/web_applications/personalization_app/personalization_app_theme_provider_impl.h
index 5ff9139..1eb6ba1 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_theme_provider_impl.h
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_theme_provider_impl.h
@@ -63,6 +63,8 @@
   // ash::ColorModeObserver:
   void OnColorModeChanged(bool dark_mode_enabled) override;
 
+  void GetColorScheme(GetColorSchemeCallback callback) override;
+
   void GetStaticColor(GetStaticColorCallback callback) override;
 
  private:
@@ -71,6 +73,8 @@
   // Notify webUI the current state of color mode auto scheduler.
   void NotifyColorModeAutoScheduleChanged();
 
+  void OnColorSchemeChanged(ColorScheme color_scheme);
+
   void OnStaticColorChanged(absl::optional<SkColor> color);
 
   // Pointer to profile of user that opened personalization SWA. Not owned.
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_theme_provider_impl_unittest.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_theme_provider_impl_unittest.cc
index 128f036c..e60d1d7 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_theme_provider_impl_unittest.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_theme_provider_impl_unittest.cc
@@ -39,6 +39,10 @@
     color_mode_auto_schedule_enabled_ = enabled;
   }
 
+  void OnColorSchemeChanged(ash::ColorScheme color_scheme) override {
+    color_scheme_ = color_scheme;
+  }
+
   void OnStaticColorChanged(absl::optional<::SkColor> static_color) override {
     static_color_ = static_color;
   }
@@ -65,7 +69,14 @@
     return color_mode_auto_schedule_enabled_;
   }
 
-  absl::optional<SkColor> get_static_color() {
+  ash::ColorScheme GetColorScheme() {
+    if (theme_observer_receiver_.is_bound()) {
+      theme_observer_receiver_.FlushForTesting();
+    }
+    return color_scheme_;
+  }
+
+  absl::optional<SkColor> GetStaticColor() {
     if (!theme_observer_receiver_.is_bound())
       return absl::nullopt;
     theme_observer_receiver_.FlushForTesting();
@@ -78,6 +89,7 @@
 
   bool dark_mode_enabled_ = false;
   bool color_mode_auto_schedule_enabled_ = false;
+  ash::ColorScheme color_scheme_ = ash::ColorScheme::kTonalSpot;
   absl::optional<::SkColor> static_color_ = absl::nullopt;
 };
 
@@ -150,10 +162,17 @@
     return test_theme_observer_.is_color_mode_auto_schedule_enabled();
   }
 
-  absl::optional<SkColor> get_static_color() {
+  ash::ColorScheme GetColorScheme() {
+    if (theme_provider_remote_.is_bound()) {
+      theme_provider_remote_.FlushForTesting();
+    }
+    return test_theme_observer_.GetColorScheme();
+  }
+
+  absl::optional<SkColor> GetStaticColor() {
     if (theme_provider_remote_.is_bound())
       theme_provider_remote_.FlushForTesting();
-    return test_theme_observer_.get_static_color();
+    return test_theme_observer_.GetStaticColor();
   }
 
   const base::HistogramTester& histogram_tester() { return histogram_tester_; }
@@ -230,11 +249,23 @@
   SetThemeObserver();
   theme_provider_remote()->FlushForTesting();
   SkColor color = SK_ColorMAGENTA;
-  EXPECT_NE(color, get_static_color());
+  EXPECT_NE(color, GetStaticColor());
+  EXPECT_NE(ash::ColorScheme::kStatic, GetColorScheme());
 
   theme_provider()->SetStaticColor(color);
 
-  EXPECT_EQ(color, get_static_color());
+  EXPECT_EQ(color, GetStaticColor());
+  EXPECT_EQ(ash::ColorScheme::kStatic, GetColorScheme());
 }
 
+TEST_F(PersonalizationAppThemeProviderImplJellyTest, SetColorScheme) {
+  SetThemeObserver();
+  theme_provider_remote()->FlushForTesting();
+  auto color_scheme = ash::ColorScheme::kExpressive;
+  EXPECT_NE(color_scheme, GetColorScheme());
+
+  theme_provider()->SetColorScheme(color_scheme);
+
+  EXPECT_EQ(color_scheme, GetColorScheme());
+}
 }  // namespace ash::personalization_app
diff --git a/chrome/browser/browsing_data/browsing_data_model_browsertest.cc b/chrome/browser/browsing_data/browsing_data_model_browsertest.cc
index a7a37bf2..823aa78 100644
--- a/chrome/browser/browsing_data/browsing_data_model_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_model_browsertest.cc
@@ -255,7 +255,7 @@
   (async () => {
     try {
       await fetch("/issue", {trustToken: {type: 'token-request'}});
-      return await document.hasPrivateStateToken($1);
+      return await document.hasPrivateToken($1, 'private-state-token');
     } catch {
       return false;
     }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 10cf5bb..20aafe6 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -1542,6 +1542,8 @@
       prefs::kThrottleNonVisibleCrossOriginIframesAllowed, true);
   registry->RegisterBooleanPref(prefs::kNewBaseUrlInheritanceBehaviorAllowed,
                                 true);
+  registry->RegisterBooleanPref(
+      policy::policy_prefs::kUseMojoVideoDecoderForPepperAllowed, true);
 }
 
 // static
@@ -2937,6 +2939,17 @@
                                     switches::kChangeStackGuardOnForkEnabled);
   }
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+
+  if (process_type != switches::kZygoteProcess) {
+    DCHECK(g_browser_process);
+    PrefService* local_state = g_browser_process->local_state();
+    DCHECK(local_state);
+    if (!local_state->GetBoolean(
+            policy::policy_prefs::kUseMojoVideoDecoderForPepperAllowed)) {
+      command_line->AppendSwitch(
+          ::switches::kDisableUseMojoVideoDecoderForPepper);
+    }
+  }
 }
 
 std::string
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index d8db703..3d4e42d 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -299,6 +299,7 @@
     "//chromeos/ash/components/smbfs/mojom",
     "//chromeos/ash/components/string_matching",
     "//chromeos/ash/components/sync_wifi",
+    "//chromeos/ash/components/system",
     "//chromeos/ash/components/tether",
     "//chromeos/ash/components/timezone",
     "//chromeos/ash/components/tpm",
@@ -365,7 +366,6 @@
     "//chromeos/services/network_config/public/cpp:cpp",
     "//chromeos/services/network_health/public/mojom",
     "//chromeos/startup:constants",
-    "//chromeos/system",
     "//chromeos/ui/base",
     "//chromeos/ui/frame",
     "//chromeos/ui/wm",
@@ -907,10 +907,10 @@
     "//chromeos/ash/components/drivefs:test_support",
     "//chromeos/ash/components/login/auth",
     "//chromeos/ash/components/settings",
+    "//chromeos/ash/components/system",
     "//chromeos/ash/services/ime:test_support",
     "//chromeos/ash/services/multidevice_setup/public/cpp:test_support",
     "//chromeos/printing",
-    "//chromeos/system",
     "//components/crx_file",
     "//components/drive",
     "//components/exo",
@@ -1064,6 +1064,7 @@
     "//chromeos/ash/components/scanning",
     "//chromeos/ash/components/settings",
     "//chromeos/ash/components/sync_wifi:test_support",
+    "//chromeos/ash/components/system",
     "//chromeos/ash/components/tether:test_support",
     "//chromeos/ash/components/tpm",
     "//chromeos/ash/services/device_sync:test_support",
@@ -1082,7 +1083,6 @@
     "//chromeos/ime:gencode",
     "//chromeos/services/machine_learning/public/cpp:stub",
     "//chromeos/services/network_config:test_support",
-    "//chromeos/system",
     "//chromeos/ui/frame:test_support",
     "//components/account_manager_core:test_support",
     "//components/app_constants",
diff --git a/chrome/browser/chromeos/extensions/wallpaper_apitest.cc b/chrome/browser/chromeos/extensions/wallpaper_apitest.cc
index dd4f328..7dfff2b 100644
--- a/chrome/browser/chromeos/extensions/wallpaper_apitest.cc
+++ b/chrome/browser/chromeos/extensions/wallpaper_apitest.cc
@@ -27,8 +27,8 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 IN_PROC_BROWSER_TEST_F(WallPaperApiTest, Wallpaper) {
-  chromeos::SystemSaltGetter::Get()->SetRawSaltForTesting(
-      chromeos::SystemSaltGetter::RawSalt({1, 2, 3, 4, 5, 6, 7, 8}));
+  ash::SystemSaltGetter::Get()->SetRawSaltForTesting(
+      ash::SystemSaltGetter::RawSalt({1, 2, 3, 4, 5, 6, 7, 8}));
 
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("wallpaper")) << message_;
diff --git a/chrome/browser/device_api/device_attribute_api.cc b/chrome/browser/device_api/device_attribute_api.cc
index a98aad6..e163c73 100644
--- a/chrome/browser/device_api/device_attribute_api.cc
+++ b/chrome/browser/device_api/device_attribute_api.cc
@@ -11,7 +11,7 @@
 #include "chrome/browser/ash/policy/handlers/device_name_policy_handler.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #elif BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chromeos/lacros/lacros_service.h"
 #endif
diff --git a/chrome/browser/device_api/device_attribute_api_browsertest.cc b/chrome/browser/device_api/device_attribute_api_browsertest.cc
index 4dce33f..501c0546 100644
--- a/chrome/browser/device_api/device_attribute_api_browsertest.cc
+++ b/chrome/browser/device_api/device_attribute_api_browsertest.cc
@@ -6,7 +6,7 @@
 
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "content/public/test/browser_test.h"
 
 namespace {
diff --git a/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.cc b/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.cc
index 23c8554..fad7737 100644
--- a/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.cc
+++ b/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.cc
@@ -40,7 +40,7 @@
 void DeviceOAuth2TokenStoreChromeOS::Init(InitCallback callback) {
   state_ = State::INITIALIZING;
   // Pull in the system salt.
-  SystemSaltGetter::Get()->GetSystemSalt(
+  ash::SystemSaltGetter::Get()->GetSystemSalt(
       base::BindOnce(&DeviceOAuth2TokenStoreChromeOS::DidGetSystemSalt,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
diff --git a/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos_unittest.cc b/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos_unittest.cc
index 610d183..87cfe31 100644
--- a/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos_unittest.cc
+++ b/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos_unittest.cc
@@ -48,7 +48,7 @@
     ash::FakeCryptohomeMiscClient::Get()->set_system_salt(
         ash::FakeCryptohomeMiscClient::GetStubSystemSalt());
 
-    chromeos::SystemSaltGetter::Initialize();
+    ash::SystemSaltGetter::Initialize();
 
     scoped_refptr<ownership::MockOwnerKeyUtil> owner_key_util_(
         new ownership::MockOwnerKeyUtil());
@@ -61,7 +61,7 @@
   void TearDown() override {
     base::ThreadPoolInstance::Get()->FlushForTesting();
     ash::DeviceSettingsService::Get()->UnsetSessionManager();
-    chromeos::SystemSaltGetter::Shutdown();
+    ash::SystemSaltGetter::Shutdown();
     ash::CryptohomeMiscClient::Shutdown();
   }
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator_browsertest.cc b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator_browsertest.cc
index d04d3d4..bdca0f3 100644
--- a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator_browsertest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator_browsertest.cc
@@ -19,7 +19,7 @@
 #include "chromeos/ash/components/dbus/shill/shill_profile_client.h"
 #include "chromeos/ash/components/dbus/shill/shill_service_client.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/device_signals/core/common/signals_constants.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_registry_simple.h"
diff --git a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/common/BUILD.gn b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/common/BUILD.gn
index f3c2ed1..d20d0e9 100644
--- a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/common/BUILD.gn
+++ b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/common/BUILD.gn
@@ -69,6 +69,6 @@
   ]
 
   if (is_chromeos_ash) {
-    deps += [ "//chromeos/system" ]
+    deps += [ "//chromeos/ash/components/system" ]
   }
 }
diff --git a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/common/common_signals_decorator_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/common/common_signals_decorator_unittest.cc
index 2e5e140..583e98b 100644
--- a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/common/common_signals_decorator_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/common/common_signals_decorator_unittest.cc
@@ -16,7 +16,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 namespace enterprise_connectors {
diff --git a/chrome/browser/enterprise/identifiers/profile_id_service_factory_unittest.cc b/chrome/browser/enterprise/identifiers/profile_id_service_factory_unittest.cc
index 4c0416c..ca4f9e2e 100644
--- a/chrome/browser/enterprise/identifiers/profile_id_service_factory_unittest.cc
+++ b/chrome/browser/enterprise/identifiers/profile_id_service_factory_unittest.cc
@@ -25,7 +25,7 @@
 #endif  // BUILDFLAG(IS_WIN)
 #else
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chromeos/startup/browser_init_params.h"
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index a796089b..3d32690 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -1170,6 +1170,7 @@
       "//chromeos/ash/components/network",
       "//chromeos/ash/components/proximity_auth",
       "//chromeos/ash/components/settings",
+      "//chromeos/ash/components/system",
       "//chromeos/ash/components/tpm",
       "//chromeos/ash/services/assistant/public/cpp",
       "//chromeos/ash/services/chromebox_for_meetings/public/cpp",
@@ -1181,7 +1182,6 @@
       "//chromeos/services/machine_learning/public/mojom",
       "//chromeos/services/media_perception/public/mojom",
       "//chromeos/services/tts/public/mojom",
-      "//chromeos/system",
       "//chromeos/ui/base",
       "//chromeos/version",
       "//components/constrained_window",
diff --git a/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_ash_apitest.cc b/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_ash_apitest.cc
index b84315a6..d1eab00 100644
--- a/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_ash_apitest.cc
+++ b/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_ash_apitest.cc
@@ -11,8 +11,8 @@
 #include "chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
diff --git a/chrome/browser/extensions/external_provider_impl_chromeos_unittest.cc b/chrome/browser/extensions/external_provider_impl_chromeos_unittest.cc
index aebbecf6..ac10186 100644
--- a/chrome/browser/extensions/external_provider_impl_chromeos_unittest.cc
+++ b/chrome/browser/extensions/external_provider_impl_chromeos_unittest.cc
@@ -23,8 +23,8 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/sync/base/command_line_switches.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/pref_names.h"
diff --git a/chrome/browser/extensions/external_provider_impl_unittest.cc b/chrome/browser/extensions/external_provider_impl_unittest.cc
index 9bcfc374..b156bb4 100644
--- a/chrome/browser/extensions/external_provider_impl_unittest.cc
+++ b/chrome/browser/extensions/external_provider_impl_unittest.cc
@@ -47,8 +47,8 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/customization/customization_document.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/user_manager/scoped_user_manager.h"
 #endif
 
diff --git a/chrome/browser/extensions/sandboxed_pages_apitest.cc b/chrome/browser/extensions/sandboxed_pages_apitest.cc
index 481af0f0..78f3a3f 100644
--- a/chrome/browser/extensions/sandboxed_pages_apitest.cc
+++ b/chrome/browser/extensions/sandboxed_pages_apitest.cc
@@ -8,9 +8,12 @@
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/file_util.h"
+#include "extensions/test/result_catcher.h"
 #include "extensions/test/test_extension_dir.h"
+#include "net/dns/mock_host_resolver.h"
 
 namespace extensions {
 
@@ -22,14 +25,15 @@
  public:
   SandboxedPagesTest() = default;
 
+  void SetUpOnMainThread() override {
+    ExtensionApiTest::SetUpOnMainThread();
+    host_resolver()->AddRule("*", "127.0.0.1");
+  }
+
   [[nodiscard]] bool RunTest(const char* extension_name,
                              const char* manifest,
                              const RunOptions& run_options,
                              const LoadOptions& load_options) {
-    const char* kCustomArg =
-        GetParam() == ManifestVersion::TWO ? "manifest_v2" : "manifest_v3";
-    SetCustomArg(kCustomArg);
-
     base::ScopedAllowBlockingForTesting scoped_allow_blocking;
 
     //  Load the extension with the given `manifest`.
@@ -66,6 +70,11 @@
   base::ScopedTempDir temp_dir_;
 };
 
+INSTANTIATE_TEST_SUITE_P(,
+                         SandboxedPagesTest,
+                         ::testing::Values(ManifestVersion::TWO,
+                                           ManifestVersion::THREE));
+
 IN_PROC_BROWSER_TEST_P(SandboxedPagesTest, SandboxedPages) {
   const char* kManifestV2 = R"(
     {
@@ -94,10 +103,12 @@
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(SandboxedPagesTest, SandboxedPagesCSP) {
+// Verifies the behavior of sandboxed pages in Manifest V2. Remote frames
+// should be disallowed.
+IN_PROC_BROWSER_TEST_F(SandboxedPagesTest, ManifestV2DisallowsWebContent) {
   ASSERT_TRUE(StartEmbeddedTestServer());
 
-  const char* kManifestV2 = R"(
+  const char* kManifest = R"(
     {
       "name": "Tests that loading web content fails inside sandboxed pages",
       "manifest_version": 2,
@@ -110,25 +121,6 @@
     }
   )";
 
-  const char* kManifestV3 = R"(
-    {
-      "name": "Tests that loading web content fails inside sandboxed pages",
-      "manifest_version": 3,
-      "version": "0.1",
-      "web_accessible_resources": [{
-        "resources" : ["local_frame.html", "remote_frame.html"],
-        "matches": ["<all_urls>"]
-      }],
-      "sandbox": {
-        "pages": ["sandboxed.html"]
-      },
-      "content_security_policy": {
-        "sandbox": "sandbox allow-scripts; child-src *;"
-      }
-    }
-  )";
-  const char* kManifest =
-      GetParam() == ManifestVersion::TWO ? kManifestV2 : kManifestV3;
   // This extension attempts to load remote web content inside a sandboxed page.
   // Loading web content will fail because of CSP. In addition to that we will
   // show manifest warnings, hence ignore_manifest_warnings is set to true.
@@ -138,18 +130,72 @@
       << message_;
 }
 
-INSTANTIATE_TEST_SUITE_P(,
-                         SandboxedPagesTest,
-                         ::testing::Values(ManifestVersion::TWO,
-                                           ManifestVersion::THREE));
+// Verifies the behavior of sandboxed pages in Manifest V3. Remote frames
+// should be allowed.
+IN_PROC_BROWSER_TEST_F(SandboxedPagesTest, ManifestV3AllowsWebContent) {
+  ASSERT_TRUE(StartEmbeddedTestServer());
+
+  static constexpr char kManifest[] =
+      R"({
+           "name": "test extension",
+           "version": "0.1",
+           "manifest_version": 3,
+           "content_security_policy": {
+             "sandbox": "sandbox allow-scripts; child-src *;"
+           },
+           "sandbox": { "pages": ["sandboxed.html"] }
+         })";
+  static constexpr char kSandboxedHtml[] =
+      R"(<html>
+           <body>Sandboxed Page</body>
+           <script>
+             var iframe = document.createElement('iframe');
+             iframe.src = 'http://example.com:%d/extensions/echo_message.html';
+             // Check that we can post-message the frame.
+             addEventListener('message', (e) => {
+               // Note: We use domAutomationController here (and
+               // DOMMessageQueue below) because since this is a sandboxed page,
+               // it doesn't have access to any chrome.* APIs, including
+               // chrome.test.
+               domAutomationController.send(e.data);
+             });
+             iframe.onload = () => {
+               iframe.contentWindow.postMessage('hello', '*');
+             };
+             document.body.appendChild(iframe);
+           </script>
+         </html>)";
+
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(kManifest);
+  test_dir.WriteFile(
+      FILE_PATH_LITERAL("sandboxed.html"),
+      base::StringPrintf(kSandboxedHtml, embedded_test_server()->port()));
+
+  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
+  ASSERT_TRUE(extension);
+
+  content::DOMMessageQueue message_queue;
+  content::RenderFrameHost* frame_host = ui_test_utils::NavigateToURL(
+      browser(), extension->GetResourceURL("sandboxed.html"));
+  ASSERT_TRUE(frame_host);
+
+  // The frame should be sandboxed, so the origin should be "null" (as opposed
+  // to `extension->origin()`).
+  EXPECT_EQ("null", frame_host->GetLastCommittedOrigin().Serialize());
+
+  std::string message;
+  ASSERT_TRUE(message_queue.WaitForMessage(&message));
+  EXPECT_EQ(R"("echo hello")", message);
+}
 
 // Verify sandbox behavior.
-IN_PROC_BROWSER_TEST_F(SandboxedPagesTest, WebAccessibleResourcesTest) {
+IN_PROC_BROWSER_TEST_P(SandboxedPagesTest, WebAccessibleResourcesTest) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // Install extension.
   TestExtensionDir extension_dir;
-  constexpr char kManifest[] = R"({
+  static constexpr char kManifestV2[] = R"({
     "name": "Extension sandbox text",
     "version": "1.0",
     "manifest_version": 2,
@@ -160,7 +206,25 @@
       "web_accessible_resource.html"
     ]
   })";
-  extension_dir.WriteManifest(kManifest);
+
+  static constexpr char kManifestV3[] =
+      R"({
+           "name": "Extension sandbox text",
+           "version": "1.0",
+           "manifest_version": 3,
+           "sandbox": {
+             "pages": ["sandboxed_page.html"]
+           },
+           "web_accessible_resources": [{
+             "resources": ["web_accessible_resource.html"],
+             "matches": ["<all_urls>"]
+           }]
+         })";
+
+  const char* manifest =
+      GetParam() == ManifestVersion::TWO ? kManifestV2 : kManifestV3;
+
+  extension_dir.WriteManifest(manifest);
   extension_dir.WriteFile(FILE_PATH_LITERAL("sandboxed_page.html"), "");
   extension_dir.WriteFile(FILE_PATH_LITERAL("page.html"), "");
   extension_dir.WriteFile(FILE_PATH_LITERAL("resource.html"), "resource.html");
diff --git a/chrome/browser/feature_guide/notifications/internal/feature_notification_guide_service_impl_unittest.cc b/chrome/browser/feature_guide/notifications/internal/feature_notification_guide_service_impl_unittest.cc
index d22b5e8..60297d2 100644
--- a/chrome/browser/feature_guide/notifications/internal/feature_notification_guide_service_impl_unittest.cc
+++ b/chrome/browser/feature_guide/notifications/internal/feature_notification_guide_service_impl_unittest.cc
@@ -116,7 +116,7 @@
       SegmentSelectionCallback callback) override {}
   void GetClassificationResult(
       const std::string& segmentation_key,
-      const PredictionOptions& prediction_options,
+      const segmentation_platform::PredictionOptions& prediction_options,
       scoped_refptr<segmentation_platform::InputContext> input_context,
       segmentation_platform::ClassificationResultCallback callback) override {}
   void EnableMetrics(bool signal_collection_allowed) override {}
diff --git a/chrome/browser/feed/android/BUILD.gn b/chrome/browser/feed/android/BUILD.gn
index b089b8d..c483a3e 100644
--- a/chrome/browser/feed/android/BUILD.gn
+++ b/chrome/browser/feed/android/BUILD.gn
@@ -173,8 +173,6 @@
     "java/res/color/tab_layout_text_color_list.xml",
     "java/res/drawable-hdpi/follow_accelerator_shadow.9.png",
     "java/res/drawable-mdpi/follow_accelerator_shadow.9.png",
-    "java/res/drawable-v24/header_title_section_tab_background.xml",
-    "java/res/drawable-v24/header_title_tab_selected_background.xml",
     "java/res/drawable-xhdpi/follow_accelerator_shadow.9.png",
     "java/res/drawable-xxhdpi/follow_accelerator_shadow.9.png",
     "java/res/drawable/back_to_top_arrow.xml",
diff --git a/chrome/browser/feed/android/java/res/drawable-v24/header_title_section_tab_background.xml b/chrome/browser/feed/android/java/res/drawable-v24/header_title_section_tab_background.xml
deleted file mode 100644
index 7655a85..0000000
--- a/chrome/browser/feed/android/java/res/drawable-v24/header_title_section_tab_background.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
-  <item android:state_enabled="false">
-      <org.chromium.components.browser_ui.widget.SurfaceColorDrawable
-          android:shape="rectangle"
-          app:surfaceElevation="@dimen/default_elevation_1">
-        <corners android:radius="@dimen/feed_header_background_corner_radius"/>
-      </org.chromium.components.browser_ui.widget.SurfaceColorDrawable>
-  </item>
-  <item android:state_enabled="true">
-    <org.chromium.components.browser_ui.widget.SurfaceColorDrawable
-        android:shape="rectangle"
-        app:surfaceElevation="@dimen/feed_header_section_tab_bg_elevation_enabled">
-      <corners android:radius="@dimen/feed_header_background_corner_radius"/>
-    </org.chromium.components.browser_ui.widget.SurfaceColorDrawable>
-  </item>
-</selector>
\ No newline at end of file
diff --git a/chrome/browser/feed/android/java/res/drawable-v24/header_title_tab_selected_background.xml b/chrome/browser/feed/android/java/res/drawable-v24/header_title_tab_selected_background.xml
deleted file mode 100644
index eac6080..0000000
--- a/chrome/browser/feed/android/java/res/drawable-v24/header_title_tab_selected_background.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-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.
--->
-
-<org.chromium.components.browser_ui.widget.SurfaceColorDrawable
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:shape="rectangle"
-    app:surfaceElevation="@dimen/feed_header_tab_selected_bg_elevation">
-  <corners android:radius="@dimen/feed_header_background_corner_radius"/>
-  <!-- The stroke will show the main background color. -->
-  <stroke android:width="2dp" android:color="@android:color/transparent" />
-</org.chromium.components.browser_ui.widget.SurfaceColorDrawable>
\ No newline at end of file
diff --git a/chrome/browser/feed/android/java/res/drawable/header_title_section_tab_background.xml b/chrome/browser/feed/android/java/res/drawable/header_title_section_tab_background.xml
index 402baa6..7655a85 100644
--- a/chrome/browser/feed/android/java/res/drawable/header_title_section_tab_background.xml
+++ b/chrome/browser/feed/android/java/res/drawable/header_title_section_tab_background.xml
@@ -5,19 +5,20 @@
 found in the LICENSE file.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-      <item android:state_enabled="false">
-        <shape
-            android:shape="rectangle">
-          <corners android:radius="@dimen/feed_header_background_corner_radius" />
-          <solid android:color="@color/section_header_bg_color_disabled_baseline"/>
-        </shape>
-      </item>
-      <item android:state_enabled="true">
-        <shape
-            android:shape="rectangle">
-          <corners android:radius="@dimen/feed_header_background_corner_radius" />
-          <solid android:color="@color/section_header_bg_color_baseline"/>
-        </shape>
-      </item>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+  <item android:state_enabled="false">
+      <org.chromium.components.browser_ui.widget.SurfaceColorDrawable
+          android:shape="rectangle"
+          app:surfaceElevation="@dimen/default_elevation_1">
+        <corners android:radius="@dimen/feed_header_background_corner_radius"/>
+      </org.chromium.components.browser_ui.widget.SurfaceColorDrawable>
+  </item>
+  <item android:state_enabled="true">
+    <org.chromium.components.browser_ui.widget.SurfaceColorDrawable
+        android:shape="rectangle"
+        app:surfaceElevation="@dimen/feed_header_section_tab_bg_elevation_enabled">
+      <corners android:radius="@dimen/feed_header_background_corner_radius"/>
+    </org.chromium.components.browser_ui.widget.SurfaceColorDrawable>
+  </item>
 </selector>
\ No newline at end of file
diff --git a/chrome/browser/feed/android/java/res/drawable/header_title_tab_selected_background.xml b/chrome/browser/feed/android/java/res/drawable/header_title_tab_selected_background.xml
index 81097cb..eac6080 100644
--- a/chrome/browser/feed/android/java/res/drawable/header_title_tab_selected_background.xml
+++ b/chrome/browser/feed/android/java/res/drawable/header_title_tab_selected_background.xml
@@ -5,9 +5,12 @@
 found in the LICENSE file.
 -->
 
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-  <corners android:radius="@dimen/feed_header_background_corner_radius" />
+<org.chromium.components.browser_ui.widget.SurfaceColorDrawable
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:shape="rectangle"
+    app:surfaceElevation="@dimen/feed_header_tab_selected_bg_elevation">
+  <corners android:radius="@dimen/feed_header_background_corner_radius"/>
+  <!-- The stroke will show the main background color. -->
   <stroke android:width="2dp" android:color="@android:color/transparent" />
-  <solid android:color="@color/section_header_active_tab_bg_color_baseline"/>
-</shape>
\ No newline at end of file
+</org.chromium.components.browser_ui.widget.SurfaceColorDrawable>
\ No newline at end of file
diff --git a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
index be51222..0b3085d9 100644
--- a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
+++ b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
@@ -54,7 +54,7 @@
 #include "chrome/browser/metrics/chromeos_metrics_provider.h"
 #include "chrome/browser/metrics/enrollment_status.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/version/version_loader.h"
 #endif
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index f792675..bfa4c41 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3352,6 +3352,11 @@
     "expiry_milestone": -1
   },
   {
+    "name": "enable-webassembly-garbage-collection",
+    "owners": [ "adamk", "jkummerow", "wasm-team@google.com" ],
+    "expiry_milestone": 122
+  },
+  {
     "name": "enable-webassembly-lazy-compilation",
     "owners": [ "clemensb", "wasm-team@google.com" ],
     "expiry_milestone": 116
@@ -5185,11 +5190,6 @@
     "expiry_milestone": 120
   },
   {
-    "name": "omnibox-remove-suggestion-header-chevron",
-    "owners": [ "rongtan", "ender", "chrome-omnibox-team@google.com" ],
-    "expiry_milestone": 120
-  },
-  {
     "name": "omnibox-report-assisted-query-stats",
     "owners": [ "mahmadi", "chrome-desktop-search@google.com" ],
     "expiry_milestone": 120
@@ -6139,6 +6139,11 @@
     "expiry_milestone": 109
   },
   {
+    "name": "service-worker-bypass-fetch-handler-for-main-resource",
+    "owners": [ "sisidovski@google.com", "chrome-worker@google.com" ],
+    "expiry_milestone": 116
+  },
+  {
     "name": "set-market-url-for-testing",
     "owners": [ "//chrome/android/java/src/org/chromium/chrome/browser/omaha/OWNERS" ],
     // This is required by test teams to verify functionality on devices which
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 64d529d..d0bec37 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -623,6 +623,14 @@
     "eligibility requirements for showing app banners, such as having a "
     "manifest, are met.";
 
+const char kServiceWorkerBypassFetchHandlerForMainResourceName[] =
+    "Bypass Service Worker Fetch Handler for main resource";
+const char kServiceWorkerBypassFetchHandlerForMainResourceDescription[] =
+    "Bypass starting a service worker and its fetch handler for main resource "
+    "requests. The service worker starts after sending a main resource request "
+    "and handles subresources. If the main resource could be handled in the "
+    "fetch handler, the feature may affect the page load.";
+
 const char kCheckOfflineCapabilityName[] = "Check offline capability for PWAs";
 const char kCheckOfflineCapabilityDescription[] =
     "Use advanced offline capability check to decide whether the browser "
@@ -1345,6 +1353,12 @@
 const char kEnableWasmLazyCompilationDescription[] =
     "Enables lazy (JIT on first call) compilation of WebAssembly modules.";
 
+const char kEnableWasmGarbageCollectionName[] =
+    "WebAssembly Garbage Collection";
+const char kEnableWasmGarbageCollectionDescription[] =
+    "Enables the experimental Garbage Collection (GC) extensions to "
+    "WebAssembly.";
+
 const char kEnableWasmRelaxedSimdName[] = "WebAssembly Relaxed SIMD";
 const char kEnableWasmRelaxedSimdDescription[] =
     "Enables the use of WebAssembly vector operations with relaxed semantics";
@@ -1994,11 +2008,6 @@
 const char kOmniboxRemoveExcessiveRecycledViewClearCallsDescription[] =
     "Remove excessive clear calls on RecycledViewPool in omnibox.";
 
-const char kOmniboxRemoveSuggestionHeaderChevronName[] =
-    "Omnibox Remove Suggestion Header Chevron";
-const char kOmniboxRemoveSuggestionHeaderChevronDescription[] =
-    "Remove the chevron on the right side of omnibox suggestion search header.";
-
 const char kOmniboxReportAssistedQueryStatsName[] =
     "Omnibox Assisted Query Stats param";
 const char kOmniboxReportAssistedQueryStatsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 8acf4ea..bdb57fe 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -340,6 +340,9 @@
 extern const char kBypassAppBannerEngagementChecksName[];
 extern const char kBypassAppBannerEngagementChecksDescription[];
 
+extern const char kServiceWorkerBypassFetchHandlerForMainResourceName[];
+extern const char kServiceWorkerBypassFetchHandlerForMainResourceDescription[];
+
 extern const char kCanvasOopRasterizationName[];
 extern const char kCanvasOopRasterizationDescription[];
 
@@ -750,6 +753,9 @@
 extern const char kEnableWasmBaselineName[];
 extern const char kEnableWasmBaselineDescription[];
 
+extern const char kEnableWasmGarbageCollectionName[];
+extern const char kEnableWasmGarbageCollectionDescription[];
+
 extern const char kEnableWasmLazyCompilationName[];
 extern const char kEnableWasmLazyCompilationDescription[];
 
@@ -1141,9 +1147,6 @@
 extern const char kOmniboxRemoveExcessiveRecycledViewClearCallsName[];
 extern const char kOmniboxRemoveExcessiveRecycledViewClearCallsDescription[];
 
-extern const char kOmniboxRemoveSuggestionHeaderChevronName[];
-extern const char kOmniboxRemoveSuggestionHeaderChevronDescription[];
-
 extern const char kOmniboxRichAutocompletionPromisingName[];
 extern const char kOmniboxRichAutocompletionPromisingDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index d704d449..f61454b 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -364,7 +364,6 @@
     &omnibox::kOmniboxAssistantVoiceSearch,
     &omnibox::kOmniboxMatchToolbarAndStatusBarColor,
     &omnibox::kOmniboxRemoveExcessiveRecycledViewClearCalls,
-    &omnibox::kOmniboxRemoveSuggestionHeaderChevron,
     &omnibox::kOmniboxMostVisitedTilesAddRecycledViewPool,
     &omnibox::kOmniboxOnClobberFocusTypeOnContent,
     &omnibox::kSuggestionAnswersColorReverse,
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 c4ab08ed..0ca125f 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
@@ -409,8 +409,6 @@
             "OmniboxMostVisitedTilesTitleWrapAround";
     public static final String OMNIBOX_REMOVE_EXCESSIVE_RECYCLED_VIEW_CLEAR_CALLS =
             "OmniboxRemoveExcessiveRecycledViewClearCalls";
-    public static final String OMNIBOX_REMOVE_SUGGESTION_HEADER_CHEVRON =
-            "OmniboxRemoveSuggestionHeaderChevron";
     public static final String OMNIBOX_UPDATED_CONNECTION_SECURITY_INDICATORS =
             "OmniboxUpdatedConnectionSecurityIndicators";
     public static final String OPAQUE_ORIGIN_FOR_INCOMING_INTENTS =
diff --git a/chrome/browser/google/BUILD.gn b/chrome/browser/google/BUILD.gn
index c8e48a1..d610031 100644
--- a/chrome/browser/google/BUILD.gn
+++ b/chrome/browser/google/BUILD.gn
@@ -40,7 +40,7 @@
 
     deps += [
       "//chrome/browser:browser_process",
-      "//chromeos/system:system",
+      "//chromeos/ash/components/system:system",
       "//components/policy/core/common:common_constants",
     ]
   }
diff --git a/chrome/browser/google/google_brand_chromeos.cc b/chrome/browser/google/google_brand_chromeos.cc
index 234ace3..5c85039 100644
--- a/chrome/browser/google/google_brand_chromeos.cc
+++ b/chrome/browser/google/google_brand_chromeos.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/google/google_brand_code_map_chromeos.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/media/cdm_storage_id.cc b/chrome/browser/media/cdm_storage_id.cc
index 28dfdff..c0a88e5 100644
--- a/chrome/browser/media/cdm_storage_id.cc
+++ b/chrome/browser/media/cdm_storage_id.cc
@@ -116,7 +116,7 @@
   CdmStorageIdCallback scoped_callback =
       mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback),
                                                   std::vector<uint8_t>());
-  chromeos::SystemSaltGetter::Get()->GetSystemSalt(
+  ash::SystemSaltGetter::Get()->GetSystemSalt(
       base::BindOnce(&ComputeAndReturnStorageId, profile_salt, origin,
                      std::move(scoped_callback)));
 #elif BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/media/webrtc/webrtc_text_log_handler.cc b/chrome/browser/media/webrtc/webrtc_text_log_handler.cc
index 44d98ae..44f2bde 100644
--- a/chrome/browser/media/webrtc/webrtc_text_log_handler.cc
+++ b/chrome/browser/media/webrtc/webrtc_text_log_handler.cc
@@ -54,7 +54,7 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #endif
 
 using base::NumberToString;
diff --git a/chrome/browser/metrics/chrome_metrics_service_client_ash_unittest.cc b/chrome/browser/metrics/chrome_metrics_service_client_ash_unittest.cc
index d516e59ea..94433044 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client_ash_unittest.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client_ash_unittest.cc
@@ -14,13 +14,13 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/ash/components/login/login_state/login_state.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
 #include "chromeos/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
 #include "chromeos/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/metrics/log_decoder.h"
 #include "components/metrics/metrics_service.h"
 #include "components/metrics/metrics_state_manager.h"
diff --git a/chrome/browser/metrics/chromeos_metrics_provider.cc b/chrome/browser/metrics/chromeos_metrics_provider.cc
index 596f93b..5fbfb44 100644
--- a/chrome/browser/metrics/chromeos_metrics_provider.cc
+++ b/chrome/browser/metrics/chromeos_metrics_provider.cc
@@ -40,10 +40,10 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/metrics/metrics_features.h"
 #include "components/metrics/metrics_service.h"
 #include "components/metrics/structured/recorder.h"
diff --git a/chrome/browser/metrics/chromeos_system_profile_provider.cc b/chrome/browser/metrics/chromeos_system_profile_provider.cc
index 1ae6322..c157f829 100644
--- a/chrome/browser/metrics/chromeos_system_profile_provider.cc
+++ b/chrome/browser/metrics/chromeos_system_profile_provider.cc
@@ -15,10 +15,10 @@
 #include "chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/metrics/structured/recorder.h"
 #include "components/prefs/pref_service.h"
 #include "components/variations/hashing.h"
diff --git a/chrome/browser/metrics/chromeos_system_profile_provider_unittest.cc b/chrome/browser/metrics/chromeos_system_profile_provider_unittest.cc
index 0a18cc7..79ac56b 100644
--- a/chrome/browser/metrics/chromeos_system_profile_provider_unittest.cc
+++ b/chrome/browser/metrics/chromeos_system_profile_provider_unittest.cc
@@ -21,13 +21,13 @@
 #include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/ash/components/login/login_state/login_state.h"
 #include "chromeos/ash/components/multidevice/remote_device_test_util.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
 #include "chromeos/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage_impl.cc b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage_impl.cc
index dfa0a71..e6783938 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage_impl.cc
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage_impl.cc
@@ -348,7 +348,7 @@
   std::vector<NearbySharePrivateCertificate> certs;
   for (const base::Value& cert_dict : list.GetList()) {
     absl::optional<NearbySharePrivateCertificate> cert(
-        NearbySharePrivateCertificate::FromDictionary(cert_dict));
+        NearbySharePrivateCertificate::FromDictionary(cert_dict.GetDict()));
     if (!cert)
       return absl::nullopt;
 
@@ -368,11 +368,12 @@
 
 void NearbyShareCertificateStorageImpl::ReplacePrivateCertificates(
     const std::vector<NearbySharePrivateCertificate>& private_certificates) {
-  base::Value list(base::Value::Type::LIST);
+  base::Value::List list;
   for (const NearbySharePrivateCertificate& cert : private_certificates) {
     list.Append(cert.ToDictionary());
   }
-  pref_service_->Set(prefs::kNearbySharingPrivateCertificateListPrefName, list);
+  pref_service_->SetList(prefs::kNearbySharingPrivateCertificateListPrefName,
+                         std::move(list));
 }
 
 void NearbyShareCertificateStorageImpl::AddPublicCertificates(
@@ -488,13 +489,14 @@
 }
 
 void NearbyShareCertificateStorageImpl::SavePublicCertificateExpirations() {
-  base::Value dict(base::Value::Type::DICTIONARY);
+  base::Value::Dict dict;
 
   for (const std::pair<std::string, base::Time>& pair :
        public_certificate_expirations_) {
-    dict.SetKey(EncodeString(pair.first), base::TimeToValue(pair.second));
+    dict.Set(EncodeString(pair.first), base::TimeToValue(pair.second));
   }
 
-  pref_service_->Set(
-      prefs::kNearbySharingPublicCertificateExpirationDictPrefName, dict);
+  pref_service_->SetDict(
+      prefs::kNearbySharingPublicCertificateExpirationDictPrefName,
+      std::move(dict));
 }
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage_impl_unittest.cc b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage_impl_unittest.cc
index 54652807..51f6fa0 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage_impl_unittest.cc
@@ -174,17 +174,16 @@
         kMetadataEncryptionKey3, kEncryptedMetadataBytes3,
         kMetadataEncryptionKeyTag3));
 
-    base::Value expiration_dict(base::Value::Type::DICTIONARY);
+    base::Value::Dict expiration_dict;
     db_entries_.clear();
     for (auto& cert : pub_certs) {
-      expiration_dict.SetKey(
-          EncodeString(cert.secret_id()),
-          base::TimeToValue(TimestampToTime(cert.end_time())));
+      expiration_dict.Set(EncodeString(cert.secret_id()),
+                          base::TimeToValue(TimestampToTime(cert.end_time())));
       db_entries_.emplace(cert.secret_id(), std::move(cert));
     }
-    pref_service_->Set(
+    pref_service_->SetDict(
         prefs::kNearbySharingPublicCertificateExpirationDictPrefName,
-        expiration_dict);
+        std::move(expiration_dict));
   }
 
   void CaptureBoolCallback(bool* dest, bool src) { *dest = src; }
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.cc b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.cc
index 12a534f..02c51b0 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.cc
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.cc
@@ -327,63 +327,63 @@
   return public_certificate;
 }
 
-base::Value NearbySharePrivateCertificate::ToDictionary() const {
-  base::Value dict(base::Value::Type::DICTIONARY);
+base::Value::Dict NearbySharePrivateCertificate::ToDictionary() const {
+  base::Value::Dict dict;
 
-  dict.SetIntKey(kVisibility, static_cast<int>(visibility_));
-  dict.SetKey(kNotBefore, base::TimeToValue(not_before_));
-  dict.SetKey(kNotAfter, base::TimeToValue(not_after_));
+  dict.Set(kVisibility, static_cast<int>(visibility_));
+  dict.Set(kNotBefore, base::TimeToValue(not_before_));
+  dict.Set(kNotAfter, base::TimeToValue(not_after_));
 
   std::vector<uint8_t> key_pair;
   key_pair_->ExportPrivateKey(&key_pair);
-  dict.SetStringKey(kKeyPair, BytesToEncodedString(key_pair));
+  dict.Set(kKeyPair, BytesToEncodedString(key_pair));
 
-  dict.SetStringKey(kSecretKey, EncodeString(secret_key_->key()));
-  dict.SetStringKey(kMetadataEncryptionKey,
-                    BytesToEncodedString(metadata_encryption_key_));
-  dict.SetStringKey(kId, BytesToEncodedString(id_));
-  dict.SetStringKey(kUnencryptedMetadata,
-                    EncodeString(unencrypted_metadata_.SerializeAsString()));
-  dict.SetStringKey(kConsumedSalts, SaltsToString(consumed_salts_));
+  dict.Set(kSecretKey, EncodeString(secret_key_->key()));
+  dict.Set(kMetadataEncryptionKey,
+           BytesToEncodedString(metadata_encryption_key_));
+  dict.Set(kId, BytesToEncodedString(id_));
+  dict.Set(kUnencryptedMetadata,
+           EncodeString(unencrypted_metadata_.SerializeAsString()));
+  dict.Set(kConsumedSalts, SaltsToString(consumed_salts_));
 
   return dict;
 }
 
 absl::optional<NearbySharePrivateCertificate>
-NearbySharePrivateCertificate::FromDictionary(const base::Value& dict) {
+NearbySharePrivateCertificate::FromDictionary(const base::Value::Dict& dict) {
   absl::optional<int> int_opt;
   const std::string* str_ptr;
   absl::optional<std::string> str_opt;
   absl::optional<base::Time> time_opt;
   absl::optional<std::vector<uint8_t>> bytes_opt;
 
-  int_opt = dict.FindIntPath(kVisibility);
+  int_opt = dict.FindInt(kVisibility);
   if (!int_opt)
     return absl::nullopt;
 
   nearby_share::mojom::Visibility visibility =
       static_cast<nearby_share::mojom::Visibility>(*int_opt);
 
-  time_opt = base::ValueToTime(dict.FindPath(kNotBefore));
+  time_opt = base::ValueToTime(dict.Find(kNotBefore));
   if (!time_opt)
     return absl::nullopt;
 
   base::Time not_before = *time_opt;
 
-  time_opt = base::ValueToTime(dict.FindPath(kNotAfter));
+  time_opt = base::ValueToTime(dict.Find(kNotAfter));
   if (!time_opt)
     return absl::nullopt;
 
   base::Time not_after = *time_opt;
 
-  bytes_opt = EncodedStringToBytes(dict.FindStringPath(kKeyPair));
+  bytes_opt = EncodedStringToBytes(dict.FindString(kKeyPair));
   if (!bytes_opt)
     return absl::nullopt;
 
   std::unique_ptr<crypto::ECPrivateKey> key_pair =
       crypto::ECPrivateKey::CreateFromPrivateKeyInfo(*bytes_opt);
 
-  str_opt = DecodeString(dict.FindStringPath(kSecretKey));
+  str_opt = DecodeString(dict.FindString(kSecretKey));
   if (!str_opt)
     return absl::nullopt;
 
@@ -391,19 +391,19 @@
       crypto::SymmetricKey::Import(crypto::SymmetricKey::Algorithm::AES,
                                    *str_opt);
 
-  bytes_opt = EncodedStringToBytes(dict.FindStringPath(kMetadataEncryptionKey));
+  bytes_opt = EncodedStringToBytes(dict.FindString(kMetadataEncryptionKey));
   if (!bytes_opt)
     return absl::nullopt;
 
   std::vector<uint8_t> metadata_encryption_key = *bytes_opt;
 
-  bytes_opt = EncodedStringToBytes(dict.FindStringPath(kId));
+  bytes_opt = EncodedStringToBytes(dict.FindString(kId));
   if (!bytes_opt)
     return absl::nullopt;
 
   std::vector<uint8_t> id = *bytes_opt;
 
-  str_opt = DecodeString(dict.FindStringPath(kUnencryptedMetadata));
+  str_opt = DecodeString(dict.FindString(kUnencryptedMetadata));
   if (!str_opt)
     return absl::nullopt;
 
@@ -411,7 +411,7 @@
   if (!unencrypted_metadata.ParseFromString(*str_opt))
     return absl::nullopt;
 
-  str_ptr = dict.FindStringPath(kConsumedSalts);
+  str_ptr = dict.FindString(kConsumedSalts);
   if (!str_ptr)
     return absl::nullopt;
 
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h
index c831335..82ad825 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h
@@ -36,7 +36,7 @@
   // Inverse operation of ToDictionary(). Returns absl::nullopt if the
   // conversion is not successful
   static absl::optional<NearbySharePrivateCertificate> FromDictionary(
-      const base::Value& dict);
+      const base::Value::Dict& dict);
 
   // Generates a random EC key pair, secret key, and metadata encryption
   // key. Derives the certificate ID from the secret key. Derives the
@@ -101,7 +101,7 @@
 
   // Converts this private certificate to a dictionary value for storage
   // in Prefs.
-  base::Value ToDictionary() const;
+  base::Value::Dict ToDictionary() const;
 
   // For testing only.
   base::queue<std::vector<uint8_t>>& next_salts_for_testing() {
diff --git a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.cc b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.cc
index 2d8358f..68d00f4 100644
--- a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.cc
+++ b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.cc
@@ -398,12 +398,12 @@
   if (new_allowlist == GetAllowedContacts())
     return false;
 
-  base::Value allowlist_value(base::Value::Type::LIST);
+  base::Value::List allowlist_value;
   for (const std::string& id : new_allowlist) {
     allowlist_value.Append(id);
   }
-  pref_service_->Set(prefs::kNearbySharingAllowedContactsPrefName,
-                     std::move(allowlist_value));
+  pref_service_->SetList(prefs::kNearbySharingAllowedContactsPrefName,
+                         std::move(allowlist_value));
 
   return true;
 }
diff --git a/chrome/browser/nearby_sharing/logging/proto_to_dictionary_conversion.cc b/chrome/browser/nearby_sharing/logging/proto_to_dictionary_conversion.cc
index fdfc34b6..48651254 100644
--- a/chrome/browser/nearby_sharing/logging/proto_to_dictionary_conversion.cc
+++ b/chrome/browser/nearby_sharing/logging/proto_to_dictionary_conversion.cc
@@ -26,184 +26,175 @@
 }
 }  // namespace
 
-base::Value ListPublicCertificatesRequestToReadableDictionary(
+base::Value::Dict ListPublicCertificatesRequestToReadableDictionary(
     const nearbyshare::proto::ListPublicCertificatesRequest& request) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("parent", request.parent());
-  dict.SetIntKey("page_size", request.page_size());
-  dict.SetStringKey("page_token", request.page_token());
+  base::Value::Dict dict;
+  dict.Set("parent", request.parent());
+  dict.Set("page_size", request.page_size());
+  dict.Set("page_token", request.page_token());
 
-  base::Value secret_ids_list(base::Value::Type::LIST);
+  base::Value::List secret_ids_list;
   for (const auto& secret_id : request.secret_ids()) {
     secret_ids_list.Append(TruncateString(Encode(secret_id)));
   }
-  dict.SetKey("secret_ids", std::move(secret_ids_list));
+  dict.Set("secret_ids", std::move(secret_ids_list));
   return dict;
 }
 
-base::Value ListPublicCertificatesResponseToReadableDictionary(
+base::Value::Dict ListPublicCertificatesResponseToReadableDictionary(
     const nearbyshare::proto::ListPublicCertificatesResponse& response) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("next_page_token", response.next_page_token());
+  base::Value::Dict dict;
+  dict.Set("next_page_token", response.next_page_token());
 
-  base::Value public_certificates_list(base::Value::Type::LIST);
+  base::Value::List public_certificates_list;
   for (const auto& public_certificate : response.public_certificates()) {
     public_certificates_list.Append(
         PublicCertificateToReadableDictionary(public_certificate));
   }
-  dict.SetKey("public_certificates", std::move(public_certificates_list));
+  dict.Set("public_certificates", std::move(public_certificates_list));
   return dict;
 }
 
-base::Value PublicCertificateToReadableDictionary(
+base::Value::Dict PublicCertificateToReadableDictionary(
     const nearbyshare::proto::PublicCertificate& certificate) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("secret_id",
-                    TruncateString(Encode(certificate.secret_id())));
-  dict.SetStringKey("secret_key",
-                    TruncateString(Encode(certificate.secret_key())));
-  dict.SetStringKey("public_key",
-                    TruncateString(Encode(certificate.public_key())));
-  dict.SetKey("start_time",
-              TimestampToReadableDictionary(certificate.start_time()));
-  dict.SetKey("end_time",
-              TimestampToReadableDictionary(certificate.end_time()));
-  dict.SetBoolKey("for_selected_contacts", certificate.for_selected_contacts());
-  dict.SetStringKey(
-      "metadata_encryption_key",
-      TruncateString(Encode(certificate.metadata_encryption_key())));
-  dict.SetStringKey(
-      "encrypted_metadata_bytes",
-      TruncateString(Encode(certificate.encrypted_metadata_bytes())));
-  dict.SetStringKey(
-      "metadata_encryption_key_tag",
-      TruncateString(Encode(certificate.metadata_encryption_key_tag())));
-  dict.SetBoolKey("for_self_share", certificate.for_self_share());
+  base::Value::Dict dict;
+  dict.Set("secret_id", TruncateString(Encode(certificate.secret_id())));
+  dict.Set("secret_key", TruncateString(Encode(certificate.secret_key())));
+  dict.Set("public_key", TruncateString(Encode(certificate.public_key())));
+  dict.Set("start_time",
+           TimestampToReadableDictionary(certificate.start_time()));
+  dict.Set("end_time", TimestampToReadableDictionary(certificate.end_time()));
+  dict.Set("for_selected_contacts", certificate.for_selected_contacts());
+  dict.Set("metadata_encryption_key",
+           TruncateString(Encode(certificate.metadata_encryption_key())));
+  dict.Set("encrypted_metadata_bytes",
+           TruncateString(Encode(certificate.encrypted_metadata_bytes())));
+  dict.Set("metadata_encryption_key_tag",
+           TruncateString(Encode(certificate.metadata_encryption_key_tag())));
+  dict.Set("for_self_share", certificate.for_self_share());
   return dict;
 }
 
-base::Value TimestampToReadableDictionary(
+base::Value::Dict TimestampToReadableDictionary(
     const nearbyshare::proto::Timestamp& timestamp) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("seconds", base::NumberToString(timestamp.seconds()));
-  dict.SetStringKey("nanos", base::NumberToString(timestamp.nanos()));
+  base::Value::Dict dict;
+  dict.Set("seconds", base::NumberToString(timestamp.seconds()));
+  dict.Set("nanos", base::NumberToString(timestamp.nanos()));
   return dict;
 }
 
-base::Value ListContactPeopleRequestToReadableDictionary(
+base::Value::Dict ListContactPeopleRequestToReadableDictionary(
     const nearbyshare::proto::ListContactPeopleRequest& request) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetIntKey("page_size", request.page_size());
-  dict.SetStringKey("page_token", request.page_token());
+  base::Value::Dict dict;
+  dict.Set("page_size", request.page_size());
+  dict.Set("page_token", request.page_token());
   return dict;
 }
 
-base::Value ListContactPeopleResponseToReadableDictionary(
+base::Value::Dict ListContactPeopleResponseToReadableDictionary(
     const nearbyshare::proto::ListContactPeopleResponse& response) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  base::Value contact_records_list(base::Value::Type::LIST);
+  base::Value::Dict dict;
+  base::Value::List contact_records_list;
   for (const auto& contact_record : response.contact_records()) {
     contact_records_list.Append(
         ContactRecordToReadableDictionary(contact_record));
   }
-  dict.SetKey("contact_records", std::move(contact_records_list));
-  dict.SetStringKey("next_page_token", response.next_page_token());
+  dict.Set("contact_records", std::move(contact_records_list));
+  dict.Set("next_page_token", response.next_page_token());
   return dict;
 }
 
-base::Value ContactRecordToReadableDictionary(
+base::Value::Dict ContactRecordToReadableDictionary(
     const nearbyshare::proto::ContactRecord& contact_record) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("id", contact_record.id());
-  dict.SetStringKey("person_name", contact_record.person_name());
-  dict.SetStringKey("image_url", contact_record.image_url());
-  base::Value identifiers_list(base::Value::Type::LIST);
+  base::Value::Dict dict;
+  dict.Set("id", contact_record.id());
+  dict.Set("person_name", contact_record.person_name());
+  dict.Set("image_url", contact_record.image_url());
+  base::Value::List identifiers_list;
   for (const auto& identifier : contact_record.identifiers()) {
     identifiers_list.Append(IdentifierToReadableDictionary(identifier));
   }
-  dict.SetKey("identifiers", std::move(identifiers_list));
+  dict.Set("identifiers", std::move(identifiers_list));
   return dict;
 }
 
-base::Value IdentifierToReadableDictionary(
+base::Value::Dict IdentifierToReadableDictionary(
     const nearbyshare::proto::Contact::Identifier& identifier) {
-  base::Value dict(base::Value::Type::DICTIONARY);
+  base::Value::Dict dict;
   if (!identifier.obfuscated_gaia().empty()) {
-    dict.SetStringKey("identifier", identifier.obfuscated_gaia());
+    dict.Set("identifier", identifier.obfuscated_gaia());
   } else if (!identifier.phone_number().empty()) {
-    dict.SetStringKey("identifier", identifier.phone_number());
+    dict.Set("identifier", identifier.phone_number());
   } else if (!identifier.account_name().empty()) {
-    dict.SetStringKey("identifier", identifier.account_name());
+    dict.Set("identifier", identifier.account_name());
   }
   return dict;
 }
 
-base::Value UpdateDeviceRequestToReadableDictionary(
+base::Value::Dict UpdateDeviceRequestToReadableDictionary(
     const nearbyshare::proto::UpdateDeviceRequest& request) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetKey("device", DeviceToReadableDictionary(request.device()));
-  dict.SetKey("update_mask",
-              FieldMaskToReadableDictionary(request.update_mask()));
+  base::Value::Dict dict;
+  dict.Set("device", DeviceToReadableDictionary(request.device()));
+  dict.Set("update_mask", FieldMaskToReadableDictionary(request.update_mask()));
   return dict;
 }
 
-base::Value DeviceToReadableDictionary(
+base::Value::Dict DeviceToReadableDictionary(
     const nearbyshare::proto::Device& device) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("name", device.name());
-  dict.SetStringKey("display_name", device.display_name());
-  base::Value contacts_list(base::Value::Type::LIST);
+  base::Value::Dict dict;
+  dict.Set("name", device.name());
+  dict.Set("display_name", device.display_name());
+  base::Value::List contacts_list;
   for (const auto& contact : device.contacts()) {
     contacts_list.Append(ContactToReadableDictionary(contact));
   }
-  dict.SetKey("contacts", std::move(contacts_list));
-  base::Value public_certificates_list(base::Value::Type::LIST);
+  dict.Set("contacts", std::move(contacts_list));
+  base::Value::List public_certificates_list;
   for (const auto& certificate : device.public_certificates()) {
     public_certificates_list.Append(
         PublicCertificateToReadableDictionary(certificate));
   }
-  dict.SetKey("public_certificates", std::move(public_certificates_list));
+  dict.Set("public_certificates", std::move(public_certificates_list));
   return dict;
 }
 
-base::Value ContactToReadableDictionary(
+base::Value::Dict ContactToReadableDictionary(
     const nearbyshare::proto::Contact& contact) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetKey("identifier",
-              IdentifierToReadableDictionary(contact.identifier()));
-  dict.SetBoolKey("is_selected", contact.is_selected());
+  base::Value::Dict dict;
+  dict.Set("identifier", IdentifierToReadableDictionary(contact.identifier()));
+  dict.Set("is_selected", contact.is_selected());
   return dict;
 }
 
-base::Value FieldMaskToReadableDictionary(
+base::Value::Dict FieldMaskToReadableDictionary(
     const nearbyshare::proto::FieldMask& mask) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  base::Value paths_list(base::Value::Type::LIST);
+  base::Value::Dict dict;
+  base::Value::List paths_list;
   for (const auto& path : mask.paths()) {
     paths_list.Append(path);
   }
-  dict.SetKey("paths", std::move(paths_list));
+  dict.Set("paths", std::move(paths_list));
   return dict;
 }
 
-base::Value UpdateDeviceResponseToReadableDictionary(
+base::Value::Dict UpdateDeviceResponseToReadableDictionary(
     const nearbyshare::proto::UpdateDeviceResponse& response) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetKey("device", DeviceToReadableDictionary(response.device()));
-  dict.SetStringKey("person_name", response.person_name());
-  dict.SetStringKey("image_url", response.image_url());
-  dict.SetStringKey("image_token", response.image_token());
+  base::Value::Dict dict;
+  dict.Set("device", DeviceToReadableDictionary(response.device()));
+  dict.Set("person_name", response.person_name());
+  dict.Set("image_url", response.image_url());
+  dict.Set("image_token", response.image_token());
   return dict;
 }
 
-base::Value EncryptedMetadataToReadableDictionary(
+base::Value::Dict EncryptedMetadataToReadableDictionary(
     const nearbyshare::proto::EncryptedMetadata& data) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("device_name", data.device_name());
-  dict.SetStringKey("full_name", data.full_name());
-  dict.SetStringKey("icon_url", data.icon_url());
-  dict.SetStringKey("bluetooth_mac_address",
-                    TruncateString(Encode(data.bluetooth_mac_address())));
-  dict.SetStringKey("obfuscated_gaia_id", data.obfuscated_gaia_id());
+  base::Value::Dict dict;
+  dict.Set("device_name", data.device_name());
+  dict.Set("full_name", data.full_name());
+  dict.Set("icon_url", data.icon_url());
+  dict.Set("bluetooth_mac_address",
+           TruncateString(Encode(data.bluetooth_mac_address())));
+  dict.Set("obfuscated_gaia_id", data.obfuscated_gaia_id());
   return dict;
 }
diff --git a/chrome/browser/nearby_sharing/logging/proto_to_dictionary_conversion.h b/chrome/browser/nearby_sharing/logging/proto_to_dictionary_conversion.h
index 9ec02cdd..11a6331 100644
--- a/chrome/browser/nearby_sharing/logging/proto_to_dictionary_conversion.h
+++ b/chrome/browser/nearby_sharing/logging/proto_to_dictionary_conversion.h
@@ -15,33 +15,33 @@
 #include "chrome/browser/nearby_sharing/proto/timestamp.pb.h"
 
 // Converts Nearby Share protos to readable, JSON-style dictionaries.
-base::Value ListPublicCertificatesRequestToReadableDictionary(
+base::Value::Dict ListPublicCertificatesRequestToReadableDictionary(
     const nearbyshare::proto::ListPublicCertificatesRequest& request);
-base::Value ListPublicCertificatesResponseToReadableDictionary(
+base::Value::Dict ListPublicCertificatesResponseToReadableDictionary(
     const nearbyshare::proto::ListPublicCertificatesResponse& response);
-base::Value PublicCertificateToReadableDictionary(
+base::Value::Dict PublicCertificateToReadableDictionary(
     const nearbyshare::proto::PublicCertificate& certificate);
-base::Value TimestampToReadableDictionary(
+base::Value::Dict TimestampToReadableDictionary(
     const nearbyshare::proto::Timestamp& timestamp);
-base::Value ListContactPeopleRequestToReadableDictionary(
+base::Value::Dict ListContactPeopleRequestToReadableDictionary(
     const nearbyshare::proto::ListContactPeopleRequest& request);
-base::Value ListContactPeopleResponseToReadableDictionary(
+base::Value::Dict ListContactPeopleResponseToReadableDictionary(
     const nearbyshare::proto::ListContactPeopleResponse& response);
-base::Value ContactRecordToReadableDictionary(
+base::Value::Dict ContactRecordToReadableDictionary(
     const nearbyshare::proto::ContactRecord& contact_record);
-base::Value IdentifierToReadableDictionary(
+base::Value::Dict IdentifierToReadableDictionary(
     const nearbyshare::proto::Contact::Identifier& identifier);
-base::Value UpdateDeviceRequestToReadableDictionary(
+base::Value::Dict UpdateDeviceRequestToReadableDictionary(
     const nearbyshare::proto::UpdateDeviceRequest& request);
-base::Value DeviceToReadableDictionary(
+base::Value::Dict DeviceToReadableDictionary(
     const nearbyshare::proto::Device& device);
-base::Value ContactToReadableDictionary(
+base::Value::Dict ContactToReadableDictionary(
     const nearbyshare::proto::Contact& contact);
-base::Value FieldMaskToReadableDictionary(
+base::Value::Dict FieldMaskToReadableDictionary(
     const nearbyshare::proto::FieldMask& mask);
-base::Value UpdateDeviceResponseToReadableDictionary(
+base::Value::Dict UpdateDeviceResponseToReadableDictionary(
     const nearbyshare::proto::UpdateDeviceResponse& response);
-base::Value EncryptedMetadataToReadableDictionary(
+base::Value::Dict EncryptedMetadataToReadableDictionary(
     const nearbyshare::proto::EncryptedMetadata& data);
 
 #endif  // CHROME_BROWSER_NEARBY_SHARING_LOGGING_PROTO_TO_DICTIONARY_CONVERSION_H_
diff --git a/chrome/browser/nearby_sharing/network_traversal_ice_config_fetcher.cc b/chrome/browser/nearby_sharing/network_traversal_ice_config_fetcher.cc
index 409e1067..786ca46 100644
--- a/chrome/browser/nearby_sharing/network_traversal_ice_config_fetcher.cc
+++ b/chrome/browser/nearby_sharing/network_traversal_ice_config_fetcher.cc
@@ -98,17 +98,19 @@
   if (!response)
     return ice_servers;
 
-  base::Value* ice_servers_json = response->FindListKey("iceServers");
+  base::Value::List* ice_servers_json =
+      response->GetDict().FindList("iceServers");
   if (!ice_servers_json)
     return ice_servers;
 
-  for (base::Value& server : ice_servers_json->GetList()) {
-    const base::Value* urls_json = server.FindListKey("urls");
+  for (base::Value& server : *ice_servers_json) {
+    base::Value::Dict& server_dict = server.GetDict();
+    const base::Value::List* urls_json = server_dict.FindList("urls");
     if (!urls_json)
       continue;
 
     std::vector<GURL> urls;
-    for (const base::Value& url_json : urls_json->GetList()) {
+    for (const base::Value& url_json : *urls_json) {
       const std::string* url = url_json.GetIfString();
       if (!url)
         continue;
@@ -122,11 +124,11 @@
     sharing::mojom::IceServerPtr ice_server(sharing::mojom::IceServer::New());
     ice_server->urls = std::move(urls);
 
-    std::string* retrieved_username = server.FindStringKey("username");
+    std::string* retrieved_username = server_dict.FindString("username");
     if (retrieved_username)
       ice_server->username.emplace(std::move(*retrieved_username));
 
-    std::string* retrieved_credential = server.FindStringKey("credential");
+    std::string* retrieved_credential = server_dict.FindString("credential");
     if (retrieved_credential)
       ice_server->credential.emplace(std::move(*retrieved_credential));
 
diff --git a/chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_base.cc b/chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_base.cc
index 16ee8b42..517a404 100644
--- a/chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_base.cc
+++ b/chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_base.cc
@@ -176,38 +176,31 @@
 
 void NearbyShareSchedulerBase::SetLastAttemptTime(
     base::Time last_attempt_time) {
-  DictionaryPrefUpdate(pref_service_, pref_name_)
-      .Get()
-      ->SetKey(kLastAttemptTimeKeyName, base::TimeToValue(last_attempt_time));
+  ScopedDictPrefUpdate(pref_service_, pref_name_)
+      ->Set(kLastAttemptTimeKeyName, base::TimeToValue(last_attempt_time));
 }
 
 void NearbyShareSchedulerBase::SetLastSuccessTime(
     base::Time last_success_time) {
-  DictionaryPrefUpdate(pref_service_, pref_name_)
-      .Get()
-      ->SetKey(kLastSuccessTimeKeyName, base::TimeToValue(last_success_time));
+  ScopedDictPrefUpdate(pref_service_, pref_name_)
+      ->Set(kLastSuccessTimeKeyName, base::TimeToValue(last_success_time));
 }
 
 void NearbyShareSchedulerBase::SetNumConsecutiveFailures(size_t num_failures) {
-  DictionaryPrefUpdate(pref_service_, pref_name_)
-      .Get()
-      ->SetStringKey(kNumConsecutiveFailuresKeyName,
-                     base::NumberToString(num_failures));
+  ScopedDictPrefUpdate(pref_service_, pref_name_)
+      ->Set(kNumConsecutiveFailuresKeyName, base::NumberToString(num_failures));
 }
 
 void NearbyShareSchedulerBase::SetHasPendingImmediateRequest(
     bool has_pending_immediate_request) {
-  DictionaryPrefUpdate(pref_service_, pref_name_)
-      .Get()
-      ->SetBoolKey(kHasPendingImmediateRequestKeyName,
-                   has_pending_immediate_request);
+  ScopedDictPrefUpdate(pref_service_, pref_name_)
+      ->Set(kHasPendingImmediateRequestKeyName, has_pending_immediate_request);
 }
 
 void NearbyShareSchedulerBase::SetIsWaitingForResult(
     bool is_waiting_for_result) {
-  DictionaryPrefUpdate(pref_service_, pref_name_)
-      .Get()
-      ->SetBoolKey(kIsWaitingForResultKeyName, is_waiting_for_result);
+  ScopedDictPrefUpdate(pref_service_, pref_name_)
+      ->Set(kIsWaitingForResultKeyName, is_waiting_for_result);
 }
 
 void NearbyShareSchedulerBase::InitializePersistedRequest() {
diff --git a/chrome/browser/net/profile_network_context_service_browsertest.cc b/chrome/browser/net/profile_network_context_service_browsertest.cc
index f55005b..1894c08 100644
--- a/chrome/browser/net/profile_network_context_service_browsertest.cc
+++ b/chrome/browser/net/profile_network_context_service_browsertest.cc
@@ -710,7 +710,7 @@
   (async () => {
     try {
       await fetch("/issue", {trustToken: {type: 'token-request'}});
-      return await document.hasPrivateStateToken($1);
+      return await document.hasPrivateToken($1, 'private-state-token');
     } catch {
       return false;
     }
diff --git a/chrome/browser/new_tab_page/modules/new_tab_page_modules.cc b/chrome/browser/new_tab_page/modules/new_tab_page_modules.cc
new file mode 100644
index 0000000..da952ad
--- /dev/null
+++ b/chrome/browser/new_tab_page/modules/new_tab_page_modules.cc
@@ -0,0 +1,69 @@
+// 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.
+
+#include "chrome/browser/new_tab_page/modules/new_tab_page_modules.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/new_tab_page/new_tab_page_util.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/search/ntp_features.h"
+
+namespace ntp {
+
+const std::vector<std::pair<const std::string, int>> MakeModuleIdNames(
+    bool drive_module_enabled) {
+  std::vector<std::pair<const std::string, int>> details;
+
+  if (IsRecipeTasksModuleEnabled()) {
+    std::vector<std::string> splitExperimentGroup = base::SplitString(
+        base::GetFieldTrialParamValueByFeature(
+            ntp_features::kNtpRecipeTasksModule,
+            ntp_features::kNtpRecipeTasksModuleExperimentGroupParam),
+        "-", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+    bool recipes_historical_experiment_enabled =
+        !splitExperimentGroup.empty() &&
+        splitExperimentGroup[0] == "historical";
+
+    details.emplace_back("recipe_tasks",
+                         recipes_historical_experiment_enabled
+                             ? IDS_NTP_MODULES_RECIPE_VIEWED_TASKS_SENTENCE
+                             : IDS_NTP_MODULES_RECIPE_TASKS_SENTENCE);
+  }
+
+  if (IsCartModuleEnabled()) {
+    details.emplace_back("chrome_cart", IDS_NTP_MODULES_CART_SENTENCE);
+  }
+
+  if (drive_module_enabled) {
+    details.emplace_back("drive", IDS_NTP_MODULES_DRIVE_SENTENCE);
+  }
+
+  if (base::FeatureList::IsEnabled(ntp_features::kNtpPhotosModule)) {
+    details.emplace_back("photos", IDS_NTP_MODULES_PHOTOS_MEMORIES_TITLE);
+  }
+
+  if (base::FeatureList::IsEnabled(ntp_features::kNtpFeedModule)) {
+    details.emplace_back("feed", IDS_NTP_MODULES_FEED_TITLE);
+  }
+
+#if !defined(OFFICIAL_BUILD)
+  if (base::FeatureList::IsEnabled(ntp_features::kNtpDummyModules)) {
+    details.emplace_back("dummy", IDS_NTP_MODULES_DUMMY_TITLE);
+
+    for (int i = 2; i <= 12; i++) {
+      details.emplace_back(base::StringPrintf("dummy%d", i),
+                           IDS_NTP_MODULES_DUMMY2_TITLE);
+    }
+  }
+#endif
+
+  return details;
+}
+
+}  // namespace ntp
diff --git a/chrome/browser/new_tab_page/modules/new_tab_page_modules.h b/chrome/browser/new_tab_page/modules/new_tab_page_modules.h
new file mode 100644
index 0000000..97e6d78e
--- /dev/null
+++ b/chrome/browser/new_tab_page/modules/new_tab_page_modules.h
@@ -0,0 +1,19 @@
+// 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 CHROME_BROWSER_NEW_TAB_PAGE_MODULES_NEW_TAB_PAGE_MODULES_H_
+#define CHROME_BROWSER_NEW_TAB_PAGE_MODULES_NEW_TAB_PAGE_MODULES_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace ntp {
+
+const std::vector<std::pair<const std::string, int>> MakeModuleIdNames(
+    bool drive_module_enabled);
+
+}  // namespace ntp
+
+#endif  // CHROME_BROWSER_NEW_TAB_PAGE_MODULES_NEW_TAB_PAGE_MODULES_H_
diff --git a/chrome/browser/new_tab_page/modules/new_tab_page_modules_unittest.cc b/chrome/browser/new_tab_page/modules/new_tab_page_modules_unittest.cc
new file mode 100644
index 0000000..d3dac676
--- /dev/null
+++ b/chrome/browser/new_tab_page/modules/new_tab_page_modules_unittest.cc
@@ -0,0 +1,52 @@
+// 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.
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/new_tab_page/modules/new_tab_page_modules.h"
+#include "components/search/ntp_features.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ntp {
+
+TEST(NewTabPageModulesTest, MakeModuleIdNames_NoDriveModule) {
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures(
+      /*enabled_features=*/{ntp_features::kNtpRecipeTasksModule},
+      /*disabled_features=*/{});
+
+  const std::vector<std::pair<const std::string, int>> module_id_names =
+      MakeModuleIdNames(false);
+  ASSERT_EQ(1u, module_id_names.size());
+}
+
+TEST(NewTabPageModulesTest, MakeModuleIdNames_WithDriveModule) {
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures(
+      /*enabled_features=*/{ntp_features::kNtpRecipeTasksModule,
+                            ntp_features::kNtpDriveModule},
+      /*disabled_features=*/{});
+
+  const std::vector<std::pair<const std::string, int>> module_id_names =
+      MakeModuleIdNames(true);
+  ASSERT_EQ(2u, module_id_names.size());
+}
+
+#if !defined(OFFICIAL_BUILD)
+TEST(NewTabPageModulesTest, MakeModuleIdNames_DummyModules) {
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures(
+      /*enabled_features=*/{ntp_features::kNtpDummyModules},
+      /*disabled_features=*/{});
+
+  const std::vector<std::pair<const std::string, int>> module_id_names =
+      MakeModuleIdNames(false);
+  ASSERT_EQ(12u, module_id_names.size());
+}
+#endif
+
+}  // namespace ntp
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 6a8366a..f597d61 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -3590,16 +3590,20 @@
 // Flaky in nearly all configurations; see https://crbug.com/856169.
 IN_PROC_BROWSER_TEST_F(PDFExtensionHitTestTest, DISABLED_MouseLeave) {
   // Load page with embedded PDF and make sure it succeeds.
-  WebContents* guest_contents = LoadPdfGetGuestContents(
+  MimeHandlerViewGuest* guest = LoadPdfGetMimeHandlerView(
       embedded_test_server()->GetURL("/pdf/pdf_embed.html"));
-  ASSERT_TRUE(guest_contents);
+  ASSERT_TRUE(guest);
+
+  content::RenderFrameHost* guest_mainframe = guest->GetGuestMainFrame();
+  ASSERT_TRUE(guest_mainframe);
+  content::WaitForHitTestData(guest_mainframe);
 
   gfx::Point point_in_parent(250, 25);
   gfx::Point point_in_pdf(250, 250);
 
   // Inject script to count MouseLeaves in the PDF.
   ASSERT_TRUE(content::ExecuteScript(
-      guest_contents,
+      guest_mainframe,
       "var enter_count = 0;\n"
       "var leave_count = 0;\n"
       "document.addEventListener('mouseenter', function (){\n"
@@ -3624,24 +3628,28 @@
   int leave_count = 0;
   do {
     ASSERT_TRUE(ExecuteScriptAndExtractInt(
-        guest_contents, "window.domAutomationController.send(leave_count);",
+        guest_mainframe, "window.domAutomationController.send(leave_count);",
         &leave_count));
   } while (!leave_count);
   int enter_count = 0;
   ASSERT_TRUE(ExecuteScriptAndExtractInt(
-      guest_contents, "window.domAutomationController.send(enter_count);",
+      guest_mainframe, "window.domAutomationController.send(enter_count);",
       &enter_count));
   EXPECT_EQ(1, enter_count);
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionHitTestTest, ContextMenuCoordinates) {
   // Load page with embedded PDF and make sure it succeeds.
-  WebContents* guest_contents = LoadPdfGetGuestContents(
+  MimeHandlerViewGuest* guest = LoadPdfGetMimeHandlerView(
       embedded_test_server()->GetURL("/pdf/pdf_embed.html"));
-  ASSERT_TRUE(guest_contents);
+  ASSERT_TRUE(guest);
+
+  content::RenderFrameHost* guest_mainframe = guest->GetGuestMainFrame();
+  ASSERT_TRUE(guest_mainframe);
+  content::WaitForHitTestData(guest_mainframe);
 
   // Observe context menu IPC.
-  content::RenderFrameHost* plugin_frame = GetPluginFrame(guest_contents);
+  content::RenderFrameHost* plugin_frame = GetPluginFrame(guest);
   content::ContextMenuInterceptor context_menu_interceptor(plugin_frame);
 
   ContextMenuWaiter menu_observer;
diff --git a/chrome/browser/policy/BUILD.gn b/chrome/browser/policy/BUILD.gn
index ee3f8eb9..251944a 100644
--- a/chrome/browser/policy/BUILD.gn
+++ b/chrome/browser/policy/BUILD.gn
@@ -91,7 +91,7 @@
       "//chromeos/ash/components/install_attributes",
       "//chromeos/ash/components/network",
       "//chromeos/ash/components/settings",
-      "//chromeos/system:system",
+      "//chromeos/ash/components/system",
       "//components/policy/core/common:common_constants",
       "//components/user_manager:user_manager",
       "//dbus",
@@ -453,7 +453,7 @@
     deps += [
       "//chrome/browser/ash",
       "//chrome/browser/ash:test_support",
-      "//chromeos/system",
+      "//chromeos/ash/components/system",
     ]
   }
 }
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 046ecfe..699c233 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -1809,6 +1809,11 @@
     prefs::kCloudApAuthEnabled,
     base::Value::Type::INTEGER },
 #endif  // BUILDFLAG(IS_WIN)
+#if BUILDFLAG(ENABLE_PPAPI)
+  { key::kUseMojoVideoDecoderForPepperAllowed,
+    policy::policy_prefs::kUseMojoVideoDecoderForPepperAllowed,
+    base::Value::Type::BOOLEAN },
+#endif
 };
 // clang-format on
 
diff --git a/chrome/browser/policy/device_management_service_configuration.cc b/chrome/browser/policy/device_management_service_configuration.cc
index 9f1274e..45935cb 100644
--- a/chrome/browser/policy/device_management_service_configuration.cc
+++ b/chrome/browser/policy/device_management_service_configuration.cc
@@ -16,7 +16,7 @@
 #include "content/public/browser/browser_context.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #endif
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) ||           \
diff --git a/chrome/browser/policy/networking/device_network_configuration_updater_ash.cc b/chrome/browser/policy/networking/device_network_configuration_updater_ash.cc
index 38b5f03..1cf9897 100644
--- a/chrome/browser/policy/networking/device_network_configuration_updater_ash.cc
+++ b/chrome/browser/policy/networking/device_network_configuration_updater_ash.cc
@@ -20,9 +20,9 @@
 #include "chromeos/ash/components/network/network_device_handler.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/cros_settings_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/components/onc/onc_parsed_certificates.h"
 #include "chromeos/components/onc/onc_utils.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/policy/policy_constants.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/cert/x509_certificate.h"
diff --git a/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc b/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc
index 4f1655b..8c195c9 100644
--- a/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc
+++ b/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc
@@ -25,12 +25,12 @@
 #include "chromeos/ash/components/network/mock_managed_network_configuration_handler.h"
 #include "chromeos/ash/components/network/onc/onc_certificate_importer.h"
 #include "chromeos/ash/components/network/policy_certificate_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/components/onc/certificate_scope.h"
 #include "chromeos/components/onc/onc_parsed_certificates.h"
 #include "chromeos/components/onc/onc_test_utils.h"
 #include "chromeos/components/onc/onc_utils.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/account_id/account_id.h"
 #include "components/onc/onc_constants.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
diff --git a/chrome/browser/resources/extensions/detail_view.html b/chrome/browser/resources/extensions/detail_view.html
index 731ff43..2d6a27a 100644
--- a/chrome/browser/resources/extensions/detail_view.html
+++ b/chrome/browser/resources/extensions/detail_view.html
@@ -403,6 +403,9 @@
         </div>
       </div>
     </template>
+    <cr-link-row class="hr"
+        id="siteSettings" label="$i18n{siteSettings}"
+        on-click="onSiteSettingsClick_" external></cr-link-row>
     <template is="dom-if" if="[[shouldShowOptionsSection_(data.*)]]">
       <div id="options-section">
         <template is="dom-if"
diff --git a/chrome/browser/resources/extensions/detail_view.ts b/chrome/browser/resources/extensions/detail_view.ts
index 508c248b..c2acfcb 100644
--- a/chrome/browser/resources/extensions/detail_view.ts
+++ b/chrome/browser/resources/extensions/detail_view.ts
@@ -283,6 +283,12 @@
     this.delegate.openUrl(this.data.manifestHomePageUrl);
   }
 
+  private onSiteSettingsClick_() {
+    this.delegate.openUrl(
+        `chrome://settings/content/siteDetails?site=chrome-extension://${
+            this.data.id}`);
+  }
+
   private onViewInStoreTap_() {
     this.delegate.openUrl(this.data.webStoreUrl);
   }
diff --git a/chrome/browser/resources/new_tab_page/customize_modules.ts b/chrome/browser/resources/new_tab_page/customize_modules.ts
index dba822b8..3b688f4 100644
--- a/chrome/browser/resources/new_tab_page/customize_modules.ts
+++ b/chrome/browser/resources/new_tab_page/customize_modules.ts
@@ -15,7 +15,7 @@
 import {getTemplate} from './customize_modules.html.js';
 import {I18nMixin, loadTimeData} from './i18n_setup.js';
 import {ChromeCartProxy} from './modules/cart/chrome_cart_proxy.js';
-import {ModuleRegistry} from './modules/module_registry.js';
+import {ModuleIdName} from './new_tab_page.mojom-webui.js';
 import {NewTabPageProxy} from './new_tab_page_proxy.js';
 
 interface ModuleSetting {
@@ -62,11 +62,7 @@
         value: () => loadTimeData.getBoolean('modulesVisibleManagedByPolicy'),
       },
 
-      modules_: {
-        type: Array,
-        value: () => ModuleRegistry.getInstance().getDescriptors().map(
-            d => ({name: d.name, id: d.id, checked: true, hidden: false})),
-      },
+      modules_: Array,
 
       // Discount toggle is a workaround for crbug.com/1199465 and will be
       // removed after module customization is better defined. Please avoid
@@ -88,7 +84,6 @@
   private modules_: ModuleSetting[];
   private discountToggle_: {enabled: boolean, initiallyEnabled: boolean};
   private discountToggleEligible_: boolean;
-
   private setDisabledModulesListenerId_: number|null = null;
 
   override connectedCallback() {
@@ -106,19 +101,28 @@
                     this.set(`modules_.${i}.disabled`, ids.includes(id));
                   });
                 });
-    NewTabPageProxy.getInstance().handler.updateDisabledModules();
 
-    if (this.modules_.some(module => module.id === 'chrome_cart')) {
-      ChromeCartProxy.getHandler().getDiscountToggleVisible().then(
-          ({toggleVisible}) => {
-            this.set('discountToggleEligible_', toggleVisible);
-          });
+    NewTabPageProxy.getInstance().handler.getModulesIdNames().then(({data}) => {
+      this.modules_ = data.map((d: ModuleIdName) => ({
+                                 name: d.name,
+                                 id: d.id,
+                                 checked: true,
+                               } as ModuleSetting));
 
-      ChromeCartProxy.getHandler().getDiscountEnabled().then(({enabled}) => {
-        this.set('discountToggle_.enabled', enabled);
-        this.discountToggle_.initiallyEnabled = enabled;
-      });
-    }
+      NewTabPageProxy.getInstance().handler.updateDisabledModules();
+
+      if (this.modules_.some(module => module.id === 'chrome_cart')) {
+        ChromeCartProxy.getHandler().getDiscountToggleVisible().then(
+            ({toggleVisible}) => {
+              this.set('discountToggleEligible_', toggleVisible);
+            });
+
+        ChromeCartProxy.getHandler().getDiscountEnabled().then(({enabled}) => {
+          this.set('discountToggle_.enabled', enabled);
+          this.discountToggle_.initiallyEnabled = enabled;
+        });
+      }
+    });
   }
 
   override disconnectedCallback() {
diff --git a/chrome/browser/resources/new_tab_page/modules/cart/module.ts b/chrome/browser/resources/new_tab_page/modules/cart/module.ts
index 9fcf264..1fd5b7e 100644
--- a/chrome/browser/resources/new_tab_page/modules/cart/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/cart/module.ts
@@ -578,5 +578,4 @@
 }
 
 export const chromeCartDescriptor: ModuleDescriptor = new ModuleDescriptor(
-    /*id=*/ 'chrome_cart',
-    /*name=*/ loadTimeData.getString('modulesCartSentence'), createCartElement);
+    /*id=*/ 'chrome_cart', createCartElement);
diff --git a/chrome/browser/resources/new_tab_page/modules/cart_v2/module.ts b/chrome/browser/resources/new_tab_page/modules/cart_v2/module.ts
index 327a4a3..9056592 100644
--- a/chrome/browser/resources/new_tab_page/modules/cart_v2/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/cart_v2/module.ts
@@ -386,5 +386,4 @@
 
 export const chromeCartDescriptor: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'chrome_cart',
-    /*name=*/ loadTimeData.getString('modulesCartSentence'),
     /*height*/ ModuleHeight.TALL, createCartElement);
diff --git a/chrome/browser/resources/new_tab_page/modules/drive/module.ts b/chrome/browser/resources/new_tab_page/modules/drive/module.ts
index 372f8f0..3d58f158 100644
--- a/chrome/browser/resources/new_tab_page/modules/drive/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/drive/module.ts
@@ -102,6 +102,4 @@
 }
 
 export const driveDescriptor: ModuleDescriptor = new ModuleDescriptor(
-    /*id=*/ 'drive',
-    /*name=*/ loadTimeData.getString('modulesDriveSentence'),
-    createDriveElement);
+    /*id=*/ 'drive', createDriveElement);
diff --git a/chrome/browser/resources/new_tab_page/modules/drive_v2/module.ts b/chrome/browser/resources/new_tab_page/modules/drive_v2/module.ts
index d47ce39c..c0c17061 100644
--- a/chrome/browser/resources/new_tab_page/modules/drive_v2/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/drive_v2/module.ts
@@ -86,5 +86,4 @@
 
 export const driveDescriptor: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id*/ 'drive',
-    /*name*/ loadTimeData.getString('modulesDriveSentence'),
     /*height*/ ModuleHeight.SHORT, createDriveElement);
diff --git a/chrome/browser/resources/new_tab_page/modules/dummy_v2/module.ts b/chrome/browser/resources/new_tab_page/modules/dummy_v2/module.ts
index c9ac5a0..c3863710 100644
--- a/chrome/browser/resources/new_tab_page/modules/dummy_v2/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/dummy_v2/module.ts
@@ -80,72 +80,60 @@
 
 export const dummyV2Descriptor: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy',
-    /*name=*/ loadTimeData.getString('modulesDummyTitle'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummyTitle')));
 
 export const dummyV2Descriptor02: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy2',
-    /*name=*/ loadTimeData.getString('modulesDummy2Title'),
     /*height*/ ModuleHeight.TALL,
     () => Promise.resolve(createDummyElement('modulesDummy2Title')));
 
 export const dummyV2Descriptor03: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy3',
-    /*name=*/ loadTimeData.getString('modulesDummy3Title'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummy3Title')));
 
 export const dummyV2Descriptor04: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy4',
-    /*name=*/ loadTimeData.getString('modulesDummy4Title'),
     /*height*/ ModuleHeight.TALL,
     () => Promise.resolve(createDummyElement('modulesDummy4Title')));
 
 export const dummyV2Descriptor05: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy5',
-    /*name=*/ loadTimeData.getString('modulesDummy5Title'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummy5Title')));
 
 export const dummyV2Descriptor06: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy6',
-    /*name=*/ loadTimeData.getString('modulesDummy6Title'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummy6Title')));
 
 export const dummyV2Descriptor07: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy7',
-    /*name=*/ loadTimeData.getString('modulesDummy7Title'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummy7Title')));
 
 export const dummyV2Descriptor08: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy8',
-    /*name=*/ loadTimeData.getString('modulesDummy8Title'),
     /*height*/ ModuleHeight.TALL,
     () => Promise.resolve(createDummyElement('modulesDummy8Title')));
 
 export const dummyV2Descriptor09: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy9',
-    /*name=*/ loadTimeData.getString('modulesDummy9Title'),
     /*height*/ ModuleHeight.TALL,
     () => Promise.resolve(createDummyElement('modulesDummy9Title')));
 
 export const dummyV2Descriptor10: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy10',
-    /*name=*/ loadTimeData.getString('modulesDummy10Title'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummy10Title')));
 
 export const dummyV2Descriptor11: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy11',
-    /*name=*/ loadTimeData.getString('modulesDummy11Title'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummy11Title')));
 
 export const dummyV2Descriptor12: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy12',
-    /*name=*/ loadTimeData.getString('modulesDummy12Title'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummy12Title')));
diff --git a/chrome/browser/resources/new_tab_page/modules/feed/module.ts b/chrome/browser/resources/new_tab_page/modules/feed/module.ts
index a115b107..3e8007331 100644
--- a/chrome/browser/resources/new_tab_page/modules/feed/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/feed/module.ts
@@ -10,7 +10,7 @@
 import {DomRepeat, DomRepeatEvent, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {Article} from '../../feed.mojom-webui.js';
-import {I18nMixin, loadTimeData} from '../../i18n_setup.js';
+import {I18nMixin} from '../../i18n_setup.js';
 import {InfoDialogElement} from '../info_dialog.js';
 import {ModuleDescriptor, ModuleDescriptorV2, ModuleHeight} from '../module_descriptor.js';
 
@@ -58,11 +58,9 @@
   return element;
 }
 
+const ID: string = 'feed';
 export const feedDescriptor: ModuleDescriptor = new ModuleDescriptor(
-    /*id=*/ 'feed',
-    /*name=*/ loadTimeData.getString('modulesFeedTitle'), createFeedElement);
-
+    /*id=*/ ID, createFeedElement);
 export const feedV2Descriptor: ModuleDescriptorV2 = new ModuleDescriptorV2(
-    /*id=*/ 'feed',
-    /*name=*/ loadTimeData.getString('modulesFeedTitle'),
+    /*id=*/ ID,
     /*height=*/ ModuleHeight.TALL, createFeedElement);
diff --git a/chrome/browser/resources/new_tab_page/modules/module_descriptor.ts b/chrome/browser/resources/new_tab_page/modules/module_descriptor.ts
index 192efcd..87c08eb 100644
--- a/chrome/browser/resources/new_tab_page/modules/module_descriptor.ts
+++ b/chrome/browser/resources/new_tab_page/modules/module_descriptor.ts
@@ -27,13 +27,10 @@
 
 export class ModuleDescriptor {
   private id_: string;
-  private name_: string;
   private initializeCallback_: InitializeModuleCallback;
 
-  constructor(
-      id: string, name: string, initializeCallback: InitializeModuleCallback) {
+  constructor(id: string, initializeCallback: InitializeModuleCallback) {
     this.id_ = id;
-    this.name_ = name;
     this.initializeCallback_ = initializeCallback;
   }
 
@@ -41,10 +38,6 @@
     return this.id_;
   }
 
-  get name(): string {
-    return this.name_;
-  }
-
   get height(): ModuleHeight {
     return ModuleHeight.DYNAMIC;
   }
@@ -80,9 +73,9 @@
   private height_: ModuleHeight;
 
   constructor(
-      id: string, name: string, height: ModuleHeight,
+      id: string, height: ModuleHeight,
       initializeCallback: InitializeModuleCallbackV2) {
-    super(id, name, initializeCallback);
+    super(id, initializeCallback);
     this.height_ = height;
   }
 
diff --git a/chrome/browser/resources/new_tab_page/modules/module_descriptors.ts b/chrome/browser/resources/new_tab_page/modules/module_descriptors.ts
index 8fb7086e..9bde8271 100644
--- a/chrome/browser/resources/new_tab_page/modules/module_descriptors.ts
+++ b/chrome/browser/resources/new_tab_page/modules/module_descriptors.ts
@@ -17,68 +17,38 @@
 import {dummyV2Descriptor, dummyV2Descriptor02, dummyV2Descriptor03, dummyV2Descriptor04, dummyV2Descriptor05, dummyV2Descriptor06, dummyV2Descriptor07, dummyV2Descriptor08, dummyV2Descriptor09, dummyV2Descriptor10, dummyV2Descriptor11, dummyV2Descriptor12} from './dummy_v2/module.js';
 // </if>
 import {feedDescriptor, feedV2Descriptor} from './feed/module.js';
-import {ModuleDescriptor, ModuleDescriptorV2} from './module_descriptor.js';
+import {ModuleDescriptor} from './module_descriptor.js';
 import {ModuleRegistry} from './module_registry.js';
 import {photosDescriptor} from './photos/module.js';
 import {recipeTasksDescriptor} from './recipes/module.js';
 import {recipeTasksDescriptor as recipeTasksV2Descriptor} from './recipes_v2/module.js';
 
+const modulesRedesignedEnabled: boolean =
+    loadTimeData.getBoolean('modulesRedesignedEnabled');
 export const descriptors: ModuleDescriptor[] = [];
-
-export const descriptorsV2: ModuleDescriptorV2[] = [];
-
-if (loadTimeData.getBoolean('recipeTasksModuleEnabled')) {
-  if (loadTimeData.getBoolean('modulesRedesignedEnabled')) {
-    descriptorsV2.push(recipeTasksV2Descriptor);
-  } else {
-    descriptors.push(recipeTasksDescriptor);
-  }
-}
-
-if (loadTimeData.getBoolean('chromeCartModuleEnabled')) {
-  if (loadTimeData.getBoolean('modulesRedesignedEnabled')) {
-    descriptorsV2.push(chromeCartV2Descriptor);
-  } else {
-    descriptors.push(chromeCartDescriptor);
-  }
-}
-
-if (loadTimeData.getBoolean('driveModuleEnabled')) {
-  if (loadTimeData.getBoolean('modulesRedesignedEnabled')) {
-    descriptorsV2.push(driveV2Descriptor);
-  } else {
-    descriptors.push(driveDescriptor);
-  }
-}
-
-if (loadTimeData.getBoolean('photosModuleEnabled')) {
-  descriptors.push(photosDescriptor);
-}
-
-if (loadTimeData.getBoolean('feedModuleEnabled')) {
-  if (loadTimeData.getBoolean('modulesRedesignedEnabled')) {
-    descriptorsV2.push(feedV2Descriptor);
-  } else {
-    descriptors.push(feedDescriptor);
-  }
-}
+descriptors.push(
+    modulesRedesignedEnabled ? recipeTasksV2Descriptor : recipeTasksDescriptor);
+descriptors.push(
+    modulesRedesignedEnabled ? chromeCartV2Descriptor : chromeCartDescriptor);
+descriptors.push(
+    modulesRedesignedEnabled ? driveV2Descriptor : driveDescriptor);
+descriptors.push(photosDescriptor);
+descriptors.push(modulesRedesignedEnabled ? feedV2Descriptor : feedDescriptor);
 
 // <if expr="not is_official_build">
-if (loadTimeData.getBoolean('dummyModulesEnabled')) {
-  if (loadTimeData.getBoolean('modulesRedesignedEnabled')) {
-    descriptorsV2.push(dummyV2Descriptor);
-    descriptorsV2.push(dummyV2Descriptor02);
-    descriptorsV2.push(dummyV2Descriptor03);
-    descriptorsV2.push(dummyV2Descriptor04);
-    descriptorsV2.push(dummyV2Descriptor05);
-    descriptorsV2.push(dummyV2Descriptor06);
-    descriptorsV2.push(dummyV2Descriptor07);
-    descriptorsV2.push(dummyV2Descriptor08);
-    descriptorsV2.push(dummyV2Descriptor09);
-    descriptorsV2.push(dummyV2Descriptor10);
-    descriptorsV2.push(dummyV2Descriptor11);
-    descriptorsV2.push(dummyV2Descriptor12);
-  }
+if (modulesRedesignedEnabled) {
+  descriptors.push(dummyV2Descriptor);
+  descriptors.push(dummyV2Descriptor02);
+  descriptors.push(dummyV2Descriptor03);
+  descriptors.push(dummyV2Descriptor04);
+  descriptors.push(dummyV2Descriptor05);
+  descriptors.push(dummyV2Descriptor06);
+  descriptors.push(dummyV2Descriptor07);
+  descriptors.push(dummyV2Descriptor08);
+  descriptors.push(dummyV2Descriptor09);
+  descriptors.push(dummyV2Descriptor10);
+  descriptors.push(dummyV2Descriptor11);
+  descriptors.push(dummyV2Descriptor12);
 }
 // </if>
 
diff --git a/chrome/browser/resources/new_tab_page/modules/module_registry.ts b/chrome/browser/resources/new_tab_page/modules/module_registry.ts
index 12f134b..a8cde61 100644
--- a/chrome/browser/resources/new_tab_page/modules/module_registry.ts
+++ b/chrome/browser/resources/new_tab_page/modules/module_registry.ts
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {loadTimeData} from '../i18n_setup.js';
+import {ModuleIdName} from '../new_tab_page.mojom-webui.js';
 import {NewTabPageProxy} from '../new_tab_page_proxy.js';
 
 import {Module, ModuleDescriptor} from './module_descriptor.js';
-import {descriptors, descriptorsV2} from './module_descriptors.js';
+import {descriptors} from './module_descriptors.js';
 
 /**
  * @fileoverview The module registry holds the descriptors of NTP modules and
@@ -17,11 +17,7 @@
 
 export class ModuleRegistry {
   static getInstance(): ModuleRegistry {
-    return instance ||
-        (instance = new ModuleRegistry(
-             loadTimeData.getBoolean('modulesRedesignedEnabled') ?
-                 descriptorsV2 :
-                 descriptors));
+    return instance || (instance = new ModuleRegistry(descriptors));
   }
 
   static setInstance(newInstance: ModuleRegistry) {
@@ -57,8 +53,14 @@
           });
       NewTabPageProxy.getInstance().handler.updateDisabledModules();
     });
-    const descriptors =
-        this.descriptors_.filter(d => !disabledIds.includes(d.id));
+
+    const descriptorsMap: Map<string, ModuleDescriptor> =
+        new Map(this.descriptors_.map(d => [d.id, d]));
+    const modulesIdNames: ModuleIdName[] =
+        (await NewTabPageProxy.getInstance().handler.getModulesIdNames()).data;
+    const descriptors: ModuleDescriptor[] =
+        modulesIdNames.filter(d => !disabledIds.includes(d.id))
+            .map(details => descriptorsMap.get(details.id)!);
 
     // Modules may have an updated order, e.g. because of drag&drop or a Finch
     // param. Apply the updated order such that modules without a specified
diff --git a/chrome/browser/resources/new_tab_page/modules/modules.ts b/chrome/browser/resources/new_tab_page/modules/modules.ts
index fa2b5986..49ac3a6d 100644
--- a/chrome/browser/resources/new_tab_page/modules/modules.ts
+++ b/chrome/browser/resources/new_tab_page/modules/modules.ts
@@ -20,7 +20,6 @@
 import {ModuleWrapperElement} from './module_wrapper.js';
 import {getTemplate} from './modules.html.js';
 
-
 export type DismissModuleEvent =
     CustomEvent<{message: string, restoreCallback: () => void}>;
 export type DisableModuleEvent = DismissModuleEvent;
diff --git a/chrome/browser/resources/new_tab_page/modules/photos/module.ts b/chrome/browser/resources/new_tab_page/modules/photos/module.ts
index 2c2cafb..e331891 100644
--- a/chrome/browser/resources/new_tab_page/modules/photos/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/photos/module.ts
@@ -263,6 +263,4 @@
 }
 
 export const photosDescriptor: ModuleDescriptor = new ModuleDescriptor(
-    /*id=*/ 'photos',
-    /*name=*/ loadTimeData.getString('modulesPhotosSentence'),
-    createPhotosElement);
+    /*id=*/ 'photos', createPhotosElement);
diff --git a/chrome/browser/resources/new_tab_page/modules/recipes/module.ts b/chrome/browser/resources/new_tab_page/modules/recipes/module.ts
index 97196760..5e5253d 100644
--- a/chrome/browser/resources/new_tab_page/modules/recipes/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/recipes/module.ts
@@ -182,10 +182,5 @@
   return element;
 }
 
-export const recipeTasksDescriptor: ModuleDescriptor = new ModuleDescriptor(
-    /*id=*/ 'recipe_tasks',
-    /*name=*/
-    loadTimeData.getBoolean('modulesRecipeHistoricalExperimentEnabled') ?
-        loadTimeData.getString('modulesRecipeViewedTasksSentence') :
-        loadTimeData.getString('modulesRecipeTasksSentence'),
-    createModule);
+export const recipeTasksDescriptor: ModuleDescriptor =
+    new ModuleDescriptor(/*id=*/ 'recipe_tasks', createModule);
diff --git a/chrome/browser/resources/new_tab_page/modules/recipes_v2/module.ts b/chrome/browser/resources/new_tab_page/modules/recipes_v2/module.ts
index 0cfdecb..240d4676 100644
--- a/chrome/browser/resources/new_tab_page/modules/recipes_v2/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/recipes_v2/module.ts
@@ -7,7 +7,7 @@
 import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 import {DomRepeat, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {I18nMixin, loadTimeData} from '../../i18n_setup.js';
+import {I18nMixin} from '../../i18n_setup.js';
 import {Recipe} from '../../recipes.mojom-webui.js';
 import {InfoDialogElement} from '../info_dialog.js';
 import {ModuleDescriptorV2, ModuleHeight} from '../module_descriptor.js';
@@ -56,5 +56,4 @@
 
 export const recipeTasksDescriptor: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'recipe_tasks',
-    /*name=*/ loadTimeData.getString('modulesRecipeTasksSentence'),
     /*height*/ ModuleHeight.TALL, createModule);
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/select_to_speak_subpage.html b/chrome/browser/resources/settings/chromeos/os_a11y_page/select_to_speak_subpage.html
index 1c277af..28783c2 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/select_to_speak_subpage.html
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/select_to_speak_subpage.html
@@ -170,12 +170,11 @@
   <cr-link-row class="settings-box"
       label="$i18n{selectToSpeakTextToSpeechSettingsLink}"
       on-click="onTextToSpeechSettingsTap_"
+      aria-show-label aria-show-sublabel
       embedded>
   </cr-link-row>
   <template is="dom-if"
       if="[[isExperimentalAccessibilitySelectToSpeakVoiceSwitchingEnabled_]]">
-    <!-- TODO(crbug.com/1354821): Figure out how to make settings-toggle-button
-    and other control labels speakable with Select-to-speak. -->
     <!-- TODO(crbug.com/1354821): Disable and turn off or hide
     voiceSwitchingToggle when enhanced network voices is enabled. -->
     <settings-toggle-button
@@ -184,7 +183,8 @@
         pref="{{prefs.settings.a11y.select_to_speak_voice_switching}}"
         label="$i18n{selectToSpeakOptionsVoiceSwitchingDescription}"
         deep-link-focus-id$="[[Setting.kSelectToSpeakVoiceSwitching]]"
-        disabled="[[prefs.settings.a11y.select_to_speak_enhanced_network_voices.value]]">
+        disabled="[[prefs.settings.a11y.select_to_speak_enhanced_network_voices.value]]"
+        aria-show-label aria-show-sublabel>
     </settings-toggle-button>
   </template>
   <settings-toggle-button
@@ -197,6 +197,7 @@
       on-settings-boolean-control-change="onEnhancedNetworkVoicesToggleChanged_"
       deep-link-focus-id$="[[Setting.kSelectToSpeakEnhancedNetworkVoices]]"
       disabled="[[prefs.settings.a11y.select_to_speak_voice_switching.value]]"
+      aria-show-label aria-show-sublabel
       no-set-pref>
   </settings-toggle-button>
     <template is="dom-if" if="[[enhancedNetworkVoicesVirtualPref_.value]]">
@@ -258,7 +259,8 @@
       class="settings-box continuation"
       pref="{{prefs.settings.a11y.select_to_speak_word_highlight}}"
       label="$i18n{selectToSpeakOptionsHighlightDescription}"
-      deep-link-focus-id$="[[Setting.kSelectToSpeakWordHighlight]]">
+      deep-link-focus-id$="[[Setting.kSelectToSpeakWordHighlight]]"
+      aria-show-label aria-show-sublabel>
   </settings-toggle-button>
   <template is="dom-if"
       if="[[prefs.settings.a11y.select_to_speak_word_highlight.value]]">
@@ -291,7 +293,8 @@
       class="settings-box continuation"
       pref="{{prefs.settings.a11y.select_to_speak_background_shading}}"
       label="$i18n{selectToSpeakOptionsBackgroundShadingDescription}"
-      deep-link-focus-id$="[[Setting.kSelectToSpeakBackgroundShading]]">
+      deep-link-focus-id$="[[Setting.kSelectToSpeakBackgroundShading]]"
+      aria-show-label aria-show-sublabel>
   </settings-toggle-button>
 </div>
 <settings-toggle-button
@@ -300,6 +303,7 @@
     pref="{{prefs.settings.a11y.select_to_speak_navigation_controls}}"
     label="$i18n{selectToSpeakOptionsNavigationControlsDescription}"
     sub-label="$i18n{selectToSpeakOptionsNavigationControlsSubtitle}"
-    deep-link-focus-id$="[[Setting.kSelectToSpeakNavigationControls]]">
+    deep-link-focus-id$="[[Setting.kSelectToSpeakNavigationControls]]"
+    aria-show-label aria-show-sublabel>
 </settings-toggle-button>
 
diff --git a/chrome/browser/resources/settings/controls/settings_toggle_button.html b/chrome/browser/resources/settings/controls/settings_toggle_button.html
index 66438be..1f6e0d2 100644
--- a/chrome/browser/resources/settings/controls/settings_toggle_button.html
+++ b/chrome/browser/resources/settings/controls/settings_toggle_button.html
@@ -56,7 +56,7 @@
     </template>
   </if>
   <div class="flex" id="labelWrapper" hidden$="[[!label]]">
-    <div class="label" aria-hidden="true">[[label]]</div>
+    <div class="label" aria-hidden="[[!ariaShowLabel]]">[[label]]</div>
     <div class="secondary label" id="sub-label">
       <template is="dom-if" if="[[subLabelIcon]]">
         <span id="sub-label-icon" aria-hidden="true">
@@ -64,7 +64,7 @@
           </iron-icon>
         </span>
       </template>
-      <span id="sub-label-text" aria-hidden="true">
+      <span id="sub-label-text" aria-hidden="[[!ariaShowSublabel]]">
         [[subLabel]]
       </span>
       <template is="dom-if" if="[[learnMoreUrl]]">
diff --git a/chrome/browser/resources/settings/controls/settings_toggle_button.ts b/chrome/browser/resources/settings/controls/settings_toggle_button.ts
index 6a06a292..2717620f 100644
--- a/chrome/browser/resources/settings/controls/settings_toggle_button.ts
+++ b/chrome/browser/resources/settings/controls/settings_toggle_button.ts
@@ -50,6 +50,18 @@
         value: '',
       },
 
+      ariaShowLabel: {
+        type: Boolean,
+        reflectToAttribute: true,
+        value: false,
+      },
+
+      ariaShowSublabel: {
+        type: Boolean,
+        reflectToAttribute: true,
+        value: false,
+      },
+
       elideLabel: {
         type: Boolean,
         reflectToAttribute: true,
@@ -80,16 +92,14 @@
   }
 
   override ariaLabel: string;
+  ariaShowLabel: boolean;
+  ariaShowSublabel: boolean;
   elideLabel: boolean;
-
   // <if expr="chromeos_ash">
   icon: string;
   // </if>
-
   learnMoreUrl: string;
-
   subLabelWithLink: string;
-
   subLabelIcon: string;
 
   override ready() {
diff --git a/chrome/browser/resources/settings/lazy_load.ts b/chrome/browser/resources/settings/lazy_load.ts
index e1d0219..00fb57dd 100644
--- a/chrome/browser/resources/settings/lazy_load.ts
+++ b/chrome/browser/resources/settings/lazy_load.ts
@@ -80,6 +80,7 @@
 export {CrTextareaElement} from 'chrome://resources/cr_elements/cr_textarea/cr_textarea.js';
 export {getToastManager} from 'chrome://resources/cr_elements/cr_toast/cr_toast_manager.js';
 export {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
+export {PaperTooltipElement} from 'chrome://resources/polymer/v3_0/paper-tooltip/paper-tooltip.js';
 export {SettingsAppearanceFontsPageElement} from './appearance_page/appearance_fonts_page.js';
 export {FontsBrowserProxy, FontsBrowserProxyImpl, FontsData} from './appearance_page/fonts_browser_proxy.js';
 export {CountryDetailManager, CountryDetailManagerImpl, SettingsAddressEditDialogElement} from './autofill_page/address_edit_dialog.js';
diff --git a/chrome/browser/resources/settings/site_settings/site_list_entry.html b/chrome/browser/resources/settings/site_settings/site_list_entry.html
index 41d2925..5b1d7ee 100644
--- a/chrome/browser/resources/settings/site_settings/site_list_entry.html
+++ b/chrome/browser/resources/settings/site_settings/site_list_entry.html
@@ -7,9 +7,14 @@
         flex: 1
       }
 
-      /* Tooltip is hidden since site-list will display a common tooltip. */
+      /* Tooltip is hidden since site-list will display a common tooltip.
+       * Not using 'display: none' because tooltip still needs to be accessible
+       * by screen readers. */
       cr-policy-pref-indicator::part(tooltip) {
-        display: none;
+        clip: rect(0 0 0 0);
+        height: 1px;
+        overflow: hidden;
+        width: 1px;
       }
     </style>
     <div class="list-item" focus-row-container>
@@ -38,7 +43,7 @@
       </div>
       <template is="dom-if" if="[[showPolicyPrefIndicator_]]">
         <cr-policy-pref-indicator pref="[[model]]"
-            icon-aria-label="[[label]]" on-mouseenter="onShowTooltip_"
+            on-mouseenter="onShowTooltip_"
             on-focus="onShowTooltip_" focus-row-control focus-type="policy">
         </cr-policy-pref-indicator>
       </template>
diff --git a/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc b/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc
index b156441..2d2627c 100644
--- a/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc
@@ -57,7 +57,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #endif
 
 namespace safe_browsing {
diff --git a/chrome/browser/support_tool/support_packet_metadata.cc b/chrome/browser/support_tool/support_packet_metadata.cc
index 57a43993..99300fb 100644
--- a/chrome/browser/support_tool/support_packet_metadata.cc
+++ b/chrome/browser/support_tool/support_packet_metadata.cc
@@ -27,7 +27,7 @@
 #include "chrome/browser/policy/policy_ui_utils.h"
 #include "chrome/browser/support_tool/data_collector.h"
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 #include "components/feedback/pii_types.h"
 #include "components/policy/core/browser/webui/json_generation.h"
diff --git a/chrome/browser/support_tool/support_tool_handler_unittest.cc b/chrome/browser/support_tool/support_tool_handler_unittest.cc
index 7a70fad9..2c12d1a 100644
--- a/chrome/browser/support_tool/support_tool_handler_unittest.cc
+++ b/chrome/browser/support_tool/support_tool_handler_unittest.cc
@@ -31,8 +31,8 @@
 #include "third_party/zlib/google/zip_reader.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 using testing::IsSupersetOf;
diff --git a/chrome/browser/tracing/chrome_background_tracing_metrics_provider_unittest.cc b/chrome/browser/tracing/chrome_background_tracing_metrics_provider_unittest.cc
index 230af23..5fda6f5b 100644
--- a/chrome/browser/tracing/chrome_background_tracing_metrics_provider_unittest.cc
+++ b/chrome/browser/tracing/chrome_background_tracing_metrics_provider_unittest.cc
@@ -25,9 +25,9 @@
 #include "chrome/browser/ash/login/demo_mode/demo_session.h"
 #include "chrome/browser/metrics/chromeos_system_profile_provider.h"
 #include "chromeos/ash/components/login/login_state/login_state.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/tpm_manager/tpm_manager_client.h"  // nogncheck
-#include "chromeos/system/fake_statistics_provider.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 namespace tracing {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 19a1103..76efbd7 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3235,6 +3235,7 @@
       "//chromeos/ash/components/proximity_auth",
       "//chromeos/ash/components/settings",
       "//chromeos/ash/components/string_matching",
+      "//chromeos/ash/components/system",
       "//chromeos/ash/components/tether",
       "//chromeos/ash/resources",
       "//chromeos/ash/services/assistant:lib",
@@ -3268,7 +3269,6 @@
       "//chromeos/services/network_config/public/cpp",
       "//chromeos/services/network_config/public/mojom",
       "//chromeos/services/network_health/public/mojom",
-      "//chromeos/system",
       "//chromeos/ui/base",
       "//chromeos/ui/frame",
       "//chromeos/ui/vector_icons",
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
index 037b86a1..06eae9e 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
@@ -98,7 +98,7 @@
             // Feature is enabled on non-tablet devices.
             mDividerLineProcessor = new DividerLineProcessor(context);
         }
-        mHeaderProcessor = new HeaderProcessor(context, host, delegate);
+        mHeaderProcessor = new HeaderProcessor(context);
         registerSuggestionProcessor(new EditUrlSuggestionProcessor(
                 context, host, delegate, mFaviconFetcher, mActivityTabSupplier, shareSupplier));
         registerSuggestionProcessor(
@@ -313,7 +313,7 @@
                 // header text may be empty.
                 if (details != null && !TextUtils.isEmpty(details.getHeaderText())) {
                     final PropertyModel model = mHeaderProcessor.createModel();
-                    mHeaderProcessor.populateModel(model, currentGroup, details.getHeaderText());
+                    mHeaderProcessor.populateModel(model, details.getHeaderText());
                     viewInfoList.add(
                             new DropdownItemViewInfo(mHeaderProcessor, model, currentGroup));
                 }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilderUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilderUnitTest.java
index 82f2c2e..1276355d 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilderUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilderUnitTest.java
@@ -119,7 +119,7 @@
                 AutocompleteResult.fromCache(actualList, groupsDetails));
 
         verifier.verify(mMockHeaderProcessor, times(1))
-                .populateModel(any(), eq(1), eq(SECTION_2_EXPANDED_WITH_HEADER.getHeaderText()));
+                .populateModel(any(), eq(SECTION_2_EXPANDED_WITH_HEADER.getHeaderText()));
         verifier.verify(mMockSuggestionProcessor, times(1))
                 .populateModel(eq(suggestion), any(), eq(0));
         verifier.verify(mMockSuggestionProcessor, times(1))
@@ -172,13 +172,13 @@
         verifier.verify(mMockSuggestionProcessor, times(1))
                 .populateModel(eq(suggestionWithNoGroup), any(), eq(0));
         verifier.verify(mMockHeaderProcessor, times(1))
-                .populateModel(any(), eq(1), eq(SECTION_2_EXPANDED_WITH_HEADER.getHeaderText()));
+                .populateModel(any(), eq(SECTION_2_EXPANDED_WITH_HEADER.getHeaderText()));
         verifier.verify(mMockSuggestionProcessor, times(1))
                 .populateModel(eq(suggestionForGroup1), any(), eq(1));
         verifier.verify(mMockSuggestionProcessor, times(1))
                 .populateModel(eq(suggestionForGroup1), any(), eq(2));
         verifier.verify(mMockHeaderProcessor, times(1))
-                .populateModel(any(), eq(2), eq(SECTION_3_EXPANDED_WITH_HEADER.getHeaderText()));
+                .populateModel(any(), eq(SECTION_3_EXPANDED_WITH_HEADER.getHeaderText()));
         verifier.verify(mMockSuggestionProcessor, times(1))
                 .populateModel(eq(suggestionForGroup2), any(), eq(3));
         verifier.verify(mMockSuggestionProcessor, times(1))
@@ -242,14 +242,14 @@
         verifier.verify(mMockSuggestionProcessor, times(1))
                 .populateModel(eq(suggestionForGroup1), any(), eq(2));
         verifier.verify(mMockHeaderProcessor, times(1))
-                .populateModel(any(), eq(2), eq(SECTION_2_EXPANDED_WITH_HEADER.getHeaderText()));
+                .populateModel(any(), eq(SECTION_2_EXPANDED_WITH_HEADER.getHeaderText()));
         verifier.verify(mMockSuggestionProcessor, times(1))
                 .populateModel(eq(suggestionForGroup2), any(), eq(3));
         verifier.verify(mMockSuggestionProcessor, times(1))
                 .populateModel(eq(suggestionForGroup2), any(), eq(4));
 
         // Make sure no other headers were ever constructed.
-        verify(mMockHeaderProcessor, times(1)).populateModel(any(), anyInt(), any());
+        verify(mMockHeaderProcessor, times(1)).populateModel(any(), any());
 
         Assert.assertEquals(6, model.size()); // 1 header + 5 suggestions.
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionView.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionView.java
index 741c25a..41167927 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionView.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionView.java
@@ -48,9 +48,6 @@
         mHeader = new HeaderView(context);
         mHeader.setLayoutParams(
                 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-        mHeader.getIconView().setVisibility(GONE);
-        mHeader.setClickable(false);
-        mHeader.setFocusable(false);
         mHeader.setVisibility(View.GONE);
         addView(mHeader);
 
@@ -111,7 +108,7 @@
 
     /** @return Header TextView element. */
     TextView getHeaderTextView() {
-        return mHeader.getTextView();
+        return mHeader;
     }
 
     /** @return Header element. */
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderProcessor.java
index adada2c..05723fc 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderProcessor.java
@@ -6,34 +6,23 @@
 
 import android.content.Context;
 
-import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.omnibox.OmniboxFeatures;
 import org.chromium.chrome.browser.omnibox.R;
 import org.chromium.chrome.browser.omnibox.suggestions.DropdownItemProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
-import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
-import org.chromium.chrome.browser.omnibox.suggestions.UrlBarDelegate;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** A class that handles model and view creation for the suggestion headers. */
 public class HeaderProcessor implements DropdownItemProcessor {
-    private final SuggestionHost mSuggestionHost;
-    private final UrlBarDelegate mUrlBarDelegate;
     private final int mMinimumHeight;
-    private boolean mShouldRemoveSuggestionHeaderChevron;
-    private boolean mAllowGroupCollapsedState;
     private boolean mShouldUseModernizedHeaderPadding;
     private Context mContext;
 
     /**
      * @param context An Android context.
      */
-    public HeaderProcessor(
-            Context context, SuggestionHost suggestionHost, UrlBarDelegate urlDelegate) {
+    public HeaderProcessor(Context context) {
         mContext = context;
-        mSuggestionHost = suggestionHost;
-        mUrlBarDelegate = urlDelegate;
         mMinimumHeight = context.getResources().getDimensionPixelSize(
                 R.dimen.omnibox_suggestion_header_height);
     }
@@ -58,33 +47,10 @@
      * @param model The model to populate.
      * @param headerText Text to be displayed for this group header.
      */
-    public void populateModel(
-            final PropertyModel model, final int groupId, final String headerText) {
+    public void populateModel(final PropertyModel model, final String headerText) {
         model.set(HeaderViewProperties.TITLE, headerText);
-        model.set(HeaderViewProperties.IS_COLLAPSED, false);
-        model.set(HeaderViewProperties.SHOULD_REMOVE_CHEVRON, mShouldRemoveSuggestionHeaderChevron);
         model.set(HeaderViewProperties.USE_MODERNIZED_HEADER_PADDING,
                 mShouldUseModernizedHeaderPadding);
-        if (mAllowGroupCollapsedState) {
-            model.set(HeaderViewProperties.DELEGATE, new HeaderViewProperties.Delegate() {
-                @Override
-                public void onHeaderSelected() {
-                    mUrlBarDelegate.setOmniboxEditingText(null);
-                }
-
-                @Override
-                public void onHeaderClicked() {
-                    final boolean newState = !model.get(HeaderViewProperties.IS_COLLAPSED);
-                    RecordHistogram.recordSparseHistogram(newState
-                                    ? "Omnibox.ToggleSuggestionGroupId.Off"
-                                    : "Omnibox.ToggleSuggestionGroupId.On",
-                            groupId);
-
-                    model.set(HeaderViewProperties.IS_COLLAPSED, newState);
-                    mSuggestionHost.setGroupCollapsedState(groupId, newState);
-                }
-            });
-        }
     }
 
     /**
@@ -93,14 +59,6 @@
      */
     @Override
     public void onNativeInitialized() {
-        mShouldRemoveSuggestionHeaderChevron = ChromeFeatureList.isEnabled(
-                ChromeFeatureList.OMNIBOX_REMOVE_SUGGESTION_HEADER_CHEVRON);
-
-        mAllowGroupCollapsedState = ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
-                ChromeFeatureList.OMNIBOX_REMOVE_SUGGESTION_HEADER_CHEVRON,
-                "allow_group_collapsed_state",
-                /* default= */ true);
-
         mShouldUseModernizedHeaderPadding =
                 OmniboxFeatures.shouldShowModernizeVisualUpdate(mContext);
     }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderView.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderView.java
index f5e2842c..2f2f0c7 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderView.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderView.java
@@ -5,37 +5,18 @@
 package org.chromium.chrome.browser.omnibox.suggestions.header;
 
 import android.content.Context;
-import android.os.Bundle;
 import android.text.TextUtils.TruncateAt;
-import android.util.TypedValue;
 import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ImageView;
 import android.widget.TextView;
 
-import androidx.appcompat.widget.AppCompatImageView;
-import androidx.core.view.AccessibilityDelegateCompat;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
 import androidx.core.widget.TextViewCompat;
 
-import org.chromium.chrome.browser.omnibox.R;
-import org.chromium.chrome.browser.omnibox.suggestions.base.SimpleHorizontalLayoutView;
-import org.chromium.chrome.browser.util.KeyNavigationUtil;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 
 /**
  * View for Group Headers.
  */
-public class HeaderView extends SimpleHorizontalLayoutView {
-    private final TextView mHeaderText;
-    private final ImageView mHeaderIcon;
-    private boolean mIsCollapsed;
-    private Runnable mOnSelectListener;
-
+public class HeaderView extends TextView {
     /**
      * Constructs a new header view.
      *
@@ -44,100 +25,12 @@
     public HeaderView(Context context) {
         super(context);
 
-        TypedValue themeRes = new TypedValue();
-        getContext().getTheme().resolveAttribute(R.attr.selectableItemBackground, themeRes, true);
-        setBackgroundResource(themeRes.resourceId);
-        setFocusable(true);
-
-        mHeaderText = new TextView(context);
-        mHeaderText.setLayoutParams(LayoutParams.forDynamicView());
-        mHeaderText.setMaxLines(1);
-        mHeaderText.setEllipsize(TruncateAt.END);
+        setMaxLines(1);
+        setEllipsize(TruncateAt.END);
         TextViewCompat.setTextAppearance(
-                mHeaderText, ChromeColors.getTextMediumThickSecondaryStyle(false));
-        mHeaderText.setGravity(Gravity.CENTER_VERTICAL);
-        mHeaderText.setTextAlignment(TextView.TEXT_ALIGNMENT_VIEW_START);
-        addView(mHeaderText);
-
-        mHeaderIcon = new AppCompatImageView(context);
-        mHeaderIcon.setScaleType(ImageView.ScaleType.CENTER);
-        mHeaderIcon.setImageResource(R.drawable.ic_expand_more_black_24dp);
-        mHeaderIcon.setLayoutParams(new LayoutParams(
-                getResources().getDimensionPixelSize(R.dimen.omnibox_suggestion_action_icon_width),
-                LayoutParams.MATCH_PARENT));
-        addView(mHeaderIcon);
-    }
-
-    /** Return ImageView used to present group header chevron. */
-    public ImageView getIconView() {
-        return mHeaderIcon;
-    }
-
-    /** Return TextView used to present group header text. */
-    public TextView getTextView() {
-        return mHeaderText;
-    }
-
-    /**
-     * Specifies whether view should be announced as expanded or collapsed.
-     *
-     * @param isCollapsed true, if view should be announced as collapsed.
-     */
-    void setCollapsedStateForAccessibility(boolean isCollapsed) {
-        mIsCollapsed = isCollapsed;
-    }
-
-    /**
-     * Specify the listener receiving calls when the view is selected.
-     */
-    void setOnSelectListener(Runnable listener) {
-        mOnSelectListener = listener;
-    }
-
-    @Override
-    public void setSelected(boolean selected) {
-        super.setSelected(selected);
-        if (selected && mOnSelectListener != null) {
-            mOnSelectListener.run();
-        }
-    }
-
-    /**
-     * Specifies whether the chevron on the right side of header should be removed.
-     *
-     * @param shouldRemoveSuggestionHeaderChevron true, if the chevron on the right side of header
-     *         should be removed.
-     */
-    void setShouldRemoveSuggestionHeaderChevron(boolean shouldRemoveSuggestionHeaderChevron) {
-        mHeaderIcon.setVisibility(shouldRemoveSuggestionHeaderChevron ? GONE : VISIBLE);
-        setClickable(!shouldRemoveSuggestionHeaderChevron);
-
-        // Remove extra accessibility announcement for expanded state, and remove click action.
-        if (!shouldRemoveSuggestionHeaderChevron) {
-            ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegateCompat() {
-                @Override
-                public void onInitializeAccessibilityNodeInfo(
-                        View host, AccessibilityNodeInfoCompat info) {
-                    super.onInitializeAccessibilityNodeInfo(host, info);
-                    AccessibilityActionCompat action = mIsCollapsed
-                            ? AccessibilityActionCompat.ACTION_EXPAND
-                            : AccessibilityActionCompat.ACTION_COLLAPSE;
-
-                    info.addAction(new AccessibilityActionCompat(
-                            AccessibilityEvent.TYPE_VIEW_CLICKED, action.getLabel()));
-                    info.addAction(action);
-                }
-
-                @Override
-                public boolean performAccessibilityAction(View host, int action, Bundle arguments) {
-                    if (action == AccessibilityNodeInfoCompat.ACTION_EXPAND
-                            || action == AccessibilityNodeInfoCompat.ACTION_COLLAPSE) {
-                        return performClick();
-                    }
-                    return super.performAccessibilityAction(host, action, arguments);
-                }
-            });
-        }
+                this, ChromeColors.getTextMediumThickSecondaryStyle(false));
+        setGravity(Gravity.CENTER_VERTICAL);
+        setTextAlignment(TextView.TEXT_ALIGNMENT_VIEW_START);
     }
 
     /**
@@ -150,22 +43,7 @@
      */
     void setUpdateHeaderPadding(
             int minHeight, int paddingStart, int paddingTop, int paddingBottom) {
-        mHeaderText.setMinHeight(minHeight);
-        mHeaderText.setPaddingRelative(paddingStart, paddingTop, 0, paddingBottom);
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
-        if ((!isRtl && KeyNavigationUtil.isGoRight(event))
-                || (isRtl && KeyNavigationUtil.isGoLeft(event))) {
-            return performClick();
-        }
-        return super.onKeyDown(keyCode, event);
-    }
-
-    @Override
-    public boolean isFocused() {
-        return super.isFocused() || (isSelected() && !isInTouchMode());
+        setMinHeight(minHeight);
+        setPaddingRelative(paddingStart, paddingTop, 0, paddingBottom);
     }
 }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewBinder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewBinder.java
index 9f781487..25f6317 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewBinder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewBinder.java
@@ -9,7 +9,6 @@
 import androidx.core.view.ViewCompat;
 import androidx.core.widget.TextViewCompat;
 
-import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.browser.omnibox.R;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
@@ -22,34 +21,15 @@
     /** @see PropertyModelChangeProcessor.ViewBinder#bind(Object, Object, Object) */
     public static void bind(PropertyModel model, HeaderView view, PropertyKey propertyKey) {
         if (HeaderViewProperties.TITLE == propertyKey) {
-            view.getTextView().setText(model.get(HeaderViewProperties.TITLE));
+            view.setText(model.get(HeaderViewProperties.TITLE));
         } else if (propertyKey == SuggestionCommonProperties.COLOR_SCHEME) {
             final boolean isIncognito = model.get(SuggestionCommonProperties.COLOR_SCHEME)
                     == BrandedColorScheme.INCOGNITO;
             TextViewCompat.setTextAppearance(
-                    view.getTextView(), ChromeColors.getTextMediumThickSecondaryStyle(isIncognito));
-            ApiCompatibilityUtils.setImageTintList(view.getIconView(),
-                    ChromeColors.getPrimaryIconTint(view.getContext(), isIncognito));
+                    view, ChromeColors.getTextMediumThickSecondaryStyle(isIncognito));
         } else if (propertyKey == SuggestionCommonProperties.LAYOUT_DIRECTION) {
             ViewCompat.setLayoutDirection(
                     view, model.get(SuggestionCommonProperties.LAYOUT_DIRECTION));
-        } else if (propertyKey == HeaderViewProperties.IS_COLLAPSED) {
-            boolean isCollapsed = model.get(HeaderViewProperties.IS_COLLAPSED);
-            view.getIconView().setImageResource(isCollapsed ? R.drawable.ic_expand_more_black_24dp
-                                                            : R.drawable.ic_expand_less_black_24dp);
-            view.setCollapsedStateForAccessibility(isCollapsed);
-        } else if (propertyKey == HeaderViewProperties.DELEGATE) {
-            HeaderViewProperties.Delegate delegate = model.get(HeaderViewProperties.DELEGATE);
-            if (delegate != null) {
-                view.setOnClickListener(v -> delegate.onHeaderClicked());
-                view.setOnSelectListener(delegate::onHeaderSelected);
-            } else {
-                view.setOnClickListener(null);
-                view.setOnSelectListener(null);
-            }
-        } else if (propertyKey == HeaderViewProperties.SHOULD_REMOVE_CHEVRON) {
-            view.setShouldRemoveSuggestionHeaderChevron(
-                    model.get(HeaderViewProperties.SHOULD_REMOVE_CHEVRON));
         } else if (propertyKey == HeaderViewProperties.USE_MODERNIZED_HEADER_PADDING) {
             boolean useModernizedHeaderPadding =
                     model.get(HeaderViewProperties.USE_MODERNIZED_HEADER_PADDING);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewBinderUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewBinderUnitTest.java
index 9cd3b15d..cb7c6f7 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewBinderUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewBinderUnitTest.java
@@ -5,32 +5,21 @@
 package org.chromium.chrome.browser.omnibox.suggestions.header;
 
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.notNull;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Resources;
-import android.os.Bundle;
 import android.view.ContextThemeWrapper;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.widget.ImageView;
-import android.widget.TextView;
 
 import androidx.test.ext.junit.rules.ActivityScenarioRule;
 
-import org.junit.Assert;
 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.Mockito;
 import org.mockito.MockitoAnnotations;
 
@@ -63,10 +52,6 @@
     Resources mResources;
 
     HeaderView mHeaderView;
-    @Mock
-    TextView mHeaderText;
-    @Mock
-    ImageView mHeaderIcon;
 
     @Before
     public void setUp() {
@@ -81,127 +66,11 @@
                 Mockito.withSettings().useConstructor(mActivity).defaultAnswer(
                         Mockito.CALLS_REAL_METHODS));
 
-        when(mHeaderView.getTextView()).thenReturn(mHeaderText);
-        when(mHeaderView.getIconView()).thenReturn(mHeaderIcon);
-
         mModel = new PropertyModel(HeaderViewProperties.ALL_KEYS);
         PropertyModelChangeProcessor.create(mModel, mHeaderView, HeaderViewBinder::bind);
     }
 
     @Test
-    public void actionIcon_iconReflectsExpandedState() {
-        // Expand.
-        mModel.set(HeaderViewProperties.IS_COLLAPSED, false);
-        verify(mHeaderIcon, times(1)).setImageResource(R.drawable.ic_expand_less_black_24dp);
-
-        // Collapse.
-        mModel.set(HeaderViewProperties.IS_COLLAPSED, true);
-        verify(mHeaderIcon, times(1)).setImageResource(R.drawable.ic_expand_more_black_24dp);
-    }
-
-    @Test
-    public void headerView_accessibilityStringReflectsExpandedState() {
-        // Expand without title.
-        mModel.set(HeaderViewProperties.IS_COLLAPSED, false);
-        verify(mHeaderView, times(1)).setCollapsedStateForAccessibility(false);
-
-        mModel.set(HeaderViewProperties.IS_COLLAPSED, true);
-        verify(mHeaderView, times(1)).setCollapsedStateForAccessibility(true);
-    }
-
-    @Test
-    public void headerView_listenerInstalledWhenDelegateIsSet() {
-        final HeaderViewProperties.Delegate delegate = mock(HeaderViewProperties.Delegate.class);
-
-        // Install.
-        mModel.set(HeaderViewProperties.DELEGATE, delegate);
-        verify(mHeaderView, times(1)).setOnClickListener(notNull());
-        verify(mHeaderView, times(1)).setOnSelectListener(notNull());
-
-        // Call.
-        Assert.assertTrue(mHeaderView.performClick());
-        verify(delegate, times(1)).onHeaderClicked();
-
-        // Select.
-        mHeaderView.setSelected(false);
-        verify(delegate, never()).onHeaderSelected();
-        mHeaderView.setSelected(true);
-        verify(delegate, times(1)).onHeaderSelected();
-
-        // Remove.
-        mModel.set(HeaderViewProperties.DELEGATE, null);
-        verify(mHeaderView, times(1)).setOnClickListener(null);
-        verify(mHeaderView, times(1)).setOnSelectListener(null);
-
-        reset(delegate);
-
-        // Call; check that click calls are no longer propagated.
-        // Note: performClick returns value indicating whether onClickListener was invoked.
-        Assert.assertFalse(mHeaderView.performClick());
-        verify(delegate, never()).onHeaderClicked();
-
-        // Select.
-        mHeaderView.setSelected(true);
-        verify(delegate, never()).onHeaderSelected();
-    }
-
-    @Test
-    public void actionIcon_accessibilityAnnouncementsReflectExpandedState() {
-        mModel.set(HeaderViewProperties.SHOULD_REMOVE_CHEVRON, false);
-
-        final AccessibilityNodeInfo info = mock(AccessibilityNodeInfo.class);
-        Bundle infoExtras = new Bundle();
-        when(info.getExtras()).thenReturn(infoExtras);
-
-        // Expand.
-        mModel.set(HeaderViewProperties.IS_COLLAPSED, false);
-        mHeaderView.onInitializeAccessibilityNodeInfo(info);
-        verify(info, times(1)).addAction(AccessibilityAction.ACTION_COLLAPSE);
-        verify(info, never()).addAction(AccessibilityAction.ACTION_EXPAND);
-
-        reset(info);
-        when(info.getExtras()).thenReturn(infoExtras);
-
-        // Collapse.
-        mModel.set(HeaderViewProperties.IS_COLLAPSED, true);
-        mHeaderView.onInitializeAccessibilityNodeInfo(info);
-        verify(info, never()).addAction(AccessibilityAction.ACTION_COLLAPSE);
-        verify(info, times(1)).addAction(AccessibilityAction.ACTION_EXPAND);
-    }
-
-    @Test
-    public void headerView_removeSuggestionHeaderChevron() {
-        // Remove Chevron.
-        mModel.set(HeaderViewProperties.SHOULD_REMOVE_CHEVRON, true);
-        verify(mHeaderView, times(1)).setShouldRemoveSuggestionHeaderChevron(true);
-
-        // Restore Chevron.
-        mModel.set(HeaderViewProperties.SHOULD_REMOVE_CHEVRON, false);
-        verify(mHeaderView, times(1)).setShouldRemoveSuggestionHeaderChevron(false);
-    }
-
-    @Test
-    public void headerView_removeSuggestionHeaderChevronRemovesAccessibilityControl() {
-        mModel.set(HeaderViewProperties.SHOULD_REMOVE_CHEVRON, true);
-
-        final AccessibilityNodeInfo info = mock(AccessibilityNodeInfo.class);
-        Bundle infoExtras = new Bundle();
-        when(info.getExtras()).thenReturn(infoExtras);
-
-        // Expand.
-        mModel.set(HeaderViewProperties.IS_COLLAPSED, false);
-        mHeaderView.onInitializeAccessibilityNodeInfo(info);
-        verify(info, never()).addAction(AccessibilityAction.ACTION_COLLAPSE);
-        verify(info, never()).addAction(AccessibilityAction.ACTION_EXPAND);
-
-        // Collapse.
-        mModel.set(HeaderViewProperties.IS_COLLAPSED, true);
-        mHeaderView.onInitializeAccessibilityNodeInfo(info);
-        verify(info, never()).addAction(AccessibilityAction.ACTION_COLLAPSE);
-        verify(info, never()).addAction(AccessibilityAction.ACTION_EXPAND);
-    }
-
-    @Test
     @DisableFeatures(ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE)
     public void headerView_useModernizedHeaderPaddingFalse() {
         mModel.set(HeaderViewProperties.USE_MODERNIZED_HEADER_PADDING, false);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewProperties.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewProperties.java
index e884f49..6abc2692 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewProperties.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewProperties.java
@@ -12,25 +12,8 @@
 
 /** The properties associated with the header suggestions. */
 public class HeaderViewProperties {
-    /** Interface that receives events from Header view. */
-    interface Delegate {
-        /** Invoked whenever header view is selected. */
-        void onHeaderSelected();
-
-        /** Invoked whenever header view is clicked. */
-        void onHeaderClicked();
-    }
-
-    /** The runnable object that is executed whenever user taps the header suggestion. */
-    public static final WritableObjectPropertyKey<Delegate> DELEGATE =
-            new WritableObjectPropertyKey<>();
-    /** The collapsed state of the header suggestion. */
-    public static final WritableBooleanPropertyKey IS_COLLAPSED = new WritableBooleanPropertyKey();
     /** The text content to be displayed as a header text. */
     public static final WritableObjectPropertyKey<String> TITLE = new WritableObjectPropertyKey<>();
-    /** The flag to state whether to remove the header chevron. */
-    public static final WritableBooleanPropertyKey SHOULD_REMOVE_CHEVRON =
-            new WritableBooleanPropertyKey();
     /**
      * The flag to state whether to use the updated padding on suggestion header for omnibox revamp
      * phase 2.
@@ -38,8 +21,8 @@
     public static final WritableBooleanPropertyKey USE_MODERNIZED_HEADER_PADDING =
             new WritableBooleanPropertyKey();
 
-    public static final PropertyKey[] ALL_UNIQUE_KEYS = new PropertyKey[] {
-            DELEGATE, IS_COLLAPSED, TITLE, SHOULD_REMOVE_CHEVRON, USE_MODERNIZED_HEADER_PADDING};
+    public static final PropertyKey[] ALL_UNIQUE_KEYS =
+            new PropertyKey[] {TITLE, USE_MODERNIZED_HEADER_PADDING};
 
     public static final PropertyKey[] ALL_KEYS =
             PropertyModel.concatKeys(ALL_UNIQUE_KEYS, SuggestionCommonProperties.ALL_KEYS);
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/BUILD.gn b/chrome/browser/ui/android/quickactionsearchwidget/BUILD.gn
index f7f4ba6..db63880 100644
--- a/chrome/browser/ui/android/quickactionsearchwidget/BUILD.gn
+++ b/chrome/browser/ui/android/quickactionsearchwidget/BUILD.gn
@@ -66,8 +66,10 @@
     "java/res/drawable/quick_action_search_widget_xsmall_button_background.xml",
     "java/res/drawable/quick_action_search_widget_xsmall_search_bar_background.xml",
     "java/res/layout/quick_action_search_widget_dino_layout.xml",
+    "java/res/layout/quick_action_search_widget_dino_preview_layout.xml",
     "java/res/layout/quick_action_search_widget_medium_layout.xml",
     "java/res/layout/quick_action_search_widget_small_layout.xml",
+    "java/res/layout/quick_action_search_widget_small_preview_layout.xml",
     "java/res/layout/quick_action_search_widget_xsmall_layout.xml",
     "java/res/values/dimens.xml",
     "java/res/values/styles.xml",
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/OWNERS b/chrome/browser/ui/android/quickactionsearchwidget/OWNERS
index f4418aa..da4be492 100644
--- a/chrome/browser/ui/android/quickactionsearchwidget/OWNERS
+++ b/chrome/browser/ui/android/quickactionsearchwidget/OWNERS
@@ -1,3 +1,2 @@
 ender@google.com
-fgorski@chromium.org
-wylieb@chromium.org
\ No newline at end of file
+wylieb@chromium.org
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_dino_layout.xml b/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_dino_layout.xml
index d5419f7..508e491 100644
--- a/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_dino_layout.xml
+++ b/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_dino_layout.xml
@@ -11,13 +11,21 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
-    tools:ignore="UselessParent">
+    tools:ignore="UselessParent,MergeRootFrame">
 
-  <!-- Note that the parent layout is not "useless". We use it to enforce a particular
-    aspect ratio of the Dino widget. There are very few layouts that are permitted to be
-    used with RemoteViews, ConstraintLayout and is sadly not one of them.
-    Please check the QuickActionSearchWidgetProviderDelegate to see how the sizes are
-    enforced for this widget. -->
+  <!-- Notes:
+    - the parent layout is not "useless". We use it to enforce a particular
+      aspect ratio of the Dino widget. There are very few layouts that are permitted to be
+      used with RemoteViews, ConstraintLayout and is sadly not one of them.
+      Please check the QuickActionSearchWidgetProviderDelegate to see how the sizes are
+      enforced for this widget.
+    - root frame merging is not really feasible, because this layout, and the preview layout
+      currently serve different purposes. the layout here is intended to be able to encompass
+      the entire area assigned to the widget on the launcher screen, while the preview layout
+      has fixed size for preview purposes.
+      In other words: we want the widget to cover the entier available area, but we want to
+      confine its bounds for the preview.
+  -->
 
   <LinearLayout
       android:id="@+id/dino_quick_action_button"
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_dino_preview_layout.xml b/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_dino_preview_layout.xml
new file mode 100644
index 0000000..1e2d35fd
--- /dev/null
+++ b/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_dino_preview_layout.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="200dp"
+    android:layout_height="200dp"
+    tools:ignore="UselessParent">
+    <include layout="@layout/quick_action_search_widget_dino_layout" />
+</FrameLayout>
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_small_preview_layout.xml b/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_small_preview_layout.xml
new file mode 100644
index 0000000..fd1fb95b
--- /dev/null
+++ b/chrome/browser/ui/android/quickactionsearchwidget/java/res/layout/quick_action_search_widget_small_preview_layout.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="350dp"
+    android:layout_height="100dp"
+    tools:ignore="UselessParent">
+    <include layout="@layout/quick_action_search_widget_small_layout" />
+</FrameLayout>
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/java/res/xml/quick_action_search_widget_dino_info.xml b/chrome/browser/ui/android/quickactionsearchwidget/java/res/xml/quick_action_search_widget_dino_info.xml
index de702c8..ffeaed30 100644
--- a/chrome/browser/ui/android/quickactionsearchwidget/java/res/xml/quick_action_search_widget_dino_info.xml
+++ b/chrome/browser/ui/android/quickactionsearchwidget/java/res/xml/quick_action_search_widget_dino_info.xml
@@ -10,5 +10,6 @@
     android:minWidth="@dimen/quick_action_search_widget_dino_min_size"
     android:minHeight="@dimen/quick_action_search_widget_dino_min_size"
     android:initialLayout="@layout/quick_action_search_widget_dino_layout"
+    android:previewLayout="@layout/quick_action_search_widget_dino_preview_layout"
     android:previewImage="@drawable/quick_action_search_widget_dino_preview"
     android:widgetCategory="home_screen" />
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/java/res/xml/quick_action_search_widget_small_info.xml b/chrome/browser/ui/android/quickactionsearchwidget/java/res/xml/quick_action_search_widget_small_info.xml
index b009048..1ae41f1 100644
--- a/chrome/browser/ui/android/quickactionsearchwidget/java/res/xml/quick_action_search_widget_small_info.xml
+++ b/chrome/browser/ui/android/quickactionsearchwidget/java/res/xml/quick_action_search_widget_small_info.xml
@@ -13,4 +13,5 @@
     android:minResizeHeight="@dimen/quick_action_search_widget_xsmall_height"
     android:resizeMode="vertical|horizontal"
     android:previewImage="@drawable/quick_action_search_widget_small_preview"
+    android:previewLayout="@layout/quick_action_search_widget_small_preview_layout"
     android:widgetCategory="home_screen|searchbox" />
diff --git a/chrome/browser/ui/android/searchactivityutils/OWNERS b/chrome/browser/ui/android/searchactivityutils/OWNERS
index f4418aa..da4be492 100644
--- a/chrome/browser/ui/android/searchactivityutils/OWNERS
+++ b/chrome/browser/ui/android/searchactivityutils/OWNERS
@@ -1,3 +1,2 @@
 ender@google.com
-fgorski@chromium.org
-wylieb@chromium.org
\ No newline at end of file
+wylieb@chromium.org
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl.cc b/chrome/browser/ui/ash/projector/projector_client_impl.cc
index 47eb904..d6462c82 100644
--- a/chrome/browser/ui/ash/projector/projector_client_impl.cc
+++ b/chrome/browser/ui/ash/projector/projector_client_impl.cc
@@ -13,8 +13,10 @@
 #include "ash/webui/projector_app/projector_app_client.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
 #include "base/bind.h"
+#include "base/containers/fixed_flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/functional/callback_helpers.h"
+#include "base/strings/string_util.h"
 #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
 #include "chrome/browser/ash/system_web_apps/types/system_web_app_type.h"
 #include "chrome/browser/browser_process.h"
@@ -48,6 +50,27 @@
   return g_browser_process->GetApplicationLocale();
 }
 
+inline const std::string GetLocaleOrLanguageForServerSideRecognition() {
+  const std::string& locale = g_browser_process->GetApplicationLocale();
+
+  // Some languages and locales need to be mapped to the default
+  // languages/locales provided by the server side speech recognition service.
+  static constexpr auto kSupportedLanguagesAndLocales =
+      base::MakeFixedFlatMap<base::StringPiece, base::StringPiece>(
+          {{"zh", "cmn-hant-tw"},
+           {"zh-tw", "cmn-hant-tw"},
+           {"ar", "ar-x-maghrebi"}});
+
+  base::fixed_flat_map<base::StringPiece, base::StringPiece,
+                       /*size_t=*/3>::const_iterator it =
+      kSupportedLanguagesAndLocales.find(base::ToLowerASCII(locale));
+  if (it != kSupportedLanguagesAndLocales.end()) {
+    return std::string(it->second);
+  }
+
+  return locale;
+}
+
 }  // namespace
 
 // static
@@ -90,12 +113,12 @@
 ProjectorClientImpl::GetSpeechRecognitionAvailability() const {
   ash::SpeechRecognitionAvailability availability;
   availability.use_on_device = true;
-  const auto& locale = GetLocale();
   availability.on_device_availability = SpeechRecognitionRecognizerClientImpl::
-      GetOnDeviceSpeechRecognitionAvailability(locale);
+      GetOnDeviceSpeechRecognitionAvailability(GetLocale());
   availability.server_based_availability =
       SpeechRecognitionRecognizerClientImpl::
-          GetServerBasedRecognitionAvailability(locale);
+          GetServerBasedRecognitionAvailability(
+              GetLocaleOrLanguageForServerSideRecognition());
 
   if (ash::features::ShouldForceEnableServerSideSpeechRecognitionForDev() ||
       (availability.on_device_availability !=
@@ -113,11 +136,16 @@
   DCHECK(availability.IsAvailable());
   DCHECK_EQ(speech_recognizer_.get(), nullptr);
   recognizer_status_ = SPEECH_RECOGNIZER_OFF;
+  const std::string locale =
+      availability.use_on_device
+          ? GetLocale()
+          : GetLocaleOrLanguageForServerSideRecognition();
+
   speech_recognizer_ = std::make_unique<SpeechRecognitionRecognizerClientImpl>(
       weak_ptr_factory_.GetWeakPtr(), ProfileManager::GetActiveUserProfile(),
       media::mojom::SpeechRecognitionOptions::New(
           media::mojom::SpeechRecognitionMode::kCaption,
-          /*enable_formatting=*/true, GetLocale(),
+          /*enable_formatting=*/true, locale,
           /*is_server_based=*/!availability.use_on_device,
           media::mojom::RecognizerClientType::kProjector));
 }
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl_unittest.cc b/chrome/browser/ui/ash/projector/projector_client_impl_unittest.cc
index d8750ec..c40a1375 100644
--- a/chrome/browser/ui/ash/projector/projector_client_impl_unittest.cc
+++ b/chrome/browser/ui/ash/projector/projector_client_impl_unittest.cc
@@ -204,7 +204,9 @@
 
 namespace {
 
+const char kArabic[] = "ar";
 const char kFrench[] = "fr";
+const char kChinese[] = "zh-TW";
 const char kUnsupportedLanguage[] = "am";
 
 bool IsEqualAvailability(const SpeechRecognitionAvailability& first,
@@ -235,6 +237,14 @@
   if (server_based_available) {
     EXPECT_TRUE(IsEqualAvailability(
         projector_client_->GetSpeechRecognitionAvailability(), availability));
+
+    SetLocale(kArabic);
+    EXPECT_TRUE(IsEqualAvailability(
+        projector_client_->GetSpeechRecognitionAvailability(), availability));
+
+    SetLocale(kChinese);
+    EXPECT_TRUE(IsEqualAvailability(
+        projector_client_->GetSpeechRecognitionAvailability(), availability));
   } else {
     availability.use_on_device = true;
     availability.on_device_availability =
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
index 0e36782..c5e5de2 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
+++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
@@ -119,11 +119,11 @@
 // So this function gives WallpaperManager independent hashing method to break
 // this dependency.
 std::string HashWallpaperFilesIdStr(const std::string& files_id_unhashed) {
-  chromeos::SystemSaltGetter* salt_getter = chromeos::SystemSaltGetter::Get();
+  ash::SystemSaltGetter* salt_getter = ash::SystemSaltGetter::Get();
   DCHECK(salt_getter);
 
   // System salt must be defined at this point.
-  const chromeos::SystemSaltGetter::RawSalt* salt = salt_getter->GetRawSalt();
+  const ash::SystemSaltGetter::RawSalt* salt = salt_getter->GetRawSalt();
   if (!salt)
     LOG(FATAL) << "WallpaperManager HashWallpaperFilesIdStr(): no salt!";
 
@@ -141,8 +141,8 @@
 
 // Returns true if wallpaper files id can be returned successfully.
 bool CanGetFilesId() {
-  return chromeos::SystemSaltGetter::IsInitialized() &&
-         chromeos::SystemSaltGetter::Get()->GetRawSalt();
+  return ash::SystemSaltGetter::IsInitialized() &&
+         ash::SystemSaltGetter::Get()->GetRawSalt();
 }
 
 void GetFilesIdSaltReady(
@@ -508,7 +508,7 @@
 void WallpaperControllerClientImpl::GetFilesId(
     const AccountId& account_id,
     base::OnceCallback<void(const std::string&)> files_id_callback) const {
-  chromeos::SystemSaltGetter::Get()->AddOnSystemSaltReady(base::BindOnce(
+  ash::SystemSaltGetter::Get()->AddOnSystemSaltReady(base::BindOnce(
       &GetFilesIdSaltReady, account_id, std::move(files_id_callback)));
 }
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
index 93949fd..9e2e18e 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -197,8 +197,14 @@
     base::TimeTicks match_selection_timestamp) {
   DCHECK(HasMatchAt(index));
 
-  omnibox_view_->OpenMatch(edit_model_->result().match_at(index), disposition,
-                           GURL(), std::u16string(), index,
+  // This is needed for mouse clicks and gestures to respect takeover actions.
+  auto& match = edit_model_->result().match_at(index);
+  if (match.action && match.action->TakesOverMatch()) {
+    return edit_model_->ExecuteAction(match, index, match_selection_timestamp,
+                                      disposition);
+  }
+
+  omnibox_view_->OpenMatch(match, disposition, GURL(), std::u16string(), index,
                            match_selection_timestamp);
 }
 
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 3b38c43a..889ddc5 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -5,9 +5,11 @@
 #include <codecvt>
 #include <string>
 
+#include "base/barrier_callback.h"
 #include "base/containers/contains.h"
 #include "base/feature_list.h"
 #include "base/files/file_util.h"
+#include "base/functional/callback.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
@@ -1602,9 +1604,28 @@
   EXPECT_EQ(browser()->tab_strip_model()->count(), 1);
 }
 
-using WebAppBrowserTest_UpdateShortcuts = WebAppBrowserTest;
+class WebAppBrowserTestUpdateShortcutResult
+    : public WebAppBrowserTest,
+      public ::testing::WithParamInterface<OsIntegrationSubManagersState> {
+ public:
+  WebAppBrowserTestUpdateShortcutResult() {
+    if (GetParam() == OsIntegrationSubManagersState::kEnabled) {
+      scoped_feature_list_.InitWithFeaturesAndParameters(
+          {{features::kOsIntegrationSubManagers, {{"stage", "write_config"}}}},
+          /*disabled_features=*/{});
+    } else {
+      scoped_feature_list_.InitWithFeatures(
+          {}, {features::kOsIntegrationSubManagers});
+    }
+  }
 
-IN_PROC_BROWSER_TEST_F(WebAppBrowserTest_UpdateShortcuts, UpdateShortcut) {
+  ~WebAppBrowserTestUpdateShortcutResult() override = default;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_P(WebAppBrowserTestUpdateShortcutResult, UpdateShortcut) {
 #if BUILDFLAG(IS_MAC)
   base::AutoReset<bool> scope_shortcut_app_update(
       &g_app_shims_allow_update_and_launch_in_tests, true);
@@ -1639,8 +1660,26 @@
 
   base::HistogramTester tester;
   base::test::TestFuture<Result> result;
+
+  auto synchronize_barrier = base::BarrierCallback<Result>(
+      /*num_callbacks=*/2,
+      base::BindOnce(
+          [&](base::OnceCallback<void(Result)> result_callback,
+              std::vector<Result> final_results) {
+            DCHECK_EQ(2u, final_results.size());
+            Result final_result = Result::kOk;
+            if (final_results[0] == Result::kError ||
+                final_results[1] == Result::kError) {
+              final_result = Result::kError;
+            }
+            std::move(result_callback).Run(final_result);
+          },
+          result.GetCallback()));
+
   provider->os_integration_manager().UpdateShortcuts(
-      app_id, "Manifest test app", result.GetCallback());
+      app_id, "Manifest test app", synchronize_barrier);
+  provider->os_integration_manager().Synchronize(
+      app_id, base::BindOnce(synchronize_barrier, Result::kOk));
   ASSERT_TRUE(result.Wait());
   EXPECT_THAT(result.Get(), testing::Eq(Result::kOk));
 
@@ -1663,6 +1702,13 @@
   EXPECT_EQ(shortcut_info->title, u"test_app_2");
 }
 
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    WebAppBrowserTestUpdateShortcutResult,
+    ::testing::Values(OsIntegrationSubManagersState::kEnabled,
+                      OsIntegrationSubManagersState::kDisabled),
+    test::GetOsIntegrationSubManagersTestName);
+
 // Tests that reparenting a display: browser app tab results in a minimal-ui
 // app window.
 IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, ReparentDisplayBrowserApp) {
diff --git a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
index ea28fe7..993eddc 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
+++ b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
@@ -349,6 +349,8 @@
   options.add_to_desktop = locations.on_desktop;
   options.add_to_quick_launch_bar = locations.in_quick_launch_bar;
   options.os_hooks[OsHookType::kRunOnOsLogin] = locations.in_startup;
+  // TODO(crbug.com/1401125): Remove InstallOsHooks() once OS integration
+  // sub managers have been implemented.
   os_integration_manager_->InstallOsHooks(app_id, base::DoNothing(), nullptr,
                                           options);
   os_integration_manager_->Synchronize(app_id, base::DoNothing());
diff --git a/chrome/browser/ui/webui/about_ui.cc b/chrome/browser/ui/webui/about_ui.cc
index 28871062..5a7c2b7 100644
--- a/chrome/browser/ui/webui/about_ui.cc
+++ b/chrome/browser/ui/webui/about_ui.cc
@@ -75,7 +75,7 @@
 #include "chrome/browser/component_updater/cros_component_manager.h"
 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
 #include "chrome/common/webui_url_constants.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/language/core/common/locale_util.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 #endif
diff --git a/chrome/browser/ui/webui/about_ui_unittest.cc b/chrome/browser/ui/webui/about_ui_unittest.cc
index 7dea398..8895b123 100644
--- a/chrome/browser/ui/webui/about_ui_unittest.cc
+++ b/chrome/browser/ui/webui/about_ui_unittest.cc
@@ -25,8 +25,8 @@
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/scoped_browser_locale.h"
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/ui/webui/ash/login/l10n_util_unittest.cc b/chrome/browser/ui/webui/ash/login/l10n_util_unittest.cc
index cb2bea3..7fe9915 100644
--- a/chrome/browser/ui/webui/ash/login/l10n_util_unittest.cc
+++ b/chrome/browser/ui/webui/ash/login/l10n_util_unittest.cc
@@ -15,7 +15,7 @@
 #include "chrome/browser/ash/customization/customization_document.h"
 #include "chrome/browser/ash/input_method/input_method_configuration.h"
 #include "chrome/browser/ui/webui/ash/login/l10n_util_test_util.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ime/ash/component_extension_ime_manager.h"
 #include "ui/base/ime/ash/mock_component_extension_ime_manager_delegate.h"
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chrome/browser/ui/webui/extensions/extensions_ui.cc
index 1179c4a..5b49aeb2 100644
--- a/chrome/browser/ui/webui/extensions/extensions_ui.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -295,6 +295,7 @@
     {"sitePermissionsOnClick", IDS_EXTENSIONS_SITE_PERMISSIONS_ON_CLICK},
     {"sitePermissionsViewAllSites",
      IDS_EXTENSIONS_SITE_PERMISSIONS_VIEW_ALL_SITES},
+    {"siteSettings", IDS_EXTENSIONS_SITE_SETTINGS},
     {"permittedSites", IDS_EXTENSIONS_PERMITTED_SITES},
     {"restrictedSites", IDS_EXTENSIONS_RESTRICTED_SITES},
     {"noSitesAdded", IDS_EXTENSIONS_NO_SITES_ADDED},
diff --git a/chrome/browser/ui/webui/management/management_ui_handler.cc b/chrome/browser/ui/webui/management/management_ui_handler.cc
index f8ed735..88a6d2a 100644
--- a/chrome/browser/ui/webui/management/management_ui_handler.cc
+++ b/chrome/browser/ui/webui/management/management_ui_handler.cc
@@ -323,8 +323,13 @@
   bool report_audio_status = false;
   ash::CrosSettings::Get()->GetBoolean(ash::kReportDeviceAudioStatus,
                                        &report_audio_status);
+  // TODO(b/262295601): Add/refine management strings corresponding to XDR
+  // reporting policy.
+  bool device_report_xdr_events = false;
+  ash::CrosSettings::Get()->GetBoolean(ash::kDeviceReportXDREvents,
+                                       &device_report_xdr_events);
   if (collector->IsReportingActivityTimes() || report_device_peripherals ||
-      report_audio_status ||
+      report_audio_status || device_report_xdr_events ||
       profile->GetPrefs()->GetBoolean(::prefs::kInsightsExtensionEnabled)) {
     AddDeviceReportingElement(report_sources, kManagementReportActivityTimes,
                               DeviceReportingType::kDeviceActivity);
@@ -347,7 +352,7 @@
     AddDeviceReportingElement(report_sources, kManagementReportCrashReports,
                               DeviceReportingType::kCrashReport);
   }
-  if (collector->IsReportingAppInfoAndActivity()) {
+  if (collector->IsReportingAppInfoAndActivity() || device_report_xdr_events) {
     AddDeviceReportingElement(report_sources,
                               kManagementReportAppInfoAndActivity,
                               DeviceReportingType::kAppInfoAndActivity);
diff --git a/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc b/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc
index 378817b..38e3c38 100644
--- a/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc
+++ b/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc
@@ -75,8 +75,8 @@
 #include "chromeos/ash/components/network/network_state_handler.h"
 #include "chromeos/ash/components/network/proxy/proxy_config_handler.h"
 #include "chromeos/ash/components/network/proxy/ui_proxy_config_service.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/dbus/power/power_manager_client.h"
-#include "chromeos/system/fake_statistics_provider.h"
 #include "components/account_id/account_id.h"
 #include "components/enterprise/browser/reporting/common_pref_names.h"
 #include "components/onc/onc_pref_names.h"
@@ -351,6 +351,7 @@
     bool report_dlp_events;
     bool report_audio_status;
     bool report_device_peripherals;
+    bool device_report_xdr_events;
     bool upload_enabled;
     bool printing_send_username_and_filename;
     bool crostini_report_usage;
@@ -376,6 +377,7 @@
     setup_config_.report_dlp_events = default_value;
     setup_config_.report_audio_status = default_value;
     setup_config_.report_device_peripherals = default_value;
+    setup_config_.device_report_xdr_events = default_value;
     setup_config_.upload_enabled = default_value;
     setup_config_.printing_send_username_and_filename = default_value;
     setup_config_.crostini_report_usage = default_value;
@@ -446,6 +448,8 @@
     settings_.device_settings()->SetBoolean(
         ash::kReportDevicePeripherals,
         GetTestConfig().report_device_peripherals);
+    settings_.device_settings()->SetBoolean(
+        ash::kDeviceReportXDREvents, GetTestConfig().device_report_xdr_events);
     profile_->GetPrefs()->SetBoolean(
         prefs::kPrintingSendUsernameAndFilenameEnabled,
         GetTestConfig().printing_send_username_and_filename);
@@ -1141,6 +1145,17 @@
   ASSERT_PRED_FORMAT2(ReportingElementsToBeEQ, info, expected_elements);
 }
 
+TEST_F(ManagementUIHandlerTests, ReportDeviceXdrEventsEnabled) {
+  ResetTestConfig(false);
+  GetTestConfig().device_report_xdr_events = true;
+  const base::Value::List info = SetUpForReportingInfo();
+  const std::map<std::string, std::string> expected_elements = {
+      {kManagementReportActivityTimes, "device activity"},
+      {kManagementReportAppInfoAndActivity, "app info and activity"}};
+
+  ASSERT_PRED_FORMAT2(ReportingElementsToBeEQ, info, expected_elements);
+}
+
 TEST_F(ManagementUIHandlerTests,
        ShowPrivacyDisclosureForSecureDnsWithIdentifiers) {
   ResetTestConfig();
diff --git a/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.cc b/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.cc
index cb52a0a..c246ae5 100644
--- a/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.cc
+++ b/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.cc
@@ -34,7 +34,7 @@
 // chrome/browser/resources/nearby_internals/types.js.
 enum class Direction { kRequest = 0, kResponse = 1 };
 
-std::string FormatAsJSON(const base::Value& value) {
+std::string FormatAsJSON(const base::Value::Dict& value) {
   std::string json;
   base::JSONWriter::WriteWithOptions(
       value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
@@ -56,7 +56,7 @@
 
 // Converts a RPC request/response to a raw dictionary value used as a
 // JSON argument to JavaScript functions.
-base::Value::Dict HttpMessageToDictionary(const base::Value& message,
+base::Value::Dict HttpMessageToDictionary(const base::Value::Dict& message,
                                           Direction dir,
                                           Rpc rpc) {
   base::Value::Dict dictionary;
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom b/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
index 384e4eb5..ca7daa90d01 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
@@ -182,6 +182,14 @@
   array<PromoPart> middle_slot_parts;
 };
 
+// Used to determine available modules for the NTP.
+struct ModuleIdName {
+  // A unique identifier associated with a module.
+  string id;
+  // The name associated with a module.
+  string name;
+};
+
 // Action the user performed while using the customize dialog. Used for metrics
 // logging only. Actions correspond to items in NTPLoggingEventType.
 enum CustomizeDialogAction {
@@ -263,6 +271,8 @@
   // `module_ids` is an array of module descriptor ids that identifies the
   // modules that loaded with data.
   OnModulesLoadedWithData(array<string> module_ids);
+  // Returns a list of available module id and name pairs.
+  GetModulesIdNames() => (array<ModuleIdName> data);
   // Sets the order of modules.
   SetModulesOrder(array<string> module_ids);
   // Returns the order of modules or an empty array if the user has not
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
index f312a3aa..1044884 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/base64.h"
 #include "base/bind.h"
@@ -29,6 +30,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/new_tab_page/modules/new_tab_page_modules.h"
 #include "chrome/browser/new_tab_page/promos/promo_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/background/ntp_background_service.h"
@@ -428,7 +430,8 @@
     ThemeService* theme_service,
     search_provider_logos::LogoService* logo_service,
     content::WebContents* web_contents,
-    const base::Time& ntp_navigation_start_time)
+    const base::Time& ntp_navigation_start_time,
+    const std::vector<std::pair<const std::string, int>> module_id_names)
     : ntp_background_service_(
           NtpBackgroundServiceFactory::GetForProfile(profile)),
       ntp_custom_background_service_(ntp_custom_background_service),
@@ -438,6 +441,7 @@
       profile_(profile),
       web_contents_(web_contents),
       ntp_navigation_start_time_(ntp_navigation_start_time),
+      module_id_names_(module_id_names),
       logger_(profile,
               GURL(chrome::kChromeUINewTabPageURL),
               ntp_navigation_start_time),
@@ -699,6 +703,18 @@
                                                   web_contents_, 0);
 }
 
+void NewTabPageHandler::GetModulesIdNames(GetModulesIdNamesCallback callback) {
+  std::vector<new_tab_page::mojom::ModuleIdNamePtr> modules_details;
+  for (const auto& id_name_pair : module_id_names_) {
+    auto module_id_name = new_tab_page::mojom::ModuleIdName::New();
+    module_id_name->id = id_name_pair.first;
+    module_id_name->name = l10n_util::GetStringUTF8(id_name_pair.second);
+    modules_details.push_back(std::move(module_id_name));
+  }
+
+  std::move(callback).Run(std::move(modules_details));
+}
+
 void NewTabPageHandler::SetModulesOrder(
     const std::vector<std::string>& module_ids) {
   base::Value::List module_ids_value;
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
index 5f8ce62..b6aa35c9 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
@@ -5,7 +5,9 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_NEW_TAB_PAGE_NEW_TAB_PAGE_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_NEW_TAB_PAGE_NEW_TAB_PAGE_HANDLER_H_
 
+#include <string>
 #include <unordered_map>
+#include <utility>
 #include <vector>
 
 #include "base/memory/raw_ptr.h"
@@ -58,15 +60,17 @@
                           public ui::SelectFileDialog::Listener,
                           public PromoServiceObserver {
  public:
-  NewTabPageHandler(mojo::PendingReceiver<new_tab_page::mojom::PageHandler>
-                        pending_page_handler,
-                    mojo::PendingRemote<new_tab_page::mojom::Page> pending_page,
-                    Profile* profile,
-                    NtpCustomBackgroundService* ntp_custom_background_service,
-                    ThemeService* theme_service,
-                    search_provider_logos::LogoService* logo_service,
-                    content::WebContents* web_contents,
-                    const base::Time& ntp_navigation_start_time);
+  NewTabPageHandler(
+      mojo::PendingReceiver<new_tab_page::mojom::PageHandler>
+          pending_page_handler,
+      mojo::PendingRemote<new_tab_page::mojom::Page> pending_page,
+      Profile* profile,
+      NtpCustomBackgroundService* ntp_custom_background_service,
+      ThemeService* theme_service,
+      search_provider_logos::LogoService* logo_service,
+      content::WebContents* web_contents,
+      const base::Time& ntp_navigation_start_time,
+      const std::vector<std::pair<const std::string, int>> module_id_names);
 
   NewTabPageHandler(const NewTabPageHandler&) = delete;
   NewTabPageHandler& operator=(const NewTabPageHandler&) = delete;
@@ -108,6 +112,7 @@
   void UpdateDisabledModules() override;
   void OnModulesLoadedWithData(
       const std::vector<std::string>& module_ids) override;
+  void GetModulesIdNames(GetModulesIdNamesCallback callback) override;
   void SetModulesOrder(const std::vector<std::string>& module_ids) override;
   void GetModulesOrder(GetModulesOrderCallback callback) override;
   void IncrementModulesShownCount() override;
@@ -199,6 +204,7 @@
   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
   raw_ptr<content::WebContents> web_contents_;
   base::Time ntp_navigation_start_time_;
+  const std::vector<std::pair<const std::string, int>> module_id_names_;
   NTPUserDataLogger logger_;
   std::unordered_map<const network::SimpleURLLoader*,
                      std::unique_ptr<network::SimpleURLLoader>>
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
index d4dab520..94b7c7f6 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
@@ -2,7 +2,10 @@
 // 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/new_tab_page/new_tab_page_handler.h"
+#include <string>
+#include <utility>
+#include <vector>
+
 #include "base/memory/raw_ptr.h"
 #include "base/memory/raw_ref.h"
 #include "base/memory/ref_counted_memory.h"
@@ -26,10 +29,12 @@
 #include "chrome/browser/ui/hats/hats_service_factory.h"
 #include "chrome/browser/ui/hats/mock_hats_service.h"
 #include "chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom.h"
+#include "chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
@@ -215,11 +220,13 @@
         .Times(1);
     webui::SetThemeProviderForTesting(&mock_theme_provider_);
     web_contents_->SetColorProviderSource(&mock_color_provider_source_);
+    const std::vector<std::pair<const std::string, int>> module_id_names = {
+        {"recipe_tasks", IDS_NTP_MODULES_RECIPE_TASKS_SENTENCE}};
     handler_ = std::make_unique<NewTabPageHandler>(
         mojo::PendingReceiver<new_tab_page::mojom::PageHandler>(),
         mock_page_.BindAndGetRemote(), profile_.get(),
         &mock_ntp_custom_background_service_, &mock_theme_service_,
-        &mock_logo_service_, web_contents_, base::Time::Now());
+        &mock_logo_service_, web_contents_, base::Time::Now(), module_id_names);
     mock_page_.FlushForTesting();
     EXPECT_EQ(handler_.get(), theme_service_observer_);
     EXPECT_EQ(handler_.get(), ntp_custom_background_service_observer_);
@@ -699,6 +706,25 @@
       "gen_204?atype=i&ct=doodle&ntp=2&cad=sh,5,ct:food_id&ei=bar_id"));
 }
 
+TEST_F(NewTabPageHandlerTest, GetModulesIdNames) {
+  std::vector<new_tab_page::mojom::ModuleIdNamePtr> modules_details;
+  base::MockCallback<NewTabPageHandler::GetModulesIdNamesCallback> callback;
+  EXPECT_CALL(callback, Run(_))
+      .Times(1)
+      .WillOnce(testing::Invoke(
+          [&modules_details](
+              std::vector<new_tab_page::mojom::ModuleIdNamePtr> arg) {
+            modules_details = std::move(arg);
+          }));
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures(
+      /*enabled_features=*/{ntp_features::kNtpRecipeTasksModule},
+      /*disabled_features=*/{});
+  handler_->GetModulesIdNames(callback.Get());
+  EXPECT_EQ(modules_details.size(), 1u);
+  EXPECT_EQ(modules_details.front()->id, "recipe_tasks");
+}
+
 TEST_F(NewTabPageHandlerTest, GetModulesOrder) {
   std::vector<std::string> module_ids;
   base::MockCallback<NewTabPageHandler::GetModulesOrderCallback> callback;
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index 548dc94..3a26125 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/cart/cart_handler.h"
 #include "chrome/browser/new_tab_page/modules/drive/drive_handler.h"
 #include "chrome/browser/new_tab_page/modules/feed/feed_handler.h"
+#include "chrome/browser/new_tab_page/modules/new_tab_page_modules.h"
 #include "chrome/browser/new_tab_page/modules/photos/photos_handler.h"
 #include "chrome/browser/new_tab_page/modules/recipes/recipes_handler.h"
 #include "chrome/browser/new_tab_page/new_tab_page_util.h"
@@ -96,14 +97,6 @@
 constexpr char kPrevNavigationTimePrefName[] = "NewTabPage.PrevNavigationTime";
 constexpr char kSignedOutNtpModulesSwitch[] = "signed-out-ntp-modules";
 
-const std::pair<const char*, const base::Feature&> kModuleFeatures[] = {
-#if !defined(OFFICIAL_BUILD)
-    {"dummyModulesEnabled", ntp_features::kNtpDummyModules},
-#endif
-    {"photosModuleEnabled", ntp_features::kNtpPhotosModule},
-    {"feedModuleEnabled", ntp_features::kNtpFeedModule},
-};
-
 void AddRawStringOrDefault(content::WebUIDataSource* source,
                            const char key[],
                            const std::string str,
@@ -473,12 +466,6 @@
                                IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_CONTENT);
   }
 
-  for (const auto& nameFeature : kModuleFeatures) {
-    source->AddBoolean(nameFeature.first,
-                       base::FeatureList::IsEnabled(nameFeature.second));
-  }
-  source->AddBoolean("recipeTasksModuleEnabled", IsRecipeTasksModuleEnabled());
-  source->AddBoolean("chromeCartModuleEnabled", IsCartModuleEnabled());
   source->AddString("photosModuleCustomArtWork",
                     base::GetFieldTrialParamValueByFeature(
                         ntp_features::kNtpPhotosModuleCustomizedOptInArtWork,
@@ -556,7 +543,9 @@
       // We initialize navigation_start_time_ to a reasonable value to account
       // for the unlikely case where the NewTabPageHandler is created before we
       // received the DidStartNavigation event.
-      navigation_start_time_(base::Time::Now()) {
+      navigation_start_time_(base::Time::Now()),
+      module_id_names_(ntp::MakeModuleIdNames(
+          NewTabPageUI::IsDriveModuleEnabledForProfile(profile_))) {
   auto* source = CreateNewTabPageUiHtmlSource(profile_);
   source->AddBoolean(
       "customBackgroundDisabledByPolicy",
@@ -766,11 +755,12 @@
     mojo::PendingReceiver<new_tab_page::mojom::PageHandler>
         pending_page_handler) {
   DCHECK(pending_page.is_valid());
+
   page_handler_ = std::make_unique<NewTabPageHandler>(
       std::move(pending_page_handler), std::move(pending_page), profile_,
       ntp_custom_background_service_, theme_service_,
       LogoServiceFactory::GetForProfile(profile_), web_contents(),
-      navigation_start_time_);
+      navigation_start_time_, module_id_names_);
 }
 
 void NewTabPageUI::CreateCustomizeThemesHandler(
@@ -855,6 +845,7 @@
   if (navigation_handle->IsInPrimaryMainFrame() &&
       navigation_handle->GetURL() == GURL(chrome::kChromeUINewTabPageURL)) {
     navigation_start_time_ = base::Time::Now();
+
     OnLoad();
     auto prev_navigation_time =
         profile_->GetPrefs()->GetTime(kPrevNavigationTimePrefName);
@@ -894,25 +885,17 @@
 void NewTabPageUI::OnLoad() {
   base::Value::Dict update;
   update.Set("navigationStartTime", navigation_start_time_.ToJsTime());
-  bool driveModuleEnabled =
-      NewTabPageUI::IsDriveModuleEnabledForProfile(profile_);
-  bool anyModuleEnabled = driveModuleEnabled;
-  for (const auto& nameFeature : kModuleFeatures) {
-    anyModuleEnabled |= base::FeatureList::IsEnabled(nameFeature.second);
-  }
-  anyModuleEnabled |= IsRecipeTasksModuleEnabled();
-  anyModuleEnabled |= IsCartModuleEnabled();
+
   // Only enable modules if account credentials are available as most modules
   // won't have data to render otherwise. We can override this behavior with the
   // "--signed-out-ntp-modules" command line switch, e.g. to allow modules in
   // perf tests, which do not support sign-in.
   update.Set("modulesEnabled",
-             anyModuleEnabled &&
+             !module_id_names_.empty() &&
                  !base::FeatureList::IsEnabled(ntp_features::kNtpModulesLoad) &&
                  (base::CommandLine::ForCurrentProcess()->HasSwitch(
                       kSignedOutNtpModulesSwitch) ||
                   HasCredentials(profile_)));
-  update.Set("driveModuleEnabled", driveModuleEnabled);
   content::WebUIDataSource::Update(profile_, chrome::kChromeUINewTabPageHost,
                                    std::move(update));
 }
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
index fe09f34..f35f31f5 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
@@ -5,6 +5,10 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_NEW_TAB_PAGE_NEW_TAB_PAGE_UI_H_
 #define CHROME_BROWSER_UI_WEBUI_NEW_TAB_PAGE_NEW_TAB_PAGE_UI_H_
 
+#include <string>
+#include <utility>
+#include <vector>
+
 #include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
 #include "chrome/browser/cart/chrome_cart.mojom.h"
@@ -244,6 +248,7 @@
   // Time the NTP started loading. Used for logging the WebUI NTP's load
   // performance.
   base::Time navigation_start_time_;
+  const std::vector<std::pair<const std::string, int>> module_id_names_;
 
   // Mojo implementations for modules:
   std::unique_ptr<RecipesHandler> recipes_handler_;
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
index 2ddb1036..fbe510a 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -1467,6 +1467,8 @@
           base::BindOnce(&AppLauncherHandler::OnOsHooksInstalled,
                          weak_ptr_factory_.GetWeakPtr(), app_id));
 
+  // TODO(crbug.com/1401125): Remove InstallOsHooks() once OS integration
+  // sub managers have been implemented.
   web_app_provider_->os_integration_manager().InstallOsHooks(
       app_id, os_hooks_barrier, /*web_app_info=*/nullptr, std::move(options));
   web_app_provider_->os_integration_manager().Synchronize(
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler_unittest.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler_unittest.cc
index 4bd03b2..c939e224 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler_unittest.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler_unittest.cc
@@ -275,11 +275,4 @@
     AppLauncherHandlerTest,
     ::testing::Values(OsIntegrationSubManagersState::kEnabled,
                       OsIntegrationSubManagersState::kDisabled),
-    [](const ::testing::TestParamInfo<OsIntegrationSubManagersState>& info) {
-      switch (info.param) {
-        case OsIntegrationSubManagersState::kEnabled:
-          return "OSIntegrationSubManagers_Enabled";
-        case OsIntegrationSubManagersState::kDisabled:
-          return "OSIntegrationSubManagers_Disabled";
-      }
-    });
+    web_app::test::GetOsIntegrationSubManagersTestName);
diff --git a/chrome/browser/ui/webui/realbox/realbox_handler.cc b/chrome/browser/ui/webui/realbox/realbox_handler.cc
index a797bb3..9938481 100644
--- a/chrome/browser/ui/webui/realbox/realbox_handler.cc
+++ b/chrome/browser/ui/webui/realbox/realbox_handler.cc
@@ -260,7 +260,9 @@
           std::u16string(), kTabIconResourceName);
     }
 
-    if (match.action &&
+    // Omit actions that takeover the whole match, because the C++ handler
+    // remaps the navigation to execute the action. (Doesn't happen in the JS.)
+    if (match.action && !match.action->TakesOverMatch() &&
         base::FeatureList::IsEnabled(omnibox::kNtpRealboxPedals)) {
       const OmniboxAction::LabelStrings& label_strings =
           match.action->GetLabelStrings();
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc
index 892a81a..fa1e9be 100644
--- a/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -71,8 +71,8 @@
 #include "chromeos/ash/components/fwupd/firmware_update_manager.h"
 #include "chromeos/ash/components/network/network_state.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/dbus/power/power_manager_client.h"
-#include "chromeos/system/statistics_provider.h"
 #include "chromeos/version/version_loader.h"
 #include "components/user_manager/user_manager.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/chrome/browser/ui/webui/settings/protocol_handlers_handler.cc b/chrome/browser/ui/webui/settings/protocol_handlers_handler.cc
index c605911..b0ff859c 100644
--- a/chrome/browser/ui/webui/settings/protocol_handlers_handler.cc
+++ b/chrome/browser/ui/webui/settings/protocol_handlers_handler.cc
@@ -355,9 +355,13 @@
                 handler.web_app_id().value(), handler.protocol());
 
             // Update registration with the OS.
+            // TODO(crbug.com/1401125): Remove UpdateProtocolHandlers() once OS
+            // integration sub managers have been implemented.
             lock.os_integration_manager().UpdateProtocolHandlers(
                 handler.web_app_id().value(),
                 /*force_shortcut_updates_if_needed=*/true, base::DoNothing());
+            lock.os_integration_manager().Synchronize(
+                handler.web_app_id().value(), base::DoNothing());
           },
           std::move(handler)));
 
diff --git a/chrome/browser/web_applications/commands/manifest_update_data_fetch_command.cc b/chrome/browser/web_applications/commands/manifest_update_data_fetch_command.cc
index b80ba8c..4a02627 100644
--- a/chrome/browser/web_applications/commands/manifest_update_data_fetch_command.cc
+++ b/chrome/browser/web_applications/commands/manifest_update_data_fetch_command.cc
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/barrier_callback.h"
 #include "base/feature_list.h"
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_macros.h"
@@ -576,11 +577,26 @@
     return;
   }
 
-  lock_->os_integration_manager().UpdateUrlHandlers(
-      app_id_,
+  auto manifest_update_origin_update_callback = base::BindOnce(
+      &ManifestUpdateDataFetchCommand::OnWebAppOriginAssociationsUpdated,
+      AsWeakPtr());
+  auto synchronize_callback = base::BarrierCallback<bool>(
+      /*num_callbacks = */ 2,
       base::BindOnce(
-          &ManifestUpdateDataFetchCommand::OnWebAppOriginAssociationsUpdated,
-          AsWeakPtr()));
+          [&](base::OnceCallback<void(bool)> final_callback,
+              std::vector<bool> final_results) {
+            DCHECK_EQ(2u, final_results.size());
+            bool final_result = final_results[0] && final_results[1];
+            std::move(final_callback).Run(final_result);
+          },
+          std::move(manifest_update_origin_update_callback)));
+
+  // TODO(crbug.com/1401125): Remove UpdateUrlHandlers() once OS integration
+  // sub managers have been implemented.
+  lock_->os_integration_manager().UpdateUrlHandlers(app_id_,
+                                                    synchronize_callback);
+  lock_->os_integration_manager().Synchronize(
+      app_id_, base::BindOnce(synchronize_callback, true));
 }
 
 void ManifestUpdateDataFetchCommand::OnWebAppOriginAssociationsUpdated(
diff --git a/chrome/browser/web_applications/commands/run_on_os_login_command.cc b/chrome/browser/web_applications/commands/run_on_os_login_command.cc
index 5f97687..c192884 100644
--- a/chrome/browser/web_applications/commands/run_on_os_login_command.cc
+++ b/chrome/browser/web_applications/commands/run_on_os_login_command.cc
@@ -193,6 +193,9 @@
     return;
   }
 
+  // TODO(crbug.com/1401125): Remove InstallOsHooks() and UninstallOsHooks()
+  // once OS integration
+  // sub managers have been implemented.
   if (login_mode_.value() == RunOnOsLoginMode::kNotRun) {
     web_app::OsHooksOptions os_hooks;
     os_hooks[web_app::OsHookType::kRunOnOsLogin] = true;
diff --git a/chrome/browser/web_applications/commands/run_on_os_login_command_unittest.cc b/chrome/browser/web_applications/commands/run_on_os_login_command_unittest.cc
index 86c6a80..1cee1ad 100644
--- a/chrome/browser/web_applications/commands/run_on_os_login_command_unittest.cc
+++ b/chrome/browser/web_applications/commands/run_on_os_login_command_unittest.cc
@@ -513,13 +513,6 @@
     RunOnOsLoginCommandTest,
     ::testing::Values(OsIntegrationSubManagersState::kEnabled,
                       OsIntegrationSubManagersState::kDisabled),
-    [](const ::testing::TestParamInfo<OsIntegrationSubManagersState>& info) {
-      switch (info.param) {
-        case OsIntegrationSubManagersState::kEnabled:
-          return "OSIntegrationSubManagers_Enabled";
-        case OsIntegrationSubManagersState::kDisabled:
-          return "OSIntegrationSubManagers_Disabled";
-      }
-    });
+    test::GetOsIntegrationSubManagersTestName);
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/commands/update_file_handler_command.cc b/chrome/browser/web_applications/commands/update_file_handler_command.cc
index 9052041..ad50b97 100644
--- a/chrome/browser/web_applications/commands/update_file_handler_command.cc
+++ b/chrome/browser/web_applications/commands/update_file_handler_command.cc
@@ -7,7 +7,8 @@
 #include <memory>
 #include <utility>
 
-#include "base/callback.h"
+#include "base/barrier_callback.h"
+#include "base/functional/callback.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/web_applications/locks/app_lock.h"
@@ -19,6 +20,27 @@
 
 namespace web_app {
 
+namespace {
+
+base::RepeatingCallback<void(Result)> CreateBarrierForSynchronizeWithResult(
+    base::OnceCallback<void(Result)> final_callback) {
+  return base::BarrierCallback<Result>(
+      /*num_callbacks=*/2, base::BindOnce(
+                               [](base::OnceCallback<void(Result)> callback,
+                                  std::vector<Result> combined_results) {
+                                 DCHECK_EQ(2u, combined_results.size());
+                                 Result final_result = Result::kOk;
+                                 if (combined_results[0] == Result::kError ||
+                                     combined_results[1] == Result::kError) {
+                                   final_result = Result::kError;
+                                 }
+                                 std::move(callback).Run(final_result);
+                               },
+                               std::move(final_callback)));
+}
+
+}  // namespace
+
 // static
 std::unique_ptr<UpdateFileHandlerCommand>
 UpdateFileHandlerCommand::CreateForPersistUserChoice(
@@ -93,6 +115,10 @@
 
   debug_info_.Set("was_update_required", true);
 
+  auto callback_for_synchronize = CreateBarrierForSynchronizeWithResult(
+      base::BindOnce(&UpdateFileHandlerCommand::OnFileHandlerUpdated,
+                     weak_factory_.GetWeakPtr(), file_handling_enabled));
+
 #if BUILDFLAG(IS_MAC)
   // On Mac, the file handlers are encoded in the app shortcut. First
   // unregister the file handlers (verifying that it finishes
@@ -105,25 +131,29 @@
                      &unregister_file_handlers_result));
   DCHECK_EQ(Result::kOk, unregister_file_handlers_result);
 
+  lock_->os_integration_manager().Synchronize(
+      app_id_, base::BindOnce(callback_for_synchronize, Result::kOk));
+
   // If we're enabling file handling, yet this app does not have any file
   // handlers there is no need to update the shortcut, as doing so would be a
   // no-op anyway.
   const apps::FileHandlers* handlers =
       lock_->registrar().GetAppFileHandlers(app_id_);
+
   if (file_handling_enabled && (!handlers || handlers->empty())) {
-    OnFileHandlerUpdated(file_handling_enabled, Result::kOk);
+    callback_for_synchronize.Run(Result::kOk);
   } else {
-    lock_->os_integration_manager().UpdateShortcuts(
-        app_id_, /*old_name=*/{},
-        base::BindOnce(&UpdateFileHandlerCommand::OnFileHandlerUpdated,
-                       weak_factory_.GetWeakPtr(), file_handling_enabled));
+    // TODO(crbug.com/1401125): Remove UpdateFileHandlers() and
+    // UpdateShortcuts() once OS integration sub managers have been implemented.
+    lock_->os_integration_manager().UpdateShortcuts(app_id_, /*old_name=*/{},
+                                                    callback_for_synchronize);
   }
 #else
+  lock_->os_integration_manager().UpdateFileHandlers(app_id_, action,
+                                                     callback_for_synchronize);
+  lock_->os_integration_manager().Synchronize(
+      app_id_, base::BindOnce(callback_for_synchronize, Result::kOk));
 
-  lock_->os_integration_manager().UpdateFileHandlers(
-      app_id_, action,
-      base::BindOnce(&UpdateFileHandlerCommand::OnFileHandlerUpdated,
-                     weak_factory_.GetWeakPtr(), file_handling_enabled));
 #endif
 }
 
diff --git a/chrome/browser/web_applications/commands/update_file_handler_command_unittest.cc b/chrome/browser/web_applications/commands/update_file_handler_command_unittest.cc
index 93cf33e..adea45a 100644
--- a/chrome/browser/web_applications/commands/update_file_handler_command_unittest.cc
+++ b/chrome/browser/web_applications/commands/update_file_handler_command_unittest.cc
@@ -10,8 +10,10 @@
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/test/web_app_test.h"
+#include "chrome/browser/web_applications/test/web_app_test_utils.h"
 #include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
+#include "chrome/common/chrome_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
 #include "url/gurl.h"
@@ -19,14 +21,24 @@
 namespace web_app {
 namespace {
 
-class UpdateFileHandlerCommandTest : public WebAppTest {
+class UpdateFileHandlerCommandTest
+    : public WebAppTest,
+      public ::testing::WithParamInterface<OsIntegrationSubManagersState> {
  public:
   const char* kTestAppName = "test app";
   const GURL kTestAppUrl = GURL("https://example.com");
 
   UpdateFileHandlerCommandTest() {
-    scoped_feature_list_.InitAndEnableFeature(
-        blink::features::kFileHandlingAPI);
+    if (GetParam() == OsIntegrationSubManagersState::kEnabled) {
+      scoped_feature_list_.InitWithFeaturesAndParameters(
+          {{features::kOsIntegrationSubManagers, {{"stage", "write_config"}}},
+           {blink::features::kFileHandlingAPI, {}}},
+          /*disabled_features=*/{});
+    } else {
+      scoped_feature_list_.InitWithFeatures(
+          {blink::features::kFileHandlingAPI},
+          {features::kOsIntegrationSubManagers});
+    }
   }
 
   ~UpdateFileHandlerCommandTest() override = default;
@@ -70,7 +82,7 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-TEST_F(UpdateFileHandlerCommandTest, UserChoiceAllowPersisted) {
+TEST_P(UpdateFileHandlerCommandTest, UserChoiceAllowPersisted) {
   const AppId app_id =
       test::InstallDummyWebApp(profile(), kTestAppName, kTestAppUrl);
   EXPECT_EQ(
@@ -90,7 +102,7 @@
           app_id));
 }
 
-TEST_F(UpdateFileHandlerCommandTest, UserChoiceDisallowPersisted) {
+TEST_P(UpdateFileHandlerCommandTest, UserChoiceDisallowPersisted) {
   const AppId app_id =
       test::InstallDummyWebApp(profile(), kTestAppName, kTestAppUrl);
   EXPECT_EQ(
@@ -110,7 +122,7 @@
           app_id));
 }
 
-TEST_F(UpdateFileHandlerCommandTest, UpdateFileHandler) {
+TEST_P(UpdateFileHandlerCommandTest, UpdateFileHandler) {
   const AppId app_id =
       test::InstallDummyWebApp(profile(), kTestAppName, kTestAppUrl);
   EXPECT_EQ(
@@ -142,5 +154,12 @@
           app_id));
 }
 
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    UpdateFileHandlerCommandTest,
+    ::testing::Values(OsIntegrationSubManagersState::kEnabled,
+                      OsIntegrationSubManagersState::kDisabled),
+    test::GetOsIntegrationSubManagersTestName);
+
 }  // namespace
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/commands/update_protocol_handler_approval_command_browsertest.cc b/chrome/browser/web_applications/commands/update_protocol_handler_approval_command_browsertest.cc
index b5f7980d..2a90937b 100644
--- a/chrome/browser/web_applications/commands/update_protocol_handler_approval_command_browsertest.cc
+++ b/chrome/browser/web_applications/commands/update_protocol_handler_approval_command_browsertest.cc
@@ -323,7 +323,8 @@
     All,
     UpdateProtocolHandlerApprovalCommandTest,
     ::testing::Values(OsIntegrationSubManagersState::kEnabled,
-                      OsIntegrationSubManagersState::kDisabled));
+                      OsIntegrationSubManagersState::kDisabled),
+    test::GetOsIntegrationSubManagersTestName);
 
 }  // namespace
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
index c5047973..6f37712 100644
--- a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
+++ b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
@@ -3455,7 +3455,28 @@
 }
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
-IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_UrlHandlers,
+class ManifestUpdateBrowserTestUrlHandlerSynchronize
+    : public ManifestUpdateManagerBrowserTest_UrlHandlers,
+      public ::testing::WithParamInterface<OsIntegrationSubManagersState> {
+ public:
+  ManifestUpdateBrowserTestUrlHandlerSynchronize() {
+    if (GetParam() == OsIntegrationSubManagersState::kEnabled) {
+      scoped_feature_list_.InitWithFeaturesAndParameters(
+          {{features::kOsIntegrationSubManagers, {{"stage", "write_config"}}}},
+          /*disabled_features=*/{});
+    } else {
+      scoped_feature_list_.InitWithFeatures(
+          {}, {features::kOsIntegrationSubManagers});
+    }
+  }
+
+  ~ManifestUpdateBrowserTestUrlHandlerSynchronize() override = default;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_P(ManifestUpdateBrowserTestUrlHandlerSynchronize,
                        NoHandlersChangeUpdateAssociations) {
   constexpr char kManifestTemplate[] = R"(
     {
@@ -3515,6 +3536,13 @@
   matches = UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd);
   ASSERT_EQ(matches.size(), 0u);
 }
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    ManifestUpdateBrowserTestUrlHandlerSynchronize,
+    ::testing::Values(OsIntegrationSubManagersState::kEnabled,
+                      OsIntegrationSubManagersState::kDisabled),
+    test::GetOsIntegrationSubManagersTestName);
 #endif
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
diff --git a/chrome/browser/web_applications/os_integration/os_integration_manager.h b/chrome/browser/web_applications/os_integration/os_integration_manager.h
index ffea2b58..813c54d 100644
--- a/chrome/browser/web_applications/os_integration/os_integration_manager.h
+++ b/chrome/browser/web_applications/os_integration/os_integration_manager.h
@@ -114,8 +114,12 @@
 
   virtual void Start();
 
-  // Start OS Integration synchronization from external points. This should be
-  // the only point of call into OsIntegrationManager from external places.
+  // Start OS Integration synchronization from external callsites. This should
+  // be the only point of call into OsIntegrationManager from external places
+  // after the OS integration sub managers have been implemented.
+  // TODO(crbug.com/1401125): Remove all install, uninstall and update functions
+  // from this file once all OS Integration sub managers have been implemented,
+  // connected to the web_app system and tested.
   virtual void Synchronize(const AppId& app_id, base::OnceClosure callback);
 
   // Install all needed OS hooks for the web app.
diff --git a/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager_unittest.cc b/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager_unittest.cc
index 612d567..fc40959a 100644
--- a/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager_unittest.cc
+++ b/chrome/browser/web_applications/os_integration/protocol_handling_sub_manager_unittest.cc
@@ -229,14 +229,7 @@
     ProtocolHandlingSubManagerTest,
     ::testing::Values(OsIntegrationSubManagersState::kEnabled,
                       OsIntegrationSubManagersState::kDisabled),
-    [](const ::testing::TestParamInfo<OsIntegrationSubManagersState>& info) {
-      switch (info.param) {
-        case OsIntegrationSubManagersState::kEnabled:
-          return "OSIntegrationSubManagers_Enabled";
-        case OsIntegrationSubManagersState::kDisabled:
-          return "OSIntegrationSubManagers_Disabled";
-      }
-    });
+    test::GetOsIntegrationSubManagersTestName);
 
 }  // namespace
 
diff --git a/chrome/browser/web_applications/os_integration/shortcut_handling_sub_manager_browsertest.cc b/chrome/browser/web_applications/os_integration/shortcut_handling_sub_manager_browsertest.cc
index d282f57f..b17a8c8 100644
--- a/chrome/browser/web_applications/os_integration/shortcut_handling_sub_manager_browsertest.cc
+++ b/chrome/browser/web_applications/os_integration/shortcut_handling_sub_manager_browsertest.cc
@@ -156,14 +156,7 @@
     ShortcutHandlingSubManagerBrowserTest,
     ::testing::Values(OsIntegrationSubManagersState::kEnabled,
                       OsIntegrationSubManagersState::kDisabled),
-    [](const ::testing::TestParamInfo<OsIntegrationSubManagersState>& info) {
-      switch (info.param) {
-        case OsIntegrationSubManagersState::kEnabled:
-          return "OSIntegrationSubManagers_Enabled";
-        case OsIntegrationSubManagersState::kDisabled:
-          return "OSIntegrationSubManagers_Disabled";
-      }
-    });
+    test::GetOsIntegrationSubManagersTestName);
 
 }  // namespace
 
diff --git a/chrome/browser/web_applications/test/web_app_test_utils.cc b/chrome/browser/web_applications/test/web_app_test_utils.cc
index 124d749..eac7334 100644
--- a/chrome/browser/web_applications/test/web_app_test_utils.cc
+++ b/chrome/browser/web_applications/test/web_app_test_utils.cc
@@ -326,6 +326,16 @@
   }
 }
 
+std::string GetOsIntegrationSubManagersTestName(
+    const ::testing::TestParamInfo<OsIntegrationSubManagersState>& info) {
+  switch (info.param) {
+    case OsIntegrationSubManagersState::kEnabled:
+      return "OSIntegrationSubManagers_Enabled";
+    case OsIntegrationSubManagersState::kDisabled:
+      return "OSIntegrationSubManagers_Disabled";
+  }
+}
+
 std::unique_ptr<WebApp> CreateWebApp(const GURL& start_url,
                                      WebAppManagement::Type source_type) {
   const AppId app_id = GenerateAppId(/*manifest_id=*/absl::nullopt, start_url);
diff --git a/chrome/browser/web_applications/test/web_app_test_utils.h b/chrome/browser/web_applications/test/web_app_test_utils.h
index f29083fd..36e3196 100644
--- a/chrome/browser/web_applications/test/web_app_test_utils.h
+++ b/chrome/browser/web_applications/test/web_app_test_utils.h
@@ -52,6 +52,9 @@
 std::string GetExternalPrefMigrationTestName(
     const ::testing::TestParamInfo<ExternalPrefMigrationTestCases>& info);
 
+std::string GetOsIntegrationSubManagersTestName(
+    const ::testing::TestParamInfo<OsIntegrationSubManagersState>& info);
+
 // Do not use this for installation! Instead, use the utilities in
 // web_app_install_test_util.h.
 std::unique_ptr<WebApp> CreateWebApp(
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index 7ec1f16..67ee012 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -549,6 +549,8 @@
           &WebAppInstallFinalizer::OnInstallHooksFinished,
           weak_ptr_factory_.GetWeakPtr(), std::move(callback), app_id));
 
+  // TODO(crbug.com/1401125): Remove InstallOsHooks() once OS integration
+  // sub managers have been implemented.
   os_integration_manager_->InstallOsHooks(
       app_id, os_hooks_barrier, /*web_app_info=*/nullptr, hooks_options);
   os_integration_manager_->Synchronize(
@@ -608,6 +610,8 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                      app_id, old_name));
 
+  // TODO(crbug.com/1401125): Remove UpdateOsHooks() once OS integration
+  // sub managers have been implemented.
   os_integration_manager_->UpdateOsHooks(app_id, old_name,
                                          file_handlers_need_os_update,
                                          web_app_info, os_hooks_barrier);
diff --git a/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc b/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc
index 1c7f8375..2d95d009 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc
@@ -462,13 +462,6 @@
     WebAppInstallFinalizerUnitTest,
     ::testing::Values(OsIntegrationSubManagersState::kEnabled,
                       OsIntegrationSubManagersState::kDisabled),
-    [](const ::testing::TestParamInfo<OsIntegrationSubManagersState>& info) {
-      switch (info.param) {
-        case OsIntegrationSubManagersState::kEnabled:
-          return "OSIntegrationSubManagers_Enabled";
-        case OsIntegrationSubManagersState::kDisabled:
-          return "OSIntegrationSubManagers_Disabled";
-      }
-    });
+    test::GetOsIntegrationSubManagersTestName);
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_install_utils.cc b/chrome/browser/web_applications/web_app_install_utils.cc
index e685e4e8..efb20f6 100644
--- a/chrome/browser/web_applications/web_app_install_utils.cc
+++ b/chrome/browser/web_applications/web_app_install_utils.cc
@@ -944,6 +944,8 @@
     options.os_hooks[OsHookType::kUninstallationViaOsSettings] = true;
     auto os_hooks_barrier =
         OsIntegrationManager::GetBarrierForSynchronize(std::move(callback));
+    // TODO(crbug.com/1401125): Remove InstallOsHooks() once OS integration
+    // sub managers have been implemented.
     os_integration_manager.InstallOsHooks(web_app->app_id(), os_hooks_barrier,
                                           nullptr, options);
     os_integration_manager.Synchronize(
@@ -969,6 +971,8 @@
   if (user_installable_before_install && !user_installable_after_install) {
     OsHooksOptions options;
     options[OsHookType::kUninstallationViaOsSettings] = true;
+    // TODO(crbug.com/1401125): Remove UninstallOsHooks() once OS integration
+    // sub managers have been implemented.
     os_integration_manager.UninstallOsHooks(web_app->app_id(), options,
                                             base::DoNothing());
     os_integration_manager.Synchronize(web_app->app_id(), base::DoNothing());
diff --git a/chrome/browser/web_applications/web_app_uninstall_job.cc b/chrome/browser/web_applications/web_app_uninstall_job.cc
index 04661cd..7b3c736b4 100644
--- a/chrome/browser/web_applications/web_app_uninstall_job.cc
+++ b/chrome/browser/web_applications/web_app_uninstall_job.cc
@@ -90,6 +90,9 @@
   auto synchronize_barrier = OsIntegrationManager::GetBarrierForSynchronize(
       base::BindOnce(&WebAppUninstallJob::OnOsHooksUninstalled,
                      weak_ptr_factory_.GetWeakPtr()));
+
+  // TODO(crbug.com/1401125): Remove UninstallAllOsHooks() once OS integration
+  // sub managers have been implemented.
   os_integration_manager.UninstallAllOsHooks(app_id_, synchronize_barrier);
   os_integration_manager.Synchronize(
       app_id_, base::BindOnce(synchronize_barrier, OsHooksErrors()));
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 2dec125..b9998620c 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1671040780-7354adbba216d8d22545e6cbff01983a0ef786cc.profdata
+chrome-linux-main-1671062375-aefeb8d4bdde8a06e7c181458988e9ba2e890bcd.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 846237e3..6eb6125 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1671040780-ab587c46ae159837cb1901b47db9f541feb1ec6f.profdata
+chrome-mac-arm-main-1671062375-4cb1051921f0b87eaf542d5997d33e43bbb7a0d0.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 7dd319db..edbedb04 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1671029899-f511e3be18f418e934aad5640a3a4197391f4ec1.profdata
+chrome-win32-main-1671051571-9ae193e475cceb4456158fc9c65a9352a6ab6915.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 5106b24..9193d194 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1671040780-fa17ea6b4718dc5c5320f88590d164f43c2aa427.profdata
+chrome-win64-main-1671062375-88f0f25426f9c22edebc016042b4b428b2a1d5e0.profdata
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index d256f912..12538e7 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -533,8 +533,8 @@
     "location": "policy",
     "platforms": ["chromeos"],
     "allowlist": [
-      // https://crbug.com/1233881
-      "EC3DE21E048B67319893889529354DFBFA96FD23",   // Smart Card Connector
+      "EC3DE21E048B67319893889529354DFBFA96FD23",   // https://crbug.com/1233881
+      "A19608AC34215B127FF9D7C006D67F5C8ED8146D",   // https://crbug.com/1233881
       // https://crbug.com/1194693
       "6B748A5C005F21B7CBCF4170C2F883E435DEB511",   // CSSI Smart Card Middleware
       "075FF17D52ED6E3C2E5EC4D99F188E7A25AF47EA"    // Beta CSSI Smart Card Middleware
@@ -549,7 +549,8 @@
     "extension_types": ["platform_app"],
     "platforms": ["chromeos", "lacros"],
     "allowlist": [
-      "EC3DE21E048B67319893889529354DFBFA96FD23"  // Smart Card Connector
+      "EC3DE21E048B67319893889529354DFBFA96FD23",  // https://crbug.com/1233881
+      "A19608AC34215B127FF9D7C006D67F5C8ED8146D"   // https://crbug.com/1233881
     ]
   }],
   "webcamPrivate": {
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 9fa559cf..ebf0861 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -675,6 +675,7 @@
     kChromeUIManagementHost,
 #endif
     kChromeUIMediaEngagementHost,
+    kChromeUIMetricsInternalsHost,
     kChromeUINetExportHost,
     kChromeUINetInternalsHost,
     kChromeUINewTabHost,
diff --git a/chrome/services/file_util/single_file_tar_file_extractor.cc b/chrome/services/file_util/single_file_tar_file_extractor.cc
index cfa629b..2821eda0 100644
--- a/chrome/services/file_util/single_file_tar_file_extractor.cc
+++ b/chrome/services/file_util/single_file_tar_file_extractor.cc
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "base/containers/span.h"
-#include "base/numerics/safe_conversions.h"
 #include "chrome/services/file_util/public/mojom/constants.mojom.h"
 #include "chrome/services/file_util/single_file_tar_reader.h"
 
diff --git a/chrome/services/file_util/single_file_tar_reader.cc b/chrome/services/file_util/single_file_tar_reader.cc
index 15c662c..6c596fdb 100644
--- a/chrome/services/file_util/single_file_tar_reader.cc
+++ b/chrome/services/file_util/single_file_tar_reader.cc
@@ -5,7 +5,6 @@
 #include "chrome/services/file_util/single_file_tar_reader.h"
 
 #include "base/check.h"
-#include "base/numerics/safe_conversions.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace {
diff --git a/chrome/services/file_util/single_file_tar_reader_unittest.cc b/chrome/services/file_util/single_file_tar_reader_unittest.cc
index e96d3ae..332f1ee 100644
--- a/chrome/services/file_util/single_file_tar_reader_unittest.cc
+++ b/chrome/services/file_util/single_file_tar_reader_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/path_service.h"
 #include "chrome/common/chrome_paths.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -44,7 +45,8 @@
 
     base::span<const uint8_t> bin_buffer;
     tar_reader.ExtractChunk(
-        base::make_span(tar_buffer.data(), static_cast<size_t>(bytes_read)),
+        base::make_span(tar_buffer.data(),
+                        base::checked_cast<size_t>(bytes_read)),
         bin_buffer);
     contents.insert(contents.begin(), bin_buffer.begin(), bin_buffer.end());
   }
@@ -91,7 +93,8 @@
 
   base::span<const uint8_t> bin_buffer;
   tar_reader.ExtractChunk(
-      base::make_span(tar_buffer.data(), static_cast<size_t>(bytes_read)),
+      base::make_span(tar_buffer.data(),
+                      base::checked_cast<size_t>(bytes_read)),
       bin_buffer);
 
   EXPECT_TRUE(tar_reader.IsComplete());
diff --git a/chrome/services/file_util/single_file_tar_xz_file_extractor.cc b/chrome/services/file_util/single_file_tar_xz_file_extractor.cc
index 53efdf4..903f2b1 100644
--- a/chrome/services/file_util/single_file_tar_xz_file_extractor.cc
+++ b/chrome/services/file_util/single_file_tar_xz_file_extractor.cc
@@ -57,9 +57,9 @@
       }
 
       absl::optional<chrome::file_util::mojom::ExtractionResult> result;
-      ExtractChunk(
-          base::make_span(xz_buffer.data(), static_cast<size_t>(bytes_read)),
-          &result);
+      ExtractChunk(base::make_span(xz_buffer.data(),
+                                   base::checked_cast<size_t>(bytes_read)),
+                   &result);
       if (result.has_value())
         return result.value();
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index ca11bd4..df57856 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -612,8 +612,8 @@
       "//chromeos/ash/components/cryptohome",
       "//chromeos/ash/components/dbus",
       "//chromeos/ash/components/dbus/concierge",
+      "//chromeos/ash/components/system",
       "//chromeos/dbus/power",
-      "//chromeos/system",
       "//components/policy/test_support",
       "//google_apis",
       "//google_apis:test_support",
@@ -4363,6 +4363,7 @@
         "//chromeos/ash/components/login/login_state:test_support",
         "//chromeos/ash/components/network/portal_detector",
         "//chromeos/ash/components/smbfs",
+        "//chromeos/ash/components/system",
         "//chromeos/ash/components/timezone",
         "//chromeos/ash/services/assistant:lib",
         "//chromeos/ash/services/assistant/public/cpp",
@@ -4380,7 +4381,6 @@
         "//chromeos/services/machine_learning/public/cpp:stub",
         "//chromeos/services/network_config:network_config",
         "//chromeos/services/tts",
-        "//chromeos/system",
         "//chromeos/ui/base",
         "//chromeos/ui/frame",
         "//chromeos/ui/frame:test_support",
@@ -7155,6 +7155,7 @@
       "../browser/new_tab_page/chrome_colors/chrome_colors_service_unittest.cc",
       "../browser/new_tab_page/modules/drive/drive_service_unittest.cc",
       "../browser/new_tab_page/modules/feed/feed_handler_unittest.cc",
+      "../browser/new_tab_page/modules/new_tab_page_modules_unittest.cc",
       "../browser/new_tab_page/modules/photos/photos_service_unittest.cc",
       "../browser/new_tab_page/modules/recipes/recipes_service_unittest.cc",
       "../browser/new_tab_page/modules/recipes/time_format_util_unittest.cc",
@@ -7670,6 +7671,7 @@
       "//chromeos/ash/components/proximity_auth:test_support",
       "//chromeos/ash/components/string_matching",
       "//chromeos/ash/components/sync_wifi",
+      "//chromeos/ash/components/system",
       "//chromeos/ash/services/assistant/public/cpp",
       "//chromeos/ash/services/device_sync/public/cpp:test_support",
       "//chromeos/ash/services/multidevice_setup/public/cpp:test_support",
@@ -7686,7 +7688,6 @@
       "//chromeos/services/assistant/public/shared",
       "//chromeos/services/network_config:in_process_instance",
       "//chromeos/services/network_config:network_config",
-      "//chromeos/system",
       "//chromeos/ui/base",
       "//chromeos/ui/wm",
       "//components/app_constants",
diff --git a/chrome/test/data/extensions/api_test/sandboxed_pages_csp/main.js b/chrome/test/data/extensions/api_test/sandboxed_pages_csp/main.js
index b1664c5..1122309 100644
--- a/chrome/test/data/extensions/api_test/sandboxed_pages_csp/main.js
+++ b/chrome/test/data/extensions/api_test/sandboxed_pages_csp/main.js
@@ -11,12 +11,12 @@
 };
 
 var loadIframeContentInSandboxedPage = function(
-    localUrl, remoteUrl, isManifestV3) {
+    localUrl, remoteUrl) {
   var sandboxedFrame = document.createElement('iframe');
   sandboxedFrame.src = 'sandboxed.html';
   sandboxedFrame.onload = function() {
     sandboxedFrame.contentWindow.postMessage(
-        JSON.stringify(['load', localUrl, remoteUrl, isManifestV3]), '*');
+        JSON.stringify(['load', localUrl, remoteUrl]), '*');
     sandboxedFrame.onload = null;
   };
   document.body.appendChild(sandboxedFrame);
@@ -30,7 +30,7 @@
         var remoteUrl = 'http://localhost:' + config.testServer.port +
             '/extensions/api_test/sandboxed_pages_csp/' + REMOTE_FILE_NAME;
         loadIframeContentInSandboxedPage(
-            LOCAL_FILE_NAME, remoteUrl, config.customArg === 'manifest_v3');
+            LOCAL_FILE_NAME, remoteUrl);
       }
     ]);
   });
diff --git a/chrome/test/data/extensions/api_test/sandboxed_pages_csp/sandboxed.html b/chrome/test/data/extensions/api_test/sandboxed_pages_csp/sandboxed.html
index 039a34c..3bcce5e 100644
--- a/chrome/test/data/extensions/api_test/sandboxed_pages_csp/sandboxed.html
+++ b/chrome/test/data/extensions/api_test/sandboxed_pages_csp/sandboxed.html
@@ -16,7 +16,7 @@
     sendResponse('failed');
 });
 
-var loadFrameExpectResponse = function(iframe, url, isManifestV3) {
+var loadFrameExpectResponse = function(iframe, url) {
   var identifier = performance.now();
   return new Promise(function(resolve, reject) {
     window.addEventListener('message', function(e) {
@@ -29,16 +29,6 @@
     });
     iframe.onerror = reject;
     iframe.onload = function() {
-      if (isManifestV3) {
-        // TODO(crbug.com/1219825): A manifest V3 extension's sandboxed frame
-        // can't currently embed an extension's web_accessible iframe due to a
-        // bug with our handling of web accessible resources. Even though the
-        // iframe load fails, the onload event is raised. Early return in this
-        // case. Note the test still makes sense since a CSP violation is not
-        // raised while loading a local url.
-        resolve();
-        return;
-      }
       iframe.contentWindow.postMessage(
           JSON.stringify(['sandboxed frame msg', identifier]), '*');
     };
@@ -46,11 +36,11 @@
   });
 };
 
-var runTestAndRespond = function(localUrl, remoteUrl, isManifestV3) {
+var runTestAndRespond = function(localUrl, remoteUrl) {
   var iframe = document.createElement('iframe');
 
   // First load local resource in |iframe|, expect the local frame to respond.
-  loadFrameExpectResponse(iframe, localUrl, isManifestV3).then(function() {
+  loadFrameExpectResponse(iframe, localUrl).then(function() {
     // Then load remote resource in |iframe|, expect the navigation to be
     // blocked by the Content-Security-Policy.
     // Rely on the SecurityPolicyViolationEvent to detect that the frame has
@@ -66,8 +56,7 @@
   if (command[0] == 'load') {
     var localUrl = command[1];
     var remoteUrl = command[2];
-    var isManifestV3 = command[3]
-    runTestAndRespond(localUrl, remoteUrl, isManifestV3);
+    runTestAndRespond(localUrl, remoteUrl);
   }
 };
 
diff --git a/chrome/test/data/extensions/echo_message.html b/chrome/test/data/extensions/echo_message.html
new file mode 100644
index 0000000..a889db9
--- /dev/null
+++ b/chrome/test/data/extensions/echo_message.html
@@ -0,0 +1,13 @@
+<!-- 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. -->
+
+<!doctype html>
+<html>
+  Sandboxed Page
+  <script>
+    onmessage = (e) => {
+      e.source.postMessage('echo ' + e.data, '*');
+    };
+  </script>
+</html>
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 033b3fb..cc8dc3d 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -21386,5 +21386,43 @@
         }
       }
     ]
+  },
+  "UseMojoVideoDecoderForPepperAllowed": {
+    "os": [
+      "win",
+      "linux",
+      "mac",
+      "chromeos_ash",
+      "chromeos_lacros"
+    ],
+    "policy_pref_mapping_tests": [
+      {
+        "policies": {},
+        "prefs": {
+          "policy.use_mojo_video_decoder_for_pepper_allowed": {
+            "location": "local_state",
+            "default_value": true
+          }
+        }
+      },
+      {
+        "policies": { "UseMojoVideoDecoderForPepperAllowed": true },
+        "prefs": {
+          "policy.use_mojo_video_decoder_for_pepper_allowed": {
+            "location": "local_state",
+            "value": true
+          }
+        }
+      },
+      {
+        "policies": { "UseMojoVideoDecoderForPepperAllowed": false },
+        "prefs": {
+          "policy.use_mojo_video_decoder_for_pepper_allowed": {
+            "location": "local_state",
+            "value": false
+          }
+        }
+      }
+    ]
   }
 }
diff --git a/chrome/test/data/webui/chromeos/personalization_app/dynamic_color_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/dynamic_color_element_test.ts
index ce1a4fa..db35891 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/dynamic_color_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/dynamic_color_element_test.ts
@@ -7,7 +7,7 @@
 import 'chrome://personalization/strings.m.js';
 import 'chrome://webui-test/mojo_webui_test_support.js';
 
-import {DynamicColorElement, SetColorSchemePrefAction, SetStaticColorPrefAction, ThemeActionName, ThemeObserver} from 'chrome://personalization/js/personalization_app.js';
+import {ColorScheme, DynamicColorElement, emptyState, SetColorSchemePrefAction, SetStaticColorPrefAction, ThemeActionName, ThemeObserver} from 'chrome://personalization/js/personalization_app.js';
 import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
 import {hexColorToSkColor} from 'chrome://resources/js/color_utils.js';
@@ -45,30 +45,34 @@
     return getStaticColorSelector().querySelectorAll('cr-button')!;
   }
 
-  function showStaticColorButtons() {
+  async function showStaticColorButtons() {
     const toggleButton = getToggleButton();
     if (toggleButton.checked) {
+      personalizationStore.expectAction(ThemeActionName.SET_COLOR_SCHEME);
       toggleButton.click();
+      await personalizationStore.waitForAction(
+          ThemeActionName.SET_COLOR_SCHEME);
     }
     assertFalse(getStaticColorSelector().hidden);
   }
 
-  function showColorSchemeButtons() {
+  async function showColorSchemeButtons() {
     const toggleButton = getToggleButton();
     if (!toggleButton.checked) {
+      personalizationStore.expectAction(ThemeActionName.SET_COLOR_SCHEME);
       toggleButton.click();
+      await personalizationStore.waitForAction(
+          ThemeActionName.SET_COLOR_SCHEME);
     }
     assertFalse(getColorSchemeSelector().hidden);
   }
 
-  setup(async () => {
+  setup(() => {
     const mocks = baseSetup();
-    personalizationStore = mocks.personalizationStore;
     themeProvider = mocks.themeProvider;
+    personalizationStore = mocks.personalizationStore;
+    personalizationStore.setReducersEnabled(true);
     ThemeObserver.initThemeObserverIfNeeded();
-
-    dynamicColorElement = initElement(DynamicColorElement)!;
-    await waitAfterNextRender(dynamicColorElement);
   });
 
   teardown(async () => {
@@ -76,7 +80,14 @@
     ThemeObserver.shutdown();
   });
 
+  async function initDynamicColorElement() {
+    dynamicColorElement = initElement(DynamicColorElement)!;
+    await waitAfterNextRender(dynamicColorElement);
+  }
+
   test('displays content', async () => {
+    await initDynamicColorElement();
+
     assertEquals(
         '[temp]Theme color[temp]Auto',
         dynamicColorElement!.shadowRoot!.getElementById(
@@ -90,12 +101,91 @@
         'when the toggle is on, the static color buttons should be hidden.');
   });
 
+  test('sets color scheme in store on first load', async () => {
+    personalizationStore.expectAction(ThemeActionName.SET_COLOR_SCHEME);
+
+    await initDynamicColorElement();
+
+    const action =
+        await personalizationStore.waitForAction(
+            ThemeActionName.SET_COLOR_SCHEME) as SetColorSchemePrefAction;
+    assertEquals(ColorScheme.kTonalSpot, action.colorScheme);
+  });
+
+  test('sets color scheme data in store on changed', async () => {
+    const colorScheme = ColorScheme.kExpressive;
+    assertDeepEquals(emptyState(), personalizationStore.data);
+    await themeProvider.whenCalled('setThemeObserver');
+    personalizationStore.expectAction(ThemeActionName.SET_COLOR_SCHEME);
+
+    themeProvider.themeObserverRemote!.onColorSchemeChanged(colorScheme);
+
+    const action =
+        await personalizationStore.waitForAction(
+            ThemeActionName.SET_COLOR_SCHEME) as SetColorSchemePrefAction;
+    assertEquals(colorScheme, action.colorScheme);
+  });
+
+  test('sets static color data in store on changed', async () => {
+    const staticColor = hexColorToSkColor('#123456');
+    assertDeepEquals(emptyState(), personalizationStore.data);
+    await themeProvider.whenCalled('setThemeObserver');
+    personalizationStore.expectAction(ThemeActionName.SET_STATIC_COLOR);
+
+    themeProvider.themeObserverRemote!.onStaticColorChanged(staticColor);
+
+    const action =
+        await personalizationStore.waitForAction(
+            ThemeActionName.SET_STATIC_COLOR) as SetStaticColorPrefAction;
+    assertDeepEquals(staticColor, action.staticColor);
+  });
+
+  test('displays color scheme on load', async () => {
+    const colorScheme = ColorScheme.kExpressive;
+    themeProvider.setColorScheme(colorScheme);
+
+    await initDynamicColorElement();
+
+    assertTrue(getToggleButton().checked, 'default toggle should be on');
+    assertFalse(
+        getColorSchemeSelector().hidden,
+        'when the toggle is on, the color scheme buttons should be visible.');
+    assertTrue(
+        getStaticColorSelector().hidden,
+        'when the toggle is on, the static color buttons should be hidden.');
+    const checkedButton = getColorSchemeSelector().querySelector(
+                              'cr-button[aria-checked="true"]') as HTMLElement;
+    assertEquals(String(colorScheme), checkedButton.dataset['colorSchemeId']);
+  });
+
+  test('displays static color on load', async () => {
+    const staticColorHex = '#edd0e4';
+    themeProvider.setStaticColor(hexColorToSkColor(staticColorHex));
+
+    await initDynamicColorElement();
+
+    assertFalse(getToggleButton().checked, 'default toggle should be off');
+    assertTrue(
+        getColorSchemeSelector().hidden,
+        'when the toggle is off, the color scheme buttons should be hidden.');
+    assertFalse(
+        getStaticColorSelector().hidden,
+        'when the toggle is off, the static color buttons should be visible.');
+    const checkedButton = getStaticColorSelector().querySelector(
+                              'cr-button[aria-checked="true"]') as HTMLElement;
+    assertEquals(staticColorHex, checkedButton.dataset['staticColor']);
+  });
+
   test('flips toggle', async () => {
+    await initDynamicColorElement();
     const toggleButton = getToggleButton();
     const colorSchemeSelector = getColorSchemeSelector();
     const staticColorSelector = getStaticColorSelector();
+    await showColorSchemeButtons();
 
+    personalizationStore.expectAction(ThemeActionName.SET_COLOR_SCHEME);
     toggleButton.click();
+    await personalizationStore.waitForAction(ThemeActionName.SET_COLOR_SCHEME);
 
     assertFalse(
         toggleButton.checked, 'after clicking toggle, toggle should be off');
@@ -106,7 +196,9 @@
         staticColorSelector.hidden,
         'when the toggle is off, the static color buttons should be visible.');
 
+    personalizationStore.expectAction(ThemeActionName.SET_COLOR_SCHEME);
     toggleButton.click();
+    await personalizationStore.waitForAction(ThemeActionName.SET_COLOR_SCHEME);
 
     assertFalse(
         colorSchemeSelector.hidden,
@@ -117,8 +209,9 @@
   });
 
   test('keypress navigates color scheme buttons', async () => {
+    await initDynamicColorElement();
     assertTrue(!!dynamicColorElement);
-    showColorSchemeButtons();
+    await showColorSchemeButtons();
     const colorSchemeButtons = getColorSchemeButtons();
     (colorSchemeButtons[0] as HTMLElement)!.focus();
 
@@ -140,8 +233,9 @@
   });
 
   test('keypress navigates static color buttons', async () => {
+    await initDynamicColorElement();
     assertTrue(!!dynamicColorElement);
-    showStaticColorButtons();
+    await showStaticColorButtons();
     const staticColorButtons = getStaticColorButtons();
     (staticColorButtons![0] as HTMLElement)!.focus();
 
@@ -163,25 +257,32 @@
     }
   });
 
-  test('sets color scheme', async () => {
+  test('set color scheme', async () => {
+    await initDynamicColorElement();
     personalizationStore.expectAction(ThemeActionName.SET_COLOR_SCHEME);
-    showColorSchemeButtons();
+    await showColorSchemeButtons();
+    const button = getColorSchemeButtons()[1]!;
+    assertEquals(button.getAttribute('aria-checked'), 'false');
 
-    getColorSchemeButtons()[1]!.click();
+    button.click();
     await themeProvider.whenCalled('setColorScheme');
 
     const action =
         await personalizationStore.waitForAction(
             ThemeActionName.SET_COLOR_SCHEME) as SetColorSchemePrefAction;
     assertTrue(!!action.colorScheme);
+    assertEquals(
+        Number(button.dataset['colorSchemeId']!),
+        personalizationStore.data.theme.colorSchemeSelected);
+    assertEquals(button.getAttribute('aria-checked'), 'true');
   });
 
   test('set static color', async () => {
+    await initDynamicColorElement();
     personalizationStore.expectAction(ThemeActionName.SET_STATIC_COLOR);
-    showStaticColorButtons();
+    await showStaticColorButtons();
     const button = getStaticColorButtons()[1]!;
     assertEquals(button.getAttribute('aria-checked'), 'false');
-    personalizationStore.setReducersEnabled(true);
 
     button.click();
     await themeProvider.whenCalled('setStaticColor');
diff --git a/chrome/test/data/webui/chromeos/personalization_app/test_theme_interface_provider.ts b/chrome/test/data/webui/chromeos/personalization_app/test_theme_interface_provider.ts
index 1c1c29b..5c1485a 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/test_theme_interface_provider.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/test_theme_interface_provider.ts
@@ -15,6 +15,7 @@
       'setColorModeAutoScheduleEnabled',
       'setColorScheme',
       'setStaticColor',
+      'getColorScheme',
       'getStaticColor',
       'isDarkModeEnabled',
       'isColorModeAutoScheduleEnabled',
@@ -25,6 +26,7 @@
   isDarkModeEnabledResponse = true;
   isColorModeAutoScheduleEnabledResponse = true;
   staticColor: SkColor|null;
+  colorScheme = ColorScheme.kTonalSpot;
 
   themeObserverRemote: ThemeObserverInterface|null = null;
 
@@ -47,11 +49,18 @@
   setColorScheme(colorScheme: ColorScheme) {
     this.methodCalled('setColorScheme', colorScheme);
     this.staticColor = null;
+    this.colorScheme = colorScheme;
   }
 
   setStaticColor(color: SkColor) {
     this.methodCalled('setStaticColor', color);
     this.staticColor = color;
+    this.colorScheme = ColorScheme.kStatic;
+  }
+
+  getColorScheme() {
+    this.methodCalled('getColorScheme');
+    return Promise.resolve({colorScheme: this.colorScheme});
   }
 
   getStaticColor() {
diff --git a/chrome/test/data/webui/extensions/detail_view_test.ts b/chrome/test/data/webui/extensions/detail_view_test.ts
index 5f8ef03..4b396d86 100644
--- a/chrome/test/data/webui/extensions/detail_view_test.ts
+++ b/chrome/test/data/webui/extensions/detail_view_test.ts
@@ -69,6 +69,7 @@
     assertFalse(testIsVisible('#extensionsOptions'));
     assertTrue(
         item.$.description.textContent!.indexOf('This is an extension') !== -1);
+    assertTrue(testIsVisible('#siteSettings'));
 
     // Check the checkboxes visibility and state. They should be visible
     // only if the associated option is enabled, and checked if the
diff --git a/chrome/test/data/webui/new_tab_page/app_test.ts b/chrome/test/data/webui/new_tab_page/app_test.ts
index 819bdb4..b18b6f6 100644
--- a/chrome/test/data/webui/new_tab_page/app_test.ts
+++ b/chrome/test/data/webui/new_tab_page/app_test.ts
@@ -48,6 +48,7 @@
     handler.setResultFor('getDoodle', Promise.resolve({
       doodle: null,
     }));
+    handler.setResultFor('getModulesIdNames', Promise.resolve({data: []}));
     windowProxy.setResultMapperFor('matchMedia', () => ({
                                                    addListener() {},
                                                    removeListener() {},
@@ -589,13 +590,13 @@
       const barElement = document.createElement('div');
       moduleResolver.resolve([
         {
-          descriptor: new ModuleDescriptor(
-              'foo', 'foo', () => Promise.resolve(fooElement)),
+          descriptor:
+              new ModuleDescriptor('foo', () => Promise.resolve(fooElement)),
           element: fooElement,
         },
         {
-          descriptor: new ModuleDescriptor(
-              'bar', 'bar', () => Promise.resolve(barElement)),
+          descriptor:
+              new ModuleDescriptor('bar', () => Promise.resolve(barElement)),
           element: barElement,
         },
       ]);
diff --git a/chrome/test/data/webui/new_tab_page/customize_dialog_test.ts b/chrome/test/data/webui/new_tab_page/customize_dialog_test.ts
index 9c92f06..0b1797d3 100644
--- a/chrome/test/data/webui/new_tab_page/customize_dialog_test.ts
+++ b/chrome/test/data/webui/new_tab_page/customize_dialog_test.ts
@@ -35,6 +35,9 @@
     handler.setResultFor('getBackgroundImages', Promise.resolve({
       images: [],
     }));
+    handler.setResultFor('getModulesIdNames', Promise.resolve({
+      data: [],
+    }));
 
     customizeDialog = document.createElement('ntp-customize-dialog');
     document.body.appendChild(customizeDialog);
diff --git a/chrome/test/data/webui/new_tab_page/customize_modules_test.ts b/chrome/test/data/webui/new_tab_page/customize_modules_test.ts
index 57a51aa..d3f8cb0f7 100644
--- a/chrome/test/data/webui/new_tab_page/customize_modules_test.ts
+++ b/chrome/test/data/webui/new_tab_page/customize_modules_test.ts
@@ -6,9 +6,9 @@
 import 'chrome://new-tab-page/lazy_load.js';
 
 import {CartHandlerRemote} from 'chrome://new-tab-page/chrome_cart.mojom-webui.js';
-import {ChromeCartProxy, CustomizeModulesElement, ModuleDescriptor, ModuleRegistry} from 'chrome://new-tab-page/lazy_load.js';
+import {ChromeCartProxy, CustomizeModulesElement} from 'chrome://new-tab-page/lazy_load.js';
 import {$$, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import {PageCallbackRouter, PageHandlerRemote, PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import {ModuleIdName, PageCallbackRouter, PageHandlerRemote, PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
@@ -20,7 +20,6 @@
 suite('NewTabPageCustomizeModulesTest', () => {
   let handler: TestBrowserProxy<PageHandlerRemote>;
   let callbackRouterRemote: PageRemote;
-  let moduleRegistry: TestBrowserProxy<ModuleRegistry>;
   let metrics: MetricsTracker;
   let cartHandler: TestBrowserProxy<CartHandlerRemote>;
 
@@ -28,11 +27,9 @@
       allDisabled: boolean,
       modules: Array<{id: string, name: string, disabled: boolean}>):
       Promise<CustomizeModulesElement> {
-    moduleRegistry.setResultFor(
-        'getDescriptors',
-        modules.map(
-            ({id, name}) =>
-                new ModuleDescriptor(id, name, () => Promise.resolve(null))));
+    handler.setResultFor('getModulesIdNames', Promise.resolve({
+      data: modules.map(({id, name}) => ({id, name} as ModuleIdName)),
+    }));
     const customizeModules = document.createElement('ntp-customize-modules');
     document.body.appendChild(customizeModules);
     assertStyle(customizeModules.$.container, 'display', 'none');
@@ -52,7 +49,6 @@
             NewTabPageProxy.setInstance(mock, new PageCallbackRouter()));
     callbackRouterRemote = NewTabPageProxy.getInstance()
                                .callbackRouter.$.bindNewPipeAndPassRemote();
-    moduleRegistry = installMock(ModuleRegistry);
     metrics = fakeMetricsPrivate();
     loadTimeData.overrideValues({modulesVisibleManagedByPolicy: false});
     cartHandler = installMock(CartHandlerRemote, ChromeCartProxy.setHandler);
diff --git a/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.ts b/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.ts
index c74d2f9..19aa9a0 100644
--- a/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.ts
@@ -29,7 +29,7 @@
   test('instantiate module with data', async () => {
     // Arrange.
     const element = createElement();
-    const moduleDescriptor = new ModuleDescriptor('foo', 'bar', () => {
+    const moduleDescriptor = new ModuleDescriptor('foo', () => {
       // Move time forward to simulate delay instantiating module.
       windowProxy.setResultFor('now', 128);
       return Promise.resolve(element);
@@ -53,7 +53,7 @@
 
   test('instantiate module without data', async () => {
     // Arrange.
-    const moduleDescriptor = new ModuleDescriptor('foo', 'bar', initNullModule);
+    const moduleDescriptor = new ModuleDescriptor('foo', initNullModule);
 
     // Act.
     const moduleElement = await moduleDescriptor.initialize(0);
@@ -69,7 +69,7 @@
   test('module load times out', async () => {
     // Arrange.
     const moduleDescriptor = new ModuleDescriptor(
-        'foo', 'bar', () => new Promise(() => {}) /* Never resolves. */);
+        'foo', () => new Promise(() => {}) /* Never resolves. */);
 
     // Act.
     const initializePromise = moduleDescriptor.initialize(123);
@@ -86,7 +86,7 @@
     test('creates element on timeout', async () => {
       // Arrange.
       const moduleDescriptor = new ModuleDescriptorV2(
-          'foo', 'bar', ModuleHeight.SHORT,
+          'foo', ModuleHeight.SHORT,
           () => new Promise(() => {}) /* Never resolves. */);
 
       // Act.
diff --git a/chrome/test/data/webui/new_tab_page/modules/module_registry_test.ts b/chrome/test/data/webui/new_tab_page/modules/module_registry_test.ts
index f28434a..023ef3e 100644
--- a/chrome/test/data/webui/new_tab_page/modules/module_registry_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/module_registry_test.ts
@@ -6,7 +6,7 @@
 
 import {ModuleDescriptor, ModuleRegistry} from 'chrome://new-tab-page/lazy_load.js';
 import {NewTabPageProxy, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import {PageCallbackRouter, PageHandlerRemote, PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import {ModuleIdName, PageCallbackRouter, PageHandlerRemote, PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
@@ -41,12 +41,15 @@
     const bazModuleResolver: PromiseResolver<HTMLElement> =
         new PromiseResolver();
     const descriptors = [
-      new ModuleDescriptor('foo', 'bli', () => Promise.resolve(fooModule)),
-      new ModuleDescriptor('bar', 'blu', initNullModule),
-      new ModuleDescriptor('baz', 'bla', () => bazModuleResolver.promise),
-      new ModuleDescriptor('buz', 'blo', () => Promise.resolve(fooModule)),
+      new ModuleDescriptor('foo', () => Promise.resolve(fooModule)),
+      new ModuleDescriptor('bar', initNullModule),
+      new ModuleDescriptor('baz', () => bazModuleResolver.promise),
+      new ModuleDescriptor('buz', () => Promise.resolve(fooModule)),
     ];
     windowProxy.setResultFor('now', 5.0);
+    handler.setResultFor('getModulesIdNames', Promise.resolve({
+      data: descriptors.map(d => ({id: d.id, name: d.id} as ModuleIdName)),
+    }));
     handler.setResultFor('getModulesOrder', Promise.resolve({
       moduleIds: [],
     }));
@@ -94,13 +97,14 @@
           const barModule = createElement();
           const bazModule = createElement();
           const descriptors = [
-            new ModuleDescriptor(
-                'foo', 'bli', () => Promise.resolve(fooModule)),
-            new ModuleDescriptor(
-                'bar', 'blu', () => Promise.resolve(barModule)),
-            new ModuleDescriptor(
-                'baz', 'bla', () => Promise.resolve(bazModule)),
+            new ModuleDescriptor('foo', () => Promise.resolve(fooModule)),
+            new ModuleDescriptor('bar', () => Promise.resolve(barModule)),
+            new ModuleDescriptor('baz', () => Promise.resolve(bazModule)),
           ];
+          handler.setResultFor('getModulesIdNames', Promise.resolve({
+            data:
+                descriptors.map(d => ({id: d.id, name: d.id} as ModuleIdName)),
+          }));
           handler.setResultFor('getModulesOrder', Promise.resolve({
             moduleIds: ['bar', 'baz', 'foo'],
           }));
@@ -131,12 +135,15 @@
       const bizModule = createElement();
       const buzModule = createElement();
       const descriptors = [
-        new ModuleDescriptor('foo', 'bli', () => Promise.resolve(fooModule)),
-        new ModuleDescriptor('bar', 'blu', () => Promise.resolve(barModule)),
-        new ModuleDescriptor('baz', 'bla', () => Promise.resolve(bazModule)),
-        new ModuleDescriptor('biz', 'blo', () => Promise.resolve(bizModule)),
-        new ModuleDescriptor('buz', 'ble', () => Promise.resolve(buzModule)),
+        new ModuleDescriptor('foo', () => Promise.resolve(fooModule)),
+        new ModuleDescriptor('bar', () => Promise.resolve(barModule)),
+        new ModuleDescriptor('baz', () => Promise.resolve(bazModule)),
+        new ModuleDescriptor('biz', () => Promise.resolve(bizModule)),
+        new ModuleDescriptor('buz', () => Promise.resolve(buzModule)),
       ];
+      handler.setResultFor('getModulesIdNames', Promise.resolve({
+        data: descriptors.map(d => ({id: d.id, name: d.id} as ModuleIdName)),
+      }));
       handler.setResultFor('getModulesOrder', Promise.resolve({
         moduleIds: ['biz', 'bar'],
       }));
diff --git a/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.ts b/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.ts
index 5dc00ef..3d23b20 100644
--- a/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.ts
@@ -40,7 +40,7 @@
 
     // Act.
     moduleWrapper.module = {
-      descriptor: new ModuleDescriptor('foo', 'Foo', initNullModule),
+      descriptor: new ModuleDescriptor('foo', initNullModule),
       element: moduleElement,
     };
     await detectedImpression;
@@ -58,7 +58,7 @@
     // Act.
     moduleWrapper.module = {
       descriptor: new ModuleDescriptorV2(
-          'foo', 'Foo', ModuleHeight.TALL, async () => createElement()),
+          'foo', ModuleHeight.TALL, async () => createElement()),
       element: createElement(),
     };
 
@@ -69,12 +69,12 @@
   test('descriptor can only be set once', () => {
     const moduleElement = createElement();
     moduleWrapper.module = {
-      descriptor: new ModuleDescriptor('foo', 'Foo', initNullModule),
+      descriptor: new ModuleDescriptor('foo', initNullModule),
       element: moduleElement,
     };
     assertThrows(() => {
       moduleWrapper.module = {
-        descriptor: new ModuleDescriptor('foo', 'Foo', initNullModule),
+        descriptor: new ModuleDescriptor('foo', initNullModule),
         element: moduleElement,
       };
     });
@@ -84,7 +84,7 @@
     // Arrange.
     const moduleElement = createElement();
     moduleWrapper.module = {
-      descriptor: new ModuleDescriptor('foo', 'Foo', initNullModule),
+      descriptor: new ModuleDescriptor('foo', initNullModule),
       element: moduleElement,
     };
 
@@ -100,7 +100,7 @@
     // Arrange.
     const moduleElement = createElement();
     moduleWrapper.module = {
-      descriptor: new ModuleDescriptor('foo', 'Foo', initNullModule),
+      descriptor: new ModuleDescriptor('foo', initNullModule),
       element: moduleElement,
     };
 
diff --git a/chrome/test/data/webui/new_tab_page/modules/modules_test.ts b/chrome/test/data/webui/new_tab_page/modules/modules_test.ts
index 3c1cb02f..7d871ddb 100644
--- a/chrome/test/data/webui/new_tab_page/modules/modules_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/modules_test.ts
@@ -45,9 +45,9 @@
   [true, false].forEach(visible => {
     test(`modules rendered if visibility ${visible}`, async () => {
       // Arrange.
-      const fooDescriptor = new ModuleDescriptor('foo', 'Foo', initNullModule);
-      const barDescriptor = new ModuleDescriptor('bar', 'Bar', initNullModule);
-      const bazDescriptor = new ModuleDescriptor('baz', 'Baz', initNullModule);
+      const fooDescriptor = new ModuleDescriptor('foo', initNullModule);
+      const barDescriptor = new ModuleDescriptor('bar', initNullModule);
+      const bazDescriptor = new ModuleDescriptor('baz', initNullModule);
       moduleRegistry.setResultFor(
           'getDescriptors', [fooDescriptor, barDescriptor, bazDescriptor]);
       // Act.
@@ -103,12 +103,9 @@
     [true, false].forEach(visible => {
       test(`first run experience shows if modules ${visible}`, async () => {
         // Arrange.
-        const fooDescriptor =
-            new ModuleDescriptor('foo', 'Foo', initNullModule);
-        const barDescriptor =
-            new ModuleDescriptor('bar', 'Bar', initNullModule);
-        const bazDescriptor =
-            new ModuleDescriptor('baz', 'Baz', initNullModule);
+        const fooDescriptor = new ModuleDescriptor('foo', initNullModule);
+        const barDescriptor = new ModuleDescriptor('bar', initNullModule);
+        const bazDescriptor = new ModuleDescriptor('baz', initNullModule);
         moduleRegistry.setResultFor(
             'getDescriptors', [fooDescriptor, barDescriptor, bazDescriptor]);
         // Act.
@@ -158,7 +155,7 @@
 
     test(`clicking customize chrome link sends event`, async () => {
       // Arrange.
-      const fooDescriptor = new ModuleDescriptor('foo', 'Foo', initNullModule);
+      const fooDescriptor = new ModuleDescriptor('foo', initNullModule);
       moduleRegistry.setResultFor('getDescriptors', [fooDescriptor]);
       const modulesElement = await createModulesElement([
         {
@@ -181,8 +178,8 @@
 
     test(`fre buttons work`, async () => {
       // Arrange.
-      const fooDescriptor = new ModuleDescriptor('foo', 'Foo', initNullModule);
-      const barDescriptor = new ModuleDescriptor('bar', 'Bar', initNullModule);
+      const fooDescriptor = new ModuleDescriptor('foo', initNullModule);
+      const barDescriptor = new ModuleDescriptor('bar', initNullModule);
 
       moduleRegistry.setResultFor(
           'getDescriptors', [fooDescriptor, barDescriptor]);
@@ -249,13 +246,13 @@
         moduleArray.push(module);
       }
       const fooDescriptor = new ModuleDescriptorV2(
-          'foo', 'foo', ModuleHeight.SHORT, async () => createElement());
+          'foo', ModuleHeight.SHORT, async () => createElement());
       const barDescriptor = new ModuleDescriptorV2(
-          'bar', 'bar', ModuleHeight.SHORT, async () => createElement());
+          'bar', ModuleHeight.SHORT, async () => createElement());
       const bazDescriptor = new ModuleDescriptorV2(
-          'baz', 'baz', ModuleHeight.SHORT, async () => createElement());
+          'baz', ModuleHeight.SHORT, async () => createElement());
       const quzDescriptor = new ModuleDescriptorV2(
-          'quz', 'quz', ModuleHeight.TALL, async () => createElement());
+          'quz', ModuleHeight.TALL, async () => createElement());
       moduleRegistry.setResultFor(
           'getDescriptors',
           [fooDescriptor, barDescriptor, bazDescriptor, quzDescriptor]);
@@ -318,11 +315,11 @@
         moduleArray.push(module);
       }
       const fooDescriptor = new ModuleDescriptorV2(
-          'foo', 'foo', ModuleHeight.SHORT, async () => createElement());
+          'foo', ModuleHeight.SHORT, async () => createElement());
       const barDescriptor = new ModuleDescriptorV2(
-          'bar', 'bar', ModuleHeight.SHORT, async () => createElement());
+          'bar', ModuleHeight.SHORT, async () => createElement());
       const bazDescriptor = new ModuleDescriptorV2(
-          'baz', 'baz', ModuleHeight.SHORT, async () => createElement());
+          'baz', ModuleHeight.SHORT, async () => createElement());
       moduleRegistry.setResultFor(
           'getDescriptors', [fooDescriptor, barDescriptor, bazDescriptor]);
 
@@ -464,7 +461,7 @@
   test('modules can be dismissed and restored', async () => {
     // Arrange.
     let restoreCalled = false;
-    const fooDescriptor = new ModuleDescriptor('foo', 'Foo', initNullModule);
+    const fooDescriptor = new ModuleDescriptor('foo', initNullModule);
     moduleRegistry.setResultFor('getDescriptors', [fooDescriptor]);
 
     // Act.
@@ -524,7 +521,7 @@
   test('modules can be disabled and restored', async () => {
     // Arrange.
     let restoreCalled = false;
-    const fooDescriptor = new ModuleDescriptor('foo', 'bar', initNullModule);
+    const fooDescriptor = new ModuleDescriptor('foo', initNullModule);
     moduleRegistry.setResultFor('getDescriptors', [fooDescriptor]);
 
     // Act.
@@ -605,8 +602,8 @@
 
   test('record number of loaded modules', async () => {
     // Arrange.
-    const fooDescriptor = new ModuleDescriptor('foo', 'Foo', initNullModule);
-    const barDescriptor = new ModuleDescriptor('bar', 'Bar', initNullModule);
+    const fooDescriptor = new ModuleDescriptor('foo', initNullModule);
+    const barDescriptor = new ModuleDescriptor('bar', initNullModule);
     moduleRegistry.setResultFor(
         'getDescriptors', [fooDescriptor, barDescriptor]);
     await createModulesElement([
@@ -628,8 +625,8 @@
 
   test('record module loaded with other modules', async () => {
     // Arrange.
-    const fooDescriptor = new ModuleDescriptor('foo', 'Foo', initNullModule);
-    const barDescriptor = new ModuleDescriptor('bar', 'Bar', initNullModule);
+    const fooDescriptor = new ModuleDescriptor('foo', initNullModule);
+    const barDescriptor = new ModuleDescriptor('bar', initNullModule);
     moduleRegistry.setResultFor(
         'getDescriptors', [fooDescriptor, barDescriptor]);
     await createModulesElement([
@@ -666,11 +663,11 @@
         moduleArray.push(module);
       }
       const fooDescriptor = new ModuleDescriptorV2(
-          'foo', 'Foo', ModuleHeight.TALL, async () => createElement());
+          'foo', ModuleHeight.TALL, async () => createElement());
       const barDescriptor = new ModuleDescriptorV2(
-          'bar', 'Bar', ModuleHeight.TALL, async () => createElement());
+          'bar', ModuleHeight.TALL, async () => createElement());
       const fooBarDescriptor = new ModuleDescriptorV2(
-          'foo bar', 'Foo Baz', ModuleHeight.TALL, async () => createElement());
+          'foo bar', ModuleHeight.TALL, async () => createElement());
 
       moduleRegistry.setResultFor(
           'getDescriptors', [fooDescriptor, barDescriptor, fooBarDescriptor]);
@@ -834,12 +831,11 @@
         moduleArray.push(module);
       }
       const fooDescriptor = new ModuleDescriptorV2(
-          'foo', 'Foo', ModuleHeight.TALL, async () => createElement());
+          'foo', ModuleHeight.TALL, async () => createElement());
       const barDescriptor = new ModuleDescriptorV2(
-          'bar', 'Bar', ModuleHeight.SHORT, async () => createElement());
+          'bar', ModuleHeight.SHORT, async () => createElement());
       const fooBarDescriptor = new ModuleDescriptorV2(
-          'foo bar', 'Foo Baz', ModuleHeight.SHORT,
-          async () => createElement());
+          'foo bar', ModuleHeight.SHORT, async () => createElement());
 
       moduleRegistry.setResultFor(
           'getDescriptors', [fooDescriptor, barDescriptor, fooBarDescriptor]);
@@ -920,12 +916,11 @@
         moduleArray.push(module);
       }
       const fooDescriptor = new ModuleDescriptorV2(
-          'foo', 'Foo', ModuleHeight.TALL, async () => createElement());
+          'foo', ModuleHeight.TALL, async () => createElement());
       const barDescriptor = new ModuleDescriptorV2(
-          'bar', 'Bar', ModuleHeight.SHORT, async () => createElement());
+          'bar', ModuleHeight.SHORT, async () => createElement());
       const fooBarDescriptor = new ModuleDescriptorV2(
-          'foo bar', 'Foo Baz', ModuleHeight.SHORT,
-          async () => createElement());
+          'foo bar', ModuleHeight.SHORT, async () => createElement());
 
       moduleRegistry.setResultFor(
           'getDescriptors', [fooDescriptor, barDescriptor, fooBarDescriptor]);
diff --git a/chrome/test/data/webui/settings/chooser_exception_list_entry_tests.ts b/chrome/test/data/webui/settings/chooser_exception_list_entry_tests.ts
index c3bab67..bec0eb5 100644
--- a/chrome/test/data/webui/settings/chooser_exception_list_entry_tests.ts
+++ b/chrome/test/data/webui/settings/chooser_exception_list_entry_tests.ts
@@ -11,6 +11,7 @@
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {TestSiteSettingsPrefsBrowserProxy} from './test_site_settings_prefs_browser_proxy.js';
+import {assertTooltipIsHidden} from './test_util.js';
 // clang-format on
 
 /**
@@ -210,10 +211,7 @@
 
         // This tooltip is never shown since a common tooltip will be used.
         assertTrue(!!paperTooltip);
-        assertEquals(
-            'none',
-            (paperTooltip!.computedStyleMap().get('display') as
-             {value: string})!.value);
+        assertTooltipIsHidden(paperTooltip);
         assertFalse(paperTooltip!._showing);
 
         const wait = eventToPromise('show-tooltip', document);
@@ -221,10 +219,7 @@
             new MouseEvent('mouseenter', {bubbles: true, composed: true}));
         return wait.then(() => {
           assertTrue(paperTooltip!._showing);
-          assertEquals(
-              'none',
-              (paperTooltip!.computedStyleMap().get('display') as
-               {value: string})!.value);
+          assertTooltipIsHidden(paperTooltip);
         });
       });
 
diff --git a/chrome/test/data/webui/settings/site_list_entry_tests.ts b/chrome/test/data/webui/settings/site_list_entry_tests.ts
index c71b977f..36374756 100644
--- a/chrome/test/data/webui/settings/site_list_entry_tests.ts
+++ b/chrome/test/data/webui/settings/site_list_entry_tests.ts
@@ -15,6 +15,7 @@
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {TestSiteSettingsPrefsBrowserProxy} from './test_site_settings_prefs_browser_proxy.js';
+import {assertTooltipIsHidden} from './test_util.js';
 
 // clang-format on
 
@@ -48,18 +49,14 @@
     const icon = prefIndicator!.shadowRoot!.querySelector('cr-tooltip-icon')!;
     const paperTooltip = icon.shadowRoot!.querySelector('paper-tooltip')!;
     // Never shown since site-list will show a common tooltip.
-    assertEquals('none', (paperTooltip.computedStyleMap().get('display') as {
-                           value: number,
-                         }).value);
+    assertTooltipIsHidden(paperTooltip);
     assertFalse(paperTooltip._showing);
     const wait = eventToPromise('show-tooltip', document);
     icon.$.indicator.dispatchEvent(
         new MouseEvent('mouseenter', {bubbles: true, composed: true}));
     return wait.then(() => {
       assertTrue(paperTooltip._showing);
-      assertEquals('none', (paperTooltip.computedStyleMap().get('display') as {
-                             value: number,
-                           }).value);
+      assertTooltipIsHidden(paperTooltip);
     });
   });
 
diff --git a/chrome/test/data/webui/settings/test_util.ts b/chrome/test/data/webui/settings/test_util.ts
index 80d0f3bd1..12e96d5 100644
--- a/chrome/test/data/webui/settings/test_util.ts
+++ b/chrome/test/data/webui/settings/test_util.ts
@@ -3,11 +3,11 @@
 // found in the LICENSE file.
 
 // clang-format off
-import {ChooserType, ContentSetting, ContentSettingProvider, ContentSettingsTypes, DefaultContentSetting, OriginInfo, RawChooserException, RawSiteException, SiteGroup, SiteSettingSource} from 'chrome://settings/lazy_load.js';
+import {ChooserType, ContentSetting, ContentSettingProvider, ContentSettingsTypes, DefaultContentSetting, OriginInfo, PaperTooltipElement, RawChooserException, RawSiteException, SiteGroup, SiteSettingSource} from 'chrome://settings/lazy_load.js';
 import {Route, Router} from 'chrome://settings/settings.js';
+import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 // clang-format on
 
-
 /**
  * Helper to create an object containing a ContentSettingsType key to array or
  * object value. This is a convenience function that can eventually be
@@ -224,3 +224,15 @@
         new URLSearchParams(window.location.search), true);
   });
 }
+
+/**
+ * Helper to assert that a paper-tooltip element is visually hidden but still
+ * accessible by screen readers.
+ */
+export function assertTooltipIsHidden(tooltip: PaperTooltipElement) {
+  const tooltipStyle = window.getComputedStyle(tooltip);
+  assertEquals('rect(0px, 0px, 0px, 0px)', tooltipStyle.clip);
+  assertEquals('1px', tooltipStyle.height);
+  assertEquals('1px', tooltipStyle.width);
+  assertEquals('hidden', tooltipStyle.overflow);
+}
diff --git a/chromeos/ash/components/BUILD.gn b/chromeos/ash/components/BUILD.gn
index abe8c0af..723b3398 100644
--- a/chromeos/ash/components/BUILD.gn
+++ b/chromeos/ash/components/BUILD.gn
@@ -44,6 +44,7 @@
     "//chromeos/ash/components/smbfs:unit_tests",
     "//chromeos/ash/components/string_matching:unit_tests",
     "//chromeos/ash/components/sync_wifi:unit_tests",
+    "//chromeos/ash/components/system:unit_tests",
     "//chromeos/ash/components/tether:unit_tests",
     "//chromeos/ash/components/timezone:unit_tests",
     "//chromeos/ash/components/tpm:unit_tests",
diff --git a/chromeos/ash/components/assistant/test_support/expect_utils.h b/chromeos/ash/components/assistant/test_support/expect_utils.h
index ea2aa21f..5da20d1f 100644
--- a/chromeos/ash/components/assistant/test_support/expect_utils.h
+++ b/chromeos/ash/components/assistant/test_support/expect_utils.h
@@ -67,9 +67,4 @@
 }  // namespace assistant
 }  // namespace ash
 
-// TODO(https://crbug.com/1164001): remove when the migration is finished.
-namespace chromeos::assistant::test {
-using ::ash::assistant::test::ExpectResult;
-}
-
 #endif  // CHROMEOS_ASH_COMPONENTS_ASSISTANT_TEST_SUPPORT_EXPECT_UTILS_H_
diff --git a/chromeos/ash/components/audio/audio_device.h b/chromeos/ash/components/audio/audio_device.h
index 8b585e46..4163607 100644
--- a/chromeos/ash/components/audio/audio_device.h
+++ b/chromeos/ash/components/audio/audio_device.h
@@ -134,12 +134,4 @@
 
 }  // namespace ash
 
-// TODO(https://crbug.com/1164001): remove after the Chrome OS source code
-// directory migration is finished.
-namespace chromeos {
-using ::ash::AudioDevice;
-using ::ash::AudioDeviceList;
-using ::ash::AudioDeviceType;
-}  // namespace chromeos
-
 #endif  // CHROMEOS_ASH_COMPONENTS_AUDIO_AUDIO_DEVICE_H_
diff --git a/chromeos/ash/components/audio/cras_audio_handler.h b/chromeos/ash/components/audio/cras_audio_handler.h
index 9a988bb..351d5f0f 100644
--- a/chromeos/ash/components/audio/cras_audio_handler.h
+++ b/chromeos/ash/components/audio/cras_audio_handler.h
@@ -843,12 +843,6 @@
 
 }  // namespace ash
 
-// TODO(https://crbug.com/1164001): remove after //chrome/browser/chromeos
-// source migration is finished.
-namespace chromeos {
-using ::ash::CrasAudioHandler;
-}
-
 namespace base {
 
 template <>
diff --git a/chromeos/ash/components/audio/sounds.h b/chromeos/ash/components/audio/sounds.h
index 89fb377..d96b0b75 100644
--- a/chromeos/ash/components/audio/sounds.h
+++ b/chromeos/ash/components/audio/sounds.h
@@ -34,10 +34,4 @@
 
 }  // namespace ash
 
-// TODO(https://crbug.com/1164001): remove after the Chrome OS source code
-// directory migration is finished.
-namespace chromeos {
-using ::ash::Sound;
-}
-
 #endif  // CHROMEOS_ASH_COMPONENTS_AUDIO_SOUNDS_H_
diff --git a/chromeos/ash/components/cryptohome/system_salt_getter.h b/chromeos/ash/components/cryptohome/system_salt_getter.h
index d030b2c..d9ef039 100644
--- a/chromeos/ash/components/cryptohome/system_salt_getter.h
+++ b/chromeos/ash/components/cryptohome/system_salt_getter.h
@@ -77,10 +77,4 @@
 
 }  // namespace ash
 
-// TODO(https://crbug.com/1164001): remove after the //chrome/browser/chromeos
-// source code migration is finished.
-namespace chromeos {
-using ::ash::SystemSaltGetter;
-}
-
 #endif  // CHROMEOS_ASH_COMPONENTS_CRYPTOHOME_SYSTEM_SALT_GETTER_H_
diff --git a/chromeos/ash/components/device_activity/BUILD.gn b/chromeos/ash/components/device_activity/BUILD.gn
index 491adb6..b4c4862 100644
--- a/chromeos/ash/components/device_activity/BUILD.gn
+++ b/chromeos/ash/components/device_activity/BUILD.gn
@@ -21,7 +21,7 @@
     "//chromeos/ash/components/dbus/session_manager",
     "//chromeos/ash/components/dbus/system_clock:system_clock",
     "//chromeos/ash/components/network",
-    "//chromeos/system",
+    "//chromeos/ash/components/system",
     "//components/policy/core/common",
     "//components/prefs:prefs",
     "//components/version_info",
@@ -79,7 +79,7 @@
     "//chromeos/ash/components/dbus/system_clock:system_clock",
     "//chromeos/ash/components/network",
     "//chromeos/ash/components/network:test_support",
-    "//chromeos/system:system",
+    "//chromeos/ash/components/system",
     "//components/policy/core/common:test_support",
     "//components/prefs:test_support",
     "//components/version_info:channel",
diff --git a/chromeos/ash/components/device_activity/DEPS b/chromeos/ash/components/device_activity/DEPS
index 62c61b4..abe60e1e 100644
--- a/chromeos/ash/components/device_activity/DEPS
+++ b/chromeos/ash/components/device_activity/DEPS
@@ -3,6 +3,7 @@
   "+chromeos/ash/components/dbus/system_clock",
   "+chromeos/ash/components/dbus/session_manager",
   "+chromeos/ash/components/network",
+  "+chromeos/ash/components/system",
   "+components/policy",
   "+components/version_info",
   "+google_apis",
diff --git a/chromeos/ash/components/device_activity/daily_use_case_impl_unittest.cc b/chromeos/ash/components/device_activity/daily_use_case_impl_unittest.cc
index 0a5a80f..46e2cac 100644
--- a/chromeos/ash/components/device_activity/daily_use_case_impl_unittest.cc
+++ b/chromeos/ash/components/device_activity/daily_use_case_impl_unittest.cc
@@ -9,7 +9,7 @@
 #include "chromeos/ash/components/device_activity/device_activity_controller.h"
 #include "chromeos/ash/components/device_activity/fake_psm_delegate.h"
 #include "chromeos/ash/components/device_activity/fresnel_pref_names.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/version_info/channel.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
diff --git a/chromeos/ash/components/device_activity/device_active_use_case.cc b/chromeos/ash/components/device_activity/device_active_use_case.cc
index b24a2c5..9421585 100644
--- a/chromeos/ash/components/device_activity/device_active_use_case.cc
+++ b/chromeos/ash/components/device_activity/device_active_use_case.cc
@@ -7,7 +7,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/prefs/pref_service.h"
 #include "components/version_info/version_info.h"
 #include "crypto/hmac.h"
diff --git a/chromeos/ash/components/device_activity/device_active_use_case_unittest.cc b/chromeos/ash/components/device_activity/device_active_use_case_unittest.cc
index 36f4daa..5b15880a 100644
--- a/chromeos/ash/components/device_activity/device_active_use_case_unittest.cc
+++ b/chromeos/ash/components/device_activity/device_active_use_case_unittest.cc
@@ -12,7 +12,7 @@
 #include "chromeos/ash/components/device_activity/first_active_use_case_impl.h"
 #include "chromeos/ash/components/device_activity/fresnel_pref_names.h"
 #include "chromeos/ash/components/device_activity/monthly_use_case_impl.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/version_info/channel.h"
 #include "private_membership_rlwe.pb.h"
diff --git a/chromeos/ash/components/device_activity/device_activity_client_unittest.cc b/chromeos/ash/components/device_activity/device_activity_client_unittest.cc
index 021cc3ce..1797793 100644
--- a/chromeos/ash/components/device_activity/device_activity_client_unittest.cc
+++ b/chromeos/ash/components/device_activity/device_activity_client_unittest.cc
@@ -27,7 +27,7 @@
 #include "chromeos/ash/components/device_activity/monthly_use_case_impl.h"
 #include "chromeos/ash/components/network/network_state_handler_observer.h"
 #include "chromeos/ash/components/network/network_state_test_helper.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/version_info/channel.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
diff --git a/chromeos/ash/components/device_activity/device_activity_controller.h b/chromeos/ash/components/device_activity/device_activity_controller.h
index 4218aad..294c5c15 100644
--- a/chromeos/ash/components/device_activity/device_activity_controller.h
+++ b/chromeos/ash/components/device_activity/device_activity_controller.h
@@ -10,7 +10,7 @@
 #include "base/component_export.h"
 #include "base/time/time.h"
 #include "chromeos/ash/components/device_activity/device_active_use_case.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
diff --git a/chromeos/ash/components/device_activity/device_activity_controller_unittest.cc b/chromeos/ash/components/device_activity/device_activity_controller_unittest.cc
index a4f4e1c..f246d66 100644
--- a/chromeos/ash/components/device_activity/device_activity_controller_unittest.cc
+++ b/chromeos/ash/components/device_activity/device_activity_controller_unittest.cc
@@ -10,7 +10,7 @@
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
 #include "chromeos/ash/components/device_activity/device_active_use_case.h"
 #include "chromeos/ash/components/device_activity/fresnel_pref_names.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/version_info/channel.h"
diff --git a/chromeos/ash/components/device_activity/first_active_use_case_impl_unittest.cc b/chromeos/ash/components/device_activity/first_active_use_case_impl_unittest.cc
index 58fac0dc..44301595 100644
--- a/chromeos/ash/components/device_activity/first_active_use_case_impl_unittest.cc
+++ b/chromeos/ash/components/device_activity/first_active_use_case_impl_unittest.cc
@@ -9,7 +9,7 @@
 #include "chromeos/ash/components/device_activity/device_activity_controller.h"
 #include "chromeos/ash/components/device_activity/fake_psm_delegate.h"
 #include "chromeos/ash/components/device_activity/fresnel_pref_names.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/version_info/channel.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
diff --git a/chromeos/ash/components/device_activity/monthly_use_case_impl_unittest.cc b/chromeos/ash/components/device_activity/monthly_use_case_impl_unittest.cc
index 15b9fe70..33ccef0 100644
--- a/chromeos/ash/components/device_activity/monthly_use_case_impl_unittest.cc
+++ b/chromeos/ash/components/device_activity/monthly_use_case_impl_unittest.cc
@@ -9,7 +9,7 @@
 #include "chromeos/ash/components/device_activity/device_activity_controller.h"
 #include "chromeos/ash/components/device_activity/fake_psm_delegate.h"
 #include "chromeos/ash/components/device_activity/fresnel_pref_names.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/version_info/channel.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
diff --git a/chromeos/ash/components/disks/disk_mount_manager.h b/chromeos/ash/components/disks/disk_mount_manager.h
index 8ed55bd..50bf4bf 100644
--- a/chromeos/ash/components/disks/disk_mount_manager.h
+++ b/chromeos/ash/components/disks/disk_mount_manager.h
@@ -264,11 +264,4 @@
 }  // namespace disks
 }  // namespace ash
 
-// TODO(https://crbug.com/1164001): remove when the migration is finished.
-namespace chromeos {
-namespace disks {
-using ::ash::disks::DiskMountManager;
-}  // namespace disks
-}  // namespace chromeos
-
 #endif  // CHROMEOS_ASH_COMPONENTS_DISKS_DISK_MOUNT_MANAGER_H_
diff --git a/chromeos/ash/components/feature_usage/feature_usage_metrics.h b/chromeos/ash/components/feature_usage/feature_usage_metrics.h
index 893e30972..1866892 100644
--- a/chromeos/ash/components/feature_usage/feature_usage_metrics.h
+++ b/chromeos/ash/components/feature_usage/feature_usage_metrics.h
@@ -115,9 +115,4 @@
 
 }  // namespace ash::feature_usage
 
-// TODO(https://crbug.com/1164001): remove after the migration is finished.
-namespace chromeos::feature_usage {
-using ::ash::feature_usage::FeatureUsageMetrics;
-}
-
 #endif  // CHROMEOS_ASH_COMPONENTS_FEATURE_USAGE_FEATURE_USAGE_METRICS_H_
diff --git a/chromeos/ash/components/install_attributes/install_attributes.h b/chromeos/ash/components/install_attributes/install_attributes.h
index c728994..4c6b0b7 100644
--- a/chromeos/ash/components/install_attributes/install_attributes.h
+++ b/chromeos/ash/components/install_attributes/install_attributes.h
@@ -246,9 +246,4 @@
 
 }  // namespace ash
 
-// TODO(https://crbug.com/1164001): remove after the migration is finished.
-namespace chromeos {
-using ::ash::InstallAttributes;
-}
-
 #endif  // CHROMEOS_ASH_COMPONENTS_INSTALL_ATTRIBUTES_INSTALL_ATTRIBUTES_H_
diff --git a/chromeos/ash/components/install_attributes/stub_install_attributes.h b/chromeos/ash/components/install_attributes/stub_install_attributes.h
index f2cc95e..b71c208 100644
--- a/chromeos/ash/components/install_attributes/stub_install_attributes.h
+++ b/chromeos/ash/components/install_attributes/stub_install_attributes.h
@@ -83,10 +83,4 @@
 
 }  // namespace ash
 
-// TODO(https://crbug.com/1164001): remove after the migration is finished.
-namespace chromeos {
-using ::ash::ScopedStubInstallAttributes;
-using ::ash::StubInstallAttributes;
-}  // namespace chromeos
-
 #endif  // CHROMEOS_ASH_COMPONENTS_INSTALL_ATTRIBUTES_STUB_INSTALL_ATTRIBUTES_H_
diff --git a/chromeos/ash/components/login/auth/auth_performer_unittest.cc b/chromeos/ash/components/login/auth/auth_performer_unittest.cc
index ef58d2f..19628afa 100644
--- a/chromeos/ash/components/login/auth/auth_performer_unittest.cc
+++ b/chromeos/ash/components/login/auth/auth_performer_unittest.cc
@@ -101,12 +101,12 @@
       : task_environment_(
             base::test::SingleThreadTaskEnvironment::MainThreadType::UI) {
     CryptohomeMiscClient::InitializeFake();
-    chromeos::SystemSaltGetter::Initialize();
+    SystemSaltGetter::Initialize();
     context_ = std::make_unique<UserContext>();
   }
 
   ~AuthPerformerTestBase() override {
-    chromeos::SystemSaltGetter::Shutdown();
+    SystemSaltGetter::Shutdown();
     CryptohomeMiscClient::Shutdown();
   }
 
diff --git a/chromeos/ash/components/login/auth/auth_session_authenticator_unittest.cc b/chromeos/ash/components/login/auth/auth_session_authenticator_unittest.cc
index d1a24ed0..4ad643a 100644
--- a/chromeos/ash/components/login/auth/auth_session_authenticator_unittest.cc
+++ b/chromeos/ash/components/login/auth/auth_session_authenticator_unittest.cc
@@ -245,7 +245,7 @@
     }
 
     CryptohomeMiscClient::InitializeFake();
-    chromeos::SystemSaltGetter::Initialize();
+    SystemSaltGetter::Initialize();
     UserDataAuthClient::OverrideGlobalInstanceForTesting(&userdataauth_);
 
     EXPECT_CALL(auth_status_consumer_, OnAuthSuccess(_))
@@ -271,7 +271,7 @@
   }
 
   ~AuthSessionAuthenticatorTest() override {
-    chromeos::SystemSaltGetter::Shutdown();
+    SystemSaltGetter::Shutdown();
     CryptohomeMiscClient::Shutdown();
   }
 
diff --git a/chromeos/ash/components/phonehub/fake_recent_apps_interaction_handler.cc b/chromeos/ash/components/phonehub/fake_recent_apps_interaction_handler.cc
index 91c8f8f5..33df52a 100644
--- a/chromeos/ash/components/phonehub/fake_recent_apps_interaction_handler.cc
+++ b/chromeos/ash/components/phonehub/fake_recent_apps_interaction_handler.cc
@@ -3,8 +3,10 @@
 // found in the LICENSE file.
 
 #include "chromeos/ash/components/phonehub/fake_recent_apps_interaction_handler.h"
+#include <utility>
 
 #include "base/containers/contains.h"
+#include "base/time/time.h"
 #include "chromeos/ash/components/phonehub/notification.h"
 
 namespace ash {
@@ -45,7 +47,8 @@
 void FakeRecentAppsInteractionHandler::NotifyRecentAppAddedOrUpdated(
     const Notification::AppMetadata& app_metadata,
     base::Time last_accessed_timestamp) {
-  recent_apps_metadata_.emplace_back(app_metadata, last_accessed_timestamp);
+  recent_apps_metadata_.emplace(recent_apps_metadata_.begin(), app_metadata,
+                                last_accessed_timestamp);
 }
 
 std::vector<Notification::AppMetadata>
@@ -59,7 +62,10 @@
 
 void FakeRecentAppsInteractionHandler::SetStreamableApps(
     const std::vector<Notification::AppMetadata>& streamable_apps) {
-  // TODO(nayebi): Do we need to implement this?
+  recent_apps_metadata_.clear();
+  for (const auto& app_metadata : streamable_apps) {
+    recent_apps_metadata_.emplace_back(app_metadata, base::Time::UnixEpoch());
+  }
 }
 
 void FakeRecentAppsInteractionHandler::ComputeAndUpdateUiState() {
diff --git a/chromeos/ash/components/phonehub/phone_status_processor.cc b/chromeos/ash/components/phonehub/phone_status_processor.cc
index 9da318c7..5a58f82 100644
--- a/chromeos/ash/components/phonehub/phone_status_processor.cc
+++ b/chromeos/ash/components/phonehub/phone_status_processor.cc
@@ -23,6 +23,7 @@
 #include "chromeos/ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "chromeos/ash/components/phonehub/mutable_phone_model.h"
 #include "chromeos/ash/components/phonehub/notification_processor.h"
+#include "chromeos/ash/components/phonehub/proto/phonehub_api.pb.h"
 #include "chromeos/ash/components/phonehub/recent_apps_interaction_handler.h"
 #include "chromeos/ash/components/phonehub/screen_lock_manager_impl.h"
 #include "chromeos/ash/services/multidevice_setup/public/cpp/prefs.h"
@@ -192,6 +193,20 @@
   return states;
 }
 
+bool ShouldUpdateRecents(
+    PhoneStatusProcessor::AppListUpdateType app_list_update_type) {
+  return app_list_update_type ==
+             PhoneStatusProcessor::AppListUpdateType::kOnlyRecentApps ||
+         app_list_update_type == PhoneStatusProcessor::AppListUpdateType::kBoth;
+}
+
+bool ShouldUpdateLauncher(
+    PhoneStatusProcessor::AppListUpdateType app_list_update_type) {
+  return app_list_update_type ==
+             PhoneStatusProcessor::AppListUpdateType::kOnlyLauncherApps ||
+         app_list_update_type == PhoneStatusProcessor::AppListUpdateType::kBoth;
+}
+
 }  // namespace
 
 PhoneStatusProcessor::PhoneStatusProcessor(
@@ -383,7 +398,8 @@
   ProcessReceivedNotifications(phone_status_snapshot.notifications());
   SetReceivedPhoneStatusModelStates(phone_status_snapshot.properties());
   if (features::IsEcheSWAEnabled()) {
-    GenerateAppListWithIcons(phone_status_snapshot.streamable_apps());
+    GenerateAppListWithIcons(phone_status_snapshot.streamable_apps(),
+                             AppListUpdateType::kBoth);
   }
   multidevice_feature_access_manager_
       ->UpdatedFeatureSetupConnectionStatusIfNeeded();
@@ -422,16 +438,25 @@
 
 void PhoneStatusProcessor::OnAppListUpdateReceived(
     const proto::AppListUpdate app_list_update) {
-  if (!app_list_update.has_all_apps())
+  if (!features::IsEcheSWAEnabled()) {
     return;
-
-  if (features::IsEcheSWAEnabled() && features::IsEcheLauncherEnabled()) {
-    GenerateAppListWithIcons(app_list_update.all_apps());
+  }
+  if (app_list_update.has_all_apps() && features::IsEcheLauncherEnabled()) {
+    GenerateAppListWithIcons(app_list_update.all_apps(),
+                             AppListUpdateType::kOnlyLauncherApps);
+  }
+  if (app_list_update.has_recent_apps()) {
+    GenerateAppListWithIcons(app_list_update.recent_apps(),
+                             AppListUpdateType::kOnlyRecentApps);
   }
 }
 
 void PhoneStatusProcessor::GenerateAppListWithIcons(
-    const proto::StreamableApps& streamable_apps) {
+    const proto::StreamableApps& streamable_apps,
+    AppListUpdateType app_list_update_type) {
+  PA_LOG(INFO) << "Received a list of " << streamable_apps.apps_size()
+               << " apps, app_list_update_type="
+               << static_cast<int>(app_list_update_type);
   if (streamable_apps.apps_size() == 0) {
     return;
   }
@@ -455,14 +480,16 @@
         IconDecoder::DecodingData(str_hash(key), app.icon()));
   }
 
-  icon_decoder_->BatchDecode(std::move(decoding_data_list),
-                             base::BindOnce(&PhoneStatusProcessor::IconsDecoded,
-                                            weak_ptr_factory_.GetWeakPtr(),
-                                            base::OwnedRef(apps_list)));
+  icon_decoder_->BatchDecode(
+      std::move(decoding_data_list),
+      base::BindOnce(&PhoneStatusProcessor::IconsDecoded,
+                     weak_ptr_factory_.GetWeakPtr(), base::OwnedRef(apps_list),
+                     app_list_update_type));
 }
 
 void PhoneStatusProcessor::IconsDecoded(
     std::vector<Notification::AppMetadata>& apps_list,
+    AppListUpdateType app_list_update_type,
     std::unique_ptr<std::vector<IconDecoder::DecodingData>> decode_items) {
   std::hash<std::string> str_hash;
   for (const IconDecoder::DecodingData& decoding_data : *decode_items) {
@@ -478,15 +505,16 @@
       }
     }
   }
-  if (recent_apps_interaction_handler_) {
+  if (recent_apps_interaction_handler_ &&
+      ShouldUpdateRecents(app_list_update_type)) {
     recent_apps_interaction_handler_->SetStreamableApps(apps_list);
   }
 
-  if (features::IsEcheLauncherEnabled() && app_stream_launcher_data_model_) {
+  if (features::IsEcheLauncherEnabled() && app_stream_launcher_data_model_ &&
+      ShouldUpdateLauncher(app_list_update_type)) {
     app_stream_launcher_data_model_->SetAppList(apps_list);
   }
-
-  if (features::IsEcheSWAEnabled() && features::IsEcheLauncherEnabled() &&
+  if (app_list_update_type == AppListUpdateType::kOnlyLauncherApps &&
       !has_received_first_app_list_update_ &&
       connection_initialized_timestamp_ != base::TimeTicks()) {
     base::UmaHistogramTimes(
diff --git a/chromeos/ash/components/phonehub/phone_status_processor.h b/chromeos/ash/components/phonehub/phone_status_processor.h
index 1db77019..68549f9 100644
--- a/chromeos/ash/components/phonehub/phone_status_processor.h
+++ b/chromeos/ash/components/phonehub/phone_status_processor.h
@@ -37,6 +37,13 @@
       public FeatureStatusProvider::Observer,
       public multidevice_setup::MultiDeviceSetupClient::Observer {
  public:
+  enum class AppListUpdateType {
+    kOnlyRecentApps = 0,
+    kOnlyLauncherApps,
+    kBoth,
+    kMaxValue = kBoth
+  };
+
   PhoneStatusProcessor(
       DoNotDisturbController* do_not_disturb_controller,
       FeatureStatusProvider* feature_status_provider,
@@ -90,10 +97,12 @@
   void SetEcheFeatureStatusReceivedFromPhoneHub(
       proto::FeatureStatus eche_feature_status);
 
-  void GenerateAppListWithIcons(const proto::StreamableApps& streamable_apps);
+  void GenerateAppListWithIcons(const proto::StreamableApps& streamable_apps,
+                                AppListUpdateType app_list_update_type);
 
   void IconsDecoded(
       std::vector<Notification::AppMetadata>& apps_list,
+      AppListUpdateType app_list_update_type,
       std::unique_ptr<std::vector<IconDecoder::DecodingData>> decode_items);
 
   DoNotDisturbController* do_not_disturb_controller_;
diff --git a/chromeos/ash/components/phonehub/phone_status_processor_unittest.cc b/chromeos/ash/components/phonehub/phone_status_processor_unittest.cc
index 3eb22f1..0bd3ff36d 100644
--- a/chromeos/ash/components/phonehub/phone_status_processor_unittest.cc
+++ b/chromeos/ash/components/phonehub/phone_status_processor_unittest.cc
@@ -455,7 +455,7 @@
 }
 
 TEST_F(PhoneStatusProcessorTest,
-       PhoneStatusSnapshotUpdate_AppStreamLauncher_enableded) {
+       PhoneStatusSnapshotUpdate_AppStreamLauncher_enabled) {
   scoped_feature_list_.Reset();
   scoped_feature_list_.InitWithFeatures(
       /*enabled_features=*/{features::kEcheSWA, features::kPhoneHubCameraRoll,
@@ -577,6 +577,18 @@
   EXPECT_EQ(u"vis", app_stream_launcher_data_model_->GetAppsListSortedByName()
                         ->at(1)
                         .visible_app_name);
+
+  EXPECT_EQ(2u,
+            fake_recent_apps_interaction_handler_->FetchRecentAppMetadataList()
+                .size());
+  EXPECT_EQ(u"vis",
+            fake_recent_apps_interaction_handler_->FetchRecentAppMetadataList()
+                .at(0)
+                .visible_app_name);
+  EXPECT_EQ(u"a_vis",
+            fake_recent_apps_interaction_handler_->FetchRecentAppMetadataList()
+                .at(1)
+                .visible_app_name);
 }
 
 TEST_F(PhoneStatusProcessorTest, PhoneStatusUpdate) {
@@ -910,7 +922,7 @@
   EXPECT_EQ("app1", app_stream_manager_observer_.last_app_stream_update_);
 }
 
-TEST_F(PhoneStatusProcessorTest, OnAppListUpdateReceived) {
+TEST_F(PhoneStatusProcessorTest, OnAppListUpdateReceived_allApps) {
   scoped_feature_list_.Reset();
   scoped_feature_list_.InitWithFeatures(
       /*enabled_features=*/{features::kEcheSWA, features::kPhoneHubCameraRoll,
@@ -940,7 +952,11 @@
 
   // Simulate receiving a proto message.
   fake_message_receiver_->NotifyAppListUpdateReceived(expected_update);
+  decoder_delegate_->CompleteAllRequests();
 
+  EXPECT_EQ(0u,
+            fake_recent_apps_interaction_handler_->FetchRecentAppMetadataList()
+                .size());
   EXPECT_EQ(2u, app_stream_launcher_data_model_->GetAppsList()->size());
   EXPECT_EQ(
       u"first_app",
@@ -950,6 +966,52 @@
       app_stream_launcher_data_model_->GetAppsList()->at(1).visible_app_name);
 }
 
+TEST_F(PhoneStatusProcessorTest, OnAppListUpdateReceived_recentApps) {
+  scoped_feature_list_.Reset();
+  scoped_feature_list_.InitWithFeatures(
+      /*enabled_features=*/{features::kEcheSWA, features::kPhoneHubCameraRoll,
+                            features::kEcheLauncher},
+      /*disabled_features=*/{});
+
+  fake_multidevice_setup_client_->SetHostStatusWithDevice(
+      std::make_pair(HostStatus::kHostVerified, test_remote_device_));
+  CreatePhoneStatusProcessor();
+
+  proto::AppListUpdate expected_update;
+  auto* streamable_apps = expected_update.mutable_recent_apps();
+  auto* app1 = streamable_apps->add_apps();
+  app1->set_package_name("pkg1");
+  app1->set_visible_name("first_app");
+  app1->set_icon("icon1");
+
+  auto* app2 = streamable_apps->add_apps();
+  app2->set_package_name("pkg2");
+  app2->set_visible_name("second_app");
+  app2->set_icon("icon2");
+
+  // Simulate feature set to enabled and connected.
+  fake_feature_status_provider_->SetStatus(FeatureStatus::kEnabledAndConnected);
+  fake_multidevice_setup_client_->SetFeatureState(
+      Feature::kPhoneHubNotifications, FeatureState::kEnabledByUser);
+
+  // Simulate receiving a proto message.
+  fake_message_receiver_->NotifyAppListUpdateReceived(expected_update);
+  decoder_delegate_->CompleteAllRequests();
+
+  EXPECT_EQ(0u, app_stream_launcher_data_model_->GetAppsList()->size());
+  EXPECT_EQ(2u,
+            fake_recent_apps_interaction_handler_->FetchRecentAppMetadataList()
+                .size());
+  EXPECT_EQ(u"first_app",
+            fake_recent_apps_interaction_handler_->FetchRecentAppMetadataList()
+                .at(0)
+                .visible_app_name);
+  EXPECT_EQ(u"second_app",
+            fake_recent_apps_interaction_handler_->FetchRecentAppMetadataList()
+                .at(1)
+                .visible_app_name);
+}
+
 TEST_F(PhoneStatusProcessorTest, OnAppListUpdateFeatureDisabled) {
   scoped_feature_list_.Reset();
   scoped_feature_list_.InitWithFeatures(
diff --git a/chromeos/ash/components/phonehub/proto/phonehub_api.proto b/chromeos/ash/components/phonehub/proto/phonehub_api.proto
index 78242dd..4a16608d 100644
--- a/chromeos/ash/components/phonehub/proto/phonehub_api.proto
+++ b/chromeos/ash/components/phonehub/proto/phonehub_api.proto
@@ -327,6 +327,7 @@
 message PhoneStatusSnapshot {
   PhoneProperties properties = 1;
   repeated Notification notifications = 2;
+  // TODO(nayebi): remove this and use AppListUpdate.recent_apps instead
   StreamableApps streamable_apps = 3;
 }
 
@@ -394,6 +395,7 @@
 message AppListUpdate {
   // List of apps available to stream, sorted by recency.
   optional StreamableApps all_apps = 1;
+  optional StreamableApps recent_apps = 2;
 }
 
 // When adding new fields to this message, update CameraRollItem#operator==
diff --git a/chromeos/ash/components/system/BUILD.gn b/chromeos/ash/components/system/BUILD.gn
new file mode 100644
index 0000000..b8e037a
--- /dev/null
+++ b/chromeos/ash/components/system/BUILD.gn
@@ -0,0 +1,62 @@
+# 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("//build/config/chromeos/ui_mode.gni")
+import("//testing/libfuzzer/fuzzer_test.gni")
+
+assert(is_chromeos_ash,
+       "Non Chrome OS or Lacros builds must not depend on //chromeos/ash")
+
+component("system") {
+  output_name = "chromeos_ash_components_system"
+  defines = [ "IS_CHROMEOS_ASH_COMPONENTS_SYSTEM_IMPL" ]
+
+  deps = [
+    "//ash/constants",
+    "//base",
+    "//ui/ozone:ozone_base",
+  ]
+  sources = [
+    "devicemode.cc",
+    "devicemode.h",
+    "factory_ping_embargo_check.cc",
+    "factory_ping_embargo_check.h",
+    "kiosk_oem_manifest_parser.cc",
+    "kiosk_oem_manifest_parser.h",
+
+    # Used when running mash, both on Linux and on real devices.
+    "fake_statistics_provider.cc",
+    "fake_statistics_provider.h",
+    "name_value_pairs_parser.cc",
+    "name_value_pairs_parser.h",
+    "scheduler_configuration_manager_base.cc",
+    "scheduler_configuration_manager_base.h",
+    "statistics_provider.cc",
+    "statistics_provider.h",
+    "statistics_provider_impl.cc",
+    "statistics_provider_impl.h",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  deps = [
+    ":system",
+    "//ash/constants",
+    "//base/test:test_support",
+    "//chromeos:test_utils",
+    "//testing/gtest",
+  ]
+  sources = [
+    "factory_ping_embargo_check_unittest.cc",
+    "kiosk_oem_manifest_parser_unittest.cc",
+    "name_value_pairs_parser_unittest.cc",
+    "statistics_provider_impl_unittest.cc",
+  ]
+}
+
+fuzzer_test("name_value_pairs_parser_fuzzer") {
+  sources = [ "name_value_pairs_parser_fuzzer.cc" ]
+  deps = [ ":system" ]
+}
diff --git a/chromeos/system/DEPS b/chromeos/ash/components/system/DEPS
similarity index 100%
rename from chromeos/system/DEPS
rename to chromeos/ash/components/system/DEPS
diff --git a/chromeos/system/devicemode.cc b/chromeos/ash/components/system/devicemode.cc
similarity index 92%
rename from chromeos/system/devicemode.cc
rename to chromeos/ash/components/system/devicemode.cc
index 9c8f69e5..96af8f7 100644
--- a/chromeos/system/devicemode.cc
+++ b/chromeos/ash/components/system/devicemode.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 "chromeos/system/devicemode.h"
+#include "chromeos/ash/components/system/devicemode.h"
 
 #include "ash/constants/ash_switches.h"
 #include "base/command_line.h"
diff --git a/chromeos/ash/components/system/devicemode.h b/chromeos/ash/components/system/devicemode.h
new file mode 100644
index 0000000..793195d
--- /dev/null
+++ b/chromeos/ash/components/system/devicemode.h
@@ -0,0 +1,19 @@
+// Copyright 2017 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_COMPONENTS_SYSTEM_DEVICEMODE_H_
+#define CHROMEOS_ASH_COMPONENTS_SYSTEM_DEVICEMODE_H_
+
+#include "base/component_export.h"
+
+namespace chromeos {
+
+// Returns true when running as system compositor. Ie. using libudev, kms and
+// evdev for input and output.
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+bool IsRunningAsSystemCompositor();
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_ASH_COMPONENTS_SYSTEM_DEVICEMODE_H_
diff --git a/chromeos/system/factory_ping_embargo_check.cc b/chromeos/ash/components/system/factory_ping_embargo_check.cc
similarity index 95%
rename from chromeos/system/factory_ping_embargo_check.cc
rename to chromeos/ash/components/system/factory_ping_embargo_check.cc
index 7039c5a6..dc0686a 100644
--- a/chromeos/system/factory_ping_embargo_check.cc
+++ b/chromeos/ash/components/system/factory_ping_embargo_check.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/system/factory_ping_embargo_check.h"
+#include "chromeos/ash/components/system/factory_ping_embargo_check.h"
 
 #include <string>
 
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 
 namespace chromeos {
 namespace system {
diff --git a/chromeos/system/factory_ping_embargo_check.h b/chromeos/ash/components/system/factory_ping_embargo_check.h
similarity index 79%
rename from chromeos/system/factory_ping_embargo_check.h
rename to chromeos/ash/components/system/factory_ping_embargo_check.h
index 09d3e122..752a5d3c 100644
--- a/chromeos/system/factory_ping_embargo_check.h
+++ b/chromeos/ash/components/system/factory_ping_embargo_check.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 CHROMEOS_SYSTEM_FACTORY_PING_EMBARGO_CHECK_H_
-#define CHROMEOS_SYSTEM_FACTORY_PING_EMBARGO_CHECK_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_SYSTEM_FACTORY_PING_EMBARGO_CHECK_H_
+#define CHROMEOS_ASH_COMPONENTS_SYSTEM_FACTORY_PING_EMBARGO_CHECK_H_
 
 #include "base/component_export.h"
 #include "base/time/time.h"
@@ -32,15 +32,15 @@
   kPassed
 };
 
-COMPONENT_EXPORT(CHROMEOS_SYSTEM)
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
 FactoryPingEmbargoState GetEnterpriseManagementPingEmbargoState(
     StatisticsProvider* statistics_provider);
 
-COMPONENT_EXPORT(CHROMEOS_SYSTEM)
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
 FactoryPingEmbargoState GetRlzPingEmbargoState(
     StatisticsProvider* statistics_provider);
 
 }  // namespace system
 }  // namespace chromeos
 
-#endif  // CHROMEOS_SYSTEM_FACTORY_PING_EMBARGO_CHECK_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_SYSTEM_FACTORY_PING_EMBARGO_CHECK_H_
diff --git a/chromeos/system/factory_ping_embargo_check_unittest.cc b/chromeos/ash/components/system/factory_ping_embargo_check_unittest.cc
similarity index 97%
rename from chromeos/system/factory_ping_embargo_check_unittest.cc
rename to chromeos/ash/components/system/factory_ping_embargo_check_unittest.cc
index a07655b0..042f4488 100644
--- a/chromeos/system/factory_ping_embargo_check_unittest.cc
+++ b/chromeos/ash/components/system/factory_ping_embargo_check_unittest.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/system/factory_ping_embargo_check.h"
+#include "chromeos/ash/components/system/factory_ping_embargo_check.h"
 
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
diff --git a/chromeos/system/fake_statistics_provider.cc b/chromeos/ash/components/system/fake_statistics_provider.cc
similarity index 96%
rename from chromeos/system/fake_statistics_provider.cc
rename to chromeos/ash/components/system/fake_statistics_provider.cc
index baf5ad72..c459de7 100644
--- a/chromeos/system/fake_statistics_provider.cc
+++ b/chromeos/ash/components/system/fake_statistics_provider.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 "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 
 #include <string>
 #include <utility>
diff --git a/chromeos/system/fake_statistics_provider.h b/chromeos/ash/components/system/fake_statistics_provider.h
similarity index 82%
rename from chromeos/system/fake_statistics_provider.h
rename to chromeos/ash/components/system/fake_statistics_provider.h
index 5493a548..dc66f3ec9 100644
--- a/chromeos/system/fake_statistics_provider.h
+++ b/chromeos/ash/components/system/fake_statistics_provider.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 CHROMEOS_SYSTEM_FAKE_STATISTICS_PROVIDER_H_
-#define CHROMEOS_SYSTEM_FAKE_STATISTICS_PROVIDER_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_SYSTEM_FAKE_STATISTICS_PROVIDER_H_
+#define CHROMEOS_ASH_COMPONENTS_SYSTEM_FAKE_STATISTICS_PROVIDER_H_
 
 #include <string>
 
@@ -11,12 +11,12 @@
 #include "base/component_export.h"
 #include "base/containers/flat_map.h"
 #include "base/strings/string_piece.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 
 namespace chromeos::system {
 
 // A fake StatisticsProvider implementation that is useful in tests.
-class COMPONENT_EXPORT(CHROMEOS_SYSTEM) FakeStatisticsProvider
+class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM) FakeStatisticsProvider
     : public StatisticsProvider {
  public:
   FakeStatisticsProvider();
@@ -51,8 +51,8 @@
 
 // A convenience subclass that automatically registers itself as the test
 // StatisticsProvider during construction and cleans up at destruction.
-class COMPONENT_EXPORT(CHROMEOS_SYSTEM) ScopedFakeStatisticsProvider
-    : public FakeStatisticsProvider {
+class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+    ScopedFakeStatisticsProvider : public FakeStatisticsProvider {
  public:
   ScopedFakeStatisticsProvider();
 
@@ -71,4 +71,4 @@
 using ::chromeos::system::ScopedFakeStatisticsProvider;
 }  // namespace ash::system
 
-#endif  // CHROMEOS_SYSTEM_FAKE_STATISTICS_PROVIDER_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_SYSTEM_FAKE_STATISTICS_PROVIDER_H_
diff --git a/chromeos/system/kiosk_oem_manifest_parser.cc b/chromeos/ash/components/system/kiosk_oem_manifest_parser.cc
similarity index 95%
rename from chromeos/system/kiosk_oem_manifest_parser.cc
rename to chromeos/ash/components/system/kiosk_oem_manifest_parser.cc
index 052f29f..d101acc 100644
--- a/chromeos/system/kiosk_oem_manifest_parser.cc
+++ b/chromeos/ash/components/system/kiosk_oem_manifest_parser.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 "chromeos/system/kiosk_oem_manifest_parser.h"
+#include "chromeos/ash/components/system/kiosk_oem_manifest_parser.h"
 
 #include <memory>
 
diff --git a/chromeos/system/kiosk_oem_manifest_parser.h b/chromeos/ash/components/system/kiosk_oem_manifest_parser.h
similarity index 80%
rename from chromeos/system/kiosk_oem_manifest_parser.h
rename to chromeos/ash/components/system/kiosk_oem_manifest_parser.h
index 57dc729f..387d01b 100644
--- a/chromeos/system/kiosk_oem_manifest_parser.h
+++ b/chromeos/ash/components/system/kiosk_oem_manifest_parser.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 CHROMEOS_SYSTEM_KIOSK_OEM_MANIFEST_PARSER_H_
-#define CHROMEOS_SYSTEM_KIOSK_OEM_MANIFEST_PARSER_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_SYSTEM_KIOSK_OEM_MANIFEST_PARSER_H_
+#define CHROMEOS_ASH_COMPONENTS_SYSTEM_KIOSK_OEM_MANIFEST_PARSER_H_
 
 #include <string>
 
@@ -13,7 +13,7 @@
 namespace chromeos {
 
 // Parser for app kiosk OEM manifest files.
-class COMPONENT_EXPORT(CHROMEOS_SYSTEM) KioskOemManifestParser {
+class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM) KioskOemManifestParser {
  public:
   // Kiosk OEM manifest.
   struct Manifest {
@@ -41,4 +41,4 @@
 
 }  // namespace chromeos
 
-#endif  // CHROMEOS_SYSTEM_KIOSK_OEM_MANIFEST_PARSER_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_SYSTEM_KIOSK_OEM_MANIFEST_PARSER_H_
diff --git a/chromeos/system/kiosk_oem_manifest_parser_unittest.cc b/chromeos/ash/components/system/kiosk_oem_manifest_parser_unittest.cc
similarity index 92%
rename from chromeos/system/kiosk_oem_manifest_parser_unittest.cc
rename to chromeos/ash/components/system/kiosk_oem_manifest_parser_unittest.cc
index a52077f3..0d18453b 100644
--- a/chromeos/system/kiosk_oem_manifest_parser_unittest.cc
+++ b/chromeos/ash/components/system/kiosk_oem_manifest_parser_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 "chromeos/system/kiosk_oem_manifest_parser.h"
+#include "chromeos/ash/components/system/kiosk_oem_manifest_parser.h"
 
 #include "chromeos/test/chromeos_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromeos/system/name_value_pairs_parser.cc b/chromeos/ash/components/system/name_value_pairs_parser.cc
similarity index 98%
rename from chromeos/system/name_value_pairs_parser.cc
rename to chromeos/ash/components/system/name_value_pairs_parser.cc
index b6518d2..fe6b758 100644
--- a/chromeos/system/name_value_pairs_parser.cc
+++ b/chromeos/ash/components/system/name_value_pairs_parser.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 "chromeos/system/name_value_pairs_parser.h"
+#include "chromeos/ash/components/system/name_value_pairs_parser.h"
 
 #include <stddef.h>
 #include <unistd.h>
diff --git a/chromeos/system/name_value_pairs_parser.h b/chromeos/ash/components/system/name_value_pairs_parser.h
similarity index 92%
rename from chromeos/system/name_value_pairs_parser.h
rename to chromeos/ash/components/system/name_value_pairs_parser.h
index 629e1dbf..79339089 100644
--- a/chromeos/system/name_value_pairs_parser.h
+++ b/chromeos/ash/components/system/name_value_pairs_parser.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 CHROMEOS_SYSTEM_NAME_VALUE_PAIRS_PARSER_H_
-#define CHROMEOS_SYSTEM_NAME_VALUE_PAIRS_PARSER_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_SYSTEM_NAME_VALUE_PAIRS_PARSER_H_
+#define CHROMEOS_ASH_COMPONENTS_SYSTEM_NAME_VALUE_PAIRS_PARSER_H_
 
 #include <string>
 #include <vector>
@@ -40,7 +40,7 @@
 
 // The parser is used to get machine info as name-value pairs. Defined here to
 // be accessible by tests.
-class COMPONENT_EXPORT(CHROMEOS_SYSTEM) NameValuePairsParser {
+class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM) NameValuePairsParser {
  public:
   using NameValueMap = base::flat_map<std::string, std::string>;
 
@@ -93,4 +93,4 @@
 }  // namespace system
 }  // namespace chromeos
 
-#endif  // CHROMEOS_SYSTEM_NAME_VALUE_PAIRS_PARSER_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_SYSTEM_NAME_VALUE_PAIRS_PARSER_H_
diff --git a/chromeos/system/name_value_pairs_parser_fuzzer.cc b/chromeos/ash/components/system/name_value_pairs_parser_fuzzer.cc
similarity index 96%
rename from chromeos/system/name_value_pairs_parser_fuzzer.cc
rename to chromeos/ash/components/system/name_value_pairs_parser_fuzzer.cc
index 7a8e7e5..3d0c5ae 100644
--- a/chromeos/system/name_value_pairs_parser_fuzzer.cc
+++ b/chromeos/ash/components/system/name_value_pairs_parser_fuzzer.cc
@@ -5,7 +5,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include "chromeos/system/name_value_pairs_parser.h"
+#include "chromeos/ash/components/system/name_value_pairs_parser.h"
 
 namespace chromeos {
 namespace system {
diff --git a/chromeos/system/name_value_pairs_parser_unittest.cc b/chromeos/ash/components/system/name_value_pairs_parser_unittest.cc
similarity index 98%
rename from chromeos/system/name_value_pairs_parser_unittest.cc
rename to chromeos/ash/components/system/name_value_pairs_parser_unittest.cc
index 0bc714d..b659edf 100644
--- a/chromeos/system/name_value_pairs_parser_unittest.cc
+++ b/chromeos/ash/components/system/name_value_pairs_parser_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 "chromeos/system/name_value_pairs_parser.h"
+#include "chromeos/ash/components/system/name_value_pairs_parser.h"
 
 #include "base/command_line.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromeos/system/scheduler_configuration_manager_base.cc b/chromeos/ash/components/system/scheduler_configuration_manager_base.cc
similarity index 88%
rename from chromeos/system/scheduler_configuration_manager_base.cc
rename to chromeos/ash/components/system/scheduler_configuration_manager_base.cc
index 6f00f84d..69446ae 100644
--- a/chromeos/system/scheduler_configuration_manager_base.cc
+++ b/chromeos/ash/components/system/scheduler_configuration_manager_base.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 "chromeos/system/scheduler_configuration_manager_base.h"
+#include "chromeos/ash/components/system/scheduler_configuration_manager_base.h"
 
 namespace chromeos {
 
diff --git a/chromeos/system/scheduler_configuration_manager_base.h b/chromeos/ash/components/system/scheduler_configuration_manager_base.h
similarity index 79%
rename from chromeos/system/scheduler_configuration_manager_base.h
rename to chromeos/ash/components/system/scheduler_configuration_manager_base.h
index 0d5cee8..e08c3dc 100644
--- a/chromeos/system/scheduler_configuration_manager_base.h
+++ b/chromeos/ash/components/system/scheduler_configuration_manager_base.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 CHROMEOS_SYSTEM_SCHEDULER_CONFIGURATION_MANAGER_BASE_H_
-#define CHROMEOS_SYSTEM_SCHEDULER_CONFIGURATION_MANAGER_BASE_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_SYSTEM_SCHEDULER_CONFIGURATION_MANAGER_BASE_H_
+#define CHROMEOS_ASH_COMPONENTS_SYSTEM_SCHEDULER_CONFIGURATION_MANAGER_BASE_H_
 
 #include <stddef.h>
 
@@ -16,7 +16,8 @@
 namespace chromeos {
 
 // A base class for SchedulerConfigurationManager.
-class COMPONENT_EXPORT(CHROMEOS_SYSTEM) SchedulerConfigurationManagerBase {
+class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+    SchedulerConfigurationManagerBase {
  public:
   class Observer : public base::CheckedObserver {
    public:
@@ -48,4 +49,4 @@
 
 }  // namespace chromeos
 
-#endif  // CHROMEOS_SYSTEM_SCHEDULER_CONFIGURATION_MANAGER_BASE_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_SYSTEM_SCHEDULER_CONFIGURATION_MANAGER_BASE_H_
diff --git a/chromeos/system/statistics_provider.cc b/chromeos/ash/components/system/statistics_provider.cc
similarity index 97%
rename from chromeos/system/statistics_provider.cc
rename to chromeos/ash/components/system/statistics_provider.cc
index ddcf59a..19e12be 100644
--- a/chromeos/system/statistics_provider.cc
+++ b/chromeos/ash/components/system/statistics_provider.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 
 #include "base/memory/singleton.h"
-#include "chromeos/system/statistics_provider_impl.h"
+#include "chromeos/ash/components/system/statistics_provider_impl.h"
 
 namespace chromeos::system {
 
diff --git a/chromeos/system/statistics_provider.h b/chromeos/ash/components/system/statistics_provider.h
similarity index 65%
rename from chromeos/system/statistics_provider.h
rename to chromeos/ash/components/system/statistics_provider.h
index d3f455e..1fa88ad 100644
--- a/chromeos/system/statistics_provider.h
+++ b/chromeos/ash/components/system/statistics_provider.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 CHROMEOS_SYSTEM_STATISTICS_PROVIDER_H_
-#define CHROMEOS_SYSTEM_STATISTICS_PROVIDER_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_SYSTEM_STATISTICS_PROVIDER_H_
+#define CHROMEOS_ASH_COMPONENTS_SYSTEM_STATISTICS_PROVIDER_H_
 
 #include "base/callback.h"
 #include "base/component_export.h"
@@ -13,51 +13,61 @@
 namespace chromeos::system {
 
 // Activation date key.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kActivateDateKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kActivateDateKey[];
 
 // The key that will be present in VPD if the device was enrolled in a domain
 // that blocks dev mode.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kBlockDevModeKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kBlockDevModeKey[];
 // The key that will be present in VPD if the device ever was enrolled.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kCheckEnrollmentKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kCheckEnrollmentKey[];
 
 // The key and values present in VPD to indicate if RLZ ping should be sent.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kShouldSendRlzPingKey[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM)
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kShouldSendRlzPingKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
 extern const char kShouldSendRlzPingValueFalse[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM)
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
 extern const char kShouldSendRlzPingValueTrue[];
 
 // The key present in VPD that indicates the date after which the RLZ ping is
 // allowed to be sent. It is in the format of "yyyy-mm-dd".
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kRlzEmbargoEndDateKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kRlzEmbargoEndDateKey[];
 
 // The key present in VPD that indicates the date after which enterprise
 // management pings are allowed to be sent. It is in the format of "yyyy-mm-dd".
-COMPONENT_EXPORT(CHROMEOS_SYSTEM)
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
 extern const char kEnterpriseManagementEmbargoEndDateKey[];
 
 // Customization ID key.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kCustomizationIdKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kCustomizationIdKey[];
 
 // Developer switch value.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kDevSwitchBootKey[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kDevSwitchBootValueDev[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM)
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kDevSwitchBootKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kDevSwitchBootValueDev[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
 extern const char kDevSwitchBootValueVerified[];
 
 // Dock MAC address key.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kDockMacAddressKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kDockMacAddressKey[];
 
 // Ethernet MAC address key.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kEthernetMacAddressKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kEthernetMacAddressKey[];
 
 // Firmware write protect switch value.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM)
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
 extern const char kFirmwareWriteProtectCurrentKey[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM)
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
 extern const char kFirmwareWriteProtectCurrentValueOn[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM)
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
 extern const char kFirmwareWriteProtectCurrentValueOff[];
 
 // Firmware type and associated values. The values are from crossystem output
@@ -65,69 +75,85 @@
 // firmware with MP and developer keys respectively, nonchrome indicates the
 // machine doesn't run on Chrome OS firmware. See crossystem source for more
 // details.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kFirmwareTypeKey[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM)
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kFirmwareTypeKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
 extern const char kFirmwareTypeValueDeveloper[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM)
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
 extern const char kFirmwareTypeValueNonchrome[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kFirmwareTypeValueNormal[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kFirmwareTypeValueNormal[];
 
 // HWID key.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kHardwareClassKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kHardwareClassKey[];
 
 // Key/values reporting if Chrome OS is running in a VM or not. These values are
 // read from crossystem output. See crossystem source for VM detection logic.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kIsVmKey[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kIsVmValueFalse[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kIsVmValueTrue[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM) extern const char kIsVmKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kIsVmValueFalse[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kIsVmValueTrue[];
 
 // Manufacture date key.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kManufactureDateKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kManufactureDateKey[];
 
 // OEM customization flag that permits exiting enterprise enrollment flow in
 // OOBE when 'oem_enterprise_managed' flag is set.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM)
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
 extern const char kOemCanExitEnterpriseEnrollmentKey[];
 
 // OEM customization directive that specified intended device purpose.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kOemDeviceRequisitionKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kOemDeviceRequisitionKey[];
 
 // OEM customization flag that enforces enterprise enrollment flow in OOBE.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM)
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
 extern const char kOemIsEnterpriseManagedKey[];
 
 // OEM customization flag that specifies if OOBE flow should be enhanced for
 // keyboard driven control.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kOemKeyboardDrivenOobeKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kOemKeyboardDrivenOobeKey[];
 
 // Offer coupon code key.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kOffersCouponCodeKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kOffersCouponCodeKey[];
 
 // Offer group key.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kOffersGroupCodeKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kOffersGroupCodeKey[];
 
 // Release Brand Code key.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kRlzBrandCodeKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kRlzBrandCodeKey[];
 
 // Regional data
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kRegionKey[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kInitialLocaleKey[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kInitialTimezoneKey[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kKeyboardLayoutKey[];
-COMPONENT_EXPORT(CHROMEOS_SYSTEM)
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM) extern const char kRegionKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kInitialLocaleKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kInitialTimezoneKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kKeyboardLayoutKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
 extern const char kKeyboardMechanicalLayoutKey[];
 
 // The key that will be present in RO VPD to indicate what identifier is used
 // for attestation-based registration of a device.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kAttestedDeviceIdKey[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kAttestedDeviceIdKey[];
 
 // Serial number key (VPD v2+ devices, Samsung: caroline and later) for use in
 // tests. Outside of tests GetEnterpriseMachineID() is the backward-compatible
 // way to obtain the serial number.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kSerialNumberKeyForTest[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kSerialNumberKeyForTest[];
 
 // This interface provides access to Chrome OS statistics.
-class COMPONENT_EXPORT(CHROMEOS_SYSTEM) StatisticsProvider {
+class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM) StatisticsProvider {
  public:
   // Represents the status of the VPD statistics source.
   enum class VpdStatus {
@@ -218,4 +244,4 @@
 using ::chromeos::system::StatisticsProvider;
 }  // namespace ash::system
 
-#endif  // CHROMEOS_SYSTEM_STATISTICS_PROVIDER_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_SYSTEM_STATISTICS_PROVIDER_H_
diff --git a/chromeos/system/statistics_provider_impl.cc b/chromeos/ash/components/system/statistics_provider_impl.cc
similarity index 98%
rename from chromeos/system/statistics_provider_impl.cc
rename to chromeos/ash/components/system/statistics_provider_impl.cc
index ad740f58..a979413 100644
--- a/chromeos/system/statistics_provider_impl.cc
+++ b/chromeos/ash/components/system/statistics_provider_impl.cc
@@ -1,8 +1,8 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/system/statistics_provider_impl.h"
+#include "chromeos/ash/components/system/statistics_provider_impl.h"
 
 #include <memory>
 #include <string>
@@ -28,7 +28,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
 #include "base/values.h"
-#include "chromeos/system/kiosk_oem_manifest_parser.h"
+#include "chromeos/ash/components/system/kiosk_oem_manifest_parser.h"
 
 namespace chromeos::system {
 
diff --git a/chromeos/system/statistics_provider_impl.h b/chromeos/ash/components/system/statistics_provider_impl.h
similarity index 91%
rename from chromeos/system/statistics_provider_impl.h
rename to chromeos/ash/components/system/statistics_provider_impl.h
index 8cea040..0b2c6fa 100644
--- a/chromeos/system/statistics_provider_impl.h
+++ b/chromeos/ash/components/system/statistics_provider_impl.h
@@ -1,9 +1,9 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_SYSTEM_STATISTICS_PROVIDER_IMPL_H_
-#define CHROMEOS_SYSTEM_STATISTICS_PROVIDER_IMPL_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_SYSTEM_STATISTICS_PROVIDER_IMPL_H_
+#define CHROMEOS_ASH_COMPONENTS_SYSTEM_STATISTICS_PROVIDER_IMPL_H_
 
 #include <memory>
 #include <string>
@@ -20,16 +20,17 @@
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/sequenced_task_runner.h"
-#include "chromeos/system/name_value_pairs_parser.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/name_value_pairs_parser.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos::system {
 
 // Result of loading values from the cached VPD file.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) extern const char kMetricVpdCacheReadResult[];
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM)
+extern const char kMetricVpdCacheReadResult[];
 
-class COMPONENT_EXPORT(CHROMEOS_SYSTEM) StatisticsProviderImpl
+class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM) StatisticsProviderImpl
     : public StatisticsProvider {
  public:
   struct StatisticsSources {
@@ -170,4 +171,4 @@
 
 }  // namespace chromeos::system
 
-#endif  // CHROMEOS_SYSTEM_STATISTICS_PROVIDER_IMPL_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_SYSTEM_STATISTICS_PROVIDER_IMPL_H_
diff --git a/chromeos/system/statistics_provider_impl_unittest.cc b/chromeos/ash/components/system/statistics_provider_impl_unittest.cc
similarity index 99%
rename from chromeos/system/statistics_provider_impl_unittest.cc
rename to chromeos/ash/components/system/statistics_provider_impl_unittest.cc
index d4749d7..81ee942 100644
--- a/chromeos/system/statistics_provider_impl_unittest.cc
+++ b/chromeos/ash/components/system/statistics_provider_impl_unittest.cc
@@ -1,8 +1,8 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// 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.
 
-#include "chromeos/system/statistics_provider_impl.h"
+#include "chromeos/ash/components/system/statistics_provider_impl.h"
 
 #include "ash/constants/ash_switches.h"
 #include "base/command_line.h"
diff --git a/chromeos/system/BUILD.gn b/chromeos/system/BUILD.gn
index dea016a..8075cec 100644
--- a/chromeos/system/BUILD.gn
+++ b/chromeos/system/BUILD.gn
@@ -2,60 +2,21 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/chromeos/ui_mode.gni")
-
-if (is_chromeos_ash) {
-  # Used to build and run fuzzer in ash-only code.
-  import("//testing/libfuzzer/fuzzer_test.gni")
-}
-
-assert(is_chromeos,
-       "Non-Chrome-OS or Lacros builds must not depend on //chromeos")
+assert(is_chromeos, "Non Chrome OS build must not depend on //chromeos")
 
 component("system") {
   output_name = "chromeos_system"
   defines = [ "IS_CHROMEOS_SYSTEM_IMPL" ]
-  deps = [ "//base" ]
+  deps = [
+    "//base",
+    "//chromeos:chromeos_export",
+  ]
   sources = [
+    "core_scheduling.cc",
+    "core_scheduling.h",
     "cpu_temperature_reader.cc",
     "cpu_temperature_reader.h",
   ]
-
-  if (is_chromeos) {
-    deps += [ "//chromeos:chromeos_export" ]
-    sources += [
-      "core_scheduling.cc",
-      "core_scheduling.h",
-    ]
-  }
-
-  if (is_chromeos_ash) {
-    deps += [
-      "//ash/constants",
-      "//base",
-      "//ui/ozone:ozone_base",
-    ]
-    sources += [
-      "devicemode.cc",
-      "devicemode.h",
-      "factory_ping_embargo_check.cc",
-      "factory_ping_embargo_check.h",
-      "kiosk_oem_manifest_parser.cc",
-      "kiosk_oem_manifest_parser.h",
-
-      # Used when running mash, both on Linux and on real devices.
-      "fake_statistics_provider.cc",
-      "fake_statistics_provider.h",
-      "name_value_pairs_parser.cc",
-      "name_value_pairs_parser.h",
-      "scheduler_configuration_manager_base.cc",
-      "scheduler_configuration_manager_base.h",
-      "statistics_provider.cc",
-      "statistics_provider.h",
-      "statistics_provider_impl.cc",
-      "statistics_provider_impl.h",
-    ]
-  }
 }
 
 source_set("unit_tests") {
@@ -66,25 +27,4 @@
     "//testing/gtest",
   ]
   sources = [ "cpu_temperature_reader_unittest.cc" ]
-
-  if (is_chromeos_ash) {
-    deps += [
-      "//ash/constants",
-      "//base/test:test_support",
-      "//chromeos:test_utils",
-    ]
-    sources += [
-      "factory_ping_embargo_check_unittest.cc",
-      "kiosk_oem_manifest_parser_unittest.cc",
-      "name_value_pairs_parser_unittest.cc",
-      "statistics_provider_impl_unittest.cc",
-    ]
-  }
-}
-
-if (is_chromeos_ash) {
-  fuzzer_test("name_value_pairs_parser_fuzzer") {
-    sources = [ "name_value_pairs_parser_fuzzer.cc" ]
-    deps = [ ":system" ]
-  }
 }
diff --git a/chromeos/system/devicemode.h b/chromeos/system/devicemode.h
deleted file mode 100644
index b60d77c..0000000
--- a/chromeos/system/devicemode.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_SYSTEM_DEVICEMODE_H_
-#define CHROMEOS_SYSTEM_DEVICEMODE_H_
-
-#include "base/component_export.h"
-
-namespace chromeos {
-
-// Returns true when running as system compositor. Ie. using libudev, kms and
-// evdev for input and output.
-COMPONENT_EXPORT(CHROMEOS_SYSTEM) bool IsRunningAsSystemCompositor();
-
-}  // namespace chromeos
-
-#endif  // CHROMEOS_SYSTEM_DEVICEMODE_H_
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index 43e56b0..953c5c8 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -326,4 +326,7 @@
 
   # https://crbug.com/1376638
   "lacros.AudioRecord",
+
+  # https://crbug.com/1399370
+  "security.ChromeSandboxed.lacros",
 ]
diff --git a/components/arc/DEPS b/components/arc/DEPS
index 1217d14..7451f92 100644
--- a/components/arc/DEPS
+++ b/components/arc/DEPS
@@ -3,9 +3,9 @@
   "+ash/constants",
   "+ash/public/cpp",
   "+chromeos/ash/components/memory",
+  "+chromeos/ash/components/system",
   "+chromeos/components/sensors",
   "+chromeos/dbus",
-  "+chromeos/system",
   "+components/guest_os",
   "+components/account_id",
   "+components/exo",
diff --git a/components/history_clusters/core/on_device_clustering_backend.cc b/components/history_clusters/core/on_device_clustering_backend.cc
index 58b7df22..beaf19f 100644
--- a/components/history_clusters/core/on_device_clustering_backend.cc
+++ b/components/history_clusters/core/on_device_clustering_backend.cc
@@ -249,7 +249,6 @@
     // Rewrite the entities for the visit, but only if it is possible that we
     // had additional metadata for it.
     if (entity_metadata_provider_) {
-      base::flat_map<std::string, int> inserted_categories;
       auto entity_it =
           visit.content_annotations.model_annotations.entities.begin();
       while (entity_it !=
@@ -302,8 +301,8 @@
   base::OnceCallback<std::vector<history::Cluster>()> clustering_callback =
       base::BindOnce(
           &OnDeviceClusteringBackend::ClusterVisitsOnBackgroundThread,
-          clustering_request_source, engagement_score_provider_ != nullptr,
-          std::move(cluster_visits), std::move(entity_id_to_metadata_map));
+          engagement_score_provider_ != nullptr, std::move(cluster_visits),
+          base::OwnedRef(std::move(entity_id_to_metadata_map)));
 
   switch (clustering_request_source) {
     case ClusteringRequestSource::kJourneysPage:
@@ -320,25 +319,54 @@
 // static
 std::vector<history::Cluster>
 OnDeviceClusteringBackend::ClusterVisitsOnBackgroundThread(
-    ClusteringRequestSource clustering_request_source,
     bool engagement_score_provider_is_valid,
     std::vector<history::ClusterVisit> visits,
-    base::flat_map<std::string, optimization_guide::EntityMetadata>
+    base::flat_map<std::string, optimization_guide::EntityMetadata>&
         entity_id_to_entity_metadata_map) {
-  base::ElapsedThreadTimer cluster_visits_timer;
+  base::ElapsedThreadTimer compute_clusters_timer;
 
-  // TODO(crbug.com/1260145): All of these objects are "stateless" between
-  // requests for clusters. If there needs to be shared state, the entire
-  // backend needs to be refactored to separate these objects from the UI and
-  // background thread.
+  // 1. Group visits into clusters.
+  base::ElapsedThreadTimer context_clusterer_timer;
   std::unique_ptr<Clusterer> clusterer = std::make_unique<Clusterer>();
+  std::vector<history::Cluster> clusters =
+      clusterer->CreateInitialClustersFromVisits(std::move(visits));
+  base::UmaHistogramTimes(
+      "History.Clusters.Backend.ContextClusterer.ThreadTime",
+      context_clusterer_timer.Elapsed());
 
+  // 2. Determine how the clusters should be displayed.
+  base::ElapsedThreadTimer compute_clusters_for_ui_timer;
+  clusters = GetClustersForUIOnBackgroundThread(
+      std::move(clusters), entity_id_to_entity_metadata_map);
+  base::UmaHistogramTimes(
+      "History.Clusters.Backend.ComputeClustersForUI.ThreadTime",
+      compute_clusters_for_ui_timer.Elapsed());
+
+  // 3. Determine the triggerability for the clusters.
+  for (auto& cluster : clusters) {
+    base::ElapsedThreadTimer cluster_triggerability_timer;
+    cluster = GetClusterTriggerabilityOnBackgroundThread(
+        engagement_score_provider_is_valid, std::move(cluster),
+        entity_id_to_entity_metadata_map);
+    base::UmaHistogramTimes(
+        "History.Clusters.Backend.ComputeClusterTriggerability.ThreadTime",
+        cluster_triggerability_timer.Elapsed());
+  }
+
+  base::UmaHistogramTimes("History.Clusters.Backend.ComputeClusters.ThreadTime",
+                          compute_clusters_timer.Elapsed());
+
+  return clusters;
+}
+
+// static
+std::vector<history::Cluster>
+OnDeviceClusteringBackend::GetClustersForUIOnBackgroundThread(
+    std::vector<history::Cluster> clusters,
+    base::flat_map<std::string, optimization_guide::EntityMetadata>&
+        entity_id_to_entity_metadata_map) {
   // The cluster processors to be run.
   std::vector<std::unique_ptr<ClusterProcessor>> cluster_processors;
-
-  // The cluster finalizers to be run.
-  std::vector<std::unique_ptr<ClusterFinalizer>> cluster_finalizers;
-
   cluster_processors.push_back(
       std::make_unique<FullMembershipClusterProcessor>());
   if (GetConfig().content_clustering_enabled) {
@@ -347,16 +375,40 @@
             &entity_id_to_entity_metadata_map));
   }
 
-  // Cluster finalizers that affect the appearance of a cluster on a UI surface.
+  // The cluster finalizers to run that affect the appearance of a cluster on a
+  // UI surface.
+  std::vector<std::unique_ptr<ClusterFinalizer>> cluster_finalizers;
   cluster_finalizers.push_back(
       std::make_unique<SimilarVisitDeduperClusterFinalizer>());
   cluster_finalizers.push_back(std::make_unique<RankingClusterFinalizer>());
   cluster_finalizers.push_back(std::make_unique<LabelClusterFinalizer>(
       &entity_id_to_entity_metadata_map));
 
-  // TODO(b/259466296): Guard the below cluster finalizers so that they don't
-  // run when we start persisting "basic" clusters at navigation, as these
-  // attributes will be computed on the fly.
+  // Process clusters.
+  for (const auto& processor : cluster_processors) {
+    processor->ProcessClusters(&clusters);
+  }
+
+  // Run finalizers that dedupe and score visits within a cluster and
+  // log several metrics about the result.
+  for (auto& cluster : clusters) {
+    for (const auto& finalizer : cluster_finalizers) {
+      finalizer->FinalizeCluster(cluster);
+    }
+  }
+
+  return clusters;
+}
+
+// static
+history::Cluster
+OnDeviceClusteringBackend::GetClusterTriggerabilityOnBackgroundThread(
+    bool engagement_score_provider_is_valid,
+    history::Cluster cluster,
+    base::flat_map<std::string, optimization_guide::EntityMetadata>&
+        entity_id_to_entity_metadata_map) {
+  // The cluster finalizers to be run.
+  std::vector<std::unique_ptr<ClusterFinalizer>> cluster_finalizers;
 
   // Cluster finalizers that affect the keywords for a cluster.
   cluster_finalizers.push_back(std::make_unique<KeywordClusterFinalizer>(
@@ -373,39 +425,11 @@
     cluster_finalizers.push_back(std::make_unique<CategoryClusterFinalizer>());
   }
 
-  // Group visits into clusters.
-  base::ElapsedThreadTimer clusterer_timer;
-  std::vector<history::Cluster> clusters =
-      clusterer->CreateInitialClustersFromVisits(std::move(visits));
-  base::UmaHistogramTimes(
-      "History.Clusters.Backend.ContextClusterer.ThreadTime",
-      clusterer_timer.Elapsed());
-
-  // Process clusters.
-  base::ElapsedThreadTimer cluster_processors_timer;
-  for (const auto& processor : cluster_processors) {
-    processor->ProcessClusters(&clusters);
+  for (const auto& finalizer : cluster_finalizers) {
+    finalizer->FinalizeCluster(cluster);
   }
-  base::UmaHistogramTimes(
-      "History.Clusters.Backend.ClusterProcessors.ThreadTime",
-      cluster_processors_timer.Elapsed());
 
-  // Run finalizers that dedupe and score visits within a cluster and
-  // log several metrics about the result.
-  base::ElapsedThreadTimer cluster_finalizers_timer;
-  for (auto& cluster : clusters) {
-    for (const auto& finalizer : cluster_finalizers) {
-      finalizer->FinalizeCluster(cluster);
-    }
-  }
-  base::UmaHistogramTimes(
-      "History.Clusters.Backend.ClusterFinalizers.ThreadTime",
-      cluster_finalizers_timer.Elapsed());
-
-  base::UmaHistogramTimes("History.Clusters.Backend.ComputeClusters.ThreadTime",
-                          cluster_visits_timer.Elapsed());
-
-  return clusters;
+  return cluster;
 }
 
 }  // namespace history_clusters
diff --git a/components/history_clusters/core/on_device_clustering_backend.h b/components/history_clusters/core/on_device_clustering_backend.h
index e9849cf..7017ae33 100644
--- a/components/history_clusters/core/on_device_clustering_backend.h
+++ b/components/history_clusters/core/on_device_clustering_backend.h
@@ -9,6 +9,7 @@
 #include "base/containers/flat_set.h"
 #include "base/containers/lru_cache.h"
 #include "base/containers/unique_ptr_adapters.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
@@ -51,7 +52,7 @@
 
  private:
   // Callback invoked when batch entity metadata has been received from
-  // |completed_task|. This will normalize |annotated_visits| and proceed to
+  // `completed_task`. This will normalize `annotated_visits` and proceed to
   // cluster them after normalization.
   void OnBatchEntityMetadataRetrieved(
       ClusteringRequestSource clustering_request_source,
@@ -63,7 +64,7 @@
           entity_metadata_map);
 
   // ProcessVisits adds additional metadata that might be used for clustering or
-  // Journeys to each visit in |annotated_visits|, such as human-readable
+  // Journeys to each visit in `annotated_visits`, such as human-readable
   // entities and categories, site engagement, etc.
   void ProcessVisits(
       ClusteringRequestSource clustering_request_source,
@@ -80,29 +81,50 @@
       optimization_guide::BatchEntityMetadataTask* completed_task,
       std::vector<history::ClusterVisit> cluster_visits,
       base::flat_map<std::string, optimization_guide::EntityMetadata>
-          human_readable_entity_name_to_entity_metadata_map,
+          entity_id_to_entity_metadata_map,
       ClustersCallback callback);
 
-  // Clusters |visits| on background thread.
+  // Clusters `visits` on background thread.
   static std::vector<history::Cluster> ClusterVisitsOnBackgroundThread(
-      ClusteringRequestSource clustering_request_source,
       bool engagement_score_provider_is_valid,
       std::vector<history::ClusterVisit> visits,
-      base::flat_map<std::string, optimization_guide::EntityMetadata>
-          human_readable_entity_name_to_entity_metadata_map);
+      base::flat_map<std::string, optimization_guide::EntityMetadata>&
+          entity_id_to_entity_metadata_map);
 
-  // The object to fetch entity metadata from. Not owned. Must outlive |this|.
-  optimization_guide::EntityMetadataProvider* entity_metadata_provider_ =
-      nullptr;
+  // Gets the displayable variant of `clusters` that will be shown on the WebUI
+  // and Side Panel on background thread. This will merge similar clusters, rank
+  // visits within the cluster, as well as provide a label.
+  //
+  // TODO(sophiechang): When we support more than one surface, add an enum for
+  //   which UI surface we want to calculate for.
+  static std::vector<history::Cluster> GetClustersForUIOnBackgroundThread(
+      std::vector<history::Cluster> clusters,
+      base::flat_map<std::string, optimization_guide::EntityMetadata>&
+          entity_id_to_entity_metadata_map);
 
-  // The object to get engagement scores from. Not owned. Must outlive |this|.
-  site_engagement::SiteEngagementScoreProvider* engagement_score_provider_ =
-      nullptr;
+  // Gets the metadata required for cluster triggerability (e.g. keywords,
+  // whether to show on prominent UI surfaces) for `cluster` on background
+  // thread.
+  static history::Cluster GetClusterTriggerabilityOnBackgroundThread(
+      bool engagement_score_proivder_is_valid,
+      history::Cluster cluster,
+      base::flat_map<std::string, optimization_guide::EntityMetadata>&
+          entity_id_to_entity_metadata_map);
 
-  // The object to fetch page load metadata from. Not owned. Must outlive
-  // |this|.
-  optimization_guide::NewOptimizationGuideDecider* optimization_guide_decider_ =
-      nullptr;
+  // Used to fetch entity metadata. Can be null if feature not enabled. Not
+  // owned. Must outlive `this`.
+  raw_ptr<optimization_guide::EntityMetadataProvider>
+      entity_metadata_provider_ = nullptr;
+
+  // Used to get engagement scores. Can be null during tests. Not owned. Must
+  // outlive `this`.
+  raw_ptr<site_engagement::SiteEngagementScoreProvider>
+      engagement_score_provider_ = nullptr;
+
+  // Used to get page load metadata. Can be null if feature not enabled. Not
+  // owned. Must outlive `this`.
+  raw_ptr<optimization_guide::NewOptimizationGuideDecider>
+      optimization_guide_decider_ = nullptr;
 
   // The set of batch entity metadata tasks currently in flight.
   base::flat_set<std::unique_ptr<optimization_guide::BatchEntityMetadataTask>,
@@ -110,7 +132,7 @@
       in_flight_batch_entity_metadata_tasks_;
 
   // The task runners to run clustering passes on.
-  // |user_visible_priority_background_task_runner_| should be used iff
+  // `user_visible_priority_background_task_runner_` should be used iff
   // clustering is blocking content on a page that user is actively looking at.
   const base::TaskTraits user_visible_task_traits_;
   const base::TaskTraits continue_on_shutdown_user_visible_task_traits_;
@@ -121,7 +143,7 @@
   scoped_refptr<base::SequencedTaskRunner>
       best_effort_priority_background_task_runner_;
 
-  // Last time |engagement_score_cache_| was refreshed.
+  // Last time `engagement_score_cache_` was refreshed.
   base::TimeTicks engagement_score_cache_last_refresh_timestamp_;
   // URL host to score mapping.
   base::HashingLRUCache<std::string, float> engagement_score_cache_;
diff --git a/components/messages/android/messages_feature.cc b/components/messages/android/messages_feature.cc
index 4193644..afc6657 100644
--- a/components/messages/android/messages_feature.cc
+++ b/components/messages/android/messages_feature.cc
@@ -38,7 +38,7 @@
 
 BASE_FEATURE(kMessagesForAndroidChromeSurvey,
              "MessagesForAndroidChromeSurvey",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kMessagesForAndroidInfrastructure,
              "MessagesForAndroidInfrastructure",
diff --git a/components/metrics/structured/key_data.cc b/components/metrics/structured/key_data.cc
index efdac0e..09e7413 100644
--- a/components/metrics/structured/key_data.cc
+++ b/components/metrics/structured/key_data.cc
@@ -16,8 +16,7 @@
 #include "crypto/hmac.h"
 #include "crypto/sha2.h"
 
-namespace metrics {
-namespace structured {
+namespace metrics::structured {
 namespace {
 
 // The expected size of a key, in bytes.
@@ -209,5 +208,4 @@
   proto_->Purge();
 }
 
-}  // namespace structured
-}  // namespace metrics
+}  // namespace metrics::structured
diff --git a/components/metrics/structured/key_data.h b/components/metrics/structured/key_data.h
index 00a0872f..513fe94 100644
--- a/components/metrics/structured/key_data.h
+++ b/components/metrics/structured/key_data.h
@@ -16,8 +16,7 @@
 #include "components/metrics/structured/storage.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-namespace metrics {
-namespace structured {
+namespace metrics::structured {
 
 class KeyDataTest;
 
@@ -26,7 +25,7 @@
 //
 // The class maintains one key and its rotation data for every project defined
 // in /tools/metrics/structured.xml. This can be used to generate:
-//  - a user ID for the project with KeyData::Id.
+//  - an ID for the project with KeyData::Id.
 //  - a hash of a given value for an event with KeyData::HmacMetric.
 //
 // KeyData performs key rotation. Every project is associated with a rotation
@@ -136,7 +135,6 @@
   base::WeakPtrFactory<KeyData> weak_factory_{this};
 };
 
-}  // namespace structured
-}  // namespace metrics
+}  // namespace metrics::structured
 
 #endif  // COMPONENTS_METRICS_STRUCTURED_KEY_DATA_H_
diff --git a/components/metrics/structured/structured_metrics_provider.cc b/components/metrics/structured/structured_metrics_provider.cc
index c95522ac..c96566cc 100644
--- a/components/metrics/structured/structured_metrics_provider.cc
+++ b/components/metrics/structured/structured_metrics_provider.cc
@@ -15,6 +15,7 @@
 #include "components/metrics/structured/enums.h"
 #include "components/metrics/structured/external_metrics.h"
 #include "components/metrics/structured/histogram_util.h"
+#include "components/metrics/structured/project_validator.h"
 #include "components/metrics/structured/storage.pb.h"
 #include "components/metrics/structured/structured_metrics_features.h"
 #include "components/metrics/structured/structured_metrics_validator.h"
@@ -137,8 +138,9 @@
   // in the first logged-in user's cryptohome. So if a second profile is added
   // we should ignore it. All init state beyond |InitState::kUninitialized| mean
   // a profile has already been added.
-  if (init_state_ != InitState::kUninitialized)
+  if (init_state_ != InitState::kUninitialized) {
     return;
+  }
   init_state_ = InitState::kProfileAdded;
 
   const auto save_delay = base::Milliseconds(kSaveDelayMs);
@@ -209,8 +211,9 @@
 absl::optional<int> StructuredMetricsProvider::LastKeyRotation(
     const uint64_t project_name_hash) {
   DCHECK(base::CurrentUIThread::IsSet());
-  if (init_state_ != InitState::kInitialized)
+  if (init_state_ != InitState::kInitialized) {
     return absl::nullopt;
+  }
   DCHECK(profile_key_data_->is_initialized());
   DCHECK(device_key_data_->is_initialized());
 
@@ -274,8 +277,9 @@
 void StructuredMetricsProvider::ProvideCurrentSessionData(
     ChromeUserMetricsExtension* uma_proto) {
   DCHECK(base::CurrentUIThread::IsSet());
-  if (!recording_enabled_ || init_state_ != InitState::kInitialized)
+  if (!recording_enabled_ || init_state_ != InitState::kInitialized) {
     return;
+  }
 
   if (base::FeatureList::IsEnabled(kDelayUploadUntilHwid) &&
       !system_profile_initialized_) {
@@ -404,7 +408,6 @@
     return;
   }
   const auto* project_validator = maybe_project_validator.value();
-
   const auto maybe_event_validator =
       project_validator->GetEventValidator(event.event_name());
   DCHECK(maybe_event_validator.has_value());
@@ -433,24 +436,10 @@
 
   event_proto->set_project_name_hash(project_validator->project_hash());
 
-  // Choose which KeyData to use for this event.
-  KeyData* key_data;
-  switch (project_validator->id_scope()) {
-    case IdScope::kPerProfile:
-      key_data = profile_key_data_.get();
-      break;
-    case IdScope::kPerDevice:
-      key_data = device_key_data_.get();
-      break;
-    default:
-      // In case id_scope is uninitialized.
-      NOTREACHED();
-  }
-
-  // TODO(crbug/1350322): Add client_id and user_id once they are added to the
-  // original proto.
+  // Sequence-related metadata.
   if (project_validator->event_type() ==
-      StructuredEventProto_EventType_SEQUENCE) {
+          StructuredEventProto_EventType_SEQUENCE &&
+      base::FeatureList::IsEnabled(kEventSequenceLogging)) {
     auto* event_sequence_metadata =
         event_proto->mutable_event_sequence_metadata();
 
@@ -460,6 +449,36 @@
         event.recorded_time_since_boot().InMilliseconds());
     event_sequence_metadata->set_event_unique_id(
         base::HashMetricName(event.event_sequence_metadata().event_unique_id));
+    event_proto->set_device_project_id(
+        device_key_data_.get()->Id(project_validator->project_hash(),
+                                   project_validator->key_rotation_period()));
+    event_proto->set_user_project_id(
+        profile_key_data_.get()->Id(project_validator->project_hash(),
+                                    project_validator->key_rotation_period()));
+  }
+
+  // Choose which KeyData to use for this event.
+  KeyData* key_data;
+  switch (project_validator->id_scope()) {
+    case IdScope::kPerProfile:
+      key_data = profile_key_data_.get();
+      break;
+    case IdScope::kPerDevice:
+      // For event sequence, use the profile key for now to hash strings.
+      //
+      // TODO(crbug/1399632): Event sequence is considered a structured metrics
+      // project. Once the client supports device/profile split of events like
+      // structured metrics, remove this.
+      if (project_validator->event_type() ==
+          StructuredEventProto_EventType_SEQUENCE) {
+        key_data = profile_key_data_.get();
+      } else {
+        key_data = device_key_data_.get();
+      }
+      break;
+    default:
+      // In case id_scope is uninitialized.
+      NOTREACHED();
   }
 
   // Set the ID for this event, if any.
diff --git a/components/metrics/structured/structured_metrics_provider.h b/components/metrics/structured/structured_metrics_provider.h
index 3702359..c199c28 100644
--- a/components/metrics/structured/structured_metrics_provider.h
+++ b/components/metrics/structured/structured_metrics_provider.h
@@ -14,6 +14,7 @@
 #include "components/metrics/metrics_provider.h"
 #include "components/metrics/structured/event.h"
 #include "components/metrics/structured/key_data.h"
+#include "components/metrics/structured/project_validator.h"
 #include "components/metrics/structured/recorder.h"
 
 namespace metrics {
diff --git a/components/metrics/structured/structured_metrics_provider_unittest.cc b/components/metrics/structured/structured_metrics_provider_unittest.cc
index b658518..9e959c4 100644
--- a/components/metrics/structured/structured_metrics_provider_unittest.cc
+++ b/components/metrics/structured/structured_metrics_provider_unittest.cc
@@ -40,7 +40,7 @@
 constexpr uint64_t kProjectFiveHash = UINT64_C(3960582687892677139);
 // The name hash of "TestProjectSix"
 constexpr uint64_t kProjectSixHash = UINT64_C(6972396123792667134);
-// The name hash for "CrOSEvents"
+// The name hash of "CrOSEvents"
 constexpr uint64_t kCrOSEventsProjectHash = UINT64_C(12657197978410187837);
 
 // The name hash of "chrome::TestProjectOne::TestEventOne".
@@ -1079,6 +1079,9 @@
 TEST_F(StructuredMetricsProviderTest, EventSequenceLogging) {
   Init();
 
+  scoped_feature_list_.InitAndEnableFeature(
+      metrics::structured::kEventSequenceLogging);
+
   const int test_time = 50;
   const double test_metric = 1.0;
 
@@ -1094,6 +1097,10 @@
   const auto& event = data.events(0);
   EXPECT_EQ(event.project_name_hash(), kCrOSEventsProjectHash);
 
+  // Sequence events should have both a device and user project id.
+  EXPECT_TRUE(event.has_device_project_id());
+  EXPECT_TRUE(event.has_user_project_id());
+
   // Verify that event sequence metadata has been serialized correctly.
   const auto& event_metadata = event.event_sequence_metadata();
   EXPECT_EQ(event_metadata.reset_counter(), 1);
diff --git a/components/omnibox/browser/actions/history_clusters_action.cc b/components/omnibox/browser/actions/history_clusters_action.cc
index 81f7fa8..4953280 100644
--- a/components/omnibox/browser/actions/history_clusters_action.cc
+++ b/components/omnibox/browser/actions/history_clusters_action.cc
@@ -106,10 +106,10 @@
               IDS_OMNIBOX_ACTION_HISTORY_CLUSTERS_SEARCH_SUGGESTION_CONTENTS,
               IDS_ACC_OMNIBOX_ACTION_HISTORY_CLUSTERS_SEARCH_SUFFIX,
               IDS_ACC_OMNIBOX_ACTION_HISTORY_CLUSTERS_SEARCH),
-          GetFullJourneysUrlForQuery(query)),
+          GetFullJourneysUrlForQuery(query),
+          takes_over_match),
       matched_keyword_data_(matched_keyword_data),
-      query_(query),
-      takes_over_match_(takes_over_match) {
+      query_(query) {
 #if BUILDFLAG(IS_ANDROID)
     CreateOrUpdateJavaObject(query);
 #endif
@@ -168,10 +168,6 @@
   OmniboxAction::Execute(context);
 }
 
-bool HistoryClustersAction::TakesOverMatch() const {
-  return takes_over_match_;
-}
-
 int32_t HistoryClustersAction::GetID() const {
   return static_cast<int32_t>(OmniboxActionId::HISTORY_CLUSTERS);
 }
@@ -217,11 +213,12 @@
   if (result.empty())
     return;
 
-  // If there's a pedal in `result`, don't add a history cluster action to avoid
-  // over-crowding.
+  // If there's any visible action in `result`, don't add a history cluster
+  // action to avoid over-crowding.
   if (!GetConfig().omnibox_action_with_pedals &&
-      base::ranges::any_of(result,
-                           [](const auto& match) { return match.action; })) {
+      base::ranges::any_of(result, [](const auto& match) {
+        return match.action && !match.action->TakesOverMatch();
+      })) {
     return;
   }
 
diff --git a/components/omnibox/browser/actions/history_clusters_action.h b/components/omnibox/browser/actions/history_clusters_action.h
index 854a6ede..41a3c5d 100644
--- a/components/omnibox/browser/actions/history_clusters_action.h
+++ b/components/omnibox/browser/actions/history_clusters_action.h
@@ -52,7 +52,6 @@
 
   void RecordActionShown(size_t position, bool executed) const override;
   void Execute(ExecutionContext& context) const override;
-  bool TakesOverMatch() const override;
   int32_t GetID() const override;
 #if defined(SUPPORT_PEDALS_VECTOR_ICONS)
   const gfx::VectorIcon& GetVectorIcon() const override;
@@ -73,9 +72,6 @@
   // Used to open journeys in side panel with relevant clusters
   std::string query_;
 
-  // Used to make the action chip take over the whole match.
-  bool takes_over_match_ = false;
-
 #if BUILDFLAG(IS_ANDROID)
   base::android::ScopedJavaGlobalRef<jobject> j_omnibox_action_;
 #endif
diff --git a/components/omnibox/browser/actions/omnibox_action.cc b/components/omnibox/browser/actions/omnibox_action.cc
index e944221..90b76be2 100644
--- a/components/omnibox/browser/actions/omnibox_action.cc
+++ b/components/omnibox/browser/actions/omnibox_action.cc
@@ -66,8 +66,10 @@
 
 // =============================================================================
 
-OmniboxAction::OmniboxAction(LabelStrings strings, GURL url)
-    : strings_(strings), url_(url) {}
+OmniboxAction::OmniboxAction(LabelStrings strings,
+                             GURL url,
+                             bool takes_over_match)
+    : strings_(strings), url_(url), takes_over_match_(takes_over_match) {}
 
 OmniboxAction::~OmniboxAction() = default;
 
@@ -87,7 +89,7 @@
 }
 
 bool OmniboxAction::TakesOverMatch() const {
-  return false;
+  return takes_over_match_;
 }
 
 #if defined(SUPPORT_PEDALS_VECTOR_ICONS)
diff --git a/components/omnibox/browser/actions/omnibox_action.h b/components/omnibox/browser/actions/omnibox_action.h
index 48173e4..5a6ed3c 100644
--- a/components/omnibox/browser/actions/omnibox_action.h
+++ b/components/omnibox/browser/actions/omnibox_action.h
@@ -126,7 +126,7 @@
     WindowOpenDisposition disposition_;
   };
 
-  OmniboxAction(LabelStrings strings, GURL url);
+  OmniboxAction(LabelStrings strings, GURL url, bool takes_over_match = false);
 
   // Provides read access to labels associated with this Action.
   const LabelStrings& GetLabelStrings() const;
@@ -153,7 +153,7 @@
   // If the user presses Enter or clicks on the match at all, the navigation
   // is ignored and the action is executed. Note, when this returns true, the
   // action chip should be un-rendered, because the whole match IS the action.
-  virtual bool TakesOverMatch() const;
+  bool TakesOverMatch() const;
 
 #if defined(SUPPORT_PEDALS_VECTOR_ICONS)
   // Returns the vector icon to represent this Action.
@@ -181,6 +181,9 @@
 
   // For navigation Actions, this holds the destination URL. Otherwise, empty.
   GURL url_;
+
+  // Used to make the action chip take over the whole match.
+  const bool takes_over_match_;
 };
 
 #endif  // COMPONENTS_OMNIBOX_BROWSER_ACTIONS_OMNIBOX_ACTION_H_
diff --git a/components/omnibox/browser/android/OWNERS b/components/omnibox/browser/android/OWNERS
index 9b9d6de..fe0051f 100644
--- a/components/omnibox/browser/android/OWNERS
+++ b/components/omnibox/browser/android/OWNERS
@@ -1,3 +1,2 @@
 ender@google.com
-fgorski@chromium.org
 tedchoc@chromium.org
diff --git a/components/omnibox/browser/autocomplete_controller.h b/components/omnibox/browser/autocomplete_controller.h
index cba7a31..e26fe5c 100644
--- a/components/omnibox/browser/autocomplete_controller.h
+++ b/components/omnibox/browser/autocomplete_controller.h
@@ -261,6 +261,8 @@
   FRIEND_TEST_ALL_PREFIXES(OmniboxEditModelPopupTest,
                            PopupStepSelectionWithHiddenGroupIds);
   FRIEND_TEST_ALL_PREFIXES(OmniboxEditModelPopupTest,
+                           PopupStepSelectionWithActions);
+  FRIEND_TEST_ALL_PREFIXES(OmniboxEditModelPopupTest,
                            PopupInlineAutocompleteAndTemporaryText);
   FRIEND_TEST_ALL_PREFIXES(OmniboxPopupContentsViewTest,
                            EmitSelectedChildrenChangedAccessibilityEvent);
diff --git a/components/omnibox/browser/history_cluster_provider.cc b/components/omnibox/browser/history_cluster_provider.cc
index 0826830..cdda923 100644
--- a/components/omnibox/browser/history_cluster_provider.cc
+++ b/components/omnibox/browser/history_cluster_provider.cc
@@ -38,6 +38,32 @@
   history_quick_provider_->AddListener(this);
 }
 
+// static
+void HistoryClusterProvider::CompleteHistoryClustersMatch(
+    const std::string& matching_text,
+    history::ClusterKeywordData matched_keyword_data,
+    AutocompleteMatch* match,
+    omnibox::GroupConfigMap* provider_suggestion_group_maps) {
+  DCHECK(match);
+  DCHECK(provider_suggestion_group_maps);
+
+  if (!history_clusters::GetConfig()
+           .omnibox_history_cluster_provider_free_ranking) {
+    match->suggestion_group_id = omnibox::GROUP_HISTORY_CLUSTER;
+    // Insert a corresponding omnibox::GroupConfig with default values in the
+    // suggestion groups map; otherwise the group ID will get dropped.
+    (*provider_suggestion_group_maps)[omnibox::GROUP_HISTORY_CLUSTER];
+  }
+
+  // It's fine to unconditionally attach this takeover action, as the action
+  // itself checks the flag to redirect the user to either the Side Panel or
+  // the traditional History/Journeys WebUI. As a side effect, it will also
+  // record the action-centric metrics.
+  match->action = base::MakeRefCounted<history_clusters::HistoryClustersAction>(
+      matching_text, std::move(matched_keyword_data),
+      /*takes_over_match=*/true);
+}
+
 void HistoryClusterProvider::Start(const AutocompleteInput& input,
                                    bool minimal_changes) {
   Stop(true, false);
@@ -109,13 +135,16 @@
   // lowest relevance with an exception for search-what-you-typed search
   // suggestions being ordered before others.
   for (const auto& search_match : search_provider_->matches()) {
-    if (client_->GetHistoryClustersService()->DoesQueryMatchAnyCluster(
-            base::UTF16ToUTF8(search_match.contents))) {
+    auto matched_keyword_data =
+        client_->GetHistoryClustersService()->DoesQueryMatchAnyCluster(
+            base::UTF16ToUTF8(search_match.contents));
+    if (matched_keyword_data) {
       client_->GetOmniboxTriggeredFeatureService()->FeatureTriggered(
           OmniboxTriggeredFeatureService::Feature::kHistoryClusterSuggestion);
       if (!history_clusters::GetConfig()
                .omnibox_history_cluster_provider_counterfactual) {
-        matches_.push_back(CreateMatch(search_match.contents));
+        matches_.push_back(CreateMatch(
+            search_match.contents, std::move(matched_keyword_data.value())));
       }
       return true;
     }
@@ -123,7 +152,9 @@
   return false;
 }
 
-AutocompleteMatch HistoryClusterProvider::CreateMatch(std::u16string text) {
+AutocompleteMatch HistoryClusterProvider::CreateMatch(
+    std::u16string text,
+    history::ClusterKeywordData matched_keyword_data) {
   AutocompleteMatch match;
   match.provider = this;
   match.type = AutocompleteMatch::Type::HISTORY_CLUSTER;
@@ -153,13 +184,9 @@
   match.contents_class.push_back(
       ACMatchClassification(0, ACMatchClassification::URL));
 
-  if (!history_clusters::GetConfig()
-           .omnibox_history_cluster_provider_free_ranking) {
-    match.suggestion_group_id = omnibox::GROUP_HISTORY_CLUSTER;
-    // Insert a corresponding omnibox::GroupConfig with default values in the
-    // suggestion groups map; otherwise the group ID will get dropped.
-    suggestion_groups_map_[omnibox::GROUP_HISTORY_CLUSTER];
-  }
+  CompleteHistoryClustersMatch(base::UTF16ToUTF8(text),
+                               std::move(matched_keyword_data), &match,
+                               &suggestion_groups_map_);
 
   return match;
 }
diff --git a/components/omnibox/browser/history_cluster_provider.h b/components/omnibox/browser/history_cluster_provider.h
index cf299b2..29a536e1 100644
--- a/components/omnibox/browser/history_cluster_provider.h
+++ b/components/omnibox/browser/history_cluster_provider.h
@@ -28,6 +28,16 @@
                          AutocompleteProvider* history_url_provider,
                          AutocompleteProvider* history_quick_provider);
 
+  // Updates `match->action` to have the `OmniboxAction`, and updates
+  // `provider_suggestion_groups_map` to contain the right groups.
+  // `matching_text` is necessary to pass the query down to the `OmniboxAction`
+  // so it can pre-populate the query field in the Journeys searchbox.
+  static void CompleteHistoryClustersMatch(
+      const std::string& matching_text,
+      history::ClusterKeywordData matched_keyword_data,
+      AutocompleteMatch* match,
+      omnibox::GroupConfigMap* provider_suggestion_groups_map);
+
   // AutocompleteProvider:
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
 
@@ -48,7 +58,9 @@
   bool CreateMatches();
 
   // Creates a `AutocompleteMatch`.
-  AutocompleteMatch CreateMatch(std::u16string text);
+  AutocompleteMatch CreateMatch(
+      std::u16string text,
+      history::ClusterKeywordData matched_keyword_data);
 
   // The `AutocompleteInput` passed to `Start()`.
   AutocompleteInput input_;
diff --git a/components/omnibox/browser/omnibox_edit_model_unittest.cc b/components/omnibox/browser/omnibox_edit_model_unittest.cc
index 1934b1217..83c77b12 100644
--- a/components/omnibox/browser/omnibox_edit_model_unittest.cc
+++ b/components/omnibox/browser/omnibox_edit_model_unittest.cc
@@ -16,6 +16,7 @@
 #include "build/build_config.h"
 #include "components/dom_distiller/core/url_constants.h"
 #include "components/dom_distiller/core/url_utils.h"
+#include "components/omnibox/browser/actions/omnibox_action.h"
 #include "components/omnibox/browser/autocomplete_controller.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/omnibox_popup_view.h"
@@ -832,7 +833,7 @@
     EXPECT_EQ(selection, model()->GetPopupSelection());
   }
 
-  // Try the kAllLines step behavior.
+  // Try the `kAllLines` step behavior.
   model()->StepPopupSelection(Selection::kBackward, Selection::kAllLines);
   EXPECT_EQ(Selection(0, Selection::NORMAL), model()->GetPopupSelection());
   model()->StepPopupSelection(Selection::kForward, Selection::kAllLines);
@@ -871,13 +872,13 @@
   model()->OnPopupResultChanged();
   EXPECT_EQ(0u, model()->GetPopupSelection().line);
 
-  // Test the simple kAllLines case.
+  // Test the simple `kAllLines` case.
   model()->StepPopupSelection(Selection::kForward, Selection::kAllLines);
   EXPECT_EQ(1u, model()->GetPopupSelection().line);
   model()->StepPopupSelection(Selection::kBackward, Selection::kAllLines);
   EXPECT_EQ(0u, model()->GetPopupSelection().line);
 
-  // Test the kStateOrLine case, forwards and backwards.
+  // Test the `kStateOrLine` case, forwards and backwards.
   for (auto selection : {
            Selection(1, Selection::NORMAL),
            Selection(2, Selection::FOCUSED_BUTTON_HEADER),
@@ -894,7 +895,7 @@
     EXPECT_EQ(selection, model()->GetPopupSelection());
   }
 
-  // Test the kWholeLine case, forwards and backwards.
+  // Test the `kWholeLine` case, forwards and backwards.
   for (auto selection : {
            Selection(0, Selection::NORMAL),
            Selection(1, Selection::NORMAL),
@@ -911,6 +912,77 @@
   }
 }
 
+// Actions are not part of the selection stepping in Android and iOS at all.
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+TEST_F(OmniboxEditModelPopupTest, PopupStepSelectionWithActions) {
+  ACMatches matches;
+  for (size_t i = 0; i < 4; ++i) {
+    AutocompleteMatch match(nullptr, 1000, false,
+                            AutocompleteMatchType::URL_WHAT_YOU_TYPED);
+    match.keyword = u"match";
+    match.allowed_to_be_default_match = true;
+    matches.push_back(match);
+  }
+  // The second match has a normal action.
+  matches[1].action =
+      base::MakeRefCounted<OmniboxAction>(OmniboxAction::LabelStrings(), GURL(),
+                                          /*takes_over_match=*/false);
+  // The fourth match has an action that takes over the match.
+  matches[3].action =
+      base::MakeRefCounted<OmniboxAction>(OmniboxAction::LabelStrings(), GURL(),
+                                          /*takes_over_match=*/true);
+
+  auto* result = &model()->autocomplete_controller()->result_;
+  result->AppendMatches(matches);
+
+  AutocompleteInput input(u"match", metrics::OmniboxEventProto::NTP,
+                          TestSchemeClassifier());
+  result->SortAndCull(input, nullptr);
+  model()->OnPopupResultChanged();
+  EXPECT_EQ(0u, model()->GetPopupSelection().line);
+
+  // Step by lines forward.
+  for (size_t n : {1, 2, 3, 0}) {
+    model()->StepPopupSelection(Selection::kForward, Selection::kWholeLine);
+    EXPECT_EQ(n, model()->GetPopupSelection().line);
+  }
+  // Step by lines backward.
+  for (size_t n : {3, 2, 1, 0}) {
+    model()->StepPopupSelection(Selection::kBackward, Selection::kWholeLine);
+    EXPECT_EQ(n, model()->GetPopupSelection().line);
+  }
+
+  // Step by states forward.
+  for (auto selection : {
+           Selection(1, Selection::NORMAL),
+           Selection(1, Selection::FOCUSED_BUTTON_ACTION),
+           Selection(2, Selection::NORMAL),
+           Selection(3, Selection::NORMAL),
+           Selection(0, Selection::NORMAL),
+       }) {
+    model()->StepPopupSelection(Selection::kForward, Selection::kStateOrLine);
+    EXPECT_EQ(selection, model()->GetPopupSelection());
+  }
+  // Step by states backward.
+  for (auto selection : {
+           Selection(3, Selection::NORMAL),
+           Selection(2, Selection::NORMAL),
+           Selection(1, Selection::FOCUSED_BUTTON_ACTION),
+           Selection(1, Selection::NORMAL),
+           Selection(0, Selection::NORMAL),
+       }) {
+    model()->StepPopupSelection(Selection::kBackward, Selection::kStateOrLine);
+    EXPECT_EQ(selection, model()->GetPopupSelection());
+  }
+
+  // Try the `kAllLines` step behavior.
+  model()->StepPopupSelection(Selection::kBackward, Selection::kAllLines);
+  EXPECT_EQ(Selection(0, Selection::NORMAL), model()->GetPopupSelection());
+  model()->StepPopupSelection(Selection::kForward, Selection::kAllLines);
+  EXPECT_EQ(Selection(3, Selection::NORMAL), model()->GetPopupSelection());
+}
+#endif
+
 TEST_F(OmniboxEditModelPopupTest, PopupInlineAutocompleteAndTemporaryText) {
   // Create a set of three matches "a|1" (inline autocompleted), "a2", "a3".
   // The third match has a suggestion group ID.
diff --git a/components/omnibox/browser/omnibox_popup_selection.cc b/components/omnibox/browser/omnibox_popup_selection.cc
index 35ce88a1..8710f24 100644
--- a/components/omnibox/browser/omnibox_popup_selection.cc
+++ b/components/omnibox/browser/omnibox_popup_selection.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "components/omnibox/browser/omnibox_popup_selection.h"
+#include "components/omnibox/browser/actions/omnibox_action.h"
 
 #include <algorithm>
 
@@ -92,7 +93,9 @@
           match.from_keyword) {
         return false;
       }
-      return match.action != nullptr;
+      // If the action takes over the whole match, don't have a separate Action
+      // control in the tab order (or rendered).
+      return match.action && !match.action->TakesOverMatch();
     case FOCUSED_BUTTON_REMOVE_SUGGESTION:
       return match.SupportsDeletion();
     default:
diff --git a/components/omnibox/browser/shortcuts_provider.cc b/components/omnibox/browser/shortcuts_provider.cc
index fbda814..a9887240 100644
--- a/components/omnibox/browser/shortcuts_provider.cc
+++ b/components/omnibox/browser/shortcuts_provider.cc
@@ -31,6 +31,7 @@
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
+#include "components/omnibox/browser/history_cluster_provider.h"
 #include "components/omnibox/browser/history_url_provider.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/browser/url_prefix.h"
@@ -352,16 +353,19 @@
         auto match = ShortcutToACMatch(*shortcut_match.shortcut,
                                        shortcut_match.relevance, input,
                                        fixed_up_input, term_string);
-    // Guard this as `history_clusters::GetConfig()` doesn't exist on iOS.
+    // Guard this as `HistoryClusterProvider` doesn't exist on iOS.
     // Though this code will never run on iOS regardless.
 #if !BUILDFLAG(IS_IOS)
-        if (!history_clusters::GetConfig()
-                 .omnibox_history_cluster_provider_free_ranking) {
-          match.suggestion_group_id = omnibox::GROUP_HISTORY_CLUSTER;
-          // Insert a corresponding omnibox::GroupConfig with default values in
-          // the suggestion groups map; otherwise the group ID will get dropped.
-          suggestion_groups_map_[omnibox::GROUP_HISTORY_CLUSTER];
-        }
+        // `term_string` is only what the user typed, e.g. "new y" instead of
+        // "new york". Use `match.description`, which is the whole string.
+        // This is a bit hacky, but accurately reflects how
+        // `HistoryClusterProvider` constructed the original match.
+        std::string matching_string = base::UTF16ToUTF8(match.description);
+        // Shortcut-generated HC matches have empty `ClusterKeywordData()`s,
+        // because it wasn't generated via an entity match in the first place.
+        HistoryClusterProvider::CompleteHistoryClustersMatch(
+            matching_string, history::ClusterKeywordData(), &match,
+            &suggestion_groups_map_);
 #endif  // !BUILDFLAG(IS_IOS)
 
         return match;
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 723cb1d..06d7e9c1 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -415,12 +415,6 @@
              "OmniboxMatchToolbarAndStatusBarColor",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Feature used to remove the chevron on the right side of suggestion list
-// header under omnibox.
-BASE_FEATURE(kOmniboxRemoveSuggestionHeaderChevron,
-             "OmniboxRemoveSuggestionHeaderChevron",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Feature used to add a recycled view pool on the most visited tile carousel.
 BASE_FEATURE(kOmniboxMostVisitedTilesAddRecycledViewPool,
              "OmniboxMostVisitedTilesAddRecycledViewPool",
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index 6720bbf7..9e36ea19 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -97,7 +97,6 @@
 BASE_DECLARE_FEATURE(kOmniboxFuzzyUrlSuggestions);
 BASE_DECLARE_FEATURE(kOmniboxDefaultBrowserPedal);
 BASE_DECLARE_FEATURE(kOmniboxMatchToolbarAndStatusBarColor);
-BASE_DECLARE_FEATURE(kOmniboxRemoveSuggestionHeaderChevron);
 BASE_DECLARE_FEATURE(kOmniboxMostVisitedTilesAddRecycledViewPool);
 BASE_DECLARE_FEATURE(kUniformRowHeight);
 BASE_DECLARE_FEATURE(kWebUIOmniboxPopup);
diff --git a/components/page_load_metrics/browser/metrics_web_contents_observer.cc b/components/page_load_metrics/browser/metrics_web_contents_observer.cc
index 228a48f..1f97cd35 100644
--- a/components/page_load_metrics/browser/metrics_web_contents_observer.cc
+++ b/components/page_load_metrics/browser/metrics_web_contents_observer.cc
@@ -147,6 +147,8 @@
   for (auto& observer : lifecycle_observers_)
     observer.OnGoingAway();
 
+  UnregisterInputEventObserver(web_contents()->GetPrimaryMainFrame());
+
   // We tear down PageLoadTrackers in WebContentsDestroyed, rather than in the
   // destructor, since `web_contents()` returns nullptr in the destructor, and
   // PageLoadMetricsObservers can cause code to execute that wants to be able to
@@ -159,20 +161,24 @@
 }
 
 void MetricsWebContentsObserver::RegisterInputEventObserver(
-    content::RenderViewHost* host) {
+    content::RenderFrameHost* host) {
   if (host != nullptr)
-    host->GetWidget()->AddInputEventObserver(this);
+    host->GetRenderWidgetHost()->AddInputEventObserver(this);
 }
 
 void MetricsWebContentsObserver::UnregisterInputEventObserver(
-    content::RenderViewHost* host) {
+    content::RenderFrameHost* host) {
   if (host != nullptr)
-    host->GetWidget()->RemoveInputEventObserver(this);
+    host->GetRenderWidgetHost()->RemoveInputEventObserver(this);
 }
 
-void MetricsWebContentsObserver::RenderViewHostChanged(
-    content::RenderViewHost* old_host,
-    content::RenderViewHost* new_host) {
+void MetricsWebContentsObserver::RenderFrameHostChanged(
+    content::RenderFrameHost* old_host,
+    content::RenderFrameHost* new_host) {
+  if (!new_host->IsInPrimaryMainFrame()) {
+    return;
+  }
+
   UnregisterInputEventObserver(old_host);
   RegisterInputEventObserver(new_host);
 }
@@ -257,8 +263,7 @@
   if (embedder_interface_->IsNoStatePrefetch(web_contents))
     in_foreground_ = false;
 
-  RegisterInputEventObserver(
-      web_contents->GetPrimaryMainFrame()->GetRenderViewHost());
+  RegisterInputEventObserver(web_contents->GetPrimaryMainFrame());
 }
 
 void MetricsWebContentsObserver::WillStartNavigationRequestImpl(
diff --git a/components/page_load_metrics/browser/metrics_web_contents_observer.h b/components/page_load_metrics/browser/metrics_web_contents_observer.h
index 71b6ea3..2fe18d1 100644
--- a/components/page_load_metrics/browser/metrics_web_contents_observer.h
+++ b/components/page_load_metrics/browser/metrics_web_contents_observer.h
@@ -91,8 +91,8 @@
   void OnVisibilityChanged(content::Visibility visibility) override;
   void PrimaryMainFrameRenderProcessGone(
       base::TerminationStatus status) override;
-  void RenderViewHostChanged(content::RenderViewHost* old_host,
-                             content::RenderViewHost* new_host) override;
+  void RenderFrameHostChanged(content::RenderFrameHost* old_host,
+                              content::RenderFrameHost* new_host) override;
   void FrameDeleted(int frame_tree_node_id) override;
   void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
   void MediaStartedPlaying(
@@ -257,9 +257,9 @@
                                           base::TimeTicks timestamp,
                                           bool is_certainly_browser_timestamp);
 
-  // Register / Unregister input event callback to given RenderViewHost
-  void RegisterInputEventObserver(content::RenderViewHost* host);
-  void UnregisterInputEventObserver(content::RenderViewHost* host);
+  // Register / Unregister input event callback to given RenderFrameHost
+  void RegisterInputEventObserver(content::RenderFrameHost* host);
+  void UnregisterInputEventObserver(content::RenderFrameHost* host);
 
   // Notify aborted provisional loads that a new navigation occurred. This is
   // used for more consistent attribution tracking for aborted provisional
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index f31946db..477ffcc 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -56,7 +56,7 @@
     deps += [ "//chromeos/startup:startup" ]
   }
   if (is_chromeos_ash) {
-    deps += [ "//chromeos/system" ]
+    deps += [ "//chromeos/ash/components/system" ]
   }
 }
 
@@ -328,7 +328,7 @@
     deps += [ "//components/policy/android:jni_headers" ]
   }
   if (is_chromeos_ash) {
-    deps += [ "//chromeos/system" ]
+    deps += [ "//chromeos/ash/components/system" ]
     sources += [
       "default_chrome_apps_migrator.cc",
       "default_chrome_apps_migrator.h",
@@ -615,7 +615,7 @@
     "//ui/base",
   ]
   if (is_chromeos_ash) {
-    deps += [ "//chromeos/system" ]
+    deps += [ "//chromeos/ash/components/system" ]
   }
   if (is_android) {
     deps += [ "//components/policy/android:test_jni_headers" ]
diff --git a/components/policy/core/common/DEPS b/components/policy/core/common/DEPS
index 13ee383..1d4395b 100644
--- a/components/policy/core/common/DEPS
+++ b/components/policy/core/common/DEPS
@@ -1,9 +1,9 @@
 include_rules = [
   "+absl/types/variant.h",
+  "+chromeos/ash/components/system",
   "+chromeos/crosapi",
   "+chromeos/lacros",
   "+chromeos/startup",
-  "+chromeos/system",
   "+components/account_id",
   "+components/reporting",
   "-components/policy/core/browser",
diff --git a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
index fe249b0..4e846ba 100644
--- a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
@@ -45,7 +45,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #endif
 
 using testing::_;
diff --git a/components/policy/core/common/cloud/cloud_policy_util.cc b/components/policy/core/common/cloud/cloud_policy_util.cc
index b16373fd..df20b05e 100644
--- a/components/policy/core/common/cloud/cloud_policy_util.cc
+++ b/components/policy/core/common/cloud/cloud_policy_util.cc
@@ -51,7 +51,7 @@
 #include "components/version_info/version_info.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #endif
diff --git a/components/policy/core/common/cloud/encrypted_reporting_job_configuration_unittest.cc b/components/policy/core/common/cloud/encrypted_reporting_job_configuration_unittest.cc
index 850e59c..25d6580 100644
--- a/components/policy/core/common/cloud/encrypted_reporting_job_configuration_unittest.cc
+++ b/components/policy/core/common/cloud/encrypted_reporting_job_configuration_unittest.cc
@@ -23,7 +23,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #endif
 
 using ::testing::_;
diff --git a/components/policy/core/common/cloud/realtime_reporting_job_configuration_unittest.cc b/components/policy/core/common/cloud/realtime_reporting_job_configuration_unittest.cc
index 0375fba..b5018977 100644
--- a/components/policy/core/common/cloud/realtime_reporting_job_configuration_unittest.cc
+++ b/components/policy/core/common/cloud/realtime_reporting_job_configuration_unittest.cc
@@ -25,7 +25,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #endif
 
 namespace em = enterprise_management;
diff --git a/components/policy/core/common/policy_pref_names.cc b/components/policy/core/common/policy_pref_names.cc
index 9dbc514..cba0860 100644
--- a/components/policy/core/common/policy_pref_names.cc
+++ b/components/policy/core/common/policy_pref_names.cc
@@ -126,5 +126,10 @@
 const char kSendMouseEventsDisabledFormControlsEnabled[] =
     "policy.send_mouse_events_disabled_form_controls_enabled";
 
+// If true the feature UseMojoVideoDecoderForPepper will be allowed, otherwise
+// feature will be forced off.
+const char kUseMojoVideoDecoderForPepperAllowed[] =
+    "policy.use_mojo_video_decoder_for_pepper_allowed";
+
 }  // namespace policy_prefs
 }  // namespace policy
diff --git a/components/policy/core/common/policy_pref_names.h b/components/policy/core/common/policy_pref_names.h
index 2037d75..4bfdc62 100644
--- a/components/policy/core/common/policy_pref_names.h
+++ b/components/policy/core/common/policy_pref_names.h
@@ -47,6 +47,7 @@
 POLICY_EXPORT extern const char kEventPathEnabled[];
 POLICY_EXPORT extern const char kOffsetParentNewSpecBehaviorEnabled[];
 POLICY_EXPORT extern const char kSendMouseEventsDisabledFormControlsEnabled[];
+POLICY_EXPORT extern const char kUseMojoVideoDecoderForPepperAllowed[];
 }  // namespace policy_prefs
 }  // namespace policy
 
diff --git a/components/policy/resources/templates/policies.yaml b/components/policy/resources/templates/policies.yaml
index ca758916..ae8ba214 100644
--- a/components/policy/resources/templates/policies.yaml
+++ b/components/policy/resources/templates/policies.yaml
@@ -1044,6 +1044,7 @@
   1043: CloudAPAuthEnabled
   1044: UsbDetectorNotificationEnabled
   1045: LacrosSelection
+  1046: UseMojoVideoDecoderForPepperAllowed
 atomic_groups:
   1: Homepage
   2: RemoteAccess
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/UseMojoVideoDecoderForPepperAllowed.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/UseMojoVideoDecoderForPepperAllowed.yaml
new file mode 100644
index 0000000..b913d7d
--- /dev/null
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/UseMojoVideoDecoderForPepperAllowed.yaml
@@ -0,0 +1,37 @@
+owners:
+- pmolinalopez@chromium.org
+- andrescj@chromium.org
+- blundell@chromium.org
+caption: Allow <ph name="PEPPER_NAME">Pepper</ph> to use a new decoder
+  for hardware accelerated video decoding.
+desc: |-
+  This policy controls whether <ph name="PEPPER_NAME">Pepper</ph> plugins can
+  use the new decoder to talk to hardware decoders instead of the legacy video
+  decoder.
+
+  The migration only affects internal implementation details and should not
+  change any behavior. However, this policy can be used in case any PPAPI
+  applications do not work as expected.
+
+  When the policy is left unset or set to Enabled the browser will decide which
+  implementation is used.
+  When the policy is set to Disabled, the browser will use the old implementation
+  until this policy expires.
+supported_on:
+- chrome.*:110-114
+- chrome_os:110-114
+device_only: false
+features:
+  dynamic_refresh: false
+  per_profile: false
+type: main
+schema:
+  type: boolean
+items:
+- caption: Allow <ph name="PEPPER_NAME">Pepper</ph> to use the new video decoder.
+  value: true
+- caption: Force <ph name="PEPPER_NAME">Pepper</ph> to use the legacy video decoder.
+  value: false
+default: true
+example_value: false
+tags: []
diff --git a/components/power_bookmarks/core/power_bookmark_service_unittest.cc b/components/power_bookmarks/core/power_bookmark_service_unittest.cc
index 984cae33..c223cff6 100644
--- a/components/power_bookmarks/core/power_bookmark_service_unittest.cc
+++ b/components/power_bookmarks/core/power_bookmark_service_unittest.cc
@@ -40,6 +40,7 @@
     std::unique_ptr<sync_pb::PowerEntity> power_entity) {
   std::unique_ptr<Power> power =
       std::make_unique<Power>(std::move(power_entity));
+  power->set_guid(base::GUID::GenerateRandomV4());
   power->set_url(url);
   power->set_power_type(power_type);
   return power;
@@ -296,7 +297,6 @@
   EXPECT_CALL(cb, Run(IsTrue()));
   auto power1 = MakePower(GURL("https://google.com"),
                           sync_pb::PowerBookmarkSpecifics::POWER_TYPE_MOCK);
-  power1->set_guid(base::GUID::GenerateRandomV4());
   auto power2 = power1->Clone();
   service()->CreatePower(std::move(power1), cb.Get());
   RunUntilIdle();
@@ -351,7 +351,6 @@
   EXPECT_CALL(cb, Run(IsTrue()));
   auto power1 = MakePower(GURL("https://google.com"),
                           sync_pb::PowerBookmarkSpecifics::POWER_TYPE_MOCK);
-  power1->set_guid(base::GUID::GenerateRandomV4());
   auto power2 = power1->Clone();
   service()->CreatePower(std::move(power1), cb.Get());
   RunUntilIdle();
diff --git a/components/power_bookmarks/core/powers/power.cc b/components/power_bookmarks/core/powers/power.cc
index b525443..b6f4778 100644
--- a/components/power_bookmarks/core/powers/power.cc
+++ b/components/power_bookmarks/core/powers/power.cc
@@ -50,11 +50,12 @@
   DCHECK(guid_ == other.guid_);
   DCHECK(url_ == other.url_);
   DCHECK(power_type_ == other.power_type_);
-  // Take the latest time modified.
-  if (time_modified_ < other.time_modified_)
+  // Take the power entity of the one with a more recent modified time.
+  if (time_modified_ < other.time_modified_) {
     time_modified_ = other.time_modified_;
-  // TODO(1382835): Powers should be able to customize the merge logic.
-  power_entity_->CopyFrom(*other.power_entity_);
+    // TODO(1382835): Powers should be able to customize the merge logic.
+    power_entity_->CopyFrom(*other.power_entity_);
+  }
 }
 
 std::unique_ptr<Power> Power::Clone() const {
diff --git a/components/power_bookmarks/core/powers/power.h b/components/power_bookmarks/core/powers/power.h
index 4698f18..30d396c 100644
--- a/components/power_bookmarks/core/powers/power.h
+++ b/components/power_bookmarks/core/powers/power.h
@@ -53,6 +53,9 @@
     return power_entity_.get();
   }
 
+  // Used to set fields to PowerEntity.
+  sync_pb::PowerEntity* power_entity() { return power_entity_.get(); }
+
   // Write the properties held in this class to power_bookmark_specifics.proto.
   // `power_bookmark_specifics` will never be nullptr.
   void ToPowerBookmarkSpecifics(
diff --git a/components/power_bookmarks/storage/power_bookmark_database_impl.cc b/components/power_bookmarks/storage/power_bookmark_database_impl.cc
index 4ad983cb..896f140d 100644
--- a/components/power_bookmarks/storage/power_bookmark_database_impl.cc
+++ b/components/power_bookmarks/storage/power_bookmark_database_impl.cc
@@ -419,11 +419,13 @@
 bool PowerBookmarkDatabaseImpl::UpdatePower(std::unique_ptr<Power> power) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!CheckIfPowerWithIdExists(&db_, power->guid())) {
+  auto existing_power = GetPowerForGUID(power->guid().AsLowercaseString());
+  if (!existing_power) {
     DLOG(ERROR)
         << "Failed to update power because the current power does not exist.";
     return false;
   }
+  existing_power->Merge(*power);
 
   sql::Transaction transaction(&db_);
   if (!transaction.Begin())
@@ -441,11 +443,12 @@
   sql::Statement save_statement(
       db_.GetCachedStatement(SQL_FROM_HERE, kUpdatePowerSaveSql));
 
-  save_statement.BindString(0, power->url().spec());
-  save_statement.BindString(1, url::Origin::Create(power->url()).Serialize());
-  save_statement.BindInt(2, power->power_type());
-  save_statement.BindTime(3, power->time_added());
-  save_statement.BindTime(4, power->time_modified());
+  save_statement.BindString(0, existing_power->url().spec());
+  save_statement.BindString(
+      1, url::Origin::Create(existing_power->url()).Serialize());
+  save_statement.BindInt(2, existing_power->power_type());
+  save_statement.BindTime(3, existing_power->time_added());
+  save_statement.BindTime(4, existing_power->time_modified());
   if (!save_statement.Run())
     return false;
 
@@ -460,11 +463,11 @@
 
   std::string data;
   sync_pb::PowerBookmarkSpecifics specifics;
-  power->ToPowerBookmarkSpecifics(&specifics);
+  existing_power->ToPowerBookmarkSpecifics(&specifics);
   bool success = specifics.SerializeToString(&data);
   DCHECK(success);
   blob_statement.BindBlob(0, data);
-  blob_statement.BindString(1, power->guid().AsLowercaseString());
+  blob_statement.BindString(1, existing_power->guid().AsLowercaseString());
   if (!blob_statement.Run())
     return false;
 
@@ -624,6 +627,7 @@
     const std::string& guid) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  DCHECK(base::IsValidGUID(guid));
   static constexpr char kGetPowerForGUIDSql[] =
       // clang-format off
       "SELECT blobs.id, blobs.specifics, saves.url "
diff --git a/components/power_bookmarks/storage/power_bookmark_database_impl_unittest.cc b/components/power_bookmarks/storage/power_bookmark_database_impl_unittest.cc
index 836feee..9ef5c208 100644
--- a/components/power_bookmarks/storage/power_bookmark_database_impl_unittest.cc
+++ b/components/power_bookmarks/storage/power_bookmark_database_impl_unittest.cc
@@ -278,6 +278,74 @@
   EXPECT_EQ(0u, stored_powers.size());
 }
 
+TEST_F(PowerBookmarkDatabaseImplTest, UpdatePowerShouldMergePower) {
+  std::unique_ptr<PowerBookmarkDatabaseImpl> pbdb =
+      std::make_unique<PowerBookmarkDatabaseImpl>(db_dir());
+  EXPECT_TRUE(pbdb->Init());
+
+  auto power = MakePower(GURL("https://google.com"),
+                         sync_pb::PowerBookmarkSpecifics::POWER_TYPE_MOCK);
+  base::Time now = base::Time::Now();
+  power->set_time_modified(now);
+  auto power2 = power->Clone();
+  power2->set_time_modified(now - base::Seconds(1));
+
+  EXPECT_TRUE(pbdb->CreatePower(std::move(power)));
+  EXPECT_TRUE(pbdb->UpdatePower(std::move(power2)));
+
+  std::vector<std::unique_ptr<Power>> stored_powers =
+      pbdb->GetPowersForURL(GURL("https://google.com"),
+                            sync_pb::PowerBookmarkSpecifics::POWER_TYPE_MOCK);
+  EXPECT_EQ(1u, stored_powers.size());
+
+  // Make sure time modified does not change after a merge.
+  EXPECT_EQ(now, stored_powers[0]->time_modified());
+}
+
+TEST_F(PowerBookmarkDatabaseImplTest, UpdateNotesWithMerge) {
+  std::unique_ptr<PowerBookmarkDatabaseImpl> pbdb =
+      std::make_unique<PowerBookmarkDatabaseImpl>(db_dir());
+  EXPECT_TRUE(pbdb->Init());
+
+  auto power = MakePower(GURL("https://google.com"),
+                         sync_pb::PowerBookmarkSpecifics::POWER_TYPE_NOTE);
+  power->power_entity()->mutable_note_entity()->set_plain_text("now");
+
+  base::Time now = base::Time::Now();
+  power->set_time_modified(now);
+  auto power_updated_before = power->Clone();
+  power_updated_before->set_time_modified(now - base::Seconds(1));
+  power_updated_before->power_entity()->mutable_note_entity()->set_plain_text(
+      "before");
+
+  auto power_updated_after = power->Clone();
+  auto after_time_modified = now + base::Seconds(1);
+  power_updated_after->set_time_modified(after_time_modified);
+  power_updated_after->power_entity()->mutable_note_entity()->set_plain_text(
+      "after");
+
+  // Merge a note with an older one. Text will not change.
+  EXPECT_TRUE(pbdb->CreatePower(std::move(power)));
+  EXPECT_TRUE(pbdb->UpdatePower(std::move(power_updated_before)));
+  std::vector<std::unique_ptr<Power>> stored_powers =
+      pbdb->GetPowersForURL(GURL("https://google.com"),
+                            sync_pb::PowerBookmarkSpecifics::POWER_TYPE_NOTE);
+  EXPECT_EQ(1u, stored_powers.size());
+  EXPECT_EQ(now, stored_powers[0]->time_modified());
+  EXPECT_EQ("now",
+            stored_powers[0]->power_entity()->note_entity().plain_text());
+
+  // Merge a note with a newer one. Text will change.
+  EXPECT_TRUE(pbdb->UpdatePower(std::move(power_updated_after)));
+  stored_powers =
+      pbdb->GetPowersForURL(GURL("https://google.com"),
+                            sync_pb::PowerBookmarkSpecifics::POWER_TYPE_NOTE);
+  EXPECT_EQ(1u, stored_powers.size());
+  EXPECT_EQ(after_time_modified, stored_powers[0]->time_modified());
+  EXPECT_EQ("after",
+            stored_powers[0]->power_entity()->note_entity().plain_text());
+}
+
 TEST_F(PowerBookmarkDatabaseImplTest, CreatePowerIfNotExist) {
   std::unique_ptr<PowerBookmarkDatabaseImpl> pbdb =
       std::make_unique<PowerBookmarkDatabaseImpl>(db_dir());
diff --git a/components/rlz/BUILD.gn b/components/rlz/BUILD.gn
index e9850ce..d2f4861 100644
--- a/components/rlz/BUILD.gn
+++ b/components/rlz/BUILD.gn
@@ -55,6 +55,6 @@
   ]
 
   if (is_chromeos_ash) {
-    deps += [ "//chromeos/system" ]
+    deps += [ "//chromeos/ash/components/system" ]
   }
 }
diff --git a/components/rlz/DEPS b/components/rlz/DEPS
index 6b1dc7a..6f99f9f 100644
--- a/components/rlz/DEPS
+++ b/components/rlz/DEPS
@@ -1,5 +1,5 @@
 include_rules = [
-  "+chromeos/system",
+  "+chromeos/ash/components/system",
   "+components/google",
   "+mojo/public/cpp/bindings",
   "+net",
diff --git a/components/rlz/rlz_tracker_unittest.cc b/components/rlz/rlz_tracker_unittest.cc
index 72d7659..c174aa3 100644
--- a/components/rlz/rlz_tracker_unittest.cc
+++ b/components/rlz/rlz_tracker_unittest.cc
@@ -26,7 +26,7 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #endif
 
 using testing::AssertionResult;
diff --git a/components/segmentation_platform/components_unittests.filter b/components/segmentation_platform/components_unittests.filter
index 3e38a6d..2a398d48 100644
--- a/components/segmentation_platform/components_unittests.filter
+++ b/components/segmentation_platform/components_unittests.filter
@@ -25,6 +25,8 @@
 PriceTrackingActionModelTest.*
 PriceTrackingInputDelegateTest.*
 QueryTilesModelTest.*
+RequestDispatcherTest.*
+RequestHandlerTest.*
 ResumeHeavyUserModelTest.*
 SearchUserModelTest.*
 SegmentInfoCacheTest.*
diff --git a/components/segmentation_platform/internal/BUILD.gn b/components/segmentation_platform/internal/BUILD.gn
index f247346..3a5d5c1 100644
--- a/components/segmentation_platform/internal/BUILD.gn
+++ b/components/segmentation_platform/internal/BUILD.gn
@@ -113,6 +113,10 @@
     "segmentation_ukm_helper.h",
     "selection/experimental_group_recorder.cc",
     "selection/experimental_group_recorder.h",
+    "selection/request_dispatcher.cc",
+    "selection/request_dispatcher.h",
+    "selection/request_handler.cc",
+    "selection/request_handler.h",
     "selection/segment_result_provider.cc",
     "selection/segment_result_provider.h",
     "selection/segment_score_provider.cc",
@@ -255,6 +259,8 @@
     "segmentation_platform_service_test_base.cc",
     "segmentation_platform_service_test_base.h",
     "segmentation_ukm_helper_unittest.cc",
+    "selection/request_dispatcher_unittest.cc",
+    "selection/request_handler_unittest.cc",
     "selection/segment_result_provider_unittest.cc",
     "selection/segment_score_provider_unittest.cc",
     "selection/segment_selector_unittest.cc",
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector.cc b/components/segmentation_platform/internal/data_collection/training_data_collector.cc
index a744ba6..526367d 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector.cc
@@ -15,18 +15,17 @@
 
 // static
 std::unique_ptr<TrainingDataCollector> TrainingDataCollector::Create(
-    SegmentInfoDatabase* segment_info_database,
     processing::FeatureListQueryProcessor* processor,
     HistogramSignalHandler* histogram_signal_handler,
-    SignalStorageConfig* signal_storage_config,
+    StorageService* storage_service,
     std::vector<std::unique_ptr<Config>>* configs,
     PrefService* profile_prefs,
     base::Clock* clock) {
   if (base::FeatureList::IsEnabled(
           features::kSegmentationStructuredMetricsFeature)) {
     return std::make_unique<TrainingDataCollectorImpl>(
-        segment_info_database, processor, histogram_signal_handler,
-        signal_storage_config, configs, profile_prefs, clock);
+        processor, histogram_signal_handler, storage_service, configs,
+        profile_prefs, clock);
   }
 
   return std::make_unique<DummyTrainingDataCollector>();
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector.h b/components/segmentation_platform/internal/data_collection/training_data_collector.h
index 99b3db8..e14c48f 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector.h
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector.h
@@ -8,6 +8,8 @@
 #include <memory>
 
 #include "components/segmentation_platform/internal/data_collection/training_data_cache.h"
+#include "components/segmentation_platform/internal/database/storage_service.h"
+#include "components/segmentation_platform/internal/execution/default_model_manager.h"
 #include "components/segmentation_platform/internal/signals/histogram_signal_handler.h"
 #include "components/segmentation_platform/public/input_context.h"
 #include "components/segmentation_platform/public/proto/model_metadata.pb.h"
@@ -28,8 +30,6 @@
 
 struct Config;
 class HistogramSignalHandler;
-class SegmentInfoDatabase;
-class SignalStorageConfig;
 
 // Collect training data and report as Ukm message. Live on main thread.
 // TODO(ssid): Make a new class that owns the training data collector and
@@ -37,10 +37,9 @@
 class TrainingDataCollector {
  public:
   static std::unique_ptr<TrainingDataCollector> Create(
-      SegmentInfoDatabase* segment_info_database,
       processing::FeatureListQueryProcessor* processor,
       HistogramSignalHandler* histogram_signal_handler,
-      SignalStorageConfig* signal_storage_config,
+      StorageService* storage_service,
       std::vector<std::unique_ptr<Config>>* configs,
       PrefService* profile_prefs,
       base::Clock* clock);
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
index 84afe11..8a365411 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
@@ -68,24 +68,41 @@
   return std::string();
 }
 
+std::map<SegmentId, absl::optional<proto::SegmentInfo>> GetPreferedSegmentInfo(
+    DefaultModelManager::SegmentInfoList&& segment_list) {
+  std::map<SegmentId, absl::optional<proto::SegmentInfo>> result;
+  for (const auto& segment_wrapper : segment_list) {
+    absl::optional<proto::SegmentInfo>& segment_info_optional =
+        result[segment_wrapper->segment_info.segment_id()];
+    if (segment_wrapper->segment_source ==
+        DefaultModelManager::SegmentSource::DATABASE) {
+      segment_info_optional = std::move(segment_wrapper->segment_info);
+    } else if (!segment_info_optional.has_value()) {
+      segment_info_optional = std::move(segment_wrapper->segment_info);
+    }
+  }
+
+  return result;
+}
+
 }  // namespace
 
 TrainingDataCollectorImpl::TrainingDataCollectorImpl(
-    SegmentInfoDatabase* segment_info_database,
     processing::FeatureListQueryProcessor* processor,
     HistogramSignalHandler* histogram_signal_handler,
-    SignalStorageConfig* signal_storage_config,
+    StorageService* storage_service,
     std::vector<std::unique_ptr<Config>>* configs,
     PrefService* profile_prefs,
     base::Clock* clock)
-    : segment_info_database_(segment_info_database),
+    : segment_info_database_(storage_service->segment_info_database()),
       feature_list_query_processor_(processor),
       histogram_signal_handler_(histogram_signal_handler),
-      signal_storage_config_(signal_storage_config),
+      signal_storage_config_(storage_service->signal_storage_config()),
       configs_(configs),
       clock_(clock),
       result_prefs_(std::make_unique<SegmentationResultPrefs>(profile_prefs)),
-      training_cache_(std::make_unique<TrainingDataCache>()) {}
+      training_cache_(std::make_unique<TrainingDataCache>()),
+      default_model_manager_(storage_service->default_model_manager()) {}
 
 TrainingDataCollectorImpl::~TrainingDataCollectorImpl() {
   histogram_signal_handler_->RemoveObserver(this);
@@ -101,19 +118,20 @@
   if (segment_ids.empty()) {
     return;
   }
-  segment_info_database_->GetSegmentInfoForSegments(
-      segment_ids,
+  default_model_manager_->GetAllSegmentInfoFromBothModels(
+      segment_ids, segment_info_database_,
       base::BindOnce(&TrainingDataCollectorImpl::OnGetSegmentsInfoList,
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
 void TrainingDataCollectorImpl::OnGetSegmentsInfoList(
-    std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> segments) {
+    DefaultModelManager::SegmentInfoList segments) {
   histogram_signal_handler_->AddObserver(this);
+  std::map<SegmentId, absl::optional<proto::SegmentInfo>> segment_list =
+      GetPreferedSegmentInfo(std::move(segments));
 
-  DCHECK(segments);
-  for (const auto& segment : *segments) {
-    const proto::SegmentInfo& segment_info = segment.second;
+  for (const auto& segment : segment_list) {
+    const proto::SegmentInfo& segment_info = segment.second.value();
 
     // Skip the segment if it is not in allowed list.
     if (!SegmentationUkmHelper::GetInstance()->CanUploadTensors(segment_info)) {
@@ -388,21 +406,29 @@
   const TrainingDataCache::RequestId request_id =
       training_cache_->GenerateNextId();
 
-  segment_info_database_->GetSegmentInfo(
-      id,
+  default_model_manager_->GetAllSegmentInfoFromBothModels(
+      {id}, segment_info_database_,
       base::BindOnce(&TrainingDataCollectorImpl::OnGetSegmentInfoAtDecisionTime,
-                     weak_ptr_factory_.GetWeakPtr(), request_id, type));
+                     weak_ptr_factory_.GetWeakPtr(), id, request_id, type,
+                     input_context));
 }
 
 void TrainingDataCollectorImpl::OnGetSegmentInfoAtDecisionTime(
-    TrainingDataCache::RequestId id,
+    proto::SegmentId segment_id,
+    TrainingDataCache::RequestId request_id,
     DecisionType type,
-    absl::optional<proto::SegmentInfo> segment) {
-  // If no segment info has been found.
-  if (!segment.has_value())
-    return;
+    scoped_refptr<InputContext> input_context,
+    DefaultModelManager::SegmentInfoList segment_list) {
+  absl::optional<proto::SegmentInfo> segment_info_optional =
+      std::move(GetPreferedSegmentInfo(std::move(segment_list))[segment_id]);
 
-  const proto::SegmentInfo& segment_info = segment.value();
+  // If no segment info list has been found.
+  if (!segment_info_optional) {
+    return;
+  }
+
+  const proto::SegmentInfo& segment_info = segment_info_optional.value();
+
   if (!CanReportTrainingData(segment_info, /*include_outputs*/ false))
     return;
 
@@ -411,18 +437,17 @@
       segment_info.model_metadata().training_outputs().trigger_config();
   if (training_config.decision_type() == type) {
     RecordTrainingDataCollectionEvent(
-        segment_info.segment_id(),
+        segment_id,
         stats::TrainingDataCollectionEvent::kImmediateCollectionStart);
 
     // Generate training data inputs.
     feature_list_query_processor_->ProcessFeatureList(
-        segment_info.model_metadata(), /*input_context*/ nullptr,
-        segment_info.segment_id(), clock_->Now(),
+        segment_info.model_metadata(), input_context, segment_id, clock_->Now(),
         /*process_option=*/
         FeatureListQueryProcessor::ProcessOption::kInputsOnly,
         base::BindOnce(
             &TrainingDataCollectorImpl::OnGetTrainingTensorsAtDecisionTime,
-            weak_ptr_factory_.GetWeakPtr(), id, segment_info));
+            weak_ptr_factory_.GetWeakPtr(), request_id, segment_info));
   }
 }
 
@@ -493,6 +518,8 @@
   // TODO(haileywang): Add state in cache for each request; never seen,
   // fulfilled, unfullfilled. (Or make triggers cancellable callbacks)
   // TODO(haileywang): Add usage of |ImmediaCollectionParam|.
+  // TODO(haileywang): Add output processing failure uma histogram (maybe
+  // success histogram too).
   OnGetTrainingTensors(absl::nullopt, segment_info, has_error,
                        cached_input_tensors, output_tensors);
 }
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
index 2a0321d6..f83c715b 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
@@ -32,10 +32,9 @@
 class TrainingDataCollectorImpl : public TrainingDataCollector,
                                   public HistogramSignalHandler::Observer {
  public:
-  TrainingDataCollectorImpl(SegmentInfoDatabase* segment_info_database,
-                            processing::FeatureListQueryProcessor* processor,
+  TrainingDataCollectorImpl(processing::FeatureListQueryProcessor* processor,
                             HistogramSignalHandler* histogram_signal_handler,
-                            SignalStorageConfig* signal_storage_config,
+                            StorageService* storage_service,
                             std::vector<std::unique_ptr<Config>>* configs,
                             PrefService* profile_prefs,
                             base::Clock* clock);
@@ -63,8 +62,7 @@
     float output_value;           // Value of the output.
   };
 
-  void OnGetSegmentsInfoList(
-      std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> segments);
+  void OnGetSegmentsInfoList(DefaultModelManager::SegmentInfoList segment_list);
 
   void ReportForSegmentsInfoList(
       const absl::optional<ImmediaCollectionParam>& param,
@@ -74,9 +72,11 @@
       absl::optional<proto::SegmentInfo> segment);
 
   void OnGetSegmentInfoAtDecisionTime(
-      TrainingDataCache::RequestId id,
+      proto::SegmentId segment_id,
+      TrainingDataCache::RequestId request_id,
       DecisionType type,
-      absl::optional<proto::SegmentInfo> segment);
+      scoped_refptr<InputContext> input_context,
+      DefaultModelManager::SegmentInfoList segment_list);
 
   void OnGetTrainingTensorsAtDecisionTime(
       TrainingDataCache::RequestId request_id,
@@ -105,12 +105,13 @@
   bool CanReportTrainingData(const proto::SegmentInfo& segment_info,
                              bool include_output);
 
-  raw_ptr<SegmentInfoDatabase> segment_info_database_;
-  raw_ptr<processing::FeatureListQueryProcessor> feature_list_query_processor_;
-  raw_ptr<HistogramSignalHandler> histogram_signal_handler_;
-  raw_ptr<SignalStorageConfig> signal_storage_config_;
-  raw_ptr<std::vector<std::unique_ptr<Config>>> configs_;
-  raw_ptr<base::Clock> clock_;
+  const raw_ptr<SegmentInfoDatabase> segment_info_database_;
+  const raw_ptr<processing::FeatureListQueryProcessor>
+      feature_list_query_processor_;
+  const raw_ptr<HistogramSignalHandler> histogram_signal_handler_;
+  const raw_ptr<SignalStorageConfig> signal_storage_config_;
+  const raw_ptr<std::vector<std::unique_ptr<Config>>> configs_;
+  const raw_ptr<base::Clock> clock_;
 
   // Helper class to read/write results to the prefs.
   std::unique_ptr<SegmentationResultPrefs> result_prefs_;
@@ -118,6 +119,9 @@
   // Cache class to temporarily store training data in the observation period.
   std::unique_ptr<TrainingDataCache> training_cache_;
 
+  // Class to get segment info from default models.
+  const raw_ptr<DefaultModelManager> default_model_manager_;
+
   // Hash of histograms for immediate training data collection. When any
   // histogram hash contained in the map is recorded, a UKM message is reported
   // right away.
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc b/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
index 38b5879..8731a4e 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
@@ -18,6 +18,7 @@
 #include "components/segmentation_platform/internal/database/test_segment_info_database.h"
 #include "components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h"
 #include "components/segmentation_platform/internal/metadata/metadata_utils.h"
+#include "components/segmentation_platform/internal/mock_ukm_data_manager.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 #include "components/segmentation_platform/internal/segmentation_ukm_helper.h"
 #include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
@@ -81,10 +82,17 @@
     ON_CALL(feature_list_processor_, ProcessFeatureList(_, _, _, _, _, _))
         .WillByDefault(
             RunOnceCallback<5>(false, inputs, ModelProvider::Response()));
-    ON_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
-        .WillByDefault(Return(true));
 
-    test_segment_info_db_ = std::make_unique<test::TestSegmentInfoDatabase>();
+    auto test_segment_info_db =
+        std::make_unique<test::TestSegmentInfoDatabase>();
+    test_segment_info_db_ = test_segment_info_db.get();
+
+    auto signal_storage_config =
+        std::make_unique<NiceMock<MockSignalStorageConfig>>();
+    signal_storage_config_ = signal_storage_config.get();
+
+    ON_CALL(*signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
+        .WillByDefault(Return(true));
 
     configs_.emplace_back(std::make_unique<Config>());
     configs_[0]->segmentation_key = kSegmentationKey;
@@ -101,21 +109,28 @@
     selected_segment.selection_time = base::Time::Now() - base::Days(1);
     result_prefs.SaveSegmentationResultToPref(kSegmentationKey,
                                               selected_segment);
+
+    storage_service_ = std::make_unique<StorageService>(
+        std::move(test_segment_info_db), nullptr,
+        std::move(signal_storage_config),
+        std::make_unique<DefaultModelManager>(nullptr,
+                                              base::flat_set<SegmentId>()),
+        &ukm_data_manager_);
+
     collector_ = std::make_unique<TrainingDataCollectorImpl>(
-        test_segment_info_db_.get(), &feature_list_processor_,
-        &histogram_signal_handler_, &signal_storage_config_, &configs_, &prefs_,
-        &clock_);
+        &feature_list_processor_, &histogram_signal_handler_,
+        storage_service_.get(), &configs_, &prefs_, &clock_);
   }
 
  protected:
   TrainingDataCollectorImpl* collector() { return collector_.get(); }
   test::TestSegmentInfoDatabase* test_segment_db() {
-    return test_segment_info_db_.get();
+    return test_segment_info_db_;
   }
   base::test::TaskEnvironment* task_environment() { return &task_environment_; }
   base::SimpleTestClock* clock() { return &clock_; }
   MockSignalStorageConfig* signal_storage_config() {
-    return &signal_storage_config_;
+    return signal_storage_config_;
   }
   processing::MockFeatureListQueryProcessor* feature_list_processor() {
     return &feature_list_processor_;
@@ -236,11 +251,13 @@
   ukm::TestAutoSetUkmRecorder test_recorder_;
   NiceMock<processing::MockFeatureListQueryProcessor> feature_list_processor_;
   NiceMock<MockHistogramSignalHandler> histogram_signal_handler_;
-  NiceMock<MockSignalStorageConfig> signal_storage_config_;
-  std::unique_ptr<test::TestSegmentInfoDatabase> test_segment_info_db_;
+  raw_ptr<NiceMock<MockSignalStorageConfig>> signal_storage_config_;
+  test::TestSegmentInfoDatabase* test_segment_info_db_;
   std::unique_ptr<TrainingDataCollectorImpl> collector_;
   TestingPrefServiceSimple prefs_;
   std::vector<std::unique_ptr<Config>> configs_;
+  NiceMock<MockUkmDataManager> ukm_data_manager_;
+  std::unique_ptr<StorageService> storage_service_;
 };
 
 // No segment info in database. Do nothing.
diff --git a/components/segmentation_platform/internal/metadata/metadata_writer.cc b/components/segmentation_platform/internal/metadata/metadata_writer.cc
index 3c9c9bce..613007e28 100644
--- a/components/segmentation_platform/internal/metadata/metadata_writer.cc
+++ b/components/segmentation_platform/internal/metadata/metadata_writer.cc
@@ -176,4 +176,12 @@
   }
 }
 
+void MetadataWriter::AddDelayTrigger(uint64_t delay_sec) {
+  auto* config =
+      metadata_->mutable_training_outputs()->mutable_trigger_config();
+  auto* trigger = config->add_observation_trigger();
+  trigger->set_delay_sec(delay_sec);
+  config->set_decision_type(proto::TrainingOutputs::TriggerConfig::ONDEMAND);
+}
+
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/metadata/metadata_writer.h b/components/segmentation_platform/internal/metadata/metadata_writer.h
index 7f5418c..dc175bf 100644
--- a/components/segmentation_platform/internal/metadata/metadata_writer.h
+++ b/components/segmentation_platform/internal/metadata/metadata_writer.h
@@ -157,6 +157,9 @@
       const std::vector<std::pair<float, std::string>>& bins,
       std::string underflow_label);
 
+  // Append a delay trigger for training data collection.
+  void AddDelayTrigger(uint64_t delay_sec);
+
  private:
   const raw_ptr<proto::SegmentationModelMetadata> metadata_;
 };
diff --git a/components/segmentation_platform/internal/scheduler/execution_service.cc b/components/segmentation_platform/internal/scheduler/execution_service.cc
index 85cb729..a7d735b 100644
--- a/components/segmentation_platform/internal/scheduler/execution_service.cc
+++ b/components/segmentation_platform/internal/scheduler/execution_service.cc
@@ -5,7 +5,6 @@
 #include "components/segmentation_platform/internal/scheduler/execution_service.h"
 
 #include "components/prefs/pref_service.h"
-#include "components/segmentation_platform/internal/data_collection/training_data_collector.h"
 #include "components/segmentation_platform/internal/database/storage_service.h"
 #include "components/segmentation_platform/internal/execution/default_model_manager.h"
 #include "components/segmentation_platform/internal/execution/execution_request.h"
@@ -56,10 +55,9 @@
           std::make_unique<processing::FeatureAggregatorImpl>());
 
   training_data_collector_ = TrainingDataCollector::Create(
-      storage_service->segment_info_database(),
       feature_list_query_processor_.get(),
-      signal_handler->deprecated_histogram_signal_handler(),
-      storage_service->signal_storage_config(), configs, profile_prefs, clock);
+      signal_handler->deprecated_histogram_signal_handler(), storage_service,
+      configs, profile_prefs, clock);
 
   model_executor_ = std::make_unique<ModelExecutorImpl>(
       clock, feature_list_query_processor_.get());
diff --git a/components/segmentation_platform/internal/scheduler/execution_service.h b/components/segmentation_platform/internal/scheduler/execution_service.h
index 4dda5831..480c153d 100644
--- a/components/segmentation_platform/internal/scheduler/execution_service.h
+++ b/components/segmentation_platform/internal/scheduler/execution_service.h
@@ -11,6 +11,7 @@
 #include "base/containers/flat_set.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/clock.h"
+#include "components/segmentation_platform/internal/data_collection/training_data_collector.h"
 #include "components/segmentation_platform/internal/execution/execution_request.h"
 #include "components/segmentation_platform/internal/execution/model_execution_manager_impl.h"
 #include "components/segmentation_platform/internal/scheduler/model_execution_scheduler.h"
@@ -31,7 +32,6 @@
 class ModelProviderFactory;
 class SignalHandler;
 class StorageService;
-class TrainingDataCollector;
 
 // Handles feature processing and model execution.
 class ExecutionService {
@@ -62,6 +62,11 @@
       std::vector<std::unique_ptr<Config>>* configs,
       PrefService* profile_prefs);
 
+  // Returns the training data collector.
+  TrainingDataCollector* training_data_collector() {
+    return training_data_collector_.get();
+  }
+
   // Called whenever a new or updated model is available. Must be a valid
   // SegmentInfo with valid metadata and features.
   void OnNewModelInfoReady(const proto::SegmentInfo& segment_info);
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
index 79a4a5e4..cf283a24 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
@@ -23,6 +23,7 @@
 #include "components/segmentation_platform/internal/platform_options.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 #include "components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h"
+#include "components/segmentation_platform/internal/selection/request_dispatcher.h"
 #include "components/segmentation_platform/internal/selection/segment_score_provider.h"
 #include "components/segmentation_platform/internal/selection/segment_selector_impl.h"
 #include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
@@ -95,6 +96,20 @@
           &SegmentationPlatformServiceImpl::OnModelRefreshNeeded,
           weak_ptr_factory_.GetWeakPtr()));
 
+  std::map<std::string, std::unique_ptr<SegmentResultProvider>>
+      result_providers;
+  for (const auto& config : configs_) {
+    result_providers[config->segmentation_key] = SegmentResultProvider::Create(
+        storage_service_->segment_info_database(),
+        storage_service_->signal_storage_config(),
+        storage_service_->default_model_manager(), &execution_service_, clock_,
+        platform_options_.force_refresh_results);
+  }
+
+  // Central class to delegate the client requests.
+  request_dispatcher_ = std::make_unique<RequestDispatcher>(
+      configs_, std::move(result_providers));
+
   for (const auto& config : configs_) {
     segment_selectors_[config->segmentation_key] =
         std::make_unique<SegmentSelectorImpl>(
@@ -141,7 +156,8 @@
     const PredictionOptions& prediction_options,
     scoped_refptr<InputContext> input_context,
     ClassificationResultCallback callback) {
-  // TODO(ritikagup@) : Implement logic.
+  request_dispatcher_->GetClassificationResult(
+      segmentation_key, prediction_options, input_context, std::move(callback));
 }
 
 SegmentSelectionResult SegmentationPlatformServiceImpl::GetCachedSegmentResult(
@@ -155,6 +171,7 @@
     const std::string& segmentation_key,
     scoped_refptr<InputContext> input_context,
     SegmentSelectionCallback callback) {
+  // TODO(shaktisahu): Delete this API after enabling RequestDispatcher.
   if (!storage_init_status_.has_value()) {
     // If the platform isn't fully initialized, cache the input arguments to run
     // later.
@@ -237,6 +254,8 @@
     selector.second->OnPlatformInitialized(&execution_service_);
   }
 
+  request_dispatcher_->OnPlatformInitialized(success);
+
   // Run any method calls that were received during initialization.
   while (!pending_actions_.empty()) {
     auto callback = std::move(pending_actions_.front());
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.h b/components/segmentation_platform/internal/segmentation_platform_service_impl.h
index 11a73d9..095fea6e 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.h
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.h
@@ -49,6 +49,7 @@
 }
 
 struct Config;
+class RequestDispatcher;
 class FieldTrialRegister;
 class ModelProviderFactory;
 class SegmentSelectorImpl;
@@ -165,6 +166,9 @@
   base::flat_map<std::string, std::unique_ptr<SegmentSelectorImpl>>
       segment_selectors_;
 
+  // For routing requests to the right handler.
+  std::unique_ptr<RequestDispatcher> request_dispatcher_;
+
   // Segment results.
   std::unique_ptr<SegmentScoreProvider> segment_score_provider_;
 
diff --git a/components/segmentation_platform/internal/selection/request_dispatcher.cc b/components/segmentation_platform/internal/selection/request_dispatcher.cc
new file mode 100644
index 0000000..a550160
--- /dev/null
+++ b/components/segmentation_platform/internal/selection/request_dispatcher.cc
@@ -0,0 +1,77 @@
+// 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.
+
+#include "components/segmentation_platform/internal/selection/request_dispatcher.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "base/task/single_thread_task_runner.h"
+#include "components/segmentation_platform/internal/selection/request_handler.h"
+#include "components/segmentation_platform/internal/selection/segment_result_provider.h"
+#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/prediction_options.h"
+
+namespace segmentation_platform {
+
+RequestDispatcher::RequestDispatcher(
+    const std::vector<std::unique_ptr<Config>>& configs,
+    std::map<std::string, std::unique_ptr<SegmentResultProvider>>
+        result_providers)
+    : configs_(configs) {
+  for (const auto& config : configs_) {
+    request_handlers_[config->segmentation_key] = RequestHandler::Create(
+        *config, std::move(result_providers[config->segmentation_key]));
+  }
+}
+
+RequestDispatcher::~RequestDispatcher() = default;
+
+void RequestDispatcher::OnPlatformInitialized(bool success) {
+  storage_init_status_ = success;
+
+  // Run any method calls that were received during initialization.
+  while (!pending_actions_.empty()) {
+    auto callback = std::move(pending_actions_.front());
+    pending_actions_.pop_front();
+    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE, std::move(callback));
+  }
+}
+
+void RequestDispatcher::GetClassificationResult(
+    const std::string& segmentation_key,
+    const PredictionOptions& options,
+    scoped_refptr<InputContext> input_context,
+    ClassificationResultCallback callback) {
+  // For on-demand results, we need to run the models for which we need DB
+  // initialization to be complete. Hence cache the request if platform
+  // initialization isn't completed yet.
+  if (options.on_demand_execution) {
+    if (!storage_init_status_.has_value()) {
+      // If the platform isn't fully initialized, cache the input arguments to
+      // run later.
+      pending_actions_.push_back(base::BindOnce(
+          &RequestDispatcher::GetClassificationResult,
+          weak_ptr_factory_.GetWeakPtr(), segmentation_key, options,
+          std::move(input_context), std::move(callback)));
+      return;
+    }
+
+    // If the platform initialization failed, invoke callback to return invalid
+    // results.
+    if (!storage_init_status_.value()) {
+      base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+          FROM_HERE,
+          base::BindOnce(std::move(callback),
+                         ClassificationResult(PredictionStatus::kFailed)));
+      return;
+    }
+  }
+
+  auto iter = request_handlers_.find(segmentation_key);
+  CHECK(iter != request_handlers_.end());
+  iter->second->GetClassificationResult(options, input_context,
+                                        std::move(callback));
+}
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/selection/request_dispatcher.h b/components/segmentation_platform/internal/selection/request_dispatcher.h
new file mode 100644
index 0000000..3bf8c8a
--- /dev/null
+++ b/components/segmentation_platform/internal/selection/request_dispatcher.h
@@ -0,0 +1,76 @@
+// 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 COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_REQUEST_DISPATCHER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_REQUEST_DISPATCHER_H_
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include "base/callback_helpers.h"
+#include "base/containers/circular_deque.h"
+#include "base/memory/scoped_refptr.h"
+#include "components/segmentation_platform/internal/selection/request_handler.h"
+#include "components/segmentation_platform/public/input_context.h"
+#include "components/segmentation_platform/public/result.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace segmentation_platform {
+struct Config;
+struct PredictionOptions;
+class SegmentResultProvider;
+
+// RequestDispatcher is the topmost layer in serving API requests for all
+// clients. It's responsible for
+// 1. Queuing API requests until the platform isn't fully initialized.
+// 2. Dispatching requests to client specific request handlers.
+class RequestDispatcher {
+ public:
+  RequestDispatcher(
+      const std::vector<std::unique_ptr<Config>>& configs,
+      std::map<std::string, std::unique_ptr<SegmentResultProvider>>
+          result_providers);
+  ~RequestDispatcher();
+
+  // Disallow copy/assign.
+  RequestDispatcher(RequestDispatcher&) = delete;
+  RequestDispatcher& operator=(RequestDispatcher&) = delete;
+
+  // Called when platform and database initializations are completed.
+  void OnPlatformInitialized(bool success);
+
+  // Client API. See `SegmentationPlatformService::GetClassificationResult`.
+  void GetClassificationResult(const std::string& segmentation_key,
+                               const PredictionOptions& options,
+                               scoped_refptr<InputContext> input_context,
+                               ClassificationResultCallback callback);
+
+  // For testing only.
+  int get_pending_actions_size_for_testing() { return pending_actions_.size(); }
+  void set_request_handler_for_testing(
+      const std::string& segmentation_key,
+      std::unique_ptr<RequestHandler> request_handler) {
+    request_handlers_[segmentation_key] = std::move(request_handler);
+  }
+
+ private:
+  // Configs for all registered clients.
+  const std::vector<std::unique_ptr<Config>>& configs_;
+
+  // Request handlers associated with the clients.
+  std::map<std::string, std::unique_ptr<RequestHandler>> request_handlers_;
+
+  // Storage initialization status.
+  absl::optional<bool> storage_init_status_;
+
+  // For caching any method calls that were received before initialization.
+  base::circular_deque<base::OnceClosure> pending_actions_;
+
+  base::WeakPtrFactory<RequestDispatcher> weak_ptr_factory_{this};
+};
+
+}  // namespace segmentation_platform
+
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_REQUEST_DISPATCHER_H_
diff --git a/components/segmentation_platform/internal/selection/request_dispatcher_unittest.cc b/components/segmentation_platform/internal/selection/request_dispatcher_unittest.cc
new file mode 100644
index 0000000..bef502b
--- /dev/null
+++ b/components/segmentation_platform/internal/selection/request_dispatcher_unittest.cc
@@ -0,0 +1,203 @@
+// 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.
+
+#include "components/segmentation_platform/internal/selection/request_dispatcher.h"
+
+#include "base/metrics/user_metrics.h"
+#include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/task_environment.h"
+#include "components/segmentation_platform/internal/selection/request_handler.h"
+#include "components/segmentation_platform/internal/selection/segment_result_provider.h"
+#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/prediction_options.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Invoke;
+
+namespace segmentation_platform {
+namespace {
+
+// Test clients.
+const char kTestClient1[] = "client_1";
+const char kTestClient2[] = "client_2";
+
+class MockRequestHandler : public RequestHandler {
+ public:
+  MockRequestHandler() = default;
+  ~MockRequestHandler() override = default;
+
+  MOCK_METHOD3(GetClassificationResult,
+               void(const PredictionOptions& options,
+                    scoped_refptr<InputContext> input_context,
+                    ClassificationResultCallback callback));
+};
+
+std::unique_ptr<Config> CreateTestConfig(const std::string& key) {
+  auto config = std::make_unique<Config>();
+  config->segmentation_key = key;
+  config->segmentation_uma_name = "TestUmaKey";
+  config->AddSegmentId(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+  return config;
+}
+
+class RequestDispatcherTest : public testing::Test {
+ public:
+  RequestDispatcherTest() = default;
+  ~RequestDispatcherTest() override = default;
+
+  void SetUp() override {
+    base::SetRecordActionTaskRunner(
+        task_environment_.GetMainThreadTaskRunner());
+
+    configs_.emplace_back(CreateTestConfig(kTestClient1));
+    configs_.emplace_back(CreateTestConfig(kTestClient2));
+    std::map<std::string, std::unique_ptr<SegmentResultProvider>>
+        result_providers;
+    request_dispatcher_ = std::make_unique<RequestDispatcher>(
+        configs_, std::move(result_providers));
+
+    auto handler1 = std::make_unique<MockRequestHandler>();
+    request_handler1_ = handler1.get();
+    request_dispatcher_->set_request_handler_for_testing(kTestClient1,
+                                                         std::move(handler1));
+
+    auto handler2 = std::make_unique<MockRequestHandler>();
+    request_handler2_ = handler2.get();
+    request_dispatcher_->set_request_handler_for_testing(kTestClient2,
+                                                         std::move(handler2));
+  }
+
+  void OnGetClassificationResult(base::RepeatingClosure closure,
+                                 const ClassificationResult& expected,
+                                 const ClassificationResult& actual) {
+    EXPECT_EQ(expected.ordered_labels, actual.ordered_labels);
+    EXPECT_EQ(expected.status, actual.status);
+    std::move(closure).Run();
+  }
+
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  std::vector<std::unique_ptr<Config>> configs_;
+  MockRequestHandler* request_handler1_ = nullptr;
+  MockRequestHandler* request_handler2_ = nullptr;
+  std::unique_ptr<RequestDispatcher> request_dispatcher_;
+};
+
+TEST_F(RequestDispatcherTest, TestRequestQueuingWithInitFailure) {
+  PredictionOptions options;
+  options.on_demand_execution = true;
+
+  EXPECT_EQ(0, request_dispatcher_->get_pending_actions_size_for_testing());
+
+  // Request handler will never be invoked if init fails.
+  EXPECT_CALL(*request_handler1_, GetClassificationResult(_, _, _)).Times(0);
+
+  base::RunLoop loop;
+  request_dispatcher_->GetClassificationResult(
+      kTestClient1, options, scoped_refptr<InputContext>(),
+      base::BindOnce(&RequestDispatcherTest::OnGetClassificationResult,
+                     base::Unretained(this), loop.QuitClosure(),
+                     ClassificationResult(PredictionStatus::kFailed)));
+  EXPECT_EQ(1, request_dispatcher_->get_pending_actions_size_for_testing());
+
+  // Finish platform initialization with failure. The request queue is flushed
+  // and callbacks are invoked with empty results.
+  request_dispatcher_->OnPlatformInitialized(false);
+  loop.Run();
+  EXPECT_EQ(0, request_dispatcher_->get_pending_actions_size_for_testing());
+}
+
+TEST_F(RequestDispatcherTest, TestRequestQueuingWithInitSuccess) {
+  base::RunLoop loop;
+  PredictionOptions options;
+  options.on_demand_execution = true;
+
+  EXPECT_EQ(0, request_dispatcher_->get_pending_actions_size_for_testing());
+
+  // Request from client 1.
+  ClassificationResult result1(PredictionStatus::kSucceeded);
+  result1.ordered_labels.emplace_back("test_label1");
+  EXPECT_CALL(*request_handler1_, GetClassificationResult(_, _, _))
+      .WillRepeatedly(Invoke([&](const PredictionOptions& options,
+                                 scoped_refptr<InputContext> input_context,
+                                 ClassificationResultCallback callback) {
+        std::move(callback).Run(result1);
+      }));
+
+  request_dispatcher_->GetClassificationResult(
+      kTestClient1, options, scoped_refptr<InputContext>(),
+      base::BindOnce(&RequestDispatcherTest::OnGetClassificationResult,
+                     base::Unretained(this), loop.QuitClosure(), result1));
+  EXPECT_EQ(1, request_dispatcher_->get_pending_actions_size_for_testing());
+
+  // Request from client 2.
+  ClassificationResult result2(PredictionStatus::kSucceeded);
+  result2.ordered_labels.emplace_back("test_label2");
+  EXPECT_CALL(*request_handler2_, GetClassificationResult(_, _, _))
+      .WillRepeatedly(Invoke([&](const PredictionOptions& options,
+                                 scoped_refptr<InputContext> input_context,
+                                 ClassificationResultCallback callback) {
+        std::move(callback).Run(result2);
+      }));
+
+  request_dispatcher_->GetClassificationResult(
+      kTestClient2, options, scoped_refptr<InputContext>(),
+      base::BindOnce(&RequestDispatcherTest::OnGetClassificationResult,
+                     base::Unretained(this), loop.QuitClosure(), result2));
+  EXPECT_EQ(2, request_dispatcher_->get_pending_actions_size_for_testing());
+
+  // Finish platform initialization with success. The request queue is flushed,
+  // and the request handler is invoked.
+  request_dispatcher_->OnPlatformInitialized(true);
+  loop.Run();
+  EXPECT_EQ(0, request_dispatcher_->get_pending_actions_size_for_testing());
+}
+
+TEST_F(RequestDispatcherTest, TestRequestAfterInitSuccess) {
+  base::RunLoop loop;
+  PredictionOptions options;
+  options.on_demand_execution = true;
+
+  // Init platform.
+  request_dispatcher_->OnPlatformInitialized(true);
+
+  // Request from client 1.
+  ClassificationResult result1(PredictionStatus::kSucceeded);
+  result1.ordered_labels.emplace_back("test_label1");
+  EXPECT_CALL(*request_handler1_, GetClassificationResult(_, _, _))
+      .WillRepeatedly(Invoke([&](const PredictionOptions& options,
+                                 scoped_refptr<InputContext> input_context,
+                                 ClassificationResultCallback callback) {
+        std::move(callback).Run(result1);
+      }));
+
+  request_dispatcher_->GetClassificationResult(
+      kTestClient1, options, scoped_refptr<InputContext>(),
+      base::BindOnce(&RequestDispatcherTest::OnGetClassificationResult,
+                     base::Unretained(this), loop.QuitClosure(), result1));
+  EXPECT_EQ(0, request_dispatcher_->get_pending_actions_size_for_testing());
+
+  // Request from client 2.
+  ClassificationResult result2(PredictionStatus::kSucceeded);
+  result2.ordered_labels.emplace_back("test_label2");
+  EXPECT_CALL(*request_handler2_, GetClassificationResult(_, _, _))
+      .WillRepeatedly(Invoke([&](const PredictionOptions& options,
+                                 scoped_refptr<InputContext> input_context,
+                                 ClassificationResultCallback callback) {
+        std::move(callback).Run(result2);
+      }));
+
+  request_dispatcher_->GetClassificationResult(
+      kTestClient2, options, scoped_refptr<InputContext>(),
+      base::BindOnce(&RequestDispatcherTest::OnGetClassificationResult,
+                     base::Unretained(this), loop.QuitClosure(), result2));
+  loop.Run();
+  EXPECT_EQ(0, request_dispatcher_->get_pending_actions_size_for_testing());
+}
+
+}  // namespace
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/selection/request_handler.cc b/components/segmentation_platform/internal/selection/request_handler.cc
new file mode 100644
index 0000000..aac19f3
--- /dev/null
+++ b/components/segmentation_platform/internal/selection/request_handler.cc
@@ -0,0 +1,130 @@
+// 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.
+
+#include "components/segmentation_platform/internal/selection/request_handler.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "base/task/single_thread_task_runner.h"
+#include "base/time/clock.h"
+#include "components/segmentation_platform/internal/post_processor/post_processor.h"
+#include "components/segmentation_platform/internal/selection/segment_result_provider.h"
+#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/input_context.h"
+#include "components/segmentation_platform/public/prediction_options.h"
+#include "components/segmentation_platform/public/proto/prediction_result.pb.h"
+
+namespace segmentation_platform {
+namespace {
+
+PredictionStatus ResultStateToPredictionStatus(
+    SegmentResultProvider::ResultState result_state) {
+  switch (result_state) {
+    case SegmentResultProvider::ResultState::kSuccessFromDatabase:
+    case SegmentResultProvider::ResultState::kDefaultModelScoreUsed:
+    case SegmentResultProvider::ResultState::kTfliteModelScoreUsed:
+      return PredictionStatus::kSucceeded;
+    case SegmentResultProvider::ResultState::kSignalsNotCollected:
+      return PredictionStatus::kNotReady;
+    default:
+      return PredictionStatus::kFailed;
+  }
+}
+
+class RequestHandlerImpl : public RequestHandler {
+ public:
+  RequestHandlerImpl(const Config& config,
+                     std::unique_ptr<SegmentResultProvider> result_provider);
+  ~RequestHandlerImpl() override;
+
+  // Disallow copy/assign.
+  RequestHandlerImpl(const RequestHandlerImpl&) = delete;
+  RequestHandlerImpl& operator=(const RequestHandlerImpl&) = delete;
+
+  // Client API. See `SegmentationPlatformService::GetClassificationResult`.
+  void GetClassificationResult(const PredictionOptions& options,
+                               scoped_refptr<InputContext> input_context,
+                               ClassificationResultCallback callback) override;
+
+ private:
+  void GetModelResult(const PredictionOptions& options,
+                      scoped_refptr<InputContext> input_context,
+                      SegmentResultProvider::SegmentResultCallback callback);
+
+  void OnGetModelResultForClassification(
+      ClassificationResultCallback classification_callback,
+      std::unique_ptr<SegmentResultProvider::SegmentResult> result);
+
+  // The config for providing client config params.
+  const Config& config_;
+
+  // The result provider responsible for getting the result, either by running
+  // the model or getting results from the cache as necessary.
+  std::unique_ptr<SegmentResultProvider> result_provider_;
+
+  base::WeakPtrFactory<RequestHandlerImpl> weak_ptr_factory_{this};
+};
+
+RequestHandlerImpl::RequestHandlerImpl(
+    const Config& config,
+    std::unique_ptr<SegmentResultProvider> result_provider)
+    : config_(config), result_provider_(std::move(result_provider)) {}
+
+RequestHandlerImpl::~RequestHandlerImpl() = default;
+
+void RequestHandlerImpl::GetClassificationResult(
+    const PredictionOptions& options,
+    scoped_refptr<InputContext> input_context,
+    ClassificationResultCallback callback) {
+  DCHECK(options.on_demand_execution);
+  GetModelResult(
+      options, input_context,
+      base::BindOnce(&RequestHandlerImpl::OnGetModelResultForClassification,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void RequestHandlerImpl::GetModelResult(
+    const PredictionOptions& options,
+    scoped_refptr<InputContext> input_context,
+    SegmentResultProvider::SegmentResultCallback callback) {
+  DCHECK_EQ(config_.segments.size(), 1u);
+  auto result_options =
+      std::make_unique<SegmentResultProvider::GetResultOptions>();
+
+  // Note that, this assumes that a client has only one model.
+  result_options->segment_id = config_.segments.begin()->first;
+  result_options->ignore_db_scores = options.on_demand_execution;
+  result_options->input_context = input_context;
+  result_options->callback = std::move(callback);
+
+  result_provider_->GetSegmentResult(std::move(result_options));
+}
+
+void RequestHandlerImpl::OnGetModelResultForClassification(
+    ClassificationResultCallback classification_callback,
+    std::unique_ptr<SegmentResultProvider::SegmentResult> result) {
+  PostProcessor post_processor;
+  PredictionStatus status = PredictionStatus::kFailed;
+  std::vector<std::string> ordered_labels;
+  if (result) {
+    ordered_labels = post_processor.GetClassifierResults(result->result);
+    status = ResultStateToPredictionStatus(result->state);
+  }
+  ClassificationResult classification_result(status);
+  classification_result.ordered_labels = ordered_labels;
+  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(classification_callback),
+                                classification_result));
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<RequestHandler> RequestHandler::Create(
+    const Config& config,
+    std::unique_ptr<SegmentResultProvider> result_provider) {
+  return std::make_unique<RequestHandlerImpl>(config,
+                                              std::move(result_provider));
+}
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/selection/request_handler.h b/components/segmentation_platform/internal/selection/request_handler.h
new file mode 100644
index 0000000..c61cb38
--- /dev/null
+++ b/components/segmentation_platform/internal/selection/request_handler.h
@@ -0,0 +1,40 @@
+// 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 COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_REQUEST_HANDLER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_REQUEST_HANDLER_H_
+
+#include "base/callback_helpers.h"
+#include "base/memory/scoped_refptr.h"
+#include "components/segmentation_platform/internal/selection/segment_result_provider.h"
+#include "components/segmentation_platform/public/input_context.h"
+#include "components/segmentation_platform/public/result.h"
+
+namespace segmentation_platform {
+struct Config;
+struct PredictionOptions;
+
+// RequestHandler handles client API requests for a single client. Internally,
+// it invokes the result provider for getting raw results and converts them to
+// the postprocessed results as required by the classification/regression API.
+class RequestHandler {
+ public:
+  RequestHandler() = default;
+  virtual ~RequestHandler() = default;
+
+  // Creates the instance.
+  static std::unique_ptr<RequestHandler> Create(
+      const Config& config,
+      std::unique_ptr<SegmentResultProvider> result_provider);
+
+  // Client API. See `SegmentationPlatformService::GetClassificationResult`.
+  virtual void GetClassificationResult(
+      const PredictionOptions& options,
+      scoped_refptr<InputContext> input_context,
+      ClassificationResultCallback callback) = 0;
+};
+
+}  // namespace segmentation_platform
+
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_REQUEST_HANDLER_H_
diff --git a/components/segmentation_platform/internal/selection/request_handler_unittest.cc b/components/segmentation_platform/internal/selection/request_handler_unittest.cc
new file mode 100644
index 0000000..999a4c8
--- /dev/null
+++ b/components/segmentation_platform/internal/selection/request_handler_unittest.cc
@@ -0,0 +1,113 @@
+// 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.
+
+#include "components/segmentation_platform/internal/selection/request_handler.h"
+
+#include "base/metrics/user_metrics.h"
+#include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/task_environment.h"
+#include "components/segmentation_platform/internal/selection/segment_result_provider.h"
+#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/prediction_options.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Invoke;
+
+namespace segmentation_platform {
+namespace {
+
+// Test Ids.
+const proto::SegmentId kSegmentId =
+    proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+
+class MockResultProvider : public SegmentResultProvider {
+ public:
+  MOCK_METHOD1(GetSegmentResult,
+               void(std::unique_ptr<GetResultOptions> options));
+};
+
+std::unique_ptr<Config> CreateTestConfig() {
+  auto config = std::make_unique<Config>();
+  config->segmentation_key = "client_key";
+  config->segmentation_uma_name = "TestUmaKey";
+  config->AddSegmentId(kSegmentId);
+  return config;
+}
+
+proto::PredictionResult CreatePredictionResultWithBinaryClassifier() {
+  proto::PredictionResult prediction_result;
+  proto::Predictor_BinaryClassifier* binary_classifier =
+      prediction_result.mutable_output_config()
+          ->mutable_predictor()
+          ->mutable_binary_classifier();
+
+  binary_classifier->set_threshold(0.5f);
+  binary_classifier->set_positive_label("positive_label");
+  binary_classifier->set_negative_label("negative_label");
+
+  prediction_result.add_result(0.8f);
+  return prediction_result;
+}
+
+class RequestHandlerTest : public testing::Test {
+ public:
+  RequestHandlerTest() = default;
+  ~RequestHandlerTest() override = default;
+
+  void SetUp() override {
+    base::SetRecordActionTaskRunner(
+        task_environment_.GetMainThreadTaskRunner());
+    config_ = CreateTestConfig();
+    auto provider = std::make_unique<MockResultProvider>();
+    result_provider_ = provider.get();
+    request_handler_ = RequestHandler::Create(*config_, std::move(provider));
+  }
+
+  void OnGetClassificationResult(base::RepeatingClosure closure,
+                                 const ClassificationResult& expected,
+                                 const ClassificationResult& actual) {
+    EXPECT_EQ(expected.ordered_labels, actual.ordered_labels);
+    EXPECT_EQ(expected.status, actual.status);
+    std::move(closure).Run();
+  }
+
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  std::unique_ptr<Config> config_;
+  MockResultProvider* result_provider_ = nullptr;
+  std::unique_ptr<RequestHandler> request_handler_;
+};
+
+TEST_F(RequestHandlerTest, TestGetClassificationResult) {
+  PredictionOptions options;
+  options.on_demand_execution = true;
+
+  EXPECT_CALL(*result_provider_, GetSegmentResult(_))
+      .Times(1)
+      .WillRepeatedly(Invoke(
+          [](std::unique_ptr<SegmentResultProvider::GetResultOptions> options) {
+            EXPECT_TRUE(options->ignore_db_scores);
+            EXPECT_EQ(options->segment_id, kSegmentId);
+            auto result =
+                std::make_unique<SegmentResultProvider::SegmentResult>(
+                    SegmentResultProvider::ResultState::kTfliteModelScoreUsed,
+                    CreatePredictionResultWithBinaryClassifier(), /*rank=*/2);
+            std::move(options->callback).Run(std::move(result));
+          }));
+
+  base::RunLoop loop;
+  ClassificationResult expected(PredictionStatus::kSucceeded);
+  expected.ordered_labels.emplace_back("positive_label");
+  request_handler_->GetClassificationResult(
+      options, scoped_refptr<InputContext>(),
+      base::BindOnce(&RequestHandlerTest::OnGetClassificationResult,
+                     base::Unretained(this), loop.QuitClosure(), expected));
+  loop.Run();
+}
+
+}  // namespace
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider.cc b/components/segmentation_platform/internal/selection/segment_result_provider.cc
index 944b0fdf..e4af5e9 100644
--- a/components/segmentation_platform/internal/selection/segment_result_provider.cc
+++ b/components/segmentation_platform/internal/selection/segment_result_provider.cc
@@ -228,14 +228,10 @@
 
   float rank = ComputeDiscreteMapping(
       request_state->options->discrete_mapping_key, *db_segment_info);
-  const auto& output = db_segment_info->prediction_result().result();
-  auto execution_result = std::make_unique<ModelExecutionResult>(
-      ModelProvider::Request(),
-      ModelProvider::Response(output.begin(), output.end()));
-  std::move(callback).Run(
-      std::move(request_state),
-      std::make_unique<SegmentResult>(ResultState::kSuccessFromDatabase, rank,
-                                      std::move(execution_result)));
+  std::move(callback).Run(std::move(request_state),
+                          std::make_unique<SegmentResult>(
+                              ResultState::kSuccessFromDatabase,
+                              db_segment_info->prediction_result(), rank));
 }
 
 void SegmentResultProviderImpl::ExecuteModelAndGetScore(
@@ -311,18 +307,19 @@
   auto* segment_info =
       FilterSegmentInfoBySource(request_state->available_segments, source);
   if (result->status == ModelExecutionStatus::kSuccess) {
-    segment_info->mutable_prediction_result()->clear_result();
-    segment_info->mutable_prediction_result()->mutable_result()->Add(
-        result->scores.begin(), result->scores.end());
-    float rank = ComputeDiscreteMapping(
-        request_state->options->discrete_mapping_key, *segment_info);
     ResultState state =
         source == DefaultModelManager::SegmentSource::DEFAULT_MODEL
             ? ResultState::kDefaultModelScoreUsed
             : ResultState::kTfliteModelScoreUsed;
+    auto prediction_result = metadata_utils::CreatePredictionResult(
+        result->scores, segment_info->model_metadata().output_config(),
+        clock_->Now());
+    segment_info->mutable_prediction_result()->CopyFrom(prediction_result);
+    float rank = ComputeDiscreteMapping(
+        request_state->options->discrete_mapping_key, *segment_info);
     std::move(callback).Run(
         std::move(request_state),
-        std::make_unique<SegmentResult>(state, rank, std::move(result)));
+        std::make_unique<SegmentResult>(state, prediction_result, rank));
   } else {
     ResultState state =
         source == DefaultModelManager::SegmentSource::DEFAULT_MODEL
@@ -347,9 +344,9 @@
     : state(state) {}
 SegmentResultProvider::SegmentResult::SegmentResult(
     ResultState state,
-    float rank,
-    std::unique_ptr<ModelExecutionResult> execution_result)
-    : state(state), rank(rank), execution_result(std::move(execution_result)) {}
+    const proto::PredictionResult& prediction_result,
+    float rank)
+    : state(state), result(prediction_result), rank(rank) {}
 SegmentResultProvider::SegmentResult::~SegmentResult() = default;
 
 SegmentResultProvider::GetResultOptions::GetResultOptions() = default;
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider.h b/components/segmentation_platform/internal/selection/segment_result_provider.h
index 2cd80987..6413264 100644
--- a/components/segmentation_platform/internal/selection/segment_result_provider.h
+++ b/components/segmentation_platform/internal/selection/segment_result_provider.h
@@ -50,18 +50,19 @@
   struct SegmentResult {
     explicit SegmentResult(ResultState state);
     SegmentResult(ResultState state,
-                  float rank,
-                  std::unique_ptr<ModelExecutionResult> execution_result);
+                  const proto::PredictionResult& result,
+                  float rank);
     ~SegmentResult();
     SegmentResult(const SegmentResult&) = delete;
     SegmentResult& operator=(const SegmentResult&) = delete;
 
     ResultState state = ResultState::kUnknown;
-    absl::optional<float> rank;
 
-    // The execution result is only available when the model is executed.
-    // TODO(ssid): Support storing inputs to disk if needed.
-    std::unique_ptr<ModelExecutionResult> execution_result;
+    // Contains the raw scores along with output config.
+    proto::PredictionResult result;
+
+    // TODO(shaktisahu): Delete this after full migration.
+    absl::optional<float> rank;
   };
   using SegmentResultCallback =
       base::OnceCallback<void(std::unique_ptr<SegmentResult>)>;
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.cc b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
index 24cce6ff5..3c9cba2a3 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_impl.cc
+++ b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
@@ -10,6 +10,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/time/clock.h"
 #include "base/time/time.h"
+#include "components/segmentation_platform/internal/data_collection/training_data_collector.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/database/signal_storage_config.h"
 #include "components/segmentation_platform/internal/metadata/metadata_utils.h"
@@ -131,6 +132,11 @@
 
 void SegmentSelectorImpl::OnPlatformInitialized(
     ExecutionService* execution_service) {
+  // If training data collector has been set for testing, do not get it from
+  // execution service.
+  if (!training_data_collector_) {
+    training_data_collector_ = execution_service->training_data_collector();
+  }
   segment_result_provider_ = SegmentResultProvider::Create(
       segment_database_, signal_storage_config_, default_model_manager_,
       execution_service, clock_, platform_options_.force_refresh_results);
@@ -276,6 +282,13 @@
   }
   ranks->insert(std::make_pair(current_segment_id, *result->rank));
 
+  if (config_->on_demand_execution && training_data_collector_) {
+    // Collect training data on demand.
+    training_data_collector_->OnDecisionTime(
+        current_segment_id, input_context,
+        proto::TrainingOutputs::TriggerConfig::ONDEMAND);
+  }
+
   GetRankForNextSegment(std::move(ranks), input_context, std::move(callback));
 }
 
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.h b/components/segmentation_platform/internal/selection/segment_selector_impl.h
index 81320c1d..5b7b3c89 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_impl.h
+++ b/components/segmentation_platform/internal/selection/segment_selector_impl.h
@@ -11,6 +11,7 @@
 #include "base/memory/raw_ptr.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/platform_options.h"
+#include "components/segmentation_platform/internal/scheduler/execution_service.h"
 #include "components/segmentation_platform/internal/selection/segment_result_provider.h"
 #include "components/segmentation_platform/internal/selection/segment_selector.h"
 #include "components/segmentation_platform/public/input_context.h"
@@ -26,7 +27,6 @@
 
 struct Config;
 class DefaultModelManager;
-class ExecutionService;
 class ExperimentalGroupRecorder;
 class FieldTrialRegister;
 class SegmentationResultPrefs;
@@ -74,6 +74,11 @@
     segment_result_provider_ = std::move(result_provider);
   }
 
+  void set_training_data_collector_for_testing(
+      TrainingDataCollector* training_data_collector) {
+    training_data_collector_ = training_data_collector;
+  }
+
  private:
   // For testing.
   friend class SegmentSelectorTest;
@@ -142,6 +147,9 @@
   // the clients in the current session.
   SegmentSelectionResult selected_segment_last_session_;
 
+  // Pointer to the training data collector.
+  raw_ptr<TrainingDataCollector> training_data_collector_{};
+
   base::WeakPtrFactory<SegmentSelectorImpl> weak_ptr_factory_{this};
 };
 
diff --git a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
index 342bc53..205ccba7 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
+++ b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
@@ -32,6 +32,8 @@
 
 namespace {
 
+using ::testing::NiceMock;
+
 class MockFieldTrialRegister : public FieldTrialRegister {
  public:
   MOCK_METHOD2(RegisterFieldTrial,
@@ -61,6 +63,20 @@
                void(std::unique_ptr<GetResultOptions> options));
 };
 
+class MockTrainingDataCollector : public TrainingDataCollector {
+ public:
+  MOCK_METHOD0(OnModelMetadataUpdated, void());
+  MOCK_METHOD0(OnServiceInitialized, void());
+  MOCK_METHOD0(ReportCollectedContinuousTrainingData, void());
+  MOCK_METHOD3(OnDecisionTime,
+               void(proto::SegmentId id,
+                    scoped_refptr<InputContext> input_context,
+                    DecisionType type));
+  MOCK_METHOD2(OnObservationTrigger,
+               void(TrainingDataCache::RequestId request_id,
+                    const proto::SegmentInfo& segment_info));
+};
+
 }  // namespace
 
 class TestSegmentationResultPrefs : public SegmentationResultPrefs {
@@ -101,6 +117,8 @@
         segment_database_.get(), &signal_storage_config_,
         std::move(prefs_moved), config_.get(), &field_trial_register_, &clock_,
         PlatformOptions::CreateDefault(), default_manager_.get());
+    segment_selector_->set_training_data_collector_for_testing(
+        &training_data_collector_);
     segment_selector_->OnPlatformInitialized(nullptr);
   }
 
@@ -145,13 +163,14 @@
   TestModelProviderFactory::Data model_providers_;
   TestModelProviderFactory provider_factory_;
   std::unique_ptr<Config> config_;
-  MockFieldTrialRegister field_trial_register_;
+  NiceMock<MockFieldTrialRegister> field_trial_register_;
   base::SimpleTestClock clock_;
   std::unique_ptr<test::TestSegmentInfoDatabase> segment_database_;
   MockSignalStorageConfig signal_storage_config_;
   std::unique_ptr<DefaultModelManager> default_manager_;
   raw_ptr<TestSegmentationResultPrefs> prefs_;
   std::unique_ptr<SegmentSelectorImpl> segment_selector_;
+  MockTrainingDataCollector training_data_collector_;
 };
 
 TEST_F(SegmentSelectorTest, FindBestSegmentFlowWithTwoSegments) {
@@ -194,6 +213,11 @@
   float mapping2[][2] = {{0.3, 1}, {0.4, 4}};
   InitializeMetadataForSegment(kSegmentId2, mapping2, 2);
 
+  EXPECT_CALL(training_data_collector_, OnDecisionTime(kSegmentId, _, _))
+      .Times(1);
+  EXPECT_CALL(training_data_collector_, OnDecisionTime(kSegmentId2, _, _))
+      .Times(1);
+
   auto result_provider = std::make_unique<MockResultProvider>();
   EXPECT_CALL(*result_provider, GetSegmentResult(_))
       .Times(2)
@@ -204,9 +228,7 @@
             auto result =
                 std::make_unique<SegmentResultProvider::SegmentResult>(
                     SegmentResultProvider::ResultState::kTfliteModelScoreUsed,
-                    rank,
-                    std::make_unique<ModelExecutionResult>(
-                        ModelExecutionStatus::kSuccess));
+                    proto::PredictionResult(), rank);
             std::move(options->callback).Run(std::move(result));
           }));
   segment_selector_->set_segment_result_provider_for_testing(
@@ -415,6 +437,8 @@
       segment_database_.get(), &signal_storage_config_, std::move(prefs_moved),
       config_.get(), &field_trial_register_, &clock_,
       PlatformOptions::CreateDefault(), default_manager_.get());
+  segment_selector_->set_training_data_collector_for_testing(
+      &training_data_collector_);
   segment_selector_->OnPlatformInitialized(nullptr);
 
   SegmentSelectionResult result;
@@ -569,6 +593,8 @@
               wait_for_subsegment.QuitClosure().Run();
           }));
 
+  segment_selector_->set_training_data_collector_for_testing(
+      &training_data_collector_);
   segment_selector_->OnPlatformInitialized(nullptr);
   wait_for_subsegment.Run();
   EXPECT_THAT(
diff --git a/components/segmentation_platform/public/BUILD.gn b/components/segmentation_platform/public/BUILD.gn
index f215391..48551646 100644
--- a/components/segmentation_platform/public/BUILD.gn
+++ b/components/segmentation_platform/public/BUILD.gn
@@ -23,6 +23,7 @@
     "local_state_helper.h",
     "model_provider.cc",
     "model_provider.h",
+    "prediction_options.h",
     "result.cc",
     "result.h",
     "segment_selection_result.cc",
diff --git a/components/segmentation_platform/public/prediction_options.h b/components/segmentation_platform/public/prediction_options.h
new file mode 100644
index 0000000..baa10acd
--- /dev/null
+++ b/components/segmentation_platform/public/prediction_options.h
@@ -0,0 +1,24 @@
+// 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 COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_PREDICTION_OPTIONS_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_PREDICTION_OPTIONS_H_
+
+namespace segmentation_platform {
+
+// Options that can be specified when invoking SegmentationPlatformService APIs.
+struct PredictionOptions {
+  PredictionOptions() = default;
+  ~PredictionOptions() = default;
+
+  PredictionOptions(const PredictionOptions&) = default;
+  PredictionOptions& operator=(const PredictionOptions&) = default;
+
+  // Set to true if on demand execution is to be done.
+  bool on_demand_execution = false;
+};
+
+}  // namespace segmentation_platform
+
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_PREDICTION_OPTIONS_H_
diff --git a/components/segmentation_platform/public/result.cc b/components/segmentation_platform/public/result.cc
index 89541695..9b26a769b 100644
--- a/components/segmentation_platform/public/result.cc
+++ b/components/segmentation_platform/public/result.cc
@@ -11,8 +11,19 @@
 
 ClassificationResult::~ClassificationResult() = default;
 
+ClassificationResult::ClassificationResult(const ClassificationResult&) =
+    default;
+
+ClassificationResult& ClassificationResult::operator=(
+    const ClassificationResult&) = default;
+
 RegressionResult::RegressionResult(PredictionStatus status) : status(status) {}
 
 RegressionResult::~RegressionResult() = default;
 
-}  // namespace segmentation_platform
\ No newline at end of file
+RegressionResult::RegressionResult(const RegressionResult&) = default;
+
+RegressionResult& RegressionResult::operator=(const RegressionResult&) =
+    default;
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/public/result.h b/components/segmentation_platform/public/result.h
index 88d3669..7004be8 100644
--- a/components/segmentation_platform/public/result.h
+++ b/components/segmentation_platform/public/result.h
@@ -28,12 +28,11 @@
   explicit ClassificationResult(PredictionStatus status);
   ~ClassificationResult();
 
-  // Disallow copy/assign.
-  ClassificationResult(const ClassificationResult&) = delete;
-  ClassificationResult& operator=(const ClassificationResult&) = delete;
+  ClassificationResult(const ClassificationResult&);
+  ClassificationResult& operator=(const ClassificationResult&);
 
   // Various error codes such as model failed or insufficient data collection.
-  const PredictionStatus status;
+  PredictionStatus status;
 
   // The list of labels arranged in descending order of result from model
   // evaluation. For BinaryClassifier, it is eithier a `positive_label` or
@@ -50,12 +49,11 @@
   explicit RegressionResult(PredictionStatus status);
   ~RegressionResult();
 
-  // Disallow copy/assign.
-  RegressionResult(const RegressionResult&) = delete;
-  RegressionResult& operator=(const RegressionResult&) = delete;
+  RegressionResult(const RegressionResult&);
+  RegressionResult& operator=(const RegressionResult&);
 
   // Various error codes such as model failed or insufficient data collection.
-  const PredictionStatus status;
+  PredictionStatus status;
 
   // The result of regression.
   float regression_result{0.0f};
diff --git a/components/segmentation_platform/public/segmentation_platform_service.h b/components/segmentation_platform/public/segmentation_platform_service.h
index 9f1302e..bcf8bbd9 100644
--- a/components/segmentation_platform/public/segmentation_platform_service.h
+++ b/components/segmentation_platform/public/segmentation_platform_service.h
@@ -13,6 +13,7 @@
 #include "base/types/id_type.h"
 #include "build/build_config.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/segmentation_platform/public/prediction_options.h"
 #include "components/segmentation_platform/public/result.h"
 
 #if BUILDFLAG(IS_ANDROID)
@@ -48,18 +49,6 @@
   SegmentationPlatformService& operator=(const SegmentationPlatformService&) =
       delete;
 
-  struct PredictionOptions {
-    PredictionOptions() = default;
-    ~PredictionOptions() = default;
-
-    // Disallow copy/assign.
-    PredictionOptions(const PredictionOptions&) = delete;
-    PredictionOptions& operator=(const PredictionOptions&) = delete;
-
-    // Set to true if on demand execution is to be done.
-    bool on_demand_execution = false;
-  };
-
   // Registers preferences used by this class in the provided |registry|.  This
   // should be called for the Profile registry.
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
diff --git a/components/sync_device_info/BUILD.gn b/components/sync_device_info/BUILD.gn
index 4cb48f7..a5dd5cb 100644
--- a/components/sync_device_info/BUILD.gn
+++ b/components/sync_device_info/BUILD.gn
@@ -62,6 +62,7 @@
 
   if (is_chromeos) {
     sources += [ "local_device_info_util_chromeos.cc" ]
+    deps += [ "//chromeos/constants" ]
   }
 
   if (is_ios) {
@@ -88,11 +89,8 @@
     sources += [ "local_device_info_util_win.cc" ]
   }
 
-  if (is_chromeos) {
-    deps += [
-      "//chromeos/constants",
-      "//chromeos/system",
-    ]
+  if (is_chromeos_ash) {
+    deps += [ "//chromeos/ash/components/system" ]
   }
 }
 
@@ -139,7 +137,7 @@
   ]
 
   if (is_chromeos_ash) {
-    deps += [ "//chromeos/system" ]
+    deps += [ "//chromeos/ash/components/system" ]
   }
 }
 
diff --git a/components/sync_device_info/device_info_sync_bridge_unittest.cc b/components/sync_device_info/device_info_sync_bridge_unittest.cc
index 185807a4..1a4fb75 100644
--- a/components/sync_device_info/device_info_sync_bridge_unittest.cc
+++ b/components/sync_device_info/device_info_sync_bridge_unittest.cc
@@ -44,7 +44,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #endif
 
 namespace syncer {
diff --git a/components/sync_device_info/local_device_info_util.cc b/components/sync_device_info/local_device_info_util.cc
index d3fe8c656..8282785 100644
--- a/components/sync_device_info/local_device_info_util.cc
+++ b/components/sync_device_info/local_device_info_util.cc
@@ -17,7 +17,7 @@
 #include "ui/base/device_form_factor.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #endif
 
 namespace syncer {
diff --git a/components/viz/service/display_embedder/skia_output_surface_dependency_impl.cc b/components/viz/service/display_embedder/skia_output_surface_dependency_impl.cc
index 87e7bc5..2183bfc 100644
--- a/components/viz/service/display_embedder/skia_output_surface_dependency_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_dependency_impl.cc
@@ -111,7 +111,7 @@
     return gl::init::CreateOffscreenGLSurfaceWithFormat(
         GetSharedContextState()->display(), gfx::Size(), format);
   } else {
-    return gpu::ImageTransportSurface::CreateNativeSurface(
+    return gpu::ImageTransportSurface::CreatePresenterOrNativeSurface(
         GetSharedContextState()->display(), stub, surface_handle_, format);
   }
 }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index bda7797..0a6188b 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2413,9 +2413,9 @@
       "//chromeos/ash/components/dbus",
       "//chromeos/ash/components/dbus/debug_daemon",
       "//chromeos/ash/components/network",
+      "//chromeos/ash/components/system",
       "//chromeos/ash/resources",
       "//chromeos/resources",
-      "//chromeos/system",
       "//components/chromeos_camera:mojo_mjpeg_decode_accelerator",
       "//components/session_manager/core",
       "//services/data_decoder/public/cpp",
diff --git a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
index d80daed4..2c7407c 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
@@ -28,6 +28,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "components/attribution_reporting/suitable_origin.h"
 #include "content/browser/aggregation_service/aggregatable_report.h"
 #include "content/browser/aggregation_service/aggregation_service.h"
 #include "content/browser/aggregation_service/aggregation_service_test_utils.h"
@@ -66,6 +67,8 @@
 
 namespace {
 
+using ::attribution_reporting::SuitableOrigin;
+
 using ::testing::_;
 using ::testing::AllOf;
 using ::testing::AnyOf;
@@ -441,9 +444,9 @@
       "https://c.example/.well-known/attribution-reporting/"
       "report-event-attribution");
 
-  const auto origin_a = url::Origin::Create(url_a);
-  const auto origin_b = url::Origin::Create(url_b);
-  const auto origin_c = url::Origin::Create(url_c);
+  const auto origin_a = *SuitableOrigin::Create(url_a);
+  const auto origin_b = *SuitableOrigin::Create(url_b);
+  const auto origin_c = *SuitableOrigin::Create(url_c);
 
   attribution_manager_->HandleSource(SourceBuilder()
                                          .SetExpiry(kImpressionExpiry)
@@ -491,8 +494,8 @@
       "https://b.example/.well-known/attribution-reporting/"
       "report-event-attribution");
 
-  const auto origin_a = url::Origin::Create(url_a);
-  const auto origin_b = url::Origin::Create(url_b);
+  const auto origin_a = *SuitableOrigin::Create(url_a);
+  const auto origin_b = *SuitableOrigin::Create(url_b);
 
   attribution_manager_->HandleSource(SourceBuilder()
                                          .SetExpiry(kImpressionExpiry)
@@ -576,8 +579,8 @@
       "https://b.example/.well-known/attribution-reporting/"
       "report-event-attribution");
 
-  const auto origin_a = url::Origin::Create(url_a);
-  const auto origin_b = url::Origin::Create(url_b);
+  const auto origin_a = *SuitableOrigin::Create(url_a);
+  const auto origin_b = *SuitableOrigin::Create(url_b);
 
   attribution_manager_->HandleSource(SourceBuilder()
                                          .SetExpiry(kImpressionExpiry)
@@ -990,7 +993,7 @@
   GURL session_only_origin("https://sessiononly.example");
   auto impression =
       SourceBuilder()
-          .SetSourceOrigin(url::Origin::Create(session_only_origin))
+          .SetSourceOrigin(*SuitableOrigin::Create(session_only_origin))
           .Build();
 
   mock_storage_policy_->AddSessionOnly(session_only_origin);
@@ -1010,8 +1013,8 @@
 
 TEST_F(AttributionManagerImplTest,
        SessionOnlyOrigins_DeletedIfAnyOriginMatches) {
-  url::Origin session_only_origin =
-      url::Origin::Create(GURL("https://sessiononly.example"));
+  const auto session_only_origin =
+      *SuitableOrigin::Deserialize("https://sessiononly.example");
   // Create impressions which each have the session only origin as one of
   // impression/conversion/reporting origin.
   auto impression1 =
@@ -1024,7 +1027,7 @@
   // Create one  impression which is not session only.
   auto impression4 = SourceBuilder().Build();
 
-  mock_storage_policy_->AddSessionOnly(session_only_origin.GetURL());
+  mock_storage_policy_->AddSessionOnly(session_only_origin->GetURL());
 
   attribution_manager_->HandleSource(impression1);
   attribution_manager_->HandleSource(impression2);
@@ -1403,9 +1406,10 @@
 
 TEST_F(AttributionManagerImplTest,
        EmbedderDisallowsReporting_DebugReportNotSent) {
-  const auto source_origin = url::Origin::Create(GURL("https://i.test"));
-  const auto destination_origin = url::Origin::Create(GURL("https://d.test"));
-  const auto reporting_origin = url::Origin::Create(GURL("https://r.test"));
+  const auto source_origin = *SuitableOrigin::Deserialize("https://i.test");
+  const auto destination_origin =
+      *SuitableOrigin::Deserialize("https://d.test");
+  const auto reporting_origin = *SuitableOrigin::Deserialize("https://r.test");
 
   cookie_checker_->AddOriginWithDebugCookieSet(reporting_origin);
 
@@ -1626,8 +1630,8 @@
 TEST_F(AttributionManagerImplTest, RegistrationsHandledInOrder) {
   cookie_checker_->DeferCallbacks();
 
-  const auto r1 = url::Origin::Create(GURL("https://r1.test"));
-  const auto r2 = url::Origin::Create(GURL("https://r2.test"));
+  const auto r1 = *SuitableOrigin::Deserialize("https://r1.test");
+  const auto r2 = *SuitableOrigin::Deserialize("https://r2.test");
 
   attribution_manager_->HandleSource(SourceBuilder()
                                          .SetSourceEventId(1)
@@ -1734,7 +1738,7 @@
     attribution_manager_->HandleSource(
         SourceBuilder()
             .SetReportingOrigin(
-                url::Origin::Create(GURL(test_case.reporting_origin)))
+                *SuitableOrigin::Deserialize(test_case.reporting_origin))
             .SetDebugKey(test_case.input_debug_key)
             .SetExpiry(kImpressionExpiry)
             .Build());
@@ -1762,7 +1766,7 @@
 
   for (const auto& test_case : kDebugKeyTestCases) {
     const auto reporting_origin =
-        url::Origin::Create(GURL(test_case.reporting_origin));
+        *SuitableOrigin::Deserialize(test_case.reporting_origin);
 
     attribution_manager_->HandleSource(SourceBuilder()
                                            .SetReportingOrigin(reporting_origin)
@@ -1792,7 +1796,7 @@
 }
 
 TEST_F(AttributionManagerImplTest, DebugReport_SentImmediately) {
-  const auto reporting_origin = url::Origin::Create(GURL("https://r1.test"));
+  const auto reporting_origin = *SuitableOrigin::Deserialize("https://r1.test");
 
   cookie_checker_->AddOriginWithDebugCookieSet(reporting_origin);
 
@@ -2096,7 +2100,7 @@
 }
 
 TEST_F(AttributionManagerImplTest, TriggerVerboseDebugReport_ReportSent) {
-  url::Origin reporting_origin = url::Origin::Create(GURL("https://r1.test"));
+  const auto reporting_origin = *SuitableOrigin::Deserialize("https://r1.test");
   cookie_checker_->AddOriginWithDebugCookieSet(reporting_origin);
 
   // Failed without debug reporting.
@@ -2119,7 +2123,7 @@
   // but no debug cookie is set, therefore no debug report is sent.
   attribution_manager_->HandleTrigger(
       TriggerBuilder()
-          .SetReportingOrigin(url::Origin::Create(GURL("https://r2.test")))
+          .SetReportingOrigin(*SuitableOrigin::Deserialize("https://r2.test"))
           .SetDebugReporting(true)
           .Build());
   task_environment_.RunUntilIdle();
@@ -2159,7 +2163,7 @@
 
 TEST_F(AttributionManagerImplTest,
        EmbedderDisallowsTriggerVerboseDebugReport_NoReportSent) {
-  url::Origin reporting_origin = url::Origin::Create(GURL("https://r1.test"));
+  const auto reporting_origin = *SuitableOrigin::Deserialize("https://r1.test");
   cookie_checker_->AddOriginWithDebugCookieSet(reporting_origin);
 
   MockAttributionReportingContentBrowserClient browser_client;
@@ -2201,7 +2205,8 @@
 TEST_F(AttributionManagerImplDebugReportTest, VerboseDebugReport_ReportSent) {
   attribution_manager_->HandleSource(SourceBuilder().Build());
 
-  const auto destination_origin = url::Origin::Create(GURL("https://d.test"));
+  const auto destination_origin =
+      *SuitableOrigin::Deserialize("https://d.test");
 
   // Failed without debug reporting.
   attribution_manager_->HandleSource(
@@ -2297,7 +2302,7 @@
 
   attribution_manager_->HandleSource(
       SourceBuilder()
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://d.test")))
+          .SetDestinationOrigin(*SuitableOrigin::Deserialize("https://d.test"))
           .SetDebugReporting(true)
           .Build());
 
@@ -2326,7 +2331,7 @@
   const int kExpectedStatus = 200;
   EXPECT_CALL(observer, OnDebugReportSent(_, kExpectedStatus, _));
 
-  url::Origin reporting_origin = url::Origin::Create(GURL("https://r1.test"));
+  const auto reporting_origin = *SuitableOrigin::Deserialize("https://r1.test");
   cookie_checker_->AddOriginWithDebugCookieSet(reporting_origin);
 
   attribution_manager_->HandleSource(SourceBuilder().Build());
@@ -2351,7 +2356,7 @@
   // debug cookie is set, therefore no debug report is sent.
   attribution_manager_->HandleSource(
       SourceBuilder()
-          .SetReportingOrigin(url::Origin::Create(GURL("https://r2.test")))
+          .SetReportingOrigin(*SuitableOrigin::Deserialize("https://r2.test"))
           .SetDebugReporting(true)
           .Build());
   task_environment_.RunUntilIdle();
diff --git a/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc b/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc
index 80a490c..629eef06 100644
--- a/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc
@@ -352,8 +352,9 @@
 TEST_F(AttributionReportNetworkSenderTest, ReportSent_RequestAttributesSet) {
   auto impression =
       SourceBuilder(base::Time())
-          .SetReportingOrigin(url::Origin::Create(GURL("https://a.com")))
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://sub.b.com")))
+          .SetReportingOrigin(*SuitableOrigin::Deserialize("https://a.com"))
+          .SetDestinationOrigin(
+              *SuitableOrigin::Deserialize("https://sub.b.com"))
           .BuildStored();
   AttributionReport report =
       ReportBuilder(AttributionInfoBuilder(impression).Build()).Build();
diff --git a/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc b/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc
index 07a7e52..5be60b9c 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc
@@ -23,6 +23,7 @@
 #include "base/time/time.h"
 #include "components/aggregation_service/aggregation_service.mojom.h"
 #include "components/attribution_reporting/filters.h"
+#include "components/attribution_reporting/suitable_origin.h"
 #include "content/browser/attribution_reporting/aggregatable_histogram_contribution.h"
 #include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_reporting.pb.h"
@@ -42,6 +43,8 @@
 
 namespace {
 
+using ::attribution_reporting::SuitableOrigin;
+
 using ::testing::AllOf;
 using ::testing::ElementsAre;
 using ::testing::IsEmpty;
@@ -614,12 +617,12 @@
       .max_attribution_reporting_origins = std::numeric_limits<int64_t>::max(),
       .max_attributions = std::numeric_limits<int64_t>::max(),
   });
-  const url::Origin source_origin =
-      url::Origin::Create(GURL("https://sub.impression.example/"));
-  const url::Origin reporting_origin =
-      url::Origin::Create(GURL("https://a.example/"));
-  const url::Origin destination_origin =
-      url::Origin::Create(GURL("https://b.example/"));
+  const auto source_origin =
+      *SuitableOrigin::Deserialize("https://sub.impression.example/");
+  const auto reporting_origin =
+      *SuitableOrigin::Deserialize("https://a.example/");
+  const auto destination_origin =
+      *SuitableOrigin::Deserialize("https://b.example/");
   storage()->StoreSource(SourceBuilder()
                              .SetExpiry(base::Days(30))
                              .SetSourceOrigin(source_origin)
@@ -665,12 +668,11 @@
       .max_attribution_reporting_origins = std::numeric_limits<int64_t>::max(),
       .max_attributions = std::numeric_limits<int64_t>::max(),
   });
-  const url::Origin source_origin =
-      url::Origin::Create(GURL("https://b.example/"));
-  const url::Origin reporting_origin =
-      url::Origin::Create(GURL("https://a.example/"));
-  const url::Origin destination_origin =
-      url::Origin::Create(GURL("https://sub.impression.example/"));
+  const auto source_origin = *SuitableOrigin::Deserialize("https://b.example/");
+  const auto reporting_origin =
+      *SuitableOrigin::Deserialize("https://a.example/");
+  const auto destination_origin =
+      *SuitableOrigin::Deserialize("https://sub.impression.example/");
   storage()->StoreSource(SourceBuilder()
                              .SetExpiry(base::Days(30))
                              .SetSourceOrigin(source_origin)
diff --git a/content/browser/attribution_reporting/attribution_storage_unittest.cc b/content/browser/attribution_reporting/attribution_storage_unittest.cc
index 6539ffd..f150e25 100644
--- a/content/browser/attribution_reporting/attribution_storage_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_unittest.cc
@@ -274,16 +274,17 @@
 
 TEST_F(AttributionStorageTest,
        CrossOriginSameDomainConversion_ImpressionConverted) {
-  auto impression =
-      SourceBuilder()
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://sub.a.test")))
-          .Build();
+  auto impression = SourceBuilder()
+                        .SetDestinationOrigin(
+                            *SuitableOrigin::Deserialize("https://sub.a.test"))
+                        .Build();
   storage()->StoreSource(impression);
   EXPECT_EQ(
       AttributionTrigger::EventLevelResult::kSuccess,
       MaybeCreateAndStoreEventLevelReport(
           TriggerBuilder()
-              .SetDestinationOrigin(url::Origin::Create(GURL("https://a.test")))
+              .SetDestinationOrigin(
+                  *SuitableOrigin::Deserialize("https://a.test"))
               .SetReportingOrigin(impression.common_info().reporting_origin())
               .Build()));
 }
@@ -390,8 +391,8 @@
 TEST_F(AttributionStorageTest,
        ConversionWithDifferentReportingOrigin_NoReportScheduled) {
   auto impression = SourceBuilder()
-                        .SetReportingOrigin(
-                            url::Origin::Create(GURL("https://different.test")))
+                        .SetReportingOrigin(*SuitableOrigin::Deserialize(
+                            "https://different.test"))
                         .Build();
   storage()->StoreSource(impression);
   EXPECT_EQ(AttributionTrigger::EventLevelResult::kNoMatchingImpressions,
@@ -405,8 +406,8 @@
 TEST_F(AttributionStorageTest,
        ConversionWithDifferentConversionOrigin_NoReportScheduled) {
   auto impression = SourceBuilder()
-                        .SetDestinationOrigin(
-                            url::Origin::Create(GURL("https://different.test")))
+                        .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                            "https://different.test"))
                         .Build();
   storage()->StoreSource(impression);
   EXPECT_EQ(AttributionTrigger::EventLevelResult::kNoMatchingImpressions,
@@ -458,7 +459,7 @@
 
   auto new_impression =
       SourceBuilder()
-          .SetSourceOrigin(url::Origin::Create(GURL("https://other.test/")))
+          .SetSourceOrigin(*SuitableOrigin::Deserialize("https://other.test/"))
           .Build();
   storage()->StoreSource(new_impression);
 
@@ -520,8 +521,8 @@
 
   // Store a new impression with a different reporting origin.
   storage()->StoreSource(SourceBuilder()
-                             .SetReportingOrigin(url::Origin::Create(
-                                 GURL("https://different.test")))
+                             .SetReportingOrigin(*SuitableOrigin::Deserialize(
+                                 "https://different.test"))
                              .Build());
 
   // The first impression should still be active and able to convert.
@@ -642,21 +643,21 @@
 
 TEST_F(AttributionStorageTest, MaxImpressionsPerOrigin_PerOriginNotSite) {
   delegate()->set_max_sources_per_origin(2);
-  storage()->StoreSource(
-      SourceBuilder()
-          .SetSourceOrigin(url::Origin::Create(GURL("https://foo.a.example")))
-          .SetSourceEventId(3)
-          .Build());
-  storage()->StoreSource(
-      SourceBuilder()
-          .SetSourceOrigin(url::Origin::Create(GURL("https://foo.a.example")))
-          .SetSourceEventId(5)
-          .Build());
-  storage()->StoreSource(
-      SourceBuilder()
-          .SetSourceOrigin(url::Origin::Create(GURL("https://bar.a.example")))
-          .SetSourceEventId(7)
-          .Build());
+  storage()->StoreSource(SourceBuilder()
+                             .SetSourceOrigin(*SuitableOrigin::Deserialize(
+                                 "https://foo.a.example"))
+                             .SetSourceEventId(3)
+                             .Build());
+  storage()->StoreSource(SourceBuilder()
+                             .SetSourceOrigin(*SuitableOrigin::Deserialize(
+                                 "https://foo.a.example"))
+                             .SetSourceEventId(5)
+                             .Build());
+  storage()->StoreSource(SourceBuilder()
+                             .SetSourceOrigin(*SuitableOrigin::Deserialize(
+                                 "https://bar.a.example"))
+                             .SetSourceEventId(7)
+                             .Build());
 
   EXPECT_THAT(storage()->GetActiveSources(),
               ElementsAre(SourceEventIdIs(3u), SourceEventIdIs(5u),
@@ -666,8 +667,8 @@
   // limit of 2.
   EXPECT_EQ(storage()
                 ->StoreSource(SourceBuilder()
-                                  .SetSourceOrigin(url::Origin::Create(
-                                      GURL("https://foo.a.example")))
+                                  .SetSourceOrigin(*SuitableOrigin::Deserialize(
+                                      "https://foo.a.example"))
                                   .SetSourceEventId(9)
                                   .Build())
                 .status,
@@ -675,11 +676,11 @@
 
   // This impression should be stored, because its origin hasn't hit the limit
   // of 2.
-  storage()->StoreSource(
-      SourceBuilder()
-          .SetSourceOrigin(url::Origin::Create(GURL("https://bar.a.example")))
-          .SetSourceEventId(11)
-          .Build());
+  storage()->StoreSource(SourceBuilder()
+                             .SetSourceOrigin(*SuitableOrigin::Deserialize(
+                                 "https://bar.a.example"))
+                             .SetSourceEventId(11)
+                             .Build());
 
   EXPECT_THAT(storage()->GetActiveSources(),
               ElementsAre(SourceEventIdIs(3u), SourceEventIdIs(5u),
@@ -804,7 +805,7 @@
 
   for (int i = 0; i < 10; i++) {
     auto origin =
-        url::Origin::Create(GURL(base::StringPrintf("https://%d.com/", i)));
+        *SuitableOrigin::Deserialize(base::StringPrintf("https://%d.com/", i));
     storage()->StoreSource(SourceBuilder(now)
                                .SetExpiry(base::Days(30))
                                .SetSourceOrigin(origin)
@@ -817,7 +818,7 @@
   // Convert half of them now, half after another day.
   for (int i = 0; i < 5; i++) {
     auto origin =
-        url::Origin::Create(GURL(base::StringPrintf("https://%d.com/", i)));
+        *SuitableOrigin::Deserialize(base::StringPrintf("https://%d.com/", i));
     EXPECT_EQ(
         AttributionTrigger::EventLevelResult::kSuccess,
         MaybeCreateAndStoreEventLevelReport(TriggerBuilder()
@@ -828,7 +829,7 @@
   task_environment_.FastForwardBy(base::Days(1));
   for (int i = 5; i < 10; i++) {
     auto origin =
-        url::Origin::Create(GURL(base::StringPrintf("https://%d.com/", i)));
+        *SuitableOrigin::Deserialize(base::StringPrintf("https://%d.com/", i));
     EXPECT_EQ(
         AttributionTrigger::EventLevelResult::kSuccess,
         MaybeCreateAndStoreEventLevelReport(TriggerBuilder()
@@ -1179,10 +1180,11 @@
     return storage()
         ->StoreSource(
             SourceBuilder()
-                .SetSourceOrigin(url::Origin::Create(GURL(source_origin)))
-                .SetReportingOrigin(url::Origin::Create(GURL(reporting_origin)))
+                .SetSourceOrigin(*SuitableOrigin::Deserialize(source_origin))
+                .SetReportingOrigin(
+                    *SuitableOrigin::Deserialize(reporting_origin))
                 .SetDestinationOrigin(
-                    url::Origin::Create(GURL(destination_origin)))
+                    *SuitableOrigin::Deserialize(destination_origin))
                 .SetExpiry(base::Days(30))
                 .Build())
         .status;
@@ -1225,10 +1227,11 @@
     return storage()
         ->StoreSource(
             SourceBuilder()
-                .SetSourceOrigin(url::Origin::Create(GURL(source_origin)))
-                .SetReportingOrigin(url::Origin::Create(GURL(reporting_origin)))
+                .SetSourceOrigin(*SuitableOrigin::Deserialize(source_origin))
+                .SetReportingOrigin(
+                    *SuitableOrigin::Deserialize(reporting_origin))
                 .SetDestinationOrigin(
-                    url::Origin::Create(GURL(destination_origin)))
+                    *SuitableOrigin::Deserialize(destination_origin))
                 .SetExpiry(expiry)
                 .Build())
         .status;
@@ -1244,14 +1247,14 @@
       store_source("https://s.test", "https://a.r.test", "https://d2.test"),
       StorableSource::Result::kInsufficientUniqueDestinationCapacity);
 
-  EXPECT_EQ(
-      AttributionTrigger::EventLevelResult::kSuccess,
-      MaybeCreateAndStoreEventLevelReport(
-          TriggerBuilder()
-              .SetReportingOrigin(url::Origin::Create(GURL("https://a.r.test")))
-              .SetDestinationOrigin(
-                  url::Origin::Create(GURL("https://d1.test")))
-              .Build()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
+                TriggerBuilder()
+                    .SetReportingOrigin(
+                        *SuitableOrigin::Deserialize("https://a.r.test"))
+                    .SetDestinationOrigin(
+                        *SuitableOrigin::Deserialize("https://d1.test"))
+                    .Build()));
 
   // Allowed by pending, dropped by unexpired (therefore dropped and not stored).
   EXPECT_EQ(
@@ -1269,14 +1272,14 @@
 TEST_F(AttributionStorageTest,
        MaxAttributionDestinationsPerSource_AppliesToNavigationSources) {
   delegate()->set_max_destinations_per_source_site_reporting_origin(1);
-  storage()->StoreSource(
-      SourceBuilder()
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://a.example/")))
-          .Build());
-  storage()->StoreSource(
-      SourceBuilder()
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://b.example")))
-          .Build());
+  storage()->StoreSource(SourceBuilder()
+                             .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                                 "https://a.example/"))
+                             .Build());
+  storage()->StoreSource(SourceBuilder()
+                             .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                                 "https://b.example"))
+                             .Build());
 
   EXPECT_THAT(storage()->GetActiveSources(), SizeIs(1));
 }
@@ -1284,14 +1287,15 @@
 TEST_F(AttributionStorageTest,
        MaxAttributionDestinationsPerSource_CountsAllSourceTypes) {
   delegate()->set_max_destinations_per_source_site_reporting_origin(1);
-  storage()->StoreSource(
-      SourceBuilder()
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://a.example/")))
-          .SetSourceType(AttributionSourceType::kNavigation)
-          .Build());
+  storage()->StoreSource(SourceBuilder()
+                             .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                                 "https://a.example/"))
+                             .SetSourceType(AttributionSourceType::kNavigation)
+                             .Build());
   AttributionStorage::StoreSourceResult result = storage()->StoreSource(
       SourceBuilder()
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://b.example")))
+          .SetDestinationOrigin(
+              *SuitableOrigin::Deserialize("https://b.example"))
           .SetSourceType(AttributionSourceType::kEvent)
           .Build());
   EXPECT_EQ(result.status,
@@ -1308,28 +1312,28 @@
 
   const base::TimeDelta expiry = base::Milliseconds(5);
 
-  storage()->StoreSource(
-      SourceBuilder()
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://a.example/")))
-          .SetSourceType(AttributionSourceType::kNavigation)
-          .SetExpiry(expiry)
-          .Build());
-  storage()->StoreSource(
-      SourceBuilder()
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://b.example")))
-          .SetSourceType(AttributionSourceType::kEvent)
-          .Build());
+  storage()->StoreSource(SourceBuilder()
+                             .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                                 "https://a.example/"))
+                             .SetSourceType(AttributionSourceType::kNavigation)
+                             .SetExpiry(expiry)
+                             .Build());
+  storage()->StoreSource(SourceBuilder()
+                             .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                                 "https://b.example"))
+                             .SetSourceType(AttributionSourceType::kEvent)
+                             .Build());
 
   EXPECT_THAT(storage()->GetActiveSources(), SizeIs(1));
 
   task_environment_.FastForwardBy(expiry);
   EXPECT_THAT(storage()->GetActiveSources(), IsEmpty());
 
-  storage()->StoreSource(
-      SourceBuilder()
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://b.example")))
-          .SetSourceType(AttributionSourceType::kEvent)
-          .Build());
+  storage()->StoreSource(SourceBuilder()
+                             .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                                 "https://b.example"))
+                             .SetSourceType(AttributionSourceType::kEvent)
+                             .Build());
 
   EXPECT_THAT(storage()->GetActiveSources(), SizeIs(1));
 }
@@ -1655,16 +1659,16 @@
 }
 
 TEST_F(AttributionStorageTest, DedupKey_Dedups) {
-  storage()->StoreSource(
-      SourceBuilder()
-          .SetSourceEventId(1)
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://a.example")))
-          .Build());
-  storage()->StoreSource(
-      SourceBuilder()
-          .SetSourceEventId(2)
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://b.example")))
-          .Build());
+  storage()->StoreSource(SourceBuilder()
+                             .SetSourceEventId(1)
+                             .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                                 "https://a.example"))
+                             .Build());
+  storage()->StoreSource(SourceBuilder()
+                             .SetSourceEventId(2)
+                             .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                                 "https://b.example"))
+                             .Build());
   EXPECT_THAT(storage()->GetActiveSources(),
               ElementsAre(DedupKeysAre(IsEmpty()), DedupKeysAre(IsEmpty())));
 
@@ -1672,7 +1676,7 @@
             MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder()
                     .SetDestinationOrigin(
-                        url::Origin::Create(GURL("https://a.example")))
+                        *SuitableOrigin::Deserialize("https://a.example"))
                     .SetDedupKey(11)
                     .SetTriggerData(71)
                     .Build()));
@@ -1683,7 +1687,7 @@
             MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder()
                     .SetDestinationOrigin(
-                        url::Origin::Create(GURL("https://a.example")))
+                        *SuitableOrigin::Deserialize("https://a.example"))
                     .SetDedupKey(12)
                     .SetTriggerData(72)
                     .Build()));
@@ -1694,7 +1698,7 @@
             MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder()
                     .SetDestinationOrigin(
-                        url::Origin::Create(GURL("https://b.example")))
+                        *SuitableOrigin::Deserialize("https://b.example"))
                     .SetDedupKey(12)
                     .SetTriggerData(73)
                     .Build()));
@@ -1702,7 +1706,8 @@
   // Shouldn't be stored because conversion destination and dedup key match.
   auto result = storage()->MaybeCreateAndStoreReport(
       TriggerBuilder()
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://a.example")))
+          .SetDestinationOrigin(
+              *SuitableOrigin::Deserialize("https://a.example"))
           .SetDedupKey(11)
           .SetTriggerData(74)
           .Build());
@@ -1715,7 +1720,7 @@
             MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder()
                     .SetDestinationOrigin(
-                        url::Origin::Create(GURL("https://b.example")))
+                        *SuitableOrigin::Deserialize("https://b.example"))
                     .SetDedupKey(12)
                     .SetTriggerData(75)
                     .Build()));
@@ -1732,11 +1737,11 @@
 }
 
 TEST_F(AttributionStorageTest, DedupKey_DedupsAfterConversionDeletion) {
-  storage()->StoreSource(
-      SourceBuilder()
-          .SetSourceEventId(1)
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://a.example")))
-          .Build());
+  storage()->StoreSource(SourceBuilder()
+                             .SetSourceEventId(1)
+                             .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                                 "https://a.example"))
+                             .Build());
   EXPECT_THAT(storage()->GetActiveSources(), SizeIs(1));
 
   task_environment_.FastForwardBy(base::Milliseconds(1));
@@ -1745,7 +1750,7 @@
             MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder()
                     .SetDestinationOrigin(
-                        url::Origin::Create(GURL("https://a.example")))
+                        *SuitableOrigin::Deserialize("https://a.example"))
                     .SetDedupKey(2)
                     .SetTriggerData(3)
                     .Build()));
@@ -1767,7 +1772,7 @@
             MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder()
                     .SetDestinationOrigin(
-                        url::Origin::Create(GURL("https://a.example")))
+                        *SuitableOrigin::Deserialize("https://a.example"))
                     .SetDedupKey(2)
                     .SetTriggerData(5)
                     .Build()));
@@ -1778,16 +1783,16 @@
 
 TEST_F(AttributionStorageTest, AggregatableDedupKey_Dedups) {
   TestAggregatableSourceProvider provider;
-  storage()->StoreSource(
-      provider.GetBuilder()
-          .SetSourceEventId(1)
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://a.example")))
-          .Build());
-  storage()->StoreSource(
-      provider.GetBuilder()
-          .SetSourceEventId(2)
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://b.example")))
-          .Build());
+  storage()->StoreSource(provider.GetBuilder()
+                             .SetSourceEventId(1)
+                             .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                                 "https://a.example"))
+                             .Build());
+  storage()->StoreSource(provider.GetBuilder()
+                             .SetSourceEventId(2)
+                             .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                                 "https://b.example"))
+                             .Build());
   EXPECT_THAT(storage()->GetActiveSources(),
               ElementsAre(AggregatableDedupKeysAre(IsEmpty()),
                           AggregatableDedupKeysAre(IsEmpty())));
@@ -1796,7 +1801,7 @@
             MaybeCreateAndStoreAggregatableReport(
                 DefaultAggregatableTriggerBuilder()
                     .SetDestinationOrigin(
-                        url::Origin::Create(GURL("https://a.example")))
+                        *SuitableOrigin::Deserialize("https://a.example"))
                     .SetAggregatableDedupKey(11)
                     .SetDebugKey(71)
                     .Build(/*generate_event_trigger_data=*/false)));
@@ -1807,7 +1812,7 @@
             MaybeCreateAndStoreAggregatableReport(
                 DefaultAggregatableTriggerBuilder()
                     .SetDestinationOrigin(
-                        url::Origin::Create(GURL("https://a.example")))
+                        *SuitableOrigin::Deserialize("https://a.example"))
                     .SetAggregatableDedupKey(12)
                     .SetDebugKey(72)
                     .Build(/*generate_event_trigger_data=*/false)));
@@ -1818,7 +1823,7 @@
             MaybeCreateAndStoreAggregatableReport(
                 DefaultAggregatableTriggerBuilder()
                     .SetDestinationOrigin(
-                        url::Origin::Create(GURL("https://b.example")))
+                        *SuitableOrigin::Deserialize("https://b.example"))
                     .SetAggregatableDedupKey(12)
                     .SetDebugKey(73)
                     .Build(/*generate_event_trigger_data=*/false)));
@@ -1828,7 +1833,7 @@
             MaybeCreateAndStoreAggregatableReport(
                 DefaultAggregatableTriggerBuilder()
                     .SetDestinationOrigin(
-                        url::Origin::Create(GURL("https://a.example")))
+                        *SuitableOrigin::Deserialize("https://a.example"))
                     .SetAggregatableDedupKey(11)
                     .SetDebugKey(74)
                     .Build(/*generate_event_trigger_data=*/false)));
@@ -1838,7 +1843,7 @@
             MaybeCreateAndStoreAggregatableReport(
                 DefaultAggregatableTriggerBuilder()
                     .SetDestinationOrigin(
-                        url::Origin::Create(GURL("https://b.example")))
+                        *SuitableOrigin::Deserialize("https://b.example"))
                     .SetAggregatableDedupKey(12)
                     .SetDebugKey(75)
                     .Build(/*generate_event_trigger_data=*/false)));
@@ -1855,12 +1860,12 @@
 
 TEST_F(AttributionStorageTest,
        AggregatableDedupKey_DedupsAfterConversionDeletion) {
-  storage()->StoreSource(
-      TestAggregatableSourceProvider()
-          .GetBuilder()
-          .SetSourceEventId(1)
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://a.example")))
-          .Build());
+  storage()->StoreSource(TestAggregatableSourceProvider()
+                             .GetBuilder()
+                             .SetSourceEventId(1)
+                             .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                                 "https://a.example"))
+                             .Build());
   EXPECT_THAT(storage()->GetActiveSources(), SizeIs(1));
 
   task_environment_.FastForwardBy(base::Milliseconds(1));
@@ -1869,7 +1874,7 @@
             MaybeCreateAndStoreAggregatableReport(
                 DefaultAggregatableTriggerBuilder()
                     .SetDestinationOrigin(
-                        url::Origin::Create(GURL("https://a.example")))
+                        *SuitableOrigin::Deserialize("https://a.example"))
                     .SetAggregatableDedupKey(2)
                     .SetDebugKey(3)
                     .Build(/*generate_event_trigger_data=*/false)));
@@ -1891,7 +1896,7 @@
             MaybeCreateAndStoreAggregatableReport(
                 DefaultAggregatableTriggerBuilder()
                     .SetDestinationOrigin(
-                        url::Origin::Create(GURL("https://a.example")))
+                        *SuitableOrigin::Deserialize("https://a.example"))
                     .SetAggregatableDedupKey(2)
                     .SetDebugKey(5)
                     .Build(/*generate_event_trigger_data=*/false)));
@@ -1901,16 +1906,17 @@
 }
 
 TEST_F(AttributionStorageTest, DedupKey_AggregatableReportNotDedups) {
-  storage()->StoreSource(
-      TestAggregatableSourceProvider()
-          .GetBuilder()
-          .SetSourceEventId(1)
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://a.example")))
-          .Build());
+  storage()->StoreSource(TestAggregatableSourceProvider()
+                             .GetBuilder()
+                             .SetSourceEventId(1)
+                             .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                                 "https://a.example"))
+                             .Build());
 
   auto result = storage()->MaybeCreateAndStoreReport(
       DefaultAggregatableTriggerBuilder()
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://a.example")))
+          .SetDestinationOrigin(
+              *SuitableOrigin::Deserialize("https://a.example"))
           .SetDedupKey(11)
           .Build());
   EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
@@ -1920,7 +1926,8 @@
 
   result = storage()->MaybeCreateAndStoreReport(
       DefaultAggregatableTriggerBuilder()
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://a.example")))
+          .SetDestinationOrigin(
+              *SuitableOrigin::Deserialize("https://a.example"))
           .SetDedupKey(11)
           .Build());
 
@@ -1931,16 +1938,17 @@
 }
 
 TEST_F(AttributionStorageTest, AggregatableDedupKey_EventLevelReportNotDedups) {
-  storage()->StoreSource(
-      TestAggregatableSourceProvider()
-          .GetBuilder()
-          .SetSourceEventId(1)
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://a.example")))
-          .Build());
+  storage()->StoreSource(TestAggregatableSourceProvider()
+                             .GetBuilder()
+                             .SetSourceEventId(1)
+                             .SetDestinationOrigin(*SuitableOrigin::Deserialize(
+                                 "https://a.example"))
+                             .Build());
 
   auto result = storage()->MaybeCreateAndStoreReport(
       DefaultAggregatableTriggerBuilder()
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://a.example")))
+          .SetDestinationOrigin(
+              *SuitableOrigin::Deserialize("https://a.example"))
           .SetAggregatableDedupKey(11)
           .Build());
   EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
@@ -1950,7 +1958,8 @@
 
   result = storage()->MaybeCreateAndStoreReport(
       DefaultAggregatableTriggerBuilder()
-          .SetDestinationOrigin(url::Origin::Create(GURL("https://a.example")))
+          .SetDestinationOrigin(
+              *SuitableOrigin::Deserialize("https://a.example"))
           .SetAggregatableDedupKey(11)
           .Build());
 
@@ -2206,8 +2215,8 @@
 }
 
 TEST_F(AttributionStorageTest, GetNextEventReportTime) {
-  const auto origin_a = url::Origin::Create(GURL("https://a.example/"));
-  const auto origin_b = url::Origin::Create(GURL("https://b.example/"));
+  const auto origin_a = *SuitableOrigin::Deserialize("https://a.example/");
+  const auto origin_b = *SuitableOrigin::Deserialize("https://b.example/");
 
   EXPECT_EQ(storage()->GetNextReportTime(base::Time::Min()), absl::nullopt);
 
@@ -2350,21 +2359,21 @@
 
   auto result = storage()->StoreSource(
       SourceBuilder()
-          .SetReportingOrigin(url::Origin::Create(GURL("https://r1.test")))
+          .SetReportingOrigin(*SuitableOrigin::Deserialize("https://r1.test"))
           .SetDebugKey(1)
           .Build());
   ASSERT_EQ(result.status, StorableSource::Result::kSuccess);
 
   result = storage()->StoreSource(
       SourceBuilder()
-          .SetReportingOrigin(url::Origin::Create(GURL("https://r2.test")))
+          .SetReportingOrigin(*SuitableOrigin::Deserialize("https://r2.test"))
           .SetDebugKey(2)
           .Build());
   ASSERT_EQ(result.status, StorableSource::Result::kSuccess);
 
   result = storage()->StoreSource(
       SourceBuilder()
-          .SetReportingOrigin(url::Origin::Create(GURL("https://r3.test")))
+          .SetReportingOrigin(*SuitableOrigin::Deserialize("https://r3.test"))
           .SetDebugKey(3)
           .Build());
   ASSERT_EQ(result.status, StorableSource::Result::kExcessiveReportingOrigins);
@@ -2384,9 +2393,9 @@
       .max_attributions = std::numeric_limits<int64_t>::max(),
   });
 
-  const auto origin1 = url::Origin::Create(GURL("https://r1.test"));
-  const auto origin2 = url::Origin::Create(GURL("https://r2.test"));
-  const auto origin3 = url::Origin::Create(GURL("https://r3.test"));
+  const auto origin1 = *SuitableOrigin::Deserialize("https://r1.test");
+  const auto origin2 = *SuitableOrigin::Deserialize("https://r2.test");
+  const auto origin3 = *SuitableOrigin::Deserialize("https://r3.test");
 
   SourceBuilder source_builder = TestAggregatableSourceProvider().GetBuilder();
   TriggerBuilder trigger_builder = DefaultAggregatableTriggerBuilder();
@@ -2519,8 +2528,8 @@
        GetAttributionReports_SetsRandomizedTriggerRate) {
   delegate()->set_randomized_response_rates(/*navigation=*/.2, /*event=*/.4);
 
-  const auto origin1 = url::Origin::Create(GURL("https://r1.test"));
-  const auto origin2 = url::Origin::Create(GURL("https://r2.test"));
+  const auto origin1 = *SuitableOrigin::Deserialize("https://r1.test");
+  const auto origin2 = *SuitableOrigin::Deserialize("https://r2.test");
 
   storage()->StoreSource(SourceBuilder()
                              .SetReportingOrigin(origin1)
@@ -2600,8 +2609,8 @@
 TEST_F(AttributionStorageTest, TriggerDataSanitized) {
   delegate()->set_trigger_data_cardinality(/*navigation=*/4, /*event=*/3);
 
-  const auto origin1 = url::Origin::Create(GURL("https://r1.test"));
-  const auto origin2 = url::Origin::Create(GURL("https://r2.test"));
+  const auto origin1 = *SuitableOrigin::Deserialize("https://r1.test");
+  const auto origin2 = *SuitableOrigin::Deserialize("https://r2.test");
 
   storage()->StoreSource(SourceBuilder()
                              .SetReportingOrigin(origin1)
diff --git a/content/browser/attribution_reporting/attribution_test_utils.cc b/content/browser/attribution_reporting/attribution_test_utils.cc
index a10f45e..31faf6e1 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.cc
+++ b/content/browser/attribution_reporting/attribution_test_utils.cc
@@ -40,7 +40,6 @@
 #include "net/base/net_errors.h"
 #include "third_party/abseil-cpp/absl/numeric/int128.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "url/origin.h"
 
 #if BUILDFLAG(IS_ANDROID)
 #include "content/browser/attribution_reporting/attribution_input_event_tracker_android.h"
@@ -500,23 +499,11 @@
   return *this;
 }
 
-SourceBuilder& SourceBuilder::SetSourceOrigin(url::Origin origin) {
-  auto suitable_origin = SuitableOrigin::Create(std::move(origin));
-  CHECK(suitable_origin);
-  return SetSourceOrigin(std::move(*suitable_origin));
-}
-
 SourceBuilder& SourceBuilder::SetSourceOrigin(SuitableOrigin origin) {
   source_origin_ = std::move(origin);
   return *this;
 }
 
-SourceBuilder& SourceBuilder::SetDestinationOrigin(url::Origin origin) {
-  auto suitable_origin = SuitableOrigin::Create(std::move(origin));
-  CHECK(suitable_origin);
-  return SetDestinationOrigin(std::move(*suitable_origin));
-}
-
 SourceBuilder& SourceBuilder::SetDestinationOrigin(SuitableOrigin origin) {
   return SetDestinationOrigins({std::move(origin)});
 }
@@ -528,12 +515,6 @@
   return *this;
 }
 
-SourceBuilder& SourceBuilder::SetReportingOrigin(url::Origin origin) {
-  auto suitable_origin = SuitableOrigin::Create(std::move(origin));
-  CHECK(suitable_origin);
-  return SetReportingOrigin(std::move(*suitable_origin));
-}
-
 SourceBuilder& SourceBuilder::SetReportingOrigin(SuitableOrigin origin) {
   reporting_origin_ = std::move(origin);
   return *this;
@@ -670,25 +651,11 @@
   return *this;
 }
 
-TriggerBuilder& TriggerBuilder::SetDestinationOrigin(
-    url::Origin destination_origin) {
-  auto suitable_origin = SuitableOrigin::Create(std::move(destination_origin));
-  CHECK(suitable_origin);
-  return SetDestinationOrigin(std::move(*suitable_origin));
-}
-
 TriggerBuilder& TriggerBuilder::SetDestinationOrigin(SuitableOrigin origin) {
   destination_origin_ = std::move(origin);
   return *this;
 }
 
-TriggerBuilder& TriggerBuilder::SetReportingOrigin(
-    url::Origin reporting_origin) {
-  auto suitable_origin = SuitableOrigin::Create(std::move(reporting_origin));
-  CHECK(suitable_origin);
-  return SetReportingOrigin(std::move(*suitable_origin));
-}
-
 TriggerBuilder& TriggerBuilder::SetReportingOrigin(SuitableOrigin origin) {
   reporting_origin_ = std::move(origin);
   return *this;
diff --git a/content/browser/attribution_reporting/attribution_test_utils.h b/content/browser/attribution_reporting/attribution_test_utils.h
index 3bd1f28a..e67a33d3 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.h
+++ b/content/browser/attribution_reporting/attribution_test_utils.h
@@ -412,22 +412,13 @@
 
   SourceBuilder& SetSourceEventId(uint64_t source_event_id);
 
-  // TODO(crbug.com/1383580): Remove this method.
-  SourceBuilder& SetSourceOrigin(url::Origin origin);
-
   SourceBuilder& SetSourceOrigin(attribution_reporting::SuitableOrigin);
 
-  // TODO(crbug.com/1383580): Remove this method.
-  SourceBuilder& SetDestinationOrigin(url::Origin origin);
-
   SourceBuilder& SetDestinationOrigin(attribution_reporting::SuitableOrigin);
 
   SourceBuilder& SetDestinationOrigins(
       base::flat_set<attribution_reporting::SuitableOrigin>);
 
-  // TODO(crbug.com/1383580): Remove this method.
-  SourceBuilder& SetReportingOrigin(url::Origin origin);
-
   SourceBuilder& SetReportingOrigin(attribution_reporting::SuitableOrigin);
 
   SourceBuilder& SetSourceType(AttributionSourceType source_type);
@@ -515,14 +506,8 @@
 
   TriggerBuilder& SetEventSourceTriggerData(uint64_t event_source_trigger_data);
 
-  // TODO(crbug.com/1383580): Remove this method.
-  TriggerBuilder& SetDestinationOrigin(url::Origin destination_origin);
-
   TriggerBuilder& SetDestinationOrigin(attribution_reporting::SuitableOrigin);
 
-  // TODO(crbug.com/1383580): Remove this method.
-  TriggerBuilder& SetReportingOrigin(url::Origin reporting_origin);
-
   TriggerBuilder& SetReportingOrigin(attribution_reporting::SuitableOrigin);
 
   TriggerBuilder& SetPriority(int64_t priority);
diff --git a/content/browser/attribution_reporting/rate_limit_table_unittest.cc b/content/browser/attribution_reporting/rate_limit_table_unittest.cc
index 89c49c4..bef9d93 100644
--- a/content/browser/attribution_reporting/rate_limit_table_unittest.cc
+++ b/content/browser/attribution_reporting/rate_limit_table_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/files/file_util.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
+#include "components/attribution_reporting/suitable_origin.h"
 #include "content/browser/attribution_reporting/attribution_info.h"
 #include "content/browser/attribution_reporting/attribution_source_type.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
@@ -30,8 +31,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
-#include "url/gurl.h"
-#include "url/origin.h"
 
 namespace content {
 
@@ -39,6 +38,8 @@
 
 using RateLimitScope = ::content::RateLimitTable::Scope;
 
+using ::attribution_reporting::SuitableOrigin;
+
 using ::testing::_;
 using ::testing::ElementsAre;
 using ::testing::Field;
@@ -83,9 +84,10 @@
     auto source_time = scope == RateLimitScope::kSource ? time : base::Time();
     auto builder = SourceBuilder(source_time);
 
-    builder.SetSourceOrigin(url::Origin::Create(GURL(source_origin)));
-    builder.SetDestinationOrigin(url::Origin::Create(GURL(destination_origin)));
-    builder.SetReportingOrigin(url::Origin::Create(GURL(reporting_origin)));
+    builder.SetSourceOrigin(*SuitableOrigin::Deserialize(source_origin));
+    builder.SetDestinationOrigin(
+        *SuitableOrigin::Deserialize(destination_origin));
+    builder.SetReportingOrigin(*SuitableOrigin::Deserialize(reporting_origin));
     builder.SetExpiry(source_expiry);
 
     return builder;
@@ -637,22 +639,24 @@
   delegate_.set_delete_expired_rate_limits_frequency(base::Minutes(4));
 
   ASSERT_TRUE(table_.AddRateLimitForAttribution(
-      &db_, AttributionInfoBuilder(SourceBuilder()
-                                       .SetSourceOrigin(url::Origin::Create(
-                                           GURL("https://s1.test")))
-                                       .BuildStored())
-                .SetTime(base::Time::Now())
-                .Build()));
+      &db_,
+      AttributionInfoBuilder(
+          SourceBuilder()
+              .SetSourceOrigin(*SuitableOrigin::Deserialize("https://s1.test"))
+              .BuildStored())
+          .SetTime(base::Time::Now())
+          .Build()));
 
   task_environment_.FastForwardBy(base::Minutes(4) - base::Milliseconds(1));
 
   ASSERT_TRUE(table_.AddRateLimitForAttribution(
-      &db_, AttributionInfoBuilder(SourceBuilder()
-                                       .SetSourceOrigin(url::Origin::Create(
-                                           GURL("https://s2.test")))
-                                       .BuildStored())
-                .SetTime(base::Time::Now())
-                .Build()));
+      &db_,
+      AttributionInfoBuilder(
+          SourceBuilder()
+              .SetSourceOrigin(*SuitableOrigin::Deserialize("https://s2.test"))
+              .BuildStored())
+          .SetTime(base::Time::Now())
+          .Build()));
 
   // Neither row has expired at this point.
   ASSERT_THAT(GetRateLimitRows(), SizeIs(2));
@@ -661,12 +665,13 @@
   task_environment_.FastForwardBy(base::Milliseconds(1));
 
   ASSERT_TRUE(table_.AddRateLimitForAttribution(
-      &db_, AttributionInfoBuilder(SourceBuilder()
-                                       .SetSourceOrigin(url::Origin::Create(
-                                           GURL("https://s3.test")))
-                                       .BuildStored())
-                .SetTime(base::Time::Now())
-                .Build()));
+      &db_,
+      AttributionInfoBuilder(
+          SourceBuilder()
+              .SetSourceOrigin(*SuitableOrigin::Deserialize("https://s3.test"))
+              .BuildStored())
+          .SetTime(base::Time::Now())
+          .Build()));
 
   // The first row should be expired at this point.
   ASSERT_THAT(
@@ -688,22 +693,25 @@
   delegate_.set_delete_expired_rate_limits_frequency(base::Minutes(4));
 
   ASSERT_TRUE(table_.AddRateLimitForSource(
-      &db_, SourceBuilder()
-                .SetSourceOrigin(url::Origin::Create(GURL("https://s1.test")))
-                .BuildStored()));
+      &db_,
+      SourceBuilder()
+          .SetSourceOrigin(*SuitableOrigin::Deserialize("https://s1.test"))
+          .BuildStored()));
 
   ASSERT_TRUE(table_.AddRateLimitForSource(
-      &db_, SourceBuilder()
-                .SetSourceOrigin(url::Origin::Create(GURL("https://s2.test")))
-                .SetExpiry(base::Minutes(5))
-                .BuildStored()));
+      &db_,
+      SourceBuilder()
+          .SetSourceOrigin(*SuitableOrigin::Deserialize("https://s2.test"))
+          .SetExpiry(base::Minutes(5))
+          .BuildStored()));
 
   task_environment_.FastForwardBy(base::Minutes(4) - base::Milliseconds(1));
 
   ASSERT_TRUE(table_.AddRateLimitForSource(
-      &db_, SourceBuilder()
-                .SetSourceOrigin(url::Origin::Create(GURL("https://s3.test")))
-                .BuildStored()));
+      &db_,
+      SourceBuilder()
+          .SetSourceOrigin(*SuitableOrigin::Deserialize("https://s3.test"))
+          .BuildStored()));
 
   // No row has expired at this point.
   ASSERT_THAT(GetRateLimitRows(), SizeIs(3));
@@ -712,9 +720,10 @@
   task_environment_.FastForwardBy(base::Milliseconds(1));
 
   ASSERT_TRUE(table_.AddRateLimitForSource(
-      &db_, SourceBuilder()
-                .SetSourceOrigin(url::Origin::Create(GURL("https://s4.test")))
-                .BuildStored()));
+      &db_,
+      SourceBuilder()
+          .SetSourceOrigin(*SuitableOrigin::Deserialize("https://s4.test"))
+          .BuildStored()));
 
   // The first row should be expired at this point. The second row is not
   // expired since the source is not expired yet.
diff --git a/content/browser/file_system_access/features.cc b/content/browser/file_system_access/features.cc
index 1750b88..b77f98f 100644
--- a/content/browser/file_system_access/features.cc
+++ b/content/browser/file_system_access/features.cc
@@ -17,6 +17,13 @@
              "FileSystemAccessDoNotOverwriteOnMove",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// TODO(crbug.com/1114923): Remove this flag eventually.
+// When enabled, the remove() method is enabled. Otherwise, throws a
+// NotSupportedError DomException.
+BASE_FEATURE(kFileSystemAccessRemove,
+             "FileSystemAccessRemove",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 // TODO(crbug.com/1254078): Remove this flag eventually.
 // When enabled, removeEntry() acquires an exclusive lock (as opposed to a
 // shared lock when disabled).
diff --git a/content/browser/file_system_access/features.h b/content/browser/file_system_access/features.h
index efd9416..2c4c82e 100644
--- a/content/browser/file_system_access/features.h
+++ b/content/browser/file_system_access/features.h
@@ -15,6 +15,7 @@
 
 // Alphabetical:
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFileSystemAccessDoNotOverwriteOnMove);
+CONTENT_EXPORT BASE_DECLARE_FEATURE(kFileSystemAccessRemove);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFileSystemAccessRemoveEntryExclusiveLock);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(
     kFileSystemAccessRenameWithoutParentAccessRequiresUserActivation);
diff --git a/content/browser/file_system_access/file_system_access_handle_base.cc b/content/browser/file_system_access/file_system_access_handle_base.cc
index 061c69d6..e452c56 100644
--- a/content/browser/file_system_access/file_system_access_handle_base.cc
+++ b/content/browser/file_system_access/file_system_access_handle_base.cc
@@ -539,6 +539,12 @@
   DCHECK_EQ(GetWritePermissionStatus(),
             blink::mojom::PermissionStatus::GRANTED);
 
+  if (!base::FeatureList::IsEnabled(features::kFileSystemAccessRemove)) {
+    std::move(callback).Run(file_system_access_error::FromStatus(
+        blink::mojom::FileSystemAccessStatus::kNotSupportedError));
+    return;
+  }
+
   // A locked file cannot be removed. Acquire a write lock and release it
   // after the remove operation completes.
   auto write_lock = manager()->TakeWriteLock(url, lock_type);
diff --git a/content/browser/interest_group/ad_auction_service_impl.cc b/content/browser/interest_group/ad_auction_service_impl.cc
index 5d72cf2..d015a62f 100644
--- a/content/browser/interest_group/ad_auction_service_impl.cc
+++ b/content/browser/interest_group/ad_auction_service_impl.cc
@@ -152,6 +152,7 @@
     const std::vector<GURL>& debug_loss_report_urls,
     const std::vector<GURL>& debug_win_report_urls,
     const blink::InterestGroupSet& interest_groups_that_bid,
+    base::flat_set<std::string> k_anon_keys_to_join,
     const network::mojom::ClientSecurityStatePtr& client_security_state,
     scoped_refptr<network::WrapperSharedURLLoaderFactory>
         trusted_url_loader_factory) {
@@ -165,6 +166,8 @@
   interest_group_manager->RecordInterestGroupBids(interest_groups_that_bid);
   interest_group_manager->RecordInterestGroupWin(winning_group_key,
                                                  winning_group_ad_metadata);
+  interest_group_manager->RegisterAdKeysAsJoined(
+      std::move(k_anon_keys_to_join));
 
   SendPrivateAggregationRequests(private_aggregation_manager, main_frame_origin,
                                  std::move(*private_aggregation_requests));
@@ -615,15 +618,9 @@
              std::vector<auction_worklet::mojom::PrivateAggregationRequestPtr>>
         private_aggregation_requests,
     blink::InterestGroupSet interest_groups_that_bid,
-    absl::optional<GURL> render_url_without_kanon_enforced,
-    std::vector<GURL> ad_component_urls_without_kanon_enforced,
-    absl::optional<GURL> render_url_with_kanon_simulated,
-    std::vector<GURL> ad_component_urls_with_kanon_simulated,
+    base::flat_set<std::string> k_anon_keys_to_join,
     std::vector<std::string> errors,
     std::unique_ptr<InterestGroupAuctionReporter> reporter) {
-  // TODO(https://crbug.com/1234419): Use the various ..._kanon_... URLs as
-  // appropriate (to update k-anon info, metrics?)
-
   // Remove `auction` from `auctions_` but tmeporarily keep it alive - on
   // success, it owns a AuctionWorkletManager::WorkletHandle for the top-level
   // auction, which `reporter` can reuse once started. Fine to delete after
@@ -650,6 +647,8 @@
       SendPrivateAggregationRequests(private_aggregation_manager_,
                                      main_frame_origin_,
                                      std::move(private_aggregation_requests));
+      GetInterestGroupManager().RegisterAdKeysAsJoined(
+          std::move(k_anon_keys_to_join));
       if (!interest_groups_that_bid.empty()) {
         GetInterestGroupManager().RecordInterestGroupBids(
             interest_groups_that_bid);
@@ -691,7 +690,7 @@
       std::move(*winning_group_key), std::move(*render_url),
       std::move(ad_component_urls), std::move(winning_group_ad_metadata),
       std::move(debug_loss_report_urls), std::move(debug_win_report_urls),
-      std::move(interest_groups_that_bid)));
+      std::move(interest_groups_that_bid), std::move(k_anon_keys_to_join)));
   if (auction_result_metrics) {
     auction_result_metrics->ReportAuctionResult(
         AdAuctionResultMetrics::AuctionResult::kSucceeded);
@@ -708,7 +707,8 @@
     std::string winning_group_ad_metadata,
     std::vector<GURL> debug_loss_report_urls,
     std::vector<GURL> debug_win_report_urls,
-    blink::InterestGroupSet interest_groups_that_bid) {
+    blink::InterestGroupSet interest_groups_that_bid,
+    base::flat_set<std::string> k_anon_keys_to_join) {
   // Forward debug information to devtools.
   //
   // TODO(https://crbug.com/1394777): Ideally this will share code with the
@@ -761,7 +761,8 @@
                       std::move(private_aggregation_requests))),
               std::move(report_urls), std::move(debug_win_report_urls),
               std::move(debug_loss_report_urls),
-              std::move(interest_groups_that_bid), GetClientSecurityState(),
+              std::move(interest_groups_that_bid),
+              std::move(k_anon_keys_to_join), GetClientSecurityState(),
               GetRefCountedTrustedURLLoaderFactory()),
           ad_component_urls, ad_beacon_map);
 
diff --git a/content/browser/interest_group/ad_auction_service_impl.h b/content/browser/interest_group/ad_auction_service_impl.h
index 4a8d37f..5ac7a25 100644
--- a/content/browser/interest_group/ad_auction_service_impl.h
+++ b/content/browser/interest_group/ad_auction_service_impl.h
@@ -133,10 +133,7 @@
           std::vector<auction_worklet::mojom::PrivateAggregationRequestPtr>>
           private_aggregation_requests,
       blink::InterestGroupSet interest_groups_that_bid,
-      absl::optional<GURL> render_url_without_kanon_enforced,
-      std::vector<GURL> ad_component_urls_without_kanon_enforced,
-      absl::optional<GURL> render_url_with_kanon_simulated,
-      std::vector<GURL> ad_component_urls_with_kanon_simulated,
+      base::flat_set<std::string> k_anon_keys_to_join,
       std::vector<std::string> errors,
       std::unique_ptr<InterestGroupAuctionReporter> reporter);
 
@@ -149,7 +146,8 @@
                           std::string winning_group_ad_metadata,
                           std::vector<GURL> debug_loss_report_urls,
                           std::vector<GURL> debug_win_report_urls,
-                          blink::InterestGroupSet interest_groups_that_bid);
+                          blink::InterestGroupSet interest_groups_that_bid,
+                          base::flat_set<std::string> k_anon_keys_to_join);
 
   // Calls LogWebFeatureForCurrentPage() for the frame to inform it of FLEDGE
   // private aggregation API usage, if `private_aggregation_requests` is
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 aa4bb83..8a2ae8b 100644
--- a/content/browser/interest_group/ad_auction_service_impl_unittest.cc
+++ b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
@@ -531,6 +531,36 @@
       auction_worklet_services_;
 };
 
+class TestKAnonymityServiceDelegate : public KAnonymityServiceDelegate {
+ public:
+  TestKAnonymityServiceDelegate() = default;
+
+  void JoinSet(std::string id,
+               base::OnceCallback<void(bool)> callback) override {
+    join_ids_.push_back(id);
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), true));
+  }
+
+  void QuerySets(
+      std::vector<std::string> ids,
+      base::OnceCallback<void(std::vector<bool>)> callback) override {
+    // Return that nothing is k-anonymous.
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  std::vector<bool>(ids.size(), false)));
+  }
+
+  base::TimeDelta GetJoinInterval() override { return base::Seconds(1); }
+
+  base::TimeDelta GetQueryInterval() override { return base::Seconds(1); }
+
+  const std::vector<std::string>& join_ids() const { return join_ids_; }
+
+ private:
+  std::vector<std::string> join_ids_;
+};
+
 }  // namespace
 
 // Tests the interest group management functionality of AdAuctionServiceImpl --
@@ -569,6 +599,9 @@
     // the auction "processes" in-process instead.
     manager_->set_auction_process_manager_for_testing(
         std::make_unique<SameProcessAuctionProcessManager>());
+    manager_->set_k_anonymity_manager_for_testing(
+        std::make_unique<InterestGroupKAnonymityManager>(manager_.get(),
+                                                         &k_anon_delegate_));
   }
 
   void TearDown() override {
@@ -841,6 +874,10 @@
   // Destroy the AdAuctionService, if there is one.
   void DestroyAdAuctionService() { ad_auction_service_.reset(); }
 
+  const std::vector<std::string>& GetKAnonJoinedIds() const {
+    return k_anon_delegate_.join_ids();
+  }
+
  protected:
   const GURL kUrlA = GURL(kOriginStringA);
   const url::Origin kOriginA = url::Origin::Create(kUrlA);
@@ -866,6 +903,7 @@
   base::test::ScopedFeatureList fenced_frame_feature_list_;
 
   AllowInterestGroupContentBrowserClient content_browser_client_;
+  TestKAnonymityServiceDelegate k_anon_delegate_;
   raw_ptr<ContentBrowserClient> old_content_browser_client_ = nullptr;
   raw_ptr<InterestGroupManagerImpl> manager_;
   data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
@@ -3824,6 +3862,14 @@
   ASSERT_EQ(
       R"({"render_url":"https://example.com/render"})",
       storage_interest_group->bidding_browser_signals->prev_wins[0]->ad_json);
+
+  // The auction should also trigger a k-anon "join" for the winning ad.
+  EXPECT_THAT(GetKAnonJoinedIds(),
+              ::testing::UnorderedElementsAre(
+                  KAnonKeyForAdBid(interest_group,
+                                   interest_group.ads.value()[0].render_url),
+                  KAnonKeyForAdNameReporting(interest_group,
+                                             interest_group.ads.value()[0])));
 }
 
 // Add an interest group, and run an ad auction. Seller rejects the bid. Bid
@@ -3872,6 +3918,9 @@
   EXPECT_EQ(1, storage_interest_group->bidding_browser_signals->bid_count);
   EXPECT_EQ(0u,
             storage_interest_group->bidding_browser_signals->prev_wins.size());
+
+  // The auction should not trigger any k-anon "joins".
+  EXPECT_THAT(GetKAnonJoinedIds(), ::testing::UnorderedElementsAre());
 }
 
 // Run ad auction when number of urn mappings has reached limit, the action
@@ -6296,4 +6345,138 @@
   EXPECT_EQ(auction_result, absl::nullopt);
 }
 
+class AdAuctionServiceImplKAnonTest
+    : public AdAuctionServiceImplTest,
+      public ::testing::WithParamInterface<
+          auction_worklet::mojom::KAnonymityBidMode> {
+ public:
+  AdAuctionServiceImplKAnonTest() {
+    std::vector<base::test::FeatureRef> enabled_features;
+    std::vector<base::test::FeatureRef> disabled_features;
+
+    switch (kanon_mode()) {
+      case auction_worklet::mojom::KAnonymityBidMode::kEnforce:
+        enabled_features.push_back(blink::features::kFledgeConsiderKAnonymity);
+        enabled_features.push_back(blink::features::kFledgeEnforceKAnonymity);
+        break;
+      case auction_worklet::mojom::KAnonymityBidMode::kSimulate:
+        enabled_features.push_back(blink::features::kFledgeConsiderKAnonymity);
+        disabled_features.push_back(blink::features::kFledgeEnforceKAnonymity);
+        break;
+      case auction_worklet::mojom::KAnonymityBidMode::kNone:
+        disabled_features.push_back(blink::features::kFledgeConsiderKAnonymity);
+        disabled_features.push_back(blink::features::kFledgeEnforceKAnonymity);
+        break;
+    }
+
+    scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
+  }
+
+  auction_worklet::mojom::KAnonymityBidMode kanon_mode() { return GetParam(); }
+
+ protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Add an interest group with a non-k-anonymous ad and run an ad auction.
+TEST_P(AdAuctionServiceImplKAnonTest, RunAdAuctionNotKAnon) {
+  constexpr char kBiddingScript[] = R"(
+function generateBid(
+    interestGroup, auctionSignals, perBuyerSignals, trustedBiddingSignals,
+    browserSignals) {
+  return {'ad': 'example', 'bid': 1, 'render': 'https://example.com/render'};
+}
+)";
+
+  constexpr char kDecisionScript[] = R"(
+function scoreAd(
+    adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) {
+  return bid;
+}
+)";
+
+  network_responder_->RegisterScriptResponse(kBiddingUrlPath, kBiddingScript);
+  network_responder_->RegisterScriptResponse(kDecisionUrlPath, kDecisionScript);
+
+  blink::InterestGroup interest_group = CreateInterestGroup();
+  interest_group.bidding_url = kUrlA.Resolve(kBiddingUrlPath);
+  interest_group.ads.emplace();
+  blink::InterestGroup::Ad ad(
+      /*render_url=*/GURL("https://example.com/render"),
+      /*metadata=*/absl::nullopt);
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
+  EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
+
+  blink::AuctionConfig auction_config;
+  auction_config.seller = kOriginA;
+  auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  auction_config.non_shared_params.interest_group_buyers = {kOriginA};
+  absl::optional<GURL> auction_result = RunAdAuctionAndFlush(auction_config);
+  switch (kanon_mode()) {
+    case auction_worklet::mojom::KAnonymityBidMode::kNone:
+    case auction_worklet::mojom::KAnonymityBidMode::kSimulate: {
+      ASSERT_NE(auction_result, absl::nullopt);
+      EXPECT_EQ(ConvertFencedFrameURNToURL(*auction_result),
+                GURL("https://example.com/render"));
+
+      // Running the auction alone should not result in updating the interest
+      // group's bid count, previous win list or trigger k-anon joins, no matter
+      // how much time passes.
+      task_environment()->RunUntilIdle();
+      auto storage_interest_group =
+          GetInterestGroup(interest_group.owner, interest_group.name);
+      ASSERT_TRUE(storage_interest_group);
+      EXPECT_EQ(0, storage_interest_group->bidding_browser_signals->bid_count);
+      EXPECT_EQ(
+          0u,
+          storage_interest_group->bidding_browser_signals->prev_wins.size());
+      EXPECT_THAT(GetKAnonJoinedIds(), ::testing::UnorderedElementsAre());
+
+      // Invoking the URN callback (which is done when the result is loaded in a
+      // frame) updates those fields.
+      InvokeCallbackForURN(*auction_result);
+      storage_interest_group =
+          GetInterestGroup(interest_group.owner, interest_group.name);
+      ASSERT_TRUE(storage_interest_group);
+      EXPECT_EQ(1, storage_interest_group->bidding_browser_signals->bid_count);
+      ASSERT_EQ(
+          1u,
+          storage_interest_group->bidding_browser_signals->prev_wins.size());
+      ASSERT_EQ(R"({"render_url":"https://example.com/render"})",
+                storage_interest_group->bidding_browser_signals->prev_wins[0]
+                    ->ad_json);
+      EXPECT_THAT(
+          GetKAnonJoinedIds(),
+          ::testing::UnorderedElementsAre(
+              KAnonKeyForAdBid(interest_group,
+                               interest_group.ads.value()[0].render_url),
+              KAnonKeyForAdNameReporting(interest_group,
+                                         interest_group.ads.value()[0])));
+      break;
+    }
+    case auction_worklet::mojom::KAnonymityBidMode::kEnforce: {
+      // The auction should fail because there were no k-anonymous bids.
+      // Since the auction failed, everything should update immediately.
+      EXPECT_FALSE(auction_result);
+      task_environment()->RunUntilIdle();
+      EXPECT_THAT(
+          GetKAnonJoinedIds(),
+          ::testing::UnorderedElementsAre(
+              KAnonKeyForAdBid(interest_group,
+                               interest_group.ads.value()[0].render_url),
+              KAnonKeyForAdNameReporting(interest_group,
+                                         interest_group.ads.value()[0])));
+      break;
+    }
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    /* no label */,
+    AdAuctionServiceImplKAnonTest,
+    ::testing::Values(auction_worklet::mojom::KAnonymityBidMode::kNone,
+                      auction_worklet::mojom::KAnonymityBidMode::kSimulate,
+                      auction_worklet::mojom::KAnonymityBidMode::kEnforce));
+
 }  // namespace content
diff --git a/content/browser/interest_group/auction_runner.cc b/content/browser/interest_group/auction_runner.cc
index ec6cdca..fc65509 100644
--- a/content/browser/interest_group/auction_runner.cc
+++ b/content/browser/interest_group/auction_runner.cc
@@ -86,25 +86,6 @@
 
   UpdateInterestGroupsPostAuction();
 
-  // Most kinds of failures here mean that the auction could not complete at
-  // all (such as seller worklet process crashing, page getting unloaded, etc.),
-  // but there plain not being a winner gets reported as a failure, too. In
-  // that case, however, it's possible that there would be a winner with or
-  // without k-anon enforcement (depending on the kanon_mode) that needs to be
-  // reported to the client.
-  absl::optional<InterestGroupAuction::AuctionResult> result =
-      auction_.final_auction_result();
-  bool may_have_valid_kanon_info =
-      result &&
-      (*result == InterestGroupAuction::AuctionResult::kAllBidsRejected ||
-       *result == InterestGroupAuction::AuctionResult::kNoBids);
-
-  bool report_kanon_enforce =
-      !manually_aborted && may_have_valid_kanon_info &&
-      (kanon_mode_ == auction_worklet::mojom::KAnonymityBidMode::kEnforce);
-  bool report_kanon_sim =
-      !manually_aborted && may_have_valid_kanon_info &&
-      (kanon_mode_ == auction_worklet::mojom::KAnonymityBidMode::kSimulate);
   std::move(callback_).Run(
       this, manually_aborted,
       /*winning_group_key=*/absl::nullopt,
@@ -113,18 +94,7 @@
       /*winning_group_ad_metadata=*/std::string(),
       std::move(debug_loss_report_urls), std::move(debug_win_report_urls),
       auction_.TakePrivateAggregationRequests(),
-      std::move(interest_groups_that_bid),
-      /*render_url_without_kanon_enforced=*/
-      report_kanon_enforce ? auction_.TakeRenderUrlWithoutKAnonEnforced()
-                           : absl::nullopt,
-      /*ad_component_urls_without_kanon_enforced=*/
-      report_kanon_enforce ? auction_.TakeComponentUrlsWithoutKAnonEnforced()
-                           : std::vector<GURL>(),
-      /*render_url_with_kanon_simulated=*/
-      report_kanon_sim ? auction_.TakeSimulatedKAnonRenderUrl() : absl::nullopt,
-      /*ad_component_urls_with_kanon_simulated=*/
-      report_kanon_sim ? auction_.TakeSimulatedKAnonComponentUrls()
-                       : std::vector<GURL>(),
+      std::move(interest_groups_that_bid), auction_.GetKAnonKeysToJoin(),
       auction_.TakeErrors(), /*reporter=*/nullptr);
 }
 
@@ -203,12 +173,6 @@
   blink::InterestGroupKey winning_group_key(
       {winning_group.owner, winning_group.name});
 
-  // TODO(https://crbug.com/1396068): Only do this if/when the winning ad is
-  // loaded in a frame.
-  interest_group_manager_->RegisterAdAsWon(
-      *auction_.top_bid()->bid->interest_group,
-      *auction_.top_bid()->bid->bid_ad);
-
   std::vector<GURL> debug_win_report_urls;
   std::vector<GURL> debug_loss_report_urls;
   auction_.TakeDebugReportUrls(debug_win_report_urls, debug_loss_report_urls);
@@ -222,10 +186,6 @@
   DCHECK(reporter);
 
   state_ = State::kSucceeded;
-  bool in_kanon_enforce =
-      (kanon_mode_ == auction_worklet::mojom::KAnonymityBidMode::kEnforce);
-  bool in_kanon_sim =
-      (kanon_mode_ == auction_worklet::mojom::KAnonymityBidMode::kSimulate);
   std::move(callback_).Run(
       this, /*manually_aborted=*/false, std::move(winning_group_key),
       auction_.top_bid()->bid->render_url,
@@ -234,18 +194,7 @@
       std::move(debug_win_report_urls),
       // In this case, the reporter has all the private aggregation requests.
       std::map<url::Origin, PrivateAggregationRequests>(),
-      std::move(interest_groups_that_bid),
-      /*render_url_without_kanon_enforced=*/
-      in_kanon_enforce ? auction_.TakeRenderUrlWithoutKAnonEnforced()
-                       : absl::nullopt,
-      /*ad_component_urls_without_kanon_enforced=*/
-      in_kanon_enforce ? auction_.TakeComponentUrlsWithoutKAnonEnforced()
-                       : std::vector<GURL>(),
-      /*render_url_with_kanon_simulated=*/
-      in_kanon_sim ? auction_.TakeSimulatedKAnonRenderUrl() : absl::nullopt,
-      /*ad_component_urls_with_kanon_simulated=*/
-      in_kanon_sim ? auction_.TakeSimulatedKAnonComponentUrls()
-                   : std::vector<GURL>(),
+      std::move(interest_groups_that_bid), auction_.GetKAnonKeysToJoin(),
       std::move(errors), std::move(reporter));
 }
 
diff --git a/content/browser/interest_group/auction_runner.h b/content/browser/interest_group/auction_runner.h
index ae7e73e5..83edc49f 100644
--- a/content/browser/interest_group/auction_runner.h
+++ b/content/browser/interest_group/auction_runner.h
@@ -100,10 +100,7 @@
       std::map<url::Origin, PrivateAggregationRequests>
           private_aggregation_requests,
       blink::InterestGroupSet interest_groups_that_bid,
-      absl::optional<GURL> render_url_without_kanon_enforced,
-      std::vector<GURL> ad_component_urls_without_kanon_enforced,
-      absl::optional<GURL> render_url_with_kanon_simulated,
-      std::vector<GURL> ad_component_urls_with_kanon_simulated,
+      base::flat_set<std::string> k_anon_keys_to_join,
       std::vector<std::string> errors,
       std::unique_ptr<InterestGroupAuctionReporter>
           interest_group_auction_reporter)>;
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index 5031bbd..cb415e6 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -965,7 +965,7 @@
                     StorageInterestGroup& group) {
   group.bidding_ads_kanon.emplace_back();
   group.bidding_ads_kanon.back().key =
-      KAnonKeyForAdBid(group.interest_group, ad);
+      KAnonKeyForAdBid(group.interest_group, ad.render_url);
   DCHECK_EQ(GURL(url),
             RenderUrlFromKAnonKeyForAdBid(group.bidding_ads_kanon.back().key));
   group.bidding_ads_kanon.back().is_k_anonymous = true;
@@ -1666,10 +1666,7 @@
     std::map<url::Origin, PrivateAggregationRequests>
         private_aggregation_requests;
     blink::InterestGroupSet interest_groups_that_bid;
-    absl::optional<GURL> render_url_without_kanon_enforced;
-    std::vector<GURL> ad_component_urls_without_kanon_enforced;
-    absl::optional<GURL> render_url_with_kanon_simulated;
-    std::vector<GURL> ad_component_urls_with_kanon_simulated;
+    base::flat_set<std::string> k_anon_keys_to_join;
 
     std::vector<std::string> errors;
   };
@@ -1884,10 +1881,7 @@
       std::map<url::Origin, PrivateAggregationRequests>
           private_aggregation_requests,
       blink::InterestGroupSet interest_groups_that_bid,
-      absl::optional<GURL> render_url_without_kanon_enforced,
-      std::vector<GURL> ad_component_urls_without_kanon_enforced,
-      absl::optional<GURL> render_url_with_kanon_simulated,
-      std::vector<GURL> ad_component_urls_with_kanon_simulated,
+      base::flat_set<std::string> k_anon_keys_to_join,
       std::vector<std::string> errors,
       std::unique_ptr<InterestGroupAuctionReporter> reporter) {
     DCHECK(auction_run_loop_);
@@ -1915,14 +1909,7 @@
     result_.interest_groups_that_bid = std::move(interest_groups_that_bid);
     result_.private_aggregation_requests =
         std::move(private_aggregation_requests);
-    result_.render_url_without_kanon_enforced =
-        std::move(render_url_without_kanon_enforced);
-    result_.ad_component_urls_without_kanon_enforced =
-        std::move(ad_component_urls_without_kanon_enforced);
-    result_.render_url_with_kanon_simulated =
-        std::move(render_url_with_kanon_simulated);
-    result_.ad_component_urls_with_kanon_simulated =
-        std::move(ad_component_urls_with_kanon_simulated);
+    result_.k_anon_keys_to_join = std::move(k_anon_keys_to_join);
 
     if (!reporter) {
       EXPECT_FALSE(result_.winning_group_id);
@@ -10810,21 +10797,25 @@
   // No k-anon authorizations.
   StartAuction(kSellerUrl, bidders);
   auction_run_loop_->Run();
+  EXPECT_THAT(
+      result_.k_anon_keys_to_join,
+      testing::UnorderedElementsAre(
+          KAnonKeyForAdBid(bidders[0].interest_group,
+                           bidders[0].interest_group.ads.value()[0].render_url),
+          KAnonKeyForAdNameReporting(
+              bidders[0].interest_group,
+              bidders[0].interest_group.ads.value()[0])));
+  histogram_tester_->ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon", false, 1);
   switch (kanon_mode()) {
     case auction_worklet::mojom::KAnonymityBidMode::kNone:
       ASSERT_TRUE(result_.ad_url.has_value());
       EXPECT_EQ(GURL("https://ad1.com"), result_.ad_url.value());
-      EXPECT_FALSE(result_.render_url_without_kanon_enforced.has_value());
-      EXPECT_FALSE(result_.render_url_with_kanon_simulated.has_value());
       EXPECT_THAT(result_.errors, testing::ElementsAre());
       break;
 
     case auction_worklet::mojom::KAnonymityBidMode::kEnforce:
       EXPECT_FALSE(result_.ad_url.has_value());
-      ASSERT_TRUE(result_.render_url_without_kanon_enforced.has_value());
-      EXPECT_EQ(GURL("https://ad1.com"),
-                result_.render_url_without_kanon_enforced.value());
-      EXPECT_FALSE(result_.render_url_with_kanon_simulated.has_value());
       EXPECT_THAT(
           result_.errors,
           testing::ElementsAre(
@@ -10835,8 +10826,6 @@
     case auction_worklet::mojom::KAnonymityBidMode::kSimulate:
       ASSERT_TRUE(result_.ad_url.has_value());
       EXPECT_EQ(GURL("https://ad1.com"), result_.ad_url.value());
-      EXPECT_FALSE(result_.render_url_without_kanon_enforced.has_value());
-      EXPECT_FALSE(result_.render_url_with_kanon_simulated.has_value());
       EXPECT_THAT(result_.errors, testing::ElementsAre());
       break;
   }
@@ -10866,29 +10855,28 @@
   EXPECT_THAT(result_.errors, testing::ElementsAre());
   ASSERT_TRUE(result_.ad_url.has_value());
   EXPECT_EQ(GURL("https://ad1.com"), result_.ad_url.value());
-  switch (kanon_mode()) {
-    case auction_worklet::mojom::KAnonymityBidMode::kNone:
-      EXPECT_FALSE(result_.render_url_without_kanon_enforced.has_value());
-      EXPECT_FALSE(result_.render_url_with_kanon_simulated.has_value());
-      break;
-
-    case auction_worklet::mojom::KAnonymityBidMode::kEnforce:
-      ASSERT_TRUE(result_.render_url_without_kanon_enforced.has_value());
-      EXPECT_EQ(GURL("https://ad1.com"),
-                result_.render_url_without_kanon_enforced.value());
-      EXPECT_FALSE(result_.render_url_with_kanon_simulated.has_value());
-      EXPECT_THAT(result_.errors, testing::ElementsAre());
-      break;
-
-    case auction_worklet::mojom::KAnonymityBidMode::kSimulate:
-      EXPECT_FALSE(result_.render_url_without_kanon_enforced.has_value());
-      ASSERT_TRUE(result_.render_url_with_kanon_simulated.has_value());
-      EXPECT_EQ(GURL("https://ad1.com"),
-                result_.render_url_with_kanon_simulated.value());
-      break;
-  }
+  EXPECT_THAT(result_.errors, testing::ElementsAre());
+  EXPECT_THAT(
+      result_.k_anon_keys_to_join,
+      testing::UnorderedElementsAre(
+          KAnonKeyForAdBid(bidders[0].interest_group,
+                           bidders[0].interest_group.ads.value()[0].render_url),
+          KAnonKeyForAdNameReporting(
+              bidders[0].interest_group,
+              bidders[0].interest_group.ads.value()[0])));
+  EXPECT_THAT(result_.errors, testing::ElementsAre());
+  histogram_tester_->ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon",
+      kanon_mode() != auction_worklet::mojom::KAnonymityBidMode::kNone, 1);
 }
 
+// Test that k-anonymity for ads with ad components is handled correctly:
+//  - All components must be k-anonymous to be eligible.
+//  - All components of the winner will be reported as joined.
+// Runs an auction with two groups where each gives a bid with two component ads
+// and all ad URLs except one component ad URL of the second bidder are
+// k-anonymous. When k-anonymity is enforced the first interest group should
+// win, despite having a lower bid.
 TEST_P(AuctionRunnerKAnonTest, ComponentURLs) {
   auction_worklet::AddJavascriptResponse(
       &url_loader_factory_, kBidder1Url,
@@ -10926,12 +10914,35 @@
   AuthorizeKAnon(bidders[1].interest_group.ad_components.value()[0],
                  "https://ad2.com/1", bidders[1]);
 
+  std::vector<std::string> ad1_k_anon_keys = {
+      KAnonKeyForAdBid(bidders[0].interest_group,
+                       bidders[0].interest_group.ads.value()[0].render_url),
+      KAnonKeyForAdNameReporting(bidders[0].interest_group,
+                                 bidders[0].interest_group.ads.value()[0]),
+      KAnonKeyForAdBid(
+          bidders[0].interest_group,
+          bidders[0].interest_group.ad_components.value()[0].render_url),
+      KAnonKeyForAdBid(
+          bidders[0].interest_group,
+          bidders[0].interest_group.ad_components.value()[1].render_url),
+  };
+
+  std::vector<std::string> ad2_k_anon_keys = {
+      KAnonKeyForAdBid(bidders[1].interest_group,
+                       bidders[1].interest_group.ads.value()[0].render_url),
+      KAnonKeyForAdNameReporting(bidders[1].interest_group,
+                                 bidders[1].interest_group.ads.value()[0]),
+      KAnonKeyForAdBid(
+          bidders[1].interest_group,
+          bidders[1].interest_group.ad_components.value()[0].render_url),
+      KAnonKeyForAdBid(
+          bidders[1].interest_group,
+          bidders[1].interest_group.ad_components.value()[1].render_url),
+  };
+
   for (bool run_as_component : {false, true}) {
     SCOPED_TRACE(run_as_component);
 
-    result_.ad_url.reset();
-    result_.render_url_without_kanon_enforced.reset();
-    result_.render_url_with_kanon_simulated.reset();
     if (run_as_component) {
       component_auctions_.emplace_back(
           CreateAuctionConfig(kSellerUrl, {{kBidder1, kBidder2}}));
@@ -10946,6 +10957,9 @@
 
     GURL expected_seller_report_url;
     std::vector<GURL> expected_report_urls;
+    base::flat_set<std::string> expected_k_anon_keys_to_join;
+    histogram_tester_->ExpectUniqueSample(
+        "Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon", false, 1);
     switch (kanon_mode()) {
       case auction_worklet::mojom::KAnonymityBidMode::kNone:
         // k-anon support is turned off entirely, so ad2 wins, and no other URLs
@@ -10955,12 +10969,10 @@
         EXPECT_THAT(result_.ad_component_urls,
                     testing::UnorderedElementsAre(GURL("https://ad2.com/1"),
                                                   GURL("https://ad2.com/2")));
-        EXPECT_FALSE(result_.render_url_without_kanon_enforced.has_value());
-        EXPECT_THAT(result_.ad_component_urls_without_kanon_enforced,
-                    testing::UnorderedElementsAre());
-        EXPECT_FALSE(result_.render_url_with_kanon_simulated.has_value());
-        EXPECT_THAT(result_.ad_component_urls_with_kanon_simulated,
-                    testing::UnorderedElementsAre());
+        // Only join for ad2
+        expected_k_anon_keys_to_join.insert(ad2_k_anon_keys.begin(),
+                                            ad2_k_anon_keys.end());
+
         expected_seller_report_url = GURL("https://reporting.example.com/2");
         expected_report_urls.push_back(
             ReportWinUrl(/*bid=*/2, /*highest_scoring_other_bid=*/1,
@@ -10979,15 +10991,11 @@
         EXPECT_THAT(result_.ad_component_urls,
                     testing::UnorderedElementsAre(GURL("https://ad1.com/1"),
                                                   GURL("https://ad1.com/2")));
-        ASSERT_TRUE(result_.render_url_without_kanon_enforced.has_value());
-        EXPECT_EQ(GURL("https://ad2.com"),
-                  result_.render_url_without_kanon_enforced.value());
-        EXPECT_THAT(result_.ad_component_urls_without_kanon_enforced,
-                    testing::UnorderedElementsAre(GURL("https://ad2.com/1"),
-                                                  GURL("https://ad2.com/2")));
-        EXPECT_FALSE(result_.render_url_with_kanon_simulated.has_value());
-        EXPECT_THAT(result_.ad_component_urls_with_kanon_simulated,
-                    testing::UnorderedElementsAre());
+
+        expected_k_anon_keys_to_join.insert(ad1_k_anon_keys.begin(),
+                                            ad1_k_anon_keys.end());
+        expected_k_anon_keys_to_join.insert(ad2_k_anon_keys.begin(),
+                                            ad2_k_anon_keys.end());
         expected_seller_report_url = GURL("https://reporting.example.com/1");
         expected_report_urls.push_back(
             ReportWinUrl(/*bid=*/1, /*highest_scoring_other_bid=*/0,
@@ -11002,21 +11010,21 @@
         EXPECT_THAT(result_.ad_component_urls,
                     testing::UnorderedElementsAre(GURL("https://ad2.com/1"),
                                                   GURL("https://ad2.com/2")));
-        EXPECT_FALSE(result_.render_url_without_kanon_enforced.has_value());
-        EXPECT_THAT(result_.ad_component_urls_without_kanon_enforced,
-                    testing::UnorderedElementsAre());
-        ASSERT_TRUE(result_.render_url_with_kanon_simulated.has_value());
-        EXPECT_EQ(GURL("https://ad1.com"),
-                  result_.render_url_with_kanon_simulated.value());
-        EXPECT_THAT(result_.ad_component_urls_with_kanon_simulated,
-                    testing::UnorderedElementsAre(GURL("https://ad1.com/1"),
-                                                  GURL("https://ad1.com/2")));
+
+        expected_k_anon_keys_to_join.insert(ad1_k_anon_keys.begin(),
+                                            ad1_k_anon_keys.end());
+        expected_k_anon_keys_to_join.insert(ad2_k_anon_keys.begin(),
+                                            ad2_k_anon_keys.end());
         expected_seller_report_url = GURL("https://reporting.example.com/2");
         expected_report_urls.push_back(
             ReportWinUrl(/*bid=*/2, /*highest_scoring_other_bid=*/1,
                          /*made_highest_scoring_other_bid=*/false));
         break;
     }
+
+    EXPECT_THAT(result_.k_anon_keys_to_join, testing::UnorderedElementsAreArray(
+                                                 expected_k_anon_keys_to_join));
+
     expected_report_urls.push_back(expected_seller_report_url);
     if (run_as_component) {
       // Both top-level and component auction report this.
@@ -11027,6 +11035,10 @@
   }
 }
 
+// Test that if there are two ads, one k-anonymous and one not k-anonymous that
+// the correct ad is the winner (depends on `kanon_mode()`). Note that the
+// non-k-anonymous ad bids higher so that it wins when k-anonymity is not
+// enforced.
 TEST_P(AuctionRunnerKAnonTest, Basic) {
   auction_worklet::AddJavascriptResponse(
       &url_loader_factory_, kBidder1Url,
@@ -11052,12 +11064,22 @@
   AuthorizeKAnon(bidders[0].interest_group.ads.value()[0], "https://ad1.com",
                  bidders[0]);
 
+  std::vector<std::string> ad1_k_anon_keys = {
+      KAnonKeyForAdBid(bidders[0].interest_group,
+                       bidders[0].interest_group.ads.value()[0].render_url),
+      KAnonKeyForAdNameReporting(bidders[0].interest_group,
+                                 bidders[0].interest_group.ads.value()[0]),
+  };
+  std::vector<std::string> ad2_k_anon_keys = {
+      KAnonKeyForAdBid(bidders[1].interest_group,
+                       bidders[1].interest_group.ads.value()[0].render_url),
+      KAnonKeyForAdNameReporting(bidders[1].interest_group,
+                                 bidders[1].interest_group.ads.value()[0]),
+  };
+
   for (bool run_as_component : {false, true}) {
     SCOPED_TRACE(run_as_component);
 
-    result_.ad_url.reset();
-    result_.render_url_without_kanon_enforced.reset();
-    result_.render_url_with_kanon_simulated.reset();
     if (run_as_component) {
       component_auctions_.emplace_back(
           CreateAuctionConfig(kSellerUrl, {{kBidder1, kBidder2}}));
@@ -11070,7 +11092,10 @@
     auction_run_loop_->Run();
     EXPECT_THAT(result_.errors, testing::ElementsAre());
     ASSERT_TRUE(result_.ad_url.has_value());
+    histogram_tester_->ExpectUniqueSample(
+        "Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon", false, 1);
 
+    base::flat_set<std::string> expected_k_anon_keys_to_join;
     GURL expected_seller_report_url;
     std::vector<GURL> expected_report_urls;
     switch (kanon_mode()) {
@@ -11078,8 +11103,8 @@
         // k-anon support is turned off entirely, so ad2 wins, and no other URLs
         // are set.
         EXPECT_EQ(GURL("https://ad2.com"), result_.ad_url.value());
-        EXPECT_FALSE(result_.render_url_without_kanon_enforced.has_value());
-        EXPECT_FALSE(result_.render_url_with_kanon_simulated.has_value());
+        expected_k_anon_keys_to_join.insert(ad2_k_anon_keys.begin(),
+                                            ad2_k_anon_keys.end());
         expected_seller_report_url = GURL("https://reporting.example.com/2");
         expected_report_urls.push_back(
             ReportWinUrl(/*bid=*/2, /*highest_scoring_other_bid=*/1,
@@ -11090,10 +11115,10 @@
         // k-anon requirement meands ad1 wins, but we also report ad2 as what
         // would have won had it been authorized.
         EXPECT_EQ(GURL("https://ad1.com"), result_.ad_url.value());
-        ASSERT_TRUE(result_.render_url_without_kanon_enforced.has_value());
-        EXPECT_EQ(GURL("https://ad2.com"),
-                  result_.render_url_without_kanon_enforced.value());
-        EXPECT_FALSE(result_.render_url_with_kanon_simulated.has_value());
+        expected_k_anon_keys_to_join.insert(ad1_k_anon_keys.begin(),
+                                            ad1_k_anon_keys.end());
+        expected_k_anon_keys_to_join.insert(ad2_k_anon_keys.begin(),
+                                            ad2_k_anon_keys.end());
         expected_seller_report_url = GURL("https://reporting.example.com/1");
         expected_report_urls.push_back(
             ReportWinUrl(/*bid=*/1, /*highest_scoring_other_bid=*/0,
@@ -11104,16 +11129,19 @@
         // Winner is ad2.com, disregarding k-anonymity, but we also report that
         // if we did care about it, ad1.com would have won.
         EXPECT_EQ(GURL("https://ad2.com"), result_.ad_url.value());
-        EXPECT_FALSE(result_.render_url_without_kanon_enforced.has_value());
-        ASSERT_TRUE(result_.render_url_with_kanon_simulated.has_value());
-        EXPECT_EQ(GURL("https://ad1.com"),
-                  result_.render_url_with_kanon_simulated.value());
+        expected_k_anon_keys_to_join.insert(ad1_k_anon_keys.begin(),
+                                            ad1_k_anon_keys.end());
+        expected_k_anon_keys_to_join.insert(ad2_k_anon_keys.begin(),
+                                            ad2_k_anon_keys.end());
         expected_seller_report_url = GURL("https://reporting.example.com/2");
         expected_report_urls.push_back(
             ReportWinUrl(/*bid=*/2, /*highest_scoring_other_bid=*/1,
                          /*made_highest_scoring_other_bid=*/false));
         break;
     }
+    EXPECT_THAT(result_.k_anon_keys_to_join, testing::UnorderedElementsAreArray(
+                                                 expected_k_anon_keys_to_join));
+
     expected_report_urls.push_back(expected_seller_report_url);
     if (run_as_component) {
       // Both top-level and component auction report this.
@@ -11150,19 +11178,29 @@
   AuthorizeKAnon(bidders[0].interest_group.ads.value()[0], "https://ad1.com",
                  bidders[0]);
 
+  std::vector<std::string> ad1_k_anon_keys = {
+      KAnonKeyForAdBid(bidders[0].interest_group,
+                       bidders[0].interest_group.ads.value()[0].render_url),
+      KAnonKeyForAdNameReporting(bidders[0].interest_group,
+                                 bidders[0].interest_group.ads.value()[0]),
+  };
+
   StartAuction(kSellerUrl, bidders);
   auction_run_loop_->Run();
   EXPECT_THAT(result_.errors, testing::ElementsAre());
   ASSERT_TRUE(result_.ad_url.has_value());
   EXPECT_EQ(GURL("https://ad1.com"), result_.ad_url.value());
+  EXPECT_THAT(result_.k_anon_keys_to_join,
+              testing::UnorderedElementsAreArray(ad1_k_anon_keys));
+
   std::vector<GURL> expected_report_urls;
   expected_report_urls.emplace_back("https://reporting.example.com/2");
   switch (kanon_mode()) {
     case auction_worklet::mojom::KAnonymityBidMode::kNone:
       // k-anon support is turned off entirely, so no other URLs
       // are set.
-      EXPECT_FALSE(result_.render_url_without_kanon_enforced.has_value());
-      EXPECT_FALSE(result_.render_url_with_kanon_simulated.has_value());
+      histogram_tester_->ExpectUniqueSample(
+          "Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon", false, 1);
       expected_report_urls.push_back(
           ReportWinUrl(/*bid=*/2, /*highest_scoring_other_bid=*/1,
                        /*made_highest_scoring_other_bid=*/false));
@@ -11170,10 +11208,8 @@
 
     case auction_worklet::mojom::KAnonymityBidMode::kEnforce:
       // The enforced winner is the same, but there is no runner-up.
-      ASSERT_TRUE(result_.render_url_without_kanon_enforced.has_value());
-      EXPECT_EQ(GURL("https://ad1.com"),
-                result_.render_url_without_kanon_enforced.value());
-      EXPECT_FALSE(result_.render_url_with_kanon_simulated.has_value());
+      histogram_tester_->ExpectUniqueSample(
+          "Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon", true, 1);
       expected_report_urls.push_back(
           ReportWinUrl(/*bid=*/2, /*highest_scoring_other_bid=*/0,
                        /*made_highest_scoring_other_bid=*/false));
@@ -11181,10 +11217,8 @@
 
     case auction_worklet::mojom::KAnonymityBidMode::kSimulate:
       // ad1.com also wins in the simulated mode.
-      EXPECT_FALSE(result_.render_url_without_kanon_enforced.has_value());
-      ASSERT_TRUE(result_.render_url_with_kanon_simulated.has_value());
-      EXPECT_EQ(GURL("https://ad1.com"),
-                result_.render_url_with_kanon_simulated.value());
+      histogram_tester_->ExpectUniqueSample(
+          "Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon", true, 1);
       expected_report_urls.push_back(
           ReportWinUrl(/*bid=*/2, /*highest_scoring_other_bid=*/1,
                        /*made_highest_scoring_other_bid=*/false));
@@ -11228,17 +11262,33 @@
   AuthorizeKAnon(bidders[0].interest_group.ads.value()[0], "https://ad1.com",
                  bidders[0]);
 
+  std::vector<std::string> ad1_k_anon_keys = {
+      KAnonKeyForAdBid(bidders[0].interest_group,
+                       bidders[0].interest_group.ads.value()[0].render_url),
+      KAnonKeyForAdNameReporting(bidders[0].interest_group,
+                                 bidders[0].interest_group.ads.value()[0]),
+  };
+  std::vector<std::string> ad2_k_anon_keys = {
+      KAnonKeyForAdBid(bidders[0].interest_group,
+                       bidders[0].interest_group.ads.value()[1].render_url),
+      KAnonKeyForAdNameReporting(bidders[0].interest_group,
+                                 bidders[0].interest_group.ads.value()[1]),
+  };
+
   StartAuction(kSellerUrl, bidders);
   auction_run_loop_->Run();
   EXPECT_THAT(result_.errors, testing::ElementsAre());
   ASSERT_TRUE(result_.ad_url.has_value());
+  histogram_tester_->ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon", false, 1);
 
+  base::flat_set<std::string> expected_k_anon_keys_to_join;
   switch (kanon_mode()) {
     case auction_worklet::mojom::KAnonymityBidMode::kNone:
       // Don't care about k-anonymity: ad2 wins, nothing else is reporter.
       EXPECT_EQ(GURL("https://ad2.com"), result_.ad_url.value());
-      EXPECT_FALSE(result_.render_url_without_kanon_enforced.has_value());
-      EXPECT_FALSE(result_.render_url_with_kanon_simulated.has_value());
+      expected_k_anon_keys_to_join.insert(ad2_k_anon_keys.begin(),
+                                          ad2_k_anon_keys.end());
       EXPECT_THAT(result_.report_urls,
                   testing::ElementsAre("https://reporting.example.com/2"));
       break;
@@ -11247,10 +11297,10 @@
       // Ad 2 is what got blocked by enforcement --- if it were authorized, it
       // would win.
       EXPECT_EQ(GURL("https://ad1.com"), result_.ad_url.value());
-      ASSERT_TRUE(result_.render_url_without_kanon_enforced.has_value());
-      EXPECT_EQ(GURL("https://ad2.com"),
-                result_.render_url_without_kanon_enforced.value());
-      EXPECT_FALSE(result_.render_url_with_kanon_simulated.has_value());
+      expected_k_anon_keys_to_join.insert(ad1_k_anon_keys.begin(),
+                                          ad1_k_anon_keys.end());
+      expected_k_anon_keys_to_join.insert(ad2_k_anon_keys.begin(),
+                                          ad2_k_anon_keys.end());
       EXPECT_THAT(result_.report_urls,
                   testing::ElementsAre("https://reporting.example.com/1"));
       break;
@@ -11259,14 +11309,16 @@
       // Winner is ad2.com, disregarding k-anonymity, but we also report that
       // if we did care about it, ad1.com would have won.
       EXPECT_EQ(GURL("https://ad2.com"), result_.ad_url.value());
-      EXPECT_FALSE(result_.render_url_without_kanon_enforced.has_value());
-      ASSERT_TRUE(result_.render_url_with_kanon_simulated.has_value());
-      EXPECT_EQ(GURL("https://ad1.com"),
-                result_.render_url_with_kanon_simulated.value());
+      expected_k_anon_keys_to_join.insert(ad1_k_anon_keys.begin(),
+                                          ad1_k_anon_keys.end());
+      expected_k_anon_keys_to_join.insert(ad2_k_anon_keys.begin(),
+                                          ad2_k_anon_keys.end());
       EXPECT_THAT(result_.report_urls,
                   testing::ElementsAre("https://reporting.example.com/2"));
       break;
   }
+  EXPECT_THAT(result_.k_anon_keys_to_join,
+              testing::UnorderedElementsAreArray(expected_k_anon_keys_to_join));
 }
 
 // Test to make sure that k-anon info doesn't get incorrectly reported when
@@ -11317,8 +11369,9 @@
 
   // Should not have anything to report.
   EXPECT_FALSE(result_.ad_url.has_value());
-  EXPECT_FALSE(result_.render_url_without_kanon_enforced.has_value());
-  EXPECT_FALSE(result_.render_url_with_kanon_simulated.has_value());
+  EXPECT_THAT(result_.k_anon_keys_to_join, testing::ElementsAre());
+  histogram_tester_->ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon", false, 0);
 }
 
 TEST_P(AuctionRunnerKAnonTest, MojoValidation) {
diff --git a/content/browser/interest_group/interest_group_auction.cc b/content/browser/interest_group/interest_group_auction.cc
index 3e70a4e..8cda40c7 100644
--- a/content/browser/interest_group/interest_group_auction.cc
+++ b/content/browser/interest_group/interest_group_auction.cc
@@ -1128,6 +1128,11 @@
     UMA_HISTOGRAM_ENUMERATION("Ads.InterestGroup.Auction.Result",
                               *final_auction_result_);
 
+    if (HasNonKAnonWinner()) {
+      UMA_HISTOGRAM_BOOLEAN("Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon",
+                            NonKAnonWinnerIsKAnon());
+    }
+
     // Only record time of full auctions and aborts.
     switch (*final_auction_result_) {
       case AuctionResult::kAborted:
@@ -1482,6 +1487,38 @@
   return url.ReplaceComponents(replacements);
 }
 
+bool InterestGroupAuction::HasNonKAnonWinner() const {
+  if (!final_auction_result_) {
+    return false;
+  }
+
+  switch (*final_auction_result_) {
+    // Bidding and scoring phase completed with no fatal error. We either
+    // succeeded or only failed because we did not have a winner. If the only
+    // reason we didn't have a winner was k-anonymity enforcement, there may
+    // still be a non-k-anon winner.
+    case AuctionResult::kSuccess:
+    case AuctionResult::kNoBids:
+    case AuctionResult::kAllBidsRejected:
+      return top_non_kanon_enforced_bid() != nullptr;
+    case AuctionResult::kAborted:
+    case AuctionResult::kBadMojoMessage:
+    case AuctionResult::kNoInterestGroups:
+    case AuctionResult::kSellerWorkletLoadFailed:
+    case AuctionResult::kSellerWorkletCrashed:
+    case AuctionResult::kSellerRejected:
+    case AuctionResult::kComponentLostAuction:
+      return false;
+  }
+}
+
+bool InterestGroupAuction::NonKAnonWinnerIsKAnon() const {
+  return top_non_kanon_enforced_bid() &&
+         top_non_kanon_enforced_bid()
+                 ->bid->auction->top_non_kanon_enforced_bid()
+                 ->bid->bid_role == Bid::BidRole::kBothKAnonModes;
+}
+
 void InterestGroupAuction::TakeDebugReportUrls(
     std::vector<GURL>& debug_win_report_urls,
     std::vector<GURL>& debug_loss_report_urls) {
@@ -1593,35 +1630,38 @@
   }
 }
 
-absl::optional<GURL> InterestGroupAuction::TakeRenderUrlWithoutKAnonEnforced() {
-  DCHECK_EQ(kanon_mode_, auction_worklet::mojom::KAnonymityBidMode::kEnforce);
-  ScoredBid* non_kanon_winner = top_non_kanon_enforced_bid();
-  return non_kanon_winner
-             ? absl::make_optional(std::move(non_kanon_winner->bid->render_url))
-             : absl::nullopt;
-}
+base::flat_set<std::string> InterestGroupAuction::GetKAnonKeysToJoin() const {
+  if (!HasNonKAnonWinner()) {
+    return {};
+  }
 
-std::vector<GURL>
-InterestGroupAuction::TakeComponentUrlsWithoutKAnonEnforced() {
-  DCHECK_EQ(kanon_mode_, auction_worklet::mojom::KAnonymityBidMode::kEnforce);
-  ScoredBid* non_kanon_winner = top_non_kanon_enforced_bid();
-  return non_kanon_winner ? std::move(non_kanon_winner->bid->ad_components)
-                          : std::vector<GURL>();
-}
+  // If the k-anon winner is the same as the non-k-anon winner then just include
+  // it once.
+  std::vector<const ScoredBid*> bids_to_include = {
+      top_non_kanon_enforced_bid()};
+  if (!NonKAnonWinnerIsKAnon()) {
+    bids_to_include.push_back(top_kanon_enforced_bid());
+  }
 
-absl::optional<GURL> InterestGroupAuction::TakeSimulatedKAnonRenderUrl() {
-  DCHECK_EQ(kanon_mode_, auction_worklet::mojom::KAnonymityBidMode::kSimulate);
-  ScoredBid* kanon_winner = top_kanon_enforced_bid();
-  return kanon_winner
-             ? absl::make_optional(std::move(kanon_winner->bid->render_url))
-             : absl::nullopt;
-}
-
-std::vector<GURL> InterestGroupAuction::TakeSimulatedKAnonComponentUrls() {
-  DCHECK_EQ(kanon_mode_, auction_worklet::mojom::KAnonymityBidMode::kSimulate);
-  ScoredBid* kanon_winner = top_kanon_enforced_bid();
-  return kanon_winner ? std::move(kanon_winner->bid->ad_components)
-                      : std::vector<GURL>();
+  // Add all the KAnon keys for the winning k-anon and non-k-anon bids.
+  std::vector<std::string> k_anon_keys_to_join;
+  for (const ScoredBid* scored_bid : bids_to_include) {
+    if (!scored_bid) {
+      continue;
+    }
+    DCHECK(scored_bid->bid);
+    const blink::InterestGroup& interest_group =
+        *scored_bid->bid->interest_group;
+    k_anon_keys_to_join.push_back(
+        KAnonKeyForAdBid(interest_group, scored_bid->bid->bid_ad->render_url));
+    k_anon_keys_to_join.push_back(
+        KAnonKeyForAdNameReporting(interest_group, *scored_bid->bid->bid_ad));
+    for (const GURL& ad_component : scored_bid->bid->ad_components) {
+      k_anon_keys_to_join.push_back(
+          KAnonKeyForAdBid(interest_group, ad_component));
+    }
+  }
+  return base::flat_set<std::string>(std::move(k_anon_keys_to_join));
 }
 
 const InterestGroupAuction::LeaderInfo& InterestGroupAuction::leader_info()
@@ -1634,13 +1674,22 @@
 }
 
 InterestGroupAuction::ScoredBid*
+InterestGroupAuction::top_kanon_enforced_bid() {
+  return kanon_enforced_auction_leader_.top_bid.get();
+}
+const InterestGroupAuction::ScoredBid*
+InterestGroupAuction::top_kanon_enforced_bid() const {
+  return kanon_enforced_auction_leader_.top_bid.get();
+}
+
+InterestGroupAuction::ScoredBid*
 InterestGroupAuction::top_non_kanon_enforced_bid() {
   return non_kanon_enforced_auction_leader_.top_bid.get();
 }
 
-InterestGroupAuction::ScoredBid*
-InterestGroupAuction::top_kanon_enforced_bid() {
-  return kanon_enforced_auction_leader_.top_bid.get();
+const InterestGroupAuction::ScoredBid*
+InterestGroupAuction::top_non_kanon_enforced_bid() const {
+  return non_kanon_enforced_auction_leader_.top_bid.get();
 }
 
 absl::optional<uint16_t> InterestGroupAuction::GetBuyerExperimentId(
diff --git a/content/browser/interest_group/interest_group_auction.h b/content/browser/interest_group/interest_group_auction.h
index fb413286..4cf355a 100644
--- a/content/browser/interest_group/interest_group_auction.h
+++ b/content/browser/interest_group/interest_group_auction.h
@@ -456,21 +456,11 @@
   // only be called once, since it moves the stored origins.
   void TakePostAuctionUpdateOwners(std::vector<url::Origin>& owners);
 
-  // For an auction in k-anon enforcement mode (a precondition for making this
-  // call), retrieves what the winner would be if enforcement were off.
-  //
-  // May only be called after the auction has completed, for either success or
-  // failure. May only be called once, since it moves the stored URLs.
-  absl::optional<GURL> TakeRenderUrlWithoutKAnonEnforced();
-  std::vector<GURL> TakeComponentUrlsWithoutKAnonEnforced();
-
-  // For an auction in k-anon simulation mode (a precondition for making this
-  // call), retrieves what the winner would be if k-anon enforcement were on.
-  //
-  // May only be called after the auction has completed, for either success or
-  // failure. May only be called once, since it moves the stored URLs.
-  absl::optional<GURL> TakeSimulatedKAnonRenderUrl();
-  std::vector<GURL> TakeSimulatedKAnonComponentUrls();
+  // Retrieves the keys that need to be joined as a result of the auction. A
+  // failed auction may result in keys that still need to be joined, for
+  // instance if the reason the auction failed was that none of the bids were
+  // k-anonymous.
+  base::flat_set<std::string> GetKAnonKeysToJoin() const;
 
   // Returns the top bid of whichever auction (k-anon or not, depending on the
   // configuration) is actually to be used for the user-facing results. May only
@@ -689,7 +679,9 @@
   // These may be null. They should only be invoked after the bidding and
   // scoring phase has completed.
   ScoredBid* top_kanon_enforced_bid();
+  const ScoredBid* top_kanon_enforced_bid() const;
   ScoredBid* top_non_kanon_enforced_bid();
+  const ScoredBid* top_non_kanon_enforced_bid() const;
 
   // -----------------------------------
   // Methods not associated with a phase
@@ -727,6 +719,11 @@
     return kanon_mode_;
   }
 
+  // Returns true if the auction had a non-k-anonymous winner.
+  bool HasNonKAnonWinner() const;
+  // Returns true if the non-k-anonymous winner of the auction is k-anonymous.
+  bool NonKAnonWinnerIsKAnon() const;
+
   // Tracing ID associated with the Auction. A nestable
   // async "Auction" trace
   // event lasts for the lifetime of `this`. Sequential events that apply to
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index 3844500d..8875beb4 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -1239,16 +1239,45 @@
   std::unique_ptr<NetworkResponder> network_responder_;
 };
 
-// At the moment, InterestGroups use URN urls when fenced frames are enabled,
-// and normal URLs when not. This means they require ads be loaded in fenced
-// frames when Chrome is running with the option enabled.
-class InterestGroupFencedFrameBrowserTest : public InterestGroupBrowserTest {
+// At the moment, InterestGroups use either:
+//   a. Web-exposed `FencedFrameConfig` objects or URN urls, when fenced frames
+//      are enabled
+//   b. Normal URLs when fenced frames are not enabled
+// This means they require ads be loaded in fenced frames when Chrome is running
+// with the option enabled. This fixture is parameterized over whether the test
+// should call `navigator.runAdAuction()` with a request to have the promise
+// resolve to a JS `FencedFrameConfig` object or a URN.
+class InterestGroupFencedFrameBrowserTest
+    : public InterestGroupBrowserTest,
+      public ::testing::WithParamInterface<bool> {
  public:
   InterestGroupFencedFrameBrowserTest() {
-    feature_list_.InitWithFeaturesAndParameters(
-        {{blink::features::kFencedFrames, {}},
-         {features::kPrivacySandboxAdsAPIsOverride, {}}},
-        {/* disabled_features */});
+    if (ResolveAuctionsToConfig()) {
+      feature_list_.InitWithFeaturesAndParameters(
+          {{blink::features::kFencedFrames, {}},
+           {features::kPrivacySandboxAdsAPIsOverride, {}},
+           // This feature allows `runAdAuction()`'s promise to resolve to a
+           // `FencedFrameConfig` object upon developer request.
+           {blink::features::kFencedFramesAPIChanges, {}}},
+          {/* disabled_features */});
+    } else {
+      feature_list_.InitWithFeaturesAndParameters(
+          {{blink::features::kFencedFrames, {}},
+           {features::kPrivacySandboxAdsAPIsOverride, {}}},
+          {/* disabled_features */});
+    }
+  }
+
+  bool ResolveAuctionsToConfig() const { return GetParam(); }
+
+  // Provides meaningful param names instead of /0 and /1.
+  static std::string DescribeParams(
+      const ::testing::TestParamInfo<ParamType>& info) {
+    if (info.param) {
+      return "ResolveAuctionsToConfig";
+    } else {
+      return "ResolveAuctionsToURN";
+    }
   }
 
   ~InterestGroupFencedFrameBrowserTest() override = default;
@@ -1267,28 +1296,70 @@
       absl::optional<ToRenderFrameHost> execution_target = absl::nullopt) {
     if (!execution_target)
       execution_target = shell();
-    content::EvalJsResult urn_url_string =
-        RunAuctionAndWait(auction_config_json, execution_target);
-    ASSERT_TRUE(urn_url_string.value.is_string())
-        << "Expected string, but got " << urn_url_string.value;
 
-    GURL urn_url(urn_url_string.ExtractString());
-    ASSERT_TRUE(urn_url.is_valid())
-        << "URL is not valid: " << urn_url_string.ExtractString();
-    EXPECT_EQ(url::kUrnScheme, urn_url.scheme_piece());
+    // There are two paths, depending on the parameter for this test, that we
+    // take for running an ad auction and navigating a fenced frame element to
+    // its result, `runAdAuction()` resolves to a:
+    //   1. FencedFrameConfig object
+    //   2. URN
+    // For (1), we:
+    //   1. Run the ad auction, specifically requesting that the returned
+    //      promise resolve to a config object that we immediately navigate to
+    //   2. Wait for the navigation to finish
+    // For (2), we:
+    //   1. Run the ad auction
+    //   2. Asynchronously later, navigate the fenced frame to the resulting URN
+    //   3. Wait for the navigation to finish
+    // The only real difference is that for (1) above, the auction and
+    // navigation happen separately.
+    if (ResolveAuctionsToConfig()) {
+      TestFrameNavigationObserver observer(
+          GetFencedFrameRenderFrameHost(*execution_target));
+      content::EvalJsResult eval_result =
+          EvalJs(execution_target ? *execution_target : shell(),
+                 base::StringPrintf(
+                     R"(
+(async function() {
+  try {
+    const auction_config = %s;
+    auction_config.resolveToConfig = true;
 
-    NavigateFencedFrameAndWait(urn_url, expected_ad_url, *execution_target);
+    const fenced_frame_config = await navigator.runAdAuction(auction_config);
+    if (!(fenced_frame_config instanceof FencedFrameConfig)) {
+      throw new Error('runAdAuction() did not return a FencedFrameConfig');
+    }
+
+    document.querySelector('fencedframe').config = fenced_frame_config;
+  } catch (e) {
+    return e.toString();
+  }
+})())",
+                     auction_config_json.c_str()));
+
+      ASSERT_TRUE(eval_result.value.is_none())
+          << "Expected string, but got " << eval_result.value;
+      WaitForFencedFrameNavigation(expected_ad_url, *execution_target,
+                                   observer);
+    } else {
+      content::EvalJsResult urn_url_string =
+          RunAuctionAndWait(auction_config_json, execution_target);
+      ASSERT_TRUE(urn_url_string.value.is_string())
+          << "Expected string, but got " << urn_url_string.value;
+
+      GURL urn_url(urn_url_string.ExtractString());
+      ASSERT_TRUE(urn_url.is_valid())
+          << "URL is not valid: " << urn_url_string.ExtractString();
+      EXPECT_EQ(url::kUrnScheme, urn_url.scheme_piece());
+
+      NavigateFencedFrameAndWait(urn_url, expected_ad_url, *execution_target);
+    }
   }
 
-  // Navigates the only fenced frame in `execution_target` to `url` and waits
-  // for the navigation to complete, expecting the frame to navigate to
-  // `expected_url`. Also checks that the URL is actually requested from the
-  // test server if `expected_url` is an HTTPS URL.
+  // Navigates the only fenced frame in `execution_target` to `url` and invokes
+  // `WaitForFencedFrameNavigation()`.
   void NavigateFencedFrameAndWait(const GURL& url,
                                   const GURL& expected_url,
                                   const ToRenderFrameHost& execution_target) {
-    // Use to wait for navigation completion in the ShadowDOM case only.
-    // Harmlessly created but not used in the MPArch case.
     TestFrameNavigationObserver observer(
         GetFencedFrameRenderFrameHost(execution_target));
 
@@ -1296,6 +1367,17 @@
         execution_target,
         JsReplace("document.querySelector('fencedframe').src = $1;", url)));
 
+    WaitForFencedFrameNavigation(expected_url, execution_target, observer);
+  }
+
+  // Waits for a fenced frame navigation to complete in `execution_target`,
+  // expecting the frame to navigate to `expected_url`. Also checks that the URL
+  // is actually requested from the test server if `expected_url` is an HTTPS
+  // URL. `observer` must be set up before the navigation-initiating code is
+  // run. We wait on it in this method.
+  void WaitForFencedFrameNavigation(const GURL& expected_url,
+                                    const ToRenderFrameHost& execution_target,
+                                    TestFrameNavigationObserver& observer) {
     // If the URL is HTTPS, wait for the URL to be requested, to make sure the
     // fenced frame actually made the request and, in the MPArch case, to make
     // sure the load actually started. On regression, this is likely to hang.
@@ -4496,7 +4578,7 @@
 // but runs with fenced frames enabled and expects to receive a URN URL to be
 // used. After the auction, loads the URL in a fenced frame, and expects the
 // correct URL is loaded.
-IN_PROC_BROWSER_TEST_F(InterestGroupFencedFrameBrowserTest,
+IN_PROC_BROWSER_TEST_P(InterestGroupFencedFrameBrowserTest,
                        RunAdAuctionWithWinner) {
   URLLoaderMonitor url_loader_monitor;
 
@@ -4641,7 +4723,7 @@
                 ->trusted_params->isolation_info.network_isolation_key());
 }
 
-IN_PROC_BROWSER_TEST_F(InterestGroupFencedFrameBrowserTest,
+IN_PROC_BROWSER_TEST_P(InterestGroupFencedFrameBrowserTest,
                        RunAdAuctionWithWinnerReplacedURN) {
   URLLoaderMonitor url_loader_monitor;
 
@@ -4709,7 +4791,7 @@
 // succeed and are then loaded in separate fenced frames. Both auctions try to
 // leave the interest group, but only the one whose ad matches the joining
 // origin should succeed.
-IN_PROC_BROWSER_TEST_F(InterestGroupFencedFrameBrowserTest,
+IN_PROC_BROWSER_TEST_P(InterestGroupFencedFrameBrowserTest,
                        RunTwoAdAuctionWithWinnerLeaveGroup) {
   URLLoaderMonitor url_loader_monitor;
 
@@ -4868,7 +4950,7 @@
 // from a nested iframe.
 //
 // TODO(crbug.com/1320438): Re-enable the test.
-IN_PROC_BROWSER_TEST_F(InterestGroupFencedFrameBrowserTest,
+IN_PROC_BROWSER_TEST_P(InterestGroupFencedFrameBrowserTest,
                        RunAdAuctionWithWinnerNestedLeaveGroup) {
   URLLoaderMonitor url_loader_monitor;
 
@@ -4942,7 +5024,7 @@
 
 // Creates a Fenced Frame and then tries to use the leaveAdInterestGroup API.
 // Leaving the interest group should silently fail.
-IN_PROC_BROWSER_TEST_F(InterestGroupFencedFrameBrowserTest,
+IN_PROC_BROWSER_TEST_P(InterestGroupFencedFrameBrowserTest,
                        LeaveAdInterestGroupNoAuction) {
   URLLoaderMonitor url_loader_monitor;
 
@@ -4991,7 +5073,7 @@
 
 // Use different origins for publisher, bidder, and seller, and make sure
 // everything works as expected.
-IN_PROC_BROWSER_TEST_F(InterestGroupFencedFrameBrowserTest, CrossOrigin) {
+IN_PROC_BROWSER_TEST_P(InterestGroupFencedFrameBrowserTest, CrossOrigin) {
   const char kPublisher[] = "a.test";
   const char kBidder[] = "b.test";
   const char kSeller[] = "c.test";
@@ -5258,7 +5340,7 @@
 
 // Test running auctions in cross-site iframes, and loading the winner into a
 // nested fenced frame.
-IN_PROC_BROWSER_TEST_F(InterestGroupFencedFrameBrowserTest, Iframe) {
+IN_PROC_BROWSER_TEST_P(InterestGroupFencedFrameBrowserTest, Iframe) {
   // Use different hostnames for each participant.
   const char kTopFrameHost[] = "a.test";
   const char kBidderHost[] = "b.test";
@@ -6517,7 +6599,7 @@
 
 // Test that when there are no ad components, an array of ad components is still
 // available, and they're all mapped to about:blank.
-IN_PROC_BROWSER_TEST_F(InterestGroupFencedFrameBrowserTest, NoAdComponents) {
+IN_PROC_BROWSER_TEST_P(InterestGroupFencedFrameBrowserTest, NoAdComponents) {
   GURL test_url =
       https_server_->GetURL("a.test", "/fenced_frames/opaque_ads.html");
   ASSERT_TRUE(NavigateToURL(shell(), test_url));
@@ -6574,7 +6656,7 @@
 // Test with an ad component. Run an auction with an ad component, load the ad
 // in a fenced frame, and the ad component in a nested fenced frame. Fully
 // exercise navigator.adAuctionComponents() on the main ad's fenced frame.
-IN_PROC_BROWSER_TEST_F(InterestGroupFencedFrameBrowserTest, AdComponents) {
+IN_PROC_BROWSER_TEST_P(InterestGroupFencedFrameBrowserTest, AdComponents) {
   GURL ad_component_url = https_server_->GetURL(
       "d.test", "/set-header?Supports-Loading-Mode: fenced-frame");
   ASSERT_NO_FATAL_FAILURE(RunBasicAuctionWithAdComponents(ad_component_url));
@@ -6608,7 +6690,7 @@
 // * The fenced frame the ad component is loaded in, though it will have a list
 //   of URNs that map to about:blank.
 // * The ad fenced frame itself, after a renderer-initiated navigation.
-IN_PROC_BROWSER_TEST_F(InterestGroupFencedFrameBrowserTest,
+IN_PROC_BROWSER_TEST_P(InterestGroupFencedFrameBrowserTest,
                        AdComponentsNotLeaked) {
   GURL ad_component_url =
       https_server_->GetURL("d.test", "/fenced_frames/opaque_ads.html");
@@ -6671,7 +6753,7 @@
 
 // Test with an ad component that tries to leave the group. Verify that leaving
 // the group from within an ad component has no effect
-IN_PROC_BROWSER_TEST_F(InterestGroupFencedFrameBrowserTest, AdComponentsLeave) {
+IN_PROC_BROWSER_TEST_P(InterestGroupFencedFrameBrowserTest, AdComponentsLeave) {
   url::Origin test_origin =
       url::Origin::Create(https_server_->GetURL("a.test", "/"));
   GURL ad_component_url = https_server_->GetURL(
@@ -6696,7 +6778,7 @@
 // Test navigating multiple fenced frames to the same render URL from a single
 // auction, when the winning bid included ad components. All fenced frames
 // navigated to the URL should get ad component URLs from the winning bid.
-IN_PROC_BROWSER_TEST_F(InterestGroupFencedFrameBrowserTest,
+IN_PROC_BROWSER_TEST_P(InterestGroupFencedFrameBrowserTest,
                        AdComponentsMainAdLoadedInMultipleFrames) {
   GURL ad_component_url = https_server_->GetURL(
       "d.test", "/set-header?Supports-Loading-Mode: fenced-frame");
@@ -6763,7 +6845,7 @@
 
 // Test with multiple ad components. Also checks that ad component metadata is
 // passed in correctly.
-IN_PROC_BROWSER_TEST_F(InterestGroupFencedFrameBrowserTest,
+IN_PROC_BROWSER_TEST_P(InterestGroupFencedFrameBrowserTest,
                        MultipleAdComponents) {
   // Note that the extra "&1" and the like are added to make the URLs unique.
   // They have no impact on the returned result, since they aren't a
@@ -9655,7 +9737,7 @@
 // Runs auction like Just like
 // InterestGroupFencedFrameBrowserTest.RunAdAuctionWithWinner but also registers
 // an ad beacon that is sent by the render URL.
-IN_PROC_BROWSER_TEST_F(InterestGroupFencedFrameBrowserTest,
+IN_PROC_BROWSER_TEST_P(InterestGroupFencedFrameBrowserTest,
                        RunAdAuctionWithWinnerRegisterAdBeaconBuyer) {
   URLLoaderMonitor url_loader_monitor;
 
@@ -9710,6 +9792,11 @@
   EXPECT_EQ(net::HttpRequestHeaders::kPostMethod, request->method);
 }
 
+INSTANTIATE_TEST_SUITE_P(All,
+                         InterestGroupFencedFrameBrowserTest,
+                         ::testing::Values(false, true),
+                         &InterestGroupFencedFrameBrowserTest::DescribeParams);
+
 class InterestGroupAuctionLimitBrowserTest : public InterestGroupBrowserTest {
  public:
   InterestGroupAuctionLimitBrowserTest() {
diff --git a/content/browser/interest_group/interest_group_k_anonymity_manager.cc b/content/browser/interest_group/interest_group_k_anonymity_manager.cc
index ea67776a..4dfca6f 100644
--- a/content/browser/interest_group/interest_group_k_anonymity_manager.cc
+++ b/content/browser/interest_group/interest_group_k_anonymity_manager.cc
@@ -6,20 +6,28 @@
 
 #include "base/bind.h"
 #include "base/containers/contains.h"
+#include "base/ranges/algorithm.h"
 #include "base/time/time.h"
 #include "content/browser/interest_group/interest_group_manager_impl.h"
 
 namespace content {
 
+namespace {
+const GURL& extractUrlFromAd(const blink::InterestGroup::Ad& ad) {
+  return ad.render_url;
+}
+}  // namespace
+
 std::string KAnonKeyForAdBid(const blink::InterestGroup& group,
-                             const blink::InterestGroup::Ad& ad) {
+                             const GURL& ad_url) {
   DCHECK(group.ads);
-  DCHECK(base::Contains(*group.ads, ad) ||
-         (group.ad_components && base::Contains(*group.ad_components, ad)));
+  DCHECK(base::ranges::count(*group.ads, ad_url, &extractUrlFromAd) > 0 ||
+         (group.ad_components &&
+          base::ranges::count(*group.ad_components, ad_url, &extractUrlFromAd) >
+              0));
   DCHECK(group.bidding_url);
   return group.owner.GetURL().spec() + '\n' +
-         group.bidding_url.value_or(GURL()).spec() + '\n' +
-         ad.render_url.spec();
+         group.bidding_url.value_or(GURL()).spec() + '\n' + ad_url.spec();
 }
 
 GURL RenderUrlFromKAnonKeyForAdBid(const std::string& key) {
@@ -98,11 +106,11 @@
   }
 }
 
-void InterestGroupKAnonymityManager::RegisterAdAsWon(
-    const blink::InterestGroup& group,
-    const blink::InterestGroup::Ad& ad) {
-  RegisterIDAsJoined(KAnonKeyForAdBid(group, ad));
-  RegisterIDAsJoined(KAnonKeyForAdNameReporting(group, ad));
+void InterestGroupKAnonymityManager::RegisterAdKeysAsJoined(
+    base::flat_set<std::string> keys) {
+  for (const auto& key : keys) {
+    RegisterIDAsJoined(key);
+  }
   // TODO(behamilton): Consider proactively starting a query here to improve the
   // speed that browsers see new ads. We will likely want to rate limit this
   // somehow though.
diff --git a/content/browser/interest_group/interest_group_k_anonymity_manager.h b/content/browser/interest_group/interest_group_k_anonymity_manager.h
index e2c46df..6096a196 100644
--- a/content/browser/interest_group/interest_group_k_anonymity_manager.h
+++ b/content/browser/interest_group/interest_group_k_anonymity_manager.h
@@ -21,16 +21,29 @@
 std::string CONTENT_EXPORT KAnonKeyFor(const url::Origin& owner,
                                        const std::string& name);
 
-// Calculates the k-anonymity key for an Ad that is used for bidding.
+// Calculates the k-anonymity key for an Ad that is used for determining if an
+// ad is k-anonymous for the purposes of bidding and winning an auction.
+// We want to avoid providing too much identifying information for event level
+// reporting in reportWin. This key is used to check that providing the interest
+// group owner and ad URL to the bidding script doesn't identify the user. It is
+// used to gate whether an ad can participate in a FLEDGE auction because event
+// level reports need to include both the owner and ad URL for the purposes of
+// an auction.
+// TODO(behamilton): Use a different key for ad components.
 std::string CONTENT_EXPORT KAnonKeyForAdBid(const blink::InterestGroup& group,
-                                            const blink::InterestGroup::Ad& ad);
+                                            const GURL& ad_url);
 
 // Given a key computed by KAnonKeyForAdBid, returns the `render_url` of the
 // ad that was used to produce it.
 GURL CONTENT_EXPORT RenderUrlFromKAnonKeyForAdBid(const std::string& key);
 
-// Calculates the k-anonymity key for an Ad that is used for reporting the
-// interest group name.
+// Calculates the k-anonymity key for reporting the interest group name in
+// reportWin along with the given Ad.
+// We want to avoid providing too much identifying information for event level
+// reporting in reportWin. This key is used to check if including the interest
+// group name along with the interest group owner and ad URL would make the user
+// too identifiable. If this key is not k-anonymous then we do not provide the
+// interest group name to reportWin.
 std::string CONTENT_EXPORT
 KAnonKeyForAdNameReporting(const blink::InterestGroup& group,
                            const blink::InterestGroup::Ad& ad);
@@ -40,7 +53,7 @@
 // InterestGroupManagerImpl for interest group k-anonymity updates. Calls
 // The InterestGroupManagerImpl to access interest group storage to perform
 // interest group updates.
-class InterestGroupKAnonymityManager {
+class CONTENT_EXPORT InterestGroupKAnonymityManager {
  public:
   InterestGroupKAnonymityManager(
       InterestGroupManagerImpl* interest_group_manager,
@@ -53,10 +66,9 @@
   void QueryKAnonymityForInterestGroup(
       const StorageInterestGroup& storage_group);
 
-  // Notify the k-anonymity service that this ad won an auction. Internally this
-  // calls RegisterIDAsJoined().
-  void RegisterAdAsWon(const blink::InterestGroup& group,
-                       const blink::InterestGroup::Ad& ad);
+  // Notify the k-anonymity service that these ad keys won an auction.
+  // Internally this calls RegisterIDAsJoined().
+  void RegisterAdKeysAsJoined(base::flat_set<std::string> keys);
 
  private:
   // Callback from k-anonymity service QuerySets(). Saves the updated results to
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 b5f1b6d..3f70797ba 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
@@ -111,13 +111,6 @@
     return result;
   }
 
-  absl::optional<base::Time> GetLastAdReported(
-      InterestGroupManagerImpl* manager,
-      const blink::InterestGroup& group,
-      const blink::InterestGroup::Ad& ad) {
-    return GetLastReported(manager, KAnonKeyForAdBid(group, ad));
-  }
-
   std::unique_ptr<InterestGroupManagerImpl> CreateManager(
       bool has_error = false) {
     delegate_ = std::make_unique<TestKAnonymityServiceDelegate>(has_error);
@@ -186,10 +179,15 @@
   blink::InterestGroup group = MakeInterestGroup(owner, "foo");
   group.bidding_url = GURL("https://www.example.com/bidding.js");
 
+  const std::string kAd1KAnonBidKey = KAnonKeyForAdBid(group, GURL(kAdURL));
+  const std::string kAd1KAnonReportNameKey =
+      KAnonKeyForAdNameReporting(group, group.ads.value()[0]);
+
   auto manager = CreateManager();
   EXPECT_FALSE(getGroup(manager.get(), owner, name));
+  EXPECT_EQ(base::Time::Min(), GetLastReported(manager.get(), kAd1KAnonBidKey));
   EXPECT_EQ(base::Time::Min(),
-            GetLastAdReported(manager.get(), group, group.ads.value()[0]));
+            GetLastReported(manager.get(), kAd1KAnonReportNameKey));
 
   manager->JoinInterestGroup(group, top_frame);
   // The group *must* exist when JoinInterestGroup returns.
@@ -200,34 +198,41 @@
 
   // Ads are *not* reported as part of joining an interest group.
   absl::optional<base::Time> reported =
-      GetLastAdReported(manager.get(), group, group.ads.value()[0]);
+      GetLastReported(manager.get(), kAd1KAnonBidKey);
   EXPECT_EQ(base::Time::Min(), reported);
+  EXPECT_EQ(base::Time::Min(),
+            GetLastReported(manager.get(), kAd1KAnonReportNameKey));
 
   base::Time before_mark_ad = base::Time::Now();
-  manager->RegisterAdAsWon(group, group.ads.value()[0]);
+  manager->RegisterAdKeysAsJoined({kAd1KAnonBidKey, kAd1KAnonReportNameKey});
 
   // k-anonymity update happens here.
   task_environment().FastForwardBy(base::Minutes(1));
 
-  reported = GetLastAdReported(manager.get(), group, group.ads.value()[0]);
+  reported = GetLastReported(manager.get(), kAd1KAnonBidKey);
   EXPECT_LE(before_mark_ad, reported);
   ASSERT_TRUE(reported);
   base::Time last_reported = *reported;
+  EXPECT_EQ(last_reported,
+            GetLastReported(manager.get(), kAd1KAnonReportNameKey));
 
-  manager->RegisterAdAsWon(group, group.ads.value()[0]);
+  manager->RegisterAdKeysAsJoined({kAd1KAnonBidKey});
   task_environment().FastForwardBy(base::Minutes(1));
 
   // Second update shouldn't have changed the update time (too recent).
+  EXPECT_EQ(last_reported, GetLastReported(manager.get(), kAd1KAnonBidKey));
   EXPECT_EQ(last_reported,
-            GetLastAdReported(manager.get(), group, group.ads.value()[0]));
+            GetLastReported(manager.get(), kAd1KAnonReportNameKey));
 
   task_environment().FastForwardBy(kJoinInterval);
 
   // Updated more than 24 hours ago, so update.
-  manager->RegisterAdAsWon(group, group.ads.value()[0]);
+  manager->RegisterAdKeysAsJoined({kAd1KAnonBidKey});
   task_environment().RunUntilIdle();
-  EXPECT_LT(last_reported,
-            GetLastAdReported(manager.get(), group, group.ads.value()[0]));
+  EXPECT_LT(last_reported, GetLastReported(manager.get(), kAd1KAnonBidKey));
+  // Other key should not have changed.
+  EXPECT_EQ(last_reported,
+            GetLastReported(manager.get(), kAd1KAnonReportNameKey));
 }
 
 TEST_F(InterestGroupKAnonymityManagerTest, HandlesServerErrors) {
@@ -239,16 +244,17 @@
 
   auto manager = CreateManager(/*has_error=*/true);
   blink::InterestGroup g = MakeInterestGroup(owner, "foo");
+  const std::string kAd1KAnonBidKey = KAnonKeyForAdBid(g, GURL(kAdURL));
 
   manager->JoinInterestGroup(g, top_frame);
   // The group *must* exist when JoinInterestGroup returns.
   ASSERT_TRUE(getGroup(manager.get(), owner, name));
-  manager->RegisterAdAsWon(g, g.ads.value()[0]);
+  manager->RegisterAdKeysAsJoined({kAd1KAnonBidKey});
 
   // k-anonymity update happens here.
   task_environment().FastForwardBy(base::Minutes(1));
 
-  // If the updates succeed then we normally would not record the update as
+  // If the update failed then we normally would not record the update as
   // having been completed, so we would try it later.
   // For now we'll record the update as having been completed to to reduce
   // bandwidth and provide more accurate use counts.
@@ -256,7 +262,7 @@
   // values below.
 
   absl::optional<base::Time> ad_reported =
-      GetLastAdReported(manager.get(), g, g.ads.value()[0]);
+      GetLastReported(manager.get(), kAd1KAnonBidKey);
   ASSERT_TRUE(ad_reported);
 
   // TODO(behamilton): Change this once we expect the server to be stable.
@@ -280,8 +286,8 @@
 
   const blink::InterestGroup::Ad& ad = group.ads.value()[0];
 
-  EXPECT_EQ(ad.render_url,
-            RenderUrlFromKAnonKeyForAdBid(KAnonKeyForAdBid(group, ad)));
+  EXPECT_EQ(ad.render_url, RenderUrlFromKAnonKeyForAdBid(
+                               KAnonKeyForAdBid(group, ad.render_url)));
 }
 
 class MockAnonymityServiceDelegate : public KAnonymityServiceDelegate {
@@ -339,14 +345,15 @@
   const GURL ad1 = GURL(kAdURL);
 
   blink::InterestGroup group1 = MakeInterestGroup(owner, "foo");
+  const std::string kAd1KAnonKey = KAnonKeyForAdBid(group1, ad1);
 
   // Join one group twice, and an overlapping group once.
   manager->JoinInterestGroup(group1, top_frame);
   manager->JoinInterestGroup(group1, top_frame);
   manager->JoinInterestGroup(MakeInterestGroup(owner, "bar"), top_frame);
 
-  manager->RegisterAdAsWon(group1, group1.ads.value()[0]);
-  manager->RegisterAdAsWon(group1, group1.ads.value()[0]);
+  manager->RegisterAdKeysAsJoined({kAd1KAnonKey});
+  manager->RegisterAdKeysAsJoined({kAd1KAnonKey});
 
   // k-anonymity update happens here.
   task_environment().FastForwardBy(base::Minutes(1));
diff --git a/content/browser/interest_group/interest_group_manager_impl.cc b/content/browser/interest_group/interest_group_manager_impl.cc
index 72b36d3..087cf580 100644
--- a/content/browser/interest_group/interest_group_manager_impl.cc
+++ b/content/browser/interest_group/interest_group_manager_impl.cc
@@ -248,10 +248,9 @@
       .WithArgs(group_key, std::move(ad_json));
 }
 
-void InterestGroupManagerImpl::RegisterAdAsWon(
-    const blink::InterestGroup& group,
-    const blink::InterestGroup::Ad& ad) {
-  k_anonymity_manager_->RegisterAdAsWon(group, ad);
+void InterestGroupManagerImpl::RegisterAdKeysAsJoined(
+    base::flat_set<std::string> keys) {
+  k_anonymity_manager_->RegisterAdKeysAsJoined(std::move(keys));
 }
 
 void InterestGroupManagerImpl::GetInterestGroup(
diff --git a/content/browser/interest_group/interest_group_manager_impl.h b/content/browser/interest_group/interest_group_manager_impl.h
index 881a879..0a126c0 100644
--- a/content/browser/interest_group/interest_group_manager_impl.h
+++ b/content/browser/interest_group/interest_group_manager_impl.h
@@ -179,10 +179,9 @@
   void RecordInterestGroupWin(const blink::InterestGroupKey& group_key,
                               const std::string& ad_json);
 
-  // Reports the ad to the k-anonymity service. Should be called when FLEDGE
-  // selects an ad.
-  void RegisterAdAsWon(const blink::InterestGroup& group,
-                       const blink::InterestGroup::Ad& ad);
+  // Reports the ad keys to the k-anonymity service. Should be called when
+  // FLEDGE selects an ad.
+  void RegisterAdKeysAsJoined(base::flat_set<std::string> keys);
 
   // Gets a single interest group.
   void GetInterestGroup(
diff --git a/content/browser/interest_group/interest_group_storage.cc b/content/browser/interest_group/interest_group_storage.cc
index 32f4c7e..d472d9348 100644
--- a/content/browser/interest_group/interest_group_storage.cc
+++ b/content/browser/interest_group/interest_group_storage.cc
@@ -1733,9 +1733,10 @@
     if (db_interest_group.interest_group.ads) {
       for (auto& ad : db_interest_group.interest_group.ads.value()) {
         absl::optional<StorageInterestGroup::KAnonymityData> ad_kanon;
-        if (!DoGetKAnonymity(
-                db, KAnonKeyForAdBid(db_interest_group.interest_group, ad),
-                ad_kanon)) {
+        if (!DoGetKAnonymity(db,
+                             KAnonKeyForAdBid(db_interest_group.interest_group,
+                                              ad.render_url),
+                             ad_kanon)) {
           return absl::nullopt;
         }
         if (!ad_kanon)
@@ -1759,9 +1760,10 @@
     if (db_interest_group.interest_group.ad_components) {
       for (auto& ad : db_interest_group.interest_group.ad_components.value()) {
         absl::optional<StorageInterestGroup::KAnonymityData> ad_kanon;
-        if (!DoGetKAnonymity(
-                db, KAnonKeyForAdBid(db_interest_group.interest_group, ad),
-                ad_kanon)) {
+        if (!DoGetKAnonymity(db,
+                             KAnonKeyForAdBid(db_interest_group.interest_group,
+                                              ad.render_url),
+                             ad_kanon)) {
           return absl::nullopt;
         }
         if (!ad_kanon)
diff --git a/content/browser/interest_group/interest_group_storage_unittest.cc b/content/browser/interest_group/interest_group_storage_unittest.cc
index 25e432f2..5eef4d2 100644
--- a/content/browser/interest_group/interest_group_storage_unittest.cc
+++ b/content/browser/interest_group/interest_group_storage_unittest.cc
@@ -550,12 +550,10 @@
   groups = storage->GetInterestGroupsForOwner(test_origin);
 
   std::vector<StorageInterestGroup::KAnonymityData> expected_bidding = {
-      {KAnonKeyForAdBid(g, g.ads.value()[0]), false, base::Time::Min()},
-      {KAnonKeyForAdBid(g, g.ads.value()[1]), false, base::Time::Min()},
-      {KAnonKeyForAdBid(g, g.ad_components.value()[0]), false,
-       base::Time::Min()},
-      {KAnonKeyForAdBid(g, g.ad_components.value()[1]), false,
-       base::Time::Min()},
+      {KAnonKeyForAdBid(g, ad1_url), false, base::Time::Min()},
+      {KAnonKeyForAdBid(g, ad2_url), false, base::Time::Min()},
+      {KAnonKeyForAdBid(g, ad1_url), false, base::Time::Min()},
+      {KAnonKeyForAdBid(g, ad3_url), false, base::Time::Min()},
   };
   std::vector<StorageInterestGroup::KAnonymityData> expected_reporting = {
       {KAnonKeyForAdNameReporting(g, g.ads.value()[0]), false,
@@ -571,8 +569,8 @@
               testing::UnorderedElementsAreArray(expected_reporting));
 
   base::Time update_time = base::Time::Now();
-  StorageInterestGroup::KAnonymityData kanon_bid{
-      KAnonKeyForAdBid(g, g.ads.value()[0]), true, update_time};
+  StorageInterestGroup::KAnonymityData kanon_bid{KAnonKeyForAdBid(g, ad1_url),
+                                                 true, update_time};
   StorageInterestGroup::KAnonymityData kanon_report{
       KAnonKeyForAdNameReporting(g, g.ads.value()[0]), true, update_time};
   storage->UpdateKAnonymity(kanon_bid);
@@ -592,8 +590,8 @@
   task_environment().FastForwardBy(base::Seconds(1));
 
   update_time = base::Time::Now();
-  kanon_bid = StorageInterestGroup::KAnonymityData{
-      KAnonKeyForAdBid(g, g.ads.value()[1]), true, update_time};
+  kanon_bid = StorageInterestGroup::KAnonymityData{KAnonKeyForAdBid(g, ad2_url),
+                                                   true, update_time};
   kanon_report = StorageInterestGroup::KAnonymityData{
       KAnonKeyForAdNameReporting(g, g.ads.value()[1]), true, update_time};
   storage->UpdateKAnonymity(kanon_bid);
@@ -636,11 +634,11 @@
   // Update the k-anonymity data.
   base::Time update_kanon_time = base::Time::Now();
   StorageInterestGroup::KAnonymityData ad1_bid_kanon{
-      KAnonKeyForAdBid(g, g.ads.value()[0]), true, update_kanon_time};
+      KAnonKeyForAdBid(g, ad1_url), true, update_kanon_time};
   StorageInterestGroup::KAnonymityData ad1_report_kanon{
       KAnonKeyForAdNameReporting(g, g.ads.value()[0]), true, update_kanon_time};
   StorageInterestGroup::KAnonymityData ad2_bid_kanon{
-      KAnonKeyForAdBid(g, g.ad_components.value()[0]), true, update_kanon_time};
+      KAnonKeyForAdBid(g, ad2_url), true, update_kanon_time};
   storage->UpdateKAnonymity(ad1_bid_kanon);
   storage->UpdateKAnonymity(ad1_report_kanon);
   storage->UpdateKAnonymity(ad2_bid_kanon);
@@ -689,11 +687,11 @@
   storage->JoinInterestGroup(g, GURL("https://owner.example.com/join3"));
 
   // K-anon data should be the default.
-  ad1_bid_kanon = {KAnonKeyForAdBid(g, g.ads.value()[0]),
+  ad1_bid_kanon = {KAnonKeyForAdBid(g, ad1_url),
                    /*is_k_anonymous=*/false, base::Time::Min()};
   ad1_report_kanon = {KAnonKeyForAdNameReporting(g, g.ads.value()[0]),
                       /*is_k_anonymous=*/false, base::Time::Min()};
-  ad2_bid_kanon = {KAnonKeyForAdBid(g, g.ad_components.value()[0]),
+  ad2_bid_kanon = {KAnonKeyForAdBid(g, ad2_url),
                    /*is_k_anonymous=*/false, base::Time::Min()};
   groups = storage->GetInterestGroupsForOwner(test_origin);
   ASSERT_EQ(1u, groups.size());
@@ -725,7 +723,7 @@
   g1.ads.emplace();
   g1.ads->push_back(blink::InterestGroup::Ad(ad1_url, "metadata1"));
 
-  std::string k_anon_key = KAnonKeyForAdBid(g1, g1.ads.value()[0]);
+  std::string k_anon_key = KAnonKeyForAdBid(g1, ad1_url);
 
   std::unique_ptr<InterestGroupStorage> storage = CreateStorage();
   storage->JoinInterestGroup(g1, joining_originA.GetURL());
diff --git a/content/browser/mojo_binder_policy_applier.cc b/content/browser/mojo_binder_policy_applier.cc
index da507713..5ed66a4c 100644
--- a/content/browser/mojo_binder_policy_applier.cc
+++ b/content/browser/mojo_binder_policy_applier.cc
@@ -4,9 +4,28 @@
 
 #include "content/browser/mojo_binder_policy_applier.h"
 
+#include "base/containers/contains.h"
+#include "base/containers/fixed_flat_set.h"
 #include "content/public/browser/mojo_binder_policy_map.h"
 #include "mojo/public/cpp/bindings/message.h"
 
+namespace {
+
+// TODO(https://crbug.com/1245961): It is not sustainable to maintain a list.
+// An ideal solution should:
+// 1. Show a pre-submit warning if a frame-scoped interface is specified with
+//    kDefer but declares synchronous methods.
+// 2. When an interface that can make sync IPC is registered with BinderMap,
+//    change its policy to kCancel by default.
+// 3. Bind these receivers to a generic implementation, and terminate the
+//    execution context if it receives a synchronous message.
+// Stores the list of interface names that declare sync methods.
+constexpr auto kSyncMethodInterfaces =
+    base::MakeFixedFlatSet<base::StringPiece>(
+        {"blink.mojom.NotificationService"});
+
+}  // namespace
+
 namespace content {
 
 MojoBinderPolicyApplier::MojoBinderPolicyApplier(
@@ -54,7 +73,11 @@
         std::move(binder_callback).Run();
         break;
       case MojoBinderNonAssociatedPolicy::kDefer:
-        deferred_binders_.push_back(std::move(binder_callback));
+        if (base::Contains(kSyncMethodInterfaces, interface_name)) {
+          std::move(binder_callback).Run();
+        } else {
+          deferred_binders_.push_back(std::move(binder_callback));
+        }
         break;
     }
     return;
@@ -71,7 +94,11 @@
       }
       break;
     case MojoBinderNonAssociatedPolicy::kDefer:
-      deferred_binders_.push_back(std::move(binder_callback));
+      if (base::Contains(kSyncMethodInterfaces, interface_name)) {
+        deferred_sync_binders_.push_back(std::move(binder_callback));
+      } else {
+        deferred_binders_.push_back(std::move(binder_callback));
+      }
       break;
     case MojoBinderNonAssociatedPolicy::kUnexpected:
       mojo::ReportBadMessage("MBPA_BAD_INTERFACE: " + interface_name);
@@ -104,6 +131,15 @@
 
 void MojoBinderPolicyApplier::PrepareToGrantAll() {
   DCHECK_EQ(mode_, Mode::kEnforce);
+
+  // The remote side would think its status has changed after the browser
+  // executes this method, so it is safe to send some synchronous method, so the
+  // browser side should make the IPC pipeline ready.
+  for (auto& deferred_binder : deferred_sync_binders_) {
+    std::move(deferred_binder).Run();
+  }
+  deferred_sync_binders_.clear();
+
   mode_ = Mode::kPrepareToGrantAll;
 }
 
diff --git a/content/browser/mojo_binder_policy_applier.h b/content/browser/mojo_binder_policy_applier.h
index f794ff9..ab24ebe 100644
--- a/content/browser/mojo_binder_policy_applier.h
+++ b/content/browser/mojo_binder_policy_applier.h
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/functional/callback_forward.h"
 #include "base/memory/raw_ref.h"
 #include "content/browser/mojo_binder_policy_map_impl.h"
 #include "content/common/content_export.h"
@@ -111,11 +112,17 @@
       MojoBinderNonAssociatedPolicy::kDefer;
   // Maps Mojo interface name to its policy.
   const raw_ref<const MojoBinderPolicyMapImpl> policy_map_;
+
   // Will be executed upon a request for a kCancel interface.
   base::OnceCallback<void(const std::string& interface_name)> cancel_callback_;
   Mode mode_ = Mode::kEnforce;
+
   // Stores binders which are delayed running.
   std::vector<base::OnceClosure> deferred_binders_;
+
+  // Stores binders that can be used to send synchronous messages but
+  // are delayed running.
+  std::vector<base::OnceClosure> deferred_sync_binders_;
 };
 
 }  // namespace content
diff --git a/content/browser/network/trust_token_browsertest.cc b/content/browser/network/trust_token_browsertest.cc
index a1e5289..29000d9e 100644
--- a/content/browser/network/trust_token_browsertest.cc
+++ b/content/browser/network/trust_token_browsertest.cc
@@ -289,7 +289,7 @@
   std::string command = JsReplace(R"(
   (async () => {
     await fetch("/issue", {trustToken: {type: 'token-request'}});
-    return await document.hasPrivateStateToken($1);
+    return await document.hasPrivateToken($1, 'private-state-token');
   })();)",
                                   IssuanceOriginFromHost("a.test"));
 
@@ -681,10 +681,11 @@
   // context's list of associated issuers.
   for (int i = 0;
        i < network::kTrustTokenPerToplevelMaxNumberOfAssociatedIssuers; ++i) {
-    ASSERT_EQ("Success",
-              EvalJs(shell(), "document.hasPrivateStateToken('https://a" +
-                                  base::NumberToString(i) +
-                                  ".test').then(()=>'Success');"));
+    ASSERT_EQ(
+        "Success",
+        EvalJs(shell(),
+               "document.hasPrivateToken('https://a" + base::NumberToString(i) +
+                   ".test', 'private-state-token').then(()=>'Success');"));
   }
 
   EXPECT_EQ("OperationError", EvalJs(shell(), R"(
@@ -717,12 +718,16 @@
              JsReplace(command,
                        server_.GetURL("a.test", "/cross-site/b.test/issue"))));
 
-  EXPECT_EQ(true,
-            EvalJs(shell(), JsReplace("document.hasPrivateStateToken($1);",
-                                      IssuanceOriginFromHost("b.test"))));
-  EXPECT_EQ(false,
-            EvalJs(shell(), JsReplace("document.hasPrivateStateToken($1);",
-                                      IssuanceOriginFromHost("a.test"))));
+  EXPECT_EQ(
+      true,
+      EvalJs(shell(),
+             JsReplace("document.hasPrivateToken($1, 'private-state-token');",
+                       IssuanceOriginFromHost("b.test"))));
+  EXPECT_EQ(
+      false,
+      EvalJs(shell(),
+             JsReplace("document.hasPrivateToken($1, 'private-state-token');",
+                       IssuanceOriginFromHost("a.test"))));
 }
 
 // When an issuance request is made in no-cors mode, a cross-origin redirect
@@ -751,12 +756,16 @@
              JsReplace(command,
                        server_.GetURL("a.test", "/cross-site/b.test/issue"))));
 
-  EXPECT_EQ(true,
-            EvalJs(shell(), JsReplace("document.hasPrivateStateToken($1);",
-                                      IssuanceOriginFromHost("a.test"))));
-  EXPECT_EQ(false,
-            EvalJs(shell(), JsReplace("document.hasPrivateStateToken($1);",
-                                      IssuanceOriginFromHost("b.test"))));
+  EXPECT_EQ(
+      true,
+      EvalJs(shell(),
+             JsReplace("document.hasPrivateToken($1, 'private-state-token');",
+                       IssuanceOriginFromHost("a.test"))));
+  EXPECT_EQ(
+      false,
+      EvalJs(shell(),
+             JsReplace("document.hasPrivateToken($1, 'private-state-token');",
+                       IssuanceOriginFromHost("b.test"))));
 }
 
 // Issuance from a context with a secure-but-non-HTTP/S top frame origin
@@ -783,7 +792,7 @@
   EXPECT_EQ(
       false,
       EvalJs(shell(),
-             JsReplace("document.hasPrivateStateToken($1);",
+             JsReplace("document.hasPrivateToken($1, 'private-state-token');",
                        url::Origin::Create(server_.base_url()).Serialize())));
 }
 
@@ -816,7 +825,7 @@
       EvalJs(shell(), JsReplace(command, server_.GetURL("a.test", "/redeem"))));
 }
 
-// hasPrivateStateToken from a context with a secure-but-non-HTTP/S top frame
+// hasPrivateToken from a context with a secure-but-non-HTTP/S top frame
 // origin should fail.
 IN_PROC_BROWSER_TEST_F(TrustTokenBrowsertest,
                        HasTrustTokenRequiresSuitableTopFrameOrigin) {
@@ -824,13 +833,15 @@
   ASSERT_TRUE(file_url.SchemeIsFile());
   ASSERT_TRUE(NavigateToURL(shell(), file_url));
 
-  EXPECT_EQ("NotAllowedError",
-            EvalJs(shell(),
-                   R"(document.hasPrivateStateToken('https://issuer.example')
+  EXPECT_EQ(
+      "NotAllowedError",
+      EvalJs(
+          shell(),
+          R"(document.hasPrivateToken('https://issuer.example', 'private-state-token')
                               .catch(error => error.name);)"));
 }
 
-// A hasPrivateStateToken call initiated from a secure context should succeed
+// A hasPrivateToken call initiated from a secure context should succeed
 // even if the initiating frame's origin is opaque (e.g. from a sandboxed
 // iframe).
 IN_PROC_BROWSER_TEST_F(TrustTokenBrowsertest,
@@ -842,9 +853,11 @@
                             ->GetPrimaryFrameTree()
                             .root();
 
-  EXPECT_EQ("Success",
-            EvalJs(root->child_at(0)->current_frame_host(),
-                   R"(document.hasPrivateStateToken('https://davids.website')
+  EXPECT_EQ(
+      "Success",
+      EvalJs(
+          root->child_at(0)->current_frame_host(),
+          R"(document.hasPrivateToken('https://davids.website', 'private-state-token')
                               .then(()=>'Success');)"));
 }
 
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 55056d9..fba64c4 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -10683,7 +10683,7 @@
     return;
   }
 
-  // TODO(crbug.com/1145346): Document.hasPrivateStateToken is restricted to
+  // TODO(crbug.com/1145346): Document.hasPrivateToken is restricted to
   // secure contexts, so we could additionally add a check verifying that the
   // bind request "is coming from a secure context"---but there's currently no
   // direct way to perform such a check in the browser.
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 33c911bf..e5e4cde8f 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -3329,6 +3329,7 @@
     switches::kDisableSpeechAPI,
     switches::kDisableThreadedCompositing,
     switches::kDisableTouchDragDrop,
+    switches::kDisableUseMojoVideoDecoderForPepper,
     switches::kDisableV8IdleTasks,
     switches::kDisableVideoCaptureUseGpuMemoryBuffer,
     switches::kDisableWebGLImageChromium,
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 953824e9..0d4b4167 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -8,6 +8,7 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <tuple>
 #include <utility>
 
 #include "base/bind.h"
@@ -4265,12 +4266,14 @@
 class ServiceWorkerBypassFetchHandlerTest
     : public ServiceWorkerBrowserTest,
       public testing::WithParamInterface<
-          ServiceWorkerBypassFetchHandlerBypassedOriginType> {
+          std::tuple<ServiceWorkerBypassFetchHandlerBypassedOriginType, bool>> {
  public:
   ServiceWorkerBypassFetchHandlerTest() {
     feature_list_.InitWithFeaturesAndParameters(
         {{features::kServiceWorkerBypassFetchHandler,
-          {{"origins_to_bypass", "https://a.test"}}}},
+          {{"origins_to_bypass", "https://a.test"},
+           {"strategy",
+            ShouldUseAllowListStrategy() ? "allowlist" : "optin"}}}},
         {});
   }
   ~ServiceWorkerBypassFetchHandlerTest() override = default;
@@ -4292,6 +4295,12 @@
     return web_contents()->GetPrimaryMainFrame();
   }
 
+ protected:
+  ServiceWorkerBypassFetchHandlerBypassedOriginType GetBypassedOriginType() {
+    return std::get<0>(GetParam());
+  }
+  bool ShouldUseAllowListStrategy() { return std::get<1>(GetParam()); }
+
  private:
   base::test::ScopedFeatureList feature_list_;
   net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
@@ -4300,13 +4309,15 @@
 INSTANTIATE_TEST_SUITE_P(
     All,
     ServiceWorkerBypassFetchHandlerTest,
-    testing::Values(
-        ServiceWorkerBypassFetchHandlerBypassedOriginType::kBypassed,
-        ServiceWorkerBypassFetchHandlerBypassedOriginType::kNotBypassed));
+    testing::Combine(
+        testing::Values(
+            ServiceWorkerBypassFetchHandlerBypassedOriginType::kBypassed,
+            ServiceWorkerBypassFetchHandlerBypassedOriginType::kNotBypassed),
+        testing::Bool()));
 
 IN_PROC_BROWSER_TEST_P(ServiceWorkerBypassFetchHandlerTest, UrlInAllowList) {
   std::string origin;
-  switch (GetParam()) {
+  switch (GetBypassedOriginType()) {
     case ServiceWorkerBypassFetchHandlerBypassedOriginType::kBypassed:
       origin = "a.test";
       break;
@@ -4358,20 +4369,155 @@
   // Navigate to the service worker's scope.
   EXPECT_TRUE(NavigateToURL(shell(), in_scope_url));
 
-  switch (GetParam()) {
-    case ServiceWorkerBypassFetchHandlerBypassedOriginType::kBypassed:
-      // If bypassing is allowed, the service worker was bypassed and the
-      // navigation request shouldn't be handled by the fetch handler.
-      EXPECT_EQ(0, EvalJs(GetPrimaryMainFrame(), script));
-      break;
-    case ServiceWorkerBypassFetchHandlerBypassedOriginType::kNotBypassed:
-      // If bypassing is not allowed, the navigation request should be handled
-      // by the fetch handler.
-      EXPECT_EQ(1, EvalJs(GetPrimaryMainFrame(), script));
-      break;
+  if (ShouldUseAllowListStrategy()) {
+    switch (GetBypassedOriginType()) {
+      case ServiceWorkerBypassFetchHandlerBypassedOriginType::kBypassed:
+        // If bypassing is allowed, the service worker was bypassed and the
+        // navigation request shouldn't be handled by the fetch handler.
+        EXPECT_EQ(0, EvalJs(GetPrimaryMainFrame(), script));
+        break;
+      case ServiceWorkerBypassFetchHandlerBypassedOriginType::kNotBypassed:
+        // If bypassing is not allowed, the navigation request should be handled
+        // by the fetch handler.
+        EXPECT_EQ(1, EvalJs(GetPrimaryMainFrame(), script));
+        break;
+    }
+  } else {
+    // If the allowlist isn't used, the service worker was bypassed and the
+    // navigation request shouldn't be handled by the fetch handler.
+    EXPECT_EQ(0, EvalJs(GetPrimaryMainFrame(), script));
   }
 }
 
+class ServiceWorkerBypassFetchHandlerOriginTrialTest
+    : public ServiceWorkerBrowserTest {
+ public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // The public key for the default privatey key used by the
+    // tools/origin_trials/generate_token.py tool.
+    static constexpr char kOriginTrialTestPublicKey[] =
+        "dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA=";
+    command_line->AppendSwitchASCII("origin-trial-public-key",
+                                    kOriginTrialTestPublicKey);
+  }
+  WebContents* web_contents() const { return shell()->web_contents(); }
+
+  RenderFrameHost* GetPrimaryMainFrame() {
+    return web_contents()->GetPrimaryMainFrame();
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBypassFetchHandlerOriginTrialTest,
+                       BypassFetchHandlerForMainResource) {
+  embedded_test_server()->StartAcceptingConnections();
+
+  // The URL that was used to register the Origin Trial token.
+  static constexpr char kOriginUrl[] = "https://127.0.0.1:44444";
+  // Generated by running (in tools/origin_trials):
+  // tools/origin_trials/generate_token.py https://127.0.0.1:44444 \
+  // ServiceWorkerBypassFetchHandlerForMainResource \
+  // --expire-timestamp=2000000000
+  static constexpr char kOriginTrialToken[] =
+      "A7lJi6aWVTbSCCs9Ju3k4SKBnTzE/"
+      "9j7OMHWdF2pjJLLZU5Fdt7IzilJOFp37hMyoeIUq4gCTdb9wSIC9jhU/"
+      "wgAAAB4eyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0NDQiLCAiZmVhdHVyZSI6I"
+      "CJTZXJ2aWNlV29ya2VyQnlwYXNzRmV0Y2hIYW5kbGVyRm9yTWFpblJlc291cmNlIiwgImV4c"
+      "GlyeSI6IDIwMDAwMDAwMDB9";
+
+  const GURL main_page_url(
+      base::StrCat({kOriginUrl, "/create_service_worker.html"}));
+  const GURL service_worker_url(
+      base::StrCat({kOriginUrl, "/fetch_event_pass_through.js"}));
+
+  std::map<GURL, int /* number_of_invocations */> expected_request_urls = {
+      {main_page_url, 2},
+      {service_worker_url, 1},
+  };
+
+  base::RunLoop run_loop;
+
+  // The origin trial token is associated with an origin. We can't guarantee the
+  // EmbeddedTestServer to use a specific port. So the URLLoaderInterceptor is
+  // used instead.
+  URLLoaderInterceptor service_worker_loader(base::BindLambdaForTesting(
+      [&](URLLoaderInterceptor::RequestParams* params) {
+        auto it = expected_request_urls.find(params->url_request.url);
+        if (it == expected_request_urls.end()) {
+          return false;
+        }
+
+        const std::string content_type =
+            base::EndsWith(params->url_request.url.path_piece(), ".js")
+                ? "text/javascript"
+                : "text/html";
+
+        const std::string origin_trial_token =
+            params->url_request.url == service_worker_url ? kOriginTrialToken
+                                                          : "";
+
+        const std::string headers = base::ReplaceStringPlaceholders(
+            "HTTP/1.1 200 OK\n"
+            "Content-type: $1\n"
+            "Origin-Trial: $2\n"
+            "\n",
+            {content_type, origin_trial_token}, {});
+
+        URLLoaderInterceptor::WriteResponse(
+            "content/test/data/service_worker" + params->url_request.url.path(),
+            params->client.get(), &headers, absl::optional<net::SSLInfo>(),
+            params->url_request.url);
+
+        if (--it->second == 0) {
+          expected_request_urls.erase(it);
+        }
+        if (expected_request_urls.empty()) {
+          run_loop.Quit();
+        }
+        return true;
+      }));
+
+  // Register a service worker.
+  WorkerRunningStatusObserver observer(public_context());
+  EXPECT_TRUE(NavigateToURL(shell(), main_page_url));
+  EXPECT_EQ("DONE", EvalJs(GetPrimaryMainFrame(),
+                           "register('/fetch_event_pass_through.js')"));
+  observer.WaitUntilRunning();
+  scoped_refptr<ServiceWorkerVersion> version =
+      wrapper()->GetLiveVersion(observer.version_id());
+  EXPECT_EQ(EmbeddedWorkerStatus::RUNNING, version->running_status());
+
+  // Stop the current running service worker.
+  StopServiceWorker(version.get());
+  EXPECT_EQ(EmbeddedWorkerStatus::STOPPED, version->running_status());
+
+  // Navigate away from the service worker's scope.
+  EXPECT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/empty.html")));
+  EXPECT_EQ(EmbeddedWorkerStatus::STOPPED, version->running_status());
+
+  // Navigate to the service worker's scope.
+  EXPECT_TRUE(NavigateToURL(shell(), main_page_url));
+
+  // The service worker was bypassed and the navigation request shouldn't be
+  // handled by the fetch handler.
+  // The script asks the service worker what fetch events it saw.
+  EXPECT_EQ(0, EvalJs(GetPrimaryMainFrame(), R"(
+      (async () => {
+        const saw_message = new Promise(resolve => {
+          navigator.serviceWorker.onmessage = event => {
+            resolve(event.data);
+          };
+        });
+        const registration = await navigator.serviceWorker.ready;
+        registration.active.postMessage('');
+        const message = await saw_message;
+        return message.length;
+      })();
+  )"));
+
+  run_loop.Run();
+}
+
 class ServiceWorkerSkipEmptyFetchHandlerBrowserTest
     : public ServiceWorkerBrowserTest,
       public testing::WithParamInterface<bool> {
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.cc b/content/browser/service_worker/service_worker_controllee_request_handler.cc
index 64ff2139f..6db1e3d 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.cc
@@ -102,24 +102,67 @@
 }
 
 bool ShouldBypassFetchHandlerForMainResource(const GURL& stripped_url) {
+  if (!base::FeatureList::IsEnabled(
+          features::kServiceWorkerBypassFetchHandler)) {
+    return false;
+  }
+
+  if (features::kServiceWorkerBypassFetchHandlerTarget.Get() !=
+      features::ServiceWorkerBypassFetchHandlerTarget::kMainResource) {
+    return false;
+  }
+
   // If the feature is enabled, the main resource request bypasses ServiceWorker
   // and starts the worker in parallel for subsequent subresources.
-  if (base::FeatureList::IsEnabled(
-          features::kServiceWorkerBypassFetchHandler) &&
-      features::kServiceWorkerBypassFetchHandlerTarget.Get() ==
-          features::ServiceWorkerBypassFetchHandlerTarget::kMainResource) {
-    // When the url is in the allowlist, fetch handlers for the main resource
-    // are bypassed.
-    const static base::NoDestructor<std::vector<url::Origin>> bypassed_origins(
-        FetchHandlerBypassedOrigins());
-    for (const auto& it : *bypassed_origins) {
-      // Skip comparing port numbers because some tests run the mock HTTP server
-      // with a random port number.
-      if (it.scheme() == stripped_url.scheme() &&
-          it.host() == stripped_url.host())
-        return true;
-    }
+  switch (features::kServiceWorkerBypassFetchHandlerStrategy.Get()) {
+    // kFeatureOptIn means that the feature relies on the manual feature
+    // toggle from about://flags etc, which is triggered by developers. We
+    // bypass fetch handler regardless of the url matching in this case.
+    case features::ServiceWorkerBypassFetchHandlerStrategy::kFeatureOptIn:
+      RecordSkipReason(
+          ServiceWorkerControlleeRequestHandler::FetchHandlerSkipReason::
+              kMainResourceSkippedDueToFeatureFlag);
+      return true;
+    // If kAllowList, the allowlist should be specified. In this case, main
+    // resource fetch handlers are bypassed only when the url's origin is in
+    // the allowlist.
+    case features::ServiceWorkerBypassFetchHandlerStrategy::kAllowList:
+      const static base::NoDestructor<std::vector<url::Origin>>
+          bypassed_origins(FetchHandlerBypassedOrigins());
+      for (const auto& it : *bypassed_origins) {
+        // Skip comparing port numbers because some tests run the mock HTTP
+        // server with a random port number.
+        if (it.scheme() == stripped_url.scheme() &&
+            it.host() == stripped_url.host()) {
+          RecordSkipReason(
+              ServiceWorkerControlleeRequestHandler::FetchHandlerSkipReason::
+                  kMainResourceSkippedBecauseMatchedWithAllowedOriginList);
+          return true;
+        }
+      }
+      return false;
   }
+
+  NOTREACHED();
+  return false;
+}
+
+bool ShouldBypassFetchHandlerForMainResourceByOriginTrial(
+    ServiceWorkerVersion* version) {
+  if (version->origin_trial_tokens() &&
+      version->origin_trial_tokens()->contains(
+          "ServiceWorkerBypassFetchHandlerForMainResource")) {
+    RecordSkipReason(
+        ServiceWorkerControlleeRequestHandler::FetchHandlerSkipReason::
+            kMainResourceSkippedDueToOriginTrial);
+    // The UseCounter for kServiceWorkerBypassFetchHandlerForMainResource should
+    // only capture the usage of this feature invoked by the Origin Trial for
+    // the OT measurement purpose.
+    version->CountFeature(blink::mojom::WebFeature::
+                              kServiceWorkerBypassFetchHandlerForMainResource);
+    return true;
+  }
+
   return false;
 }
 
@@ -511,12 +554,14 @@
     }
   }
 
-  // If the feature is enabled, the main resource request bypasses ServiceWorker
-  // and starts the worker in parallel for subsequent subresources.
-  if (ShouldBypassFetchHandlerForMainResource(stripped_url_)) {
-    registration->active_version()->CountFeature(
-        blink::mojom::WebFeature::
-            kServiceWorkerBypassFetchHandlerForMainResource);
+  // Check if the fetch handler should bypassed or not.
+  // First, check the origin trial token. If there is no valid origin trial
+  // token, then check the eligibility based on the feature flag and the url.
+  if (ShouldBypassFetchHandlerForMainResourceByOriginTrial(
+          registration->active_version()) ||
+      ShouldBypassFetchHandlerForMainResource(stripped_url_)) {
+    // If true, the main resource request bypasses ServiceWorker and starts the
+    // worker in parallel for subsequent subresources.
     CompleteWithoutLoader();
     if (registration->active_version()->running_status() ==
             EmbeddedWorkerStatus::STARTING ||
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.h b/content/browser/service_worker/service_worker_controllee_request_handler.h
index 864cfad..3f852e4 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.h
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.h
@@ -45,12 +45,22 @@
  public:
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
+  //
+  // Only one reason is recorded even if multiple reasons are matched.
+  // The order is following:
+  // 1. kSkippedForEmptyFetchHandler
+  // 2. kMainResourceSkippedDueToOriginTrial
+  // 3. kMainResourceSkippedDueToFeatureFlag
+  // 4. kMainResourceSkippedBecauseMatchedWithAllowedOriginList
   enum class FetchHandlerSkipReason {
     kNoFetchHandler = 0,
     kNotSkipped = 1,
     kSkippedForEmptyFetchHandler = 2,
+    kMainResourceSkippedDueToOriginTrial = 3,
+    kMainResourceSkippedDueToFeatureFlag = 4,
+    kMainResourceSkippedBecauseMatchedWithAllowedOriginList = 5,
 
-    kMaxValue = kSkippedForEmptyFetchHandler,
+    kMaxValue = kMainResourceSkippedBecauseMatchedWithAllowedOriginList,
   };
 
   // If |skip_service_worker| is true, service workers are bypassed for
diff --git a/content/browser/storage_partition_impl_unittest.cc b/content/browser/storage_partition_impl_unittest.cc
index f43e0b5..66882b3 100644
--- a/content/browser/storage_partition_impl_unittest.cc
+++ b/content/browser/storage_partition_impl_unittest.cc
@@ -35,6 +35,7 @@
 #include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "components/attribution_reporting/suitable_origin.h"
 #include "components/services/storage/dom_storage/async_dom_storage_database.h"
 #include "components/services/storage/dom_storage/dom_storage_database.h"
 #include "components/services/storage/dom_storage/local_storage_database.pb.h"
@@ -107,6 +108,8 @@
 namespace content {
 namespace {
 
+using ::attribution_reporting::SuitableOrigin;
+
 const char kCacheKey[] = "key";
 const char kCacheValue[] = "cached value";
 
@@ -274,7 +277,7 @@
     interest_group_manager->JoinInterestGroup(group, origin.GetURL());
 
     // Update the K-anonymity so that we can tell when it gets removed.
-    k_anon_key = KAnonKeyForAdBid(group, group.ads.value()[0]);
+    k_anon_key = KAnonKeyForAdBid(group, GURL("https://owner.example.com/ad1"));
     interest_group_manager->UpdateLastKAnonymityReported(k_anon_key);
   }
 
@@ -1823,8 +1826,8 @@
 
   base::Time now = base::Time::Now();
   for (int i = 0; i < 20; i++) {
-    auto origin = url::Origin::Create(
-        GURL(base::StringPrintf("https://www.%d.test/", i)));
+    auto origin = *SuitableOrigin::Deserialize(
+        base::StringPrintf("https://www.%d.test/", i));
     auto source = SourceBuilder(now)
                       .SetExpiry(base::Days(2))
                       .SetSourceOrigin(origin)
@@ -1850,12 +1853,12 @@
 
   base::Time now = base::Time::Now();
   for (int i = 0; i < 5; i++) {
-    auto impression =
-        url::Origin::Create(GURL(base::StringPrintf("https://imp-%d.com/", i)));
-    auto reporter = url::Origin::Create(
-        GURL(base::StringPrintf("https://reporter-%d.com/", i)));
-    auto conv = url::Origin::Create(
-        GURL(base::StringPrintf("https://conv-%d.com/", i)));
+    auto impression = *SuitableOrigin::Deserialize(
+        base::StringPrintf("https://imp-%d.com/", i));
+    auto reporter = *SuitableOrigin::Deserialize(
+        base::StringPrintf("https://reporter-%d.com/", i));
+    auto conv = *SuitableOrigin::Deserialize(
+        base::StringPrintf("https://conv-%d.com/", i));
     attribution_manager->HandleSource(SourceBuilder(now)
                                           .SetSourceOrigin(impression)
                                           .SetReportingOrigin(reporter)
diff --git a/content/browser/tracing/tracing_controller_browsertest.cc b/content/browser/tracing/tracing_controller_browsertest.cc
index 885468a..233eb95 100644
--- a/content/browser/tracing/tracing_controller_browsertest.cc
+++ b/content/browser/tracing/tracing_controller_browsertest.cc
@@ -36,8 +36,8 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chromeos/ash/components/dbus/debug_daemon/debug_daemon_client.h"
-#include "chromeos/system/fake_statistics_provider.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #endif
 
 using base::trace_event::RECORD_CONTINUOUSLY;
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index 0311acd..e743645 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -58,7 +58,7 @@
 #include "v8/include/v8-version-string.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "content/browser/tracing/cros_tracing_agent.h"
 #endif
 
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index d91627e4..45038772 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -293,6 +293,8 @@
     {wf::EnableGetDisplayMediaSet, features::kGetDisplayMediaSet},
     {wf::EnableGetDisplayMediaSetAutoSelectAllScreens,
      features::kGetDisplayMediaSetAutoSelectAllScreens},
+    {wf::EnableServiceWorkerBypassFetchHandler,
+     features::kServiceWorkerBypassFetchHandler},
   };
   for (const auto& mapping : blinkFeatureToBaseFeatureMapping) {
     SetRuntimeFeatureFromChromiumFeature(
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 04740b4..4561219b 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -930,6 +930,16 @@
              "ServiceWorkerBypassFetchHandler",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+const base::FeatureParam<ServiceWorkerBypassFetchHandlerStrategy>::Option
+    service_worker_bypass_fetch_handler_strategy_options[] = {
+        {ServiceWorkerBypassFetchHandlerStrategy::kFeatureOptIn, "optin"},
+        {ServiceWorkerBypassFetchHandlerStrategy::kAllowList, "allowlist"}};
+const base::FeatureParam<ServiceWorkerBypassFetchHandlerStrategy>
+    kServiceWorkerBypassFetchHandlerStrategy{
+        &kServiceWorkerBypassFetchHandler, "strategy",
+        ServiceWorkerBypassFetchHandlerStrategy::kFeatureOptIn,
+        &service_worker_bypass_fetch_handler_strategy_options};
+
 const base::FeatureParam<ServiceWorkerBypassFetchHandlerTarget>::Option
     service_worker_bypass_fetch_handler_target_options[] = {{
         ServiceWorkerBypassFetchHandlerTarget::kMainResource,
@@ -1269,6 +1279,12 @@
              "WebAssemblyDynamicTiering",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// Enable support for the WebAssembly Garbage Collection proposal:
+// https://github.com/WebAssembly/gc.
+BASE_FEATURE(kWebAssemblyGarbageCollection,
+             "WebAssemblyGarbageCollection",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Enable WebAssembly lazy compilation (JIT on first call).
 BASE_FEATURE(kWebAssemblyLazyCompilation,
              "WebAssemblyLazyCompilation",
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index aa57d09..be83b73 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -215,6 +215,30 @@
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kDisableProcessReuse);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSkipEarlyCommitPendingForCrashedFrame);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kServiceWorkerBypassFetchHandler);
+// ServiceWorkerBypassFetchHandlerStrategy provides the info how to decide if
+// the request should bypass fetch handlers or not.
+enum class ServiceWorkerBypassFetchHandlerStrategy {
+  // Use the allowlist provided by
+  // kServiceWorkerBypassFetchHandlerBypassedOrigins. If the request url's
+  // origin is in the list, fetch handlers are bypassed.
+  kAllowList,
+
+  // This option is to run the feature locally for the debugging purpose. It is
+  // used for the feature toggle in about:flags etc. It simply bypasses fetch
+  // handlers for all the main resource requests regardless of the url while the
+  // feature is enabled.
+  //
+  // This is set as a default value, but the origin trial uses a different
+  // mechanism to enable the feature per origin. When the feature is enabled by
+  // the origin trial, ServiceWorkerVersion in content/browser should contain
+  // the origin trial token. If the browser successfully confirm the token,
+  // fetch handlers are always bypassed regardless of
+  // ServiceWorkerBypassFetchHandlerStrategy.
+  kFeatureOptIn,
+};
+CONTENT_EXPORT extern const base::FeatureParam<
+    ServiceWorkerBypassFetchHandlerStrategy>
+    kServiceWorkerBypassFetchHandlerStrategy;
 enum class ServiceWorkerBypassFetchHandlerTarget {
   // Bypass fetch handlers for main resource (navigation) requests. Fetch
   // handlers will be bypassed regardless of the current ServiceWorker running
@@ -267,6 +291,7 @@
 CONTENT_EXPORT BASE_DECLARE_FEATURE(
     kEnableExperimentalWebAssemblyStackSwitching);
 #endif  // defined(ARCH_CPU_X86_64)
+CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebAssemblyGarbageCollection);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebAssemblyLazyCompilation);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebAssemblyRelaxedSimd);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebAssemblyTiering);
diff --git a/content/renderer/pepper/pepper_video_decoder_host.cc b/content/renderer/pepper/pepper_video_decoder_host.cc
index ec9d317..4f06fe5 100644
--- a/content/renderer/pepper/pepper_video_decoder_host.cc
+++ b/content/renderer/pepper/pepper_video_decoder_host.cc
@@ -160,7 +160,7 @@
   min_picture_count_ = min_picture_count;
 
   if (acceleration != PP_HARDWAREACCELERATION_NONE) {
-    if (!base::FeatureList::IsEnabled(media::kUseMojoVideoDecoderForPepper)) {
+    if (!media::IsUseMojoVideoDecoderForPepperEnabled()) {
       // This is not synchronous, but subsequent IPC messages will be buffered,
       // so it is okay to immediately send IPC messages.
       if (command_buffer->channel()) {
diff --git a/content/renderer/pepper/video_decoder_shim.cc b/content/renderer/pepper/video_decoder_shim.cc
index 896593aa..04aa8f8 100644
--- a/content/renderer/pepper/video_decoder_shim.cc
+++ b/content/renderer/pepper/video_decoder_shim.cc
@@ -221,7 +221,7 @@
     media::VideoDecoderConfig config,
     media::GpuVideoAcceleratorFactories* gpu_factories) {
   DCHECK(use_hw_decoder_);
-  CHECK(base::FeatureList::IsEnabled(media::kUseMojoVideoDecoderForPepper));
+  CHECK(media::IsUseMojoVideoDecoderForPepperEnabled());
 
   DCHECK(gpu_factories->GetTaskRunner()->RunsTasksInCurrentSequence());
   if (!gpu_factories->IsGpuVideoDecodeAcceleratorEnabled()) {
@@ -282,8 +282,9 @@
   // media::kUseMojoVideoDecoderForPepper path. However we keep it here because
   // we don't want to change anything about the previous codepath as part of
   // introducing the new flag-guarded codepath.
-  if (!base::FeatureList::IsEnabled(media::kUseMojoVideoDecoderForPepper))
+  if (!media::IsUseMojoVideoDecoderForPepperEnabled()) {
     DCHECK(decoder_);
+  }
 
   // Clear pending decodes now. We don't want OnDecodeComplete to call DoDecode
   // again.
@@ -412,7 +413,7 @@
   scoped_refptr<viz::ContextProviderCommandBuffer>
       shared_main_thread_context_provider =
           RenderThreadImpl::current()->SharedMainThreadContextProvider();
-  if (base::FeatureList::IsEnabled(media::kUseMojoVideoDecoderForPepper) &&
+  if (media::IsUseMojoVideoDecoderForPepperEnabled() &&
       !shared_main_thread_context_provider) {
     return nullptr;
   }
@@ -451,8 +452,7 @@
       texture_pool_size_(texture_pool_size),
       num_pending_decodes_(0),
       use_hw_decoder_(use_hw_decoder) {
-  CHECK(!use_hw_decoder_ ||
-        base::FeatureList::IsEnabled(media::kUseMojoVideoDecoderForPepper));
+  CHECK(!use_hw_decoder_ || media::IsUseMojoVideoDecoderForPepperEnabled());
   DCHECK(host_);
   DCHECK(media_task_runner_.get());
   DCHECK(shared_main_thread_context_provider_.get());
diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc
index bc75e105..07a0e9c4 100644
--- a/content/renderer/render_process_impl.cc
+++ b/content/renderer/render_process_impl.cc
@@ -169,6 +169,11 @@
                         "--no-experimental-wasm-stack-switching");
 #endif  // defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64)
 
+  SetV8FlagIfFeature(features::kWebAssemblyGarbageCollection,
+                     "--experimental-wasm-gc");
+  SetV8FlagIfNotFeature(features::kWebAssemblyGarbageCollection,
+                        "--no-experimental-wasm-gc");
+
   SetV8FlagIfFeature(features::kWebAssemblyLazyCompilation,
                      "--wasm-lazy-compilation");
   SetV8FlagIfNotFeature(features::kWebAssemblyLazyCompilation,
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index e6dec2f..27228180 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1955,7 +1955,7 @@
       "//chromeos/ash/components/audio",
       "//chromeos/ash/components/dbus/audio",
       "//chromeos/ash/components/dbus/debug_daemon",
-      "//chromeos/system",
+      "//chromeos/ash/components/system",
     ]
   }
 
diff --git a/content/test/attribution_simulator_input_parser_unittest.cc b/content/test/attribution_simulator_input_parser_unittest.cc
index 5c834ba..df72b79 100644
--- a/content/test/attribution_simulator_input_parser_unittest.cc
+++ b/content/test/attribution_simulator_input_parser_unittest.cc
@@ -189,12 +189,12 @@
           Pair(SourceBuilder(kOffsetTime + base::Milliseconds(1643235574123))
                    .SetSourceType(AttributionSourceType::kNavigation)
                    .SetReportingOrigin(
-                       url::Origin::Create(GURL("https://a.r.test")))
+                       *SuitableOrigin::Deserialize("https://a.r.test"))
                    .SetSourceOrigin(
-                       url::Origin::Create(GURL("https://a.s.test")))
+                       *SuitableOrigin::Deserialize("https://a.s.test"))
                    .SetSourceEventId(123)
                    .SetDestinationOrigin(
-                       url::Origin::Create(GURL("https://a.d.test")))
+                       *SuitableOrigin::Deserialize("https://a.d.test"))
                    .SetExpiry(base::Days(10))
                    .SetEventReportWindow(base::Days(10))
                    .SetAggregatableReportWindow(base::Days(10))
@@ -206,12 +206,12 @@
           Pair(SourceBuilder(kOffsetTime + base::Milliseconds(1643235573123))
                    .SetSourceType(AttributionSourceType::kEvent)
                    .SetReportingOrigin(
-                       url::Origin::Create(GURL("https://b.r.test")))
+                       *SuitableOrigin::Deserialize("https://b.r.test"))
                    .SetSourceOrigin(
-                       url::Origin::Create(GURL("https://b.s.test")))
+                       *SuitableOrigin::Deserialize("https://b.s.test"))
                    .SetSourceEventId(0)  // default
                    .SetDestinationOrigin(
-                       url::Origin::Create(GURL("https://b.d.test")))
+                       *SuitableOrigin::Deserialize("https://b.d.test"))
                    .SetExpiry(base::Days(30))                    // default
                    .SetEventReportWindow(base::Days(30))         // default
                    .SetAggregatableReportWindow(base::Days(30))  // default
@@ -223,12 +223,12 @@
               SourceBuilder(kOffsetTime + base::Milliseconds(1643235575123))
                   .SetSourceType(AttributionSourceType::kEvent)
                   .SetReportingOrigin(
-                      url::Origin::Create(GURL("https://c.r.test")))
+                      *SuitableOrigin::Deserialize("https://c.r.test"))
                   .SetSourceOrigin(
-                      url::Origin::Create(GURL("https://c.s.test")))
+                      *SuitableOrigin::Deserialize("https://c.s.test"))
                   .SetSourceEventId(789)
                   .SetDestinationOrigin(
-                      url::Origin::Create(GURL("https://c.d.test")))
+                      *SuitableOrigin::Deserialize("https://c.d.test"))
                   .SetExpiry(base::Days(10))  // rounded to whole number of days
                   .SetEventReportWindow(
                       base::Days(10))  // rounded to whole number of days
@@ -247,12 +247,12 @@
               SourceBuilder(kOffsetTime + base::Milliseconds(1643235576123))
                   .SetSourceType(AttributionSourceType::kEvent)
                   .SetReportingOrigin(
-                      url::Origin::Create(GURL("https://c.r.test")))
+                      *SuitableOrigin::Deserialize("https://c.r.test"))
                   .SetSourceOrigin(
-                      url::Origin::Create(GURL("https://c.s.test")))
+                      *SuitableOrigin::Deserialize("https://c.s.test"))
                   .SetSourceEventId(789)
                   .SetDestinationOrigin(
-                      url::Origin::Create(GURL("https://c.d.test")))
+                      *SuitableOrigin::Deserialize("https://c.d.test"))
                   .SetExpiry(base::Days(10))  // rounded to whole number of days
                   .SetEventReportWindow(
                       base::Days(8))  // rounded to whole number of days
diff --git a/content/test/data/accessibility/html/svg-elements-not-mapped.html b/content/test/data/accessibility/html/svg-elements-not-mapped.html
index 38ccc21..d90290a5 100644
--- a/content/test/data/accessibility/html/svg-elements-not-mapped.html
+++ b/content/test/data/accessibility/html/svg-elements-not-mapped.html
@@ -8,20 +8,20 @@
 <body>
 <svg aria-label="animate should not be exposed">
   <rect x="5" y="5" width="20" height="15">
-    <animate attributeName="opacity" from="1" to="0" dur="5s" repeatCount="indefinite"/>
+    <animate attributeName="opacity" from="1" to="0" dur="500ms" repeatCount="2"/>
   </rect>
 </svg>
 <svg aria-label="animateMotion should not be exposed">
   <path id="path2" d="M100,250 C 100,50 400,50 400,222" fill="none" stroke="blue" stroke-width="7.06" />
   <path d="M-25,-12.5 L25,-12.5 L 0,-87.5 z" fill="yellow" stroke="red" stroke-width="7.06">
-    <animateMotion dur="6s" repeatCount="indefinite" rotate="auto">
+    <animateMotion dur="500ms" repeatCount="2" rotate="auto">
       <mpath href="#path2"/>
     </animateMotion>
   </path>
 </svg>
 <svg aria-label="animateTransform should not be exposed">
   <rect width="100" height="100">
-    <animateTransform attributeName="transform" type="scale" from="2" to="3" repeatCount="3" dur="4s" fill="freeze"/>
+    <animateTransform attributeName="transform" type="scale" from="2" to="3" repeatCount="3" dur="500ms" fill="freeze"/>
   </rect>
 </svg>
 <svg aria-label="clipPath should not be exposed">
@@ -42,7 +42,7 @@
 </svg>
 <svg aria-label="discard should not be exposed">
   <ellipse cx="98.5" cy="17.5" rx="20.5" ry="17.5" fill="blue" stroke="black" transform="translate(9 252) translate(3 -296)">
-    <animateTransform attributeName="transform" begin="0s" dur="2s" fill="remove" calcMode="linear" type="translate" additive="sum" from="0 0" to="-18 305"/>
+    <animateTransform attributeName="transform" begin="0s" dur="500ms" fill="remove" calcMode="linear" type="translate" additive="sum" from="0 0" to="-18 305"/>
     <discard begin="2s"/>
   </ellipse>
 </svg>
@@ -120,7 +120,7 @@
 <svg aria-label="mpath should not be exposed">
   <path id="path16" d="M100,250 C 100,50 400,50 400,222" fill="none" stroke="blue" stroke-width="7.06"/>
   <path d="M-25,-12.5 L25,-12.5 L 0,-87.5 z" fill="yellow" stroke="red" stroke-width="7.06">
-    <animateMotion dur="6s" repeatCount="indefinite" rotate="auto">
+    <animateMotion dur="500ms" repeatCount="2" rotate="auto">
       <mpath xlink:href="#path16"/>
     </animateMotion>
   </path>
@@ -156,7 +156,7 @@
     .round { rx: 5px; fill: green; }
   </style>
   <rect id="clickable" width="10" height="10">
-    <set attributeName="class" to="round" begin="clickable.click" dur="2s" />
+    <set attributeName="class" to="round" begin="clickable.click" dur="500ms" />
   </rect>
 </svg>
 <svg aria-label="switch should not be exposed">
diff --git a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
index 30b202b..46ad2d2 100644
--- a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
@@ -122,8 +122,6 @@
 
 # canvas2d flakily crashes, which then affects the following test.
 # finder:disable-stale Nature of crash causes stale result finder to remove these
-crbug.com/1383609 [ android android-sm-a135m ] GpuProcess_canvas2d [ Failure ]
-crbug.com/1383609 [ android android-sm-a135m ] GpuProcess_css3d [ Failure ]
 crbug.com/1383609 [ android android-sm-a235m ] GpuProcess_canvas2d [ Failure ]
 crbug.com/1383609 [ android android-sm-a235m ] GpuProcess_css3d [ Failure ]
 # finder:enable-stale
diff --git a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
index 454cf9d..1556baa 100644
--- a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
@@ -237,8 +237,6 @@
 # finder:group-end
 
 # 2DCanvasWebGL flakily crashes, which then affects the following test.
-crbug.com/1383609 [ android android-sm-a135m ] TraceTest_2DCanvasWebGL [ Failure ]
-crbug.com/1383609 [ android android-sm-a135m ] TraceTest_BackgroundImage [ Failure ]
 crbug.com/1383609 [ android android-sm-a235m ] TraceTest_2DCanvasWebGL [ Failure ]
 crbug.com/1383609 [ android android-sm-a235m ] TraceTest_BackgroundImage [ Failure ]
 
diff --git a/docs/enterprise/enrollment.md b/docs/enterprise/enrollment.md
index 8e879be..30e85a24 100644
--- a/docs/enterprise/enrollment.md
+++ b/docs/enterprise/enrollment.md
@@ -70,7 +70,7 @@
 
 ### OEM-triggered Enrollment
 
-Device manufacturers can provide special [OEM manifest](https://cs.chromium.org/chromium/src/chromeos/system/statistics_provider.cc?rcl=2e366a611abdd2be6995e625f3281d40fab5b5e3&l=83)
+Device manufacturers can provide special [OEM manifest](https://cs.chromium.org/chromium/src/chromeos/ash/components/system/statistics_provider.cc?rcl=2e366a611abdd2be6995e625f3281d40fab5b5e3&l=83)
 that controls if device should be enrolled, and if enrollment is forced.
 Authentication is the same as in **Manual enrollment** case.
 
diff --git a/extensions/browser/api/storage/session_storage_manager_unittest.cc b/extensions/browser/api/storage/session_storage_manager_unittest.cc
index d91cf22..54e4dd2 100644
--- a/extensions/browser/api/storage/session_storage_manager_unittest.cc
+++ b/extensions/browser/api/storage/session_storage_manager_unittest.cc
@@ -44,10 +44,10 @@
         value_string_("value"),
         value_list_(base::Value::Type::LIST),
         value_dict_(base::Value::Type::DICTIONARY) {
-    value_list_.Append(1);
-    value_list_.Append(2);
-    value_dict_.SetIntKey("int", 123);
-    value_dict_.SetStringKey("string", "abc");
+    value_list_.GetList().Append(1);
+    value_list_.GetList().Append(2);
+    value_dict_.GetDict().Set("int", 123);
+    value_dict_.GetDict().Set("string", "abc");
   }
 
  protected:
diff --git a/extensions/browser/api/storage/settings_quota_unittest.cc b/extensions/browser/api/storage/settings_quota_unittest.cc
index f1bb6f6..3dd0c003 100644
--- a/extensions/browser/api/storage/settings_quota_unittest.cc
+++ b/extensions/browser/api/storage/settings_quota_unittest.cc
@@ -31,7 +31,7 @@
         byte_value_256_(base::Value(base::Value::Type::LIST)),
         delegate_(new value_store::TestingValueStore()) {
     for (int i = 1; i < 89; ++i) {
-      byte_value_256_.Append(i);
+      byte_value_256_.GetList().Append(i);
     }
     ValidateByteValues();
   }
diff --git a/extensions/browser/api/storage/storage_api.cc b/extensions/browser/api/storage/storage_api.cc
index 50ddb66..bf65d68 100644
--- a/extensions/browser/api/storage/storage_api.cc
+++ b/extensions/browser/api/storage/storage_api.cc
@@ -55,19 +55,19 @@
 std::vector<std::string> GetKeysFromDict(const base::Value& dict) {
   DCHECK(dict.is_dict());
   std::vector<std::string> keys;
-  keys.reserve(dict.DictSize());
-  for (auto value : dict.DictItems()) {
+  keys.reserve(dict.GetDict().size());
+  for (auto value : dict.GetDict()) {
     keys.push_back(value.first);
   }
   return keys;
 }
 
-// Converts a map to a Value::Type::DICTIONARY.
-base::Value MapAsValueDict(
+// Converts a map to a Value::Dict.
+base::Value::Dict MapAsValueDict(
     const std::map<std::string, const base::Value*>& values) {
-  base::Value dict(base::Value::Type::DICTIONARY);
+  base::Value::Dict dict;
   for (const auto& value : values)
-    dict.SetKey(value.first, value.second->Clone());
+    dict.Set(value.first, value.second->Clone());
   return dict;
 }
 
@@ -91,16 +91,16 @@
 // Returns a nested dictionary Value converted from a ValueChange.
 base::Value ValueChangeToValue(
     std::vector<SessionStorageManager::ValueChange> changes) {
-  base::Value changes_value(base::Value::Type::DICTIONARY);
+  base::Value::Dict changes_value;
   for (auto& change : changes) {
-    base::Value change_value(base::Value::Type::DICTIONARY);
+    base::Value::Dict change_value;
     if (change.old_value.has_value())
-      change_value.SetKey("oldValue", std::move(change.old_value.value()));
+      change_value.Set("oldValue", std::move(change.old_value.value()));
     if (change.new_value)
-      change_value.SetKey("newValue", change.new_value->Clone());
-    changes_value.SetKey(change.key, std::move(change_value));
+      change_value.Set("newValue", change.new_value->Clone());
+    changes_value.Set(change.key, std::move(change_value));
   }
-  return changes_value;
+  return base::Value(std::move(changes_value));
 }
 
 }  // namespace
@@ -280,7 +280,7 @@
     return BadMessage();
   base::Value& input = mutable_args()[0];
 
-  base::Value value_dict(base::Value::Type::DICTIONARY);
+  base::Value::Dict value_dict;
   SessionStorageManager* session_manager =
       SessionStorageManager::GetForBrowserContext(browser_context());
 
@@ -303,12 +303,12 @@
       std::map<std::string, const base::Value*> values =
           session_manager->Get(extension_id(), GetKeysFromDict(input));
 
-      for (auto default_value : input.DictItems()) {
+      for (auto default_value : input.GetDict()) {
         auto value_it = values.find(default_value.first);
-        value_dict.SetKey(default_value.first,
-                          value_it != values.end()
-                              ? value_it->second->Clone()
-                              : std::move(default_value.second));
+        value_dict.Set(default_value.first,
+                       value_it != values.end()
+                           ? value_it->second->Clone()
+                           : std::move(default_value.second));
       }
       break;
     }
@@ -316,7 +316,7 @@
       return BadMessage();
   }
 
-  return OneArgument(std::move(value_dict));
+  return OneArgument(base::Value(std::move(value_dict)));
 }
 
 ExtensionFunction::ResponseValue
@@ -404,7 +404,7 @@
   mutable_args().erase(args().begin());
 
   std::map<std::string, base::Value> values;
-  for (auto item : input.DictItems()) {
+  for (auto item : input.GetDict()) {
     values.emplace(std::move(item.first), std::move(item.second));
   }
 
diff --git a/extensions/browser/extension_function.cc b/extensions/browser/extension_function.cc
index cd10fd86..9d94c36 100644
--- a/extensions/browser/extension_function.cc
+++ b/extensions/browser/extension_function.cc
@@ -171,6 +171,10 @@
     } else {
       base::UmaHistogramSparse("Extensions.Functions.SucceededTime.Over10ms",
                                histogram_value);
+      if (elapsed_time >= base::Seconds(270)) {
+        base::UmaHistogramSparse("Extensions.Functions.SucceededTime.Over270s",
+                                 histogram_value);
+      }
     }
     UMA_HISTOGRAM_TIMES("Extensions.Functions.SucceededTotalExecutionTime",
                         elapsed_time);
@@ -187,6 +191,10 @@
     } else {
       base::UmaHistogramSparse("Extensions.Functions.FailedTime.Over10ms",
                                histogram_value);
+      if (elapsed_time >= base::Seconds(270)) {
+        base::UmaHistogramSparse("Extensions.Functions.FailedTime.Over270s",
+                                 histogram_value);
+      }
     }
     base::UmaHistogramTimes(
         WrapUma("Extensions.Functions.FailedTotalExecutionTime",
diff --git a/extensions/common/api/_behavior_features.json b/extensions/common/api/_behavior_features.json
index 55b1803..b6f110f 100644
--- a/extensions/common/api/_behavior_features.json
+++ b/extensions/common/api/_behavior_features.json
@@ -49,7 +49,8 @@
     "allowlist": [
       // https://crbug.com/626342
       "85DA1AC24AF23CDA9F5A19858EB9C6E9E1BA57F6",  // Sign-in Screen Test App
-      "EC3DE21E048B67319893889529354DFBFA96FD23",  // Smart Card Connector
+      "EC3DE21E048B67319893889529354DFBFA96FD23",  // https://crbug.com/1233881
+      "A19608AC34215B127FF9D7C006D67F5C8ED8146D",  // https://crbug.com/1233881
       "6B748A5C005F21B7CBCF4170C2F883E435DEB511"   // CSSI Smart Card Middleware
     ]
   }, {
@@ -59,8 +60,8 @@
     "location": "policy",
     "platforms": ["chromeos"],
     "allowlist": [
-      // https://crbug.com/1233881
-      "EC3DE21E048B67319893889529354DFBFA96FD23",   // Smart Card Connector
+      "EC3DE21E048B67319893889529354DFBFA96FD23",   // https://crbug.com/1233881
+      "A19608AC34215B127FF9D7C006D67F5C8ED8146D",   // https://crbug.com/1233881
       // https://crbug.com/1194693
       "6B748A5C005F21B7CBCF4170C2F883E435DEB511",   // CSSI Smart Card Middleware
       "075FF17D52ED6E3C2E5EC4D99F188E7A25AF47EA"    // Beta CSSI Smart Card Middleware
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json
index f6988f3..5ef3aa5 100644
--- a/extensions/common/api/_permission_features.json
+++ b/extensions/common/api/_permission_features.json
@@ -548,8 +548,8 @@
       "location": "policy",
       "platforms": ["chromeos"],
       "allowlist": [
-        // https://crbug.com/1233881
-        "EC3DE21E048B67319893889529354DFBFA96FD23",   // Smart Card Connector
+        "EC3DE21E048B67319893889529354DFBFA96FD23",   // https://crbug.com/1233881
+        "A19608AC34215B127FF9D7C006D67F5C8ED8146D",   // https://crbug.com/1233881
         // https://crbug.com/1194693
         "6B748A5C005F21B7CBCF4170C2F883E435DEB511",   // CSSI Smart Card Middleware
         "075FF17D52ED6E3C2E5EC4D99F188E7A25AF47EA"    // Beta CSSI Smart Card Middleware
diff --git a/extensions/common/csp_validator.cc b/extensions/common/csp_validator.cc
index 69d759c..5f0edbe 100644
--- a/extensions/common/csp_validator.cc
+++ b/extensions/common/csp_validator.cc
@@ -595,9 +595,10 @@
   return csp_enforcer.Enforce(csp_parser.directives(), warnings);
 }
 
-std::string GetEffectiveSandoxedPageCSP(const std::string& policy,
-                                        std::string manifest_key,
-                                        std::vector<InstallWarning>* warnings) {
+std::string GetSandboxedPageCSPDisallowingRemoteSources(
+    const std::string& policy,
+    std::string manifest_key,
+    std::vector<InstallWarning>* warnings) {
   CSPParser csp_parser(policy);
   AppSandboxPageCSPEnforcer csp_enforcer(std::move(manifest_key));
   return csp_enforcer.Enforce(csp_parser.directives(), warnings);
diff --git a/extensions/common/csp_validator.h b/extensions/common/csp_validator.h
index a6aa067..9a6f7a3 100644
--- a/extensions/common/csp_validator.h
+++ b/extensions/common/csp_validator.h
@@ -110,18 +110,17 @@
     int options,
     std::vector<InstallWarning>* warnings);
 
-// Given the Content Security Policy of an app sandbox page, returns the
-// effective CSP for that sandbox page.
-//
-// The effective policy restricts the page from loading external web content
+// Given a `policy`, returns a sandboxed page CSP that disallows remote sources.
+// The returned policy restricts the page from loading external web content
 // (frames and scripts) within the page. This is done through adding 'self'
 // directive source to relevant CSP directive names.
 //
 // If |warnings| is not nullptr, any validation errors are appended to
 // |warnings|.
-std::string GetEffectiveSandoxedPageCSP(const std::string& policy,
-                                        std::string manifest_key,
-                                        std::vector<InstallWarning>* warnings);
+std::string GetSandboxedPageCSPDisallowingRemoteSources(
+    const std::string& policy,
+    std::string manifest_key,
+    std::vector<InstallWarning>* warnings);
 
 // Checks whether the given |policy| enforces a unique origin sandbox as
 // defined by http://www.whatwg.org/specs/web-apps/current-work/multipage/
diff --git a/extensions/common/csp_validator_fuzzer.cc b/extensions/common/csp_validator_fuzzer.cc
index ef176ab..a1310eb15 100644
--- a/extensions/common/csp_validator_fuzzer.cc
+++ b/extensions/common/csp_validator_fuzzer.cc
@@ -47,8 +47,8 @@
       /*options=*/fuzzed_data_provider.ConsumeIntegralInRange(0, 4),
       &install_warnings);
 
-  csp_validator::GetEffectiveSandoxedPageCSP(content_security_policy,
-                                             manifest_key, &install_warnings);
+  csp_validator::GetSandboxedPageCSPDisallowingRemoteSources(
+      content_security_policy, manifest_key, &install_warnings);
 
   std::u16string error;
   csp_validator::DoesCSPDisallowRemoteCode(content_security_policy,
diff --git a/extensions/common/csp_validator_unittest.cc b/extensions/common/csp_validator_unittest.cc
index f64a663..6aa541e 100644
--- a/extensions/common/csp_validator_unittest.cc
+++ b/extensions/common/csp_validator_unittest.cc
@@ -15,16 +15,16 @@
 #include "extensions/common/manifest_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using extensions::csp_validator::ContentSecurityPolicyIsLegal;
-using extensions::csp_validator::GetEffectiveSandoxedPageCSP;
-using extensions::csp_validator::SanitizeContentSecurityPolicy;
-using extensions::csp_validator::ContentSecurityPolicyIsSandboxed;
-using extensions::csp_validator::OPTIONS_NONE;
-using extensions::csp_validator::OPTIONS_ALLOW_UNSAFE_EVAL;
-using extensions::csp_validator::OPTIONS_ALLOW_INSECURE_OBJECT_SRC;
 using extensions::ErrorUtils;
 using extensions::InstallWarning;
 using extensions::Manifest;
+using extensions::csp_validator::ContentSecurityPolicyIsLegal;
+using extensions::csp_validator::ContentSecurityPolicyIsSandboxed;
+using extensions::csp_validator::GetSandboxedPageCSPDisallowingRemoteSources;
+using extensions::csp_validator::OPTIONS_ALLOW_INSECURE_OBJECT_SRC;
+using extensions::csp_validator::OPTIONS_ALLOW_UNSAFE_EVAL;
+using extensions::csp_validator::OPTIONS_NONE;
+using extensions::csp_validator::SanitizeContentSecurityPolicy;
 
 namespace {
 
@@ -70,7 +70,7 @@
 
 SanitizedCSPResult SanitizeSandboxPageCSP(const std::string& policy) {
   SanitizedCSPResult result;
-  result.csp = GetEffectiveSandoxedPageCSP(
+  result.csp = GetSandboxedPageCSPDisallowingRemoteSources(
       policy, extensions::manifest_keys::kSandboxedPagesCSP, &result.warnings);
   return result;
 }
diff --git a/extensions/common/manifest_handlers/csp_info.cc b/extensions/common/manifest_handlers/csp_info.cc
index 7c6e5e2..d6aa43a 100644
--- a/extensions/common/manifest_handlers/csp_info.cc
+++ b/extensions/common/manifest_handlers/csp_info.cc
@@ -243,8 +243,12 @@
     return false;
   }
 
+  // Since this is a MV2 extension / app, we don't allow remote sources in
+  // the CSP.
+  constexpr bool kAllowRemoteSources = false;
   if (!ParseSandboxCSP(extension, error, keys::kSandboxedPagesCSP,
-                       GetManifestPath(extension, keys::kSandboxedPagesCSP))) {
+                       GetManifestPath(extension, keys::kSandboxedPagesCSP),
+                       kAllowRemoteSources)) {
     return false;
   }
 
@@ -260,6 +264,9 @@
     return false;
   }
 
+  // Since this is an MV3 extension, we allow remote sources in the
+  // sandboxed page CSP.
+  constexpr bool kAllowRemoteSources = true;
   return ParseExtensionPagesCSP(
              extension, error, keys::kContentSecurityPolicy_ExtensionPagesPath,
              GetManifestPath(
@@ -267,7 +274,8 @@
          ParseSandboxCSP(
              extension, error, keys::kContentSecurityPolicy_SandboxedPagesPath,
              GetManifestPath(extension,
-                             keys::kContentSecurityPolicy_SandboxedPagesPath));
+                             keys::kContentSecurityPolicy_SandboxedPagesPath),
+             kAllowRemoteSources);
 }
 
 bool CSPHandler::ParseExtensionPagesCSP(
@@ -315,7 +323,8 @@
 bool CSPHandler::ParseSandboxCSP(Extension* extension,
                                  std::u16string* error,
                                  base::StringPiece manifest_key,
-                                 const base::Value* sandbox_csp) {
+                                 const base::Value* sandbox_csp,
+                                 bool allow_remote_sources) {
   if (!sandbox_csp) {
     SetSandboxCSP(extension, kDefaultSandboxedPageContentSecurityPolicy);
     return true;
@@ -335,9 +344,12 @@
   }
 
   std::vector<InstallWarning> warnings;
-  std::string effective_sandbox_csp =
-      csp_validator::GetEffectiveSandoxedPageCSP(
-          sandbox_csp_str, std::string(manifest_key), &warnings);
+  std::string effective_sandbox_csp = sandbox_csp_str;
+  if (!allow_remote_sources) {
+    effective_sandbox_csp =
+        csp_validator::GetSandboxedPageCSPDisallowingRemoteSources(
+            sandbox_csp_str, std::string(manifest_key), &warnings);
+  }
   SetSandboxCSP(extension, std::move(effective_sandbox_csp));
   extension->AddInstallWarnings(std::move(warnings));
   return true;
diff --git a/extensions/common/manifest_handlers/csp_info.h b/extensions/common/manifest_handlers/csp_info.h
index ba6607d8..de4df56f 100644
--- a/extensions/common/manifest_handlers/csp_info.h
+++ b/extensions/common/manifest_handlers/csp_info.h
@@ -87,11 +87,14 @@
                               const base::Value* content_security_policy);
 
   // Parses the content security policy specified in the manifest for sandboxed
-  // pages. This should be called after ParseExtensionPagesCSP.
+  // pages. This should be called after ParseExtensionPagesCSP. If
+  // `allow_remote_sources` is true, this allows the extension to specify remote
+  // sources in the sandbox CSP.
   bool ParseSandboxCSP(Extension* extension,
                        std::u16string* error,
                        base::StringPiece manifest_key,
-                       const base::Value* sandbox_csp);
+                       const base::Value* sandbox_csp,
+                       bool allow_remote_sources);
 
   // Helper to set the extension pages content security policy manifest data.
   bool SetExtensionPagesCSP(Extension* extension,
diff --git a/extensions/common/manifest_handlers/csp_info_unittest.cc b/extensions/common/manifest_handlers/csp_info_unittest.cc
index 10289f6..f290e030 100644
--- a/extensions/common/manifest_handlers/csp_info_unittest.cc
+++ b/extensions/common/manifest_handlers/csp_info_unittest.cc
@@ -188,7 +188,7 @@
   ScopedCurrentChannel channel(version_info::Channel::UNKNOWN);
 
   const char kCustomSandboxedCSP[] =
-      "sandbox; script-src 'self'; child-src 'self';";
+      "sandbox; script-src https://www.google.com";
   const char kCustomExtensionPagesCSP[] = "script-src; object-src;";
 
   struct {
diff --git a/gpu/command_buffer/service/common_decoder.cc b/gpu/command_buffer/service/common_decoder.cc
index af37308..9399a298 100644
--- a/gpu/command_buffer/service/common_decoder.cc
+++ b/gpu/command_buffer/service/common_decoder.cc
@@ -45,9 +45,12 @@
 
 void CommonDecoder::Bucket::SetSize(size_t size) {
   if (size != size_) {
-    data_.reset(size ? new int8_t[size] : nullptr);
+    // Note: the `()` after `new[]` is significant: it ensures the elements are
+    // value-initialized (not to be confused with default *initialized*). In the
+    // case of int8_t, that means the returned buffer will be
+    // zero-initialized.
+    data_.reset(size ? new int8_t[size]() : nullptr);
     size_ = size;
-    memset(data_.get(), 0, size);
   }
 }
 
diff --git a/gpu/ipc/service/BUILD.gn b/gpu/ipc/service/BUILD.gn
index e5bc3c86..239ae8dd 100644
--- a/gpu/ipc/service/BUILD.gn
+++ b/gpu/ipc/service/BUILD.gn
@@ -39,6 +39,7 @@
     "image_decode_accelerator_stub.cc",
     "image_decode_accelerator_stub.h",
     "image_decode_accelerator_worker.h",
+    "image_transport_surface.cc",
     "image_transport_surface.h",
     "image_transport_surface_delegate.h",
     "pass_through_image_transport_surface.cc",
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.cc b/gpu/ipc/service/gles2_command_buffer_stub.cc
index 7de3d1b..10a8520 100644
--- a/gpu/ipc/service/gles2_command_buffer_stub.cc
+++ b/gpu/ipc/service/gles2_command_buffer_stub.cc
@@ -263,7 +263,7 @@
             gl::GLSurfaceFormat::COLOR_SPACE_DISPLAY_P3);
         break;
     }
-    surface_ = ImageTransportSurface::CreateNativeSurface(
+    surface_ = ImageTransportSurface::CreatePresenterOrNativeSurface(
         display, weak_ptr_factory_.GetWeakPtr(), surface_handle_,
         surface_format);
     if (!surface_ || !surface_->Initialize(surface_format)) {
diff --git a/gpu/ipc/service/image_transport_surface.cc b/gpu/ipc/service/image_transport_surface.cc
new file mode 100644
index 0000000..1476348a
--- /dev/null
+++ b/gpu/ipc/service/image_transport_surface.cc
@@ -0,0 +1,19 @@
+// 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.
+
+#include "gpu/ipc/service/image_transport_surface.h"
+
+namespace gpu {
+scoped_refptr<gl::GLSurface>
+ImageTransportSurface::CreatePresenterOrNativeSurface(
+    gl::GLDisplay* display,
+    base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
+    SurfaceHandle surface_handle,
+    gl::GLSurfaceFormat format) {
+  if (auto surface = CreatePresenter(display, delegate, surface_handle, format))
+    return surface;
+
+  return CreateNativeGLSurface(display, delegate, surface_handle, format);
+}
+}  // namespace gpu
\ No newline at end of file
diff --git a/gpu/ipc/service/image_transport_surface.h b/gpu/ipc/service/image_transport_surface.h
index d0873635..49d6f56 100644
--- a/gpu/ipc/service/image_transport_surface.h
+++ b/gpu/ipc/service/image_transport_surface.h
@@ -13,6 +13,7 @@
 #include "gpu/ipc/common/surface_handle.h"
 #include "gpu/ipc/service/gpu_ipc_service_export.h"
 #include "ui/gl/gl_surface.h"
+#include "ui/gl/presenter.h"
 
 namespace gpu {
 class ImageTransportSurfaceDelegate;
@@ -24,10 +25,29 @@
 
 class GPU_IPC_SERVICE_EXPORT ImageTransportSurface {
  public:
+  // Temporary helper function that will try to create Presenter first and
+  // fallback to the GLSurface if it fails.
+  // TODO(vasilyt): Remove this.
+  static scoped_refptr<gl::GLSurface> CreatePresenterOrNativeSurface(
+      gl::GLDisplay* display,
+      base::WeakPtr<ImageTransportSurfaceDelegate> stub,
+      SurfaceHandle surface_handle,
+      gl::GLSurfaceFormat format);
+
+  // Creates the appropriate presenter if surfaceless presentation is supported.
+  // This will be implemented separately by each platform. On failure, a null
+  // scoped_refptr should be returned. Callers should try to fallback to
+  // presentation using GLSurface by calling `CreateNativeGLSurface` below.
+  static scoped_refptr<gl::Presenter> CreatePresenter(
+      gl::GLDisplay* display,
+      base::WeakPtr<ImageTransportSurfaceDelegate> stub,
+      SurfaceHandle surface_handle,
+      gl::GLSurfaceFormat format);
+
   // Creates the appropriate native surface depending on the GL implementation.
   // This will be implemented separately by each platform. On failure, a null
   // scoped_refptr should be returned.
-  static scoped_refptr<gl::GLSurface> CreateNativeSurface(
+  static scoped_refptr<gl::GLSurface> CreateNativeGLSurface(
       gl::GLDisplay* display,
       base::WeakPtr<ImageTransportSurfaceDelegate> stub,
       SurfaceHandle surface_handle,
diff --git a/gpu/ipc/service/image_transport_surface_android.cc b/gpu/ipc/service/image_transport_surface_android.cc
index 4c6ab490..12fa381 100644
--- a/gpu/ipc/service/image_transport_surface_android.cc
+++ b/gpu/ipc/service/image_transport_surface_android.cc
@@ -21,7 +21,51 @@
 namespace gpu {
 
 // static
-scoped_refptr<gl::GLSurface> ImageTransportSurface::CreateNativeSurface(
+scoped_refptr<gl::Presenter> ImageTransportSurface::CreatePresenter(
+    gl::GLDisplay* display,
+    base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
+    SurfaceHandle surface_handle,
+    gl::GLSurfaceFormat format) {
+  if (gl::GetGLImplementation() == gl::kGLImplementationMockGL ||
+      gl::GetGLImplementation() == gl::kGLImplementationStubGL)
+    return nullptr;
+
+  if (!delegate ||
+      !delegate->GetFeatureInfo()->feature_flags().android_surface_control) {
+    return nullptr;
+  }
+
+  DCHECK(GpuSurfaceLookup::GetInstance());
+  DCHECK_NE(surface_handle, kNullSurfaceHandle);
+  // On Android, the surface_handle is the id of the surface in the
+  // GpuSurfaceTracker/GpuSurfaceLookup
+  bool can_be_used_with_surface_control = false;
+  gl::ScopedJavaSurface scoped_java_surface =
+      GpuSurfaceLookup::GetInstance()->AcquireJavaSurface(
+          surface_handle, &can_be_used_with_surface_control);
+  gl::ScopedANativeWindow window(scoped_java_surface);
+  if (!window) {
+    LOG(WARNING) << "Failed to acquire ANativeWindow";
+    return nullptr;
+  }
+
+  if (!can_be_used_with_surface_control) {
+    return nullptr;
+  }
+
+  scoped_refptr<gl::Presenter> surface = new gl::GLSurfaceEGLSurfaceControl(
+      display->GetAs<gl::GLDisplayEGL>(), std::move(window),
+      base::SingleThreadTaskRunner::GetCurrentDefault());
+
+  bool initialize_success = surface->Initialize(format);
+  if (!initialize_success)
+    return nullptr;
+
+  return surface;
+}
+
+// static
+scoped_refptr<gl::GLSurface> ImageTransportSurface::CreateNativeGLSurface(
     gl::GLDisplay* display,
     base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
     SurfaceHandle surface_handle,
@@ -42,18 +86,9 @@
     LOG(WARNING) << "Failed to acquire ANativeWindow";
     return nullptr;
   }
-  scoped_refptr<gl::GLSurface> surface;
 
-  if (delegate &&
-      delegate->GetFeatureInfo()->feature_flags().android_surface_control &&
-      can_be_used_with_surface_control) {
-    surface = new gl::GLSurfaceEGLSurfaceControl(
-        display->GetAs<gl::GLDisplayEGL>(), std::move(window),
-        base::SingleThreadTaskRunner::GetCurrentDefault());
-  } else {
-    surface = new gl::NativeViewGLSurfaceEGL(display->GetAs<gl::GLDisplayEGL>(),
-                                             std::move(window), nullptr);
-  }
+  scoped_refptr<gl::GLSurface> surface = new gl::NativeViewGLSurfaceEGL(
+      display->GetAs<gl::GLDisplayEGL>(), std::move(window), nullptr);
 
   bool initialize_success = surface->Initialize(format);
   if (!initialize_success)
diff --git a/gpu/ipc/service/image_transport_surface_fuchsia.cc b/gpu/ipc/service/image_transport_surface_fuchsia.cc
index af93d31..61f47d0 100644
--- a/gpu/ipc/service/image_transport_surface_fuchsia.cc
+++ b/gpu/ipc/service/image_transport_surface_fuchsia.cc
@@ -12,7 +12,16 @@
 namespace gpu {
 
 // static
-scoped_refptr<gl::GLSurface> ImageTransportSurface::CreateNativeSurface(
+scoped_refptr<gl::Presenter> ImageTransportSurface::CreatePresenter(
+    gl::GLDisplay* display,
+    base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
+    SurfaceHandle surface_handle,
+    gl::GLSurfaceFormat format) {
+  return nullptr;
+}
+
+// static
+scoped_refptr<gl::GLSurface> ImageTransportSurface::CreateNativeGLSurface(
     gl::GLDisplay* display,
     base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
     SurfaceHandle surface_handle,
diff --git a/gpu/ipc/service/image_transport_surface_linux.cc b/gpu/ipc/service/image_transport_surface_linux.cc
index 2e07507c..67b690c 100644
--- a/gpu/ipc/service/image_transport_surface_linux.cc
+++ b/gpu/ipc/service/image_transport_surface_linux.cc
@@ -11,23 +11,32 @@
 namespace gpu {
 
 // static
-scoped_refptr<gl::GLSurface> ImageTransportSurface::CreateNativeSurface(
+scoped_refptr<gl::Presenter> ImageTransportSurface::CreatePresenter(
     gl::GLDisplay* display,
     base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
     SurfaceHandle surface_handle,
     gl::GLSurfaceFormat format) {
   DCHECK_NE(surface_handle, kNullSurfaceHandle);
-  scoped_refptr<gl::GLSurface> surface;
-  bool override_vsync_for_multi_window_swap = false;
 #if BUILDFLAG(IS_OZONE)
-  surface = gl::init::CreateSurfacelessViewGLSurface(display, surface_handle);
+  return gl::init::CreateSurfacelessViewGLSurface(display, surface_handle);
+#else
+  return nullptr;
 #endif
-  if (!surface) {
-    surface = gl::init::CreateViewGLSurface(display, surface_handle);
-    if (gl::GetGLImplementation() == gl::kGLImplementationDesktopGL ||
-        gl::GetGLImplementation() == gl::kGLImplementationEGLANGLE) {
-      override_vsync_for_multi_window_swap = true;
-    }
+}
+
+// static
+scoped_refptr<gl::GLSurface> ImageTransportSurface::CreateNativeGLSurface(
+    gl::GLDisplay* display,
+    base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
+    SurfaceHandle surface_handle,
+    gl::GLSurfaceFormat format) {
+  DCHECK_NE(surface_handle, kNullSurfaceHandle);
+  scoped_refptr<gl::GLSurface> surface =
+      gl::init::CreateViewGLSurface(display, surface_handle);
+  bool override_vsync_for_multi_window_swap = false;
+  if (gl::GetGLImplementation() == gl::kGLImplementationDesktopGL ||
+      gl::GetGLImplementation() == gl::kGLImplementationEGLANGLE) {
+    override_vsync_for_multi_window_swap = true;
   }
   if (!surface)
     return surface;
diff --git a/gpu/ipc/service/image_transport_surface_mac.mm b/gpu/ipc/service/image_transport_surface_mac.mm
index 6c21b17..3f6c36a 100644
--- a/gpu/ipc/service/image_transport_surface_mac.mm
+++ b/gpu/ipc/service/image_transport_surface_mac.mm
@@ -14,25 +14,36 @@
 namespace gpu {
 
 // static
-scoped_refptr<gl::GLSurface> ImageTransportSurface::CreateNativeSurface(
+scoped_refptr<gl::Presenter> ImageTransportSurface::CreatePresenter(
+    gl::GLDisplay* display,
+    base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
+    SurfaceHandle surface_handle,
+    gl::GLSurfaceFormat format) {
+  DCHECK_NE(surface_handle, kNullSurfaceHandle);
+  if (gl::GetGLImplementation() == gl::kGLImplementationEGLGLES2 ||
+      gl::GetGLImplementation() == gl::kGLImplementationEGLANGLE) {
+    return base::WrapRefCounted<gl::Presenter>(
+        new ImageTransportSurfaceOverlayMacEGL(
+            display->GetAs<gl::GLDisplayEGL>(), delegate));
+  }
+
+  return nullptr;
+}
+
+// static
+scoped_refptr<gl::GLSurface> ImageTransportSurface::CreateNativeGLSurface(
     gl::GLDisplay* display,
     base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
     SurfaceHandle surface_handle,
     gl::GLSurfaceFormat format) {
   DCHECK_NE(surface_handle, kNullSurfaceHandle);
 
-  switch (gl::GetGLImplementation()) {
-    case gl::kGLImplementationEGLGLES2:
-    case gl::kGLImplementationEGLANGLE:
-      return base::WrapRefCounted<gl::GLSurface>(
-          new ImageTransportSurfaceOverlayMacEGL(
-              display->GetAs<gl::GLDisplayEGL>(), delegate));
-    case gl::kGLImplementationMockGL:
-    case gl::kGLImplementationStubGL:
-      return base::WrapRefCounted<gl::GLSurface>(new gl::GLSurfaceStub);
-    default:
-      return nullptr;
+  if (gl::GetGLImplementation() == gl::kGLImplementationMockGL ||
+      gl::GetGLImplementation() == gl::kGLImplementationStubGL) {
+    return base::WrapRefCounted<gl::GLSurface>(new gl::GLSurfaceStub);
   }
+
+  return nullptr;
 }
 
 }  // namespace gpu
diff --git a/gpu/ipc/service/image_transport_surface_overlay_mac.h b/gpu/ipc/service/image_transport_surface_overlay_mac.h
index 9666bba..c454d2f 100644
--- a/gpu/ipc/service/image_transport_surface_overlay_mac.h
+++ b/gpu/ipc/service/image_transport_surface_overlay_mac.h
@@ -15,8 +15,8 @@
 #include "ui/gfx/presentation_feedback.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_surface.h"
-#include "ui/gl/gl_surface_egl.h"
 #include "ui/gl/gpu_switching_observer.h"
+#include "ui/gl/presenter.h"
 
 @class CAContext;
 @class CALayer;
@@ -32,7 +32,7 @@
 
 namespace gpu {
 
-class ImageTransportSurfaceOverlayMacEGL : public gl::GLSurfaceEGL,
+class ImageTransportSurfaceOverlayMacEGL : public gl::Presenter,
                                            public ui::GpuSwitchingObserver {
  public:
   ImageTransportSurfaceOverlayMacEGL(
diff --git a/gpu/ipc/service/image_transport_surface_overlay_mac.mm b/gpu/ipc/service/image_transport_surface_overlay_mac.mm
index 320e3b9..62b8409 100644
--- a/gpu/ipc/service/image_transport_surface_overlay_mac.mm
+++ b/gpu/ipc/service/image_transport_surface_overlay_mac.mm
@@ -49,7 +49,7 @@
 ImageTransportSurfaceOverlayMacEGL::ImageTransportSurfaceOverlayMacEGL(
     gl::GLDisplayEGL* display,
     base::WeakPtr<ImageTransportSurfaceDelegate> delegate)
-    : gl::GLSurfaceEGL(display),
+    : gl::Presenter(display, gfx::Size()),
       delegate_(delegate),
       use_remote_layer_api_(ui::RemoteLayerAPISupported()),
       scale_factor_(1),
diff --git a/gpu/ipc/service/image_transport_surface_win.cc b/gpu/ipc/service/image_transport_surface_win.cc
index b33a629..210d607 100644
--- a/gpu/ipc/service/image_transport_surface_win.cc
+++ b/gpu/ipc/service/image_transport_surface_win.cc
@@ -40,7 +40,16 @@
 }  // namespace
 
 // static
-scoped_refptr<gl::GLSurface> ImageTransportSurface::CreateNativeSurface(
+scoped_refptr<gl::Presenter> ImageTransportSurface::CreatePresenter(
+    gl::GLDisplay* display,
+    base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
+    SurfaceHandle surface_handle,
+    gl::GLSurfaceFormat format) {
+  return nullptr;
+}
+
+// static
+scoped_refptr<gl::GLSurface> ImageTransportSurface::CreateNativeGLSurface(
     gl::GLDisplay* display,
     base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
     SurfaceHandle surface_handle,
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 6be0ee3..5e7a111d 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -274,6 +274,12 @@
 const char kCastStreamingForceEnableHardwareVp8[] =
     "cast-streaming-force-enable-hardware-vp8";
 
+// Disables the code path that makes Pepper use the MojoVideoDecoder for
+// hardware accelerated video decoding. It overrides the value of the
+// kUseMojoVideoDecoderForPepper feature flag.
+const char kDisableUseMojoVideoDecoderForPepper[] =
+    "disable-use-mojo-video-decoder-for-pepper";
+
 }  // namespace switches
 
 namespace media {
@@ -1305,6 +1311,19 @@
 }
 #endif
 
+bool IsUseMojoVideoDecoderForPepperEnabled() {
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableUseMojoVideoDecoderForPepper)) {
+    LOG(WARNING) << "UseMojoVideoDecoderForPepper: Disabled by policy";
+    return false;
+  }
+
+  auto enabled = base::FeatureList::IsEnabled(kUseMojoVideoDecoderForPepper);
+  LOG(WARNING) << "UseMojoVideoDecoderForPepper: feature controlled: "
+               << enabled;
+  return enabled;
+}
+
 // Return bitmask of audio formats supported by EDID.
 uint32_t GetPassthroughAudioFormats() {
 #if BUILDFLAG(ENABLE_PASSTHROUGH_AUDIO_CODECS)
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index a43427e..a310f45 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -138,6 +138,8 @@
 // to take effect.
 MEDIA_EXPORT extern const char kCastStreamingForceEnableHardwareVp8[];
 
+MEDIA_EXPORT extern const char kDisableUseMojoVideoDecoderForPepper[];
+
 }  // namespace switches
 
 namespace media {
@@ -378,6 +380,8 @@
 MEDIA_EXPORT bool IsMediaFoundationD3D11VideoCaptureEnabled();
 #endif
 
+MEDIA_EXPORT bool IsUseMojoVideoDecoderForPepperEnabled();
+
 enum class kCrosGlobalMediaControlsPinOptions {
   kPin,
   kNotPin,
diff --git a/media/gpu/video_encode_accelerator_perf_tests.cc b/media/gpu/video_encode_accelerator_perf_tests.cc
index 1410206..ce290e8 100644
--- a/media/gpu/video_encode_accelerator_perf_tests.cc
+++ b/media/gpu/video_encode_accelerator_perf_tests.cc
@@ -319,10 +319,12 @@
 }
 
 struct BitstreamQualityMetrics {
-  BitstreamQualityMetrics(const PSNRVideoFrameValidator* const psnr_validator,
-                          const SSIMVideoFrameValidator* const ssim_validator,
-                          const absl::optional<size_t>& spatial_idx,
-                          const absl::optional<size_t>& temporal_idx);
+  BitstreamQualityMetrics(
+      const PSNRVideoFrameValidator* const psnr_validator,
+      const SSIMVideoFrameValidator* const ssim_validator,
+      const PSNRVideoFrameValidator* const bottom_row_psnr_validator,
+      const absl::optional<size_t>& spatial_idx,
+      const absl::optional<size_t>& temporal_idx);
 
   void Output(uint32_t target_bitrate, uint32_t actual_bitrate);
 
@@ -345,30 +347,37 @@
   static QualityStats ComputeQualityStats(
       const std::map<size_t, double>& values);
 
-  void WriteToConsole(const std::string& svc_text,
-                      const BitstreamQualityMetrics::QualityStats& psnr_stats,
-                      const BitstreamQualityMetrics::QualityStats& ssim_stats,
-                      uint32_t target_bitrate,
-                      uint32_t actual_bitrate) const;
-  void WriteToFile(const std::string& svc_text,
-                   const BitstreamQualityMetrics::QualityStats& psnr_stats,
-                   const BitstreamQualityMetrics::QualityStats& ssim_stats,
-                   uint32_t target_bitrate,
-                   uint32_t actual_bitrate) const;
+  void WriteToConsole(
+      const std::string& svc_text,
+      const BitstreamQualityMetrics::QualityStats& psnr_stats,
+      const BitstreamQualityMetrics::QualityStats& ssim_stats,
+      const BitstreamQualityMetrics::QualityStats& bottom_row_psnr_stats,
+      uint32_t target_bitrate,
+      uint32_t actual_bitrate) const;
+  void WriteToFile(
+      const std::string& svc_text,
+      const BitstreamQualityMetrics::QualityStats& psnr_stats,
+      const BitstreamQualityMetrics::QualityStats& ssim_stats,
+      const BitstreamQualityMetrics::QualityStats& bottom_row_psnr_stats,
+      uint32_t target_bitrate,
+      uint32_t actual_bitrate) const;
 
   const raw_ptr<const PSNRVideoFrameValidator> psnr_validator;
   const raw_ptr<const SSIMVideoFrameValidator> ssim_validator;
+  const raw_ptr<const PSNRVideoFrameValidator> bottom_row_psnr_validator;
 };
 
 BitstreamQualityMetrics::BitstreamQualityMetrics(
     const PSNRVideoFrameValidator* const psnr_validator,
     const SSIMVideoFrameValidator* const ssim_validator,
+    const PSNRVideoFrameValidator* const bottom_row_psnr_validator,
     const absl::optional<size_t>& spatial_idx,
     const absl::optional<size_t>& temporal_idx)
     : spatial_idx(spatial_idx),
       temporal_idx(temporal_idx),
       psnr_validator(psnr_validator),
-      ssim_validator(ssim_validator) {}
+      ssim_validator(ssim_validator),
+      bottom_row_psnr_validator(bottom_row_psnr_validator) {}
 
 // static
 BitstreamQualityMetrics::QualityStats
@@ -408,16 +417,20 @@
 
   auto psnr_stats = ComputeQualityStats(psnr_validator->GetPSNRValues());
   auto ssim_stats = ComputeQualityStats(ssim_validator->GetSSIMValues());
+  auto bottom_row_psnr_stats =
+      ComputeQualityStats(bottom_row_psnr_validator->GetPSNRValues());
 
-  WriteToConsole(svc_text, psnr_stats, ssim_stats, target_bitrate,
-                 actual_bitrate);
-  WriteToFile(svc_text, psnr_stats, ssim_stats, target_bitrate, actual_bitrate);
+  WriteToConsole(svc_text, psnr_stats, ssim_stats, bottom_row_psnr_stats,
+                 target_bitrate, actual_bitrate);
+  WriteToFile(svc_text, psnr_stats, ssim_stats, bottom_row_psnr_stats,
+              target_bitrate, actual_bitrate);
 }
 
 void BitstreamQualityMetrics::WriteToConsole(
     const std::string& svc_text,
     const BitstreamQualityMetrics::QualityStats& psnr_stats,
     const BitstreamQualityMetrics::QualityStats& ssim_stats,
+    const BitstreamQualityMetrics::QualityStats& bottom_row_psnr_stats,
     uint32_t target_bitrate,
     uint32_t actual_bitrate) const {
   const auto default_ssize = std::cout.precision();
@@ -443,6 +456,14 @@
             << std::endl;
   std::cout << "PSNR - percentile 75: " << psnr_stats.percentile_75
             << std::endl;
+  std::cout << "Bottom row PSNR - average:       " << bottom_row_psnr_stats.avg
+            << std::endl;
+  std::cout << "Bottom row PSNR - percentile 25: "
+            << bottom_row_psnr_stats.percentile_25 << std::endl;
+  std::cout << "Bottom row PSNR - percentile 50: "
+            << bottom_row_psnr_stats.percentile_50 << std::endl;
+  std::cout << "Bottom row PSNR - percentile 75: "
+            << bottom_row_psnr_stats.percentile_75 << std::endl;
   std::cout.precision(default_ssize);
 }
 
@@ -450,6 +471,7 @@
     const std::string& svc_text,
     const BitstreamQualityMetrics::QualityStats& psnr_stats,
     const BitstreamQualityMetrics::QualityStats& ssim_stats,
+    const BitstreamQualityMetrics::QualityStats& bottom_row_psnr_stats,
     uint32_t target_bitrate,
     uint32_t actual_bitrate) const {
   base::FilePath output_folder_path = base::FilePath(g_env->OutputFolder());
@@ -467,6 +489,8 @@
       base::Value((actual_bitrate * 100.0 / target_bitrate) - 100.0));
   metrics.SetKey("SSIMAverage", base::Value(ssim_stats.avg));
   metrics.SetKey("PSNRAverage", base::Value(psnr_stats.avg));
+  metrics.SetKey("BottomRowPSNRAverage",
+                 base::Value(bottom_row_psnr_stats.avg));
   // Write ssim values bitstream delivery times to json.
   base::Value ssim_values(base::Value::Type::LIST);
   for (double value : ssim_stats.values_in_order)
@@ -479,6 +503,12 @@
     psnr_values.Append(value);
   metrics.SetKey("PSNRValues", std::move(psnr_values));
 
+  // Write bottom row psnr values to json.
+  base::Value bottom_row_psnr_values(base::Value::Type::LIST);
+  for (double value : bottom_row_psnr_stats.values_in_order)
+    bottom_row_psnr_values.Append(value);
+  metrics.SetKey("BottomRowPSNRValues", std::move(bottom_row_psnr_values));
+
   // Write json to file.
   std::string metrics_str;
   ASSERT_TRUE(base::JSONWriter::WriteWithOptions(
@@ -570,11 +600,20 @@
         VideoFrameValidator::ValidationMode::kAverage,
         /*tolerance=*/0.0);
     LOG_ASSERT(psnr_validator);
+    auto bottom_row_psnr_validator = PSNRVideoFrameValidator::Create(
+        get_model_frame_cb,
+        /*corrupt_frame_processor=*/nullptr,
+        VideoFrameValidator::ValidationMode::kAverage,
+        /*tolerance=*/0.0,
+        base::BindRepeating(&BottomRowCrop, kDefaultBottomRowCropHeight));
+    LOG_ASSERT(bottom_row_psnr_validator);
     quality_metrics_.push_back(BitstreamQualityMetrics(
         psnr_validator.get(), ssim_validator.get(),
-        spatial_layer_index_to_decode, temporal_layer_index_to_decode));
+        bottom_row_psnr_validator.get(), spatial_layer_index_to_decode,
+        temporal_layer_index_to_decode));
     video_frame_processors.push_back(std::move(ssim_validator));
     video_frame_processors.push_back(std::move(psnr_validator));
+    video_frame_processors.push_back(std::move(bottom_row_psnr_validator));
 
     VideoDecoderConfig decoder_config(
         VideoCodecProfileToVideoCodec(profile), profile,
diff --git a/rlz/BUILD.gn b/rlz/BUILD.gn
index a9c7adca..2b53c761 100644
--- a/rlz/BUILD.gn
+++ b/rlz/BUILD.gn
@@ -87,7 +87,7 @@
       deps += [
         "//chromeos/ash/components/dbus",
         "//chromeos/ash/components/dbus/debug_daemon",
-        "//chromeos/system",
+        "//chromeos/ash/components/system",
       ]
     }
 
@@ -143,7 +143,7 @@
     if (is_chromeos_ash) {
       deps += [
         "//chromeos/ash/components/dbus",
-        "//chromeos/system",
+        "//chromeos/ash/components/system",
       ]
     }
 
@@ -166,7 +166,7 @@
       "//build:chromeos_buildflags",
     ]
     if (is_chromeos_ash) {
-      public_deps += [ "//chromeos/system" ]
+      public_deps += [ "//chromeos/ash/components/system" ]
     }
   }
 
@@ -203,7 +203,7 @@
       deps += [
         "//chromeos/ash/components/dbus:test_support",
         "//chromeos/ash/components/dbus/debug_daemon",
-        "//chromeos/system",
+        "//chromeos/ash/components/system",
       ]
     }
   }
diff --git a/rlz/DEPS b/rlz/DEPS
index d4319b36..b9fcc386 100644
--- a/rlz/DEPS
+++ b/rlz/DEPS
@@ -5,7 +5,7 @@
 include_rules = [
   "+build",
   "+chromeos/ash/components/dbus",
-  "+chromeos/system",
+  "+chromeos/ash/components/system",
   "+dbus",
   "+mojo/core/embedder",
   "+net",  # This is only used when force_rlz_use_chrome_net=1 is passed to gyp.
diff --git a/rlz/chromeos/lib/rlz_value_store_chromeos.cc b/rlz/chromeos/lib/rlz_value_store_chromeos.cc
index cecb7a1..19698a5f 100644
--- a/rlz/chromeos/lib/rlz_value_store_chromeos.cc
+++ b/rlz/chromeos/lib/rlz_value_store_chromeos.cc
@@ -26,8 +26,8 @@
 #include "base/values.h"
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
 #include "chromeos/ash/components/dbus/debug_daemon/debug_daemon_client.h"
-#include "chromeos/system/factory_ping_embargo_check.h"
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/factory_ping_embargo_check.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "dbus/bus.h"
 #include "rlz/lib/lib_values.h"
 #include "rlz/lib/recursive_cross_process_lock_posix.h"
diff --git a/rlz/lib/financial_ping_test.cc b/rlz/lib/financial_ping_test.cc
index ad0ec95..ffc51c8 100644
--- a/rlz/lib/financial_ping_test.cc
+++ b/rlz/lib/financial_ping_test.cc
@@ -39,7 +39,7 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/factory_ping_embargo_check.h"
+#include "chromeos/ash/components/system/factory_ping_embargo_check.h"
 #include "rlz/chromeos/lib/rlz_value_store_chromeos.h"
 #endif
 
diff --git a/rlz/test/rlz_test_helpers.h b/rlz/test/rlz_test_helpers.h
index 28a7aa07..e827238a 100644
--- a/rlz/test/rlz_test_helpers.h
+++ b/rlz/test/rlz_test_helpers.h
@@ -21,7 +21,7 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #endif
 
 // A test helper class that constructs and destructs platform dependent machine
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index 3fa0120..4941002e 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -1914,7 +1914,7 @@
       {
         "args": [],
         "cros_board": "dedede",
-        "cros_img": "dedede-release/R109-15236.27.0",
+        "cros_img": "dedede-release/R109-15236.35.0",
         "name": "lacros_all_tast_tests DEDEDE_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -1978,7 +1978,7 @@
       {
         "args": [],
         "cros_board": "eve",
-        "cros_img": "eve-release/R109-15236.27.0",
+        "cros_img": "eve-release/R109-15236.35.0",
         "name": "lacros_all_tast_tests EVE_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2121,7 +2121,7 @@
       {
         "args": [],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R109-15236.27.0",
+        "cros_img": "jacuzzi-release/R109-15236.35.0",
         "name": "lacros_all_tast_tests JACUZZI_RELEASE_BETA",
         "resultdb": {
           "enable": true,
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 588f155a..e885af5 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -1494,9 +1494,9 @@
       {
         "args": [],
         "bucket": "chromiumos-image-archive",
-        "cros_board": "volteer",
-        "cros_img": "volteer-public/R110-15276.0.0",
-        "name": "lacros_all_tast_tests VOLTEER_PUBLIC",
+        "cros_board": "zork",
+        "cros_img": "zork-public/R110-15277.0.0",
+        "name": "lacros_all_tast_tests ZORK_PUBLIC",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -1506,7 +1506,7 @@
         "test": "lacros_all_tast_tests",
         "test_id_prefix": "ninja://chromeos/lacros:lacros_all_tast_tests/",
         "timeout_sec": 10800,
-        "variant_id": "VOLTEER_PUBLIC"
+        "variant_id": "ZORK_PUBLIC"
       }
     ]
   },
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index c7b571d2..275227f 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -5363,7 +5363,7 @@
         "autotest_name": "chromium_GPU",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R110-15271.0.0",
+        "cros_img": "kevin-public/R110-15277.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-cmd-decoder=validating",
         "merge": {
           "args": [],
@@ -5394,7 +5394,7 @@
         "autotest_name": "chromium_GPU",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R110-15271.0.0",
+        "cros_img": "kevin-public/R110-15277.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc",
         "merge": {
           "args": [],
@@ -5425,7 +5425,7 @@
         "autotest_name": "chromium_GPU",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R110-15271.0.0",
+        "cros_img": "kevin-public/R110-15277.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc",
         "merge": {
           "args": [],
@@ -5460,7 +5460,7 @@
         "autotest_name": "chromium_GPU",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R110-15271.0.0",
+        "cros_img": "kevin-public/R110-15277.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --force_high_performance_gpu",
         "merge": {
           "args": [],
@@ -5496,7 +5496,7 @@
         "autotest_name": "chromium_GPU",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R110-15271.0.0",
+        "cros_img": "kevin-public/R110-15277.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-cmd-decoder=validating",
         "merge": {
           "args": [],
@@ -5532,7 +5532,7 @@
         "autotest_name": "chromium_GPU",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R110-15271.0.0",
+        "cros_img": "kevin-public/R110-15277.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating",
         "merge": {
           "args": [],
@@ -5568,7 +5568,7 @@
         "autotest_name": "chromium_GPU",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R110-15271.0.0",
+        "cros_img": "kevin-public/R110-15277.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-cmd-decoder=validating",
         "merge": {
           "args": [],
@@ -5605,7 +5605,7 @@
         "autotest_name": "chromium_GPU",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R110-15271.0.0",
+        "cros_img": "kevin-public/R110-15277.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-cmd-decoder=validating",
         "merge": {
           "args": [],
@@ -5636,7 +5636,7 @@
         "autotest_name": "chromium_GPU",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R110-15271.0.0",
+        "cros_img": "kevin-public/R110-15277.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc",
         "merge": {
           "args": [],
@@ -5667,7 +5667,7 @@
         "autotest_name": "chromium_GPU",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R110-15271.0.0",
+        "cros_img": "kevin-public/R110-15277.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-cmd-decoder=validating",
         "merge": {
           "args": [],
@@ -5701,7 +5701,7 @@
         "autotest_name": "chromium_GPU",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R110-15271.0.0",
+        "cros_img": "kevin-public/R110-15277.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-gl=angle --use-angle=gles --use-cmd-decoder=passthrough --force_high_performance_gpu",
         "merge": {
           "args": [],
@@ -5735,7 +5735,7 @@
         "autotest_name": "chromium_GPU",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R110-15271.0.0",
+        "cros_img": "kevin-public/R110-15277.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-cmd-decoder=validating --force_high_performance_gpu",
         "merge": {
           "args": [],
@@ -5768,7 +5768,7 @@
         "autotest_name": "chromium_GPU",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R110-15271.0.0",
+        "cros_img": "kevin-public/R110-15277.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-cmd-decoder=validating --force_high_performance_gpu",
         "merge": {
           "args": [],
diff --git a/testing/buildbot/internal.chromeos.fyi.json b/testing/buildbot/internal.chromeos.fyi.json
index d06e503..630242c5 100644
--- a/testing/buildbot/internal.chromeos.fyi.json
+++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -1120,7 +1120,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R109-15236.27.0",
+        "cros_img": "octopus-release/R109-15236.35.0",
         "name": "lacros_fyi_tast_tests OCTOPUS_RELEASE_BETA",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1166,7 +1166,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R109-15236.27.0",
+        "cros_img": "octopus-release/R109-15236.35.0",
         "name": "ozone_unittests OCTOPUS_RELEASE_BETA",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1219,7 +1219,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R109-15236.27.0",
+        "cros_img": "hana-release/R109-15236.35.0",
         "name": "lacros_all_tast_tests HANA_RELEASE_BETA",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1267,7 +1267,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R109-15236.27.0",
+        "cros_img": "strongbad-release/R109-15236.35.0",
         "name": "lacros_all_tast_tests STRONGBAD_RELEASE_BETA",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1313,7 +1313,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R109-15236.27.0",
+        "cros_img": "hana-release/R109-15236.35.0",
         "name": "ozone_unittests HANA_RELEASE_BETA",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1357,7 +1357,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R109-15236.27.0",
+        "cros_img": "strongbad-release/R109-15236.35.0",
         "name": "ozone_unittests STRONGBAD_RELEASE_BETA",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1401,7 +1401,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R109-15236.27.0",
+        "cros_img": "hana-release/R109-15236.35.0",
         "name": "viz_unittests HANA_RELEASE_BETA",
         "swarming": {},
         "test": "viz_unittests",
@@ -1445,7 +1445,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R109-15236.27.0",
+        "cros_img": "strongbad-release/R109-15236.35.0",
         "name": "viz_unittests STRONGBAD_RELEASE_BETA",
         "swarming": {},
         "test": "viz_unittests",
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 0c950a1f..f0c71e4c 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -360,7 +360,7 @@
   },
   'chromeos-kevin-skylab': {
     'cros_board': 'kevin',
-    'cros_img': 'kevin-public/R110-15271.0.0',
+    'cros_img': 'kevin-public/R110-15277.0.0',
     'bucket': 'chromiumos-image-archive',
     'timeout_sec': 10800,
   },
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 5ab0efcd..c304385e 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -7186,7 +7186,7 @@
     'lacros_skylab_tests_amd64_generic_rel': {
       'lacros_skylab_tests': {
         'variants': [
-          'CROS_VOLTEER_PUBLIC',
+          'CROS_ZORK_PUBLIC',
         ]
       },
     },
diff --git a/testing/buildbot/tryserver.chromium.chromiumos.json b/testing/buildbot/tryserver.chromium.chromiumos.json
index 67932ef0..a25cd673 100644
--- a/testing/buildbot/tryserver.chromium.chromiumos.json
+++ b/testing/buildbot/tryserver.chromium.chromiumos.json
@@ -9,9 +9,9 @@
       {
         "args": [],
         "bucket": "chromiumos-image-archive",
-        "cros_board": "volteer",
-        "cros_img": "volteer-public/R110-15276.0.0",
-        "name": "lacros_all_tast_tests VOLTEER_PUBLIC",
+        "cros_board": "zork",
+        "cros_img": "zork-public/R110-15277.0.0",
+        "name": "lacros_all_tast_tests ZORK_PUBLIC",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -21,7 +21,7 @@
         "test": "lacros_all_tast_tests",
         "test_id_prefix": "ninja://chromeos/lacros:lacros_all_tast_tests/",
         "timeout_sec": 10800,
-        "variant_id": "VOLTEER_PUBLIC"
+        "variant_id": "ZORK_PUBLIC"
       }
     ]
   }
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 6be43c10..9ffd7c8 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -488,8 +488,8 @@
   'CROS_DEDEDE_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'dedede',
-      'cros_chrome_version': '109.0.5414.29',
-      'cros_img': 'dedede-release/R109-15236.27.0',
+      'cros_chrome_version': '109.0.5414.41',
+      'cros_img': 'dedede-release/R109-15236.35.0',
     },
     'enabled': True,
     'identifier': 'DEDEDE_RELEASE_BETA',
@@ -524,8 +524,8 @@
   'CROS_EVE_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'eve',
-      'cros_chrome_version': '109.0.5414.29',
-      'cros_img': 'eve-release/R109-15236.27.0',
+      'cros_chrome_version': '109.0.5414.41',
+      'cros_img': 'eve-release/R109-15236.35.0',
     },
     'enabled': True,
     'identifier': 'EVE_RELEASE_BETA',
@@ -570,8 +570,8 @@
   'CROS_HANA_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'hana',
-      'cros_chrome_version': '109.0.5414.29',
-      'cros_img': 'hana-release/R109-15236.27.0',
+      'cros_chrome_version': '109.0.5414.41',
+      'cros_img': 'hana-release/R109-15236.35.0',
     },
     'enabled': True,
     'identifier': 'HANA_RELEASE_BETA',
@@ -615,8 +615,8 @@
   'CROS_JACUZZI_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'jacuzzi',
-      'cros_chrome_version': '109.0.5414.29',
-      'cros_img': 'jacuzzi-release/R109-15236.27.0',
+      'cros_chrome_version': '109.0.5414.41',
+      'cros_img': 'jacuzzi-release/R109-15236.35.0',
     },
     'enabled': True,
     'identifier': 'JACUZZI_RELEASE_BETA',
@@ -681,8 +681,8 @@
   'CROS_OCTOPUS_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'octopus',
-      'cros_chrome_version': '109.0.5414.29',
-      'cros_img': 'octopus-release/R109-15236.27.0',
+      'cros_chrome_version': '109.0.5414.41',
+      'cros_img': 'octopus-release/R109-15236.35.0',
     },
     'enabled': True,
     'identifier': 'OCTOPUS_RELEASE_BETA',
@@ -717,8 +717,8 @@
   'CROS_STRONGBAD_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'strongbad',
-      'cros_chrome_version': '109.0.5414.29',
-      'cros_img': 'strongbad-release/R109-15236.27.0',
+      'cros_chrome_version': '109.0.5414.41',
+      'cros_img': 'strongbad-release/R109-15236.35.0',
     },
     'enabled': True,
     'identifier': 'STRONGBAD_RELEASE_BETA',
@@ -742,15 +742,15 @@
     'enabled': True,
     'identifier': 'TROGDOR64_RELEASE_LKGM',
   },
-  'CROS_VOLTEER_PUBLIC': {
+  'CROS_ZORK_PUBLIC': {
     'skylab': {
-      'cros_board': 'volteer',
+      'cros_board': 'zork',
       'cros_chrome_version': '110.0.5464.0',
-      'cros_img': 'volteer-public/R110-15276.0.0',
+      'cros_img': 'zork-public/R110-15277.0.0',
       'bucket': 'chromiumos-image-archive',
     },
     'enabled': True,
-    'identifier': 'VOLTEER_PUBLIC',
+    'identifier': 'ZORK_PUBLIC',
   },
   'LACROS_AMD64_GENERIC': {
     'args': [
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 40e21512..518d2d8 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -7349,7 +7349,6 @@
                         "OmniboxFocusTriggersSRPZeroSuggest",
                         "OmniboxMaxURLMatches",
                         "OmniboxMostVisitedTilesTitleWrapAround",
-                        "OmniboxRemoveSuggestionHeaderChevron",
                         "OmniboxRetainSuggestionsWithHeaders",
                         "OmniboxUIExperimentMaxAutocompleteMatches",
                         "OrganicRepeatableQueries"
diff --git a/third_party/androidx/build.gradle.template b/third_party/androidx/build.gradle.template
index 8cebb4d..98f549d 100644
--- a/third_party/androidx/build.gradle.template
+++ b/third_party/androidx/build.gradle.template
@@ -94,10 +94,10 @@
     androidTestCompile 'androidx.test:monitor:1.4.0-rc01'
     androidTestCompile 'androidx.test:rules:1.2.0'
     androidTestCompile 'androidx.test:runner:1.2.0'
-    androidTestCompile 'androidx.test.espresso:espresso-contrib:3.5.0'
-    androidTestCompile 'androidx.test.espresso:espresso-core:3.5.0'
-    androidTestCompile 'androidx.test.espresso:espresso-intents:3.5.0'
-    androidTestCompile 'androidx.test.espresso:espresso-web:3.5.0'
+    androidTestCompile 'androidx.test.espresso:espresso-contrib:3.2.0'
+    androidTestCompile 'androidx.test.espresso:espresso-core:3.2.0'
+    androidTestCompile 'androidx.test.espresso:espresso-intents:3.2.0'
+    androidTestCompile 'androidx.test.espresso:espresso-web:3.2.0'
     androidTestCompile 'androidx.test.ext:junit:1.1.1'
     androidTestCompile 'androidx.test.services:storage:1.4.1'
     androidTestCompile 'androidx.test.uiautomator:uiautomator:2.2.0'
diff --git a/third_party/blink/common/associated_interfaces/associated_interface_provider.cc b/third_party/blink/common/associated_interfaces/associated_interface_provider.cc
index 076391a..6abf3cab 100644
--- a/third_party/blink/common/associated_interfaces/associated_interface_provider.cc
+++ b/third_party/blink/common/associated_interfaces/associated_interface_provider.cc
@@ -7,7 +7,7 @@
 #include <map>
 
 #include "base/no_destructor.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 
 namespace blink {
@@ -101,7 +101,8 @@
 AssociatedInterfaceProvider*
 AssociatedInterfaceProvider::GetEmptyAssociatedInterfaceProvider() {
   static base::NoDestructor<AssociatedInterfaceProvider>
-      associated_interface_provider(base::ThreadTaskRunnerHandle::Get());
+      associated_interface_provider(
+          base::SingleThreadTaskRunner::GetCurrentDefault());
   return associated_interface_provider.get();
 }
 
diff --git a/third_party/blink/common/browser_interface_broker_proxy.cc b/third_party/blink/common/browser_interface_broker_proxy.cc
index c29ea3f..d486901 100644
--- a/third_party/blink/common/browser_interface_broker_proxy.cc
+++ b/third_party/blink/common/browser_interface_broker_proxy.cc
@@ -6,8 +6,8 @@
 
 #include <tuple>
 
+#include "base/task/single_thread_task_runner.h"
 #include "base/threading/sequence_local_storage_slot.h"
-#include "base/threading/thread_task_runner_handle.h"
 
 namespace blink {
 
@@ -68,7 +68,8 @@
     auto& proxy = proxy_slot.GetOrCreateValue();
     mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker> remote;
     std::ignore = remote.InitWithNewPipeAndPassReceiver();
-    proxy.Bind(std::move(remote), base::ThreadTaskRunnerHandle::Get());
+    proxy.Bind(std::move(remote),
+               base::SingleThreadTaskRunner::GetCurrentDefault());
   }
 
   return proxy_slot.GetOrCreateValue();
diff --git a/third_party/blink/common/loader/mime_sniffing_throttle.cc b/third_party/blink/common/loader/mime_sniffing_throttle.cc
index df593cc7..2a2bdfab 100644
--- a/third_party/blink/common/loader/mime_sniffing_throttle.cc
+++ b/third_party/blink/common/loader/mime_sniffing_throttle.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/public/common/loader/mime_sniffing_throttle.h"
 
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/base/mime_sniffer.h"
@@ -60,7 +60,8 @@
     std::tie(new_remote, new_receiver, mime_sniffing_loader) =
         MimeSniffingURLLoader::CreateLoader(
             weak_factory_.GetWeakPtr(), response_url, response_head->Clone(),
-            task_runner_ ? task_runner_ : base::ThreadTaskRunnerHandle::Get());
+            task_runner_ ? task_runner_
+                         : base::SingleThreadTaskRunner::GetCurrentDefault());
     delegate_->InterceptResponse(std::move(new_remote), std::move(new_receiver),
                                  &source_loader, &source_client_receiver,
                                  &body);
diff --git a/third_party/blink/common/loader/throttling_url_loader.cc b/third_party/blink/common/loader/throttling_url_loader.cc
index 86b59db..1d00d3e 100644
--- a/third_party/blink/common/loader/throttling_url_loader.cc
+++ b/third_party/blink/common/loader/throttling_url_loader.cc
@@ -14,7 +14,6 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "net/http/http_status_code.h"
 #include "net/http/http_util.h"
@@ -335,8 +334,8 @@
 
     auto throttles =
         std::make_unique<std::vector<ThrottleEntry>>(std::move(throttles_));
-    base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
-                                                    std::move(throttles));
+    base::SingleThreadTaskRunner::GetCurrentDefault()->DeleteSoon(
+        FROM_HERE, std::move(throttles));
   }
 }
 
diff --git a/third_party/blink/common/loader/throttling_url_loader_unittest.cc b/third_party/blink/common/loader/throttling_url_loader_unittest.cc
index 73606200..b6a55192 100644
--- a/third_party/blink/common/loader/throttling_url_loader_unittest.cc
+++ b/third_party/blink/common/loader/throttling_url_loader_unittest.cc
@@ -419,7 +419,7 @@
     loader_ = ThrottlingURLLoader::CreateLoaderAndStart(
         factory_.shared_factory(), std::move(throttles_), /*request_id=*/0,
         /*options=*/0, &request, &client_, TRAFFIC_ANNOTATION_FOR_TESTS,
-        base::ThreadTaskRunnerHandle::Get());
+        base::SingleThreadTaskRunner::GetCurrentDefault());
     factory_.factory_remote().FlushForTesting();
   }
 
@@ -658,7 +658,7 @@
   loader_ = ThrottlingURLLoader::CreateLoaderAndStart(
       factory_.shared_factory(), std::move(throttles_), 0, 0, &request,
       &client_, TRAFFIC_ANNOTATION_FOR_TESTS,
-      base::ThreadTaskRunnerHandle::Get());
+      base::SingleThreadTaskRunner::GetCurrentDefault());
 
   loader_ = nullptr;
 
diff --git a/third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom b/third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom
index b02cb2d..46bc13e 100644
--- a/third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom
+++ b/third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom
@@ -18,7 +18,9 @@
   kNoModificationAllowedError,
   // Modifications to the underlying file system were not allowed in that way.
   kInvalidModificationError,
-  // The object being operated on was in an invalid state for the operation.
+  // The feature is not yet launched.
+  kNotSupportedError,
+  // The object being operated on was in an invalid state for the operation
   kInvalidState,
   // An invalid argument was passed to a method.
   kInvalidArgument,
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index 46e3314..b89eabe 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -3749,7 +3749,7 @@
   kCSSAtRuleOrnaments = 4408,
   kCSSAtRuleAnnotation = 4409,
   kServiceWorkerBypassFetchHandlerForMainResource = 4410,
-  kV8Document_HasPrivateStateToken_Method = 4411,
+  kV8Document_HasPrivateToken_Method = 4411,
   kServiceWorkerSkippedForEmptyFetchHandler = 4412,
   kImageSet = 4413,
   kWindowCloseHistoryLengthOne = 4414,
diff --git a/third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h b/third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h
index 2d62b0d8..a959139 100644
--- a/third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h
+++ b/third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h
@@ -21,11 +21,11 @@
 class WebThreadScheduler;
 class WebMockThreadScheduler;
 
-// Creates simple scheduling infrastructure for unit tests.
-// It allows creation of FrameSchedulers and PageSchedulers, but doesn't provide
-// any task running infrastructure, relying on the presence of
-// ThreadTaskRunnerHandle::Get() instead, meaning that the users also have to
-// create base::debug::TaskEnvironment.
+// Creates simple scheduling infrastructure for unit tests.  It allows creation
+// of FrameSchedulers and PageSchedulers, but doesn't provide any task running
+// infrastructure, relying on the presence of
+// SingleThreadTaskRunner::GetCurrentDefault() instead, meaning that the users
+// also have to create base::debug::TaskEnvironment.
 std::unique_ptr<WebThreadScheduler> CreateWebMainThreadSchedulerForTests();
 
 // Simple scheduling infrastructure for unit tests, with the addition of mocked
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni
index 35f003a..71f7b10 100644
--- a/third_party/blink/renderer/bindings/generated_in_core.gni
+++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -1739,6 +1739,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_elementcreationoptions_string.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_event_string.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_event_string.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_fencedframeconfig_usvstring.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_fencedframeconfig_usvstring.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_file_formdata_usvstring.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_file_formdata_usvstring.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_file_usvstring.cc",
diff --git a/third_party/blink/renderer/core/animation/interpolable_color.cc b/third_party/blink/renderer/core/animation/interpolable_color.cc
index 4514922..04006c6 100644
--- a/third_party/blink/renderer/core/animation/interpolable_color.cc
+++ b/third_party/blink/renderer/core/animation/interpolable_color.cc
@@ -14,20 +14,8 @@
 
 namespace blink {
 
-InterpolableColor::InterpolableColor() {
-  // Transparent black
-  param0_ = std::make_unique<InterpolableNumber>(0);
-  param1_ = std::make_unique<InterpolableNumber>(0);
-  param2_ = std::make_unique<InterpolableNumber>(0);
-  alpha_ = std::make_unique<InterpolableNumber>(0);
-
-  // For non-keyword colors, all keywords are set to zero.
-  color_keyword_fractions_ =
-      std::make_unique<InterpolableList>(kColorKeywordCount);
-  for (unsigned i = 0; i < kColorKeywordCount; i++) {
-    color_keyword_fractions_->Set(i, std::make_unique<InterpolableNumber>(0));
-  }
-}
+// All colors are zero-initialized (transparent black).
+InterpolableColor::InterpolableColor() = default;
 
 std::unique_ptr<InterpolableColor> InterpolableColor::Create(Color color) {
   std::unique_ptr<InterpolableColor> result =
@@ -39,10 +27,10 @@
 
   // All params are stored pre-multiplied.
   // https://www.w3.org/TR/css-color-4/#interpolation-alpha
-  result->param0_->Set(color.Param0() * color.FloatAlpha());
-  result->param1_->Set(color.Param1() * color.FloatAlpha());
-  result->param2_->Set(color.Param2() * color.FloatAlpha());
-  result->alpha_->Set(color.FloatAlpha());
+  result->param0_.Set(color.Param0() * color.FloatAlpha());
+  result->param1_.Set(color.Param1() * color.FloatAlpha());
+  result->param2_.Set(color.Param2() * color.FloatAlpha());
+  result->alpha_.Set(color.FloatAlpha());
 
   return result;
 }
@@ -59,8 +47,7 @@
   // creation the entry for the correct keyword is set to "1" and all others are
   // "0". These values are interpolated as normal. When the color is resolved
   // the proper fraction of the keyword color is added in.
-  result->color_keyword_fractions_->Set(
-      keyword_index, std::make_unique<InterpolableNumber>(1));
+  result->color_keyword_fractions_.Set(keyword_index, InterpolableNumber(1));
   // Keyword colors are functionally legacy colors for interpolation.
   result->color_interpolation_space_ = Color::ColorInterpolationSpace::kSRGB;
 
@@ -91,11 +78,11 @@
 }
 
 InterpolableColor::InterpolableColor(
-    std::unique_ptr<InterpolableNumber> param0,
-    std::unique_ptr<InterpolableNumber> param1,
-    std::unique_ptr<InterpolableNumber> param2,
-    std::unique_ptr<InterpolableNumber> alpha,
-    std::unique_ptr<InterpolableList> color_keyword_fractions,
+    InterpolableNumber param0,
+    InterpolableNumber param1,
+    InterpolableNumber param2,
+    InterpolableNumber alpha,
+    InterpolableNumberList color_keyword_fractions,
     Color::ColorInterpolationSpace color_interpolation_space)
     : param0_(std::move(param0)),
       param1_(std::move(param1)),
@@ -103,38 +90,33 @@
       alpha_(std::move(alpha)),
       color_keyword_fractions_(std::move(color_keyword_fractions)),
       color_interpolation_space_(color_interpolation_space) {
-  DCHECK(param0_);
-  DCHECK(param1_);
-  DCHECK(param2_);
-  DCHECK(alpha_);
-  DCHECK(color_keyword_fractions_);
-  DCHECK_EQ(color_keyword_fractions_->length(), kColorKeywordCount);
+  DCHECK_EQ(color_keyword_fractions_.length(), kColorKeywordCount);
 }
 
 InterpolableColor* InterpolableColor::RawClone() const {
-  DCHECK_EQ(color_keyword_fractions_->length(), kColorKeywordCount);
-  return new InterpolableColor(
-      param0_->Clone(), param1_->Clone(), param2_->Clone(), alpha_->Clone(),
-      color_keyword_fractions_->Clone(), color_interpolation_space_);
+  DCHECK_EQ(color_keyword_fractions_.length(), kColorKeywordCount);
+  return new InterpolableColor(param0_, param1_, param2_, alpha_,
+                               color_keyword_fractions_.Clone(),
+                               color_interpolation_space_);
 }
 
 InterpolableColor* InterpolableColor::RawCloneAndZero() const {
-  return new InterpolableColor(param0_->CloneAndZero(), param1_->CloneAndZero(),
-                               param2_->CloneAndZero(), alpha_->CloneAndZero(),
-                               color_keyword_fractions_->CloneAndZero(),
+  return new InterpolableColor(InterpolableNumber(0), InterpolableNumber(0),
+                               InterpolableNumber(0), InterpolableNumber(0),
+                               color_keyword_fractions_.CloneAndZero(),
                                color_interpolation_space_);
 }
 
 Color InterpolableColor::GetColor() const {
   // Prevent dividing by zero.
-  if (alpha_->Value() == 0)
+  if (alpha_.Value() == 0)
     return Color::kTransparent;
 
   // All params are stored pre-multiplied.
-  float param0 = param0_->Value() / alpha_->Value();
-  float param1 = param1_->Value() / alpha_->Value();
-  float param2 = param2_->Value() / alpha_->Value();
-  float alpha = ClampTo<double>(alpha_->Value(), 0, 1);
+  float param0 = param0_.Value() / alpha_.Value();
+  float param1 = param1_.Value() / alpha_.Value();
+  float param2 = param2_.Value() / alpha_.Value();
+  float alpha = ClampTo<double>(alpha_.Value(), 0, 1);
 
   switch (color_interpolation_space_) {
     case Color::ColorInterpolationSpace::kSRGB:
@@ -154,12 +136,12 @@
     const InterpolableValue& other) const {
   const InterpolableColor& other_color = To<InterpolableColor>(other);
   DCHECK_EQ(color_interpolation_space_, other_color.color_interpolation_space_);
-  param0_->AssertCanInterpolateWith(*other_color.param0_);
-  param1_->AssertCanInterpolateWith(*other_color.param1_);
-  param2_->AssertCanInterpolateWith(*other_color.param2_);
-  alpha_->AssertCanInterpolateWith(*other_color.alpha_);
-  color_keyword_fractions_->AssertCanInterpolateWith(
-      *other_color.color_keyword_fractions_);
+  param0_.AssertCanInterpolateWith(other_color.param0_);
+  param1_.AssertCanInterpolateWith(other_color.param1_);
+  param2_.AssertCanInterpolateWith(other_color.param2_);
+  alpha_.AssertCanInterpolateWith(other_color.alpha_);
+  color_keyword_fractions_.AssertCanInterpolateWith(
+      other_color.color_keyword_fractions_);
 }
 
 bool InterpolableColor::IsKeywordColor() const {
@@ -167,9 +149,8 @@
   // important for resolving the color. If any of these store a non-zero value,
   // then the interpolated color is not the same as the color produced by simply
   // looking at the param values and color interpolation space.
-  for (wtf_size_t i = 0; i < color_keyword_fractions_->length(); i++) {
-    double keyword_fraction =
-        To<InterpolableNumber>(*(color_keyword_fractions_->Get(i))).Value();
+  for (wtf_size_t i = 0; i < color_keyword_fractions_.length(); i++) {
+    double keyword_fraction = color_keyword_fractions_.Get(i).Value();
     if (keyword_fraction != 0)
       return false;
   }
@@ -183,10 +164,10 @@
 
   Color underlying_color = GetColor();
   underlying_color.ConvertToColorInterpolationSpace(color_interpolation_space);
-  param0_->Set(underlying_color.Param0() * underlying_color.FloatAlpha());
-  param1_->Set(underlying_color.Param1() * underlying_color.FloatAlpha());
-  param2_->Set(underlying_color.Param2() * underlying_color.FloatAlpha());
-  alpha_->Set(underlying_color.FloatAlpha());
+  param0_.Set(underlying_color.Param0() * underlying_color.FloatAlpha());
+  param1_.Set(underlying_color.Param1() * underlying_color.FloatAlpha());
+  param2_.Set(underlying_color.Param2() * underlying_color.FloatAlpha());
+  alpha_.Set(underlying_color.FloatAlpha());
 
   color_interpolation_space_ = color_interpolation_space;
 }
@@ -212,26 +193,25 @@
 void InterpolableColor::Scale(double scale) {
 // A guard to prevent overload with very large values.
 #if DCHECK_IS_ON()
-  bool param0_is_positive = param0_->Value() > 0.0;
-  bool param1_is_positive = param1_->Value() > 0.0;
-  bool param2_is_positive = param2_->Value() > 0.0;
-  bool alpha_is_positive = alpha_->Value() > 0.0;
+  bool param0_is_positive = param0_.Value() > 0.0;
+  bool param1_is_positive = param1_.Value() > 0.0;
+  bool param2_is_positive = param2_.Value() > 0.0;
+  bool alpha_is_positive = alpha_.Value() > 0.0;
 #endif
 
-  param0_->Scale(scale);
-  param1_->Scale(scale);
-  param2_->Scale(scale);
-  alpha_->Scale(scale);
-  color_keyword_fractions_->Scale(scale);
+  param0_.Scale(scale);
+  param1_.Scale(scale);
+  param2_.Scale(scale);
+  alpha_.Scale(scale);
+  color_keyword_fractions_.Scale(scale);
 
 #if DCHECK_IS_ON()
-  DCHECK_EQ(param0_is_positive * (scale > 0), param0_->Value() > 0.0);
-  DCHECK_EQ(param1_is_positive * (scale > 0), param1_->Value() > 0.0);
-  DCHECK_EQ(param2_is_positive * (scale > 0), param2_->Value() > 0.0);
-  DCHECK_EQ(alpha_is_positive * (scale > 0), alpha_->Value() > 0.0);
-  for (unsigned i = 0; i < color_keyword_fractions_->length(); i++) {
-    double fraction =
-        To<InterpolableNumber>(color_keyword_fractions_->Get(i))->Value();
+  DCHECK_EQ(param0_is_positive * (scale > 0), param0_.Value() > 0.0);
+  DCHECK_EQ(param1_is_positive * (scale > 0), param1_.Value() > 0.0);
+  DCHECK_EQ(param2_is_positive * (scale > 0), param2_.Value() > 0.0);
+  DCHECK_EQ(alpha_is_positive * (scale > 0), alpha_.Value() > 0.0);
+  for (unsigned i = 0; i < color_keyword_fractions_.length(); i++) {
+    double fraction = color_keyword_fractions_.Get(i).Value();
     DCHECK_GE(fraction, 0);
     DCHECK_LE(fraction, 1);
   }
@@ -240,11 +220,11 @@
 
 void InterpolableColor::Add(const InterpolableValue& other) {
   const InterpolableColor& other_color = To<InterpolableColor>(other);
-  param0_->Add(*other_color.param0_);
-  param1_->Add(*other_color.param1_);
-  param2_->Add(*other_color.param2_);
-  alpha_->Add(*other_color.alpha_);
-  color_keyword_fractions_->Add(*other_color.color_keyword_fractions_);
+  param0_.Add(other_color.param0_);
+  param1_.Add(other_color.param1_);
+  param2_.Add(other_color.param2_);
+  alpha_.Add(other_color.alpha_);
+  color_keyword_fractions_.Add(other_color.color_keyword_fractions_);
 }
 
 void InterpolableColor::Interpolate(const InterpolableValue& to,
@@ -257,29 +237,29 @@
   DCHECK_EQ(result_color.color_interpolation_space_,
             color_interpolation_space_);
 
-  param0_->Interpolate(*to_color.param0_, progress, *result_color.param0_);
-  param1_->Interpolate(*to_color.param1_, progress, *result_color.param1_);
-  param2_->Interpolate(*to_color.param2_, progress, *result_color.param2_);
-  alpha_->Interpolate(*to_color.alpha_, progress, *result_color.alpha_);
+  param0_.Interpolate(to_color.param0_, progress, result_color.param0_);
+  param1_.Interpolate(to_color.param1_, progress, result_color.param1_);
+  param2_.Interpolate(to_color.param2_, progress, result_color.param2_);
+  alpha_.Interpolate(to_color.alpha_, progress, result_color.alpha_);
 
-  color_keyword_fractions_->Interpolate(*to_color.color_keyword_fractions_,
-                                        progress,
-                                        *result_color.color_keyword_fractions_);
+  color_keyword_fractions_.Interpolate(to_color.color_keyword_fractions_,
+                                       progress,
+                                       result_color.color_keyword_fractions_);
 }
 
 void InterpolableColor::Composite(const InterpolableColor& other,
                                   double fraction) {
-  param0_->ScaleAndAdd(fraction, *other.param0_);
-  param1_->ScaleAndAdd(fraction, *other.param1_);
-  param2_->ScaleAndAdd(fraction, *other.param2_);
+  param0_.ScaleAndAdd(fraction, other.param0_);
+  param1_.ScaleAndAdd(fraction, other.param1_);
+  param2_.ScaleAndAdd(fraction, other.param2_);
   // TODO(crbug.com/981326): Test coverage has historically been missing for
   // composition of transparent colors. We should aim for interop with Firefox
   // and Safari.
-  if (alpha_->Value() != other.alpha_->Value())
-    alpha_->ScaleAndAdd(fraction, *other.alpha_);
+  if (alpha_.Value() != other.alpha_.Value())
+    alpha_.ScaleAndAdd(fraction, other.alpha_);
 
-  color_keyword_fractions_->ScaleAndAdd(fraction,
-                                        *other.color_keyword_fractions_);
+  color_keyword_fractions_.ScaleAndAdd(fraction,
+                                       other.color_keyword_fractions_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/interpolable_color.h b/third_party/blink/renderer/core/animation/interpolable_color.h
index c4055d9..08a535c 100644
--- a/third_party/blink/renderer/core/animation/interpolable_color.h
+++ b/third_party/blink/renderer/core/animation/interpolable_color.h
@@ -63,18 +63,17 @@
 
   bool IsKeywordColor() const;
 
-  double Param0() const { return param0_->Value(); }
-  double Param1() const { return param1_->Value(); }
-  double Param2() const { return param2_->Value(); }
-  double Alpha() const { return alpha_->Value(); }
+  double Param0() const { return param0_.Value(); }
+  double Param1() const { return param1_.Value(); }
+  double Param2() const { return param2_.Value(); }
+  double Alpha() const { return alpha_.Value(); }
   Color::ColorInterpolationSpace ColorInterpolationSpace() const {
     return color_interpolation_space_;
   }
 
   double GetColorFraction(ColorKeyword keyword) const {
     int keyword_index = static_cast<int>(keyword);
-    return To<InterpolableNumber>(color_keyword_fractions_->Get(keyword_index))
-        ->Value();
+    return color_keyword_fractions_.Get(keyword_index).Value();
   }
 
   std::unique_ptr<InterpolableColor> Clone() const {
@@ -84,11 +83,14 @@
   void Composite(const InterpolableColor& other, double fraction);
 
  private:
-  InterpolableColor(std::unique_ptr<InterpolableNumber> param0,
-                    std::unique_ptr<InterpolableNumber> param1,
-                    std::unique_ptr<InterpolableNumber> param2,
-                    std::unique_ptr<InterpolableNumber> alpha,
-                    std::unique_ptr<InterpolableList> color_keyword_fractions,
+  using InterpolableNumberList =
+      StaticInterpolableList<InterpolableNumber, kColorKeywordCount>;
+
+  InterpolableColor(InterpolableNumber param0,
+                    InterpolableNumber param1,
+                    InterpolableNumber param2,
+                    InterpolableNumber alpha,
+                    InterpolableNumberList color_keyword_fractions,
                     Color::ColorInterpolationSpace color_interpolation_space);
 
   void ConvertToColorInterpolationSpace(
@@ -98,12 +100,12 @@
 
   // All color params are stored premultiplied by alpha.
   // https://csswg.sesse.net/css-color-4/#interpolation-space
-  std::unique_ptr<InterpolableNumber> param0_;
-  std::unique_ptr<InterpolableNumber> param1_;
-  std::unique_ptr<InterpolableNumber> param2_;
-  std::unique_ptr<InterpolableNumber> alpha_;
+  InterpolableNumber param0_;
+  InterpolableNumber param1_;
+  InterpolableNumber param2_;
+  InterpolableNumber alpha_;
 
-  std::unique_ptr<InterpolableList> color_keyword_fractions_;
+  InterpolableNumberList color_keyword_fractions_;
 
   Color::ColorInterpolationSpace color_interpolation_space_ =
       Color::ColorInterpolationSpace::kNone;
diff --git a/third_party/blink/renderer/core/animation/interpolable_value.h b/third_party/blink/renderer/core/animation/interpolable_value.h
index 7d556d1c5..62f1b3b 100644
--- a/third_party/blink/renderer/core/animation/interpolable_value.h
+++ b/third_party/blink/renderer/core/animation/interpolable_value.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLABLE_VALUE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLABLE_VALUE_H_
 
+#include <array>
 #include <memory>
 #include <utility>
 
@@ -82,6 +83,7 @@
 
 class CORE_EXPORT InterpolableNumber final : public InterpolableValue {
  public:
+  InterpolableNumber() = default;
   explicit InterpolableNumber(double value) : value_(value) {}
 
   double Value() const { return value_; }
@@ -112,14 +114,13 @@
     return new InterpolableNumber(0);
   }
 
-  double value_;
+  double value_ = 0.;
 };
 
 class CORE_EXPORT InterpolableList final : public InterpolableValue {
  public:
   explicit InterpolableList(wtf_size_t size) : values_(size) {}
 
-  // Move-only; use Clone() to make a copy.
   InterpolableList(const InterpolableList&) = delete;
   InterpolableList& operator=(const InterpolableList&) = delete;
   InterpolableList(InterpolableList&&) = default;
@@ -167,6 +168,61 @@
   Vector<std::unique_ptr<InterpolableValue>> values_;
 };
 
+template <typename T, size_t Size>
+class CORE_EXPORT StaticInterpolableList final {
+ public:
+  const T& Get(wtf_size_t position) const { return values_[position]; }
+  T& GetMutable(wtf_size_t position) { return values_[position]; }
+
+  wtf_size_t length() const { return static_cast<wtf_size_t>(values_.size()); }
+
+  void Set(wtf_size_t position, T value) {
+    values_[position] = std::move(value);
+  }
+
+  StaticInterpolableList Clone() const { return *this; }
+  StaticInterpolableList CloneAndZero() const { return {}; }
+
+  void Interpolate(const StaticInterpolableList& to,
+                   const double progress,
+                   StaticInterpolableList& result) const {
+    for (wtf_size_t i = 0; i < length(); i++) {
+      values_[i].Interpolate(to.values_[i], progress, result.values_[i]);
+    }
+  }
+
+  bool Equals(const StaticInterpolableList& other) const {
+    for (wtf_size_t i = 0; i < length(); i++) {
+      if (!values_[i].Equals(other.values_[i]))
+        return false;
+    }
+    return true;
+  }
+
+  void Scale(double scale) {
+    for (auto& val : values_)
+      val.Scale(scale);
+  }
+
+  void Add(const StaticInterpolableList& other) {
+    for (wtf_size_t i = 0; i < length(); i++)
+      values_[i].Add(other.values_[i]);
+  }
+
+  // We override this to avoid two passes on the list from the base version.
+  void ScaleAndAdd(double scale, const StaticInterpolableList& other) {
+    for (wtf_size_t i = 0; i < length(); i++)
+      values_[i].ScaleAndAdd(scale, other.values_[i]);
+  }
+
+  void AssertCanInterpolateWith(const StaticInterpolableList& other) const {
+    DCHECK_EQ(other.length(), length());
+  }
+
+ private:
+  std::array<T, Size> values_;
+};
+
 template <>
 struct DowncastTraits<InterpolableNumber> {
   static bool AllowFrom(const InterpolableValue& value) {
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 0f03c463..725ad63e 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -6405,12 +6405,14 @@
 ScriptPromise Document::hasTrustToken(ScriptState* script_state,
                                       const String& issuer,
                                       ExceptionState& exception_state) {
-  return hasPrivateStateToken(script_state, issuer, exception_state);
+  return hasPrivateToken(script_state, issuer, "private-state-token",
+                         exception_state);
 }
 
-ScriptPromise Document::hasPrivateStateToken(ScriptState* script_state,
-                                             const String& issuer,
-                                             ExceptionState& exception_state) {
+ScriptPromise Document::hasPrivateToken(ScriptState* script_state,
+                                        const String& issuer,
+                                        const String& type,
+                                        ExceptionState& exception_state) {
   ScriptPromiseResolver* resolver =
       MakeGarbageCollected<ScriptPromiseResolver>(script_state);
 
@@ -6425,9 +6427,16 @@
   if (!issuer_url.ProtocolIsInHTTPFamily() ||
       !issuer_origin->IsPotentiallyTrustworthy()) {
     exception_state.ThrowTypeError(
-        "hasPrivateStateToken: Trust token issuer origins must be both HTTP(S) "
-        "and "
-        "secure (\"potentially trustworthy\").");
+        "hasPrivateToken: Private Token issuer origins must be both HTTP(S) "
+        "and secure (\"potentially trustworthy\").");
+    resolver->Reject(exception_state);
+    return promise;
+  }
+
+  if (type != "private-state-token") {
+    exception_state.ThrowTypeError(
+        "hasPrivateToken: Private Token types other than "
+        "private-state-token are unsupported.");
     resolver->Reject(exception_state);
     return promise;
   }
@@ -6440,7 +6449,7 @@
     // case there are other situations in which the top frame origin might be
     // absent.
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "hasPrivateStateToken: Cannot execute in "
+                                      "hasPrivateToken: Cannot execute in "
                                       "documents lacking top-frame origins.");
     resolver->Reject(exception_state);
     return promise;
@@ -6451,7 +6460,7 @@
       top_frame_origin->Protocol() != url::kHttpScheme) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kNotAllowedError,
-        "hasPrivateStateToken: Cannot execute in "
+        "hasPrivateToken: Cannot execute in "
         "documents without secure, HTTP(S), top-frame origins.");
     resolver->Reject(exception_state);
     return promise;
@@ -6490,7 +6499,7 @@
               ScriptState::Scope scope(state);
               resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
                   state->GetIsolate(), DOMExceptionCode::kOperationError,
-                  "Failed to retrieve hasPrivateStateToken response. (Would "
+                  "Failed to retrieve hasPrivateToken response. (Would "
                   "associating the given issuer with this top-level origin "
                   "have exceeded its number-of-issuers limit?)"));
             }
@@ -6505,6 +6514,7 @@
 
 ScriptPromise Document::hasRedemptionRecord(ScriptState* script_state,
                                             const String& issuer,
+                                            const String& type,
                                             ExceptionState& exception_state) {
   ScriptPromiseResolver* resolver =
       MakeGarbageCollected<ScriptPromiseResolver>(script_state);
@@ -6520,9 +6530,16 @@
   if (!issuer_url.ProtocolIsInHTTPFamily() ||
       !issuer_origin->IsPotentiallyTrustworthy()) {
     exception_state.ThrowTypeError(
-        "hasRedemptionRecord: Trust token issuer origins must be both HTTP(S) "
-        "and "
-        "secure (\"potentially trustworthy\").");
+        "hasRedemptionRecord: Private Token issuer origins must be both "
+        "HTTP(S) and secure (\"potentially trustworthy\").");
+    resolver->Reject(exception_state);
+    return promise;
+  }
+
+  if (type != "private-state-token") {
+    exception_state.ThrowTypeError(
+        "hasRedemptionRecord: Private Token types other than "
+        "private-state-token are unsupported.");
     resolver->Reject(exception_state);
     return promise;
   }
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 333b5ea..a020c70 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1202,15 +1202,16 @@
   // https://wicg.github.io/scroll-to-text-fragment/#feature-detectability
   FragmentDirective& fragmentDirective() const;
 
-  // Sends a query via Mojo to ask whether the user has any private state
+  // Sends a query via Mojo to ask whether the user has any private
   // tokens. This can reject on permissions errors (e.g. associating |issuer|
   // with the top-level origin would exceed the top-level origin's limit on the
   // number of associated issuers) or on other internal errors (e.g. the network
   // service is unavailable).
-  ScriptPromise hasPrivateStateToken(ScriptState* script_state,
-                                     const String& issuer,
-                                     ExceptionState&);
-  // Being renamed to hasPrivateStateToken, will remove after all users have
+  ScriptPromise hasPrivateToken(ScriptState* script_state,
+                                const String& issuer,
+                                const String& type,
+                                ExceptionState&);
+  // Being renamed to hasPrivateToken, will remove after all users have
   // changed to new name.
   ScriptPromise hasTrustToken(ScriptState* script_state,
                               const String& issuer,
@@ -1223,6 +1224,7 @@
   // is unavailable).
   ScriptPromise hasRedemptionRecord(ScriptState* script_state,
                                     const String& issuer,
+                                    const String& type,
                                     ExceptionState&);
 
   void ariaNotify(const String announcement, const AriaNotificationOptions*);
@@ -2128,7 +2130,7 @@
   void DisplayNoneChangedForFrame();
 
   // Handles a connection error to |trust_token_query_answerer_| by rejecting
-  // all pending promises created by |hasPrivateStateToken| and
+  // all pending promises created by |hasPrivateToken| and
   // |hasRedemptionRecord|.
   void TrustTokenQueryAnswererConnectionError();
 
diff --git a/third_party/blink/renderer/core/dom/document.idl b/third_party/blink/renderer/core/dom/document.idl
index af2775f..490762f 100644
--- a/third_party/blink/renderer/core/dom/document.idl
+++ b/third_party/blink/renderer/core/dom/document.idl
@@ -198,12 +198,11 @@
     [MeasureAs=PrefixedPageVisibility, ImplementedAs=visibilityState] readonly attribute DOMString webkitVisibilityState;
     [MeasureAs=PrefixedPageVisibility, ImplementedAs=hidden] readonly attribute boolean webkitHidden;
 
-    // Trust Token API (https://github.com/wicg/trust-token-api)
+    // Private Token API (https://github.com/wicg/trust-token-api)
     [CallWith=ScriptState, Measure, RaisesException, NewObject, SecureContext, RuntimeEnabled=PrivateStateTokens] Promise<boolean> hasTrustToken(USVString issuer);
-    [CallWith=ScriptState, Measure, RaisesException, NewObject, SecureContext, RuntimeEnabled=PrivateStateTokens] Promise<boolean> hasPrivateStateToken(USVString issuer);
+    [CallWith=ScriptState, Measure, RaisesException, NewObject, SecureContext, RuntimeEnabled=PrivateStateTokens] Promise<boolean> hasPrivateToken(USVString issuer, USVString type);
 
-    // Trust Token API (https://github.com/wicg/trust-token-api)
-    [CallWith=ScriptState, Measure, RaisesException, NewObject, SecureContext, RuntimeEnabled=PrivateStateTokens] Promise<boolean> hasRedemptionRecord(USVString issuer);
+    [CallWith=ScriptState, Measure, RaisesException, NewObject, SecureContext, RuntimeEnabled=PrivateStateTokens] Promise<boolean> hasRedemptionRecord(USVString issuer, USVString type);
 
     // Confirmation of Action API
     // https://github.com/WICG/aom/blob/gh-pages/notification-api.md
diff --git a/third_party/blink/renderer/core/dom/document_test.cc b/third_party/blink/renderer/core/dom/document_test.cc
index a3393b9..8c06524 100644
--- a/third_party/blink/renderer/core/dom/document_test.cc
+++ b/third_party/blink/renderer/core/dom/document_test.cc
@@ -1339,8 +1339,9 @@
                                  ExceptionState::kExecutionContext, "Document",
                                  "hasRedemptionRecord");
 
-  auto promise = document.hasRedemptionRecord(
-      script_state, "https://issuer.example", exception_state);
+  auto promise =
+      document.hasRedemptionRecord(script_state, "https://issuer.example",
+                                   "private-state-token", exception_state);
 
   ScriptPromiseTester promise_tester(script_state, promise);
   promise_tester.WaitUntilSettled();
@@ -1367,8 +1368,9 @@
                                  ExceptionState::kExecutionContext, "Document",
                                  "hasTrustToken");
 
-  auto promise = document.hasRedemptionRecord(
-      script_state, "https://issuer.example", exception_state);
+  auto promise =
+      document.hasRedemptionRecord(script_state, "https://issuer.example",
+                                   "private-state-token", exception_state);
 
   ScriptPromiseTester promise_tester(script_state, promise);
   promise_tester.WaitUntilSettled();
@@ -1395,8 +1397,9 @@
                                  ExceptionState::kExecutionContext, "Document",
                                  "hasRedemptionRecord");
 
-  auto promise = document.hasRedemptionRecord(
-      script_state, "https://issuer.example", exception_state);
+  auto promise =
+      document.hasRedemptionRecord(script_state, "https://issuer.example",
+                                   "private-state-token", exception_state);
 
   ScriptPromiseTester promise_tester(script_state, promise);
   promise_tester.WaitUntilSettled();
@@ -1416,9 +1419,9 @@
 
   Document& document = scope.GetDocument();
 
-  auto promise = document.hasRedemptionRecord(scope.GetScriptState(),
-                                              "https://issuer.example",
-                                              scope.GetExceptionState());
+  auto promise = document.hasRedemptionRecord(
+      scope.GetScriptState(), "https://issuer.example", "private-state-token",
+      scope.GetExceptionState());
   DocumentTest::SimulateTrustTokenQueryAnswererConnectionError(&document);
 
   ASSERT_TRUE(promise.IsAssociatedWith(scope.GetScriptState()));
@@ -1442,8 +1445,9 @@
                                  ExceptionState::kExecutionContext, "Document",
                                  "hasRedemptionRecord");
 
-  auto promise = document.hasRedemptionRecord(
-      script_state, "https://issuer.example", exception_state);
+  auto promise =
+      document.hasRedemptionRecord(script_state, "https://issuer.example",
+                                   "private-state-token", exception_state);
 
   ScriptPromiseTester promise_tester(script_state, promise);
   promise_tester.WaitUntilSettled();
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.cc
index 19a2d2b..88b096b4 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.cc
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.cc
@@ -5,13 +5,9 @@
 #include "third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.h"
 
 #include <algorithm>
-#include "third_party/blink/renderer/core/dom/first_letter_pseudo_element.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
 #include "third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.h"
-#include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
-#include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
-#include "third_party/blink/renderer/core/layout/line/root_inline_box.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
 
 namespace blink {
@@ -128,16 +124,6 @@
   }
 }
 
-bool TextIteratorTextNodeHandler::ShouldHandleFirstLetter(
-    const LayoutText& layout_text) const {
-  if (handled_first_letter_)
-    return false;
-  if (!layout_text.IsTextFragment())
-    return false;
-  const auto& text_fragment = To<LayoutTextFragment>(layout_text);
-  return offset_ < text_fragment.TextStartOffset();
-}
-
 void TextIteratorTextNodeHandler::HandleTextNodeInRange(const Text* node,
                                                         unsigned start_offset,
                                                         unsigned end_offset) {
@@ -149,8 +135,6 @@
   text_node_ = node;
   offset_ = start_offset;
   end_offset_ = end_offset;
-  handled_first_letter_ = false;
-  first_letter_text_ = nullptr;
   mapping_units_.clear();
 
   const NGOffsetMapping* const mapping =
@@ -184,51 +168,4 @@
   HandleTextNodeStartFrom(node, 0);
 }
 
-void TextIteratorTextNodeHandler::HandleTextNodeFirstLetter(
-    LayoutTextFragment* layout_object) {
-  handled_first_letter_ = true;
-
-  if (!layout_object->IsRemainingTextLayoutObject())
-    return;
-
-  FirstLetterPseudoElement* first_letter_element =
-      layout_object->GetFirstLetterPseudoElement();
-  if (!first_letter_element)
-    return;
-
-  LayoutObject* pseudo_layout_object = first_letter_element->GetLayoutObject();
-  if (pseudo_layout_object->Style()->Visibility() != EVisibility::kVisible &&
-      !IgnoresStyleVisibility())
-    return;
-
-  LayoutObject* first_letter = pseudo_layout_object->SlowFirstChild();
-
-  sorted_text_boxes_.clear();
-  CHECK(first_letter && first_letter->IsText());
-  first_letter_text_ = To<LayoutText>(first_letter);
-}
-
-void TextIteratorTextNodeHandler::EmitChar16Before(UChar code_unit,
-                                                   unsigned offset) {
-  text_state_.EmitChar16Before(code_unit, *text_node_, offset);
-}
-
-void TextIteratorTextNodeHandler::EmitReplacmentCodeUnit(UChar code_unit,
-                                                         unsigned offset) {
-  text_state_.EmitReplacmentCodeUnit(code_unit, *text_node_, offset);
-}
-
-void TextIteratorTextNodeHandler::EmitText(const LayoutText* layout_object,
-                                           unsigned text_start_offset,
-                                           unsigned text_end_offset) {
-  String string = behavior_.EmitsOriginalText() ? layout_object->OriginalText()
-                                                : layout_object->GetText();
-  if (behavior_.EmitsSpaceForNbsp())
-    string.Replace(kNoBreakSpaceCharacter, kSpaceCharacter);
-  text_state_.EmitText(*text_node_,
-                       text_start_offset + layout_object->TextStartOffset(),
-                       text_end_offset + layout_object->TextStartOffset(),
-                       string, text_start_offset, text_end_offset);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.h b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.h
index 82ade8f..aa3b1639 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.h
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.h
@@ -14,9 +14,6 @@
 
 namespace blink {
 
-class InlineTextBox;
-class LayoutText;
-class LayoutTextFragment;
 class TextIteratorTextState;
 
 // TextIteratorTextNodeHandler extracts plain text from a text node by calling
@@ -48,10 +45,6 @@
                              unsigned end_offset);
 
  private:
-  void HandlePreFormattedTextNode();
-  void HandleTextNodeFirstLetter(LayoutTextFragment*);
-  bool ShouldHandleFirstLetter(const LayoutText&) const;
-  void ProceedToRemainingText();
   void HandleTextNodeWithLayoutNG();
 
   // Used when the visibility of the style should not affect text gathering.
@@ -59,16 +52,6 @@
     return behavior_.IgnoresStyleVisibility();
   }
 
-  // Emits |code_unit| before |offset| of characters in |text_node_|.
-  void EmitChar16Before(UChar code_unit, unsigned offset);
-  // Emits |code_unit| as replacement of a code unit after |offset| in
-  // |text_node_|.
-  void EmitReplacmentCodeUnit(UChar code_unit, unsigned offset);
-
-  void EmitText(const LayoutText* layout_object,
-                unsigned text_start_offset,
-                unsigned text_end_offset);
-
   // The current text node and offset range, from which text should be emitted.
   const Text* text_node_ = nullptr;
   unsigned offset_ = 0;
@@ -78,16 +61,6 @@
   NGOffsetMapping::UnitVector mapping_units_;
   wtf_size_t mapping_units_index_ = 0;
 
-  // Used when deciding text fragment created by :first-letter should be looked
-  // into.
-  bool handled_first_letter_ = false;
-  // Used to point to LayoutText object for :first-letter.
-  LayoutText* first_letter_text_ = nullptr;
-
-  // Used when text boxes are out of order (Hebrew/Arabic w/ embedded LTR text)
-  HeapVector<Member<InlineTextBox>> sorted_text_boxes_;
-  wtf_size_t sorted_text_boxes_position_ = 0;
-
   const TextIteratorBehavior behavior_;
 
   // Contains state of emitted text.
diff --git a/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_config.cc b/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_config.cc
index 4df0fb93..13124d1b 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_config.cc
+++ b/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_config.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/core/html/fenced_frame/fenced_frame_config.h"
+#include "third_party/blink/public/common/fenced_frame/fenced_frame_utils.h"
 
 namespace blink {
 
@@ -32,6 +33,11 @@
     url_attribute_visibility_ = AttributeVisibility::kTransparent;
     url_ = KURL(mapped_url.value().potentially_opaque_value.value());
   }
+
+  const absl::optional<GURL>& urn = config.urn();
+  CHECK(blink::IsValidUrnUuidURL(*urn));
+  KURL urn_uuid = KURL(*urn);
+  urn_.emplace(std::move(urn_uuid));
 }
 
 V8UnionOpaquePropertyOrUSVString* FencedFrameConfig::url() const {
diff --git a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
index 4590fbe8..194dbcd8 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
+++ b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
@@ -322,14 +322,8 @@
 
 void HTMLFencedFrameElement::setConfig(FencedFrameConfig* config) {
   config_ = config;
-
   if (config_) {
-    // Navigate to the specified url in the installed config.
-    DCHECK(config_->url());
-    KURL url =
-        config_
-            ->GetValueIgnoringVisibility<FencedFrameConfig::Attribute::kURL>();
-    Navigate(url);
+    NavigateToConfig();
   }
 }
 
@@ -559,6 +553,26 @@
   }
 }
 
+void HTMLFencedFrameElement::NavigateToConfig() {
+  CHECK(config_);
+
+  // Prioritize navigating to `config_`'s internal URN if it exists. If so, that
+  // means it was created by information from the browser process, and the URN
+  // is stored in the `FencedFrameURLMapping`. Otherwise, `config_` was
+  // constructed from script and has a user-supplied URL that `this` will
+  // navigate to instead.
+  if (config_->urn(PassKey())) {
+    KURL url = config_->urn(PassKey()).value();
+    CHECK(IsValidUrnUuidURL(GURL(url)));
+    Navigate(url);
+  } else {
+    CHECK(config_->url());
+    Navigate(
+        config_
+            ->GetValueIgnoringVisibility<FencedFrameConfig::Attribute::kURL>());
+  }
+}
+
 void HTMLFencedFrameElement::CreateDelegateAndNavigate() {
   TRACE_EVENT0("navigation",
                "HTMLFencedFrameElement::CreateDelegateAndNavigate");
@@ -582,25 +596,11 @@
 
   frame_delegate_ = FencedFrameDelegate::Create(this);
 
-  KURL url;
-  // If there is a config, the urn or url will be retrieved from it.
-  if (!config_) {
-    url = GetNonEmptyURLAttribute(html_names::kSrcAttr);
-  } else if (config_->urn(PassKey())) {
-    // TODO(lbrady) Right now, the URN should always be null. It will eventually
-    // be able to have a value read into it. We will enable the DCHECK at that
-    // point.
-    // DCHECK(IsValidUrnUuidURL(GURL(config_->urn().value())));
-    NOTREACHED();
-    url = config_->urn(PassKey()).value();
+  if (config_) {
+    NavigateToConfig();
   } else {
-    DCHECK(config_->url());
-    url =
-        config_
-            ->GetValueIgnoringVisibility<FencedFrameConfig::Attribute::kURL>();
+    Navigate(GetNonEmptyURLAttribute(html_names::kSrcAttr));
   }
-
-  Navigate(url);
 }
 
 void HTMLFencedFrameElement::AttachLayoutTree(AttachContext& context) {
diff --git a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h
index 5ebcad90..06d8a74 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h
+++ b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h
@@ -122,6 +122,13 @@
   // `isConnected()`. It will be deferred if the page is currently prerendering.
   void Navigate(const KURL& url);
 
+  // This method delegates to `Navigate()` above only if `this` has a non-null
+  // `config_`. If that's the case, this method pulls the appropriate URL off of
+  // the config (either supplied by script, or the internal urn uuid that maps
+  // to a resource in the browser process's `FencedFrameURLMapping`), and
+  // navigates to it.
+  void NavigateToConfig();
+
   // Delegate creation will be deferred if the page is currently prerendering.
   void CreateDelegateAndNavigate();
 
diff --git a/third_party/blink/renderer/modules/ad_auction/auction_ad_config.idl b/third_party/blink/renderer/modules/ad_auction/auction_ad_config.idl
index 6732e7f..15bcceb6f 100644
--- a/third_party/blink/renderer/modules/ad_auction/auction_ad_config.idl
+++ b/third_party/blink/renderer/modules/ad_auction/auction_ad_config.idl
@@ -22,4 +22,5 @@
   record<USVString, record<USVString, double>> perBuyerPrioritySignals;
   sequence<AuctionAdConfig> componentAuctions;
   AbortSignal? signal;
+  boolean resolveToConfig;
 };
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
index 3b707332..2c2f0b8 100644
--- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
+++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/navigator.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
+#include "third_party/blink/renderer/core/html/fenced_frame/fenced_frame_config.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/modules/ad_auction/ads.h"
@@ -1484,10 +1485,16 @@
     scoped_abort_state =
         std::make_unique<ScopedAbortState>(signal, abort_handle);
   }
+
+  bool resolve_to_config =
+      config->getResolveToConfigOr(false) &&
+      RuntimeEnabledFeatures::FencedFramesAPIChangesEnabled(context);
+
   ad_auction_service_->RunAdAuction(
       std::move(mojo_config), std::move(abort_receiver),
       WTF::BindOnce(&NavigatorAuction::AuctionComplete, WrapPersistent(this),
-                    WrapPersistent(resolver), std::move(scoped_abort_state)));
+                    WrapPersistent(resolver), std::move(scoped_abort_state),
+                    resolve_to_config));
   return promise;
 }
 
@@ -1784,6 +1791,7 @@
 void NavigatorAuction::AuctionComplete(
     ScriptPromiseResolver* resolver,
     std::unique_ptr<ScopedAbortState> scoped_abort_state,
+    bool resolve_to_config,
     bool manually_aborted,
     const absl::optional<FencedFrame::RedactedFencedFrameConfig>&
         result_config) {
@@ -1800,7 +1808,11 @@
   } else if (result_config) {
     DCHECK(result_config->mapped_url().has_value());
     DCHECK(!result_config->mapped_url()->potentially_opaque_value.has_value());
-    resolver->Resolve(KURL(result_config->urn().value()));
+    if (resolve_to_config) {
+      resolver->Resolve(FencedFrameConfig::From(result_config.value()));
+    } else {
+      resolver->Resolve(KURL(result_config->urn().value()));
+    }
   } else {
     resolver->Resolve(v8::Null(script_state->GetIsolate()));
   }
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.h b/third_party/blink/renderer/modules/ad_auction/navigator_auction.h
index 05d992a..4e7cba2 100644
--- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.h
+++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.h
@@ -186,6 +186,7 @@
   void AuctionComplete(
       ScriptPromiseResolver*,
       std::unique_ptr<ScopedAbortState>,
+      bool resolve_to_config,
       bool manually_aborted,
       const absl::optional<FencedFrame::RedactedFencedFrameConfig>&);
   // Completion callback for Mojo call made by deprecatedURNToURL().
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.idl b/third_party/blink/renderer/modules/ad_auction/navigator_auction.idl
index 91373b16..1c0012a 100644
--- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.idl
+++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.idl
@@ -9,6 +9,8 @@
 // Common interest group APIs are behind AdInterestGroupAPI and specific ad
 // serving APIs are behing Fledge and Parakeet respectively.
 
+typedef (USVString or FencedFrameConfig) AdAuctionResponse;
+
 [
   ImplementedAs=NavigatorAuction,
   RuntimeEnabled=AdInterestGroupAPI,
@@ -24,7 +26,7 @@
   void updateAdInterestGroups();
 
   [RuntimeEnabled=Fledge, CallWith=ScriptState, Measure, RaisesException]
-  Promise<USVString?> runAdAuction(AuctionAdConfig config);
+  Promise<AdAuctionResponse?> runAdAuction(AuctionAdConfig config);
 
   [RuntimeEnabled=Fledge, CallWith=ScriptState, Measure, RaisesException]
   sequence<USVString> adAuctionComponents([Clamp] unsigned short numComponents);
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_error.cc b/third_party/blink/renderer/modules/file_system_access/file_system_access_error.cc
index c8f15bb..25c6a926 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_error.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_error.cc
@@ -53,6 +53,10 @@
       resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
           isolate, DOMExceptionCode::kSecurityError, message));
       break;
+    case mojom::blink::FileSystemAccessStatus::kNotSupportedError:
+      resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
+          isolate, DOMExceptionCode::kNotSupportedError, message));
+      break;
     case mojom::blink::FileSystemAccessStatus::kInvalidState:
       resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
           isolate, DOMExceptionCode::kInvalidStateError, message));
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_handle.idl b/third_party/blink/renderer/modules/file_system_access/file_system_handle.idl
index 0d463bb4..fc21d1fa 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_handle.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_handle.idl
@@ -40,10 +40,8 @@
         RuntimeEnabled=FileSystemAccessAPIExperimental
     ]  Promise<void> move(FileSystemDirectoryHandle destination_directory,
                           USVString new_entry_name);
-    [
-        CallWith=ScriptState,
-        RuntimeEnabled=FileSystemAccessAPIExperimental
-    ] Promise<void> remove(optional FileSystemRemoveOptions options = {});
+    [CallWith=ScriptState]
+    Promise<void> remove(optional FileSystemRemoveOptions options = {});
 
     [CallWith=ScriptState, Measure]
     Promise<boolean> isSameEntry(FileSystemHandle other);
diff --git a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
index 87d03dd..97364a3 100644
--- a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/task/sequenced_task_runner.h"
 #include "build/build_config.h"
 #include "cc/paint/skia_paint_canvas.h"
 #include "media/base/bind_to_current_loop.h"
@@ -353,7 +353,7 @@
     base::TimeTicks capture_timestamp) {
   // Cache the thread sending frames on first frame arrival.
   if (!origin_task_runner_.get())
-    origin_task_runner_ = base::SequencedTaskRunnerHandle::Get();
+    origin_task_runner_ = base::SequencedTaskRunner::GetCurrentDefault();
 
   DCHECK_CALLED_ON_VALID_SEQUENCE(origin_sequence_checker_);
   if (paused_)
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
index a00ad41..2876ee0 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
@@ -8,7 +8,6 @@
 
 #include "base/feature_list.h"
 #include "base/task/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "media/base/audio_parameters.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -58,7 +57,7 @@
           media::AudioProcessor::GetDefaultOutputFormat(
               capture_data_source_params,
               settings))),
-      main_thread_runner_(base::ThreadTaskRunnerHandle::Get()),
+      main_thread_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
       aec_dump_agent_impl_(AecDumpAgentImpl::Create(this)),
       stopped_(false) {
   DCHECK(main_thread_runner_);
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_track.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_track.cc
index 370e3ed9..10bfcc1 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_track.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_track.cc
@@ -960,8 +960,9 @@
   DCHECK_CALLED_ON_VALID_THREAD(main_render_thread_checker_);
 
   frame_deliverer_->AddCropVersionCallback(
-      crop_version, base::BindPostTask(base::ThreadTaskRunnerHandle::Get(),
-                                       std::move(callback)));
+      crop_version,
+      base::BindPostTask(base::SingleThreadTaskRunner::GetCurrentDefault(),
+                         std::move(callback)));
 }
 
 void MediaStreamVideoTrack::RemoveCropVersionCallback(uint32_t crop_version) {
diff --git a/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc b/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc
index 73f24a6..6058bdee 100644
--- a/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc
+++ b/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc
@@ -18,7 +18,7 @@
 #include "base/sequence_checker.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/sequenced_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "media/base/bind_to_current_loop.h"
@@ -586,7 +586,7 @@
     base::WeakPtr<MediaStreamVideoSource> media_stream_video_source)
     : video_task_runner_(video_task_runner),
       media_stream_video_source_(media_stream_video_source),
-      renderer_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      renderer_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
       muted_state_(false),
       frame_counter_(0),
       old_frame_counter_snapshot_(0),
diff --git a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc
index 8f78279..b4ddfa52 100644
--- a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc
+++ b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc
@@ -15,7 +15,7 @@
 #include "base/callback.h"
 #include "base/sequence_checker.h"
 #include "base/task/sequenced_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "cc/layers/video_frame_provider_client_impl.h"
@@ -163,7 +163,7 @@
                  scoped_refptr<base::SequencedTaskRunner> media_task_runner,
                  scoped_refptr<base::TaskRunner> worker_task_runner,
                  media::GpuVideoAcceleratorFactories* gpu_factories)
-      : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      : main_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
         player_(player),
         enqueue_frame_cb_(std::move(enqueue_frame_cb)),
         media_task_runner_(media_task_runner),
diff --git a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.cc b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.cc
index 228df53..9d98a49 100644
--- a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.cc
+++ b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.cc
@@ -15,7 +15,6 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/task/bind_post_task.h"
 #include "base/task/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "cc/paint/skia_paint_canvas.h"
@@ -189,7 +188,7 @@
     const base::WeakPtr<WebMediaPlayerMS>& player)
     : video_frame_compositor_task_runner_(video_frame_compositor_task_runner),
       video_task_runner_(video_task_runner),
-      main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      main_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
       player_(player),
       video_frame_provider_client_(nullptr),
       current_frame_rendered_(false),
diff --git a/third_party/blink/renderer/platform/exported/platform.cc b/third_party/blink/renderer/platform/exported/platform.cc
index 6290858..5bbab48 100644
--- a/third_party/blink/renderer/platform/exported/platform.cc
+++ b/third_party/blink/renderer/platform/exported/platform.cc
@@ -35,7 +35,6 @@
 #include "base/allocator/partition_allocator/memory_reclaimer.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread_checker.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "build/build_config.h"
 #include "components/viz/common/gpu/raster_context_provider.h"
@@ -146,22 +145,22 @@
  public:
   SimpleMainThread() = default;
 
-  // We rely on base::ThreadTaskRunnerHandle for tasks posted on the main
-  // thread. The task runner handle may not be available on Blink's startup
-  // (== on SimpleMainThread's construction), because some tests like
-  // blink_platform_unittests do not set up a global task environment.
-  // In those cases, a task environment is set up on a test fixture's
-  // creation, and GetTaskRunner() returns the right task runner during
-  // a test.
+  // We rely on base::SingleThreadTaskRunner::CurrentDefaultHandle for tasks
+  // posted on the main thread. The task runner handle may not be available on
+  // Blink's startup (== on SimpleMainThread's construction), because some tests
+  // like blink_platform_unittests do not set up a global task environment.  In
+  // those cases, a task environment is set up on a test fixture's creation, and
+  // GetTaskRunner() returns the right task runner during a test.
   //
-  // If GetTaskRunner() can be called from a non-main thread (including
-  // a worker thread running Mojo callbacks), we need to somehow get a task
-  // runner for the main thread. This is not possible with
-  // ThreadTaskRunnerHandle. We currently deal with this issue by setting
-  // the main thread task runner on the test startup and clearing it on
-  // the test tear-down. This is what SetMainThreadTaskRunnerForTesting() for.
-  // This function is called from Platform::SetMainThreadTaskRunnerForTesting()
-  // and Platform::UnsetMainThreadTaskRunnerForTesting().
+  // If GetTaskRunner() can be called from a non-main thread (including a worker
+  // thread running Mojo callbacks), we need to somehow get a task runner for
+  // the main thread. This is not possible with
+  // SingleThreadTaskRunner::CurrentDefaultHandle. We currently deal with this
+  // issue by setting the main thread task runner on the test startup and
+  // clearing it on the test tear-down. This is what
+  // SetMainThreadTaskRunnerForTesting() for.  This function is called from
+  // Platform::SetMainThreadTaskRunnerForTesting() and
+  // Platform::UnsetMainThreadTaskRunnerForTesting().
 
   ThreadScheduler* Scheduler() override { return &scheduler_; }
 
@@ -170,7 +169,7 @@
     if (main_thread_task_runner_for_testing_)
       return main_thread_task_runner_for_testing_;
     DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
+    return base::SingleThreadTaskRunner::GetCurrentDefault();
   }
 
   void SetMainThreadTaskRunnerForTesting(
@@ -227,7 +226,7 @@
 
   ThreadState* thread_state = ThreadState::AttachMainThread();
   new BlinkGCMemoryDumpProvider(
-      thread_state, base::ThreadTaskRunnerHandle::Get(),
+      thread_state, base::SingleThreadTaskRunner::GetCurrentDefault(),
       BlinkGCMemoryDumpProvider::HeapType::kBlinkMainThread);
 
   MemoryPressureListenerRegistry::Initialize();
@@ -242,25 +241,25 @@
   g_gc_task_runner = new GCTaskRunner(Thread::MainThread());
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       PartitionAllocMemoryDumpProvider::Instance(), "PartitionAlloc",
-      base::ThreadTaskRunnerHandle::Get());
+      base::SingleThreadTaskRunner::GetCurrentDefault());
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       FontCacheMemoryDumpProvider::Instance(), "FontCaches",
-      base::ThreadTaskRunnerHandle::Get());
+      base::SingleThreadTaskRunner::GetCurrentDefault());
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       MemoryCacheDumpProvider::Instance(), "MemoryCache",
-      base::ThreadTaskRunnerHandle::Get());
+      base::SingleThreadTaskRunner::GetCurrentDefault());
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       InstanceCountersMemoryDumpProvider::Instance(), "BlinkObjectCounters",
-      base::ThreadTaskRunnerHandle::Get());
+      base::SingleThreadTaskRunner::GetCurrentDefault());
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       ParkableStringManagerDumpProvider::Instance(), "ParkableStrings",
-      base::ThreadTaskRunnerHandle::Get());
+      base::SingleThreadTaskRunner::GetCurrentDefault());
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       &ParkableImageManager::Instance(), "ParkableImages",
-      base::ThreadTaskRunnerHandle::Get());
+      base::SingleThreadTaskRunner::GetCurrentDefault());
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       CanvasMemoryDumpProvider::Instance(), "Canvas",
-      base::ThreadTaskRunnerHandle::Get());
+      base::SingleThreadTaskRunner::GetCurrentDefault());
 
   SkGraphics::SetVariableColrV1EnabledFunc(
       RuntimeEnabledFeatures::VariableCOLRV1Enabled);
@@ -285,7 +284,8 @@
   DCHECK(WTF::IsMainThread());
   DCHECK(Thread::MainThread()->IsSimpleMainThread());
   static_cast<SimpleMainThread*>(Thread::MainThread())
-      ->SetMainThreadTaskRunnerForTesting(base::ThreadTaskRunnerHandle::Get());
+      ->SetMainThreadTaskRunnerForTesting(
+          base::SingleThreadTaskRunner::GetCurrentDefault());
 }
 
 void Platform::UnsetMainThreadTaskRunnerForTesting() {
diff --git a/third_party/blink/renderer/platform/exported/resource_load_info_notifier_wrapper.cc b/third_party/blink/renderer/platform/exported/resource_load_info_notifier_wrapper.cc
index f443c48..d507c68 100644
--- a/third_party/blink/renderer/platform/exported/resource_load_info_notifier_wrapper.cc
+++ b/third_party/blink/renderer/platform/exported/resource_load_info_notifier_wrapper.cc
@@ -6,7 +6,7 @@
 
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 #include "build/build_config.h"
 #include "net/base/ip_endpoint.h"
 #include "net/url_request/redirect_info.h"
@@ -26,7 +26,7 @@
         weak_wrapper_resource_load_info_notifier)
     : ResourceLoadInfoNotifierWrapper(
           std::move(weak_wrapper_resource_load_info_notifier),
-          base::ThreadTaskRunnerHandle::Get()) {}
+          base::SingleThreadTaskRunner::GetCurrentDefault()) {}
 
 ResourceLoadInfoNotifierWrapper::ResourceLoadInfoNotifierWrapper(
     base::WeakPtr<WeakWrapperResourceLoadInfoNotifier>
diff --git a/third_party/blink/renderer/platform/exported/video_capture/web_video_capture_impl_manager.cc b/third_party/blink/renderer/platform/exported/video_capture/web_video_capture_impl_manager.cc
index 866b777..2cec01d 100644
--- a/third_party/blink/renderer/platform/exported/video_capture/web_video_capture_impl_manager.cc
+++ b/third_party/blink/renderer/platform/exported/video_capture/web_video_capture_impl_manager.cc
@@ -26,7 +26,7 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/ranges/algorithm.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/token.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/capture/mojom/video_capture_types.mojom-blink.h"
@@ -74,7 +74,8 @@
 
 WebVideoCaptureImplManager::WebVideoCaptureImplManager()
     : next_client_id_(0),
-      render_main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      render_main_task_runner_(
+          base::SingleThreadTaskRunner::GetCurrentDefault()),
       is_suspending_all_(false) {}
 
 WebVideoCaptureImplManager::~WebVideoCaptureImplManager() {
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
index d02ecc84..3d9dcf48 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -7,8 +7,8 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "cc/input/main_thread_scrolling_reason.h"
 #include "cc/layers/layer.h"
@@ -79,7 +79,7 @@
  protected:
   PaintArtifactCompositorTest()
       : task_runner_(base::MakeRefCounted<base::TestSimpleTaskRunner>()),
-        task_runner_handle_(task_runner_) {}
+        task_runner_current_default_handle_(task_runner_) {}
 
   void SetUp() override {
     // Delay constructing the compositor until after the feature is set.
@@ -248,7 +248,8 @@
   MockScrollCallbacks scroll_callbacks_;
   std::unique_ptr<PaintArtifactCompositor> paint_artifact_compositor_;
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
+  base::SingleThreadTaskRunner::CurrentDefaultHandle
+      task_runner_current_default_handle_;
   cc::FakeLayerTreeHostClient layer_tree_host_client_;
   std::unique_ptr<LayerTreeHostEmbedder> layer_tree_;
 };
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_texture_alpha_clearer.cc b/third_party/blink/renderer/platform/graphics/gpu/webgpu_texture_alpha_clearer.cc
index 1aa6962..1e64d087 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_texture_alpha_clearer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_texture_alpha_clearer.cc
@@ -77,6 +77,8 @@
 void WebGPUTextureAlphaClearer::ClearAlpha(WGPUTexture texture) {
   const auto& procs = dawn_control_client_->GetProcs();
 
+  // Push an error scope to capture errors here.
+  procs.devicePushErrorScope(device_, WGPUErrorFilter_Validation);
   WGPUTextureView attachment_view = procs.textureCreateView(texture, nullptr);
 
   WGPUDawnEncoderInternalUsageDescriptor internal_usage_desc = {
@@ -115,6 +117,20 @@
   procs.commandEncoderRelease(command_encoder);
   procs.commandBufferRelease(command_buffer);
   procs.textureViewRelease(attachment_view);
+
+  // Pop the error scope and swallow errors. There are errors
+  // when the configured canvas produces an error GPUTexture. Errors from
+  // the alpha clear should be hidden from the application.
+  procs.devicePopErrorScope(
+      device_,
+      [](WGPUErrorType type, const char* message, void*) {
+        if (type == WGPUErrorType_NoError) {
+          return;
+        }
+        DCHECK(type == WGPUErrorType_Validation);
+        DLOG(ERROR) << "WebGPUTextureAlphaClearer errored:" << message;
+      },
+      nullptr);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
index b6da23bd..92d678f7 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
@@ -11,9 +11,9 @@
 #include "base/feature_list.h"
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/threading/platform_thread.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "cc/metrics/video_playback_roughness_reporter.h"
@@ -484,7 +484,7 @@
   if (!MaybeAcceptContextProvider(std::move(context_provider))) {
     constexpr base::TimeDelta kGetContextProviderRetryTimeout =
         base::Milliseconds(150);
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+    base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
         FROM_HERE,
         base::BindOnce(
             context_provider_callback_, context_provider_,
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender.cc
index 6100309..4f566a90 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender.cc
@@ -16,8 +16,8 @@
 #include "base/rand_util.h"
 #include "base/strings/string_util.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/task/thread_pool.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
@@ -480,7 +480,7 @@
     // constructed, due to a URLLoaderThrottle that changed the starting
     // URL. Handle this in a posted task, as we don't have the loader
     // pointer yet.
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
+    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
         FROM_HERE, base::BindOnce(&WebResourceRequestSender::OnReceivedRedirect,
                                   weak_factory_.GetWeakPtr(), redirect_info,
                                   std::move(response_head), task_runner));
diff --git a/third_party/blink/renderer/platform/loader/tracked_child_url_loader_factory_bundle.cc b/third_party/blink/renderer/platform/loader/tracked_child_url_loader_factory_bundle.cc
index 4c96d31..57f64f1 100644
--- a/third_party/blink/renderer/platform/loader/tracked_child_url_loader_factory_bundle.cc
+++ b/third_party/blink/renderer/platform/loader/tracked_child_url_loader_factory_bundle.cc
@@ -97,8 +97,8 @@
 void TrackedChildURLLoaderFactoryBundle::AddObserverOnMainThread() {
   DCHECK(main_thread_host_bundle_);
 
-  // Required by |SequencedTaskRunnerHandle::Get()| below.
-  if (!base::SequencedTaskRunnerHandle::IsSet())
+  // Required by |SequencedTaskRunner::GetCurrentDefault()| below.
+  if (!base::SequencedTaskRunner::HasCurrentDefault())
     return;
 
   main_thread_host_bundle_->second->PostTask(
@@ -108,7 +108,7 @@
           main_thread_host_bundle_->first, base::UnsafeDanglingUntriaged(this),
           std::make_unique<
               HostChildURLLoaderFactoryBundle::ObserverPtrAndTaskRunner>(
-              AsWeakPtr(), base::SequencedTaskRunnerHandle::Get())));
+              AsWeakPtr(), base::SequencedTaskRunner::GetCurrentDefault())));
 }
 
 void TrackedChildURLLoaderFactoryBundle::RemoveObserverOnMainThread() {
@@ -145,7 +145,7 @@
       base::WrapUnique(static_cast<ChildPendingURLLoaderFactoryBundle*>(
           ChildURLLoaderFactoryBundle::Clone().release()));
 
-  DCHECK(base::SequencedTaskRunnerHandle::IsSet());
+  DCHECK(base::SequencedTaskRunner::HasCurrentDefault());
   auto main_thread_host_bundle_clone = std::make_unique<
       TrackedChildURLLoaderFactoryBundle::HostPtrAndTaskRunner>(AsWeakPtr(),
                                                                 task_runner_);
diff --git a/third_party/blink/renderer/platform/mediastream/audio_service_audio_processor_proxy.cc b/third_party/blink/renderer/platform/mediastream/audio_service_audio_processor_proxy.cc
index 4551d71..7999c51e 100644
--- a/third_party/blink/renderer/platform/mediastream/audio_service_audio_processor_proxy.cc
+++ b/third_party/blink/renderer/platform/mediastream/audio_service_audio_processor_proxy.cc
@@ -5,14 +5,14 @@
 #include "third_party/blink/renderer/platform/mediastream/audio_service_audio_processor_proxy.h"
 
 #include "base/bind.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/timer/timer.h"
 #include "media/base/audio_processor_controls.h"
 
 namespace blink {
 
 AudioServiceAudioProcessorProxy::AudioServiceAudioProcessorProxy()
-    : main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+    : main_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
   weak_this_ = weak_ptr_factory_.GetWeakPtr();
 }
diff --git a/third_party/blink/renderer/platform/p2p/filtering_network_manager.cc b/third_party/blink/renderer/platform/p2p/filtering_network_manager.cc
index e1c6965b..00e93b3 100644
--- a/third_party/blink/renderer/platform/p2p/filtering_network_manager.cc
+++ b/third_party/blink/renderer/platform/p2p/filtering_network_manager.cc
@@ -8,7 +8,7 @@
 
 #include "base/location.h"
 #include "base/logging.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 #include "media/base/media_permission.h"
 #include "third_party/blink/renderer/platform/p2p/ipc_network_manager.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -219,7 +219,7 @@
   // Post a task to avoid reentrancy.
   //
   // TODO(crbug.com/787254): Use Frame-based TaskRunner here.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
+  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
       FROM_HERE,
       WTF::BindOnce(&FilteringNetworkManager::SendNetworksChangedSignal,
                     GetWeakPtr()));
diff --git a/third_party/blink/renderer/platform/p2p/filtering_network_manager_test.cc b/third_party/blink/renderer/platform/p2p/filtering_network_manager_test.cc
index df4ae16..4fea4f4b 100644
--- a/third_party/blink/renderer/platform/p2p/filtering_network_manager_test.cc
+++ b/third_party/blink/renderer/platform/p2p/filtering_network_manager_test.cc
@@ -14,8 +14,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/notreached.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "media/base/media_permission.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -161,7 +161,7 @@
   FilteringNetworkManagerTest()
       : media_permission_(new MockMediaPermission()),
         task_runner_(new base::TestSimpleTaskRunner()),
-        task_runner_handle_(task_runner_) {
+        task_runner_current_default_handle_(task_runner_) {
     networks_.emplace_back("test_eth0", "Test Network Adapter 1",
                            rtc::IPAddress(0x12345600U), 24,
                            rtc::ADAPTER_TYPE_ETHERNET),
@@ -262,7 +262,8 @@
 
   std::vector<const rtc::Network*> network_list_;
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
+  base::SingleThreadTaskRunner::CurrentDefaultHandle
+      task_runner_current_default_handle_;
 };
 
 // Test that when multiple routes is not requested, SignalNetworksChanged is
diff --git a/third_party/blink/renderer/platform/p2p/ipc_network_manager.cc b/third_party/blink/renderer/platform/p2p/ipc_network_manager.cc
index d33f0629..c0be671 100644
--- a/third_party/blink/renderer/platform/p2p/ipc_network_manager.cc
+++ b/third_party/blink/renderer/platform/p2p/ipc_network_manager.cc
@@ -14,7 +14,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/sys_byteorder.h"
 #include "base/task/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "components/webrtc/net_address_utils.h"
 #include "net/base/ip_address.h"
 #include "net/base/network_change_notifier.h"
@@ -77,7 +76,7 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (network_list_received_) {
     // Post a task to avoid reentrancy.
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
+    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
         FROM_HERE, WTF::BindOnce(&IpcNetworkManager::SendNetworksChangedSignal,
                                  weak_factory_.GetWeakPtr()));
   } else {
diff --git a/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc b/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc
index 11da010..d6b1336 100644
--- a/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc
+++ b/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc
@@ -5,7 +5,7 @@
 #include "third_party/blink/renderer/platform/p2p/socket_dispatcher.h"
 
 #include "base/memory/scoped_refptr.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/types/pass_key.h"
 #include "services/network/public/cpp/p2p_param_traits.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
@@ -38,7 +38,7 @@
 
 P2PSocketDispatcher::P2PSocketDispatcher(MojoBindingContext& context, PassKey)
     : Supplement(context),
-      main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      main_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
       network_list_observers_(
           new base::ObserverListThreadSafe<blink::NetworkListObserver>()),
       network_notification_client_receiver_(this, &context) {}
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.cc
index 3b1da4e..ca9dbc57 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.cc
@@ -12,7 +12,6 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/task/sequenced_task_runner.h"
-#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/trace_event/base_tracing.h"
 #include "build/build_config.h"
 #include "media/base/decoder_factory.h"
@@ -338,10 +337,10 @@
   }
   // ScopedVideoDecoder uses the task runner to make sure the decoder is
   // destructed on the correct thread.
-  return decoder
-             ? std::make_unique<ScopedVideoDecoder>(
-                   base::SequencedTaskRunnerHandle::Get(), std::move(decoder))
-             : nullptr;
+  return decoder ? std::make_unique<ScopedVideoDecoder>(
+                       base::SequencedTaskRunner::GetCurrentDefault(),
+                       std::move(decoder))
+                 : nullptr;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/peerconnection/vsync_tick_provider_test.cc b/third_party/blink/renderer/platform/peerconnection/vsync_tick_provider_test.cc
index db557dc11..c957aa7 100644
--- a/third_party/blink/renderer/platform/peerconnection/vsync_tick_provider_test.cc
+++ b/third_party/blink/renderer/platform/peerconnection/vsync_tick_provider_test.cc
@@ -6,9 +6,9 @@
 
 #include <memory>
 
+#include "base/task/sequenced_task_runner.h"
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
-#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
 #include "metronome_source.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -97,7 +97,7 @@
   FakeVSyncProvider fake_begin_frame_provider_;
   std::unique_ptr<VSyncTickProvider> begin_frame_tick_provider_ =
       VSyncTickProvider::Create(fake_begin_frame_provider_,
-                                base::SequencedTaskRunnerHandle::Get(),
+                                base::SequencedTaskRunner::GetCurrentDefault(),
                                 std::move(fake_default_tick_provider_holder_));
 };
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index a437ceb..4e9532ba 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2503,6 +2503,11 @@
       status: {"Android": "", "default": "stable"},
     },
     {
+      name: "ServiceWorkerBypassFetchHandler",
+      origin_trial_feature_name: "ServiceWorkerBypassFetchHandlerForMainResource",
+      public: true,
+    },
+    {
       name: "ServiceWorkerClientLifecycleState",
       status: "experimental",
     },
diff --git a/third_party/blink/renderer/platform/scheduler/TaskSchedulingInBlink.md b/third_party/blink/renderer/platform/scheduler/TaskSchedulingInBlink.md
index 85584cb..1a2e08d 100644
--- a/third_party/blink/renderer/platform/scheduler/TaskSchedulingInBlink.md
+++ b/third_party/blink/renderer/platform/scheduler/TaskSchedulingInBlink.md
@@ -66,15 +66,15 @@
 New task runners might be added in the future; [contact the team](#contact)
 if you think you need a new one.
 
-## `ThreadTaskRunnerHandle::Get`
+## `SingleThreadTaskRunner::GetCurrentDefault`
 
-`base::ThreadTaskRunnerHandle::Get` is a way to get a task runner and
-it returns a default task runner for a thread. Because tasks posted to it
+`base::SingleThreadTaskRunner::GetCurrentDefault` is a way to get a task runner
+and it returns a default task runner for a thread. Because tasks posted to it
 lack any attribution, the scheduler can’t properly schedule and prioritise them.
 
-`base::ThreadTaskRunnerHandle::Get` usages are **banned** in `blink/` and
-`content/renderer/` directories and strongly discouraged elsewhere in the
-renderer process.
+`base::SingleThreadTaskRunner::GetCurrentDefault` usages are **banned** in
+`blink/` and `content/renderer/` directories and strongly discouraged elsewhere
+in the renderer process.
 
 Please help us to convert them to the appropriate task runner
 (usually per-frame one). See [Task Type Usage Guideline](
diff --git a/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc b/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc
index 367f69a..a9f31ee 100644
--- a/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h"
 
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/public/agent_group_scheduler.h"
@@ -37,7 +37,7 @@
   void Shutdown() override {}
   // Returns the input task runner.
   scoped_refptr<base::SingleThreadTaskRunner> InputTaskRunner() override {
-    return base::ThreadTaskRunnerHandle::Get();
+    return base::SingleThreadTaskRunner::GetCurrentDefault();
   }
   void WillBeginFrame(const viz::BeginFrameArgs& args) override {}
   void BeginFrameNotExpectedSoon() override {}
@@ -70,7 +70,7 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override {
     DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
+    return base::SingleThreadTaskRunner::GetCurrentDefault();
   }
 
   PageScheduler* GetPageScheduler() const override {
@@ -113,12 +113,12 @@
   std::unique_ptr<WebResourceLoadingTaskRunnerHandle>
   CreateResourceLoadingTaskRunnerHandle() override {
     return WebResourceLoadingTaskRunnerHandle::CreateUnprioritized(
-        base::ThreadTaskRunnerHandle::Get());
+        base::SingleThreadTaskRunner::GetCurrentDefault());
   }
   std::unique_ptr<WebResourceLoadingTaskRunnerHandle>
   CreateResourceLoadingMaybeUnfreezableTaskRunnerHandle() override {
     return WebResourceLoadingTaskRunnerHandle::CreateUnprioritized(
-        base::ThreadTaskRunnerHandle::Get());
+        base::SingleThreadTaskRunner::GetCurrentDefault());
   }
   std::unique_ptr<WebSchedulingTaskQueue> CreateWebSchedulingTaskQueue(
       WebSchedulingPriority) override {
@@ -149,7 +149,7 @@
   }
   void ReportActiveSchedulerTrackedFeatures() override {}
   scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override {
-    return base::ThreadTaskRunnerHandle::Get();
+    return base::SingleThreadTaskRunner::GetCurrentDefault();
   }
 
  private:
@@ -210,7 +210,7 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
       MainThreadTaskRunnerRestricted) const override {
-    return base::ThreadTaskRunnerHandle::Get();
+    return base::SingleThreadTaskRunner::GetCurrentDefault();
   }
 
   ThreadScheduler* Scheduler() override { return scheduler_; }
@@ -252,22 +252,22 @@
   scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner()
       override {
     DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
+    return base::SingleThreadTaskRunner::GetCurrentDefault();
   }
 
   scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override {
     DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
+    return base::SingleThreadTaskRunner::GetCurrentDefault();
   }
 
   scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override {
     DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
+    return base::SingleThreadTaskRunner::GetCurrentDefault();
   }
 
   scoped_refptr<base::SingleThreadTaskRunner> CleanupTaskRunner() override {
     DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
+    return base::SingleThreadTaskRunner::GetCurrentDefault();
   }
 
   std::unique_ptr<MainThread> CreateMainThread() override {
@@ -286,7 +286,7 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override {
     DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
+    return base::SingleThreadTaskRunner::GetCurrentDefault();
   }
 
   AgentGroupScheduler* GetCurrentAgentGroupScheduler() override {
@@ -322,10 +322,10 @@
     return CreateDummyPageScheduler();
   }
   scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override {
-    return base::ThreadTaskRunnerHandle::Get();
+    return base::SingleThreadTaskRunner::GetCurrentDefault();
   }
   scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override {
-    return base::ThreadTaskRunnerHandle::Get();
+    return base::SingleThreadTaskRunner::GetCurrentDefault();
   }
   WebThreadScheduler& GetMainThreadScheduler() override {
     return *main_thread_scheduler_;
diff --git a/third_party/blink/renderer/platform/scheduler/common/simple_main_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/common/simple_main_thread_scheduler.cc
index f807839..0d368fd 100644
--- a/third_party/blink/renderer/platform/scheduler/common/simple_main_thread_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/simple_main_thread_scheduler.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/platform/scheduler/common/simple_main_thread_scheduler.h"
 
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -40,17 +40,17 @@
 
 scoped_refptr<base::SingleThreadTaskRunner>
 SimpleMainThreadScheduler::V8TaskRunner() {
-  return base::ThreadTaskRunnerHandle::Get();
+  return base::SingleThreadTaskRunner::GetCurrentDefault();
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
 SimpleMainThreadScheduler::CleanupTaskRunner() {
-  return base::ThreadTaskRunnerHandle::Get();
+  return base::SingleThreadTaskRunner::GetCurrentDefault();
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
 SimpleMainThreadScheduler::NonWakingTaskRunner() {
-  return base::ThreadTaskRunnerHandle::Get();
+  return base::SingleThreadTaskRunner::GetCurrentDefault();
 }
 
 AgentGroupScheduler* SimpleMainThreadScheduler::CreateAgentGroupScheduler() {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index 448d36df..f2c5064 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -316,7 +316,7 @@
 
   // Register a tracing state observer unless we're running in a test without a
   // task runner. Note that it's safe to remove a non-existent observer.
-  if (base::ThreadTaskRunnerHandle::IsSet()) {
+  if (base::SingleThreadTaskRunner::HasCurrentDefault()) {
     base::trace_event::TraceLog::GetInstance()->AddAsyncEnabledStateObserver(
         weak_factory_.GetWeakPtr());
   }
@@ -2200,9 +2200,9 @@
   current_agent_group_scheduler_ = next_agent_group_scheduler;
 
   scoped_refptr<base::SingleThreadTaskRunner> previous_task_runner =
-      base::ThreadTaskRunnerHandle::Get();
-  std::unique_ptr<base::ThreadTaskRunnerHandleOverride>
-      thread_task_runner_handle_override;
+      base::SingleThreadTaskRunner::GetCurrentDefault();
+  std::unique_ptr<base::SingleThreadTaskRunner::CurrentHandleOverride>
+      single_thread_task_runner_current_handle_override;
   if (scheduling_settings().mbi_override_task_runner_handle &&
       next_task_runner != previous_task_runner) {
     // per-thread and per-AgentSchedulingGroup task runner allows nested
@@ -2214,16 +2214,16 @@
     // STTR/STR::GetCurrentDefault() properly. So there is no concern about
     // returning an unexpected task runner from STTR/STR::GetCurrentDefault() in
     // this specific case.
-    thread_task_runner_handle_override =
-        std::unique_ptr<base::ThreadTaskRunnerHandleOverride>(
-            new base::ThreadTaskRunnerHandleOverride(
+    single_thread_task_runner_current_handle_override =
+        std::unique_ptr<base::SingleThreadTaskRunner::CurrentHandleOverride>(
+            new base::SingleThreadTaskRunner::CurrentHandleOverride(
                 next_task_runner,
                 /*allow_nested_runloop=*/true));
   }
 
   main_thread_only().agent_group_scheduler_scope_stack.emplace_back(
       AgentGroupSchedulerScope{
-          std::move(thread_task_runner_handle_override),
+          std::move(single_thread_task_runner_current_handle_override),
           previous_agent_group_scheduler, next_agent_group_scheduler,
           std::move(previous_task_runner), std::move(next_task_runner),
           trace_event_scope_name, trace_event_scope_id});
@@ -2239,7 +2239,8 @@
     DCHECK_EQ(base::SequencedTaskRunner::GetCurrentDefault(),
               agent_group_scheduler_scope.current_task_runner);
   }
-  agent_group_scheduler_scope.thread_task_runner_handle_override = nullptr;
+  agent_group_scheduler_scope
+      .single_thread_task_runner_current_handle_override = nullptr;
   DCHECK_EQ(base::SingleThreadTaskRunner::GetCurrentDefault(),
             agent_group_scheduler_scope.previous_task_runner);
   DCHECK_EQ(base::SequencedTaskRunner::GetCurrentDefault(),
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
index eb0c52a1..a65e8d1 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -449,8 +449,8 @@
   void AddAgentGroupScheduler(AgentGroupSchedulerImpl*);
 
   struct AgentGroupSchedulerScope {
-    std::unique_ptr<base::ThreadTaskRunnerHandleOverride>
-        thread_task_runner_handle_override;
+    std::unique_ptr<base::SingleThreadTaskRunner::CurrentHandleOverride>
+        single_thread_task_runner_current_handle_override;
     WeakPersistent<AgentGroupScheduler> previous_agent_group_scheduler;
     WeakPersistent<AgentGroupScheduler> current_agent_group_scheduler;
     scoped_refptr<base::SingleThreadTaskRunner> previous_task_runner;
diff --git a/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_manager.cc b/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_manager.cc
index 9e15456..c149b87 100644
--- a/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_manager.cc
+++ b/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_manager.cc
@@ -3,7 +3,7 @@
 #include <algorithm>
 
 #include "base/task/sequence_manager/test/test_task_queue.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 #include "third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_pool_manager.h"
 
 namespace base {
@@ -53,9 +53,9 @@
 
   test_task_runner_->AdvanceMockTickClock(initial_time - base::TimeTicks());
 
-  manager_ =
-      SequenceManagerForTest::Create(nullptr, ThreadTaskRunnerHandle::Get(),
-                                     test_task_runner_->GetMockTickClock());
+  manager_ = SequenceManagerForTest::Create(
+      nullptr, SingleThreadTaskRunner::GetCurrentDefault(),
+      test_task_runner_->GetMockTickClock());
 
   TaskQueue::Spec spec = TaskQueue::Spec(QueueName::DEFAULT_TQ);
   task_queues_.emplace_back(MakeRefCounted<TaskQueueWithVoters>(
diff --git a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_impl.cc b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_impl.cc
index 2e724525..19ae6ab 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_impl.cc
@@ -17,7 +17,6 @@
 #include "base/task/sequence_manager/task_queue.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread_restrictions.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "base/time/default_tick_clock.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h"
@@ -133,7 +132,7 @@
   ThreadState* thread_state = ThreadState::AttachCurrentThread();
   gc_task_runner_ = std::make_unique<GCTaskRunner>(thread);
   blink_gc_memory_dump_provider_ = std::make_unique<BlinkGCMemoryDumpProvider>(
-      thread_state, base::ThreadTaskRunnerHandle::Get(),
+      thread_state, base::SingleThreadTaskRunner::GetCurrentDefault(),
       BlinkGCMemoryDumpProvider::HeapType::kBlinkWorkerThread);
 }
 
diff --git a/third_party/blink/renderer/platform/testing/compositor_test.cc b/third_party/blink/renderer/platform/testing/compositor_test.cc
index 799cd7c..b046138 100644
--- a/third_party/blink/renderer/platform/testing/compositor_test.cc
+++ b/third_party/blink/renderer/platform/testing/compositor_test.cc
@@ -7,7 +7,8 @@
 namespace blink {
 
 CompositorTest::CompositorTest()
-    : runner_(new base::TestMockTimeTaskRunner), runner_handle_(runner_) {}
+    : runner_(new base::TestMockTimeTaskRunner),
+      runner_current_default_handle_(runner_) {}
 
 CompositorTest::~CompositorTest() = default;
 
diff --git a/third_party/blink/renderer/platform/testing/compositor_test.h b/third_party/blink/renderer/platform/testing/compositor_test.h
index c9ab89d..a1aa997 100644
--- a/third_party/blink/renderer/platform/testing/compositor_test.h
+++ b/third_party/blink/renderer/platform/testing/compositor_test.h
@@ -6,8 +6,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_COMPOSITOR_TEST_H_
 
 #include "base/memory/scoped_refptr.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/test/test_mock_time_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace blink {
@@ -23,7 +23,8 @@
   // cc::LayerTreeHost requires a task runner, so we use a mock task runner
   // and bind it as the current ThreadTaskRunnerHandle for this thread.
   scoped_refptr<base::TestMockTimeTaskRunner> runner_;
-  base::ThreadTaskRunnerHandle runner_handle_;
+  base::SingleThreadTaskRunner::CurrentDefaultHandle
+      runner_current_default_handle_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/input/frame_widget_input_handler_impl.cc b/third_party/blink/renderer/platform/widget/input/frame_widget_input_handler_impl.cc
index c2cbd3e..c09540c 100644
--- a/third_party/blink/renderer/platform/widget/input/frame_widget_input_handler_impl.cc
+++ b/third_party/blink/renderer/platform/widget/input/frame_widget_input_handler_impl.cc
@@ -299,7 +299,7 @@
               FROM_HERE,
               base::BindOnce(std::move(callback), std::move(result)));
         },
-        base::ThreadTaskRunnerHandle::Get(), std::move(callback));
+        base::SingleThreadTaskRunner::GetCurrentDefault(), std::move(callback));
   }
 
   RunOnMainThread(base::BindOnce(
@@ -378,7 +378,7 @@
           callback_task_runner->PostTask(FROM_HERE,
                                          base::BindOnce(std::move(callback)));
         },
-        base::ThreadTaskRunnerHandle::Get(), std::move(callback));
+        base::SingleThreadTaskRunner::GetCurrentDefault(), std::move(callback));
   }
 
   RunOnMainThread(base::BindOnce(
diff --git a/third_party/blink/renderer/platform/widget/input/widget_input_handler_impl.cc b/third_party/blink/renderer/platform/widget/input/widget_input_handler_impl.cc
index fad78a63..75f05b78 100644
--- a/third_party/blink/renderer/platform/widget/input/widget_input_handler_impl.cc
+++ b/third_party/blink/renderer/platform/widget/input/widget_input_handler_impl.cc
@@ -8,7 +8,7 @@
 
 #include "base/bind.h"
 #include "base/check.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 #include "build/build_config.h"
 #include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
 #include "third_party/blink/public/common/input/web_coalesced_input_event.h"
@@ -96,10 +96,10 @@
     int32_t start,
     int32_t end,
     WidgetInputHandlerImpl::ImeSetCompositionCallback callback) {
-  RunOnMainThread(base::BindOnce(&ImeSetCompositionOnMainThread, widget_,
-                                 base::ThreadTaskRunnerHandle::Get(), text,
-                                 ime_text_spans, range, start, end,
-                                 std::move(callback)));
+  RunOnMainThread(
+      base::BindOnce(&ImeSetCompositionOnMainThread, widget_,
+                     base::SingleThreadTaskRunner::GetCurrentDefault(), text,
+                     ime_text_spans, range, start, end, std::move(callback)));
 }
 
 static void ImeCommitTextOnMainThread(
@@ -120,10 +120,10 @@
     const gfx::Range& range,
     int32_t relative_cursor_position,
     ImeCommitTextCallback callback) {
-  RunOnMainThread(
-      base::BindOnce(&ImeCommitTextOnMainThread, widget_,
-                     base::ThreadTaskRunnerHandle::Get(), text, ime_text_spans,
-                     range, relative_cursor_position, std::move(callback)));
+  RunOnMainThread(base::BindOnce(
+      &ImeCommitTextOnMainThread, widget_,
+      base::SingleThreadTaskRunner::GetCurrentDefault(), text, ime_text_spans,
+      range, relative_cursor_position, std::move(callback)));
 }
 
 void WidgetInputHandlerImpl::ImeFinishComposingText(bool keep_selection) {
diff --git a/third_party/blink/renderer/platform/widget/widget_base.cc b/third_party/blink/renderer/platform/widget/widget_base.cc
index 901e7b1..88e685e 100644
--- a/third_party/blink/renderer/platform/widget/widget_base.cc
+++ b/third_party/blink/renderer/platform/widget/widget_base.cc
@@ -8,7 +8,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/ranges/algorithm.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/timer/elapsed_timer.h"
 #include "build/build_config.h"
 #include "cc/animation/animation_host.h"
@@ -137,7 +137,7 @@
   base::SingleThreadTaskRunner* compositor_impl_side_task_runner =
       Platform::Current()->CompositorThreadTaskRunner()
           ? Platform::Current()->CompositorThreadTaskRunner().get()
-          : base::ThreadTaskRunnerHandle::Get().get();
+          : base::SingleThreadTaskRunner::GetCurrentDefault().get();
   return std::make_unique<viz::BackToBackBeginFrameSource>(
       std::make_unique<viz::DelayBasedTimeSource>(
           compositor_impl_side_task_runner));
@@ -295,7 +295,7 @@
     // compositor thread.
 
     scoped_refptr<base::SingleThreadTaskRunner> cleanup_runner =
-        base::ThreadTaskRunnerHandle::Get();
+        base::SingleThreadTaskRunner::GetCurrentDefault();
     cleanup_runner->PostNonNestableTask(
         FROM_HERE, base::BindOnce(
                        [](std::unique_ptr<LayerTreeView> view,
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index ed0f457d..8d05024 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -1468,6 +1468,11 @@
         # WTF::RefCounted should be used instead. base::RefCountedThreadSafe is
         # still needed for cross_thread_copier.h though.
         'allowed': ['base::RefCountedThreadSafe', '(?!base::RefCounted).+'],
+        # This is required to supplant less fine-grained inclass_disallows. We
+        # want to allow everything that the normal ones are allowing here, for
+        # the same reasons.
+        'inclass_allowed':
+        ['base::RefCountedThreadSafe::.+', '(?!base::RefCounted).+'],
         'disallowed': [
             # TODO(https://crbug.com/1267866): this warning is shown twice for
             # renderer/platform/ violations.
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 aec68bb..d4dafce6 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
@@ -89104,6 +89104,58 @@
        {}
       ]
      ],
+     "out-of-flow-in-multicolumn-100.html": [
+      "1bf71e832de9a2be139af258f6a4c25215d0a0b1",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "out-of-flow-in-multicolumn-101.html": [
+      "ed7e1e234d8aa0a12a61b68e5de93d10ce7a4ce5",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "out-of-flow-in-multicolumn-102.html": [
+      "45cbc0b4533c7c87fed3be713474202fe29e1f47",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "out-of-flow-in-multicolumn-103.html": [
+      "b54cbcd85a88a6e845adec74daefd43349ee4f13",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "overflow-clip-000.html": [
       "72b10f5cdd3092a042f1f90bff04e9428d61608e",
       [
@@ -133056,7 +133108,7 @@
       ]
      ],
      "object-fit-contain-png-001c.html": [
-      "4fb7f113897e22dbf6eec0b7c9c9a83b136ebd8d",
+      "0f8195157e310cd69c05dcfd8a786972b981d2f1",
       [
        null,
        [
@@ -133065,7 +133117,23 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            20
+           ],
+           [
+            0,
+            2000
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "object-fit-contain-png-001e.html": [
@@ -133121,7 +133189,7 @@
       ]
      ],
      "object-fit-contain-png-002c.html": [
-      "738c015a52e471528dd7855e7e75ff064bc6a881",
+      "14834316a32efbf8f80c4612a93951e0fa792c51",
       [
        null,
        [
@@ -133130,7 +133198,23 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            20
+           ],
+           [
+            0,
+            2000
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "object-fit-contain-png-002e.html": [
@@ -133966,7 +134050,7 @@
       ]
      ],
      "object-fit-fill-png-001c.html": [
-      "36031175e63f751e7d46af6cd43d92485e8af544",
+      "0e2a3883fe2f05a2c89dbc0d87617aa965097330",
       [
        null,
        [
@@ -133975,7 +134059,23 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            20
+           ],
+           [
+            0,
+            3200
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "object-fit-fill-png-001e.html": [
@@ -134031,7 +134131,7 @@
       ]
      ],
      "object-fit-fill-png-002c.html": [
-      "a332c37f4f043650e1052d18a67e86bd6fc1cdfa",
+      "43bcced9f26c672c641bd5e1c6e48855ad1b23dc",
       [
        null,
        [
@@ -134040,7 +134140,23 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            20
+           ],
+           [
+            0,
+            3200
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "object-fit-fill-png-002e.html": [
@@ -259406,16 +259522,6 @@
    }
   },
   "support": {
-   ".cache": {
-    "gitignore2.json": [
-     "618b7edf44dadc669172c5cab7ebe16fa4648cd8",
-     []
-    ],
-    "mtime.json": [
-     "ba3aa9b32bf802709f10d947b82e3c270d7d1c40",
-     []
-    ]
-   },
    ".gitignore": [
     "d93e645d547894b50149d3726de2654957b6e06f",
     []
@@ -303146,10 +303252,6 @@
       "column-rule-shorthand.html.ini": [
        "12cb8d80daab01297951f78ecffe5cf2ff477ce6",
        []
-      ],
-      "columns-valid-expected.txt": [
-       "6afe2f9fe710327ada7c080d692841146c7ccf16",
-       []
       ]
      },
      "reference": {
@@ -369076,8 +369178,12 @@
       "02aafbb5c78368c69dbf1cdae81b432d988e17d1",
       []
      ],
-     "parent-frame-with-child.sub.html": [
-      "32dd4cb48bc2cba536b991dcfdff9e9adbd649c9",
+     "parent-frame-with-cross-origin-child.sub.html": [
+      "4be0df872cbfd22dfcbd6b4e8e9005a7a79e88ae",
+      []
+     ],
+     "parent-frame-with-same-origin-child.html": [
+      "c9248a4e8bb9f4107be82947436e469c3c74aaa6",
       []
      ],
      "postmessage-entry.html": [
@@ -378179,6 +378285,10 @@
       []
      ],
      "no-vary-search": {
+      "README.txt": [
+       "60ac226f8cd4c8f82f0df9cf641cbe26ac39fed5",
+       []
+      ],
       "prefetch-single.https.html.ini": [
        "68ee4fce4f7110c2f2a387f2ba6dd920f5eff793",
        []
@@ -428594,6 +428704,20 @@
        {}
       ]
      ],
+     "missing-values-first-keyframe.html": [
+      "311e26259c7f0cb5a799018218b249acaf84acf5",
+      [
+       null,
+       {}
+      ]
+     ],
+     "missing-values-last-keyframe.html": [
+      "04c4eba45e4d5d112839244467eddacc5bac4412",
+      [
+       null,
+       {}
+      ]
+     ],
      "parsing": {
       "animation-composition-computed.tentative.html": [
        "535a40c9ca9d5c875b2b9eb7f7a16b22d55071fe",
@@ -435360,7 +435484,7 @@
        ]
       ],
       "font-variant-alternates-invalid.html": [
-       "baf35acb8a11d8d5319712b68191529f175d895b",
+       "8cd17d0849f4241a012de4523429483305d9e7ea",
        [
         null,
         {}
@@ -567607,8 +567731,22 @@
        {}
       ]
      ],
+     "include-frames-from-child-cross-origin-grandchild.sub.html": [
+      "dcd716de90070e9e871a661789e2942f0a3f418e",
+      [
+       null,
+       {}
+      ]
+     ],
+     "include-frames-from-child-same-origin-grandchild.sub.html": [
+      "991715f643e2ac87cc019c22a5dfb98fd4681438",
+      [
+       null,
+       {}
+      ]
+     ],
      "include-frames-one-local-child-one-local-grandchild.html": [
-      "ee2dadde8f997b4e1902362ca67de8e51a88025a",
+      "06a13f36e0b3ff5a9f8a82aab3cda2d037f3183b",
       [
        null,
        {}
@@ -567629,7 +567767,7 @@
       ]
      ],
      "include-frames-one-remote-child-one-local-grandchild.sub.html": [
-      "a9efe4146161fbb0296a2612c8e2a489a620c000",
+      "3248d601292f0bdf9ea7dee413404db144ccd111",
       [
        null,
        {}
@@ -569346,7 +569484,7 @@
      ]
     ],
     "pointerevent_pointercapture_in_frame.html": [
-     "c4332ac459f571762a726210b0c791dfebbc1654",
+     "9edacd7d234633ebec2a12c0e42a4eb5b469c7aa",
      [
       "pointerevents/pointerevent_pointercapture_in_frame.html?mouse",
       {
@@ -623161,7 +623299,7 @@
       ]
      ],
      "setParameters-active.https.html": [
-      "86afa4082e27e5b6afa43f3e2a1f71766511dcc7",
+      "36f096add385d86bab2aaab16c58f98a2c48c2c4",
       [
        null,
        {
diff --git a/third_party/blink/web_tests/external/wpt/generic-sensor/resources/iframe_sensor_handler.html b/third_party/blink/web_tests/external/wpt/generic-sensor/resources/iframe_sensor_handler.html
index 23e8e1b..4528c57 100644
--- a/third_party/blink/web_tests/external/wpt/generic-sensor/resources/iframe_sensor_handler.html
+++ b/third_party/blink/web_tests/external/wpt/generic-sensor/resources/iframe_sensor_handler.html
@@ -21,10 +21,13 @@
         return Promise.reject('"create_sensor" must be called first');
       }
 
-      return new Promise(resolve => {
+      return new Promise((resolve, reject) => {
         sensor.addEventListener('reading', () => {
           resolve('success');
         }, { once: true });
+        sensor.addEventListener('error', e => {
+          reject(`${e.error.name}: ${e.error.message}`);
+        }, { once: true });
         sensor.start();
       });
     } else if (e.data.command === 'is_sensor_suspended') {
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/notification-on-activation.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/notification-on-activation.html
index 03e36f0..0a85746 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/notification-on-activation.html
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/notification-on-activation.html
@@ -26,6 +26,7 @@
   document.addEventListener('prerenderingchange', () => {
     // Accessing the Notification API is allowed after the prerendering state
     // changed.
+    const permission = Notification.permission;
     const notification = new Notification('New Notification');
 
     notification.onerror = function(_) {
diff --git a/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/README.txt b/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/README.txt
index a86468d..82d3cc8 100644
--- a/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/README.txt
+++ b/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/README.txt
@@ -1,4 +1,5 @@
-These tests confirm that Trust Tokens protocol operations executed correctly end
-to end (in contrast to just checking, as the parent directory's
-trust-token-parameter-validation does, that a method's interface is present and
-correctly rejects invalid arguments).
+These tests confirm that Private State Tokens protocol operations
+executed correctly end to end (in contrast to just checking, as the
+parent directory's trust-token-parameter-validation does, that a
+method's interface is present and correctly rejects invalid
+arguments).
diff --git a/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/has-trust-token-with-no-top-frame.tentative.https.html b/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/has-trust-token-with-no-top-frame.tentative.https.html
index 38ebed9..587c641 100644
--- a/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/has-trust-token-with-no-top-frame.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/has-trust-token-with-no-top-frame.tentative.https.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<title>Tests the Private State Token API's hasPrivateStateToken behavior in documents with no top frame</title>
+<title>Tests the Private Token API's hasPrivateToken behavior in documents with no top frame</title>
 <link rel="help" href="https://github.com/WICG/trust-token-api" />
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
@@ -15,8 +15,8 @@
     frame.remove();
 
     test(() => {
-      assert_equals(cachedDocument.hasPrivateStateToken("https://issuer.example"), undefined,
+      assert_equals(cachedDocument.hasPrivateToken("https://issuer.example", 'private-state-token'), undefined,
         "Can't construct a Promise in a destroyed execution context.");
-    }, 'hasPrivateStateToken in a destroyed document.');
+    }, 'hasPrivateToken in a destroyed document.');
   </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/has-trust-token-with-no-top-frame.tentative.https.html.ini b/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/has-trust-token-with-no-top-frame.tentative.https.html.ini
index 0f5ab0b..3adfdab 100644
--- a/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/has-trust-token-with-no-top-frame.tentative.https.html.ini
+++ b/third_party/blink/web_tests/external/wpt/trust-tokens/end-to-end/has-trust-token-with-no-top-frame.tentative.https.html.ini
@@ -1,3 +1,3 @@
 [has-trust-token-with-no-top-frame.tentative.https.html]
-  [hasPrivateStateToken in a destroyed document.]
+  [hasPrivateToken in a destroyed document.]
     expected: FAIL
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/simulcast/setParameters-active.https.html b/third_party/blink/web_tests/external/wpt/webrtc/simulcast/setParameters-active.https.html
index 86afa40..36f096a 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/simulcast/setParameters-active.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/simulcast/setParameters-active.https.html
@@ -33,6 +33,56 @@
 
   await negotiateSimulcastAndWaitForVideo(t, rids, pc1, pc2);
 
+  // Deactivate first sender.
+  const parameters = pc1.getSenders()[0].getParameters();
+  parameters.encodings[0].active = false;
+  await pc1.getSenders()[0].setParameters(parameters);
+
+  // Assert (almost) no new frames are received on the first encoding.
+  // Without any action we would expect to have received around 30fps.
+  await new Promise(resolve => t.step_timeout(resolve, 100)); // Wait a bit.
+  const initialStats = await queryReceiverStats(pc2);
+  await new Promise(resolve => t.step_timeout(resolve, 1000)); // Wait more.
+  const subsequentStats = await queryReceiverStats(pc2);
+
+  assert_equals(subsequentStats[0], initialStats[0]);
+  assert_greater_than(subsequentStats[1], initialStats[1]);
+}, 'Simulcast setParameters active=false on first encoding stops sending frames for that encoding');
+
+promise_test(async t => {
+  const rids = [0, 1];
+  const pc1 = new RTCPeerConnection();
+  t.add_cleanup(() => pc1.close());
+  const pc2 = new RTCPeerConnection();
+  t.add_cleanup(() => pc2.close());
+
+  await negotiateSimulcastAndWaitForVideo(t, rids, pc1, pc2);
+
+  // Deactivate second sender.
+  const parameters = pc1.getSenders()[0].getParameters();
+  parameters.encodings[1].active = false;
+  await pc1.getSenders()[0].setParameters(parameters);
+
+  // Assert (almost) no new frames are received on the second encoding.
+  // Without any action we would expect to have received around 30fps.
+  await new Promise(resolve => t.step_timeout(resolve, 100)); // Wait a bit.
+  const initialStats = await queryReceiverStats(pc2);
+  await new Promise(resolve => t.step_timeout(resolve, 1000)); // Wait more.
+  const subsequentStats = await queryReceiverStats(pc2);
+
+  assert_equals(subsequentStats[1], initialStats[1]);
+  assert_greater_than(subsequentStats[0], initialStats[0]);
+}, 'Simulcast setParameters active=false on second encoding stops sending frames for that encoding');
+
+promise_test(async t => {
+  const rids = [0, 1];
+  const pc1 = new RTCPeerConnection();
+  t.add_cleanup(() => pc1.close());
+  const pc2 = new RTCPeerConnection();
+  t.add_cleanup(() => pc2.close());
+
+  await negotiateSimulcastAndWaitForVideo(t, rids, pc1, pc2);
+
   // Deactivate all senders.
   const parameters = pc1.getSenders()[0].getParameters();
   parameters.encodings.forEach(e => {
diff --git a/third_party/blink/web_tests/http/tests/loading/trust-tokens/has-trust-token.tentative.https.html b/third_party/blink/web_tests/http/tests/loading/trust-tokens/has-trust-token.tentative.https.html
index 4ce2532..b0274d4 100644
--- a/third_party/blink/web_tests/http/tests/loading/trust-tokens/has-trust-token.tentative.https.html
+++ b/third_party/blink/web_tests/http/tests/loading/trust-tokens/has-trust-token.tentative.https.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<title>Tests the Private State Token API's hasPrivateStateToken function (tentative: the API is a prototype).</title>
+<title>Tests the Private Token API's hasPrivateToken function (tentative: the API is a prototype).</title>
 <link rel="help" href="https://github.com/WICG/trust-token-api#trust-token-redemption" />
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
@@ -9,19 +9,25 @@
   'use strict';
 
   promise_test((t) => {
-      return promise_rejects_js(t, TypeError, document.hasPrivateStateToken(
-        "http://not-a-secure-url.example"));
+      return promise_rejects_js(t, TypeError, document.hasPrivateToken(
+        "http://not-a-secure-url.example", 'private-state-token'));
     },
-    'hasPrivateStateToken requires a secure URL as its issuer argument.');
+    'hasPrivateToken requires a secure URL as its issuer argument.');
 
   promise_test((t) => {
-      return promise_rejects_js(t, TypeError, document.hasPrivateStateToken(
-        "file:///"));
+      return promise_rejects_js(t, TypeError, document.hasPrivateToken(
+        "file:///", 'private-state-token'));
     },
-    'hasPrivateStateToken requires a HTTP(S) URL as its issuer argument.');
+    'hasPrivateToken requires a HTTP(S) URL as its issuer argument.');
 
-  // These hasPrivateStateToken calls all affect global state: each call in the form
-  // of hasPrivateStateToken(issuer) will result in |issuer| becoming associated in
+  promise_test((t) => {
+      return promise_rejects_js(t, TypeError, document.hasPrivateToken(
+        "https://issuer.example/", 'some-other-token'));
+    },
+    'hasPrivateToken type must be private-state-token.');
+
+  // These hasPrivateToken calls all affect global state: each call in the form
+  // of hasPrivateToken(issuer) will result in |issuer| becoming associated in
   // persistent storage with the calling top frame's origin.
   //
   // TODO(davidvc, crbug.com/1063140): Once it's possible to write WPTs that
@@ -33,25 +39,25 @@
           await new Promise(res => window.testRunner.clearTrustTokenState(res));
       });
 
-      let result = await document.hasPrivateStateToken("https://issuer.example/");
+      let result = await document.hasPrivateToken("https://issuer.example/", 'private-state-token');
       assert_false(result, "The client should not possess any private state tokens for " +
         "https://issuer.example since it has not executed an issuance operation" +
         " against that issuer.");
 
-      result = await document.hasPrivateStateToken("https://issuer2.example/");
+      result = await document.hasPrivateToken("https://issuer2.example/", 'private-state-token');
       assert_false(result, "The client should not possess any private state tokens for" +
         " https://issuer2.example since it has not executed an issuance " +
         "operation against that issuer.");
 
-      await promise_rejects_dom(t, "OperationError", document.hasPrivateStateToken(
-          "https://issuer3.example/"),
-        "The first two hasPrivateStateToken operations associated this top-level" +
+      await promise_rejects_dom(t, "OperationError", document.hasPrivateToken(
+          "https://issuer3.example/", 'private-state-token'),
+        "The first two hasPrivateToken operations associated this top-level" +
         " origin with the maximum number of issuers (2), so an attempt to " +
-        " execute hasPrivateStateToken against another issuer should fail.");
+        " execute hasPrivateToken against another issuer should fail.");
 
-      result = await document.hasPrivateStateToken("https://issuer2.example/");
+      result = await document.hasPrivateToken("https://issuer2.example/", 'private-state-token');
       assert_false(result, "Since this top-level origin is already associated " +
-        "with https://issuer2.example, subsequent hasPrivateStateToken operations should " +
+        "with https://issuer2.example, subsequent hasPrivateToken operations should " +
         "not error out even though the top-level origin is at its " +
         "number-of-issuers limit.");
 
@@ -59,11 +65,11 @@
         await new Promise(res =>
           window.testRunner.clearTrustTokenState(res)
         );
-        result = await document.hasPrivateStateToken("https://issuer3.example/");
+        result = await document.hasPrivateToken("https://issuer3.example/", 'private-state-token');
         assert_false(result, "Since state was cleared, it should be possible to" +
-          "run hasPrivateStateToken against more issuers.");
+          "run hasPrivateToken against more issuers.");
       }
-    }, "When given a valid, secure origin, hasPrivateStateToken should succeed " +
+    }, "When given a valid, secure origin, hasPrivateToken should succeed " +
     "unless associating that origin with the top-level domain would exceed " +
     "the top-level origin's number-of-associated-issuers limit.");
 </script>
diff --git a/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 9ba3370..0ab826d4 100644
--- a/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -586,6 +586,7 @@
     method constructor
     method isSameEntry
     method queryPermission
+    method remove
     method requestPermission
 interface FileSystemWritableFileStream : WritableStream
     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 70e904c8..22724f1 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -584,6 +584,7 @@
 [Worker]     method constructor
 [Worker]     method isSameEntry
 [Worker]     method queryPermission
+[Worker]     method remove
 [Worker]     method requestPermission
 [Worker] interface FileSystemSyncAccessHandle
 [Worker]     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 133e6667..936d08a7 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -2319,6 +2319,7 @@
     method constructor
     method isSameEntry
     method queryPermission
+    method remove
     method requestPermission
 interface FileSystemWritableFileStream : WritableStream
     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
index dcfde575..147e97e2 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -523,6 +523,7 @@
 [Worker]     method constructor
 [Worker]     method isSameEntry
 [Worker]     method queryPermission
+[Worker]     method remove
 [Worker]     method requestPermission
 [Worker] interface FileSystemWritableFileStream : WritableStream
 [Worker]     attribute @@toStringTag
diff --git a/third_party/nearby/BUILD.gn b/third_party/nearby/BUILD.gn
index b1ae746..cc97f1f 100644
--- a/third_party/nearby/BUILD.gn
+++ b/third_party/nearby/BUILD.gn
@@ -792,6 +792,7 @@
     "src/presence/implementation/advertisement_decoder.cc",
     "src/presence/implementation/advertisement_factory.cc",
     "src/presence/implementation/base_broadcast_request.cc",
+    "src/presence/implementation/broadcast_manager.cc",
     "src/presence/implementation/credential_manager_impl.cc",
     "src/presence/implementation/encryption.cc",
     "src/presence/implementation/ldt.cc",
diff --git a/third_party/nearby/README.chromium b/third_party/nearby/README.chromium
index dda6a8b..64c9fa5 100644
--- a/third_party/nearby/README.chromium
+++ b/third_party/nearby/README.chromium
@@ -1,7 +1,7 @@
 Name: Nearby Connections Library
 Short Name: Nearby
 URL: https://github.com/google/nearby
-Version: b3f85342261db10bbeb2e5a07c7e54b616309b9e
+Version: ad9c26ec909292c46e3fd71993d8185463615b26
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 65dd903f..dea3f1d 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -32328,6 +32328,7 @@
   <int value="1043" label="CloudAPAuthEnabled"/>
   <int value="1044" label="UsbDetectorNotificationEnabled"/>
   <int value="1045" label="LacrosSelection"/>
+  <int value="1046" label="UseMojoVideoDecoderForPepperAllowed"/>
 </enum>
 
 <enum name="EnterprisePoliciesSources">
@@ -36633,6 +36634,15 @@
   <int value="3" label="Handshake completed successfully"/>
 </enum>
 
+<enum name="FastPairInitializePairingProcessEvent">
+  <int value="0" label="Initialization started"/>
+  <int value="1" label="Already pairing failure"/>
+  <int value="2" label="Passed to pairing dialog"/>
+  <int value="3" label="Exhausted retries failure"/>
+  <int value="4" label="Handshake reused"/>
+  <int value="5" label="Initialization complete"/>
+</enum>
+
 <enum name="FastPairInitialSuccessFunnelEvent">
   <int value="0" label="Notification clicked"/>
   <int value="1" label="V1 device detected"/>
@@ -41626,7 +41636,7 @@
   <int value="4408" label="CSSAtRuleOrnaments"/>
   <int value="4409" label="CSSAtRuleAnnotation"/>
   <int value="4410" label="ServiceWorkerBypassFetchHandlerForMainResource"/>
-  <int value="4411" label="V8Document_HasPrivateStateToken_Method"/>
+  <int value="4411" label="V8Document_HasPrivateToken_Method"/>
   <int value="4412" label="ServiceWorkerSkippedForEmptyFetchHandler"/>
   <int value="4413" label="ImageSet"/>
   <int value="4414" label="WindowCloseHistoryLengthOne"/>
@@ -57746,6 +57756,7 @@
   <int value="-1720576957" label="ThirdPartyStoragePartitioning:disabled"/>
   <int value="-1719833926" label="disable-answers-in-suggest"/>
   <int value="-1719699712" label="AudioPlayerJsModules:enabled"/>
+  <int value="-1718808435" label="WebAssemblyGarbageCollection:disabled"/>
   <int value="-1718644168" label="SharingHubLinkToggle:disabled"/>
   <int value="-1718074215" label="HomepageSettingsUIConversion:enabled"/>
   <int value="-1716654100" label="tab-capture-downscale-quality"/>
@@ -62472,6 +62483,7 @@
   <int value="1037038569" label="OriginAgentClusterDefaultEnable:enabled"/>
   <int value="1037961753" label="CCTIncognito:disabled"/>
   <int value="1038264914" label="PerDeskShelf:enabled"/>
+  <int value="1038818497" label="WebAssemblyGarbageCollection:enabled"/>
   <int value="1038906720" label="SharedHighlightingManager:disabled"/>
   <int value="1041376342" label="EnableTouchpadsInDiagnosticsApp:enabled"/>
   <int value="1042202617" label="RemoteCopyReceiver:enabled"/>
@@ -62931,6 +62943,7 @@
   <int value="1296661698" label="CrOSLateBootMediaDynamicCgroup:disabled"/>
   <int value="1296878388" label="PermissionPredictions:disabled"/>
   <int value="1296958520" label="hide-active-apps-from-shelf"/>
+  <int value="1297007123" label="ServiceWorkerBypassFetchHandler:enabled"/>
   <int value="1298734793" label="OsSettingsAppNotificationsPage:disabled"/>
   <int value="1298788828" label="WebID:disabled"/>
   <int value="1298927156" label="TouchpadOverscrollHistoryNavigation:enabled"/>
@@ -63560,6 +63573,7 @@
   <int value="1685280468" label="AppServiceShelf:enabled"/>
   <int value="1687272287" label="HandwritingLibraryDlc:disabled"/>
   <int value="1687544136" label="AndroidManagedByMenuItem:enabled"/>
+  <int value="1687768359" label="ServiceWorkerBypassFetchHandler:disabled"/>
   <int value="1688075820" label="OmniboxExperimentalKeywordMode:disabled"/>
   <int value="1689001971" label="ProjectorLocalPlayback:enabled"/>
   <int value="1689123607" label="enable-app-link"/>
@@ -91105,6 +91119,9 @@
   <int value="0" label="No Fetch Handler"/>
   <int value="1" label="Not Skipped"/>
   <int value="2" label="Skipped for Empty Fetch Handler"/>
+  <int value="3" label="Main resource is skipped by origin trial"/>
+  <int value="4" label="Main resource is skipped by feature flag"/>
+  <int value="5" label="Main resource is skipped by allowed origin list"/>
 </enum>
 
 <enum name="ServiceWorkerFetchHandlerType">
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml
index 18e695ff..d6eed612 100644
--- a/tools/metrics/histograms/metadata/extensions/histograms.xml
+++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -29,6 +29,8 @@
       summary="Execution took between 5ms and 10ms (slow)."/>
   <variant name=".LessThan1ms" summary="Execution took less than 1ms (fast)."/>
   <variant name=".Over10ms" summary="Execution took over 10ms (glacial)."/>
+  <variant name=".Over270s"
+      summary="Execution took over 4 and half minutes, close to timeout."/>
 </variants>
 
 <variants name="ExtensionMessagingPortType">
diff --git a/tools/metrics/histograms/metadata/fastpair/histograms.xml b/tools/metrics/histograms/metadata/fastpair/histograms.xml
index 15ab7a0f..d9854a6 100644
--- a/tools/metrics/histograms/metadata/fastpair/histograms.xml
+++ b/tools/metrics/histograms/metadata/fastpair/histograms.xml
@@ -91,6 +91,107 @@
   </token>
 </histogram>
 
+<histogram name="FastPair.{PairingScenario}.Initialization"
+    enum="FastPairInitializePairingProcessEvent" expires_after="2023-06-01">
+  <owner>jackshira@chromium.org</owner>
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the flow for the initialization of the GATT connection and the
+    process for grabbing the classic address for the Bluetooth device and
+    validating encryption keys. This flow is common across all three Fast Pair
+    pairing scenarios. Emitted on the following steps: initialization started
+    (when the process is kicked off), already pairing failure (when the device
+    is already pairing when the notification is clicked), passed to pairing
+    dialog (when a V1 device is detected, indicated that the device will not be
+    logging the subsequent metrics in the flow), exhausted retries failure (on
+    failure after three retry attempts), handshake reused (when a GATT handshake
+    already exists, bypassing the GATT handshake flow for this attempt), and
+    initialization complete (after the GATT handshake has completed, and the
+    classic address for the device is known).
+  </summary>
+  <token key="PairingScenario">
+    <variant name="InitialPairing"
+        summary="the first time a user pairs a device via Fast Pair, device
+                 needs to be in pairing mode, account key written to the
+                 device to save it to their account, discovered via BLE
+                 scanning"/>
+    <variant name="RetroactivePairing"
+        summary="user elects to save a device paired via classic Bluetooth
+                 settings to their Google account, account key written to
+                 device"/>
+    <variant name="SubsequentPairing"
+        summary="device is already saved to the user's account and is
+                 discovered via not-discoverable advertisement, which means
+                 the device does not have to be in pairing mode, no account
+                 key saved to device, discovered via BLE scanning"/>
+  </token>
+</histogram>
+
+<histogram name="FastPair.{PairingScenario}.Initialization.FailureReason"
+    enum="FastPairPairFailure" expires_after="2023-06-01">
+  <owner>jackshira@chromium.org</owner>
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the failure reason for initialization of the GATT connection and
+    handshake (process for grabbing the classic address for the Bluetooth device
+    and validating encryption keys). Emitted on every failure attempt. The
+    initialization can succeed overall on a third retry, and still emit to this
+    bucket on previous failed attempts. No metric is emitted on success. The
+    only `FastPairPairFailure`s that will be related to this flow are: Pairing
+    Device Lost, Data Encryptor Retrieval, Key-based Pairing Response Decrypt,
+    Incorrect Key-based Pairing Response Type, Create GATT Connection, Pairing
+    Device Lost Between GATT Connection Attempts, GATT Service Discovery
+    Timeout, Key-based Pairing Characteristic Discovery, Key-based Pairing
+    Characteristic Notify Session, Key-based Pairing Response Timeout, and
+    Key-based Pairing Characteristic Write.
+  </summary>
+  <token key="PairingScenario">
+    <variant name="InitialPairing"
+        summary="the first time a user pairs a device via Fast Pair, device
+                 needs to be in pairing mode, discovered via BLE scanning,
+                 account key written to the device and saved to their account"/>
+    <variant name="RetroactivePairing"
+        summary="user elects to save a device paired via classic Bluetooth
+                 settings to their Google account, account key written to
+                 device"/>
+    <variant name="SubsequentPairing"
+        summary="device is already saved to the user's account and is
+                 discovered via not-discoverable advertisement over BLE
+                 scanning, the device does not have to be in pairing mode, no
+                 account key saved to device"/>
+  </token>
+</histogram>
+
+<histogram
+    name="FastPair.{PairingScenario}.Initialization.RetriesBeforeSuccess"
+    units="count" expires_after="2023-06-01">
+  <owner>jackshira@chromium.org</owner>
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the number of retries that occurred before the successful completion
+    of the initialization flow for the GATT connection and handshake. Emitted
+    only on success of the initialization flow. No metric is emitted on failure.
+  </summary>
+  <token key="PairingScenario">
+    <variant name="InitialPairing"
+        summary="the first time a user pairs a device via Fast Pair, device
+                 needs to be in pairing mode, discovered via BLE scanning,
+                 account key written to the device and saved to their account"/>
+    <variant name="RetroactivePairing"
+        summary="user elects to save a device paired via classic Bluetooth
+                 settings to their Google account, account key written to
+                 device"/>
+    <variant name="SubsequentPairing"
+        summary="device is already saved to the user's account and is
+                 discovered via not-discoverable advertisement over BLE
+                 scanning, the device does not have to be in pairing mode, no
+                 account key saved to device"/>
+  </token>
+</histogram>
+
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/history/histograms.xml b/tools/metrics/histograms/metadata/history/histograms.xml
index 7fff9a1..03d72b1 100644
--- a/tools/metrics/histograms/metadata/history/histograms.xml
+++ b/tools/metrics/histograms/metadata/history/histograms.xml
@@ -679,6 +679,9 @@
 
 <histogram name="History.Clusters.Backend.ClusterFinalizers.ThreadTime"
     units="ms" expires_after="2023-06-04">
+  <obsolete>
+    Obsolete as of 12/2022.
+  </obsolete>
   <owner>sophiechang@chromium.org</owner>
   <owner>chrome-journeys@google.com</owner>
   <component>UI&gt;Browser&gt;Journeys</component>
@@ -694,6 +697,9 @@
 
 <histogram name="History.Clusters.Backend.ClusterProcessors.ThreadTime"
     units="ms" expires_after="2023-06-04">
+  <obsolete>
+    Obsolete as of 12/2022.
+  </obsolete>
   <owner>sophiechang@chromium.org</owner>
   <owner>chrome-journeys@google.com</owner>
   <component>UI&gt;Browser&gt;Journeys</component>
@@ -703,7 +709,8 @@
     hop or queuing delay. Logged when clusters have been calculated based on any
     request to cluster visits, which could be triggered from several things,
     including loading the journeys UI, putting a query in the journeys page,
-    etc...
+    etc. This is a segment within
+    `History.Clusters.Backend.ComputeClusters.ThreadTime`.
   </summary>
 </histogram>
 
@@ -738,7 +745,41 @@
     background worker. This does not include any thread hop or queuing delay.
     Logged when clusters have been calculated based on any request to cluster
     visits, which could be triggered from several things, including loading the
-    journeys UI, putting a query in the journeys page, etc...
+    journeys UI, putting a query in the journeys page, etc.
+  </summary>
+</histogram>
+
+<histogram name="History.Clusters.Backend.ComputeClustersForUI.ThreadTime"
+    units="ms" expires_after="2023-06-04">
+  <owner>sophiechang@chromium.org</owner>
+  <owner>chrome-journeys@google.com</owner>
+  <component>UI&gt;Browser&gt;Journeys</component>
+  <summary>
+    Logs the time taken to determine how clusters should be displayed once the
+    initial context clusters have been formed, which is performed on a
+    background worker. This does not include any thread hop or queuing delay.
+    Logged when clusters have been calculated based on any request to cluster
+    visits, which could be triggered from several things, including loading the
+    journeys UI, putting a query in the journeys page, etc. This is a segment
+    within `History.Clusters.Backend.ComputeClusters.ThreadTime`.
+  </summary>
+</histogram>
+
+<histogram
+    name="History.Clusters.Backend.ComputeClusterTriggerability.ThreadTime"
+    units="ms" expires_after="2023-06-04">
+  <owner>sophiechang@chromium.org</owner>
+  <owner>chrome-journeys@google.com</owner>
+  <component>UI&gt;Browser&gt;Journeys</component>
+  <summary>
+    Logs the time taken to run the steps during a clustering pass that are
+    required for determinining the triggerability for a cluster, which is
+    performed on a background worker. This does not include any thread hop or
+    queuing delay. Logged once per resulting cluster when clusters have been
+    calculated based on any clustering request, which could be triggered from
+    several things, including loading the journeys UI, putting a query in the
+    journeys page, etc. This is a segment within
+    `History.Clusters.Backend.ComputeClusters.ThreadTime`.
   </summary>
 </histogram>
 
@@ -753,7 +794,8 @@
     queuing delay. Logged when clusters have been calculated based on any
     request to cluster visits, which could be triggered from several things,
     including loading the journeys UI, putting a query in the journeys page,
-    etc...
+    etc. This is a segment within
+    `History.Clusters.Backend.ComputeClusters.ThreadTime`.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index b59c6ed..a265efff 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -625,6 +625,16 @@
   </summary>
 </histogram>
 
+<histogram name="Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon"
+    enum="Boolean" expires_after="2023-07-31">
+  <owner>behamilton@google.com</owner>
+  <owner>morlovich@chromium.org</owner>
+  <summary>
+    Recorded at the end of a FLEDGE auction when there is a non-k-anonymous
+    winner. True when the non-k-anonymous winner is k-anonymous.
+  </summary>
+</histogram>
+
 <histogram name="Ads.InterestGroup.Auction.NumAuctionsPerPage" units="auctions"
     expires_after="2023-07-31">
   <owner>caraitto@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 24101be..e2cf607 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -13,8 +13,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v31.0/linux-arm/trace_processor_shell"
         },
         "mac": {
-            "hash": "60685262d29ae642b3846ca2900284b2b44be891",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/ae7f044c460c89dac35657f618549998d665d7eb/trace_processor_shell"
+            "hash": "b72f040e74e03e3bc24b02998166261b18360ab8",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/aa7e493dcf9bf77707735b98723b916e08561bbe/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "5f47ee79e59d00bf3889d30ca52315522c158040",
diff --git a/ui/accessibility/accessibility_features.cc b/ui/accessibility/accessibility_features.cc
index bd42918..07e5e72 100644
--- a/ui/accessibility/accessibility_features.cc
+++ b/ui/accessibility/accessibility_features.cc
@@ -185,7 +185,7 @@
 
 BASE_FEATURE(kAccessibilitySelectToSpeakPageMigration,
              "AccessibilitySelectToSpeakPageMigration",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 bool IsAccessibilitySelectToSpeakPageMigrationEnabled() {
   return base::FeatureList::IsEnabled(
diff --git a/ui/aura/window_event_dispatcher.cc b/ui/aura/window_event_dispatcher.cc
index aef8d1a..eb28800 100644
--- a/ui/aura/window_event_dispatcher.cc
+++ b/ui/aura/window_event_dispatcher.cc
@@ -536,11 +536,6 @@
     return;
   }
 
-  if (host_->compositor() && cc::CustomMetricRecorder::Get()) {
-    event_metrics_monitors_.push_back(
-        CreateScropedMetricsMonitorForEvent(*event));
-  }
-
   // The held events are already in |window()|'s coordinate system. So it is
   // not necessary to apply the transform to convert from the host's
   // coordinate system to |window()|'s coordinate system.
@@ -558,13 +553,6 @@
     return;
 
   observer_notifiers_.pop();
-  if (host_->compositor() && cc::CustomMetricRecorder::Get()) {
-    std::unique_ptr<cc::EventsMetricsManager::ScopedMonitor> monitor =
-        std::move(event_metrics_monitors_.back());
-    event_metrics_monitors_.pop_back();
-    if (event->handled() && ShouldReportEventLatency(target, details))
-      monitor->SetSaveMetrics();
-  }
 }
 
 bool WindowEventDispatcher::ShouldReportEventLatency(
@@ -593,6 +581,11 @@
 ui::EventDispatchDetails WindowEventDispatcher::PreDispatchEvent(
     ui::EventTarget* target,
     ui::Event* event) {
+  if (host_->compositor() && cc::CustomMetricRecorder::Get()) {
+    event_metrics_monitors_.push_back(
+        CreateScropedMetricsMonitorForEvent(*event));
+  }
+
   Window* target_window = static_cast<Window*>(target);
   CHECK(window()->Contains(target_window));
 
@@ -700,11 +693,22 @@
                 touchevent.unique_event_id(), event.result(),
                 false /* is_source_touch_event_set_blocking */, window);
 
-        return ProcessGestures(window, std::move(gestures));
+        details = ProcessGestures(window, std::move(gestures));
       }
     }
   }
 
+  // Note this must run after processing events corresponding to the event
+  // monitor creation code in PreDispatchEvent to track latencies properly.
+  if (!details.dispatcher_destroyed && host_->compositor() &&
+      cc::CustomMetricRecorder::Get()) {
+    std::unique_ptr<cc::EventsMetricsManager::ScopedMonitor> monitor =
+        std::move(event_metrics_monitors_.back());
+    event_metrics_monitors_.pop_back();
+    if (event.handled() && ShouldReportEventLatency(target, details))
+      monitor->SetSaveMetrics();
+  }
+
   return details;
 }
 
diff --git a/ui/base/ime/DEPS b/ui/base/ime/DEPS
index b7643a9..8a989b06 100644
--- a/ui/base/ime/DEPS
+++ b/ui/base/ime/DEPS
@@ -1,5 +1,5 @@
 include_rules = [
-  "+chromeos/system",
+  "+chromeos/ash/components/system",
   "+mojo/public",
   "+services/metrics/public/cpp",
   "+ui/linux",
diff --git a/ui/base/ime/ash/BUILD.gn b/ui/base/ime/ash/BUILD.gn
index 93fa708..58b747a7 100644
--- a/ui/base/ime/ash/BUILD.gn
+++ b/ui/base/ime/ash/BUILD.gn
@@ -71,8 +71,8 @@
     ":ime_types",
     ":typing_session_manager",
     "//build:branding_buildflags",
+    "//chromeos/ash/components/system",
     "//chromeos/ash/services/ime/public/mojom",
-    "//chromeos/system",
     "//services/metrics/public/cpp:ukm_builders",
     "//third_party/icu",
     "//ui/base",
diff --git a/ui/base/ime/ash/input_method_ash.cc b/ui/base/ime/ash/input_method_ash.cc
index da1d339..effb6f4 100644
--- a/ui/base/ime/ash/input_method_ash.cc
+++ b/ui/base/ime/ash/input_method_ash.cc
@@ -20,7 +20,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/third_party/icu/icu_utf.h"
 #include "base/time/default_clock.h"
-#include "chromeos/system/devicemode.h"
+#include "chromeos/ash/components/system/devicemode.h"
 #include "ui/base/ime/ash/ime_bridge.h"
 #include "ui/base/ime/ash/ime_keyboard.h"
 #include "ui/base/ime/ash/input_method_manager.h"
diff --git a/ui/display/manager/BUILD.gn b/ui/display/manager/BUILD.gn
index c3f77729..257d9a2 100644
--- a/ui/display/manager/BUILD.gn
+++ b/ui/display/manager/BUILD.gn
@@ -69,7 +69,7 @@
       "update_display_configuration_task.h",
     ]
     deps += [
-      "//chromeos/system",
+      "//chromeos/ash/components/system",
       "//chromeos/ui/base",
     ]
   }
diff --git a/ui/display/manager/display_configurator.cc b/ui/display/manager/display_configurator.cc
index d659db7d..f371c2d 100644
--- a/ui/display/manager/display_configurator.cc
+++ b/ui/display/manager/display_configurator.cc
@@ -14,7 +14,7 @@
 #include "base/logging.h"
 #include "base/syslog_logging.h"
 #include "base/time/time.h"
-#include "chromeos/system/devicemode.h"
+#include "chromeos/ash/components/system/devicemode.h"
 #include "ui/display/display.h"
 #include "ui/display/display_features.h"
 #include "ui/display/display_switches.h"
diff --git a/ui/display/manager/display_manager.cc b/ui/display/manager/display_manager.cc
index 4698d46..fd6a6da 100644
--- a/ui/display/manager/display_manager.cc
+++ b/ui/display/manager/display_manager.cc
@@ -30,7 +30,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "chromeos/system/devicemode.h"
+#include "chromeos/ash/components/system/devicemode.h"
 #include "chromeos/ui/base/display_util.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/display/display.h"
diff --git a/ui/display/manager/display_manager_utilities.cc b/ui/display/manager/display_manager_utilities.cc
index 11803290..fa18fc4 100644
--- a/ui/display/manager/display_manager_utilities.cc
+++ b/ui/display/manager/display_manager_utilities.cc
@@ -20,7 +20,7 @@
 #include "ui/gfx/geometry/size_f.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chromeos/system/statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #endif
 
 namespace display {
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index 56ee545..abbf478 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -122,6 +122,8 @@
     "gpu_switching_manager.h",
     "gpu_timing.cc",
     "gpu_timing.h",
+    "presenter.cc",
+    "presenter.h",
     "progress_reporter.h",
     "scoped_binders.cc",
     "scoped_binders.h",
diff --git a/ui/gl/dc_layer_tree.cc b/ui/gl/dc_layer_tree.cc
index eaf93f8..676cd67e 100644
--- a/ui/gl/dc_layer_tree.cc
+++ b/ui/gl/dc_layer_tree.cc
@@ -267,6 +267,13 @@
     dcomp_visual_content_ = std::move(dcomp_visual_content);
     needs_commit = true;
     hr = content_visual_->SetContent(dcomp_visual_content_.Get());
+    if (FAILED(hr)) {
+      // This can be changed back to a CHECK_EQ once
+      // DirectCompositionPixelTest.RootSurfaceDrawOffset in
+      // ui/gl/direct_composition_surface_win_unittest.cc is removed.
+      DLOG(ERROR) << "SetContent failed: "
+                  << logging::SystemErrorCodeToString(hr);
+    }
   }
 
   if (dcomp_surface_serial_ != dcomp_surface_serial) {
diff --git a/ui/gl/gl_surface_egl_surface_control.cc b/ui/gl/gl_surface_egl_surface_control.cc
index 7db58f0..0f96bde4 100644
--- a/ui/gl/gl_surface_egl_surface_control.cc
+++ b/ui/gl/gl_surface_egl_surface_control.cc
@@ -80,7 +80,7 @@
     GLDisplayEGL* display,
     scoped_refptr<gfx::SurfaceControl::Surface> root_surface,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : GLSurfaceEGL(display),
+    : Presenter(display, gfx::Size()),
       child_surface_name_(BuildSurfaceName(kChildSurfaceName)),
       root_surface_(std::move(root_surface)),
       transaction_ack_timeout_manager_(task_runner),
diff --git a/ui/gl/gl_surface_egl_surface_control.h b/ui/gl/gl_surface_egl_surface_control.h
index a6b72d97..1498a4de 100644
--- a/ui/gl/gl_surface_egl_surface_control.h
+++ b/ui/gl/gl_surface_egl_surface_control.h
@@ -17,7 +17,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/android/android_surface_control_compat.h"
 #include "ui/gl/gl_export.h"
-#include "ui/gl/gl_surface_egl.h"
+#include "ui/gl/presenter.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -32,7 +32,7 @@
 class ScopedANativeWindow;
 class ScopedJavaSurfaceControl;
 
-class GL_EXPORT GLSurfaceEGLSurfaceControl : public GLSurfaceEGL {
+class GL_EXPORT GLSurfaceEGLSurfaceControl : public Presenter {
  public:
   GLSurfaceEGLSurfaceControl(
       GLDisplayEGL* display,
diff --git a/ui/gl/init/create_gr_gl_interface.cc b/ui/gl/init/create_gr_gl_interface.cc
index 17d85fe..02a6d783b 100644
--- a/ui/gl/init/create_gr_gl_interface.cc
+++ b/ui/gl/init/create_gr_gl_interface.cc
@@ -231,6 +231,7 @@
     "GL_EXT_multi_draw_indirect",
     "GL_EXT_raster_multisample",
     "GL_NV_bindless_texture",
+    "GL_NV_framebuffer_mixed_samples",
     "GL_NV_texture_barrier",
     "GL_OES_sample_shading",
     "GL_EXT_draw_instanced",
diff --git a/ui/gl/init/gl_factory.h b/ui/gl/init/gl_factory.h
index b3396d9..0685262 100644
--- a/ui/gl/init/gl_factory.h
+++ b/ui/gl/init/gl_factory.h
@@ -18,6 +18,7 @@
 #include "ui/gl/gl_surface_format.h"
 #include "ui/gl/gpu_preference.h"
 #include "ui/gl/init/gl_init_export.h"
+#include "ui/gl/presenter.h"
 
 namespace gl {
 
@@ -100,7 +101,7 @@
 // semantics - there is no default framebuffer and the primary surface must
 // be presented as an overlay. If surfaceless mode is not supported or
 // enabled it will return a null pointer.
-GL_INIT_EXPORT scoped_refptr<GLSurface> CreateSurfacelessViewGLSurface(
+GL_INIT_EXPORT scoped_refptr<Presenter> CreateSurfacelessViewGLSurface(
     GLDisplay* display,
     gfx::AcceleratedWidget window);
 #endif  // BUILDFLAG(IS_OZONE)
diff --git a/ui/gl/init/gl_factory_ozone.cc b/ui/gl/init/gl_factory_ozone.cc
index 4ec5b03..0c01aea 100644
--- a/ui/gl/init/gl_factory_ozone.cc
+++ b/ui/gl/init/gl_factory_ozone.cc
@@ -15,6 +15,7 @@
 #include "ui/gl/gl_surface.h"
 #include "ui/gl/gl_surface_stub.h"
 #include "ui/gl/init/ozone_util.h"
+#include "ui/gl/presenter.h"
 
 namespace gl {
 namespace init {
@@ -76,7 +77,7 @@
   return nullptr;
 }
 
-scoped_refptr<GLSurface> CreateSurfacelessViewGLSurface(
+scoped_refptr<Presenter> CreateSurfacelessViewGLSurface(
     GLDisplay* display,
     gfx::AcceleratedWidget window) {
   TRACE_EVENT0("gpu", "gl::init::CreateSurfacelessViewGLSurface");
diff --git a/ui/gl/presenter.cc b/ui/gl/presenter.cc
new file mode 100644
index 0000000..94e86c7
--- /dev/null
+++ b/ui/gl/presenter.cc
@@ -0,0 +1,13 @@
+// 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.
+
+#include "ui/gl/presenter.h"
+
+namespace gl {
+
+Presenter::Presenter(GLDisplayEGL* display, const gfx::Size& size)
+    : SurfacelessEGL(display, size) {}
+Presenter::~Presenter() = default;
+
+}  // namespace gl
\ No newline at end of file
diff --git a/ui/gl/presenter.h b/ui/gl/presenter.h
new file mode 100644
index 0000000..a61876d
--- /dev/null
+++ b/ui/gl/presenter.h
@@ -0,0 +1,26 @@
+// 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 UI_GL_PRESENTER_H_
+#define UI_GL_PRESENTER_H_
+
+#include "ui/gl/gl_export.h"
+#include "ui/gl/gl_surface_egl.h"
+
+namespace gl {
+
+// Class that is used for presentation on the surfaceless platforms. Temporarily
+// is in ui/gl and subclasses SurfacelessEGL. Base class will be removed and
+// class will be moved to ui/gfx
+class GL_EXPORT Presenter : public SurfacelessEGL {
+ public:
+  Presenter(GLDisplayEGL* display, const gfx::Size& size);
+
+ protected:
+  ~Presenter() override;
+};
+
+}  // namespace gl
+
+#endif  // UI_GL_PRESENTER_H_
diff --git a/ui/ozone/BUILD.gn b/ui/ozone/BUILD.gn
index 45bf099..99430a45 100644
--- a/ui/ozone/BUILD.gn
+++ b/ui/ozone/BUILD.gn
@@ -161,7 +161,7 @@
   visibility += [
     # Everyone should depend on //ui/ozone instead except a handful of
     # things that would otherwise create a cycle.
-    "//chromeos/system:system",
+    "//chromeos/ash/components/system",
     "//ui/base/ime/ash/*",
     "//ui/events/ozone/*",
     "//ui/ozone/common/*",
diff --git a/ui/ozone/common/gl_ozone_egl.cc b/ui/ozone/common/gl_ozone_egl.cc
index d30bac5d..9b1d37e 100644
--- a/ui/ozone/common/gl_ozone_egl.cc
+++ b/ui/ozone/common/gl_ozone_egl.cc
@@ -85,7 +85,7 @@
                                  compatible_surface, attribs);
 }
 
-scoped_refptr<gl::GLSurface> GLOzoneEGL::CreateSurfacelessViewGLSurface(
+scoped_refptr<gl::Presenter> GLOzoneEGL::CreateSurfacelessViewGLSurface(
     gl::GLDisplay* display,
     gfx::AcceleratedWidget window) {
   // This will usually not be implemented by the platform specific version.
diff --git a/ui/ozone/common/gl_ozone_egl.h b/ui/ozone/common/gl_ozone_egl.h
index 5fcbe95a..54c89f7 100644
--- a/ui/ozone/common/gl_ozone_egl.h
+++ b/ui/ozone/common/gl_ozone_egl.h
@@ -51,7 +51,7 @@
   scoped_refptr<gl::GLSurface> CreateViewGLSurface(
       gl::GLDisplay* display,
       gfx::AcceleratedWidget window) override = 0;
-  scoped_refptr<gl::GLSurface> CreateSurfacelessViewGLSurface(
+  scoped_refptr<gl::Presenter> CreateSurfacelessViewGLSurface(
       gl::GLDisplay* display,
       gfx::AcceleratedWidget window) override;
   scoped_refptr<gl::GLSurface> CreateOffscreenGLSurface(
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
index 7ab7f602..5eac547f 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
@@ -24,6 +24,7 @@
 #include "ui/gfx/native_pixmap.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_surface_egl.h"
+#include "ui/gl/presenter.h"
 #include "ui/ozone/common/egl_util.h"
 #include "ui/ozone/common/gl_ozone_egl.h"
 #include "ui/ozone/common/native_pixmap_egl_binding.h"
@@ -140,12 +141,16 @@
     return nullptr;
   }
 
-  scoped_refptr<gl::GLSurface> CreateSurfacelessViewGLSurface(
+  scoped_refptr<gl::Presenter> CreateSurfacelessViewGLSurface(
       gl::GLDisplay* display,
       gfx::AcceleratedWidget window) override {
-    return gl::InitializeGLSurface(new GbmSurfaceless(
-        surface_factory_, display->GetAs<gl::GLDisplayEGL>(),
-        drm_thread_proxy_->CreateDrmWindowProxy(window), window));
+    scoped_refptr<gl::Presenter> presenter =
+        base::MakeRefCounted<GbmSurfaceless>(
+            surface_factory_, display->GetAs<gl::GLDisplayEGL>(),
+            drm_thread_proxy_->CreateDrmWindowProxy(window), window);
+    if (!presenter->Initialize(gl::GLSurfaceFormat()))
+      return nullptr;
+    return presenter;
   }
 
   scoped_refptr<gl::GLSurface> CreateOffscreenGLSurface(
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
index f8b3e2c..1db16e94 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
@@ -41,7 +41,7 @@
                                gl::GLDisplayEGL* display,
                                std::unique_ptr<DrmWindowProxy> window,
                                gfx::AcceleratedWidget widget)
-    : SurfacelessEGL(display, gfx::Size()),
+    : Presenter(display, gfx::Size()),
       surface_factory_(surface_factory),
       window_(std::move(window)),
       widget_(widget),
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.h b/ui/ozone/platform/drm/gpu/gbm_surfaceless.h
index a34cea4..6fcc92b6 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surfaceless.h
+++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.h
@@ -12,8 +12,8 @@
 #include "ui/gfx/gpu_fence_handle.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gl/gl_image.h"
-#include "ui/gl/gl_surface_egl.h"
 #include "ui/gl/gl_surface_overlay.h"
+#include "ui/gl/presenter.h"
 #include "ui/gl/scoped_binders.h"
 #include "ui/ozone/platform/drm/gpu/drm_overlay_plane.h"
 
@@ -30,7 +30,7 @@
 // displaying happens directly through NativePixmap buffers. CC would call into
 // SurfaceFactoryOzone to allocate the buffers and then call
 // ScheduleOverlayPlane(..) to schedule the buffer for presentation.
-class GbmSurfaceless : public gl::SurfacelessEGL {
+class GbmSurfaceless : public gl::Presenter {
  public:
   GbmSurfaceless(GbmSurfaceFactory* surface_factory,
                  gl::GLDisplayEGL* display,
diff --git a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
index 10662dd..1c23174 100644
--- a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
+++ b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
@@ -109,7 +109,7 @@
     gl::GLDisplayEGL* display,
     WaylandBufferManagerGpu* buffer_manager,
     gfx::AcceleratedWidget widget)
-    : SurfacelessEGL(display, gfx::Size()),
+    : Presenter(display, gfx::Size()),
       buffer_manager_(buffer_manager),
       widget_(widget),
       solid_color_buffers_holder_(std::make_unique<SolidColorBufferHolder>()),
diff --git a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
index 638cadfc..b9a25c9 100644
--- a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
+++ b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
@@ -12,7 +12,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "ui/gfx/native_widget_types.h"
-#include "ui/gl/gl_surface_egl.h"
+#include "ui/gl/presenter.h"
 #include "ui/ozone/platform/wayland/common/wayland_overlay_config.h"
 #include "ui/ozone/platform/wayland/gpu/wayland_surface_gpu.h"
 #include "ui/ozone/public/swap_completion_callback.h"
@@ -27,8 +27,7 @@
 // and displaying happens directly through NativePixmap buffers. CC would call
 // into SurfaceFactoryOzone to allocate the buffers and then call
 // ScheduleOverlayPlane(..) to schedule the buffer for presentation.
-class GbmSurfacelessWayland : public gl::SurfacelessEGL,
-                              public WaylandSurfaceGpu {
+class GbmSurfacelessWayland : public gl::Presenter, public WaylandSurfaceGpu {
  public:
   GbmSurfacelessWayland(gl::GLDisplayEGL* display,
                         WaylandBufferManagerGpu* buffer_manager,
diff --git a/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc b/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
index c192a83..ab19a324 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
+++ b/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
@@ -63,7 +63,7 @@
       gl::GLDisplay* display,
       gfx::AcceleratedWidget widget) override;
 
-  scoped_refptr<gl::GLSurface> CreateSurfacelessViewGLSurface(
+  scoped_refptr<gl::Presenter> CreateSurfacelessViewGLSurface(
       gl::GLDisplay* display,
       gfx::AcceleratedWidget window) override;
 
@@ -126,7 +126,7 @@
       display->GetAs<gl::GLDisplayEGL>(), std::move(egl_window), window));
 }
 
-scoped_refptr<gl::GLSurface> GLOzoneEGLWayland::CreateSurfacelessViewGLSurface(
+scoped_refptr<gl::Presenter> GLOzoneEGLWayland::CreateSurfacelessViewGLSurface(
     gl::GLDisplay* display,
     gfx::AcceleratedWidget window) {
   if (gl::IsSoftwareGLImplementation(gl::GetGLImplementationParts())) {
@@ -136,8 +136,12 @@
   // If there is a gbm device available, use surfaceless gl surface.
   if (!buffer_manager_->GetGbmDevice())
     return nullptr;
-  return gl::InitializeGLSurface(new GbmSurfacelessWayland(
-      display->GetAs<gl::GLDisplayEGL>(), buffer_manager_, window));
+  scoped_refptr<gl::Presenter> presenter =
+      base::MakeRefCounted<GbmSurfacelessWayland>(
+          display->GetAs<gl::GLDisplayEGL>(), buffer_manager_, window);
+  if (!presenter->Initialize(gl::GLSurfaceFormat()))
+    return nullptr;
+  return presenter;
 #else
   return nullptr;
 #endif
diff --git a/ui/ozone/public/gl_ozone.h b/ui/ozone/public/gl_ozone.h
index d0d911f..01b1d916 100644
--- a/ui/ozone/public/gl_ozone.h
+++ b/ui/ozone/public/gl_ozone.h
@@ -20,6 +20,7 @@
 class GLContext;
 class GLShareGroup;
 class GLSurface;
+class Presenter;
 
 struct GLContextAttribs;
 struct GLVersionInfo;
@@ -99,7 +100,7 @@
   // overlay-only displays. This will return null if surfaceless mode is
   // unsupported.
   // TODO(spang): Consider deprecating this and using OverlaySurface for GL.
-  virtual scoped_refptr<gl::GLSurface> CreateSurfacelessViewGLSurface(
+  virtual scoped_refptr<gl::Presenter> CreateSurfacelessViewGLSurface(
       gl::GLDisplay* display,
       gfx::AcceleratedWidget window) = 0;
 
diff --git a/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html b/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html
index 9715d13..9cdda39 100644
--- a/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html
+++ b/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html
@@ -46,11 +46,12 @@
     aria-hidden="true">
 </iron-icon>
 <div id="labelWrapper" hidden="[[hideLabelWrapper_]]">
-  <div id="label" aria-hidden="true">
+  <div id="label" aria-hidden="[[!ariaShowLabel]]">
     [[label]]
     <slot name="label"></slot>
   </div>
-  <div id="subLabel" class="cr-secondary-text" aria-hidden="true">
+  <div id="subLabel" class="cr-secondary-text"
+      aria-hidden="[[!ariaShowSublabel]]">
     [[subLabel]]
     <slot name="sub-label"></slot>
   </div>
diff --git a/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.ts b/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.ts
index e117f8d9..8e2366d 100644
--- a/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.ts
+++ b/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.ts
@@ -40,6 +40,18 @@
 
   static get properties() {
     return {
+      ariaShowLabel: {
+        type: Boolean,
+        reflectToAttribute: true,
+        value: false,
+      },
+
+      ariaShowSublabel: {
+        type: Boolean,
+        reflectToAttribute: true,
+        value: false,
+      },
+
       startIcon: {
         type: String,
         value: '',
@@ -80,6 +92,8 @@
     };
   }
 
+  ariaShowLabel: boolean;
+  ariaShowSublabel: boolean;
   startIcon: string;
   label: string;
   subLabel: string;