diff --git a/DEPS b/DEPS
index d5b2acd..539f735 100644
--- a/DEPS
+++ b/DEPS
@@ -307,19 +307,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': 'f50771e71e15bbd7f0a8acf44843c676e993a4fa',
+  'skia_revision': '7085c37a04e4b9c27dc49c9a99c01ba1e9540ec2',
   # 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': '17f98f9b999d414bece0cdf872f20408f1555969',
+  'v8_revision': '09f80223ca394e7b254d3a928fc11f0df1aecbb4',
   # 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': 'a12c00a1df80da516294391029e89a6c10edb16b',
+  'angle_revision': 'a48a2c7a277ad5d9a69e2f830ffc730e3cb6af77',
   # 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': '29dd647656bd443becda0ac9eedac68f8447b760',
+  'swiftshader_revision': 'a113fba5d9469933a4ddd7652cefa3a3640fc25b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -386,7 +386,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'a00ddc259e61f8ab17babc995bbf17685f77cba6',
+  'devtools_frontend_revision': '9920f9fcb53121f62bb7c2831baf2a9656a71354',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -446,7 +446,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': '33c5f44ea1f39cf435ee711a59d73cb946c0ae2a',
+  'nearby_revision': '4935a2ffdb17da23a2b21957b8e61a493786d703',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling securemessage
   # and whatever else without interference from each other.
@@ -817,12 +817,12 @@
 
   'src/clank': {
     'url': 'https://chrome-internal.googlesource.com/clank/internal/apps.git' + '@' +
-    '68d635599de34c6d34ad68e376cbdc8b4c0951b6',
+    'ffa550390968fbb1c9a3cca80f767b6d8e2595fd',
     'condition': 'checkout_android and checkout_src_internal and not checkout_clank_via_src_internal',
   },
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + '106fafbd1540947039d4c27f3b5c9e7ff8a0df08',
+    'url': Var('chromium_git') + '/website.git' + '@' + '6ffe567aed8b447ffec7316357391f80997930a8',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1034,7 +1034,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_build_tools/aapt2',
-              'version': 'nSnWUNu6ssPA-kPMvFQj4JjDXRWj2iubvvjfT1F6HCMC',
+              'version': '-QrdsGmvVhHeoRc5wKCnU2LXEjk1s0ocheitXWf5dhYC',
           },
       ],
       'condition': 'checkout_android',
@@ -1067,7 +1067,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/lint',
-               'version': 'oJIo5mCU1gyxTf-EnMPZXg_GG2-3YOE_xUWXtmwO96IC',
+               'version': 'QchUp_CPEekuo_inqr_aTnbotGejxQt4FGgFbQT0qBMC',
           },
       ],
       'condition': 'checkout_android',
@@ -1078,7 +1078,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/manifest_merger',
-               'version': 'xd-wXGBtd-G1FJXc_owo3j_wxWs4YxgOfQ-tKWHwN5AC',
+               'version': '10z1KegIoj_7T--lXulnk4MUKHMHEo_onhwh_4FvyMQC',
           },
       ],
       'condition': 'checkout_android',
@@ -1244,13 +1244,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1adbbff2c0a0df872cce7adb9e86bbaa68444028',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '7fb87695ee8c7e489e1c1cab3c34760cf50e0149',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + 'a04519ecf82cf63c8babdbb701939637a278c6ab',
+      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + '2dfd6af7270511681002e66aa5b58b2e059749a0',
     'condition': 'checkout_src_internal',
   },
 
@@ -1617,7 +1617,7 @@
 
   'src/third_party/nasm': {
       'url': Var('chromium_git') + '/chromium/deps/nasm.git' + '@' +
-      'fc8e0bd892ae133602f5a6f7a9a6840aa1b75989'
+      '0873b2bae6a5388a1c55deac8456e3c60a47ca08'
   },
 
   'src/third_party/neon_2_sse/src':
@@ -1665,7 +1665,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '06c0150ef04cb2b72c8df56acd8d22a185d81cb7',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '6232b55ef794877510be9435549503968813d0f6',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1847,10 +1847,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'd1b65aa5a88f6efd900604dfcda840154e9f16e2',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '2fb4b12a6c5174def779ea889da3f810c2814684',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '074471be8391ee860b069b5bb032435c6077ff56',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '957ffed4cad13a7700755eb3cd6b4fa7d6c450c7',
+    Var('webrtc_git') + '/src.git' + '@' + '8f7ad88d0e09a0ead0fcd2f578849c3f7a57baad',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1920,7 +1920,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c0d57f0ad7858cabcaf874c70c152dd6d1e53795',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@73ba6b14c246fb66556a59488536490796c5bcf5',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_client_hints_controller_delegate.cc b/android_webview/browser/aw_client_hints_controller_delegate.cc
index a0c488ef..18cd044 100644
--- a/android_webview/browser/aw_client_hints_controller_delegate.cc
+++ b/android_webview/browser/aw_client_hints_controller_delegate.cc
@@ -5,6 +5,7 @@
 #include "android_webview/browser/aw_client_hints_controller_delegate.h"
 
 #include "android_webview/browser/aw_contents.h"
+#include "android_webview/browser/aw_cookie_access_policy.h"
 #include "base/notreached.h"
 #include "components/embedder_support/user_agent_utils.h"
 #include "content/public/browser/client_hints_controller_delegate.h"
@@ -58,10 +59,25 @@
 }
 
 bool AwClientHintsControllerDelegate::AreThirdPartyCookiesBlocked(
-    const GURL& url) {
-  // TODO(crbug.com/921655): Actually implement function.
-  NOTIMPLEMENTED();
-  return false;
+    const GURL& url,
+    content::RenderFrameHost* rfh) {
+  if (!rfh) {
+    return true;
+  }
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(rfh);
+  if (!web_contents) {
+    // TODO(crbug.com/921655): Detect and support service workers here.
+    return true;
+  }
+  AwContents* aw_contents = AwContents::FromWebContents(web_contents);
+  if (!aw_contents) {
+    return true;
+  }
+  // Despite the name of the function we want to mirror the function of
+  // ClientHints::AreThirdPartyCookiesBlocked and check the global block too.
+  return !AwCookieAccessPolicy::GetInstance()->GetShouldAcceptCookies() ||
+         !aw_contents->AllowThirdPartyCookies();
 }
 
 blink::UserAgentMetadata
diff --git a/android_webview/browser/aw_client_hints_controller_delegate.h b/android_webview/browser/aw_client_hints_controller_delegate.h
index 15bf3ab..70f2031 100644
--- a/android_webview/browser/aw_client_hints_controller_delegate.h
+++ b/android_webview/browser/aw_client_hints_controller_delegate.h
@@ -45,7 +45,8 @@
   bool IsJavaScriptAllowed(const GURL& url,
                            content::RenderFrameHost* parent_rfh) override;
 
-  bool AreThirdPartyCookiesBlocked(const GURL& url) override;
+  bool AreThirdPartyCookiesBlocked(const GURL& url,
+                                   content::RenderFrameHost* rfh) override;
 
   blink::UserAgentMetadata GetUserAgentMetadata() override;
 
diff --git a/android_webview/browser/aw_client_hints_controller_delegate_unittest.cc b/android_webview/browser/aw_client_hints_controller_delegate_unittest.cc
index be40da87..b3fbc5d 100644
--- a/android_webview/browser/aw_client_hints_controller_delegate_unittest.cc
+++ b/android_webview/browser/aw_client_hints_controller_delegate_unittest.cc
@@ -47,9 +47,11 @@
 }
 
 TEST_F(AwClientHintsControllerDelegateTest, AreThirdPartyCookiesBlocked) {
-  // TODO(crbug.com/921655): Actually test function once implemented.
-  EXPECT_FALSE(
-      client_hints_controller_delegate_->AreThirdPartyCookiesBlocked(GURL("")));
+  EXPECT_TRUE(client_hints_controller_delegate_->AreThirdPartyCookiesBlocked(
+      GURL(""), nullptr));
+  EXPECT_TRUE(client_hints_controller_delegate_->AreThirdPartyCookiesBlocked(
+      GURL("https://example.com"), nullptr));
+  // TODO(crbug.com/921655): Add integration test when the rest is implemented.
 }
 
 TEST_F(AwClientHintsControllerDelegateTest, GetUserAgentMetadata) {
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index c6b5b2d..3866f3d72 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1218,6 +1218,8 @@
     "system/eche/eche_tray.cc",
     "system/eche/eche_tray.h",
     "system/enterprise/enterprise_domain_observer.h",
+    "system/federated/federated_service_controller.cc",
+    "system/federated/federated_service_controller.h",
     "system/firmware_update/firmware_update_notification_controller.cc",
     "system/firmware_update/firmware_update_notification_controller.h",
     "system/geolocation/geolocation_controller.cc",
@@ -1370,14 +1372,8 @@
     "system/message_center/notifier_settings_view.h",
     "system/message_center/session_state_notification_blocker.cc",
     "system/message_center/session_state_notification_blocker.h",
-    "system/message_center/stacked_notification_bar.cc",
-    "system/message_center/stacked_notification_bar.h",
     "system/message_center/unified_message_center_bubble.cc",
     "system/message_center/unified_message_center_bubble.h",
-    "system/message_center/unified_message_center_view.cc",
-    "system/message_center/unified_message_center_view.h",
-    "system/message_center/unified_message_list_view.cc",
-    "system/message_center/unified_message_list_view.h",
     "system/microphone_mute/microphone_mute_notification_controller.cc",
     "system/microphone_mute/microphone_mute_notification_controller.h",
     "system/model/clock_model.cc",
@@ -1500,6 +1496,12 @@
     "system/night_light/night_light_feature_pod_controller.h",
     "system/notification_center/notification_center_tray.cc",
     "system/notification_center/notification_center_tray.h",
+    "system/notification_center/notification_center_view.cc",
+    "system/notification_center/notification_center_view.h",
+    "system/notification_center/notification_list_view.cc",
+    "system/notification_center/notification_list_view.h",
+    "system/notification_center/stacked_notification_bar.cc",
+    "system/notification_center/stacked_notification_bar.h",
     "system/overview/overview_button_tray.cc",
     "system/overview/overview_button_tray.h",
     "system/palette/common_palette_tool.cc",
@@ -2406,6 +2408,8 @@
     "//chromeos/ash/services/assistant/public/mojom",
     "//chromeos/ash/services/bluetooth_config/public/cpp",
     "//chromeos/ash/services/bluetooth_config/public/mojom",
+    "//chromeos/ash/services/federated/public/cpp",
+    "//chromeos/ash/services/federated/public/mojom",
     "//chromeos/ash/services/hotspot_config/public/mojom",
     "//chromeos/ash/services/libassistant/public/cpp:structs",
     "//chromeos/ash/services/libassistant/public/mojom",
@@ -2915,6 +2919,7 @@
     "system/diagnostics/telemetry_log_unittest.cc",
     "system/eche/eche_icon_loading_indicator_view_unittest.cc",
     "system/eche/eche_tray_unittest.cc",
+    "system/federated/federated_service_controller_unittest.cc",
     "system/firmware_update/firmware_update_notification_controller_unittest.cc",
     "system/geolocation/geolocation_controller_test_util.cc",
     "system/geolocation/geolocation_controller_test_util.h",
@@ -2951,8 +2956,6 @@
     "system/message_center/notifier_settings_view_unittest.cc",
     "system/message_center/session_state_notification_blocker_unittest.cc",
     "system/message_center/unified_message_center_bubble_unittest.cc",
-    "system/message_center/unified_message_center_view_unittest.cc",
-    "system/message_center/unified_message_list_view_unittest.cc",
     "system/microphone_mute/microphone_mute_notification_controller_unittest.cc",
     "system/nearby_share/nearby_share_feature_pod_controller_unittest.cc",
     "system/network/active_network_icon_unittest.cc",
@@ -2977,6 +2980,8 @@
     "system/night_light/night_light_controller_unittest.cc",
     "system/night_light/night_light_feature_pod_controller_unittest.cc",
     "system/notification_center/notification_center_tray_unittest.cc",
+    "system/notification_center/notification_center_view_unittest.cc",
+    "system/notification_center/notification_list_view_unittest.cc",
     "system/overview/overview_button_tray_unittest.cc",
     "system/palette/mock_palette_tool_delegate.cc",
     "system/palette/mock_palette_tool_delegate.h",
@@ -3249,6 +3254,8 @@
     "//chromeos/ash/services/assistant/public/cpp",
     "//chromeos/ash/services/assistant/public/mojom",
     "//chromeos/ash/services/bluetooth_config:test_support",
+    "//chromeos/ash/services/federated/public/cpp",
+    "//chromeos/ash/services/federated/public/cpp:test_support",
     "//chromeos/ash/services/multidevice_setup/public/cpp:test_support",
     "//chromeos/ash/services/multidevice_setup/public/mojom",
     "//chromeos/ash/services/nearby/public/cpp",
diff --git a/ash/DEPS b/ash/DEPS
index 049a7e9..4f206007 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -91,6 +91,7 @@
   "+chromeos/ash/services/assistant/public/cpp" ,
   "+chromeos/ash/services/assistant/test_support",
   "+chromeos/ash/services/bluetooth_config",
+  "+chromeos/ash/services/federated/public",
   "+chromeos/ash/services/libassistant/public",
   "+chromeos/ash/services/nearby/public",
   "+chromeos/ash/services/multidevice_setup/public",
diff --git a/ash/app_list/app_list_presenter_impl.cc b/ash/app_list/app_list_presenter_impl.cc
index 8251a22..4582a6f 100644
--- a/ash/app_list/app_list_presenter_impl.cc
+++ b/ash/app_list/app_list_presenter_impl.cc
@@ -87,19 +87,6 @@
   }
 }
 
-// Whether the shelf is oriented on the side, not on the bottom.
-bool IsSideShelf(Shelf* shelf) {
-  switch (shelf->alignment()) {
-    case ShelfAlignment::kBottom:
-    case ShelfAlignment::kBottomLocked:
-      return false;
-    case ShelfAlignment::kLeft:
-    case ShelfAlignment::kRight:
-      return true;
-  }
-  return false;
-}
-
 // Whether the shelf background type indicates that shelf has rounded corners.
 bool IsShelfBackgroundTypeWithRoundedCorners(
     ShelfBackgroundType background_type) {
@@ -319,7 +306,7 @@
   }
   layer->SetOpacity(initial_opacity);
 
-  view_->Show(preferred_state, IsSideShelf(shelf));
+  view_->Show(preferred_state);
 
   if (app_list_features::IsAnimateScaleOnTabletModeTransitionEnabled()) {
     // If there was no aborted dismiss animation before - set the initial value,
diff --git a/ash/app_list/model/search/search_result.h b/ash/app_list/model/search/search_result.h
index 583adf7..bc67797 100644
--- a/ash/app_list/model/search/search_result.h
+++ b/ash/app_list/model/search/search_result.h
@@ -40,7 +40,6 @@
   using Tags = ash::SearchResultTags;
   using Action = ash::SearchResultAction;
   using Actions = ash::SearchResultActions;
-  using DisplayIndex = ash::SearchResultDisplayIndex;
   using IconInfo = ash::SearchResultIconInfo;
   using IconShape = ash::SearchResultIconShape;
   using TextItem = ash::SearchResultTextItem;
@@ -144,16 +143,6 @@
     metadata_->metrics_type = metrics_type;
   }
 
-  DisplayIndex display_index() const { return metadata_->display_index; }
-  void set_display_index(DisplayIndex display_index) {
-    metadata_->display_index = display_index;
-  }
-
-  float position_priority() const { return metadata_->position_priority; }
-  void set_position_priority(float position_priority) {
-    metadata_->position_priority = position_priority;
-  }
-
   const Actions& actions() const { return metadata_->actions; }
   void SetActions(const Actions& sets);
 
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index de549e9..09c1b67 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -497,10 +497,9 @@
   GetViewAccessibility().OverridePreviousFocus(search_box_widget);
 }
 
-void AppListView::Show(AppListViewState preferred_state, bool is_side_shelf) {
+void AppListView::Show(AppListViewState preferred_state) {
   if (!time_shown_.has_value())
     time_shown_ = base::Time::Now();
-  is_side_shelf_ = is_side_shelf;
 
   AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
   AddAccelerator(ui::Accelerator(ui::VKEY_BROWSER_BACK, ui::EF_NONE));
@@ -638,11 +637,6 @@
 }
 
 gfx::Insets AppListView::GetMainViewInsetsForShelf() const {
-  if (is_side_shelf()) {
-    // Set both horizontal insets so the app list remains centered on the
-    // screen.
-    return gfx::Insets::VH(0, delegate_->GetShelfSize());
-  }
   return gfx::Insets::TLBR(0, 0, delegate_->GetShelfSize(), 0);
 }
 
@@ -725,15 +719,7 @@
   // Do not update the contents view state on closing.
   if (target_state != AppListViewState::kClosed) {
     app_list_main_view_->contents_view()->SetActiveState(
-        AppListState::kStateApps, !is_side_shelf_);
-  }
-
-  if (target_state == AppListViewState::kClosed && is_side_shelf_ &&
-      !app_list_features::IsAnimateScaleOnTabletModeTransitionEnabled()) {
-    // Reset the search box to be shown again. This is done after the
-    // animation is complete normally, but there is no animation when
-    // |is_side_shelf_|.
-    search_box_view_->ClearSearchAndDeactivateSearchBox();
+        AppListState::kStateApps, /*animate=*/true);
   }
 }
 
@@ -1052,8 +1038,8 @@
 
 base::TimeDelta AppListView::GetStateTransitionAnimationDuration(
     AppListViewState target_state) {
-  if (is_side_shelf_ || (target_state == AppListViewState::kClosed &&
-                         delegate_->ShouldDismissImmediately())) {
+  if (target_state == AppListViewState::kClosed &&
+      delegate_->ShouldDismissImmediately()) {
     return base::Milliseconds(kAppListAnimationDurationImmediateMs);
   }
 
@@ -1082,14 +1068,6 @@
 
 void AppListView::ApplyBoundsAnimation(AppListViewState target_state,
                                        base::TimeDelta duration_ms) {
-  if (is_side_shelf_) {
-    // There is no animation in side shelf.
-    // Mark the state transition as complete directly, as no animations that
-    // for `state_transition_notifier_` to observe are run in this case.
-    state_transition_notifier_->SetTransitionDone();
-    return;
-  }
-
   gfx::Rect target_bounds = GetPreferredWidgetBoundsForState(target_state);
 
   // When closing the view should animate to the shelf bounds. The workspace
@@ -1402,12 +1380,8 @@
     case AppListViewState::kClosed:
       if (app_list_features::IsAnimateScaleOnTabletModeTransitionEnabled())
         return fullscreen_height;
-      // Align the widget y with shelf y to avoid flicker in show animation. In
-      // side shelf mode, the widget y is the top of work area because the
-      // widget does not animate.
-      return (is_side_shelf_ ? work_area_bounds.y()
-                             : work_area_bounds.bottom()) -
-             display.bounds().y();
+      // Align the widget y with shelf y to avoid flicker in show animation.
+      return work_area_bounds.bottom() - display.bounds().y();
   }
 }
 
diff --git a/ash/app_list/views/app_list_view.h b/ash/app_list/views/app_list_view.h
index 034bced..3ab64dd5 100644
--- a/ash/app_list/views/app_list_view.h
+++ b/ash/app_list/views/app_list_view.h
@@ -150,10 +150,8 @@
   void InitChildWidget();
 
   // Sets the state of all child views to be re-shown, then shows the view.
-  // |preferred_state| - The initial app list view state. It may be overridden
-  // depending on device state. For example, peeking state is not supported in
-  // tablet mode, or for side shelf.
-  void Show(AppListViewState preferred_state, bool is_side_shelf);
+  // |preferred_state| - The initial app list view state.
+  void Show(AppListViewState preferred_state);
 
   // If |drag_and_drop_host| is not nullptr it will be called upon drag and drop
   // operations outside the application list. This has to be called after
@@ -284,8 +282,6 @@
 
   bool is_tablet_mode() const { return delegate_->IsInTabletMode(); }
 
-  bool is_side_shelf() const { return is_side_shelf_; }
-
   void SetShelfHasRoundedCorners(bool shelf_has_rounded_corners);
 
   bool shelf_has_rounded_corners() const { return shelf_has_rounded_corners_; }
@@ -410,9 +406,6 @@
   // The time the AppListView was requested to be shown. Used for metrics.
   absl::optional<base::Time> time_shown_;
 
-  // Whether the shelf is oriented on the side.
-  bool is_side_shelf_ = false;
-
   // Whether the shelf has rounded corners.
   bool shelf_has_rounded_corners_ = false;
 
diff --git a/ash/app_list/views/app_list_view_unittest.cc b/ash/app_list/views/app_list_view_unittest.cc
index c1ee1d5..6f56049 100644
--- a/ash/app_list/views/app_list_view_unittest.cc
+++ b/ash/app_list/views/app_list_view_unittest.cc
@@ -221,9 +221,7 @@
   }
 
  protected:
-  void Show(bool is_side_shelf = false) {
-    view_->Show(AppListViewState::kFullscreenAllApps, is_side_shelf);
-  }
+  void Show() { view_->Show(AppListViewState::kFullscreenAllApps); }
 
   void Initialize(bool is_tablet_mode) {
     delegate_ = std::make_unique<AppListTestViewDelegate>();
@@ -596,9 +594,7 @@
     view_->SetState(state);
   }
 
-  void Show() {
-    view_->Show(AppListViewState::kFullscreenAllApps, /*is_side_shelf=*/false);
-  }
+  void Show() { view_->Show(AppListViewState::kFullscreenAllApps); }
 
   SearchResultListView* GetSearchResultListView() {
     return contents_view()
@@ -1146,8 +1142,7 @@
 
 // Tests that search box becomes focused when it is activated.
 TEST_F(AppListViewFocusTest, SetFocusOnSearchboxWhenActivated) {
-  app_list_view()->Show(AppListViewState::kFullscreenAllApps,
-                        /*is_side_shelf=*/false);
+  app_list_view()->Show(AppListViewState::kFullscreenAllApps);
 
   // Press tab several times to move focus out of the search box.
   SimulateKeyPress(ui::VKEY_TAB, false);
@@ -1363,7 +1358,7 @@
   // Put into fullscreen using side-shelf.
   Initialize(/*is_tablet_mode=*/true);
 
-  Show(true /*is_side_shelf*/);
+  Show();
   SetTextInSearchBox(u"kitty");
   view_->AcceleratorPressed(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
 
diff --git a/ash/app_list/views/contents_view.cc b/ash/app_list/views/contents_view.cc
index 7e4acc9..d63209e 100644
--- a/ash/app_list/views/contents_view.cc
+++ b/ash/app_list/views/contents_view.cc
@@ -147,11 +147,11 @@
   if (assistant_page_view_)
     assistant_page_view_->SetVisible(false);
   SetActiveState(AppListState::kStateApps, /*animate=*/false);
-  // In side shelf, the opacity of the contents is not animated so set it to the
-  // final state. In tablet mode, opacity of the elements is controlled by the
+
+  // In tablet mode, opacity of the elements is controlled by the
   // AppListControllerImpl which expects these elements to be opaque.
   // Otherwise the contents animate from 0 to 1 so set the initial opacity to 0.
-  if (app_list_view_->is_side_shelf() || app_list_view_->is_tablet_mode()) {
+  if (app_list_view_->is_tablet_mode()) {
     AnimateToViewState(AppListViewState::kFullscreenAllApps, base::TimeDelta());
   } else if (!last_target_view_state_.has_value() ||
              *last_target_view_state_ != AppListViewState::kClosed) {
diff --git a/ash/app_list/views/continue_task_container_view.cc b/ash/app_list/views/continue_task_container_view.cc
index dd63819..43d6d36b 100644
--- a/ash/app_list/views/continue_task_container_view.cc
+++ b/ash/app_list/views/continue_task_container_view.cc
@@ -45,17 +45,6 @@
 constexpr int kRowSpacing = 8;
 constexpr size_t kMaxFilesForContinueSection = 4;
 
-struct CompareByDisplayIndexAndPositionPriority {
-  bool operator()(const SearchResult* result1,
-                  const SearchResult* result2) const {
-    SearchResultDisplayIndex index1 = result1->display_index();
-    SearchResultDisplayIndex index2 = result2->display_index();
-    if (index1 != index2)
-      return index1 < index2;
-    return result1->position_priority() > result2->position_priority();
-  }
-};
-
 std::vector<SearchResult*> GetTasksResultsForContinueSection(
     SearchModel::SearchResults* results) {
   auto continue_filter = [](const SearchResult& r) -> bool {
@@ -66,9 +55,6 @@
       results, base::BindRepeating(continue_filter),
       /*max_results=*/4);
 
-  std::sort(continue_results.begin(), continue_results.end(),
-            CompareByDisplayIndexAndPositionPriority());
-
   return continue_results;
 }
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index cf179d8..2fdf877 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -3013,7 +3013,7 @@
         IME menu button
       </message>
       <message name="IDS_ASH_SELECT_TO_SPEAK_TRAY_ACCESSIBLE_NAME" desc="The accessible text for the select-to-speak menu icon in the status tray.">
-        Select-to-Speak button
+        Select-to-speak button
       </message>
       <message name="IDS_ASH_AUTOCLICK_TRAY_ACCESSIBLE_NAME" desc="The accessible text for the automatic clicks menu icon in the status tray.">
         Automatic clicks button
diff --git a/ash/ash_strings_grd/IDS_ASH_SELECT_TO_SPEAK_TRAY_ACCESSIBLE_NAME.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SELECT_TO_SPEAK_TRAY_ACCESSIBLE_NAME.png.sha1
new file mode 100644
index 0000000..984cecb
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SELECT_TO_SPEAK_TRAY_ACCESSIBLE_NAME.png.sha1
@@ -0,0 +1 @@
+1184c16c08d46b87ecf556b255497b912bce2bc4
\ No newline at end of file
diff --git a/ash/components/arc/arc_prefs.cc b/ash/components/arc/arc_prefs.cc
index bcc55ea0..cab9ee5 100644
--- a/ash/components/arc/arc_prefs.cc
+++ b/ash/components/arc/arc_prefs.cc
@@ -142,6 +142,10 @@
 // A preferece to keep ARC snapshot related info in dictionary.
 const char kArcSnapshotInfo[] = "arc.snapshot";
 
+// A preference to keep track of whether or not Android WebView was used in the
+// current ARC session.
+const char kWebViewProcessStarted[] = "arc.webview.started";
+
 void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
   // Sorted in lexicographical order.
   registry->RegisterStringPref(kArcSerialNumberSalt, std::string());
@@ -151,6 +155,7 @@
 
   registry->RegisterIntegerPref(kAnrPendingCount, 0);
   registry->RegisterTimeDeltaPref(kAnrPendingDuration, base::TimeDelta());
+  registry->RegisterBooleanPref(kWebViewProcessStarted, false);
 }
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
diff --git a/ash/components/arc/arc_prefs.h b/ash/components/arc/arc_prefs.h
index 06ce82c..b04c5e2 100644
--- a/ash/components/arc/arc_prefs.h
+++ b/ash/components/arc/arc_prefs.h
@@ -53,6 +53,7 @@
 ARC_EXPORT extern const char kArcSnapshotHours[];
 ARC_EXPORT extern const char kArcSnapshotInfo[];
 ARC_EXPORT extern const char kStabilityMetrics[];
+ARC_EXPORT extern const char kWebViewProcessStarted[];
 
 void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
 void RegisterProfilePrefs(PrefRegistrySimple* registry);
diff --git a/ash/components/arc/metrics/arc_metrics_service.cc b/ash/components/arc/metrics/arc_metrics_service.cc
index a6fa57d..15fea7fd 100644
--- a/ash/components/arc/metrics/arc_metrics_service.cc
+++ b/ash/components/arc/metrics/arc_metrics_service.cc
@@ -30,6 +30,7 @@
 #include "chromeos/dbus/power_manager/idle.pb.h"
 #include "components/exo/wm_helper.h"
 #include "components/metrics/psi_memory_parser.h"
+#include "components/prefs/pref_service.h"
 #include "components/user_prefs/user_prefs.h"
 #include "content/public/browser/browser_context.h"
 #include "ui/events/ozone/gamepad/gamepad_provider_ozone.h"
@@ -870,6 +871,12 @@
       kUmaDataSizeInKiloBytesMin, kUmaDataSizeInKiloBytesMax, kUmaNumBuckets);
 }
 
+void ArcMetricsService::ReportWebViewProcessStarted() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(prefs_);
+  prefs_->SetBoolean(prefs::kWebViewProcessStarted, true);
+}
+
 void ArcMetricsService::OnWindowActivated(
     wm::ActivationChangeObserver::ActivationReason reason,
     aura::Window* gained_active,
@@ -925,8 +932,14 @@
 }
 
 void ArcMetricsService::OnArcSessionStopped() {
+  DCHECK(prefs_);
+
   boot_type_ = mojom::BootType::UNKNOWN;
   metrics_anr_.reset();
+
+  base::UmaHistogramBoolean("Arc.Session.HasWebViewUsage",
+                            prefs_->GetBoolean(prefs::kWebViewProcessStarted));
+  prefs_->SetBoolean(prefs::kWebViewProcessStarted, false);
 }
 
 void ArcMetricsService::AddAppKillObserver(AppKillObserver* obs) {
diff --git a/ash/components/arc/metrics/arc_metrics_service.h b/ash/components/arc/metrics/arc_metrics_service.h
index e074e50..0489792f 100644
--- a/ash/components/arc/metrics/arc_metrics_service.h
+++ b/ash/components/arc/metrics/arc_metrics_service.h
@@ -171,6 +171,7 @@
       uint32_t number_of_directories,
       uint32_t number_of_non_directories,
       uint32_t size_in_kilobytes) override;
+  void ReportWebViewProcessStarted() override;
 
   // wm::ActivationChangeObserver overrides.
   // Records to UMA when a user has interacted with an ARC app window.
diff --git a/ash/components/arc/metrics/arc_metrics_service_unittest.cc b/ash/components/arc/metrics/arc_metrics_service_unittest.cc
index 503283c4..f751b79d 100644
--- a/ash/components/arc/metrics/arc_metrics_service_unittest.cc
+++ b/ash/components/arc/metrics/arc_metrics_service_unittest.cc
@@ -425,6 +425,43 @@
   service()->RemoveBootTypeObserver(&observer);
 }
 
+TEST_F(ArcMetricsServiceTest, ReportWebViewProcessStarted_NoUsageReported) {
+  base::HistogramTester tester;
+
+  service()->OnArcSessionStopped();
+
+  tester.ExpectUniqueSample("Arc.Session.HasWebViewUsage",
+                            static_cast<base::HistogramBase::Sample>(0), 1);
+}
+
+TEST_F(ArcMetricsServiceTest, ReportWebViewProcessStarted_OneUsageReported) {
+  base::HistogramTester tester;
+
+  service()->ReportWebViewProcessStarted();
+  service()->OnArcSessionStopped();
+
+  tester.ExpectUniqueSample("Arc.Session.HasWebViewUsage",
+                            static_cast<base::HistogramBase::Sample>(1), 1);
+}
+
+TEST_F(ArcMetricsServiceTest, ReportWebViewProcessStarted_SomeUsageReported) {
+  base::HistogramTester tester;
+
+  // 3 sessions with webview reported in 2 sessions.
+  service()->ReportWebViewProcessStarted();
+  service()->OnArcSessionStopped();
+
+  service()->OnArcSessionStopped();
+
+  service()->ReportWebViewProcessStarted();
+  service()->OnArcSessionStopped();
+
+  tester.ExpectBucketCount("Arc.Session.HasWebViewUsage",
+                           static_cast<base::HistogramBase::Sample>(0), 1);
+  tester.ExpectBucketCount("Arc.Session.HasWebViewUsage",
+                           static_cast<base::HistogramBase::Sample>(1), 2);
+}
+
 class ArcVmArcMetricsServiceTest
     : public ArcMetricsServiceTest,
       public testing::WithParamInterface<
diff --git a/ash/components/arc/mojom/metrics.mojom b/ash/components/arc/mojom/metrics.mojom
index 1edbf999..09d8b8bb 100644
--- a/ash/components/arc/mojom/metrics.mojom
+++ b/ash/components/arc/mojom/metrics.mojom
@@ -1,7 +1,7 @@
 // Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-// Next MinVersion: 27
+// Next MinVersion: 28
 module arc.mojom;
 
 import "mojo/public/mojom/base/time.mojom";
@@ -366,7 +366,7 @@
 };
 
 // Deprecated method IDs: 25
-// Next method ID: 31
+// Next method ID: 32
 interface MetricsHost {
   // Reports boot progress events from ARC instance.
   ReportBootProgress@0(array<BootProgressEvent> events,
@@ -500,6 +500,9 @@
   [MinVersion=25] ReportTotalFileStatsOfAndroidDataSubdir@29(
       AndroidDataSubdirectory target, uint32 number_of_directories,
       uint32 number_of_non_directories, uint32 size_in_kilobytes);
+
+  // Reports first WebView started event to collect WebView usage in ARC.
+  [MinVersion=27] ReportWebViewProcessStarted@31();
 };
 
 // Deprecated method IDs: 0
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 0cc28026..7a7e66f 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -576,7 +576,7 @@
 // selection flow triggered by the stylus long press action.
 BASE_FEATURE(kDeprecateAssistantStylusFeatures,
              "DeprecateAssistantStylusFeatures",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables or disables Sync for desk templates on ChromeOS.
 BASE_FEATURE(kDeskTemplateSync,
@@ -889,6 +889,21 @@
              "FastPairSavedDevicesStrictOptIn",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Enables the federated service. If enabled, launches federated service when
+// user first login.
+BASE_FEATURE(kFederatedService,
+             "FederatedService",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+// Enables the federated service to schedule tasks. If disabled, federated
+// service works as a simple example receiver and storage.
+// This is useful when we want to disable the federated tasks only and allow the
+// customers to report examples, because e.g. the tensorflow graphs cost too
+// much resources while example storage is supposed to be cheap and safe.
+BASE_FEATURE(kFederatedServiceScheduleTasks,
+             "FederatedServiceScheduleTasks",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Enables experimental UI features in Files app.
 BASE_FEATURE(kFilesAppExperimental,
              "FilesAppExperimental",
@@ -2137,6 +2152,11 @@
 const base::FeatureParam<int> kDeviceForceScheduledRebootMaxDelay{
     &kDeviceForceScheduledReboot, "max-delay-in-seconds", 120};
 
+// Enables settings to be split per device.
+BASE_FEATURE(kInputDeviceSettingsSplit,
+             "InputDeviceSettingsSplit",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Enables or disables whether to store UMA logs per-user and whether metrics
 // consent is per-user.
 BASE_FEATURE(kPerUserMetrics,
@@ -2385,6 +2405,10 @@
   return base::FeatureList::IsEnabled(kDeskTemplateSync);
 }
 
+bool IsInputDeviceSettingsSplitEnabled() {
+  return base::FeatureList::IsEnabled(kInputDeviceSettingsSplit);
+}
+
 bool IsDisplayAlignmentAssistanceEnabled() {
   return base::FeatureList::IsEnabled(kDisplayAlignAssist);
 }
@@ -2476,6 +2500,15 @@
   return base::FeatureList::IsEnabled(kFastPairSavedDevicesStrictOptIn);
 }
 
+bool IsFederatedServiceEnabled() {
+  return base::FeatureList::IsEnabled(kFederatedService);
+}
+
+bool IsFederatedServiceScheduleTasksEnabled() {
+  return IsFederatedServiceEnabled() &&
+         base::FeatureList::IsEnabled(kFederatedServiceScheduleTasks);
+}
+
 bool IsFileManagerFuseBoxEnabled() {
   return base::FeatureList::IsEnabled(kFuseBox);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index ebaf45b5..b268fc3 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -183,6 +183,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::FeatureParam<int> kDeviceForceScheduledRebootMaxDelay;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+BASE_DECLARE_FEATURE(kInputDeviceSettingsSplit);
+COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kDiacriticsOnPhysicalKeyboardLongpress);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kDisableCryptAuthV1DeviceSync);
@@ -269,6 +271,9 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kFastPairSavedDevices);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kFastPairSavedDevicesStrictOptIn);
+COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kFederatedService);
+COMPONENT_EXPORT(ASH_CONSTANTS)
+BASE_DECLARE_FEATURE(kFederatedServiceScheduleTasks);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kFilesAppExperimental);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kFilesExtractArchive);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kFilesInlineSyncStatus);
@@ -653,6 +658,7 @@
 bool IsDeprecateAssistantStylusFeaturesEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDesksCloseAllEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDeskTemplateSyncEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsInputDeviceSettingsSplitEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDisplayAlignmentAssistanceEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDragUnpinnedAppToPinEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDragWindowToNewDeskEnabled();
@@ -676,6 +682,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFastPairSubsequentPairingUXEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFastPairSavedDevicesEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFastPairSavedDevicesStrictOptInEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFederatedServiceEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFederatedServiceScheduleTasksEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFileManagerFuseBoxEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFileManagerFuseBoxDebugEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFilesWebDriveOfficeEnabled();
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index 92224ba2..082da6c 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -30,6 +30,7 @@
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "base/command_line.h"
+#include "base/containers/flat_map.h"
 #include "base/format_macros.h"
 #include "base/numerics/math_constants.h"
 #include "base/run_loop.h"
@@ -106,7 +107,16 @@
 
   const vector<display::Display>& changed() const { return changed_; }
   const vector<display::Display>& added() const { return added_; }
-  uint32_t changed_metrics() const { return changed_metrics_; }
+  uint32_t changed_metrics() const {
+    uint32_t changed_metrics = 0;
+    for (const auto& display_metrics : changed_metrics_) {
+      changed_metrics |= display_metrics.second;
+    }
+    return changed_metrics;
+  }
+  uint32_t changed_metrics(int64_t display_id) const {
+    return changed_metrics_.at(display_id);
+  }
 
   string GetCountSummary() const {
     return StringPrintf("%" PRIuS " %" PRIuS " %" PRIuS " %" PRIuS " %" PRIuS,
@@ -118,7 +128,7 @@
     changed_.clear();
     added_.clear();
     removed_count_ = will_process_count_ = did_process_count_ = 0U;
-    changed_metrics_ = 0U;
+    changed_metrics_.clear();
     root_window_destroyed_ = false;
   }
 
@@ -147,7 +157,8 @@
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t changed_metrics) override {
     changed_.push_back(display);
-    changed_metrics_ |= changed_metrics;
+    if (!changed_metrics_.try_emplace(display.id(), changed_metrics).second)
+      changed_metrics_[display.id()] |= changed_metrics;
   }
   void OnDisplayAdded(const display::Display& new_display) override {
     added_.push_back(new_display);
@@ -197,7 +208,7 @@
   size_t will_process_count_ = 0u;
   size_t did_process_count_ = 0u;
   bool root_window_destroyed_ = false;
-  uint32_t changed_metrics_ = 0u;
+  base::flat_map<int64_t, uint32_t> changed_metrics_;
   bool check_root_window_on_destruction_ = true;
 
   absl::optional<display::ScopedDisplayObserver> display_observer_;
@@ -2362,7 +2373,30 @@
                 .ToString());
 }
 
-TEST_F(DisplayManagerTest, NotifyPrimaryChange) {
+TEST_F(DisplayManagerTest, NotifyPrimaryChangeSwapped) {
+  UpdateDisplay("500x400,500x400");
+  int64_t old_primary_id = GetPrimaryDisplay().id();
+  int64_t new_primary_id = GetSecondaryDisplay().id();
+  SwapPrimaryDisplay();
+
+  // Old primary display.
+  EXPECT_TRUE(changed_metrics(old_primary_id) &
+              display::DisplayObserver::DISPLAY_METRIC_BOUNDS);
+  EXPECT_TRUE(changed_metrics(old_primary_id) &
+              display::DisplayObserver::DISPLAY_METRIC_WORK_AREA);
+  EXPECT_FALSE(changed_metrics(old_primary_id) &
+               display::DisplayObserver::DISPLAY_METRIC_PRIMARY);
+
+  // New primary display.
+  EXPECT_TRUE(changed_metrics(new_primary_id) &
+              display::DisplayObserver::DISPLAY_METRIC_BOUNDS);
+  EXPECT_TRUE(changed_metrics(new_primary_id) &
+              display::DisplayObserver::DISPLAY_METRIC_WORK_AREA);
+  EXPECT_TRUE(changed_metrics(new_primary_id) &
+              display::DisplayObserver::DISPLAY_METRIC_PRIMARY);
+}
+
+TEST_F(DisplayManagerTest, NotifyPrimaryChangeDock) {
   UpdateDisplay("500x400,500x400");
   SwapPrimaryDisplay();
   reset();
diff --git a/ash/public/cpp/app_list/app_list_notifier.h b/ash/public/cpp/app_list/app_list_notifier.h
index 38722dd..3dd040b 100644
--- a/ash/public/cpp/app_list/app_list_notifier.h
+++ b/ash/public/cpp/app_list/app_list_notifier.h
@@ -54,7 +54,7 @@
 
     // Called when the |location| UI view displayed |results|, but the user
     // launched a result in a different UI view. This can only happen when
-    // |location| is kList or kTile.
+    // |location| is kContinue or kRecentApps.
     virtual void OnIgnore(Location location,
                           const std::vector<Result>& results,
                           const std::u16string& query) {}
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h
index eec7192..67b7ff4a 100644
--- a/ash/public/cpp/app_list/app_list_types.h
+++ b/ash/public/cpp/app_list/app_list_types.h
@@ -429,17 +429,6 @@
   kLast,  // Don't use over IPC
 };
 
-// Which index in the UI container should the result be placed in.
-enum SearchResultDisplayIndex {
-  kFirstIndex,
-  kSecondIndex,
-  kThirdIndex,
-  kFourthIndex,
-  kFifthIndex,
-  kSixthIndex,
-  kUndefined,
-};
-
 // Actions for search results. These map to the buttons beside some search
 // results, and do not include the launching of the result itself.
 // TODO(crbug.com/1263751): Currently these are only relevant to omnibox
@@ -680,13 +669,6 @@
   // Which UI container(s) the result should be displayed in.
   SearchResultDisplayType display_type = SearchResultDisplayType::kList;
 
-  // Which index in the UI container should the result be placed in.
-  SearchResultDisplayIndex display_index = SearchResultDisplayIndex::kUndefined;
-
-  // A score to settle conflicts between two apps with the same requested
-  // |display_index|.
-  float position_priority = 0.0f;
-
   // A score to determine the result display order.
   double display_score = 0;
 
diff --git a/ash/public/cpp/test/shell_test_api.h b/ash/public/cpp/test/shell_test_api.h
index 13a0b9da..b09f7a9 100644
--- a/ash/public/cpp/test/shell_test_api.h
+++ b/ash/public/cpp/test/shell_test_api.h
@@ -5,12 +5,8 @@
 #ifndef ASH_PUBLIC_CPP_TEST_SHELL_TEST_API_H_
 #define ASH_PUBLIC_CPP_TEST_SHELL_TEST_API_H_
 
-#include <memory>
-#include <vector>
-
 #include "ash/ash_export.h"
 #include "ash/public/cpp/overview_test_api.h"
-#include "base/callback_forward.h"
 
 namespace aura {
 class Window;
@@ -25,11 +21,9 @@
 }
 
 namespace ash {
-enum class AppListViewState;
 class DragDropController;
 class MessageCenterController;
 class NativeCursorManagerAsh;
-class PaginationModel;
 class PowerPrefs;
 class ScreenPositionController;
 class Shell;
@@ -94,41 +88,11 @@
   // of on a device.
   void AddRemoveDisplay();
 
-  // Runs the callback when the WindowTreeHost of the primary display is no
-  // longer holding pointer events. See
-  // |aura::WindowTreeHost::holding_pointer_moves_| for details.
-  void WaitForNoPointerHoldLock();
-
-  // Runs the callback when the compositor of the primary display has presented
-  // a frame on screen.
-  void WaitForNextFrame(base::OnceClosure closure);
-
   // Runs the callback when the overview state becomes |state|.
   void WaitForOverviewAnimationState(OverviewAnimationState state);
 
-  // Runs the callback when the launcher state becomes |state| after
-  // state transition animation.
-  void WaitForLauncherAnimationState(AppListViewState state);
-
   void WaitForWindowFinishAnimating(aura::Window* window);
 
-  // Creates a closure that, when run, starts waiter for the window's current
-  // animator to finish animating.
-  // It can be used to wait for window animations when the window layer is
-  // recreated while the animation is set up (as is the case for window hide
-  // animations).
-  // Example usage:
-  //   base::OnceClosure waiter =
-  //   CreateWaiterForFinishingWindowAnimation(window);
-  //   aura::WindowState::Get(window)->Minimize();
-  //   std::move(waiter).Run();
-  base::OnceClosure CreateWaiterForFinishingWindowAnimation(
-      aura::Window* window);
-
-  // Returns the pagination model of the currently visible app-list view.
-  // It returns nullptr when app-list is not shown.
-  PaginationModel* GetAppListPaginationModel();
-
   // Returns true if the context menu associated with the primary root window is
   // shown.
   bool IsContextMenuShown() const;
diff --git a/ash/shell.cc b/ash/shell.cc
index 4bf8ffad..42fca7a 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -121,6 +121,7 @@
 #include "ash/system/camera/autozoom_controller_impl.h"
 #include "ash/system/caps_lock_notification_controller.h"
 #include "ash/system/diagnostics/diagnostics_log_controller.h"
+#include "ash/system/federated/federated_service_controller.h"
 #include "ash/system/firmware_update/firmware_update_notification_controller.h"
 #include "ash/system/geolocation/geolocation_controller.h"
 #include "ash/system/human_presence/human_presence_orientation_controller.h"
@@ -988,6 +989,9 @@
 
   multi_capture_service_client_.reset();
 
+  // Observes `SessionController` and must be destroyed before it.
+  federated_service_controller_.reset();
+
   UsbguardClient::Shutdown();
 
   // Must be shut down after detachable_base_handler_.
@@ -1485,6 +1489,11 @@
   if (chromeos::wm::features::IsFloatWindowEnabled())
     float_controller_ = std::make_unique<FloatController>();
 
+  if (features::IsFederatedServiceEnabled()) {
+    federated_service_controller_ =
+        std::make_unique<federated::FederatedServiceController>();
+  }
+
   // Injects the factory which fulfills the implementation of the text context
   // menu exclusive to CrOS.
   views::ViewsTextServicesContextMenuChromeos::SetImplFactory(
diff --git a/ash/shell.h b/ash/shell.h
index 7ef0198..0315f34 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -234,6 +234,10 @@
 class DiagnosticsLogController;
 }  // namespace diagnostics
 
+namespace federated {
+class FederatedServiceController;
+}  // namespace federated
+
 namespace quick_pair {
 class Mediator;
 }  // namespace quick_pair
@@ -455,6 +459,10 @@
     return event_transformation_handler_.get();
   }
 
+  federated::FederatedServiceController* federated_service_controller() {
+    return federated_service_controller_.get();
+  }
+
   FirmwareUpdateNotificationController*
   firmware_update_notification_controller() {
     return firmware_update_notification_controller_.get();
@@ -1052,6 +1060,9 @@
 
   std::unique_ptr<MultiCaptureServiceClient> multi_capture_service_client_;
 
+  std::unique_ptr<federated::FederatedServiceController>
+      federated_service_controller_;
+
   std::unique_ptr<quick_pair::Mediator> quick_pair_mediator_;
 
   base::ObserverList<ShellObserver>::Unchecked shell_observers_;
diff --git a/ash/shell_test_api.cc b/ash/shell_test_api.cc
index 44f6d0c1..54c2761 100644
--- a/ash/shell_test_api.cc
+++ b/ash/shell_test_api.cc
@@ -5,14 +5,10 @@
 #include "ash/public/cpp/test/shell_test_api.h"
 
 #include <memory>
-#include <utility>
 
 #include "ash/accelerators/accelerator_commands.h"
 #include "ash/accelerators/accelerator_controller_impl.h"
 #include "ash/accelerometer/accelerometer_reader.h"
-#include "ash/app_list/app_list_controller_impl.h"
-#include "ash/app_list/app_list_presenter_impl.h"
-#include "ash/app_list/views/app_list_view.h"
 #include "ash/hud_display/hud_display.h"
 #include "ash/keyboard/keyboard_controller_impl.h"
 #include "ash/public/cpp/autotest_private_api_utils.h"
@@ -30,9 +26,7 @@
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "components/prefs/testing_pref_service.h"
-#include "ui/aura/window_tree_host.h"
 #include "ui/compositor/compositor.h"
-#include "ui/compositor/compositor_observer.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/layer_animator.h"
@@ -43,43 +37,6 @@
 namespace ash {
 namespace {
 
-// Wait for a WindowTreeHost to no longer be holding pointer events.
-class PointerMoveLoopWaiter : public ui::CompositorObserver {
- public:
-  explicit PointerMoveLoopWaiter(aura::WindowTreeHost* window_tree_host)
-      : window_tree_host_(window_tree_host) {
-    window_tree_host_->compositor()->AddObserver(this);
-  }
-
-  PointerMoveLoopWaiter(const PointerMoveLoopWaiter&) = delete;
-  PointerMoveLoopWaiter& operator=(const PointerMoveLoopWaiter&) = delete;
-
-  ~PointerMoveLoopWaiter() override {
-    window_tree_host_->compositor()->RemoveObserver(this);
-  }
-
-  void Wait() {
-    // Use a while loop as it's possible for releasing the lock to trigger
-    // processing events, which again grabs the lock.
-    while (window_tree_host_->holding_pointer_moves()) {
-      run_loop_ = std::make_unique<base::RunLoop>(
-          base::RunLoop::Type::kNestableTasksAllowed);
-      run_loop_->Run();
-      run_loop_.reset();
-    }
-  }
-
-  // ui::CompositorObserver:
-  void OnCompositingEnded(ui::Compositor* compositor) override {
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-
- private:
-  aura::WindowTreeHost* window_tree_host_;
-  std::unique_ptr<base::RunLoop> run_loop_;
-};
-
 class WindowAnimationWaiter : public ui::LayerAnimationObserver {
  public:
   explicit WindowAnimationWaiter(aura::Window* window)
@@ -197,25 +154,6 @@
   shell_->display_manager()->AddRemoveDisplay();
 }
 
-void ShellTestApi::WaitForNoPointerHoldLock() {
-  aura::WindowTreeHost* primary_host =
-      Shell::GetPrimaryRootWindowController()->GetHost();
-  if (primary_host->holding_pointer_moves())
-    PointerMoveLoopWaiter(primary_host).Wait();
-}
-
-void ShellTestApi::WaitForNextFrame(base::OnceClosure closure) {
-  Shell::GetPrimaryRootWindowController()
-      ->GetHost()
-      ->compositor()
-      ->RequestPresentationTimeForNextFrame(base::BindOnce(
-          [](base::OnceClosure closure,
-             const gfx::PresentationFeedback& feedback) {
-            std::move(closure).Run();
-          },
-          std::move(closure)));
-}
-
 void ShellTestApi::WaitForOverviewAnimationState(OverviewAnimationState state) {
   auto* overview_controller = shell_->overview_controller();
   if (state == OverviewAnimationState::kEnterAnimationComplete &&
@@ -238,32 +176,11 @@
   run_loop.Run();
 }
 
-void ShellTestApi::WaitForLauncherAnimationState(
-    AppListViewState target_state) {
-  base::RunLoop run_loop;
-  WaitForLauncherState(target_state, run_loop.QuitWhenIdleClosure());
-  run_loop.Run();
-}
-
 void ShellTestApi::WaitForWindowFinishAnimating(aura::Window* window) {
   WindowAnimationWaiter waiter(window);
   waiter.Wait();
 }
 
-base::OnceClosure ShellTestApi::CreateWaiterForFinishingWindowAnimation(
-    aura::Window* window) {
-  auto waiter = std::make_unique<WindowAnimationWaiter>(window);
-  return base::BindOnce(&WindowAnimationWaiter::Wait, std::move(waiter));
-}
-
-PaginationModel* ShellTestApi::GetAppListPaginationModel() {
-  AppListView* view =
-      Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView();
-  if (!view)
-    return nullptr;
-  return view->GetAppsPaginationModel();
-}
-
 bool ShellTestApi::IsContextMenuShown() const {
   return Shell::GetPrimaryRootWindowController()->IsContextMenuShown();
 }
diff --git a/ash/system/federated/OWNERS b/ash/system/federated/OWNERS
new file mode 100644
index 0000000..5455ea1
--- /dev/null
+++ b/ash/system/federated/OWNERS
@@ -0,0 +1,2 @@
+alanlxl@chromium.org
+amoylan@chromium.org
diff --git a/ash/system/federated/federated_service_controller.cc b/ash/system/federated/federated_service_controller.cc
new file mode 100644
index 0000000..1193a24
--- /dev/null
+++ b/ash/system/federated/federated_service_controller.cc
@@ -0,0 +1,83 @@
+// 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/system/federated/federated_service_controller.h"
+
+#include "ash/constants/ash_features.h"
+#include "ash/login_status.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#include "base/i18n/timezone.h"
+#include "chromeos/ash/services/federated/public/cpp/federated_example_util.h"
+#include "chromeos/ash/services/federated/public/cpp/service_connection.h"
+#include "components/user_manager/user_type.h"
+
+namespace ash::federated {
+namespace {
+
+chromeos::federated::mojom::ExamplePtr CreateBrellaAnalyticsExamplePtr() {
+  auto example = chromeos::federated::mojom::Example::New();
+  example->features = chromeos::federated::mojom::Features::New();
+  auto& feature_map = example->features->feature;
+  feature_map["timezone_code"] =
+      federated::CreateStringList({base::CountryCodeForCurrentTimezone()});
+  return example;
+}
+
+// Returns whether federated can run for this type of logged-in user.
+bool IsValidPrimaryUserType(const user_manager::UserType user_type) {
+  // Primary user session must have user_type = regular or child (v.s. guest,
+  // public account, kiosk app).
+  return user_type == user_manager::USER_TYPE_REGULAR ||
+         user_type == user_manager::USER_TYPE_CHILD;
+}
+
+}  // namespace
+
+FederatedServiceController::FederatedServiceController() {
+  SessionControllerImpl* session_controller =
+      Shell::Get()->session_controller();
+  DCHECK(session_controller);
+  session_observation_.Observe(session_controller);
+}
+
+FederatedServiceController::~FederatedServiceController() = default;
+
+void FederatedServiceController::OnLoginStatusChanged(
+    LoginStatus login_status) {
+  // Federated service daemon uses cryptohome as example store and we only treat
+  // it available when a proper primary user type has signed in.
+
+  // Actually once `federated_service_` gets bound, even if availability is set
+  // false because of subsequent LoginStatus changes, it keeps bound and it's
+  // safe to call `federated_service_->ReportExample()`. But on the ChromeOS
+  // daemon side it loses a valid ctyptohome hence no valid example storage, all
+  // reported examples are abandoned.
+
+  auto* primary_user_session =
+      Shell::Get()->session_controller()->GetPrimaryUserSession();
+
+  service_available_ =
+      primary_user_session != nullptr &&
+      IsValidPrimaryUserType(primary_user_session->user_info.type);
+
+  if (service_available_ && !federated_service_.is_bound()) {
+    federated::ServiceConnection::GetInstance()->BindReceiver(
+        federated_service_.BindNewPipeAndPassReceiver());
+
+    if (features::IsFederatedServiceScheduleTasksEnabled()) {
+      federated_service_->StartScheduling();
+    }
+
+    // On session first login, reports one example for
+    // "timezone_code_population", a trivial F.A. task for prove-out purpose.
+    if (!reported_) {
+      federated_service_->ReportExample("timezone_code_population",
+                                        CreateBrellaAnalyticsExamplePtr());
+      reported_ = true;
+    }
+  }
+}
+
+}  // namespace ash::federated
diff --git a/ash/system/federated/federated_service_controller.h b/ash/system/federated/federated_service_controller.h
new file mode 100644
index 0000000..7478cce
--- /dev/null
+++ b/ash/system/federated/federated_service_controller.h
@@ -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.
+
+#ifndef ASH_SYSTEM_FEDERATED_FEDERATED_SERVICE_CONTROLLER_H_
+#define ASH_SYSTEM_FEDERATED_FEDERATED_SERVICE_CONTROLLER_H_
+
+#include "ash/ash_export.h"
+#include "ash/public/cpp/session/session_controller.h"
+#include "ash/public/cpp/session/session_observer.h"
+#include "base/scoped_observation.h"
+#include "chromeos/ash/services/federated/public/mojom/federated_service.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace ash::federated {
+
+// FederatedServiceController listens to LoginStatus and invokes federated
+// service when user signs in therefore cryptohome is mounted. After that it
+// keeps listening to the LoginStatus and changes the availability accordingly.
+class ASH_EXPORT FederatedServiceController : public SessionObserver {
+ public:
+  FederatedServiceController();
+  FederatedServiceController(const FederatedServiceController&) = delete;
+  FederatedServiceController& operator=(const FederatedServiceController&) =
+      delete;
+  ~FederatedServiceController() override;
+
+  // SessionObserver:
+  void OnLoginStatusChanged(LoginStatus login_status) override;
+
+  // If false, federated customers reporting examples is a no-op. Federated
+  // service will abandon the example because of no valid cryptohome hence no
+  // example database.
+  // To avoid data loss and meaningless calls, customers should always check
+  // `service_available()` before reporting examples.
+  bool service_available() const { return service_available_; }
+
+ private:
+  base::ScopedObservation<SessionController, SessionObserver>
+      session_observation_{this};
+
+  // A clone of primordial FederatedService interface.
+  mojo::Remote<chromeos::federated::mojom::FederatedService> federated_service_;
+  bool service_available_ = false;
+
+  bool reported_ = false;
+
+  base::WeakPtrFactory<FederatedServiceController> weak_ptr_factory_{this};
+};
+}  // namespace ash::federated
+
+#endif  // ASH_SYSTEM_FEDERATED_FEDERATED_SERVICE_CONTROLLER_H_
diff --git a/ash/system/federated/federated_service_controller_unittest.cc b/ash/system/federated/federated_service_controller_unittest.cc
new file mode 100644
index 0000000..aa32d06
--- /dev/null
+++ b/ash/system/federated/federated_service_controller_unittest.cc
@@ -0,0 +1,97 @@
+// 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/system/federated/federated_service_controller.h"
+
+#include "ash/constants/ash_features.h"
+#include "ash/public/cpp/session/session_types.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/test/scoped_feature_list.h"
+#include "chromeos/ash/services/federated/public/cpp/fake_service_connection.h"
+#include "chromeos/ash/services/federated/public/cpp/service_connection.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash::federated {
+
+class FederatedServiceControllerTestBase : public NoSessionAshTestBase {
+ public:
+  FederatedServiceControllerTestBase()
+      : NoSessionAshTestBase(
+            base::test::TaskEnvironment::TimeSource::MOCK_TIME),
+        scoped_fake_service_connection_for_test_(&fake_service_connection_) {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{features::kFederatedService,
+                              features::kFederatedServiceScheduleTasks},
+        /*disabled_features=*/{});
+  }
+
+  FederatedServiceControllerTestBase(
+      const FederatedServiceControllerTestBase&) = delete;
+  FederatedServiceControllerTestBase& operator=(
+      const FederatedServiceControllerTestBase&) = delete;
+
+  ~FederatedServiceControllerTestBase() override = default;
+
+  void SetUp() override {
+    AshTestBase::SetUp();
+
+    controller_ = Shell::Get()->federated_service_controller();
+  }
+
+ protected:
+  FederatedServiceController* controller_ = nullptr;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  FakeServiceConnectionImpl fake_service_connection_;
+  ScopedFakeServiceConnectionForTest scoped_fake_service_connection_for_test_;
+};
+
+TEST_F(FederatedServiceControllerTestBase, NormalUserLogin) {
+  SimulateUserLogin("user@gmail.com");
+  EXPECT_TRUE(controller_->service_available());
+
+  GetSessionControllerClient()->LockScreen();
+  EXPECT_TRUE(controller_->service_available());
+
+  GetSessionControllerClient()->UnlockScreen();
+  EXPECT_TRUE(controller_->service_available());
+
+  // Signing out means ash-chrome exit and NoSessionAshTestBase does not
+  // simulate exactly what happens in production.
+  // On a real ChromeOS device when the user signs out, ash-chrome exits and
+  // re-starts, hence a brand-new federated_service_controller. On ChromeOS side
+  // the federated_service daemon also quits because of broken mojo connection,
+  // and waits to be re-launched by the new federated_service_controller.
+  GetSessionControllerClient()->RequestSignOut();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(controller_->service_available());
+}
+
+TEST_F(FederatedServiceControllerTestBase, ChildUserLogin) {
+  SimulateUserLogin("user@gmail.com", user_manager::USER_TYPE_CHILD);
+  EXPECT_TRUE(controller_->service_available());
+}
+
+TEST_F(FederatedServiceControllerTestBase, InvalidLoginStatusAndUserType) {
+  SimulateGuestLogin();
+  EXPECT_FALSE(controller_->service_available());
+  ClearLogin();
+
+  SimulateKioskMode(user_manager::USER_TYPE_ARC_KIOSK_APP);
+
+  EXPECT_FALSE(controller_->service_available());
+  ClearLogin();
+
+  SimulateKioskMode(user_manager::USER_TYPE_KIOSK_APP);
+
+  EXPECT_FALSE(controller_->service_available());
+  ClearLogin();
+
+  EXPECT_FALSE(controller_->service_available());
+}
+
+}  // namespace ash::federated
diff --git a/ash/system/message_center/ash_notification_view_unittest.cc b/ash/system/message_center/ash_notification_view_unittest.cc
index 820bc5520..c09fd1d8 100644
--- a/ash/system/message_center/ash_notification_view_unittest.cc
+++ b/ash/system/message_center/ash_notification_view_unittest.cc
@@ -14,8 +14,8 @@
 #include "ash/system/message_center/message_center_style.h"
 #include "ash/system/message_center/metrics_utils.h"
 #include "ash/system/message_center/unified_message_center_bubble.h"
-#include "ash/system/message_center/unified_message_center_view.h"
-#include "ash/system/message_center/unified_message_list_view.h"
+#include "ash/system/notification_center/notification_center_view.h"
+#include "ash/system/notification_center/notification_list_view.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -164,8 +164,8 @@
     return static_cast<AshNotificationView*>(
         GetPrimaryUnifiedSystemTray()
             ->message_center_bubble()
-            ->message_center_view()
-            ->message_list_view()
+            ->notification_center_view()
+            ->notification_list_view()
             ->GetMessageViewForNotificationId(std::string(id)));
   }
 
diff --git a/ash/system/message_center/metrics_utils_unittest.cc b/ash/system/message_center/metrics_utils_unittest.cc
index 0310242..5bd039ab 100644
--- a/ash/system/message_center/metrics_utils_unittest.cc
+++ b/ash/system/message_center/metrics_utils_unittest.cc
@@ -7,8 +7,8 @@
 #include "ash/constants/ash_features.h"
 #include "ash/system/message_center/ash_message_popup_collection.h"
 #include "ash/system/message_center/unified_message_center_bubble.h"
-#include "ash/system/message_center/unified_message_center_view.h"
-#include "ash/system/message_center/unified_message_list_view.h"
+#include "ash/system/notification_center/notification_center_view.h"
+#include "ash/system/notification_center/notification_list_view.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -184,8 +184,8 @@
   views::View* GetNotificationViewFromMessageCenter(const std::string& id) {
     return GetPrimaryUnifiedSystemTray()
         ->message_center_bubble()
-        ->message_center_view()
-        ->message_list_view()
+        ->notification_center_view()
+        ->notification_list_view()
         ->GetMessageViewForNotificationId(id);
   }
 
diff --git a/ash/system/message_center/notification_grouping_controller.cc b/ash/system/message_center/notification_grouping_controller.cc
index 19fff0db..34ba1ec8 100644
--- a/ash/system/message_center/notification_grouping_controller.cc
+++ b/ash/system/message_center/notification_grouping_controller.cc
@@ -9,8 +9,8 @@
 #include "ash/system/message_center/message_center_utils.h"
 #include "ash/system/message_center/metrics_utils.h"
 #include "ash/system/message_center/unified_message_center_bubble.h"
-#include "ash/system/message_center/unified_message_center_view.h"
-#include "ash/system/message_center/unified_message_list_view.h"
+#include "ash/system/notification_center/notification_center_view.h"
+#include "ash/system/notification_center/notification_list_view.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "base/ranges/algorithm.h"
 #include "ui/display/display.h"
@@ -343,11 +343,11 @@
 message_center::NotificationViewController*
 NotificationGroupingController::GetActiveNotificationViewController() {
   if (tray_->IsMessageCenterBubbleShown()) {
-    auto* message_list_view = tray_->message_center_bubble()
-                                  ->message_center_view()
-                                  ->message_list_view();
-    if (message_list_view)
-      return message_list_view;
+    auto* notification_list_view = tray_->message_center_bubble()
+                                       ->notification_center_view()
+                                       ->notification_list_view();
+    if (notification_list_view)
+      return notification_list_view;
   }
   return tray_->GetMessagePopupCollection();
 }
diff --git a/ash/system/message_center/unified_message_center_bubble.cc b/ash/system/message_center/unified_message_center_bubble.cc
index 1c68d3d..24beb130 100644
--- a/ash/system/message_center/unified_message_center_bubble.cc
+++ b/ash/system/message_center/unified_message_center_bubble.cc
@@ -14,7 +14,7 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/system_shadow.h"
 #include "ash/system/message_center/message_center_style.h"
-#include "ash/system/message_center/unified_message_center_view.h"
+#include "ash/system/notification_center/notification_center_view.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_event_filter.h"
 #include "ash/system/tray/tray_utils.h"
@@ -86,14 +86,14 @@
 
   bubble_view_ = new TrayBubbleView(init_params);
 
-  message_center_view_ =
-      bubble_view_->AddChildView(std::make_unique<UnifiedMessageCenterView>(
+  notification_center_view_ =
+      bubble_view_->AddChildView(std::make_unique<NotificationCenterView>(
           nullptr /* parent */, tray->model(), this));
 
   time_to_click_recorder_ =
-      std::make_unique<TimeToClickRecorder>(this, message_center_view_);
+      std::make_unique<TimeToClickRecorder>(this, notification_center_view_);
 
-  message_center_view_->AddObserver(this);
+  notification_center_view_->AddObserver(this);
 }
 
 void UnifiedMessageCenterBubble::ShowBubble() {
@@ -123,7 +123,7 @@
   }
 
   bubble_view_->InitializeAndShowBubble();
-  message_center_view_->Init();
+  notification_center_view_->Init();
   UpdateBubbleState();
 
   tray_->tray_event_filter()->AddBubble(this);
@@ -134,8 +134,8 @@
   if (bubble_widget_) {
     tray_->tray_event_filter()->RemoveBubble(this);
     tray_->bubble()->unified_view()->RemoveObserver(this);
-    CHECK(message_center_view_);
-    message_center_view_->RemoveObserver(this);
+    CHECK(notification_center_view_);
+    notification_center_view_->RemoveObserver(this);
 
     bubble_view_->ResetDelegate();
     bubble_widget_->RemoveObserver(this);
@@ -150,20 +150,20 @@
 }
 
 void UnifiedMessageCenterBubble::CollapseMessageCenter() {
-  if (message_center_view_->collapsed())
+  if (notification_center_view_->collapsed())
     return;
 
-  message_center_view_->SetCollapsed(true /*animate*/);
+  notification_center_view_->SetCollapsed(true /*animate*/);
 }
 
 void UnifiedMessageCenterBubble::ExpandMessageCenter() {
-  if (!message_center_view_->collapsed())
+  if (!notification_center_view_->collapsed())
     return;
 
   if (tray_->IsShowingCalendarView())
     tray_->bubble()->unified_system_tray_controller()->TransitionToMainView(
         /*restore_focus=*/true);
-  message_center_view_->SetExpanded();
+  notification_center_view_->SetExpanded();
   UpdatePosition();
   tray_->EnsureQuickSettingsCollapsed(true /*animate*/);
 }
@@ -171,8 +171,8 @@
 void UnifiedMessageCenterBubble::UpdatePosition() {
   int available_height = CalculateAvailableHeight();
 
-  message_center_view_->SetMaxHeight(available_height);
-  message_center_view_->SetAvailableHeight(available_height);
+  notification_center_view_->SetMaxHeight(available_height);
+  notification_center_view_->SetAvailableHeight(available_height);
 
   if (!tray_->bubble())
     return;
@@ -208,11 +208,11 @@
                     kUnifiedMessageCenterBubbleSpacing);
   bubble_view_->ChangeAnchorRect(anchor_rect);
 
-  message_center_view_->UpdateNotificationBar();
+  notification_center_view_->UpdateNotificationBar();
 
   if (!features::IsNotificationsRefreshEnabled()) {
     bubble_view_->layer()->StackAtTop(border_->layer());
-    border_->layer()->SetBounds(message_center_view_->GetContentsBounds());
+    border_->layer()->SetBounds(notification_center_view_->GetContentsBounds());
   }
 
   if (shadow_) {
@@ -221,12 +221,13 @@
     // than its blur region. To avoid this, we hide the shadow when the message
     // center has no notifications.
     shadow_->GetLayer()->SetVisible(
-        message_center_view_->message_list_view()->GetTotalNotificationCount());
+        notification_center_view_->notification_list_view()
+            ->GetTotalNotificationCount());
   }
 }
 
 void UnifiedMessageCenterBubble::FocusEntered(bool reverse) {
-  message_center_view_->FocusEntered(reverse);
+  notification_center_view_->FocusEntered(reverse);
 }
 
 bool UnifiedMessageCenterBubble::FocusOut(bool reverse) {
@@ -238,12 +239,12 @@
 }
 
 bool UnifiedMessageCenterBubble::IsMessageCenterVisible() {
-  return !!bubble_widget_ && message_center_view_ &&
-         message_center_view_->GetVisible();
+  return !!bubble_widget_ && notification_center_view_ &&
+         notification_center_view_->GetVisible();
 }
 
 bool UnifiedMessageCenterBubble::IsMessageCenterCollapsed() {
-  return message_center_view_->collapsed();
+  return notification_center_view_->collapsed();
 }
 
 TrayBackgroundView* UnifiedMessageCenterBubble::GetTray() const {
@@ -277,7 +278,7 @@
     views::View* starting_view) {
   // Hide the message center widget if the message center is not
   // visible. This is to ensure we do not see an empty bubble.
-  if (observed_view != message_center_view_)
+  if (observed_view != notification_center_view_)
     return;
 
   bubble_view_->UpdateBubble();
@@ -287,7 +288,7 @@
   CHECK_EQ(bubble_widget_, widget);
   tray_->tray_event_filter()->RemoveBubble(this);
   tray_->bubble()->unified_view()->RemoveObserver(this);
-  message_center_view_->RemoveObserver(this);
+  notification_center_view_->RemoveObserver(this);
   bubble_widget_->RemoveObserver(this);
   bubble_widget_ = nullptr;
   shadow_.reset();
@@ -311,15 +312,16 @@
 
 void UnifiedMessageCenterBubble::UpdateBubbleState() {
   if (CalculateAvailableHeight() < kMessageCenterCollapseThreshold &&
-      message_center_view_->message_list_view()->GetTotalNotificationCount()) {
+      notification_center_view_->notification_list_view()
+          ->GetTotalNotificationCount()) {
     if (tray_->IsQuickSettingsExplicitlyExpanded()) {
-      message_center_view_->SetCollapsed(false /*animate*/);
+      notification_center_view_->SetCollapsed(false /*animate*/);
     } else {
-      message_center_view_->SetExpanded();
+      notification_center_view_->SetExpanded();
       tray_->EnsureQuickSettingsCollapsed(false /*animate*/);
     }
-  } else if (message_center_view_->collapsed()) {
-    message_center_view_->SetExpanded();
+  } else if (notification_center_view_->collapsed()) {
+    notification_center_view_->SetExpanded();
   }
 
   UpdatePosition();
diff --git a/ash/system/message_center/unified_message_center_bubble.h b/ash/system/message_center/unified_message_center_bubble.h
index 8bdef3f..8f59310 100644
--- a/ash/system/message_center/unified_message_center_bubble.h
+++ b/ash/system/message_center/unified_message_center_bubble.h
@@ -19,10 +19,10 @@
 namespace ash {
 
 class UnifiedSystemTray;
-class UnifiedMessageCenterView;
+class NotificationCenterView;
 class SystemShadow;
 
-// Manages the bubble that contains UnifiedMessageCenterView.
+// Manages the bubble that contains `NotificationCenterView`.
 // Shows the bubble on `ShowBubble()`, and closes the bubble on the destructor.
 class ASH_EXPORT UnifiedMessageCenterBubble
     : public ScreenLayoutObserver,
@@ -43,9 +43,10 @@
   gfx::Rect GetBoundsInScreen() const;
 
   // We need the code to show the bubble explicitly separated from the
-  // contructor. This is to prevent trigerring the TrayEventFilter from within
-  // the constructor. Doing so can cause a crash when the TrayEventFilter tries
-  // to reference the message center bubble before it is fully instantiated.
+  // constructor. This is to prevent triggering the `TrayEventFilter` from
+  // within the constructor. Doing so can cause a crash when the
+  // `TrayEventFilter` tries to reference the message center bubble before it is
+  // fully instantiated.
   void ShowBubble();
 
   // Collapse the bubble to only have the notification bar visible.
@@ -58,7 +59,7 @@
   // widget whenever the quick settings widget is resized.
   void UpdatePosition();
 
-  // Inform `UnifiedMessageCenterView` of focus being acquired. The oldest
+  // Inform `NotificationCenterView` of focus being acquired. The oldest
   // notification should be focused if `reverse` is `true`. Otherwise, if
   // `reverse` is `false`, the newest notification should be focused.
   void FocusEntered(bool reverse);
@@ -97,8 +98,8 @@
   // ScreenLayoutObserver:
   void OnDisplayConfigurationChanged() override;
 
-  UnifiedMessageCenterView* message_center_view() {
-    return message_center_view_;
+  NotificationCenterView* notification_center_view() {
+    return notification_center_view_;
   }
 
  private:
@@ -113,13 +114,21 @@
   // TimeToClickRecorder::Delegate:
   void RecordTimeToClick() override;
 
+  // Owned by `StatusAreaWidget`.
   UnifiedSystemTray* const tray_;
+
+  // Unowned, the widget is created by a static function in
+  // `BubbleDialogDelegateView`.
+  views::Widget* bubble_widget_ = nullptr;
+
+  // Owned by `bubble_view_`
+  NotificationCenterView* notification_center_view_ = nullptr;
+
+  // Owned by `bubble_widget_`.
+  TrayBubbleView* bubble_view_ = nullptr;
+
   std::unique_ptr<Border> border_;
   std::unique_ptr<SystemShadow> shadow_;
-
-  views::Widget* bubble_widget_ = nullptr;
-  TrayBubbleView* bubble_view_ = nullptr;
-  UnifiedMessageCenterView* message_center_view_ = nullptr;
   std::unique_ptr<TimeToClickRecorder> time_to_click_recorder_;
 };
 
diff --git a/ash/system/message_center/unified_message_center_bubble_unittest.cc b/ash/system/message_center/unified_message_center_bubble_unittest.cc
index 79a21689..3ec023f 100644
--- a/ash/system/message_center/unified_message_center_bubble_unittest.cc
+++ b/ash/system/message_center/unified_message_center_bubble_unittest.cc
@@ -10,7 +10,7 @@
 #include "ash/constants/ash_pref_names.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
-#include "ash/system/message_center/unified_message_center_view.h"
+#include "ash/system/notification_center/notification_center_view.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/system/unified/unified_system_tray_bubble.h"
@@ -80,8 +80,8 @@
     message_center::MessageCenter::Get()->RemoveAllNotifications(
         /*by_user=*/true, MessageCenter::RemoveType::ALL);
     GetMessageCenterBubble()
-        ->message_center_view()
-        ->message_list_view()
+        ->notification_center_view()
+        ->notification_list_view()
         ->ResetBounds();
   }
 
@@ -104,7 +104,7 @@
   }
 
   bool IsMessageCenterCollapsed() {
-    return GetMessageCenterBubble()->message_center_view()->collapsed();
+    return GetMessageCenterBubble()->notification_center_view()->collapsed();
   }
 
   bool IsQuickSettingsCollapsed() {
@@ -146,13 +146,13 @@
 
   views::View* GetFirstMessageCenterFocusable() {
     return GetMessageCenterBubble()
-        ->message_center_view()
+        ->notification_center_view()
         ->GetFirstFocusableChild();
   }
 
   views::View* GetLastMessageCenterFocusable() {
     return GetMessageCenterBubble()
-        ->message_center_view()
+        ->notification_center_view()
         ->GetLastFocusableChild();
   }
 
@@ -345,8 +345,9 @@
     ASSERT_FALSE(GetMessageCenterBubble()->IsMessageCenterCollapsed());
 
     // Add enough notifications so that the scroll bar is visible.
-    while (
-        !GetMessageCenterBubble()->message_center_view()->IsScrollBarVisible())
+    while (!GetMessageCenterBubble()
+                ->notification_center_view()
+                ->IsScrollBarVisible())
       AddNotification();
 
     // The message center bubble should be positioned above the system tray
diff --git a/ash/system/message_center/unified_message_center_view.cc b/ash/system/notification_center/notification_center_view.cc
similarity index 78%
rename from ash/system/message_center/unified_message_center_view.cc
rename to ash/system/notification_center/notification_center_view.cc
index f026ce3..31ef7ea6 100644
--- a/ash/system/message_center/unified_message_center_view.cc
+++ b/ash/system/notification_center/notification_center_view.cc
@@ -1,8 +1,8 @@
-// Copyright 2018 The Chromium Authors
+// 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/system/message_center/unified_message_center_view.h"
+#include "ash/system/notification_center/notification_center_view.h"
 
 #include <algorithm>
 #include <memory>
@@ -13,9 +13,9 @@
 #include "ash/system/message_center/ash_message_center_lock_screen_controller.h"
 #include "ash/system/message_center/message_center_constants.h"
 #include "ash/system/message_center/message_center_scroll_bar.h"
-#include "ash/system/message_center/stacked_notification_bar.h"
 #include "ash/system/message_center/unified_message_center_bubble.h"
-#include "ash/system/message_center/unified_message_list_view.h"
+#include "ash/system/notification_center/notification_list_view.h"
+#include "ash/system/notification_center/stacked_notification_bar.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_popup_utils.h"
 #include "ash/system/unified/unified_system_tray_model.h"
@@ -24,6 +24,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/user_metrics.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/compositor/layer.h"
 #include "ui/gfx/animation/linear_animation.h"
 #include "ui/message_center/message_center.h"
@@ -46,12 +47,12 @@
 
 class ScrollerContentsView : public views::View {
  public:
-  explicit ScrollerContentsView(UnifiedMessageListView* message_list_view) {
+  explicit ScrollerContentsView(NotificationListView* notification_list_view) {
     auto* contents_layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
         views::BoxLayout::Orientation::kVertical));
     contents_layout->set_cross_axis_alignment(
         views::BoxLayout::CrossAxisAlignment::kStretch);
-    AddChildView(message_list_view);
+    AddChildView(notification_list_view);
   }
 
   ScrollerContentsView(const ScrollerContentsView&) = delete;
@@ -69,7 +70,7 @@
 
 }  // namespace
 
-UnifiedMessageCenterView::UnifiedMessageCenterView(
+NotificationCenterView::NotificationCenterView(
     UnifiedSystemTrayView* parent,
     scoped_refptr<UnifiedSystemTrayModel> model,
     UnifiedMessageCenterBubble* bubble)
@@ -80,7 +81,7 @@
       // TODO(crbug.com/1247455): Determine how to use ScrollWithLayers without
       // breaking ARC.
       scroller_(new views::ScrollView()),
-      message_list_view_(new UnifiedMessageListView(this, model)),
+      notification_list_view_(new NotificationListView(this, model)),
       last_scroll_position_from_bottom_(0),
       is_notifications_refresh_enabled_(
           features::IsNotificationsRefreshEnabled()),
@@ -98,7 +99,7 @@
   }
 }
 
-UnifiedMessageCenterView::~UnifiedMessageCenterView() {
+NotificationCenterView::~NotificationCenterView() {
   DCHECK(model_);
   model_->set_notification_target_mode(
       UnifiedSystemTrayModel::NotificationTargetMode::LAST_NOTIFICATION);
@@ -106,8 +107,8 @@
   RemovedFromWidget();
 }
 
-void UnifiedMessageCenterView::Init() {
-  message_list_view_->Init();
+void NotificationCenterView::Init() {
+  notification_list_view_->Init();
 
   if (!is_notifications_refresh_enabled_)
     AddChildView(notification_bar_);
@@ -117,7 +118,7 @@
   // TODO(crbug.com/1247455): Be able to do
   // SetContentsLayerType(LAYER_NOT_DRAWN).
   scroller_->SetContents(
-      std::make_unique<ScrollerContentsView>(message_list_view_));
+      std::make_unique<ScrollerContentsView>(notification_list_view_));
   scroller_->SetBackgroundColor(absl::nullopt);
   scroller_->SetVerticalScrollBar(base::WrapUnique(scroll_bar_));
   scroller_->SetDrawOverflowIndicator(false);
@@ -132,14 +133,14 @@
     AddChildView(notification_bar_);
 }
 
-bool UnifiedMessageCenterView::UpdateNotificationBar() {
+bool NotificationCenterView::UpdateNotificationBar() {
   return notification_bar_->Update(
-      message_list_view_->GetTotalNotificationCount(),
-      message_list_view_->GetTotalPinnedNotificationCount(),
+      notification_list_view_->GetTotalNotificationCount(),
+      notification_list_view_->GetTotalPinnedNotificationCount(),
       GetStackedNotifications());
 }
 
-void UnifiedMessageCenterView::SetMaxHeight(int max_height) {
+void NotificationCenterView::SetMaxHeight(int max_height) {
   int max_scroller_height = max_height;
   if (notification_bar_->GetVisible()) {
     max_scroller_height -=
@@ -151,12 +152,12 @@
   scroller_->ClipHeightTo(0, max_scroller_height);
 }
 
-void UnifiedMessageCenterView::SetAvailableHeight(int available_height) {
+void NotificationCenterView::SetAvailableHeight(int available_height) {
   available_height_ = available_height;
   UpdateVisibility();
 }
 
-void UnifiedMessageCenterView::SetExpanded() {
+void NotificationCenterView::SetExpanded() {
   if (!collapsed_)
     return;
 
@@ -165,7 +166,7 @@
   scroller_->SetVisible(true);
 }
 
-void UnifiedMessageCenterView::SetCollapsed(bool animate) {
+void NotificationCenterView::SetCollapsed(bool animate) {
   if (!GetVisible() || collapsed_)
     return;
 
@@ -183,40 +184,40 @@
   }
 }
 
-void UnifiedMessageCenterView::ClearAllNotifications() {
+void NotificationCenterView::ClearAllNotifications() {
   base::RecordAction(
       base::UserMetricsAction("StatusArea_Notifications_StackingBarClearAll"));
 
-  message_list_view_->ClearAllWithAnimation();
+  notification_list_view_->ClearAllWithAnimation();
 }
 
-void UnifiedMessageCenterView::ExpandMessageCenter() {
+void NotificationCenterView::ExpandMessageCenter() {
   base::RecordAction(
       base::UserMetricsAction("StatusArea_Notifications_SeeAllNotifications"));
   message_center_bubble_->ExpandMessageCenter();
 }
 
-bool UnifiedMessageCenterView::IsNotificationBarVisible() const {
+bool NotificationCenterView::IsNotificationBarVisible() const {
   return notification_bar_->GetVisible();
 }
 
-bool UnifiedMessageCenterView::IsScrollBarVisible() const {
+bool NotificationCenterView::IsScrollBarVisible() const {
   return scroll_bar_->GetVisible();
 }
 
-void UnifiedMessageCenterView::OnNotificationSlidOut() {
+void NotificationCenterView::OnNotificationSlidOut() {
   if (notification_bar_->GetVisible()) {
     UpdateNotificationBar();
     if (!notification_bar_->GetVisible())
       StartHideStackingBarAnimation();
   }
 
-  if (!message_list_view_->GetTotalNotificationCount()) {
+  if (!notification_list_view_->GetTotalNotificationCount()) {
     StartCollapseAnimation();
   }
 }
 
-void UnifiedMessageCenterView::ListPreferredSizeChanged() {
+void NotificationCenterView::ListPreferredSizeChanged() {
   UpdateVisibility();
   PreferredSizeChanged();
   SetMaxHeight(available_height_);
@@ -225,25 +226,25 @@
     GetWidget()->SynthesizeMouseMoveEvent();
 }
 
-void UnifiedMessageCenterView::ConfigureMessageView(
+void NotificationCenterView::ConfigureMessageView(
     message_center::MessageView* message_view) {
   message_view->set_scroller(scroller_);
 }
 
-void UnifiedMessageCenterView::AddedToWidget() {
+void NotificationCenterView::AddedToWidget() {
   focus_manager_ = GetFocusManager();
   if (focus_manager_)
     focus_manager_->AddFocusChangeListener(this);
 }
 
-void UnifiedMessageCenterView::RemovedFromWidget() {
+void NotificationCenterView::RemovedFromWidget() {
   if (!focus_manager_)
     return;
   focus_manager_->RemoveFocusChangeListener(this);
   focus_manager_ = nullptr;
 }
 
-void UnifiedMessageCenterView::Layout() {
+void NotificationCenterView::Layout() {
   if (is_notifications_refresh_enabled_)
     return views::View::Layout();
 
@@ -256,8 +257,7 @@
                                       ? kStackedNotificationBarCollapsedHeight
                                       : notification_bar_expanded_height;
     int notification_bar_offset = 0;
-    if (animation_state_ ==
-        UnifiedMessageCenterAnimationState::HIDE_STACKING_BAR)
+    if (animation_state_ == NotificationCenterAnimationState::HIDE_STACKING_BAR)
       notification_bar_offset = GetAnimationValue() * notification_bar_height;
 
     counter_bounds.set_height(notification_bar_height);
@@ -276,7 +276,7 @@
   ScrollToTarget();
 }
 
-gfx::Size UnifiedMessageCenterView::CalculatePreferredSize() const {
+gfx::Size NotificationCenterView::CalculatePreferredSize() const {
   if (is_notifications_refresh_enabled_)
     return views::View::CalculatePreferredSize();
 
@@ -285,13 +285,12 @@
   if (notification_bar_->GetVisible()) {
     int bar_height = kStackedNotificationBarHeight;
 
-    if (animation_state_ ==
-        UnifiedMessageCenterAnimationState::HIDE_STACKING_BAR)
+    if (animation_state_ == NotificationCenterAnimationState::HIDE_STACKING_BAR)
       bar_height -= GetAnimationValue() * bar_height;
     preferred_size.set_height(preferred_size.height() + bar_height);
   }
 
-  if (animation_state_ == UnifiedMessageCenterAnimationState::COLLAPSE) {
+  if (animation_state_ == NotificationCenterAnimationState::COLLAPSE) {
     int height = preferred_size.height() * (1.0 - GetAnimationValue());
 
     if (collapsed_)
@@ -305,11 +304,7 @@
   return preferred_size;
 }
 
-const char* UnifiedMessageCenterView::GetClassName() const {
-  return "UnifiedMessageCenterView";
-}
-
-void UnifiedMessageCenterView::OnMessageCenterScrolled() {
+void NotificationCenterView::OnMessageCenterScrolled() {
   last_scroll_position_from_bottom_ =
       scroll_bar_->GetMaxPosition() - scroller_->GetVisibleRect().y();
 
@@ -328,12 +323,12 @@
   }
 }
 
-void UnifiedMessageCenterView::OnWillChangeFocus(views::View* before,
-                                                 views::View* now) {}
+void NotificationCenterView::OnWillChangeFocus(views::View* before,
+                                               views::View* now) {}
 
-void UnifiedMessageCenterView::OnDidChangeFocus(views::View* before,
-                                                views::View* now) {
-  if (message_list_view_->is_deleting_removed_notifications())
+void NotificationCenterView::OnDidChangeFocus(views::View* before,
+                                              views::View* now) {
+  if (notification_list_view_->is_deleting_removed_notifications())
     return;
 
   OnMessageCenterScrolled();
@@ -362,17 +357,17 @@
   }
 }
 
-void UnifiedMessageCenterView::AnimationEnded(const gfx::Animation* animation) {
+void NotificationCenterView::AnimationEnded(const gfx::Animation* animation) {
   // This is also called from AnimationCanceled().
   animation_->SetCurrentValue(1.0);
   PreferredSizeChanged();
 
-  animation_state_ = UnifiedMessageCenterAnimationState::IDLE;
+  animation_state_ = NotificationCenterAnimationState::IDLE;
   notification_bar_->SetAnimationState(animation_state_);
   UpdateVisibility();
 }
 
-void UnifiedMessageCenterView::AnimationProgressed(
+void NotificationCenterView::AnimationProgressed(
     const gfx::Animation* animation) {
   // Make the scroller containing notifications invisible and change the
   // notification bar to it's collapsed state in the middle of the animation to
@@ -385,43 +380,42 @@
   PreferredSizeChanged();
 }
 
-void UnifiedMessageCenterView::AnimationCanceled(
+void NotificationCenterView::AnimationCanceled(
     const gfx::Animation* animation) {
   AnimationEnded(animation);
 }
 
-void UnifiedMessageCenterView::StartHideStackingBarAnimation() {
+void NotificationCenterView::StartHideStackingBarAnimation() {
   animation_->End();
-  animation_state_ = UnifiedMessageCenterAnimationState::HIDE_STACKING_BAR;
+  animation_state_ = NotificationCenterAnimationState::HIDE_STACKING_BAR;
   notification_bar_->SetAnimationState(animation_state_);
   animation_->SetDuration(kHideStackingBarAnimationDuration);
   animation_->Start();
 }
 
-void UnifiedMessageCenterView::StartCollapseAnimation() {
+void NotificationCenterView::StartCollapseAnimation() {
   animation_->End();
-  animation_state_ = UnifiedMessageCenterAnimationState::COLLAPSE;
+  animation_state_ = NotificationCenterAnimationState::COLLAPSE;
   notification_bar_->SetAnimationState(animation_state_);
   animation_->SetDuration(kCollapseAnimationDuration);
   animation_->Start();
 }
 
-double UnifiedMessageCenterView::GetAnimationValue() const {
+double NotificationCenterView::GetAnimationValue() const {
   return gfx::Tween::CalculateValue(gfx::Tween::FAST_OUT_SLOW_IN,
                                     animation_->GetCurrentValue());
 }
 
-void UnifiedMessageCenterView::UpdateVisibility() {
+void NotificationCenterView::UpdateVisibility() {
   SessionControllerImpl* session_controller =
       Shell::Get()->session_controller();
 
-  SetVisible(
-      available_height_ >= kUnifiedNotificationMinimumHeight &&
-      (animation_state_ == UnifiedMessageCenterAnimationState::COLLAPSE ||
-       message_list_view_->GetPreferredSize().height() > 0) &&
-      session_controller->ShouldShowNotificationTray() &&
-      (!session_controller->IsScreenLocked() ||
-       AshMessageCenterLockScreenController::IsEnabled()));
+  SetVisible(available_height_ >= kUnifiedNotificationMinimumHeight &&
+             (animation_state_ == NotificationCenterAnimationState::COLLAPSE ||
+              notification_list_view_->GetPreferredSize().height() > 0) &&
+             session_controller->ShouldShowNotificationTray() &&
+             (!session_controller->IsScreenLocked() ||
+              AshMessageCenterLockScreenController::IsEnabled()));
 
   DCHECK(model_);
   if (!GetVisible()) {
@@ -439,7 +433,7 @@
   }
 }
 
-void UnifiedMessageCenterView::ScrollToTarget() {
+void NotificationCenterView::ScrollToTarget() {
   // Following logic doesn't work when the view is invisible, because it uses
   // the height of |scroller_|.
   if (!GetVisible())
@@ -451,7 +445,7 @@
 
   // Notification views may be deleted during an animation, so wait until it
   // finishes before scrolling to a new target (see crbug.com/954001).
-  if (message_list_view_->IsAnimating())
+  if (notification_list_view_->IsAnimating())
     target_mode = UnifiedSystemTrayModel::NotificationTargetMode::LAST_POSITION;
 
   int position;
@@ -468,9 +462,9 @@
       const gfx::Rect& target_rect =
           (model_->notification_target_mode() ==
            UnifiedSystemTrayModel::NotificationTargetMode::NOTIFICATION_ID)
-              ? message_list_view_->GetNotificationBounds(
+              ? notification_list_view_->GetNotificationBounds(
                     model_->notification_target_id())
-              : message_list_view_->GetLastNotificationBounds();
+              : notification_list_view_->GetLastNotificationBounds();
 
       const int last_notification_offset =
           target_rect.height() - scroller_->height();
@@ -493,7 +487,7 @@
 }
 
 std::vector<message_center::Notification*>
-UnifiedMessageCenterView::GetStackedNotifications() const {
+NotificationCenterView::GetStackedNotifications() const {
   // CountNotificationsAboveY() only works after SetBoundsRect() is called at
   // least once.
   if (scroller_->bounds().IsEmpty())
@@ -502,23 +496,23 @@
   if (collapsed_) {
     // When in collapsed state, all notifications are hidden, so all
     // notifications are stacked.
-    return message_list_view_->GetAllNotifications();
+    return notification_list_view_->GetAllNotifications();
   }
 
   if (is_notifications_refresh_enabled_) {
     const int y_offset = scroller_->GetVisibleRect().bottom() - scroller_->y();
-    return message_list_view_->GetNotificationsBelowY(y_offset);
+    return notification_list_view_->GetNotificationsBelowY(y_offset);
   }
 
   const int notification_bar_height =
       IsNotificationBarVisible() ? kStackedNotificationBarHeight : 0;
   const int y_offset = scroller_->GetVisibleRect().y() - scroller_->y() +
                        notification_bar_height;
-  return message_list_view_->GetNotificationsAboveY(y_offset);
+  return notification_list_view_->GetNotificationsAboveY(y_offset);
 }
 
 std::vector<std::string>
-UnifiedMessageCenterView::GetNonVisibleNotificationIdsInViewHierarchy() const {
+NotificationCenterView::GetNonVisibleNotificationIdsInViewHierarchy() const {
   // CountNotificationsAboveY() only works after SetBoundsRect() is called at
   // least once.
   if (scroller_->bounds().IsEmpty())
@@ -526,7 +520,7 @@
   if (collapsed_) {
     // When in collapsed state, all notifications are hidden, so all
     // notifications are stacked.
-    return message_list_view_->GetAllNotificationIds();
+    return notification_list_view_->GetAllNotificationIds();
   }
 
   const int notification_bar_height =
@@ -534,31 +528,31 @@
   const int y_offset_above = scroller_->GetVisibleRect().y() - scroller_->y() +
                              notification_bar_height;
   auto above_id_list =
-      message_list_view_->GetNotificationIdsAboveY(y_offset_above);
+      notification_list_view_->GetNotificationIdsAboveY(y_offset_above);
   const int y_offset_below =
       scroller_->GetVisibleRect().bottom() - scroller_->y();
   const auto below_id_list =
-      message_list_view_->GetNotificationIdsBelowY(y_offset_below);
+      notification_list_view_->GetNotificationIdsBelowY(y_offset_below);
   above_id_list.insert(above_id_list.end(), below_id_list.begin(),
                        below_id_list.end());
   return above_id_list;
 }
 
-void UnifiedMessageCenterView::FocusOut(bool reverse) {
+void NotificationCenterView::FocusOut(bool reverse) {
   if (message_center_bubble_ && message_center_bubble_->FocusOut(reverse)) {
     GetFocusManager()->ClearFocus();
     GetFocusManager()->SetStoredFocusView(nullptr);
   }
 }
 
-void UnifiedMessageCenterView::FocusEntered(bool reverse) {
+void NotificationCenterView::FocusEntered(bool reverse) {
   views::View* focus_view =
       reverse ? GetLastFocusableChild() : GetFirstFocusableChild();
   GetFocusManager()->ClearFocus();
   GetFocusManager()->SetFocusedView(focus_view);
 }
 
-views::View* UnifiedMessageCenterView::GetFirstFocusableChild() {
+views::View* NotificationCenterView::GetFirstFocusableChild() {
   views::FocusTraversable* dummy_focus_traversable;
   views::View* dummy_focus_traversable_view;
   return focus_search_->FindNextFocusableView(
@@ -569,7 +563,7 @@
       &dummy_focus_traversable, &dummy_focus_traversable_view);
 }
 
-views::View* UnifiedMessageCenterView::GetLastFocusableChild() {
+views::View* NotificationCenterView::GetLastFocusableChild() {
   views::FocusTraversable* focus_traversable = nullptr;
   views::View* dummy_focus_traversable_view = nullptr;
   views::View* last_view = focus_search_->FindNextFocusableView(
@@ -590,4 +584,7 @@
       &focus_traversable, &dummy_focus_traversable_view);
 }
 
+BEGIN_METADATA(NotificationCenterView, views::View);
+END_METADATA
+
 }  // namespace ash
diff --git a/ash/system/message_center/unified_message_center_view.h b/ash/system/notification_center/notification_center_view.h
similarity index 73%
rename from ash/system/message_center/unified_message_center_view.h
rename to ash/system/notification_center/notification_center_view.h
index 566eda2..1eb44a6 100644
--- a/ash/system/message_center/unified_message_center_view.h
+++ b/ash/system/notification_center/notification_center_view.h
@@ -1,13 +1,13 @@
-// Copyright 2018 The Chromium Authors
+// 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_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_CENTER_VIEW_H_
-#define ASH_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_CENTER_VIEW_H_
+#ifndef ASH_SYSTEM_NOTIFICATION_CENTER_NOTIFICATION_CENTER_VIEW_H_
+#define ASH_SYSTEM_NOTIFICATION_CENTER_NOTIFICATION_CENTER_VIEW_H_
 
 #include "ash/ash_export.h"
 #include "ash/system/message_center/message_center_scroll_bar.h"
-#include "ash/system/message_center/unified_message_list_view.h"
+#include "ash/system/notification_center/notification_list_view.h"
 #include "base/memory/scoped_refptr.h"
 #include "ui/gfx/animation/animation_delegate.h"
 #include "ui/views/background.h"
@@ -36,12 +36,15 @@
 class UnifiedSystemTrayModel;
 class UnifiedSystemTrayView;
 
-// Note: This enum represents the current animation state for
-// UnifiedMessageCenterView. There is an equivalent animation state enum in
-// the child UnifiedMessageListView. The animations for these two views can
-// occur simultaneously or independently, so states for both views are tracked
-// separately.
-enum class UnifiedMessageCenterAnimationState {
+// TODO(b:252887883): Clean up old animation code once the new clear all
+// animation is implemented.
+
+// Note: This enum represents the current animation
+// state for `NotificationCenterView`. There is an equivalent animation state
+// enum in the child `NotificationListView`. The animations for these two views
+// can occur simultaneously or independently, so states for both views are
+// tracked separately.
+enum class NotificationCenterAnimationState {
   // No animation is running.
   IDLE,
 
@@ -51,27 +54,28 @@
 
   // Animating collapsing the entire message center. Runs after the user
   // dismisses the last notification and during the clear all animation.
-  // TODO(tengs): This animation is not yet implemented.
   COLLAPSE,
 };
 
 // Manages scrolling of notification list.
-class ASH_EXPORT UnifiedMessageCenterView
+class ASH_EXPORT NotificationCenterView
     : public views::View,
       public MessageCenterScrollBar::Observer,
       public views::FocusChangeListener,
       public gfx::AnimationDelegate {
  public:
-  UnifiedMessageCenterView(UnifiedSystemTrayView* parent,
-                           scoped_refptr<UnifiedSystemTrayModel> model,
-                           UnifiedMessageCenterBubble* bubble);
+  METADATA_HEADER(NotificationCenterView);
 
-  UnifiedMessageCenterView(const UnifiedMessageCenterView&) = delete;
-  UnifiedMessageCenterView& operator=(const UnifiedMessageCenterView&) = delete;
+  NotificationCenterView(UnifiedSystemTrayView* parent,
+                         scoped_refptr<UnifiedSystemTrayModel> model,
+                         UnifiedMessageCenterBubble* bubble);
 
-  ~UnifiedMessageCenterView() override;
+  NotificationCenterView(const NotificationCenterView&) = delete;
+  NotificationCenterView& operator=(const NotificationCenterView&) = delete;
 
-  // Initializes the `UnifiedMessageListView` with existing notifications.
+  ~NotificationCenterView() override;
+
+  // Initializes the `NotificationListView` with existing notifications.
   // Should be called after ctor.
   void Init();
 
@@ -83,27 +87,27 @@
   // Sets the maximum height that the view can take.
   // TODO(tengs): The layout of this view is heavily dependant on this max
   // height (equal to the height of the entire tray), but we should refactor and
-  // consolidate this function with SetAvailableHeight().
+  // consolidate this function with `SetAvailableHeight()`.
   void SetMaxHeight(int max_height);
 
   // Sets the height available to the message center view. This is the remaining
   // height after counting the system menu, which may be expanded or collapsed.
   void SetAvailableHeight(int available_height);
 
-  // Called from UnifiedMessageListView when the preferred size is changed.
+  // Called from `NotificationListView` when the preferred size is changed.
   void ListPreferredSizeChanged();
 
-  // Called from the UnifiedMessageListView after a notification is dismissed by
+  // Called from the `NotificationListView` after a notification is dismissed by
   // the user and the slide animation is finished.
   void OnNotificationSlidOut();
 
-  // Configures MessageView to forward scroll events. Called from
-  // UnifiedMessageListView.
+  // Configures `MessageView` to forward scroll events. Called from
+  // `NotificationListView`.
   void ConfigureMessageView(message_center::MessageView* message_view);
 
-  // Count number of notifications that are still in the MessageCenter that are
-  // above visible area. NOTE: views may be in the view hierarchy, but no longer
-  // in the message center.
+  // Count number of notifications that are still in the `MessageCenter` that
+  // are above visible area. NOTE: views may be in the view hierarchy, but no
+  // longer in the message center.
   std::vector<message_center::Notification*> GetStackedNotifications() const;
 
   // Count the number of notifications that are not visible in the scrollable
@@ -143,7 +147,6 @@
   void RemovedFromWidget() override;
   void Layout() override;
   gfx::Size CalculatePreferredSize() const override;
-  const char* GetClassName() const override;
 
   // MessageCenterScrollBar::Observer:
   void OnMessageCenterScrolled() override;
@@ -157,14 +160,16 @@
   void AnimationProgressed(const gfx::Animation* animation) override;
   void AnimationCanceled(const gfx::Animation* animation) override;
 
-  UnifiedMessageListView* message_list_view() { return message_list_view_; }
+  NotificationListView* notification_list_view() {
+    return notification_list_view_;
+  }
   bool collapsed() { return collapsed_; }
 
  private:
-  friend class UnifiedMessageCenterViewTest;
+  friend class NotificationCenterViewTest;
   friend class UnifiedMessageCenterBubbleTest;
 
-  // Starts the animation to hide the notification stacking bar.
+  // Starts the animation to hide the `StackedNotificationBar`.
   void StartHideStackingBarAnimation();
 
   // Starts the animation to collapse the message center.
@@ -192,7 +197,7 @@
   StackedNotificationBar* const notification_bar_;
   views::ScrollBar* scroll_bar_;
   views::ScrollView* const scroller_;
-  UnifiedMessageListView* const message_list_view_;
+  NotificationListView* const notification_list_view_;
 
   // Position from the bottom of scroll contents in dip.
   int last_scroll_position_from_bottom_;
@@ -208,8 +213,8 @@
   bool collapsed_ = false;
 
   // Tracks the current animation state.
-  UnifiedMessageCenterAnimationState animation_state_ =
-      UnifiedMessageCenterAnimationState::IDLE;
+  NotificationCenterAnimationState animation_state_ =
+      NotificationCenterAnimationState::IDLE;
   const std::unique_ptr<gfx::LinearAnimation> animation_;
 
   const std::unique_ptr<views::FocusSearch> focus_search_;
@@ -219,4 +224,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_CENTER_VIEW_H_
+#endif  // ASH_SYSTEM_NOTIFICATION_CENTER_NOTIFICATION_CENTER_VIEW_H_
diff --git a/ash/system/message_center/unified_message_center_view_unittest.cc b/ash/system/notification_center/notification_center_view_unittest.cc
similarity index 71%
rename from ash/system/message_center/unified_message_center_view_unittest.cc
rename to ash/system/notification_center/notification_center_view_unittest.cc
index 2937d71..aa0c0e0a 100644
--- a/ash/system/message_center/unified_message_center_view_unittest.cc
+++ b/ash/system/notification_center/notification_center_view_unittest.cc
@@ -1,8 +1,8 @@
-// Copyright 2018 The Chromium Authors
+// 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/system/message_center/unified_message_center_view.h"
+#include "ash/system/notification_center/notification_center_view.h"
 
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
@@ -11,7 +11,7 @@
 #include "ash/system/message_center/ash_message_center_lock_screen_controller.h"
 #include "ash/system/message_center/message_center_constants.h"
 #include "ash/system/message_center/message_center_scroll_bar.h"
-#include "ash/system/message_center/stacked_notification_bar.h"
+#include "ash/system/notification_center/stacked_notification_bar.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
 #include "ash/system/unified/unified_system_tray_model.h"
@@ -42,33 +42,31 @@
 
 constexpr int kDefaultMaxHeight = 500;
 
-class TestUnifiedMessageCenterView : public UnifiedMessageCenterView {
+class TestNotificationCenterView : public NotificationCenterView {
  public:
-  explicit TestUnifiedMessageCenterView(UnifiedSystemTrayModel* model)
-      : UnifiedMessageCenterView(nullptr /*parent*/,
-                                 model,
-                                 nullptr /*bubble*/) {}
+  explicit TestNotificationCenterView(UnifiedSystemTrayModel* model)
+      : NotificationCenterView(nullptr /*parent*/, model, nullptr /*bubble*/) {}
 
-  TestUnifiedMessageCenterView(const TestUnifiedMessageCenterView&) = delete;
-  TestUnifiedMessageCenterView& operator=(const TestUnifiedMessageCenterView&) =
+  TestNotificationCenterView(const TestNotificationCenterView&) = delete;
+  TestNotificationCenterView& operator=(const TestNotificationCenterView&) =
       delete;
 
-  ~TestUnifiedMessageCenterView() override = default;
+  ~TestNotificationCenterView() override = default;
 };
 
 }  // namespace
 
-class UnifiedMessageCenterViewTest : public AshTestBase,
-                                     public views::ViewObserver,
-                                     public testing::WithParamInterface<bool> {
+class NotificationCenterViewTest : public AshTestBase,
+                                   public views::ViewObserver,
+                                   public testing::WithParamInterface<bool> {
  public:
-  UnifiedMessageCenterViewTest() = default;
+  NotificationCenterViewTest() = default;
 
-  UnifiedMessageCenterViewTest(const UnifiedMessageCenterViewTest&) = delete;
-  UnifiedMessageCenterViewTest& operator=(const UnifiedMessageCenterViewTest&) =
+  NotificationCenterViewTest(const NotificationCenterViewTest&) = delete;
+  NotificationCenterViewTest& operator=(const NotificationCenterViewTest&) =
       delete;
 
-  ~UnifiedMessageCenterViewTest() override = default;
+  ~NotificationCenterViewTest() override = default;
 
   // AshTestBase:
   void SetUp() override {
@@ -93,7 +91,7 @@
 
   void TearDown() override {
     base::RunLoop().RunUntilIdle();
-    message_center_view_.reset();
+    notification_center_view_.reset();
     model_.reset();
     AshTestBase::TearDown();
   }
@@ -130,10 +128,10 @@
     return ids;
   }
 
-  std::unique_ptr<TestUnifiedMessageCenterView> CreateMessageCenterViewImpl(
+  std::unique_ptr<TestNotificationCenterView> CreateMessageCenterViewImpl(
       int max_height) {
     auto message_center_view =
-        std::make_unique<TestUnifiedMessageCenterView>(model_.get());
+        std::make_unique<TestNotificationCenterView>(model_.get());
     message_center_view->Init();
     message_center_view->AddObserver(this);
     message_center_view->SetMaxHeight(max_height);
@@ -146,74 +144,80 @@
   }
 
   virtual void CreateMessageCenterView(int max_height = kDefaultMaxHeight) {
-    message_center_view_ = CreateMessageCenterViewImpl(max_height);
+    notification_center_view_ = CreateMessageCenterViewImpl(max_height);
   }
 
-  void AnimateMessageListToValue(float value) {
-    GetMessageListView()->animation_->SetCurrentValue(value);
-    GetMessageListView()->AnimationProgressed(
-        GetMessageListView()->animation_.get());
+  void AnimateNotificationListToValue(float value) {
+    GetNotificationListView()->animation_->SetCurrentValue(value);
+    GetNotificationListView()->AnimationProgressed(
+        GetNotificationListView()->animation_.get());
   }
 
-  void AnimateMessageListToMiddle() { AnimateMessageListToValue(0.5); }
-
-  void AnimateMessageListToEnd() {
-    FinishMessageListSlideOutAnimations();
-    GetMessageListView()->animation_->End();
+  void AnimateNotificationListToMiddle() {
+    AnimateNotificationListToValue(0.5);
   }
 
-  void AnimateMessageListUntilIdle() {
-    while (GetMessageListView()->animation_->is_animating()) {
-      GetMessageListView()->animation_->End();
+  void AnimateNotificationListToEnd() {
+    FinishNotificationListSlideOutAnimations();
+    GetNotificationListView()->animation_->End();
+  }
+
+  void AnimateNotificationListUntilIdle() {
+    while (GetNotificationListView()->animation_->is_animating()) {
+      GetNotificationListView()->animation_->End();
     }
   }
 
-  void FinishMessageListSlideOutAnimations() { base::RunLoop().RunUntilIdle(); }
+  void FinishNotificationListSlideOutAnimations() {
+    base::RunLoop().RunUntilIdle();
+  }
 
   gfx::Rect GetMessageViewVisibleBounds(size_t index) {
-    gfx::Rect bounds = GetMessageListView()->children()[index]->bounds();
+    gfx::Rect bounds = GetNotificationListView()->children()[index]->bounds();
     bounds -= GetScroller()->GetVisibleRect().OffsetFromOrigin();
     bounds += GetScroller()->bounds().OffsetFromOrigin();
     return bounds;
   }
 
-  UnifiedMessageListView* GetMessageListView() {
-    return message_center_view()->message_list_view_;
+  NotificationListView* GetNotificationListView() {
+    return notification_center_view()->notification_list_view_;
   }
 
   gfx::LinearAnimation* GetMessageCenterAnimation() {
-    return message_center_view()->animation_.get();
+    return notification_center_view()->animation_.get();
   }
 
-  views::ScrollView* GetScroller() { return message_center_view()->scroller_; }
+  views::ScrollView* GetScroller() {
+    return notification_center_view()->scroller_;
+  }
 
   views::ScrollBar* GetScrollBar() {
-    return message_center_view()->scroll_bar_;
+    return notification_center_view()->scroll_bar_;
   }
 
   views::View* GetScrollerContents() {
-    return message_center_view()->scroller_->contents();
+    return notification_center_view()->scroller_->contents();
   }
 
   StackedNotificationBar* GetNotificationBar() {
-    return message_center_view()->notification_bar_;
+    return notification_center_view()->notification_bar_;
   }
 
   views::View* GetNotificationBarIconsContainer() {
-    return message_center_view()
+    return notification_center_view()
         ->notification_bar_->notification_icons_container_;
   }
 
   views::View* GetNotificationBarLabel() {
-    return message_center_view()->notification_bar_->count_label_;
+    return notification_center_view()->notification_bar_->count_label_;
   }
 
   views::View* GetNotificationBarClearAllButton() {
-    return message_center_view()->notification_bar_->clear_all_button_;
+    return notification_center_view()->notification_bar_->clear_all_button_;
   }
 
   views::View* GetNotificationBarExpandAllButton() {
-    return message_center_view()->notification_bar_->expand_all_button_;
+    return notification_center_view()->notification_bar_->expand_all_button_;
   }
 
   int total_notification_count() {
@@ -235,18 +239,19 @@
 
   message_center::MessageView* ToggleFocusToMessageView(size_t index,
                                                         bool reverse) {
-    auto* focus_manager = message_center_view()->GetFocusManager();
+    auto* focus_manager = notification_center_view()->GetFocusManager();
     if (!focus_manager)
       return nullptr;
 
     message_center::MessageView* focused_message_view = nullptr;
     const size_t max_focus_toggles =
-        GetMessageListView()->children().size() * 5;
+        GetNotificationListView()->children().size() * 5;
     for (size_t i = 0; i < max_focus_toggles; ++i) {
       focus_manager->AdvanceFocus(reverse);
       auto* focused_view = focus_manager->GetFocusedView();
-      // The MessageView is wrapped in container view in the MessageList.
-      if (focused_view->parent() == GetMessageListView()->children()[index]) {
+      // The MessageView is wrapped in container view in the NotificationList.
+      if (focused_view->parent() ==
+          GetNotificationListView()->children()[index]) {
         focused_message_view =
             static_cast<message_center::MessageView*>(focused_view);
         break;
@@ -261,19 +266,19 @@
     // relayout, and then this view will relayout. In test, we don't have
     // TrayBubbleView as the parent, so we need to ensure Layout() is executed
     // in some circumstances.
-    views::test::RunScheduledLayout(message_center_view_.get());
+    views::test::RunScheduledLayout(notification_center_view_.get());
   }
 
   void UpdateNotificationBarForTest() {
     // TODO(crbug/1357232): Refactor so this code mirrors production better.
     // Outside of tests, the notification bar is updated with a call to
-    // UnifiedMessageCenterBubble::UpdatePosition(), but this function is not
+    // NotificationCenterBubble::UpdatePosition(), but this function is not
     // triggered when adding notifications in tests.
-    message_center_view_->UpdateNotificationBar();
+    notification_center_view_->UpdateNotificationBar();
   }
 
-  virtual TestUnifiedMessageCenterView* message_center_view() {
-    return message_center_view_.get();
+  virtual TestNotificationCenterView* notification_center_view() {
+    return notification_center_view_.get();
   }
 
   int size_changed_count() const { return size_changed_count_; }
@@ -285,24 +290,23 @@
   int size_changed_count_ = 0;
 
   scoped_refptr<UnifiedSystemTrayModel> model_;
-  std::unique_ptr<TestUnifiedMessageCenterView> message_center_view_;
+  std::unique_ptr<TestNotificationCenterView> notification_center_view_;
   std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
 };
 
-class UnifiedMessageCenterViewInWidgetTest
-    : public UnifiedMessageCenterViewTest {
+class NotificationCenterViewInWidgetTest : public NotificationCenterViewTest {
  public:
-  UnifiedMessageCenterViewInWidgetTest() = default;
-  UnifiedMessageCenterViewInWidgetTest(
-      const UnifiedMessageCenterViewInWidgetTest&) = delete;
-  UnifiedMessageCenterViewInWidgetTest& operator=(
-      const UnifiedMessageCenterViewInWidgetTest&) = delete;
-  ~UnifiedMessageCenterViewInWidgetTest() override = default;
+  NotificationCenterViewInWidgetTest() = default;
+  NotificationCenterViewInWidgetTest(
+      const NotificationCenterViewInWidgetTest&) = delete;
+  NotificationCenterViewInWidgetTest& operator=(
+      const NotificationCenterViewInWidgetTest&) = delete;
+  ~NotificationCenterViewInWidgetTest() override = default;
 
   void TearDown() override {
     widget_.reset();
 
-    UnifiedMessageCenterViewTest::TearDown();
+    NotificationCenterViewTest::TearDown();
   }
 
  protected:
@@ -312,7 +316,7 @@
         CreateMessageCenterViewImpl(max_height));
   }
 
-  TestUnifiedMessageCenterView* message_center_view() override {
+  TestNotificationCenterView* notification_center_view() override {
     return message_center_;
   }
 
@@ -320,65 +324,66 @@
 
  private:
   std::unique_ptr<views::Widget> widget_;
-  TestUnifiedMessageCenterView* message_center_ = nullptr;
+  TestNotificationCenterView* message_center_ = nullptr;
 };
 
 INSTANTIATE_TEST_SUITE_P(All,
-                         UnifiedMessageCenterViewTest,
+                         NotificationCenterViewTest,
                          testing::Bool() /* IsNotificationsRefreshEnabled() */);
 
 // Flaky: https://crbug.com/1293165
-TEST_P(UnifiedMessageCenterViewTest, DISABLED_AddAndRemoveNotification) {
+TEST_P(NotificationCenterViewTest, DISABLED_AddAndRemoveNotification) {
   CreateMessageCenterView();
-  EXPECT_FALSE(message_center_view()->GetVisible());
+  EXPECT_FALSE(notification_center_view()->GetVisible());
 
   auto id0 = AddNotification();
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  EXPECT_TRUE(notification_center_view()->GetVisible());
 
   // The notification first slides out of the list.
   MessageCenter::Get()->RemoveNotification(id0, true /* by_user */);
-  AnimateMessageListToEnd();
+  AnimateNotificationListToEnd();
 
   // After all the last notifiation slides out, the message center and list
   // should collapse.
   auto* collapse_animation = GetMessageCenterAnimation();
   collapse_animation->SetCurrentValue(0.5);
-  message_center_view()->AnimationProgressed(collapse_animation);
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  notification_center_view()->AnimationProgressed(collapse_animation);
+  EXPECT_TRUE(notification_center_view()->GetVisible());
 
   // The message center is now hidden after all animations complete.
   collapse_animation->End();
-  AnimateMessageListToEnd();
-  EXPECT_FALSE(message_center_view()->GetVisible());
+  AnimateNotificationListToEnd();
+  EXPECT_FALSE(notification_center_view()->GetVisible());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, RemoveNotificationAtTail) {
+TEST_P(NotificationCenterViewTest, RemoveNotificationAtTail) {
   // No special scroll behavior with the Notifications Refresh anymore.
   if (IsNotificationsRefreshEnabled())
     return;
   // Show message center with multiple notifications.
   AddManyNotifications();
   CreateMessageCenterView();
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  EXPECT_TRUE(notification_center_view()->GetVisible());
 
   // The message center should autoscroll to the bottom of the list after adding
   // a new notification.
   auto id_to_remove = AddNotification();
   RelayoutMessageCenterViewForTest();
   int scroll_position = GetScroller()->GetVisibleRect().y();
-  EXPECT_EQ(GetMessageListView()->height() - GetScroller()->height(),
+  EXPECT_EQ(GetNotificationListView()->height() - GetScroller()->height(),
             scroll_position);
 
   // Get the height of last notification and then remove it.
   int removed_notification_height =
-      GetMessageViewVisibleBounds(GetMessageListView()->children().size() - 1)
+      GetMessageViewVisibleBounds(GetNotificationListView()->children().size() -
+                                  1)
           .height();
   MessageCenter::Get()->RemoveNotification(id_to_remove, true /* by_user */);
   scroll_position = GetScroller()->GetVisibleRect().y();
 
   // The scroll position should be reduced by the height of the removed
   // notification after collapsing.
-  AnimateMessageListToEnd();
+  AnimateNotificationListToEnd();
   RelayoutMessageCenterViewForTest();
 
   EXPECT_EQ(scroll_position - removed_notification_height -
@@ -386,43 +391,44 @@
             GetScroller()->GetVisibleRect().y());
 
   // Check that the list is still scrolled to the bottom.
-  EXPECT_EQ(GetMessageListView()->height() - GetScroller()->height(),
+  EXPECT_EQ(GetNotificationListView()->height() - GetScroller()->height(),
             GetScroller()->GetVisibleRect().y());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, ContentsRelayout) {
+TEST_P(NotificationCenterViewTest, ContentsRelayout) {
   std::vector<std::string> ids = AddManyNotifications();
   CreateMessageCenterView();
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  EXPECT_TRUE(notification_center_view()->GetVisible());
   // MessageCenterView is maxed out.
-  EXPECT_GT(GetMessageListView()->bounds().height(),
-            message_center_view()->bounds().height());
+  EXPECT_GT(GetNotificationListView()->bounds().height(),
+            notification_center_view()->bounds().height());
   const int previous_contents_height = GetScrollerContents()->height();
-  const int previous_list_height = GetMessageListView()->height();
+  const int previous_list_height = GetNotificationListView()->height();
 
   MessageCenter::Get()->RemoveNotification(ids.back(), true /* by_user */);
-  AnimateMessageListToEnd();
+  AnimateNotificationListToEnd();
   RelayoutMessageCenterViewForTest();
 
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  EXPECT_TRUE(notification_center_view()->GetVisible());
   EXPECT_GT(previous_contents_height, GetScrollerContents()->height());
-  EXPECT_GT(previous_list_height, GetMessageListView()->height());
+  EXPECT_GT(previous_list_height, GetNotificationListView()->height());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, InsufficientHeight) {
+TEST_P(NotificationCenterViewTest, InsufficientHeight) {
   CreateMessageCenterView();
   AddNotification();
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  EXPECT_TRUE(notification_center_view()->GetVisible());
 
-  message_center_view()->SetAvailableHeight(kUnifiedNotificationMinimumHeight -
-                                            1);
-  EXPECT_FALSE(message_center_view()->GetVisible());
+  notification_center_view()->SetAvailableHeight(
+      kUnifiedNotificationMinimumHeight - 1);
+  EXPECT_FALSE(notification_center_view()->GetVisible());
 
-  message_center_view()->SetAvailableHeight(kUnifiedNotificationMinimumHeight);
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  notification_center_view()->SetAvailableHeight(
+      kUnifiedNotificationMinimumHeight);
+  EXPECT_TRUE(notification_center_view()->GetVisible());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, NotVisibleWhenLocked) {
+TEST_P(NotificationCenterViewTest, NotVisibleWhenLocked) {
   // Disable the lock screen notification if the feature is enable.
   PrefService* user_prefs =
       Shell::Get()->session_controller()->GetLastActiveUserPrefService();
@@ -437,10 +443,10 @@
   BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
   CreateMessageCenterView();
 
-  EXPECT_FALSE(message_center_view()->GetVisible());
+  EXPECT_FALSE(notification_center_view()->GetVisible());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, VisibleWhenLocked) {
+TEST_P(NotificationCenterViewTest, VisibleWhenLocked) {
   // This test is only valid if the lock screen feature is enabled.
   // TODO(yoshiki): Clean up after the feature is launched crbug.com/913764.
   if (!features::IsLockScreenNotificationsEnabled())
@@ -460,53 +466,53 @@
   BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
   CreateMessageCenterView();
 
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  EXPECT_TRUE(notification_center_view()->GetVisible());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, ClearAllPressed) {
+TEST_P(NotificationCenterViewTest, ClearAllPressed) {
   AddNotification();
   AddNotification();
   CreateMessageCenterView();
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  EXPECT_TRUE(notification_center_view()->GetVisible());
   EXPECT_TRUE(GetNotificationBar()->GetVisible());
 
   // When Clear All button is pressed, all notifications are removed and the
   // view becomes invisible.
-  message_center_view()->ClearAllNotifications();
-  AnimateMessageListUntilIdle();
-  EXPECT_FALSE(message_center_view()->GetVisible());
+  notification_center_view()->ClearAllNotifications();
+  AnimateNotificationListUntilIdle();
+  EXPECT_FALSE(notification_center_view()->GetVisible());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, InitialPosition) {
+TEST_P(NotificationCenterViewTest, InitialPosition) {
   AddNotification();
   AddNotification();
   CreateMessageCenterView();
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  EXPECT_TRUE(notification_center_view()->GetVisible());
 
   // MessageCenterView is not maxed out.
-  EXPECT_LT(GetMessageListView()->bounds().height(),
-            message_center_view()->bounds().height());
+  EXPECT_LT(GetNotificationListView()->bounds().height(),
+            notification_center_view()->bounds().height());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, InitialPositionMaxOut) {
+TEST_P(NotificationCenterViewTest, InitialPositionMaxOut) {
   AddManyNotifications();
   CreateMessageCenterView();
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  EXPECT_TRUE(notification_center_view()->GetVisible());
 
   // MessageCenterView is maxed out.
-  EXPECT_GT(GetMessageListView()->bounds().height(),
-            message_center_view()->bounds().height());
+  EXPECT_GT(GetNotificationListView()->bounds().height(),
+            notification_center_view()->bounds().height());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, InitialPositionWithLargeNotification) {
+TEST_P(NotificationCenterViewTest, InitialPositionWithLargeNotification) {
   AddNotification();
   AddNotification();
   CreateMessageCenterView(60 /* max_height */);
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  EXPECT_TRUE(notification_center_view()->GetVisible());
 
   // MessageCenterView is shorter than the notification.
   gfx::Rect message_view_bounds = GetMessageViewVisibleBounds(1);
-  EXPECT_LT(message_center_view()->bounds().height(),
+  EXPECT_LT(notification_center_view()->bounds().height(),
             message_view_bounds.height());
 
   // Top of the second notification aligns with the top of MessageCenterView.
@@ -514,49 +520,49 @@
     EXPECT_EQ(kStackedNotificationBarHeight, message_view_bounds.y());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, ScrollPositionWhenResized) {
+TEST_P(NotificationCenterViewTest, ScrollPositionWhenResized) {
   // We keep the scroll position at the top after the notifications refresh.
   if (IsNotificationsRefreshEnabled())
     return;
 
   AddManyNotifications();
   CreateMessageCenterView();
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  EXPECT_TRUE(notification_center_view()->GetVisible());
 
   // MessageCenterView is maxed out.
-  EXPECT_GT(GetMessageListView()->bounds().height(),
-            message_center_view()->bounds().height());
+  EXPECT_GT(GetNotificationListView()->bounds().height(),
+            notification_center_view()->bounds().height());
   gfx::Rect previous_visible_rect = GetScroller()->GetVisibleRect();
 
-  gfx::Size new_size = message_center_view()->size();
+  gfx::Size new_size = notification_center_view()->size();
   new_size.set_height(250);
-  message_center_view()->SetPreferredSize(new_size);
-  OnViewPreferredSizeChanged(message_center_view());
+  notification_center_view()->SetPreferredSize(new_size);
+  OnViewPreferredSizeChanged(notification_center_view());
 
   EXPECT_EQ(previous_visible_rect.bottom(),
             GetScroller()->GetVisibleRect().bottom());
 
   GetScroller()->ScrollToPosition(GetScrollBar(), 200);
-  message_center_view()->OnMessageCenterScrolled();
+  notification_center_view()->OnMessageCenterScrolled();
   previous_visible_rect = GetScroller()->GetVisibleRect();
 
   new_size.set_height(300);
-  message_center_view()->SetPreferredSize(new_size);
-  OnViewPreferredSizeChanged(message_center_view());
+  notification_center_view()->SetPreferredSize(new_size);
+  OnViewPreferredSizeChanged(notification_center_view());
 
   EXPECT_EQ(previous_visible_rect.bottom(),
             GetScroller()->GetVisibleRect().bottom());
 }
 
 // Tests basic layout of the StackingNotificationBar.
-TEST_P(UnifiedMessageCenterViewTest, StackingCounterLabelLayout) {
+TEST_P(NotificationCenterViewTest, StackingCounterLabelLayout) {
   AddManyNotifications();
 
   // MessageCenterView is maxed out.
   CreateMessageCenterView();
 
-  EXPECT_GT(GetMessageListView()->bounds().height(),
-            message_center_view()->bounds().height());
+  EXPECT_GT(GetNotificationListView()->bounds().height(),
+            notification_center_view()->bounds().height());
 
   EXPECT_TRUE(GetNotificationBar()->GetVisible());
 
@@ -576,7 +582,7 @@
 }
 
 // Tests that the NotificationBarLabel is invisible when scrolled to the top.
-TEST_P(UnifiedMessageCenterViewTest, StackingCounterLabelInvisible) {
+TEST_P(NotificationCenterViewTest, StackingCounterLabelInvisible) {
   AddManyNotifications();
   CreateMessageCenterView();
 
@@ -587,7 +593,7 @@
                                   features::IsNotificationsRefreshEnabled()
                                       ? GetScrollBar()->bounds().bottom()
                                       : 0);
-  message_center_view()->OnMessageCenterScrolled();
+  notification_center_view()->OnMessageCenterScrolled();
 
   EXPECT_FALSE(GetNotificationBarLabel()->GetVisible());
   // ClearAll label should always be visible.
@@ -595,7 +601,7 @@
 }
 
 // Tests that the NotificationBarLabel is visible when scrolling down.
-TEST_P(UnifiedMessageCenterViewTest, StackingCounterLabelVisible) {
+TEST_P(NotificationCenterViewTest, StackingCounterLabelVisible) {
   AddManyNotifications();
   CreateMessageCenterView();
 
@@ -604,7 +610,7 @@
   GetScroller()->ScrollToPosition(
       GetScrollBar(),
       features::IsNotificationsRefreshEnabled() ? 0 : scroll_amount);
-  message_center_view()->OnMessageCenterScrolled();
+  notification_center_view()->OnMessageCenterScrolled();
 
   EXPECT_TRUE(GetNotificationBarLabel()->GetVisible());
   // ClearAll label should always be visible.
@@ -612,7 +618,7 @@
 }
 
 // Tests that the +n notifications label hides after being shown.
-TEST_P(UnifiedMessageCenterViewTest, StackingCounterLabelHidesAfterShown) {
+TEST_P(NotificationCenterViewTest, StackingCounterLabelHidesAfterShown) {
   AddManyNotifications();
   CreateMessageCenterView();
 
@@ -622,7 +628,7 @@
   GetScroller()->ScrollToPosition(
       GetScrollBar(),
       features::IsNotificationsRefreshEnabled() ? bottom_position : 0);
-  message_center_view()->OnMessageCenterScrolled();
+  notification_center_view()->OnMessageCenterScrolled();
 
   EXPECT_FALSE(GetNotificationBarLabel()->GetVisible());
 
@@ -632,7 +638,7 @@
                                   features::IsNotificationsRefreshEnabled()
                                       ? bottom_position - scroll_amount
                                       : scroll_amount);
-  message_center_view()->OnMessageCenterScrolled();
+  notification_center_view()->OnMessageCenterScrolled();
 
   ASSERT_TRUE(GetNotificationBarLabel()->GetVisible());
 
@@ -642,7 +648,7 @@
                                   features::IsNotificationsRefreshEnabled()
                                       ? GetScrollBar()->bounds().bottom()
                                       : 0);
-  message_center_view()->OnMessageCenterScrolled();
+  notification_center_view()->OnMessageCenterScrolled();
 
   EXPECT_FALSE(GetNotificationBarLabel()->GetVisible());
   // ClearAll label should always be visible.
@@ -654,7 +660,7 @@
 // time (this prevents the user from over-scrolling and showing multiple
 // animations when they scroll very quickly). Before, users could scroll fast
 // and have a large amount of icons, instead of keeping it to 3.
-TEST_P(UnifiedMessageCenterViewTest, StackingIconsNeverMoreThanThree) {
+TEST_P(NotificationCenterViewTest, StackingIconsNeverMoreThanThree) {
   for (int i = 0; i < 20; ++i)
     AddNotification(false);
   CreateMessageCenterView();
@@ -662,7 +668,7 @@
   auto bottom_position = GetScrollBar()->bounds().bottom();
   if (features::IsNotificationsRefreshEnabled()) {
     GetScroller()->ScrollToPosition(GetScrollBar(), bottom_position);
-    message_center_view()->OnMessageCenterScrolled();
+    notification_center_view()->OnMessageCenterScrolled();
   }
 
   // Force animations to happen, so we can see if multiple animations trigger.
@@ -675,7 +681,7 @@
                                     features::IsNotificationsRefreshEnabled()
                                         ? bottom_position - scroll_amount
                                         : scroll_amount);
-    message_center_view()->OnMessageCenterScrolled();
+    notification_center_view()->OnMessageCenterScrolled();
 
     auto icons_container_children =
         GetNotificationBarIconsContainer()->children();
@@ -695,21 +701,21 @@
 }
 
 // Flaky: crbug.com/1163575
-TEST_P(UnifiedMessageCenterViewTest,
+TEST_P(NotificationCenterViewTest,
        DISABLED_StackingCounterNotificationRemoval) {
   std::vector<std::string> ids = AddManyNotifications();
   CreateMessageCenterView();
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  EXPECT_TRUE(notification_center_view()->GetVisible());
 
   // MessageCenterView is maxed out.
-  EXPECT_GT(GetMessageListView()->bounds().height(),
-            message_center_view()->bounds().height());
+  EXPECT_GT(GetNotificationListView()->bounds().height(),
+            notification_center_view()->bounds().height());
 
   // Dismiss until there are 2 notifications. The bar should still be visible.
   EXPECT_TRUE(GetNotificationBar()->GetVisible());
   for (size_t i = 0; (i + 2) < ids.size(); ++i) {
     MessageCenter::Get()->RemoveNotification(ids[i], true /* by_user */);
-    AnimateMessageListToEnd();
+    AnimateNotificationListToEnd();
   }
   EXPECT_TRUE(GetNotificationBar()->GetVisible());
   EXPECT_FALSE(GetNotificationBarLabel()->GetVisible());
@@ -717,8 +723,8 @@
 
   // The MessageCenterView should be tall enough to contain the bar, two
   // notifications.
-  EXPECT_EQ(kStackedNotificationBarHeight + GetMessageListView()->height(),
-            message_center_view()->height());
+  EXPECT_EQ(kStackedNotificationBarHeight + GetNotificationListView()->height(),
+            notification_center_view()->height());
 
   // Dismiss until there is only 1 notification left. The bar should be
   // hidden after an animation.
@@ -727,24 +733,24 @@
   EXPECT_TRUE(GetNotificationBar()->GetVisible());
 
   // The HIDE_STACKING_BAR animation starts after the notification is slid out.
-  AnimateMessageListToEnd();
+  AnimateNotificationListToEnd();
   auto* hide_animation = GetMessageCenterAnimation();
   EXPECT_TRUE(hide_animation->is_animating());
   EXPECT_TRUE(GetNotificationBar()->GetVisible());
 
   // Animate to middle. The bar should still be visible.
-  AnimateMessageListToMiddle();
+  AnimateNotificationListToMiddle();
   hide_animation->SetCurrentValue(0.5);
-  message_center_view()->AnimationProgressed(hide_animation);
+  notification_center_view()->AnimationProgressed(hide_animation);
   EXPECT_TRUE(GetNotificationBar()->GetVisible());
 
   // Animate to end. The bar should now be hidden.
-  AnimateMessageListToEnd();
+  AnimateNotificationListToEnd();
   hide_animation->End();
   EXPECT_FALSE(GetNotificationBar()->GetVisible());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, StackingCounterLabelRelaidOutOnScroll) {
+TEST_P(NotificationCenterViewTest, StackingCounterLabelRelaidOutOnScroll) {
   // Open the message center at the top of the notification list so the stacking
   // bar is hidden by default.
   std::string id = AddNotification();
@@ -760,7 +766,7 @@
 
   if (features::IsNotificationsRefreshEnabled()) {
     GetScroller()->ScrollToPosition(GetScrollBar(), bottom_position);
-    message_center_view()->OnMessageCenterScrolled();
+    notification_center_view()->OnMessageCenterScrolled();
   }
 
   EXPECT_FALSE(GetNotificationBarLabel()->GetVisible());
@@ -771,7 +777,7 @@
                                   features::IsNotificationsRefreshEnabled()
                                       ? bottom_position - scroll_amount
                                       : scroll_amount);
-  message_center_view()->OnMessageCenterScrolled();
+  notification_center_view()->OnMessageCenterScrolled();
   RelayoutMessageCenterViewForTest();
   EXPECT_TRUE(GetNotificationBarLabel()->GetVisible());
   int label_width = GetNotificationBarLabel()->bounds().width();
@@ -784,12 +790,12 @@
                                   features::IsNotificationsRefreshEnabled()
                                       ? bottom_position - scroll_amount
                                       : scroll_amount);
-  message_center_view()->OnMessageCenterScrolled();
+  notification_center_view()->OnMessageCenterScrolled();
   RelayoutMessageCenterViewForTest();
   EXPECT_GT(GetNotificationBarLabel()->bounds().width(), label_width);
 }
 
-TEST_P(UnifiedMessageCenterViewTest, StackingCounterVisibility) {
+TEST_P(NotificationCenterViewTest, StackingCounterVisibility) {
   std::string id0 = AddNotification();
   std::string id1 = AddNotification();
   CreateMessageCenterView();
@@ -799,7 +805,7 @@
   EXPECT_TRUE(GetNotificationBarClearAllButton()->GetVisible());
 
   MessageCenter::Get()->RemoveNotification(id0, true /* by_user */);
-  AnimateMessageListToEnd();
+  AnimateNotificationListToEnd();
   auto* hide_animation = GetMessageCenterAnimation();
   hide_animation->End();
 
@@ -833,12 +839,12 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(All,
-                         UnifiedMessageCenterViewInWidgetTest,
+                         NotificationCenterViewInWidgetTest,
                          testing::Bool() /* IsNotificationsRefreshEnabled()
                          */);
 
 // We need a widget to initialize a FocusManager.
-TEST_P(UnifiedMessageCenterViewInWidgetTest,
+TEST_P(NotificationCenterViewInWidgetTest,
        FocusClearedAfterNotificationRemoval) {
   CreateMessageCenterView();
 
@@ -857,11 +863,11 @@
 
   // Remove the notification and observe that the focus is cleared.
   MessageCenter::Get()->RemoveNotification(id1, true /* by_user */);
-  AnimateMessageListToEnd();
-  EXPECT_FALSE(message_center_view()->GetFocusManager()->GetFocusedView());
+  AnimateNotificationListToEnd();
+  EXPECT_FALSE(notification_center_view()->GetFocusManager()->GetFocusedView());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, CollapseAndExpand_NonAnimated) {
+TEST_P(NotificationCenterViewTest, CollapseAndExpand_NonAnimated) {
   AddNotification();
   AddNotification();
   CreateMessageCenterView();
@@ -870,66 +876,66 @@
   EXPECT_FALSE(GetNotificationBarExpandAllButton()->GetVisible());
 
   // Set to collapsed state.
-  message_center_view()->SetCollapsed(false /* animate */);
+  notification_center_view()->SetCollapsed(false /* animate */);
   EXPECT_FALSE(GetScroller()->GetVisible());
   EXPECT_TRUE(GetNotificationBar()->GetVisible());
   EXPECT_TRUE(GetNotificationBarExpandAllButton()->GetVisible());
   EXPECT_FALSE(GetNotificationBarClearAllButton()->GetVisible());
 
   // Set back to expanded state.
-  message_center_view()->SetExpanded();
+  notification_center_view()->SetExpanded();
   EXPECT_FALSE(GetNotificationBarExpandAllButton()->GetVisible());
   EXPECT_TRUE(GetNotificationBarClearAllButton()->GetVisible());
   EXPECT_TRUE(GetScroller()->GetVisible());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, CollapseAndExpand_Animated) {
+TEST_P(NotificationCenterViewTest, CollapseAndExpand_Animated) {
   AddNotification();
   AddNotification();
   CreateMessageCenterView();
   EXPECT_TRUE(GetScroller()->GetVisible());
 
   // Set to collapsed state with animation.
-  message_center_view()->SetCollapsed(true /* animate */);
+  notification_center_view()->SetCollapsed(true /* animate */);
   auto* collapse_animation = GetMessageCenterAnimation();
   EXPECT_TRUE(collapse_animation->is_animating());
 
   // The scroller should be hidden at the half way point.
   collapse_animation->SetCurrentValue(0.5);
-  message_center_view()->AnimationProgressed(collapse_animation);
+  notification_center_view()->AnimationProgressed(collapse_animation);
   EXPECT_FALSE(GetScroller()->GetVisible());
   EXPECT_TRUE(GetNotificationBar()->GetVisible());
 
   collapse_animation->End();
-  AnimateMessageListToEnd();
+  AnimateNotificationListToEnd();
   EXPECT_TRUE(GetNotificationBarExpandAllButton()->GetVisible());
   EXPECT_FALSE(GetNotificationBarClearAllButton()->GetVisible());
 
   // Set back to expanded state.
-  message_center_view()->SetExpanded();
+  notification_center_view()->SetExpanded();
   EXPECT_FALSE(GetNotificationBarExpandAllButton()->GetVisible());
   EXPECT_TRUE(GetNotificationBarClearAllButton()->GetVisible());
   EXPECT_TRUE(GetScroller()->GetVisible());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, CollapseAndExpand_NoNotifications) {
+TEST_P(NotificationCenterViewTest, CollapseAndExpand_NoNotifications) {
   CreateMessageCenterView();
-  EXPECT_FALSE(message_center_view()->GetVisible());
+  EXPECT_FALSE(notification_center_view()->GetVisible());
 
   // Setting to the collapsed state should do nothing.
-  message_center_view()->SetCollapsed(true /* animate */);
-  EXPECT_FALSE(message_center_view()->GetVisible());
+  notification_center_view()->SetCollapsed(true /* animate */);
+  EXPECT_FALSE(notification_center_view()->GetVisible());
 
   // Same with setting it back to the expanded state.
-  message_center_view()->SetExpanded();
-  EXPECT_FALSE(message_center_view()->GetVisible());
+  notification_center_view()->SetExpanded();
+  EXPECT_FALSE(notification_center_view()->GetVisible());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, ClearAllButtonHeight) {
+TEST_P(NotificationCenterViewTest, ClearAllButtonHeight) {
   std::string id0 = AddNotification();
   std::string id1 = AddNotification();
   CreateMessageCenterView();
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  EXPECT_TRUE(notification_center_view()->GetVisible());
   EXPECT_TRUE(GetNotificationBar()->GetVisible());
   EXPECT_TRUE(GetNotificationBarClearAllButton()->GetVisible());
 
@@ -945,13 +951,13 @@
             GetNotificationBarClearAllButton()->height());
 }
 
-TEST_P(UnifiedMessageCenterViewTest, StackedNotificationCount) {
+TEST_P(NotificationCenterViewTest, StackedNotificationCount) {
   // There should not be any stacked notifications in the expanded message
   // center with just one notification added.
   AddNotification();
   CreateMessageCenterView();
-  message_center_view()->SetExpanded();
-  EXPECT_TRUE(message_center_view()->GetVisible());
+  notification_center_view()->SetExpanded();
+  EXPECT_TRUE(notification_center_view()->GetVisible());
   EXPECT_EQ(1, total_notification_count());
   EXPECT_EQ(0, stacked_notification_count());
 
diff --git a/ash/system/message_center/unified_message_list_view.cc b/ash/system/notification_center/notification_list_view.cc
similarity index 88%
rename from ash/system/message_center/unified_message_list_view.cc
rename to ash/system/notification_center/notification_list_view.cc
index cae7fc4..83c3364 100644
--- a/ash/system/message_center/unified_message_list_view.cc
+++ b/ash/system/notification_center/notification_list_view.cc
@@ -1,8 +1,8 @@
-// Copyright 2018 The Chromium Authors
+// 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/system/message_center/unified_message_list_view.h"
+#include "ash/system/notification_center/notification_list_view.h"
 
 #include <string>
 
@@ -15,7 +15,7 @@
 #include "ash/system/message_center/message_view_factory.h"
 #include "ash/system/message_center/metrics_utils.h"
 #include "ash/system/message_center/notification_swipe_control_view.h"
-#include "ash/system/message_center/unified_message_center_view.h"
+#include "ash/system/notification_center/notification_center_view.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/unified_system_tray_model.h"
 #include "base/auto_reset.h"
@@ -27,6 +27,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/ranges/algorithm.h"
 #include "base/time/time.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/compositor/compositor.h"
 #include "ui/gfx/animation/linear_animation.h"
 #include "ui/gfx/canvas.h"
@@ -86,13 +87,12 @@
 }  // namespace
 
 // Container view of notification and swipe control.
-// All children of UnifiedMessageListView should be MessageViewContainer.
-class UnifiedMessageListView::MessageViewContainer
-    : public MessageView::Observer,
-      public views::View {
+// All children of NotificationListView should be MessageViewContainer.
+class NotificationListView::MessageViewContainer : public MessageView::Observer,
+                                                   public views::View {
  public:
   MessageViewContainer(MessageView* message_view,
-                       UnifiedMessageListView* list_view)
+                       NotificationListView* list_view)
       : message_view_(message_view),
         list_view_(list_view),
         control_view_(new NotificationSwipeControlView(message_view)) {
@@ -224,7 +224,7 @@
     return message_view_->GetSlideAmount() < 0 ? -1 : 1;
   }
 
-  // Allows UnifiedMessageListView to force preferred size to change during
+  // Allows NotificationListView to force preferred size to change during
   // animations.
   void TriggerPreferredSizeChangedForAnimation() {
     views::View::PreferredSizeChanged();
@@ -237,7 +237,7 @@
       return;
 
     // PreferredSizeChanged will trigger
-    // UnifiedMessageListView::ChildPreferredSizeChanged.
+    // NotificationListView::ChildPreferredSizeChanged.
     base::ScopedClosureRunner defer_preferred_size_changed(base::BindOnce(
         &MessageViewContainer::PreferredSizeChanged, base::Unretained(this)));
 
@@ -407,12 +407,12 @@
   bool need_update_corner_radius_ = true;
 
   MessageView* const message_view_;
-  UnifiedMessageListView* const list_view_;
+  NotificationListView* const list_view_;
   NotificationSwipeControlView* const control_view_;
 };
 
-UnifiedMessageListView::UnifiedMessageListView(
-    UnifiedMessageCenterView* message_center_view,
+NotificationListView::NotificationListView(
+    NotificationCenterView* message_center_view,
     scoped_refptr<UnifiedSystemTrayModel> model)
     : views::AnimationDelegateViews(this),
       message_center_view_(message_center_view),
@@ -432,14 +432,14 @@
   }
 }
 
-UnifiedMessageListView::~UnifiedMessageListView() {
+NotificationListView::~NotificationListView() {
   DCHECK(model_);
   model_->ClearNotificationChanges();
   for (auto* view : children())
     AsMVC(view)->StoreExpandedState(model_.get());
 }
 
-void UnifiedMessageListView::Init() {
+void NotificationListView::Init() {
   DCHECK(model_);
   bool is_latest = true;
   for (auto* notification :
@@ -459,7 +459,7 @@
   UpdateBounds();
 }
 
-void UnifiedMessageListView::ClearAllWithAnimation() {
+void NotificationListView::ClearAllWithAnimation() {
   if (state_ == State::CLEAR_ALL_STACKED || state_ == State::CLEAR_ALL_VISIBLE)
     return;
   ResetBounds();
@@ -487,7 +487,7 @@
 }
 
 std::vector<message_center::Notification*>
-UnifiedMessageListView::GetAllNotifications() const {
+NotificationListView::GetAllNotifications() const {
   std::vector<message_center::Notification*> notifications;
   for (views::View* view : children()) {
     // The view may be present in the view hierarchy, but deleted in the message
@@ -500,7 +500,7 @@
   return notifications;
 }
 
-std::vector<std::string> UnifiedMessageListView::GetAllNotificationIds() const {
+std::vector<std::string> NotificationListView::GetAllNotificationIds() const {
   std::vector<std::string> notifications;
   for (views::View* view : children()) {
     notifications.insert(notifications.begin(),
@@ -510,7 +510,7 @@
 }
 
 std::vector<message_center::Notification*>
-UnifiedMessageListView::GetNotificationsAboveY(int y_offset) const {
+NotificationListView::GetNotificationsAboveY(int y_offset) const {
   std::vector<message_center::Notification*> notifications;
   for (views::View* view : children()) {
     const int bottom_limit =
@@ -526,7 +526,7 @@
 }
 
 std::vector<message_center::Notification*>
-UnifiedMessageListView::GetNotificationsBelowY(int y_offset) const {
+NotificationListView::GetNotificationsBelowY(int y_offset) const {
   std::vector<message_center::Notification*> notifications;
   for (views::View* view : children()) {
     const int bottom_limit =
@@ -541,7 +541,7 @@
   return notifications;
 }
 
-std::vector<std::string> UnifiedMessageListView::GetNotificationIdsAboveY(
+std::vector<std::string> NotificationListView::GetNotificationIdsAboveY(
     int y_offset) const {
   std::vector<std::string> notifications;
   for (views::View* view : children()) {
@@ -555,7 +555,7 @@
   return notifications;
 }
 
-std::vector<std::string> UnifiedMessageListView::GetNotificationIdsBelowY(
+std::vector<std::string> NotificationListView::GetNotificationIdsBelowY(
     int y_offset) const {
   std::vector<std::string> notifications;
   for (views::View* view : children()) {
@@ -568,11 +568,11 @@
   return notifications;
 }
 
-int UnifiedMessageListView::GetTotalNotificationCount() const {
+int NotificationListView::GetTotalNotificationCount() const {
   return static_cast<int>(children().size());
 }
 
-int UnifiedMessageListView::GetTotalPinnedNotificationCount() const {
+int NotificationListView::GetTotalPinnedNotificationCount() const {
   int count = 0;
   for (auto* child : children()) {
     if (AsMVC(child)->IsPinned())
@@ -581,11 +581,11 @@
   return count;
 }
 
-bool UnifiedMessageListView::IsAnimating() const {
+bool NotificationListView::IsAnimating() const {
   return animation_->is_animating();
 }
 
-bool UnifiedMessageListView::IsAnimatingExpandOrCollapseContainer(
+bool NotificationListView::IsAnimatingExpandOrCollapseContainer(
     const views::View* view) const {
   if (!view || !expand_or_collapsing_container_)
     return false;
@@ -596,7 +596,7 @@
   return message_view_container == expand_or_collapsing_container_;
 }
 
-void UnifiedMessageListView::ChildPreferredSizeChanged(views::View* child) {
+void NotificationListView::ChildPreferredSizeChanged(views::View* child) {
   if (ignore_size_change_)
     return;
 
@@ -630,13 +630,13 @@
   ResetBounds();
 }
 
-void UnifiedMessageListView::PreferredSizeChanged() {
+void NotificationListView::PreferredSizeChanged() {
   views::View::PreferredSizeChanged();
   if (message_center_view_)
     message_center_view_->ListPreferredSizeChanged();
 }
 
-void UnifiedMessageListView::Layout() {
+void NotificationListView::Layout() {
   for (auto* child : children()) {
     auto* view = AsMVC(child);
     if (state_ == State::IDLE) {
@@ -648,7 +648,7 @@
   }
 }
 
-gfx::Rect UnifiedMessageListView::GetNotificationBounds(
+gfx::Rect NotificationListView::GetNotificationBounds(
     const std::string& notification_id) const {
   const MessageViewContainer* child = nullptr;
   if (!notification_id.empty())
@@ -656,11 +656,11 @@
   return child ? child->bounds() : GetLastNotificationBounds();
 }
 
-gfx::Rect UnifiedMessageListView::GetLastNotificationBounds() const {
+gfx::Rect NotificationListView::GetLastNotificationBounds() const {
   return children().empty() ? gfx::Rect() : children().back()->bounds();
 }
 
-gfx::Rect UnifiedMessageListView::GetNotificationBoundsBelowY(
+gfx::Rect NotificationListView::GetNotificationBoundsBelowY(
     int y_offset) const {
   const auto it = base::ranges::lower_bound(
       children(), y_offset, {},
@@ -668,7 +668,7 @@
   return (it == children().cend()) ? gfx::Rect() : (*it)->bounds();
 }
 
-gfx::Size UnifiedMessageListView::CalculatePreferredSize() const {
+gfx::Size NotificationListView::CalculatePreferredSize() const {
   if (state_ == State::IDLE)
     return gfx::Size(message_view_width_, target_height_);
 
@@ -677,17 +677,13 @@
                                                target_height_));
 }
 
-const char* UnifiedMessageListView::GetClassName() const {
-  return "UnifiedMessageListView";
-}
-
-void UnifiedMessageListView::AnimateResize() {
-  // TODO(crbug/1330026): Refactor UnifiedMessageListView animations to use
+void NotificationListView::AnimateResize() {
+  // TODO(crbug/1330026): Refactor NotificationListView animations to use
   // animation builder instead of the existing layout based animations.
 }
 
 message_center::MessageView*
-UnifiedMessageListView::GetMessageViewForNotificationId(const std::string& id) {
+NotificationListView::GetMessageViewForNotificationId(const std::string& id) {
   auto it = base::ranges::find(children(), id, [](auto* child) {
     DCHECK(child->GetClassName() == kMessageViewContainerClassName);
     return static_cast<MessageViewContainer*>(child)
@@ -700,7 +696,7 @@
   return static_cast<MessageViewContainer*>(*it)->message_view();
 }
 
-void UnifiedMessageListView::ConvertNotificationViewToGroupedNotificationView(
+void NotificationListView::ConvertNotificationViewToGroupedNotificationView(
     const std::string& ungrouped_notification_id,
     const std::string& new_grouped_notification_id) {
   auto* message_view =
@@ -709,14 +705,14 @@
     message_view->set_notification_id(new_grouped_notification_id);
 }
 
-void UnifiedMessageListView::ConvertGroupedNotificationViewToNotificationView(
+void NotificationListView::ConvertGroupedNotificationViewToNotificationView(
     const std::string& grouped_notification_id,
     const std::string& new_single_notification_id) {
   GetMessageViewForNotificationId(grouped_notification_id)
       ->set_notification_id(new_single_notification_id);
 }
 
-void UnifiedMessageListView::OnNotificationAdded(const std::string& id) {
+void NotificationListView::OnNotificationAdded(const std::string& id) {
   auto* notification = MessageCenter::Get()->FindVisibleNotificationById(id);
   if (!notification)
     return;
@@ -772,8 +768,8 @@
   ResetBounds();
 }
 
-void UnifiedMessageListView::OnNotificationRemoved(const std::string& id,
-                                                   bool by_user) {
+void NotificationListView::OnNotificationRemoved(const std::string& id,
+                                                 bool by_user) {
   if (ignore_notification_remove_)
     return;
 
@@ -794,7 +790,7 @@
     child->SlideOutAndClose();
 }
 
-void UnifiedMessageListView::OnNotificationSlidOut() {
+void NotificationListView::OnNotificationSlidOut() {
   DeleteRemovedNotifications();
 
   // |message_center_view_| can be null in tests.
@@ -806,7 +802,7 @@
   StartAnimation();
 }
 
-void UnifiedMessageListView::OnNotificationUpdated(const std::string& id) {
+void NotificationListView::OnNotificationUpdated(const std::string& id) {
   auto* notification = MessageCenter::Get()->FindVisibleNotificationById(id);
   if (!notification)
     return;
@@ -823,8 +819,7 @@
   ResetBounds();
 }
 
-void UnifiedMessageListView::OnSlideStarted(
-    const std::string& notification_id) {
+void NotificationListView::OnSlideStarted(const std::string& notification_id) {
   // When the swipe control for |notification_id| is shown, hide all other swipe
   // controls.
   for (auto* child : children()) {
@@ -834,25 +829,25 @@
   }
 }
 
-void UnifiedMessageListView::OnCloseButtonPressed(
+void NotificationListView::OnCloseButtonPressed(
     const std::string& notification_id) {
   metrics_utils::LogClosedByUser(notification_id, /*is_swipe=*/false,
                                  /*is_popup=*/false);
 }
 
-void UnifiedMessageListView::OnSettingsButtonPressed(
+void NotificationListView::OnSettingsButtonPressed(
     const std::string& notification_id) {
   metrics_utils::LogSettingsShown(notification_id, /*is_slide_controls=*/false,
                                   /*is_popup=*/false);
 }
 
-void UnifiedMessageListView::OnSnoozeButtonPressed(
+void NotificationListView::OnSnoozeButtonPressed(
     const std::string& notification_id) {
   metrics_utils::LogSnoozed(notification_id, /*is_slide_controls=*/false,
                             /*is_popup=*/false);
 }
 
-void UnifiedMessageListView::AnimationEnded(const gfx::Animation* animation) {
+void NotificationListView::AnimationEnded(const gfx::Animation* animation) {
   if (throughput_tracker_) {
     // Reset `throughput_tracker_` to reset animation metrics recording.
     throughput_tracker_->Stop();
@@ -886,7 +881,7 @@
     StartAnimation();
 }
 
-void UnifiedMessageListView::AnimationProgressed(
+void NotificationListView::AnimationProgressed(
     const gfx::Animation* animation) {
   if (state_ == State::EXPAND_OR_COLLAPSE)
     expand_or_collapsing_container_->TriggerPreferredSizeChangedForAnimation();
@@ -894,12 +889,11 @@
   PreferredSizeChanged();
 }
 
-void UnifiedMessageListView::AnimationCanceled(
-    const gfx::Animation* animation) {
+void NotificationListView::AnimationCanceled(const gfx::Animation* animation) {
   AnimationEnded(animation);
 }
 
-MessageView* UnifiedMessageListView::CreateMessageView(
+MessageView* NotificationListView::CreateMessageView(
     const Notification& notification) {
   auto* message_view =
       MessageViewFactory::Create(notification, /*shown_in_popup=*/false)
@@ -908,7 +902,7 @@
   return message_view;
 }
 
-void UnifiedMessageListView::ConfigureMessageView(
+void NotificationListView::ConfigureMessageView(
     message_center::MessageView* message_view) {
   // Setting grouped notifications as nested is handled in
   // `AshNotificationView`.
@@ -924,37 +918,37 @@
 }
 
 std::vector<message_center::Notification*>
-UnifiedMessageListView::GetStackedNotifications() const {
+NotificationListView::GetStackedNotifications() const {
   return message_center_view_->GetStackedNotifications();
 }
 
 std::vector<std::string>
-UnifiedMessageListView::GetNonVisibleNotificationIdsInViewHierarchy() const {
+NotificationListView::GetNonVisibleNotificationIdsInViewHierarchy() const {
   return message_center_view_->GetNonVisibleNotificationIdsInViewHierarchy();
 }
 
 // static
-const UnifiedMessageListView::MessageViewContainer*
-UnifiedMessageListView::AsMVC(const views::View* v) {
+const NotificationListView::MessageViewContainer* NotificationListView::AsMVC(
+    const views::View* v) {
   return static_cast<const MessageViewContainer*>(v);
 }
 
 // static
-UnifiedMessageListView::MessageViewContainer* UnifiedMessageListView::AsMVC(
+NotificationListView::MessageViewContainer* NotificationListView::AsMVC(
     views::View* v) {
   return static_cast<MessageViewContainer*>(v);
 }
 
-const UnifiedMessageListView::MessageViewContainer*
-UnifiedMessageListView::GetNotificationById(const std::string& id) const {
+const NotificationListView::MessageViewContainer*
+NotificationListView::GetNotificationById(const std::string& id) const {
   const auto i = base::ranges::find(children(), id, [](const auto* v) {
     return AsMVC(v)->GetNotificationId();
   });
   return (i == children().cend()) ? nullptr : AsMVC(*i);
 }
 
-UnifiedMessageListView::MessageViewContainer*
-UnifiedMessageListView::GetNextRemovableNotification() {
+NotificationListView::MessageViewContainer*
+NotificationListView::GetNextRemovableNotification() {
   if (is_notifications_refresh_enabled_) {
     const auto i = base::ranges::find_if_not(
         base::Reversed(children()),
@@ -967,13 +961,13 @@
   return (i == children().cend()) ? nullptr : AsMVC(*i);
 }
 
-void UnifiedMessageListView::CollapseAllNotifications() {
+void NotificationListView::CollapseAllNotifications() {
   base::AutoReset<bool> auto_reset(&ignore_size_change_, true);
   for (auto* child : children())
     AsMVC(child)->Collapse();
 }
 
-void UnifiedMessageListView::UpdateBorders(bool force_update) {
+void NotificationListView::UpdateBorders(bool force_update) {
   // The top notification is drawn with rounded corners when the stacking bar
   // is not shown.
   bool is_top = state_ != State::MOVE_DOWN;
@@ -987,7 +981,7 @@
   }
 }
 
-void UnifiedMessageListView::UpdateBounds() {
+void NotificationListView::UpdateBounds() {
   int y = 0;
   for (auto* child : children()) {
     auto* view = AsMVC(child);
@@ -1012,7 +1006,7 @@
   target_height_ = y;
 }
 
-void UnifiedMessageListView::ResetBounds() {
+void NotificationListView::ResetBounds() {
   DeleteRemovedNotifications();
   UpdateBounds();
 
@@ -1023,7 +1017,7 @@
     PreferredSizeChanged();
 }
 
-void UnifiedMessageListView::InterruptClearAll() {
+void NotificationListView::InterruptClearAll() {
   if (state_ != State::CLEAR_ALL_STACKED && state_ != State::CLEAR_ALL_VISIBLE)
     return;
 
@@ -1036,7 +1030,7 @@
   DeleteRemovedNotifications();
 }
 
-void UnifiedMessageListView::DeleteRemovedNotifications() {
+void NotificationListView::DeleteRemovedNotifications() {
   DCHECK(model_);
   views::View::Views removed_views;
   base::ranges::copy_if(children(), std::back_inserter(removed_views),
@@ -1055,7 +1049,7 @@
   UpdateBorders(/*force_update=*/false);
 }
 
-void UnifiedMessageListView::StartAnimation() {
+void NotificationListView::StartAnimation() {
   DCHECK_NE(state_, State::IDLE);
 
   base::TimeDelta animation_duration;
@@ -1098,7 +1092,7 @@
   animation_->Start();
 }
 
-void UnifiedMessageListView::UpdateClearAllAnimation() {
+void NotificationListView::UpdateClearAllAnimation() {
   DCHECK(state_ == State::CLEAR_ALL_STACKED ||
          state_ == State::CLEAR_ALL_VISIBLE);
 
@@ -1141,7 +1135,7 @@
   }
 }
 
-double UnifiedMessageListView::GetCurrentValue() const {
+double NotificationListView::GetCurrentValue() const {
   gfx::Tween::Type tween;
   switch (state_) {
     case State::IDLE:
@@ -1164,4 +1158,7 @@
   return gfx::Tween::CalculateValue(tween, animation_->GetCurrentValue());
 }
 
+BEGIN_METADATA(NotificationListView, views::View);
+END_METADATA
+
 }  // namespace ash
diff --git a/ash/system/message_center/unified_message_list_view.h b/ash/system/notification_center/notification_list_view.h
similarity index 88%
rename from ash/system/message_center/unified_message_list_view.h
rename to ash/system/notification_center/notification_list_view.h
index 001fb362..4de1e4b6 100644
--- a/ash/system/message_center/unified_message_list_view.h
+++ b/ash/system/notification_center/notification_list_view.h
@@ -1,9 +1,9 @@
-// Copyright 2018 The Chromium Authors
+// 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_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_LIST_VIEW_H_
-#define ASH_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_LIST_VIEW_H_
+#ifndef ASH_SYSTEM_NOTIFICATION_CENTER_NOTIFICATION_LIST_VIEW_H_
+#define ASH_SYSTEM_NOTIFICATION_CENTER_NOTIFICATION_LIST_VIEW_H_
 
 #include "ash/ash_export.h"
 #include "ash/system/unified/unified_system_tray_model.h"
@@ -29,25 +29,26 @@
 
 namespace ash {
 
-class UnifiedMessageCenterView;
+class NotificationCenterView;
 class UnifiedSystemTrayModel;
 
 // Manages list of notifications. The class doesn't know about the ScrollView
-// it's enclosed. This class is used only from UnifiedMessageCenterView.
-class ASH_EXPORT UnifiedMessageListView
+// it's enclosed. This class is used only from NotificationCenterView.
+class ASH_EXPORT NotificationListView
     : public views::View,
       public message_center::MessageCenterObserver,
       public message_center::NotificationViewController,
       public message_center::MessageView::Observer,
       public views::AnimationDelegateViews {
  public:
+  METADATA_HEADER(NotificationListView);
+
   // |message_center_view| can be null in unit tests.
-  UnifiedMessageListView(UnifiedMessageCenterView* message_center_view,
-                         scoped_refptr<UnifiedSystemTrayModel> model);
-  UnifiedMessageListView(const UnifiedMessageListView& other) = delete;
-  UnifiedMessageListView& operator=(const UnifiedMessageListView& other) =
-      delete;
-  ~UnifiedMessageListView() override;
+  NotificationListView(NotificationCenterView* message_center_view,
+                       scoped_refptr<UnifiedSystemTrayModel> model);
+  NotificationListView(const NotificationListView& other) = delete;
+  NotificationListView& operator=(const NotificationListView& other) = delete;
+  ~NotificationListView() override;
 
   // Initializes the view with existing notifications. Should be called right
   // after ctor.
@@ -122,7 +123,6 @@
   void PreferredSizeChanged() override;
   void Layout() override;
   gfx::Size CalculatePreferredSize() const override;
-  const char* GetClassName() const override;
 
   // message_center::NotificationViewController:
   void AnimateResize() override;
@@ -171,13 +171,13 @@
       const;
 
  private:
-  friend class UnifiedMessageCenterViewTest;
-  friend class UnifiedMessageListViewTest;
+  friend class NotificationCenterViewTest;
+  friend class NotificationListViewTest;
   friend class UnifiedMessageCenterBubbleTest;
   class Background;
   class MessageViewContainer;
 
-  // UnifiedMessageListView always runs a single animation at one time. When
+  // NotificationListView always runs a single animation at one time. When
   // `state_` is IDLE, `animation_->is_animating()` is always false and vice
   // versa.
   enum class State {
@@ -207,7 +207,7 @@
   const MessageViewContainer* GetNotificationById(const std::string& id) const;
   MessageViewContainer* GetNotificationById(const std::string& id) {
     return const_cast<MessageViewContainer*>(
-        static_cast<const UnifiedMessageListView*>(this)->GetNotificationById(
+        static_cast<const NotificationListView*>(this)->GetNotificationById(
             id));
   }
 
@@ -249,7 +249,7 @@
   // Updates the state between each Clear All animation phase.
   void UpdateClearAllAnimation();
 
-  UnifiedMessageCenterView* const message_center_view_;
+  NotificationCenterView* const message_center_view_;
   scoped_refptr<UnifiedSystemTrayModel> model_;
 
   // Non-null during State::EXPAND_OR_COLLAPSE. Keeps track of the
@@ -265,7 +265,7 @@
   // ClearAllWithAnimation().
   bool ignore_notification_remove_ = false;
 
-  // Manages notification closing animation. UnifiedMessageListView does not use
+  // Manages notification closing animation. `NotificationListView` does not use
   // implicit animation.
   const std::unique_ptr<gfx::LinearAnimation> animation_;
 
@@ -274,15 +274,15 @@
 
   State state_ = State::IDLE;
 
-  // The height the UnifiedMessageListView starts animating from. If not
+  // The height the `NotificationListView` starts animating from. If not
   // animating, it's ignored.
   int start_height_ = 0;
 
-  // The final height of the UnifiedMessageListView. If not animating, it's same
+  // The final height of `the NotificationListView`. If not animating, it's same
   // as height().
   int target_height_ = 0;
 
-  // True if the UnifiedMessageListView is currently deleting notifications
+  // True if the `NotificationListView` is currently deleting notifications
   // marked for removal. This check is needed to prevent re-entrancing issues
   // (e.g. crbug.com/933327) caused by the View destructor.
   bool is_deleting_removed_notifications_ = false;
@@ -301,4 +301,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_LIST_VIEW_H_
+#endif  // ASH_SYSTEM_NOTIFICATION_CENTER_NOTIFICATION_LIST_VIEW_H_
diff --git a/ash/system/message_center/unified_message_list_view_unittest.cc b/ash/system/notification_center/notification_list_view_unittest.cc
similarity index 90%
rename from ash/system/message_center/unified_message_list_view_unittest.cc
rename to ash/system/notification_center/notification_list_view_unittest.cc
index cc8961be..f862c59 100644
--- a/ash/system/message_center/unified_message_list_view_unittest.cc
+++ b/ash/system/notification_center/notification_list_view_unittest.cc
@@ -1,8 +1,8 @@
-// Copyright 2018 The Chromium Authors
+// 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/system/message_center/unified_message_list_view.h"
+#include "ash/system/notification_center/notification_list_view.h"
 
 #include "ash/bubble/bubble_constants.h"
 #include "ash/constants/ash_features.h"
@@ -71,16 +71,15 @@
   absl::optional<float> slide_amount_;
 };
 
-class TestUnifiedMessageListView : public UnifiedMessageListView {
+class TestNotificationListView : public NotificationListView {
  public:
-  explicit TestUnifiedMessageListView(UnifiedSystemTrayModel* model)
-      : UnifiedMessageListView(nullptr, model) {}
+  explicit TestNotificationListView(UnifiedSystemTrayModel* model)
+      : NotificationListView(nullptr, model) {}
 
-  TestUnifiedMessageListView(const TestUnifiedMessageListView&) = delete;
-  TestUnifiedMessageListView& operator=(const TestUnifiedMessageListView&) =
-      delete;
+  TestNotificationListView(const TestNotificationListView&) = delete;
+  TestNotificationListView& operator=(const TestNotificationListView&) = delete;
 
-  ~TestUnifiedMessageListView() override = default;
+  ~TestNotificationListView() override = default;
 
   void set_stacked_notification_count(int stacked_notification_count) {
     stacked_notifications_.clear();
@@ -99,7 +98,7 @@
     }
   }
 
-  // UnifiedMessageListView:
+  // NotificationListView:
   message_center::MessageView* CreateMessageView(
       const message_center::Notification& notification) override {
     auto* message_view = new TestNotificationView(notification);
@@ -126,16 +125,15 @@
 
 // The base test class, has no params so tests with no params can inherit from
 // this.
-class UnifiedMessageListViewTest : public AshTestBase,
-                                   public views::ViewObserver {
+class NotificationListViewTest : public AshTestBase,
+                                 public views::ViewObserver {
  public:
-  UnifiedMessageListViewTest() = default;
+  NotificationListViewTest() = default;
 
-  UnifiedMessageListViewTest(const UnifiedMessageListViewTest&) = delete;
-  UnifiedMessageListViewTest& operator=(const UnifiedMessageListViewTest&) =
-      delete;
+  NotificationListViewTest(const NotificationListViewTest&) = delete;
+  NotificationListViewTest& operator=(const NotificationListViewTest&) = delete;
 
-  ~UnifiedMessageListViewTest() override = default;
+  ~NotificationListViewTest() override = default;
 
   void SetUp() override {
     AshTestBase::SetUp();
@@ -143,7 +141,7 @@
   }
 
   void TearDown() override {
-    message_list_view_.reset();
+    notification_list_view_.reset();
     model_.reset();
     AshTestBase::TearDown();
   }
@@ -191,15 +189,15 @@
   }
 
   void CreateMessageListView() {
-    message_list_view_ =
-        std::make_unique<TestUnifiedMessageListView>(model_.get());
-    message_list_view_->Init();
-    message_list_view_->AddObserver(this);
-    OnViewPreferredSizeChanged(message_list_view_.get());
+    notification_list_view_ =
+        std::make_unique<TestNotificationListView>(model_.get());
+    notification_list_view_->Init();
+    notification_list_view_->AddObserver(this);
+    OnViewPreferredSizeChanged(notification_list_view_.get());
     size_changed_count_ = 0;
   }
 
-  void DestroyMessageListView() { message_list_view_.reset(); }
+  void DestroyMessageListView() { notification_list_view_.reset(); }
 
   TestNotificationView* GetMessageViewAt(size_t index) const {
     return static_cast<TestNotificationView*>(
@@ -228,8 +226,8 @@
 
   bool IsAnimating() { return message_list_view()->animation_->is_animating(); }
 
-  TestUnifiedMessageListView* message_list_view() const {
-    return message_list_view_.get();
+  TestNotificationListView* message_list_view() const {
+    return notification_list_view_.get();
   }
 
   int size_changed_count() const { return size_changed_count_; }
@@ -243,22 +241,22 @@
   int size_changed_count_ = 0;
 
   scoped_refptr<UnifiedSystemTrayModel> model_;
-  std::unique_ptr<TestUnifiedMessageListView> message_list_view_;
+  std::unique_ptr<TestNotificationListView> notification_list_view_;
 };
 
 // Tests with NotificationsRefresh enabled and disabled.
-class ParameterizedUnifiedMessageListViewTest
-    : public UnifiedMessageListViewTest,
+class ParameterizedNotificationListViewTest
+    : public NotificationListViewTest,
       public testing::WithParamInterface<bool> {
  public:
-  ParameterizedUnifiedMessageListViewTest() = default;
+  ParameterizedNotificationListViewTest() = default;
 
-  ParameterizedUnifiedMessageListViewTest(
-      const ParameterizedUnifiedMessageListViewTest&) = delete;
-  ParameterizedUnifiedMessageListViewTest& operator=(
-      const ParameterizedUnifiedMessageListViewTest&) = delete;
+  ParameterizedNotificationListViewTest(
+      const ParameterizedNotificationListViewTest&) = delete;
+  ParameterizedNotificationListViewTest& operator=(
+      const ParameterizedNotificationListViewTest&) = delete;
 
-  ~ParameterizedUnifiedMessageListViewTest() override = default;
+  ~ParameterizedNotificationListViewTest() override = default;
 
   // AshTestBase:
   void SetUp() override {
@@ -275,7 +273,7 @@
                                  chromeos::features::kDarkLightMode});
     }
 
-    UnifiedMessageListViewTest::SetUp();
+    NotificationListViewTest::SetUp();
   }
 
   int GetMessageCenterNotificationCornerRadius() {
@@ -291,10 +289,10 @@
 };
 
 INSTANTIATE_TEST_SUITE_P(All,
-                         ParameterizedUnifiedMessageListViewTest,
+                         ParameterizedNotificationListViewTest,
                          testing::Bool() /* IsNotificationsRefreshEnabled() */);
 
-TEST_P(ParameterizedUnifiedMessageListViewTest, Open) {
+TEST_P(ParameterizedNotificationListViewTest, Open) {
   auto id0 = AddNotification();
   auto id1 = AddNotification();
   auto id2 = AddNotification();
@@ -354,7 +352,7 @@
   EXPECT_LT(0, message_list_view()->GetPreferredSize().height());
 }
 
-TEST_P(ParameterizedUnifiedMessageListViewTest, AddNotifications) {
+TEST_P(ParameterizedNotificationListViewTest, AddNotifications) {
   CreateMessageListView();
   EXPECT_EQ(0, message_list_view()->GetPreferredSize().height());
 
@@ -368,9 +366,9 @@
   EXPECT_EQ(kMessageCenterNotificationTopBottomCornerRadius,
             GetMessageViewAt(0)->bottom_radius());
 
-  int previous_message_list_view_height =
+  int previous_notification_list_view_height =
       message_list_view()->GetPreferredSize().height();
-  EXPECT_LT(0, previous_message_list_view_height);
+  EXPECT_LT(0, previous_notification_list_view_height);
 
   gfx::Rect previous_bounds = GetMessageViewBounds(0);
   auto id1 = AddNotification();
@@ -380,7 +378,7 @@
             GetMessageViewAt(features::IsNotificationsRefreshEnabled() ? 0 : 1)
                 ->notification_id());
 
-  EXPECT_LT(previous_message_list_view_height,
+  EXPECT_LT(previous_notification_list_view_height,
             message_list_view()->GetPreferredSize().height());
 
   if (!IsNotificationsRefreshEnabled()) {
@@ -411,7 +409,7 @@
             GetMessageViewAt(1)->bottom_radius());
 }
 
-TEST_P(ParameterizedUnifiedMessageListViewTest, RemoveNotification) {
+TEST_P(ParameterizedNotificationListViewTest, RemoveNotification) {
   auto id0 = AddNotification();
   auto id1 = AddNotification();
 
@@ -449,7 +447,7 @@
   EXPECT_EQ(0, message_list_view()->GetPreferredSize().height());
 }
 
-TEST_P(ParameterizedUnifiedMessageListViewTest, CollapseOlderNotifications) {
+TEST_P(ParameterizedNotificationListViewTest, CollapseOlderNotifications) {
   AddNotification();
   CreateMessageListView();
   EXPECT_TRUE(GetMessageViewAt(0)->IsExpanded());
@@ -494,7 +492,7 @@
   }
 }
 
-TEST_P(ParameterizedUnifiedMessageListViewTest, RemovingNotificationAnimation) {
+TEST_P(ParameterizedNotificationListViewTest, RemovingNotificationAnimation) {
   auto id0 = AddNotification(/*pinned=*/false);
   auto id1 = AddNotification();
   auto id2 = AddNotification();
@@ -536,7 +534,7 @@
 }
 
 // Flaky: https://crbug.com/1292774.
-TEST_P(ParameterizedUnifiedMessageListViewTest, DISABLED_ResetAnimation) {
+TEST_P(ParameterizedNotificationListViewTest, DISABLED_ResetAnimation) {
   auto id0 = AddNotification();
   auto id1 = AddNotification();
   CreateMessageListView();
@@ -555,7 +553,7 @@
   EXPECT_EQ(id2, GetMessageViewAt(1)->notification_id());
 }
 
-TEST_P(ParameterizedUnifiedMessageListViewTest, KeepManuallyExpanded) {
+TEST_P(ParameterizedNotificationListViewTest, KeepManuallyExpanded) {
   AddNotification();
   AddNotification();
   CreateMessageListView();
@@ -609,7 +607,7 @@
   }
 }
 
-TEST_P(ParameterizedUnifiedMessageListViewTest,
+TEST_P(ParameterizedNotificationListViewTest,
        ClearAllWithOnlyVisibleNotifications) {
   AddNotification();
   AddNotification();
@@ -652,7 +650,7 @@
   EXPECT_FALSE(IsAnimating());
 }
 
-TEST_P(ParameterizedUnifiedMessageListViewTest,
+TEST_P(ParameterizedNotificationListViewTest,
        ClearAllWithStackingNotifications) {
   AddNotification();
   AddNotification();
@@ -700,7 +698,7 @@
   EXPECT_FALSE(IsAnimating());
 }
 
-TEST_P(ParameterizedUnifiedMessageListViewTest, ClearAllClosedInTheMiddle) {
+TEST_P(ParameterizedNotificationListViewTest, ClearAllClosedInTheMiddle) {
   AddNotification();
   AddNotification();
   AddNotification();
@@ -713,7 +711,7 @@
   EXPECT_TRUE(MessageCenter::Get()->GetVisibleNotifications().empty());
 }
 
-TEST_P(ParameterizedUnifiedMessageListViewTest, ClearAllInterrupted) {
+TEST_P(ParameterizedNotificationListViewTest, ClearAllInterrupted) {
   AddNotification();
   AddNotification();
   AddNotification();
@@ -727,8 +725,7 @@
   EXPECT_TRUE(MessageCenter::Get()->FindVisibleNotificationById(new_id));
 }
 
-TEST_P(ParameterizedUnifiedMessageListViewTest,
-       ClearAllWithPinnedNotifications) {
+TEST_P(ParameterizedNotificationListViewTest, ClearAllWithPinnedNotifications) {
   AddNotification(/*pinned=*/true);
   AddNotification();
   AddNotification();
@@ -740,7 +737,7 @@
 }
 
 // Flaky: https://crbug.com/1292701.
-TEST_P(ParameterizedUnifiedMessageListViewTest,
+TEST_P(ParameterizedNotificationListViewTest,
        DISABLED_UserSwipesAwayNotification) {
   // Show message list with two notifications.
   AddNotification();
@@ -767,7 +764,7 @@
   EXPECT_FALSE(message_list_view()->IsAnimating());
 }
 
-TEST_P(ParameterizedUnifiedMessageListViewTest, InitInSortedOrder) {
+TEST_P(ParameterizedNotificationListViewTest, InitInSortedOrder) {
   // MessageViews should be ordered, from top down: [ id1, id2, id0 ].
   auto id0 = AddNotification(/*pinned=*/true);
   OffsetNotificationTimestamp(id0, 2000 /* milliseconds */);
@@ -789,8 +786,7 @@
   }
 }
 
-TEST_P(ParameterizedUnifiedMessageListViewTest,
-       NotificationAddedInSortedOrder) {
+TEST_P(ParameterizedNotificationListViewTest, NotificationAddedInSortedOrder) {
   auto id0 = AddNotification(/*pinned=*/true);
   OffsetNotificationTimestamp(id0, 3000 /* milliseconds */);
   auto id1 = AddNotification();
@@ -829,19 +825,18 @@
 }
 
 // Tests only with NotificationsRefresh enabled.
-class RefreshedUnifiedMessageListView : public UnifiedMessageListViewTest {
+class RefreshedNotificationListView : public NotificationListViewTest {
  public:
-  RefreshedUnifiedMessageListView() = default;
-  RefreshedUnifiedMessageListView(const RefreshedUnifiedMessageListView&) =
-      delete;
-  RefreshedUnifiedMessageListView& operator=(
-      const RefreshedUnifiedMessageListView&) = delete;
-  ~RefreshedUnifiedMessageListView() override = default;
+  RefreshedNotificationListView() = default;
+  RefreshedNotificationListView(const RefreshedNotificationListView&) = delete;
+  RefreshedNotificationListView& operator=(
+      const RefreshedNotificationListView&) = delete;
+  ~RefreshedNotificationListView() override = default;
 
   void SetUp() override {
     scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
     scoped_feature_list_->InitAndEnableFeature(features::kNotificationsRefresh);
-    UnifiedMessageListViewTest::SetUp();
+    NotificationListViewTest::SetUp();
   }
 
   // Start sliding the message view at the given index in the list.
@@ -856,7 +851,7 @@
 };
 
 // Tests that preferred size changes upon toggle of expand/collapse.
-TEST_F(RefreshedUnifiedMessageListView, PreferredSizeChangesOnToggle) {
+TEST_F(RefreshedNotificationListView, PreferredSizeChangesOnToggle) {
   AddNotification(/*pinned=*/false, /*expandable=*/true);
   AddNotification(/*pinned=*/false, /*expandable=*/true);
   CreateMessageListView();
@@ -890,7 +885,7 @@
 
 // Tests that expanding a notification while a different notification is
 // expanding is handled gracefully.
-TEST_F(RefreshedUnifiedMessageListView, TwoExpandsInARow) {
+TEST_F(RefreshedNotificationListView, TwoExpandsInARow) {
   AddNotification(/*pinned=*/false, /*expandable=*/true);
   AddNotification(/*pinned=*/false, /*expandable=*/true);
   CreateMessageListView();
@@ -930,7 +925,7 @@
 }
 
 // Tests that collapsing/expanding is reversible.
-TEST_F(RefreshedUnifiedMessageListView, ReverseExpand) {
+TEST_F(RefreshedNotificationListView, ReverseExpand) {
   AddNotification(/*pinned=*/false, /*expandable=*/true);
   AddNotification(/*pinned=*/false, /*expandable=*/true);
   CreateMessageListView();
@@ -957,7 +952,7 @@
 }
 
 // Tests that destroying during a collapse animation does not crash.
-TEST_F(RefreshedUnifiedMessageListView, DestroyMessageListViewDuringCollapse) {
+TEST_F(RefreshedNotificationListView, DestroyMessageListViewDuringCollapse) {
   AddNotification(/*pinned=*/false, /*expandable=*/true);
   AddNotification(/*pinned=*/false, /*expandable=*/true);
   CreateMessageListView();
@@ -970,7 +965,7 @@
 
 // Tests that closing a notification while its collapse animation is ongoing
 // works properly.
-TEST_F(RefreshedUnifiedMessageListView, RemoveNotificationDuringCollapse) {
+TEST_F(RefreshedNotificationListView, RemoveNotificationDuringCollapse) {
   auto id1 = AddNotification(/*pinned=*/false, /*expandable=*/true);
   CreateMessageListView();
   auto* message_view = GetMessageViewAt(0);
@@ -996,7 +991,7 @@
 // Tests that expanding a notification at various stages while it is being
 // closed does not result in an animation.
 // TODO(crbug.com/1292775): Test is flaky.
-TEST_F(RefreshedUnifiedMessageListView,
+TEST_F(RefreshedNotificationListView,
        DISABLED_CollapseDuringCloseResultsInNoCollapseAnimation) {
   auto id1 = AddNotification(/*pinned=*/false, /*expandable=*/true);
   AddNotification(/*pinned=*/false, /*expandable=*/true);
@@ -1008,13 +1003,13 @@
   MessageCenter::Get()->RemoveNotification(id1, /*by_user=*/true);
   EXPECT_EQ(notification_container->GetPreferredSize(), pre_remove_size);
   // Removing the notification does not trigger an animation at the level of
-  // UnifiedMessageListView
+  // NotificationListView
   EXPECT_FALSE(message_list_view()->IsAnimating());
   EXPECT_FALSE(message_list_view()->IsAnimatingExpandOrCollapseContainer(
       notification_container));
 
   // Trigger the collapse before slide out completes, this should not trigger an
-  // animation for UnifiedMessageListView, and no animation should occur.
+  // animation for NotificationListView, and no animation should occur.
   // SlideOut animation happens at a lower level. Also, size changes should be
   // ignored when being removed.
   GetMessageViewAt(0)->SetExpanded(/*expanded=*/false);
@@ -1034,8 +1029,7 @@
 // Tests that collapsing a notification while it is being moved automatically
 // completes both animations.
 // TODO(crbug.com/1292816): Test is flaky.
-TEST_F(RefreshedUnifiedMessageListView,
-       DISABLED_CollapseDuringMoveNoAnimation) {
+TEST_F(RefreshedNotificationListView, DISABLED_CollapseDuringMoveNoAnimation) {
   auto to_be_removed_notification =
       AddNotification(/*pinned=*/false, /*expandable=*/true);
   auto to_be_collapsed_notification =
@@ -1071,7 +1065,7 @@
 
 // Tests that moving a notification while it is already collapsing completes
 // both animations.
-TEST_F(RefreshedUnifiedMessageListView, MoveDuringCollapseNoAnimation) {
+TEST_F(RefreshedNotificationListView, MoveDuringCollapseNoAnimation) {
   auto to_be_removed_notification =
       AddNotification(/*pinned=*/false, /*expandable=*/true);
   auto to_be_collapsed_notification =
@@ -1101,7 +1095,7 @@
       to_be_collapsed_message_view_container->GetPreferredSize().height());
 }
 
-TEST_F(RefreshedUnifiedMessageListView, SlideNotification) {
+TEST_F(RefreshedNotificationListView, SlideNotification) {
   // Show message list with four notifications.
   auto id0 = AddNotification();
   auto id1 = AddNotification();
diff --git a/ash/system/message_center/stacked_notification_bar.cc b/ash/system/notification_center/stacked_notification_bar.cc
similarity index 94%
rename from ash/system/message_center/stacked_notification_bar.cc
rename to ash/system/notification_center/stacked_notification_bar.cc
index 6e37f1e0..a549dfd 100644
--- a/ash/system/message_center/stacked_notification_bar.cc
+++ b/ash/system/notification_center/stacked_notification_bar.cc
@@ -1,8 +1,8 @@
-// Copyright 2019 The Chromium Authors
+// 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/system/message_center/stacked_notification_bar.h"
+#include "ash/system/notification_center/stacked_notification_bar.h"
 
 #include "ash/constants/ash_features.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -43,7 +43,7 @@
 
   StackingBarLabelButton(PressedCallback callback,
                          const std::u16string& text,
-                         UnifiedMessageCenterView* message_center_view)
+                         NotificationCenterView* notification_center_view)
       : PillButton(
             std::move(callback),
             text,
@@ -53,7 +53,7 @@
             /*use_light_colors=*/!features::IsNotificationsRefreshEnabled(),
             /*rounded_highlight_path=*/
             features::IsNotificationsRefreshEnabled()),
-        message_center_view_(message_center_view) {
+        notification_center_view_(notification_center_view) {
     const SkColor bg_color =
         features::IsNotificationsRefreshEnabled()
             ? gfx::kPlaceholderColor
@@ -70,12 +70,12 @@
 
   // PillButton:
   void AboutToRequestFocusFromTabTraversal(bool reverse) override {
-    if (message_center_view_->collapsed() && HasFocus())
-      message_center_view_->FocusOut(reverse);
+    if (notification_center_view_->collapsed() && HasFocus())
+      notification_center_view_->FocusOut(reverse);
   }
 
  private:
-  UnifiedMessageCenterView* message_center_view_;
+  NotificationCenterView* notification_center_view_;
 };
 
 BEGIN_METADATA(StackingBarLabelButton, PillButton)
@@ -241,24 +241,24 @@
 };
 
 StackedNotificationBar::StackedNotificationBar(
-    UnifiedMessageCenterView* message_center_view)
-    : message_center_view_(message_center_view),
+    NotificationCenterView* notification_center_view)
+    : notification_center_view_(notification_center_view),
       notification_icons_container_(
           AddChildView(std::make_unique<views::View>())),
       count_label_(AddChildView(std::make_unique<views::Label>())),
       spacer_(AddChildView(std::make_unique<views::View>())),
       clear_all_button_(AddChildView(std::make_unique<StackingBarLabelButton>(
-          base::BindRepeating(&UnifiedMessageCenterView::ClearAllNotifications,
-                              base::Unretained(message_center_view_)),
+          base::BindRepeating(&NotificationCenterView::ClearAllNotifications,
+                              base::Unretained(notification_center_view_)),
           l10n_util::GetStringUTF16(
               IDS_ASH_MESSAGE_CENTER_CLEAR_ALL_BUTTON_LABEL),
-          message_center_view))),
+          notification_center_view))),
       expand_all_button_(AddChildView(std::make_unique<StackingBarLabelButton>(
-          base::BindRepeating(&UnifiedMessageCenterView::ExpandMessageCenter,
-                              base::Unretained(message_center_view_)),
+          base::BindRepeating(&NotificationCenterView::ExpandMessageCenter,
+                              base::Unretained(notification_center_view_)),
           l10n_util::GetStringUTF16(
               IDS_ASH_MESSAGE_CENTER_EXPAND_ALL_NOTIFICATIONS_BUTTON_LABEL),
-          message_center_view))),
+          notification_center_view))),
       layout_manager_(SetLayoutManager(std::make_unique<views::BoxLayout>(
           views::BoxLayout::Orientation::kHorizontal,
           features::IsNotificationsRefreshEnabled() ? kNotificationBarPadding
@@ -336,7 +336,7 @@
 }
 
 void StackedNotificationBar::SetAnimationState(
-    UnifiedMessageCenterAnimationState animation_state) {
+    NotificationCenterAnimationState animation_state) {
   animation_state_ = animation_state;
   UpdateVisibility();
 }
@@ -559,15 +559,15 @@
     clear_all_button_->SetVisible(show_clear_all);
 
   switch (animation_state_) {
-    case UnifiedMessageCenterAnimationState::IDLE:
+    case NotificationCenterAnimationState::IDLE:
       SetVisible(
           (stacked_notification_count_ && total_notification_count_ > 1) ||
           show_clear_all || expand_all_button_->GetVisible());
       break;
-    case UnifiedMessageCenterAnimationState::HIDE_STACKING_BAR:
+    case NotificationCenterAnimationState::HIDE_STACKING_BAR:
       SetVisible(true);
       break;
-    case UnifiedMessageCenterAnimationState::COLLAPSE:
+    case NotificationCenterAnimationState::COLLAPSE:
       SetVisible(
           (stacked_notification_count_ && total_notification_count_ > 1) ||
           show_clear_all || expand_all_button_->GetVisible());
@@ -580,7 +580,8 @@
   // know the position where it may have been added.
   notification_icons_container_->RemoveAllChildViews();
   stacked_notification_count_ = 0;
-  UpdateStackedNotifications(message_center_view_->GetStackedNotifications());
+  UpdateStackedNotifications(
+      notification_center_view_->GetStackedNotifications());
 }
 
 void StackedNotificationBar::OnNotificationRemoved(const std::string& id,
diff --git a/ash/system/message_center/stacked_notification_bar.h b/ash/system/notification_center/stacked_notification_bar.h
similarity index 84%
rename from ash/system/message_center/stacked_notification_bar.h
rename to ash/system/notification_center/stacked_notification_bar.h
index de61f90..84d1b1b 100644
--- a/ash/system/message_center/stacked_notification_bar.h
+++ b/ash/system/notification_center/stacked_notification_bar.h
@@ -1,11 +1,11 @@
-// Copyright 2019 The Chromium Authors
+// 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_SYSTEM_MESSAGE_CENTER_STACKED_NOTIFICATION_BAR_H_
-#define ASH_SYSTEM_MESSAGE_CENTER_STACKED_NOTIFICATION_BAR_H_
+#ifndef ASH_SYSTEM_NOTIFICATION_CENTER_STACKED_NOTIFICATION_BAR_H_
+#define ASH_SYSTEM_NOTIFICATION_CENTER_STACKED_NOTIFICATION_BAR_H_
 
-#include "ash/system/message_center/unified_message_center_view.h"
+#include "ash/system/notification_center/notification_center_view.h"
 #include "base/memory/weak_ptr.h"
 #include "ui/message_center/message_center_observer.h"
 #include "ui/views/view.h"
@@ -28,7 +28,7 @@
                                public message_center::MessageCenterObserver {
  public:
   explicit StackedNotificationBar(
-      UnifiedMessageCenterView* message_center_view);
+      NotificationCenterView* notification_center_view);
 
   StackedNotificationBar(const StackedNotificationBar&) = delete;
   StackedNotificationBar& operator=(const StackedNotificationBar&) = delete;
@@ -43,7 +43,7 @@
               std::vector<message_center::Notification*> stacked_notifications);
 
   // Sets the current animation state.
-  void SetAnimationState(UnifiedMessageCenterAnimationState animation_state);
+  void SetAnimationState(NotificationCenterAnimationState animation_state);
 
   // Set notification bar state to collapsed.
   void SetCollapsed();
@@ -62,7 +62,7 @@
 
  private:
   class StackedNotificationBarIcon;
-  friend class UnifiedMessageCenterViewTest;
+  friend class NotificationCenterViewTest;
 
   // Clean up icon view after it's removal animation is complete, adds an icon
   // for `notification` if needed. Called from a callback registered in
@@ -100,10 +100,10 @@
   int pinned_notification_count_ = 0;
   int stacked_notification_count_ = 0;
 
-  UnifiedMessageCenterAnimationState animation_state_ =
-      UnifiedMessageCenterAnimationState::IDLE;
+  NotificationCenterAnimationState animation_state_ =
+      NotificationCenterAnimationState::IDLE;
 
-  UnifiedMessageCenterView* const message_center_view_;
+  NotificationCenterView* const notification_center_view_;
   views::View* notification_icons_container_;
   views::Label* const count_label_;
   views::View* const spacer_;
@@ -116,4 +116,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_SYSTEM_MESSAGE_CENTER_STACKED_NOTIFICATION_BAR_H_
+#endif  // ASH_SYSTEM_NOTIFICATION_CENTER_STACKED_NOTIFICATION_BAR_H_
diff --git a/ash/system/palette/palette_tray_unittest.cc b/ash/system/palette/palette_tray_unittest.cc
index 8a457c98..4e2dc45 100644
--- a/ash/system/palette/palette_tray_unittest.cc
+++ b/ash/system/palette/palette_tray_unittest.cc
@@ -10,6 +10,7 @@
 #include "ash/assistant/assistant_controller_impl.h"
 #include "ash/assistant/test/test_assistant_service.h"
 #include "ash/assistant/util/assistant_util.h"
+#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/constants/ash_switches.h"
 #include "ash/highlighter/highlighter_controller.h"
@@ -51,7 +52,10 @@
 
 class PaletteTrayTest : public AshTestBase {
  public:
-  PaletteTrayTest() = default;
+  PaletteTrayTest() {
+    feature_list_.InitAndDisableFeature(
+        ash::features::kDeprecateAssistantStylusFeatures);
+  }
 
   PaletteTrayTest(const PaletteTrayTest&) = delete;
   PaletteTrayTest& operator=(const PaletteTrayTest&) = delete;
@@ -104,6 +108,8 @@
   PaletteTray* palette_tray_ = nullptr;  // not owned
 
   std::unique_ptr<PaletteTrayTestApi> test_api_;
+
+  base::test::ScopedFeatureList feature_list_;
 };
 
 // Verify the palette tray button exists and but is not visible initially.
diff --git a/ash/system/palette/tools/metalayer_unittest.cc b/ash/system/palette/tools/metalayer_unittest.cc
index ed9c825..0d5e052 100644
--- a/ash/system/palette/tools/metalayer_unittest.cc
+++ b/ash/system/palette/tools/metalayer_unittest.cc
@@ -4,6 +4,7 @@
 
 #include <memory>
 
+#include "ash/constants/ash_features.h"
 #include "ash/highlighter/highlighter_controller.h"
 #include "ash/highlighter/highlighter_controller_test_api.h"
 #include "ash/public/cpp/assistant/assistant_state.h"
@@ -17,6 +18,7 @@
 #include "ash/test/ash_test_base.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "chromeos/ash/services/assistant/public/cpp/assistant_prefs.h"
 #include "components/prefs/pref_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -30,7 +32,10 @@
 // Base class for all metalayer ash tests.
 class MetalayerToolTest : public AshTestBase {
  public:
-  MetalayerToolTest() = default;
+  MetalayerToolTest() {
+    scoped_feature_list_.InitAndDisableFeature(
+        ash::features::kDeprecateAssistantStylusFeatures);
+  }
 
   MetalayerToolTest(const MetalayerToolTest&) = delete;
   MetalayerToolTest& operator=(const MetalayerToolTest&) = delete;
@@ -63,6 +68,7 @@
   std::unique_ptr<HighlighterControllerTestApi> highlighter_test_api_;
   std::unique_ptr<MockPaletteToolDelegate> palette_tool_delegate_;
   std::unique_ptr<PaletteTool> tool_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 }  // namespace
diff --git a/ash/system/privacy/privacy_indicators_controller_unittest.cc b/ash/system/privacy/privacy_indicators_controller_unittest.cc
index 8a2de481..9977248 100644
--- a/ash/system/privacy/privacy_indicators_controller_unittest.cc
+++ b/ash/system/privacy/privacy_indicators_controller_unittest.cc
@@ -9,8 +9,8 @@
 #include "ash/constants/ash_constants.h"
 #include "ash/system/message_center/ash_message_popup_collection.h"
 #include "ash/system/message_center/unified_message_center_bubble.h"
-#include "ash/system/message_center/unified_message_center_view.h"
-#include "ash/system/message_center/unified_message_list_view.h"
+#include "ash/system/notification_center/notification_center_view.h"
+#include "ash/system/notification_center/notification_list_view.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "base/bind.h"
@@ -62,8 +62,8 @@
   views::View* GetNotificationViewFromMessageCenter(const std::string& id) {
     return GetPrimaryUnifiedSystemTray()
         ->message_center_bubble()
-        ->message_center_view()
-        ->message_list_view()
+        ->notification_center_view()
+        ->notification_list_view()
         ->GetMessageViewForNotificationId(id);
   }
 
diff --git a/ash/system/time/calendar_view_unittest.cc b/ash/system/time/calendar_view_unittest.cc
index 56be474..0e348663 100644
--- a/ash/system/time/calendar_view_unittest.cc
+++ b/ash/system/time/calendar_view_unittest.cc
@@ -13,7 +13,7 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/icon_button.h"
 #include "ash/system/message_center/unified_message_center_bubble.h"
-#include "ash/system/message_center/unified_message_center_view.h"
+#include "ash/system/notification_center/notification_center_view.h"
 #include "ash/system/time/calendar_event_list_view.h"
 #include "ash/system/time/calendar_model.h"
 #include "ash/system/time/calendar_month_view.h"
@@ -2039,7 +2039,7 @@
   views::FocusManager* message_center_focus_manager() {
     return GetPrimaryUnifiedSystemTray()
         ->message_center_bubble()
-        ->message_center_view()
+        ->notification_center_view()
         ->GetFocusManager();
   }
 
diff --git a/ash/system/unified/unified_system_tray_unittest.cc b/ash/system/unified/unified_system_tray_unittest.cc
index 53e627e..d96fcf2 100644
--- a/ash/system/unified/unified_system_tray_unittest.cc
+++ b/ash/system/unified/unified_system_tray_unittest.cc
@@ -13,7 +13,7 @@
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/message_center/unified_message_center_bubble.h"
-#include "ash/system/message_center/unified_message_center_view.h"
+#include "ash/system/notification_center/notification_center_view.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/status_area_widget_test_helper.h"
 #include "ash/system/time/time_tray_item_view.h"
@@ -281,7 +281,7 @@
   tray->ShowBubble();
 
   auto* message_center_view =
-      tray->message_center_bubble()->message_center_view();
+      tray->message_center_bubble()->notification_center_view();
   auto* focus_manager = message_center_view->GetFocusManager();
 
   AddNotification();
@@ -323,7 +323,7 @@
   tray->ShowBubble();
 
   auto* message_center_bubble = tray->message_center_bubble();
-  auto* message_center_view = message_center_bubble->message_center_view();
+  auto* message_center_view = message_center_bubble->notification_center_view();
 
   AddNotification();
   AddNotification();
@@ -501,7 +501,7 @@
 
   // Ensure message center is collapsed when Calendar is not being shown.
   auto* message_center_view =
-      tray->message_center_bubble()->message_center_view();
+      tray->message_center_bubble()->notification_center_view();
   EXPECT_FALSE(tray->IsShowingCalendarView());
   EXPECT_TRUE(message_center_view->collapsed());
 
diff --git a/ash/system/unified/unified_system_tray_view.cc b/ash/system/unified/unified_system_tray_view.cc
index 4dd06b0a..481a1a5 100644
--- a/ash/system/unified/unified_system_tray_view.cc
+++ b/ash/system/unified/unified_system_tray_view.cc
@@ -12,7 +12,7 @@
 #include "ash/shell.h"
 #include "ash/system/media/unified_media_controls_container.h"
 #include "ash/system/message_center/ash_message_center_lock_screen_controller.h"
-#include "ash/system/message_center/unified_message_center_view.h"
+#include "ash/system/notification_center/notification_center_view.h"
 #include "ash/system/tray/interacted_by_tap_recorder.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/detailed_view_controller.h"
diff --git a/base/allocator/partition_alloc_support.cc b/base/allocator/partition_alloc_support.cc
index 54781e0..1b83b3f 100644
--- a/base/allocator/partition_alloc_support.cc
+++ b/base/allocator/partition_alloc_support.cc
@@ -503,7 +503,7 @@
                << "The dangling raw_ptr was released at:\n"
                << stack_trace_release << task_trace_release;
   }
-  IMMEDIATE_CRASH();
+  ImmediateCrash();
 }
 
 void ClearDanglingRawPtrBuffer() {
@@ -557,7 +557,7 @@
   LOG(ERROR) << "Detected dangling raw_ptr in unretained with id="
              << StringPrintf("0x%016" PRIxPTR, id) << ":\n\n"
              << task_trace << stack_trace;
-  IMMEDIATE_CRASH();
+  ImmediateCrash();
 }
 
 void InstallUnretainedDanglingRawPtrChecks() {
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc
index 428c555..d5b5b05 100644
--- a/base/allocator/partition_allocator/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -2110,11 +2110,10 @@
 //
 // Disable these test on Windows, since they run slower, so tend to timout and
 // cause flake.
-//
-// For Fuchsia, see https://crbug.com/779645.
 #if !BUILDFLAG(IS_WIN) &&          \
     (!defined(ARCH_CPU_64_BITS) || \
-     (BUILDFLAG(IS_POSIX) && !(BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID))))
+     (BUILDFLAG(IS_POSIX) && !(BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID)))) || \
+    BUILDFLAG(IS_FUCHSIA)
 #define MAYBE_RepeatedAllocReturnNullDirect RepeatedAllocReturnNullDirect
 #define MAYBE_RepeatedReallocReturnNullDirect RepeatedReallocReturnNullDirect
 #define MAYBE_RepeatedTryReallocReturnNullDirect \
diff --git a/base/android/orderfile/orderfile_call_graph_instrumentation.cc b/base/android/orderfile/orderfile_call_graph_instrumentation.cc
index 00b754a..a2a3660 100644
--- a/base/android/orderfile/orderfile_call_graph_instrumentation.cc
+++ b/base/android/orderfile/orderfile_call_graph_instrumentation.cc
@@ -180,7 +180,7 @@
     // Only the code in the native library is instrumented. Callees are expected
     // to be within the native library bounds.
     Disable();
-    IMMEDIATE_CRASH();
+    ImmediateCrash();
   }
 
   size_t offset = callee_address - start;
diff --git a/base/android/orderfile/orderfile_instrumentation.cc b/base/android/orderfile/orderfile_instrumentation.cc
index da73d4d32..2b0983ff 100644
--- a/base/android/orderfile/orderfile_instrumentation.cc
+++ b/base/android/orderfile/orderfile_instrumentation.cc
@@ -139,7 +139,7 @@
       // deadlock.  By crashing immediately we at least have a chance to get a
       // stack trace from the system to give some clue about the nature of the
       // problem.
-      IMMEDIATE_CRASH();
+      ImmediateCrash();
     }
 
     // We should really crash at the first instance, but it does happen on bots,
diff --git a/base/check.h b/base/check.h
index 3fbd09c..ea8ef67b 100644
--- a/base/check.h
+++ b/base/check.h
@@ -117,7 +117,7 @@
 // Note that this uses IMMEDIATE_CRASH_ALWAYS_INLINE to force-inline in debug
 // mode as well. See LoggingTest.CheckCausesDistinctBreakpoints.
 [[noreturn]] IMMEDIATE_CRASH_ALWAYS_INLINE void CheckFailure() {
-  IMMEDIATE_CRASH();
+  base::ImmediateCrash();
 }
 
 // Discard log strings to reduce code bloat.
diff --git a/base/check_example.cc b/base/check_example.cc
index 6c00cd0..3bfc184 100644
--- a/base/check_example.cc
+++ b/base/check_example.cc
@@ -18,7 +18,7 @@
 // See https://crbug.com/672699.
 
 #define BLINK_RELEASE_ASSERT_EQUIVALENT(assertion) \
-  (UNLIKELY(!(assertion)) ? (IMMEDIATE_CRASH()) : (void)0)
+  (UNLIKELY(!(assertion)) ? (base::ImmediateCrash()) : (void)0)
 
 void DoCheck(bool b) {
   CHECK(b) << "DoCheck " << b;
diff --git a/base/feature_list.cc b/base/feature_list.cc
index 471e2451..a2d8a8c1 100644
--- a/base/feature_list.cc
+++ b/base/feature_list.cc
@@ -9,9 +9,7 @@
 
 #include <stddef.h>
 
-#include "base/base_paths.h"
 #include "base/base_switches.h"
-#include "base/containers/contains.h"
 #include "base/debug/alias.h"
 #include "base/debug/stack_trace.h"
 #include "base/logging.h"
@@ -20,8 +18,8 @@
 #include "base/metrics/field_trial_param_associator.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/persistent_memory_allocator.h"
+#include "base/no_destructor.h"
 #include "base/notreached.h"
-#include "base/path_service.h"
 #include "base/pickle.h"
 #include "base/rand_util.h"
 #include "base/strings/string_piece.h"
@@ -40,9 +38,57 @@
 // have more control over initialization timing. Leaky.
 FeatureList* g_feature_list_instance = nullptr;
 
-// Tracks whether the FeatureList instance was initialized via an accessor, and
-// which Feature that accessor was for, if so.
-const Feature* g_initialized_from_accessor = nullptr;
+// Tracks access to Feature state before FeatureList registration.
+class EarlyFeatureAccessTracker {
+ public:
+  static EarlyFeatureAccessTracker* GetInstance() {
+    static NoDestructor<EarlyFeatureAccessTracker> instance;
+    return instance.get();
+  }
+
+  // Invoked when `feature` is accessed before FeatureList registration.
+  void AccessedFeature(const Feature& feature) {
+    AutoLock lock(lock_);
+    if (feature_)
+      return;
+
+    feature_ = &feature;
+    stack_trace_ = std::make_unique<debug::StackTrace>();
+  }
+
+  // Asserts that no feature was accessed before FeatureList registration.
+  void AssertNoAccess() {
+    AutoLock lock(lock_);
+    if (!feature_)
+      return;
+
+    DEBUG_ALIAS_FOR_CSTR(feature_name, feature_->name, 128);
+    CHECK(!feature_) << "Accessed feature " << feature_->name
+                     << " before FeatureList registration, in stack:"
+                     << stack_trace_->ToString();
+  }
+
+  // Resets the state of this tracker.
+  void Reset() {
+    AutoLock lock(lock_);
+    feature_ = nullptr;
+    stack_trace_.reset();
+  }
+
+ private:
+  friend class NoDestructor<EarlyFeatureAccessTracker>;
+
+  EarlyFeatureAccessTracker() = default;
+  ~EarlyFeatureAccessTracker() = default;
+
+  Lock lock_;
+
+  // First feature to be accessed before FeatureList registration.
+  const Feature* feature_ GUARDED_BY(lock_) = nullptr;
+
+  // Stack trace of the first feature access before FeatureList registration.
+  std::unique_ptr<debug::StackTrace> stack_trace_ GUARDED_BY(lock_);
+};
 
 // Controls whether a feature's override state will be cached in
 // `base::Feature::cached_value`. This field and the associated `base::Feature`
@@ -385,7 +431,7 @@
   CHECK(g_use_allowed) << "base::Feature not permitted for this module.";
 #endif
   if (!g_feature_list_instance) {
-    g_initialized_from_accessor = &feature;
+    EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(feature);
     return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
   }
   return g_feature_list_instance->IsFeatureEnabled(feature);
@@ -402,7 +448,7 @@
   CHECK(g_use_allowed) << "base::Feature not permitted for this module.";
 #endif
   if (!g_feature_list_instance) {
-    g_initialized_from_accessor = &feature;
+    EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(feature);
     // If there is no feature list, there can be no overrides.
     return absl::nullopt;
   }
@@ -416,7 +462,7 @@
   CHECK(g_use_allowed) << "base::Feature not permitted for this module.";
 #endif
   if (!g_feature_list_instance) {
-    g_initialized_from_accessor = &feature;
+    EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(feature);
     return nullptr;
   }
   return g_feature_list_instance->GetAssociatedFieldTrial(feature);
@@ -490,10 +536,7 @@
   // If the singleton was previously initialized from within an accessor, we
   // want to prevent callers from reinitializing the singleton and masking the
   // accessor call(s) which likely returned incorrect information.
-  if (g_initialized_from_accessor) {
-    DEBUG_ALIAS_FOR_CSTR(accessor_name, g_initialized_from_accessor->name, 128);
-    CHECK(!g_initialized_from_accessor);
-  }
+  EarlyFeatureAccessTracker::GetInstance()->AssertNoAccess();
   bool instance_existed_before = false;
   if (g_feature_list_instance) {
     if (g_feature_list_instance->initialized_from_command_line_)
@@ -524,10 +567,16 @@
   // Note: Intentional leak of global singleton.
   g_feature_list_instance = instance.release();
 
+  // TODO(crbug.com/1358639): Enable this check on all platforms.
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
+  EarlyFeatureAccessTracker::GetInstance()->AssertNoAccess();
+#endif
+
 #if !BUILDFLAG(IS_NACL)
   // Configured first because it takes precedence over the getrandom() trial.
   internal::ConfigureBoringSSLBackedRandBytesFieldTrial();
 #endif
+
 #if BUILDFLAG(IS_ANDROID)
   internal::ConfigureRandBytesFieldTrial();
 #endif
@@ -559,7 +608,7 @@
 std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
   FeatureList* old_instance = g_feature_list_instance;
   g_feature_list_instance = nullptr;
-  g_initialized_from_accessor = nullptr;
+  EarlyFeatureAccessTracker::GetInstance()->Reset();
   return WrapUnique(old_instance);
 }
 
@@ -575,7 +624,7 @@
 void FeatureList::ForbidUseForCurrentModule() {
 #if DCHECK_IS_ON()
   // Verify there hasn't been any use prior to being called.
-  DCHECK(!g_initialized_from_accessor);
+  EarlyFeatureAccessTracker::GetInstance()->AssertNoAccess();
   g_use_allowed = false;
 #endif  // DCHECK_IS_ON()
 }
diff --git a/base/feature_list.h b/base/feature_list.h
index 409522fa..e9388b4 100644
--- a/base/feature_list.h
+++ b/base/feature_list.h
@@ -378,10 +378,17 @@
   // OWNERS.
   std::unique_ptr<Accessor> ConstructAccessor();
 
-  // Returns whether the given |feature| is enabled. Must only be called after
-  // the singleton instance has been registered via SetInstance(). Additionally,
-  // a feature with a given name must only have a single corresponding Feature
-  // struct, which is checked in builds with DCHECKs enabled.
+  // Returns whether the given `feature` is enabled.
+  //
+  // If no `FeatureList` instance is registered, this returns the feature's
+  // default state. Registering a `FeatureList` later will fail.
+  //
+  // TODO(crbug.com/1358639): Make registering a `FeatureList` later fail on
+  // iOS, Android and ChromeOS. This currently only works on Windows, Mac and
+  // Linux.
+  //
+  // A feature with a given name must only have a single corresponding Feature
+  // instance, which is checked in builds with DCHECKs enabled.
   static bool IsEnabled(const Feature& feature);
 
   // Some characters are not allowed to appear in feature names or the
diff --git a/base/files/scoped_file_linux.cc b/base/files/scoped_file_linux.cc
index 6fd3eec6..e72b5b724 100644
--- a/base/files/scoped_file_linux.cc
+++ b/base/files/scoped_file_linux.cc
@@ -29,7 +29,7 @@
 NOINLINE void CrashOnFdOwnershipViolation() {
   RAW_LOG(ERROR, "Crashing due to FD ownership violation:\n");
   base::debug::StackTrace().Print();
-  IMMEDIATE_CRASH();
+  base::ImmediateCrash();
 }
 
 bool CanTrack(int fd) {
diff --git a/base/immediate_crash_unittest.cc b/base/immediate_crash_unittest.cc
index 806e2fc..eb38509 100644
--- a/base/immediate_crash_unittest.cc
+++ b/base/immediate_crash_unittest.cc
@@ -23,10 +23,10 @@
 
 namespace {
 
-// If IMMEDIATE_CRASH() is not treated as noreturn by the compiler, the compiler
+// If ImmediateCrash() is not treated as noreturn by the compiler, the compiler
 // will complain that not all paths through this function return a value.
 [[maybe_unused]] int TestImmediateCrashTreatedAsNoReturn() {
-  IMMEDIATE_CRASH();
+  ImmediateCrash();
 }
 
 #if defined(ARCH_CPU_X86_FAMILY)
@@ -201,13 +201,13 @@
 
 }  // namespace
 
-// Attempts to verify the actual instructions emitted by IMMEDIATE_CRASH().
+// Attempts to verify the actual instructions emitted by ImmediateCrash().
 // While the test results are highly implementation-specific, this allows macro
 // changes (e.g. CLs like https://crrev.com/671123) to be verified using the
 // trybots/waterfall, without having to build and disassemble Chrome on
 // multiple platforms. This makes it easier to evaluate changes to
-// IMMEDIATE_CRASH() against its requirements (e.g. size of emitted sequence,
-// whether or not multiple IMMEDIATE_CRASH sequences can be folded together, et
+// ImmediateCrash() against its requirements (e.g. size of emitted sequence,
+// whether or not multiple ImmediateCrash sequences can be folded together, et
 // cetera). Please see immediate_crash.h for more details about the
 // requirements.
 //
diff --git a/base/libcpp_hardening_test.cc b/base/libcpp_hardening_test.cc
index bb516d99..fef2062 100644
--- a/base/libcpp_hardening_test.cc
+++ b/base/libcpp_hardening_test.cc
@@ -46,7 +46,7 @@
 //
 // We also have to prevent this test from running on Android because even though
 // death tests are supported on Android, GTest death tests don't work with
-// IMMEDIATE_CRASH() (https://crbug.com/1353549#c2).
+// base::ImmediateCrash() (https://crbug.com/1353549#c2).
 #if GTEST_HAS_DEATH_TEST && !GTEST_OS_LINUX_ANDROID
   EXPECT_DEATH(vec[3], Not(ContainsRegex(".*assertion.*failed:")));
 #else
diff --git a/base/logging.cc b/base/logging.cc
index f934ccb..5a432f0 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -956,7 +956,7 @@
 #endif
 
       // Crash the process to generate a dump.
-      IMMEDIATE_CRASH();
+      base::ImmediateCrash();
     }
   }
 }
@@ -1194,7 +1194,7 @@
   }
 
   if (level == LOGGING_FATAL)
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
 }
 
 // This was defined at the beginning of this file.
diff --git a/base/mac/mac_util.h b/base/mac/mac_util.h
index 4672b1e..11b52915 100644
--- a/base/mac/mac_util.h
+++ b/base/mac/mac_util.h
@@ -31,19 +31,6 @@
 // is a static value; do not release it!
 BASE_EXPORT CGColorSpaceRef GetSystemColorSpace();
 
-// Checks if the current application is set as a Login Item, so it will launch
-// on Login. If a non-NULL pointer to is_hidden is passed, the Login Item also
-// is queried for the 'hide on launch' flag.
-BASE_EXPORT bool CheckLoginItemStatus(bool* is_hidden);
-
-// Adds current application to the set of Login Items with specified "hide"
-// flag. This has the same effect as adding/removing the application in
-// SystemPreferences->Accounts->LoginItems or marking Application in the Dock
-// as "Options->Open on Login".
-// Does nothing if the application is already set up as Login Item with
-// specified hide flag.
-BASE_EXPORT void AddToLoginItems(bool hide_on_startup);
-
 // Adds the specified application to the set of Login Items with specified
 // "hide" flag. This has the same effect as adding/removing the application in
 // SystemPreferences->Accounts->LoginItems or marking Application in the Dock
@@ -53,10 +40,7 @@
 BASE_EXPORT void AddToLoginItems(const FilePath& app_bundle_file_path,
                                  bool hide_on_startup);
 
-// Removes the current application from the list Of Login Items.
-BASE_EXPORT void RemoveFromLoginItems();
-
-// Removes the specified application from the list Of Login Items.
+// Removes the specified application from the list of Login Items.
 BASE_EXPORT void RemoveFromLoginItems(const FilePath& app_bundle_file_path);
 
 // Returns true if the current process was automatically launched as a
diff --git a/base/mac/mac_util.mm b/base/mac/mac_util.mm
index 4ea6802..c04d5102 100644
--- a/base/mac/mac_util.mm
+++ b/base/mac/mac_util.mm
@@ -155,26 +155,6 @@
   return g_system_color_space;
 }
 
-bool CheckLoginItemStatus(bool* is_hidden) {
-  LoginItemsFileList login_items;
-  if (!login_items.Initialize())
-    return false;
-
-  base::ScopedCFTypeRef<LSSharedFileListItemRef> item(
-      login_items.GetLoginItemForMainApp());
-  if (!item.get())
-    return false;
-
-  if (is_hidden)
-    *is_hidden = IsHiddenLoginItem(item);
-
-  return true;
-}
-
-void AddToLoginItems(bool hide_on_startup) {
-  AddToLoginItems(base::mac::MainBundlePath(), hide_on_startup);
-}
-
 void AddToLoginItems(const FilePath& app_bundle_file_path,
                      bool hide_on_startup) {
   LoginItemsFileList login_items;
@@ -215,10 +195,6 @@
   }
 }
 
-void RemoveFromLoginItems() {
-  RemoveFromLoginItems(base::mac::MainBundlePath());
-}
-
 void RemoveFromLoginItems(const FilePath& app_bundle_file_path) {
   LoginItemsFileList login_items;
   if (!login_items.Initialize())
diff --git a/base/nodebug_assertion.cc b/base/nodebug_assertion.cc
index e257041..f9860cee 100644
--- a/base/nodebug_assertion.cc
+++ b/base/nodebug_assertion.cc
@@ -11,7 +11,7 @@
 
 _LIBCPP_NORETURN BASE_EXPORT void __libcpp_verbose_abort(char const* format,
                                                          ...) {
-  IMMEDIATE_CRASH();
+  base::ImmediateCrash();
 }
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/base/power_monitor/power_monitor_device_source.h b/base/power_monitor/power_monitor_device_source.h
index d5e2676f..c56f203c 100644
--- a/base/power_monitor/power_monitor_device_source.h
+++ b/base/power_monitor/power_monitor_device_source.h
@@ -26,6 +26,8 @@
 
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_ionotificationportref.h"
+#include "base/power_monitor/battery_level_provider.h"
+#include "base/power_monitor/iopm_power_source_sampling_event_source.h"
 #include "base/power_monitor/thermal_state_observer_mac.h"
 #endif  // BUILDFLAG(IS_MAC)
 
@@ -132,11 +134,15 @@
   // Notifier reference for the |notification_port_|.
   io_object_t notifier_ = IO_OBJECT_NULL;
 
-  // Run loop source to observe power-source-change events.
-  ScopedCFTypeRef<CFRunLoopSourceRef> power_source_run_loop_source_;
+  // Generates power-source-change events.
+  IOPMPowerSourceSamplingEventSource power_source_event_source_;
+
+  std::unique_ptr<BatteryLevelProvider> battery_level_provider_;
 
   // Observer of thermal state events: critical temperature etc.
   std::unique_ptr<ThermalStateObserverMac> thermal_state_observer_;
+
+  bool is_on_battery_ = false;
 #endif
 
 #if BUILDFLAG(IS_IOS)
diff --git a/base/power_monitor/power_monitor_device_source_mac.mm b/base/power_monitor/power_monitor_device_source_mac.mm
index c7741c0..7d69309 100644
--- a/base/power_monitor/power_monitor_device_source_mac.mm
+++ b/base/power_monitor/power_monitor_device_source_mac.mm
@@ -19,54 +19,6 @@
 
 namespace base {
 
-void ProcessPowerEventHelper(PowerMonitorSource::PowerEvent event) {
-  PowerMonitorSource::ProcessPowerEvent(event);
-}
-
-bool PowerMonitorDeviceSource::IsOnBatteryPower() {
-  base::ScopedCFTypeRef<CFTypeRef> info(IOPSCopyPowerSourcesInfo());
-  if (!info)
-    return false;
-  base::ScopedCFTypeRef<CFArrayRef> power_sources_list(
-      IOPSCopyPowerSourcesList(info));
-  if (!power_sources_list)
-    return false;
-
-  bool found_battery = false;
-  const CFIndex count = CFArrayGetCount(power_sources_list);
-  for (CFIndex i = 0; i < count; ++i) {
-    const CFDictionaryRef description = IOPSGetPowerSourceDescription(
-        info, CFArrayGetValueAtIndex(power_sources_list, i));
-    if (!description)
-      continue;
-
-    CFStringRef current_state = base::mac::GetValueFromDictionary<CFStringRef>(
-        description, CFSTR(kIOPSPowerSourceStateKey));
-    if (!current_state)
-      continue;
-
-    // We only report "on battery power" if no source is on AC power.
-    if (CFStringCompare(current_state, CFSTR(kIOPSOffLineValue), 0) ==
-        kCFCompareEqualTo) {
-      continue;
-    } else if (CFStringCompare(current_state, CFSTR(kIOPSACPowerValue), 0) ==
-               kCFCompareEqualTo) {
-      return false;
-    }
-
-    DCHECK_EQ(CFStringCompare(current_state, CFSTR(kIOPSBatteryPowerValue), 0),
-              kCFCompareEqualTo)
-        << "Power source state is not one of 3 documented values";
-
-    found_battery = true;
-  }
-
-  // At this point, either there were no readable batteries found, in which case
-  // this Mac is not on battery power, or all the readable batteries were on
-  // battery power, in which case, count this as being on battery power.
-  return found_battery;
-}
-
 PowerThermalObserver::DeviceThermalState
 PowerMonitorDeviceSource::GetCurrentThermalState() {
   return thermal_state_observer_->GetCurrentThermalState();
@@ -76,14 +28,6 @@
   return thermal_state_observer_->GetCurrentSpeedLimit();
 }
 
-namespace {
-
-void BatteryEventCallback(void*) {
-  ProcessPowerEventHelper(PowerMonitorSource::POWER_STATE_EVENT);
-}
-
-}  // namespace
-
 void PowerMonitorDeviceSource::PlatformInit() {
   power_manager_port_ = IORegisterForSystemPower(
       this,
@@ -97,16 +41,25 @@
       IONotificationPortGetRunLoopSource(notification_port_.get()),
       kCFRunLoopCommonModes);
 
-  // Create and add the power-source-change event source to the runloop.
-  power_source_run_loop_source_.reset(
-      IOPSNotificationCreateRunLoopSource(&BatteryEventCallback, nullptr));
-  // Verify that the source was created. This may fail if the sandbox does not
-  // permit the process to access the underlying system service. See
-  // https://crbug.com/897557 for an example of such a configuration bug.
-  DCHECK(power_source_run_loop_source_);
+  battery_level_provider_ = BatteryLevelProvider::Create();
 
-  CFRunLoopAddSource(CFRunLoopGetCurrent(), power_source_run_loop_source_,
-                     kCFRunLoopDefaultMode);
+  // Create and add the power-source-change event source to the runloop.
+  power_source_event_source_.Start(base::BindRepeating(
+      [](PowerMonitorDeviceSource* self,
+         BatteryLevelProvider* battery_level_provider) {
+        battery_level_provider->GetBatteryState(base::BindOnce(
+            [](PowerMonitorDeviceSource* self,
+               const absl::optional<BatteryLevelProvider::BatteryState>&
+                   battery_state) {
+              self->is_on_battery_ =
+                  battery_state.has_value() &&
+                  !battery_state->is_external_power_connected;
+              PowerMonitorSource::ProcessPowerEvent(
+                  PowerMonitorSource::POWER_STATE_EVENT);
+            },
+            Unretained(self)));
+      },
+      Unretained(this), battery_level_provider_.get()));
 
   thermal_state_observer_ = std::make_unique<ThermalStateObserverMac>(
       BindRepeating(&PowerMonitorSource::ProcessThermalEvent),
@@ -119,10 +72,6 @@
       IONotificationPortGetRunLoopSource(notification_port_.get()),
       kCFRunLoopCommonModes);
 
-  CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
-                        power_source_run_loop_source_.get(),
-                        kCFRunLoopDefaultMode);
-
   // Deregister for system power notifications.
   IODeregisterForSystemPower(&notifier_);
 
@@ -132,6 +81,10 @@
   power_manager_port_ = IO_OBJECT_NULL;
 }
 
+bool PowerMonitorDeviceSource::IsOnBatteryPower() {
+  return is_on_battery_;
+}
+
 void PowerMonitorDeviceSource::SystemPowerEventCallback(
     void* refcon,
     io_service_t service,
@@ -146,13 +99,13 @@
                          reinterpret_cast<intptr_t>(message_argument));
       break;
     case kIOMessageSystemWillSleep:
-      ProcessPowerEventHelper(PowerMonitorSource::SUSPEND_EVENT);
+      PowerMonitorSource::ProcessPowerEvent(PowerMonitorSource::SUSPEND_EVENT);
       IOAllowPowerChange(thiz->power_manager_port_,
                          reinterpret_cast<intptr_t>(message_argument));
       break;
 
     case kIOMessageSystemWillPowerOn:
-      ProcessPowerEventHelper(PowerMonitorSource::RESUME_EVENT);
+      PowerMonitorSource::ProcessPowerEvent(PowerMonitorSource::RESUME_EVENT);
       break;
   }
 }
diff --git a/base/process/process_stubs.cc b/base/process/process_stubs.cc
index b8a1eee..b819b0b 100644
--- a/base/process/process_stubs.cc
+++ b/base/process/process_stubs.cc
@@ -48,7 +48,7 @@
 void Process::TerminateCurrentProcessImmediately(int exit_code) {
   // This method is marked noreturn, so we crash rather than just provide an
   // empty stub implementation.
-  IMMEDIATE_CRASH();
+  ImmediateCrash();
 }
 
 bool Process::IsValid() const {
diff --git a/base/process/process_win.cc b/base/process/process_win.cc
index ea56447..7503838 100644
--- a/base/process/process_win.cc
+++ b/base/process/process_win.cc
@@ -86,7 +86,7 @@
   ::TerminateProcess(GetCurrentProcess(), static_cast<UINT>(exit_code));
   // There is some ambiguity over whether the call above can return. Rather than
   // hitting confusing crashes later on we should crash right here.
-  IMMEDIATE_CRASH();
+  ImmediateCrash();
 }
 
 bool Process::IsValid() const {
diff --git a/base/profiler/module_cache_unittest.cc b/base/profiler/module_cache_unittest.cc
index 1427e3d7..2244151 100644
--- a/base/profiler/module_cache_unittest.cc
+++ b/base/profiler/module_cache_unittest.cc
@@ -96,7 +96,7 @@
 }
 
 #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_IOS) && !defined(ARCH_CPU_ARM64)) || \
-    (BUILDFLAG(IS_FUCHSIA) && !defined(ARCH_CPU_ARM64)) || BUILDFLAG(IS_WIN)
+    BUILDFLAG(IS_WIN)
 #define MAYBE_TEST(TestSuite, TestName) TEST(TestSuite, TestName)
 #else
 #define MAYBE_TEST(TestSuite, TestName) TEST(TestSuite, DISABLED_##TestName)
diff --git a/base/system/sys_info_posix.cc b/base/system/sys_info_posix.cc
index 6e40753..57fa77f8 100644
--- a/base/system/sys_info_posix.cc
+++ b/base/system/sys_info_posix.cc
@@ -120,9 +120,9 @@
   // disables hyper-threading for the current application, which effectively
   // limits the number of concurrently executing threads to the number of
   // physical cores.
-  if (base::FeatureList::IsEnabled(
-          base::kNumberOfCoresWithCpuSecurityMitigation) &&
-      is_cpu_security_mitigation_enabled) {
+  if (is_cpu_security_mitigation_enabled &&
+      base::FeatureList::IsEnabled(
+          base::kNumberOfCoresWithCpuSecurityMitigation)) {
     absl::optional<int> number_of_physical_cores =
         internal::NumberOfPhysicalProcessors();
     if (number_of_physical_cores.has_value())
@@ -300,4 +300,4 @@
 
 #endif  // BUILDFLAG(IS_MAC)
 
-}  // namespace base
\ No newline at end of file
+}  // namespace base
diff --git a/base/test/immediate_crash_test_helper.cc b/base/test/immediate_crash_test_helper.cc
index 3a2eb9c..ff205b88 100644
--- a/base/test/immediate_crash_test_helper.cc
+++ b/base/test/immediate_crash_test_helper.cc
@@ -15,17 +15,17 @@
 
 IMMEDIATE_CRASH_TEST_HELPER_EXPORT int TestFunction1(int x, int y) {
   if (x < 1)
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   if (y < 1)
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   return x + y;
 }
 
 IMMEDIATE_CRASH_TEST_HELPER_EXPORT int TestFunction2(int x, int y) {
   if (x < 2)
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   if (y < 2)
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   return x * y;
 }
 
diff --git a/base/test/launcher/test_launcher_unittest.cc b/base/test/launcher/test_launcher_unittest.cc
index 977d85f8..b52b051 100644
--- a/base/test/launcher/test_launcher_unittest.cc
+++ b/base/test/launcher/test_launcher_unittest.cc
@@ -1127,7 +1127,7 @@
 }
 // Basic test to crash
 TEST(MockUnitTests, DISABLED_CrashTest) {
-  IMMEDIATE_CRASH();
+  ImmediateCrash();
 }
 // Basic test will not be reached, due to the preceding crash in the same batch.
 TEST(MockUnitTests, DISABLED_NoRunTest) {
diff --git a/base/win/atl_throw.cc b/base/win/atl_throw.cc
index 252fd54..7e86c86 100644
--- a/base/win/atl_throw.cc
+++ b/base/win/atl_throw.cc
@@ -18,7 +18,7 @@
   base::debug::Alias(&hr);
   if (hr == E_OUTOFMEMORY)
     base::TerminateBecauseOutOfMemory(0);
-  IMMEDIATE_CRASH();
+  ImmediateCrash();
 }
 
 }  // namespace win
diff --git a/build/android/pylib/local/device/local_device_test_run.py b/build/android/pylib/local/device/local_device_test_run.py
index 273dd0b3..d166834 100644
--- a/build/android/pylib/local/device/local_device_test_run.py
+++ b/build/android/pylib/local/device/local_device_test_run.py
@@ -109,15 +109,15 @@
                 base_test_result.BaseTestResult(
                     self._GetUniqueTestName(test),
                     base_test_result.ResultType.TIMEOUT))
-        except Exception as e:  # pylint: disable=broad-except
+        except device_errors.DeviceUnreachableError:
+          # If the device is no longer reachable then terminate this
+          # run_tests_on_device call.
+          raise
+        except base_error.BaseError:
+          # If we get a device error but believe the device is still
+          # reachable, attempt to continue using it.
           if isinstance(tests, test_collection.TestCollection):
             rerun = test
-          if (isinstance(e, device_errors.DeviceUnreachableError)
-              or not isinstance(e, base_error.BaseError)):
-            # If we get a device error but believe the device is still
-            # reachable, attempt to continue using it. Otherwise, raise
-            # the exception and terminate this run_tests_on_device call.
-            raise
 
           consecutive_device_errors += 1
           if consecutive_device_errors >= 3:
diff --git a/build/install-build-deps.sh b/build/install-build-deps.sh
index dfe8843..5fe7669 100755
--- a/build/install-build-deps.sh
+++ b/build/install-build-deps.sh
@@ -98,7 +98,7 @@
 distro_codename=$(lsb_release --codename --short)
 distro_id=$(lsb_release --id --short)
 # TODO(crbug.com/1199405): Remove 16.04 (xenial).
-supported_codenames="(xenial|bionic|disco|eoan|focal|groovy|jammy)"
+supported_codenames="(xenial|bionic|focal|jammy)"
 supported_ids="(Debian)"
 if [ 0 -eq "${do_unsupported-0}" ] && [ 0 -eq "${do_quick_check-0}" ] ; then
   if [[ ! $distro_codename =~ $supported_codenames &&
@@ -107,9 +107,6 @@
       "\tUbuntu 16.04 LTS (xenial with EoL April 2024)\n" \
       "\tUbuntu 18.04 LTS (bionic with EoL April 2028)\n" \
       "\tUbuntu 20.04 LTS (focal with EoL April 2030)\n" \
-      "\tUbuntu 19.04 (disco)\n" \
-      "\tUbuntu 19.10 (eoan)\n" \
-      "\tUbuntu 20.10 (groovy)\n" \
       "\tUbuntu 22.04 LTS (jammy with EoL April 2032)\n" \
       "\tDebian 10 (buster) or later" >&2
     exit 1
@@ -457,23 +454,11 @@
                 gcc-5-multilib-arm-linux-gnueabihf
                 gcc-arm-linux-gnueabihf"
     ;;
-  disco|eoan)
-    arm_list+=" g++-9-multilib-arm-linux-gnueabihf
-                gcc-9-multilib-arm-linux-gnueabihf
-                gcc-arm-linux-gnueabihf"
-    ;;
   focal)
     arm_list+=" g++-10-multilib-arm-linux-gnueabihf
                 gcc-10-multilib-arm-linux-gnueabihf
                 gcc-arm-linux-gnueabihf"
     ;;
-  groovy)
-    arm_list+=" g++-10-multilib-arm-linux-gnueabihf
-                gcc-10-multilib-arm-linux-gnueabihf
-                gcc-arm-linux-gnueabihf
-                g++-10-arm-linux-gnueabihf
-                gcc-10-arm-linux-gnueabihf"
-    ;;
   jammy)
     arm_list+=" gcc-arm-linux-gnueabihf
                 g++-11-arm-linux-gnueabihf
@@ -807,4 +792,4 @@
   for CHROMIUM_LOCALE in ${CHROMIUM_LOCALES}; do
     sudo locale-gen ${CHROMIUM_LOCALE}
   done
-fi
\ No newline at end of file
+fi
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 34e94ab8..e6ecb4eb 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -74,6 +74,7 @@
   "junit/src/org/chromium/chrome/browser/compositor/layouts/SceneOverlayTest.java",
   "junit/src/org/chromium/chrome/browser/compositor/layouts/StaticLayoutUnitTest.java",
   "junit/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStackerUnitTest.java",
+  "junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java",
   "junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java",
   "junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStackerUnitTest.java",
   "junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabUsageTrackerTest.java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
index 453a2a1..4c794480 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
@@ -309,10 +309,29 @@
         return ENABLE_LAUNCH_POLISH.getValue();
     }
 
+    private static boolean sFolioEnabledForTesting;
+    private static boolean sDetachedEnabledForTesting;
+    /**
+     * Set folio disabled/enabled for testing.
+     */
+    public static void setTabStripRedesignEnableFolioForTesting(boolean enabled) {
+        sFolioEnabledForTesting = enabled;
+    }
+
+    /**
+     * Set folio disabled/enabled for testing.
+     */
+    public static void setTabStripRedesignEnableDetachedForTesting(boolean enabled) {
+        sDetachedEnabledForTesting = enabled;
+    }
+
     /**
      * @return Whether Folio for tab strip redesign is enabled.
      */
     public static boolean isTabStripFolioEnabled() {
+        if (sFolioEnabledForTesting) {
+            return sFolioEnabledForTesting;
+        }
         return TAB_STRIP_REDESIGN_ENABLE_FOLIO.getValue();
     }
 
@@ -320,6 +339,9 @@
      * @return Whether Detached for tab strip redesign is enabled.
      */
     public static boolean isTabStripDetachedEnabled() {
+        if (sDetachedEnabledForTesting) {
+            return sDetachedEnabledForTesting;
+        }
         return TAB_STRIP_REDESIGN_ENABLE_DETACHED.getValue();
     }
 
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
index 5b9a057f..4f57270 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
@@ -285,7 +285,11 @@
 
     @Test
     @MediumTest
+    @Features.DisableFeatures({ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
     public void testDisableTabGroupsContinuation() {
+        // TabSelectionEditorV2 enables the menu, but not edit text for this test
+        // so needs to be disabled.
+
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         createTabs(cta, false, 2);
         enterTabSwitcher(cta);
@@ -304,8 +308,12 @@
 
     @Test
     @MediumTest
+    @Features.DisableFeatures({ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
     @Features.EnableFeatures(ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID)
     public void testEnableTabGroupsContinuation() {
+        // TabSelectionEditorV2 enables the menu. Ensure Tab Groups Continuation also enables the
+        // menu independently.
+
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         createTabs(cta, false, 2);
         enterTabSwitcher(cta);
@@ -606,8 +614,11 @@
 
     @Test
     @MediumTest
+    @Features.DisableFeatures({ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
     @Features.EnableFeatures(ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID)
     public void testSelectionEditorUngroup() throws ExecutionException {
+        // TabSelectionEditorV2 replaces this behavior.
+
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         assertTrue(cta.getTabModelSelector().getTabModelFilterProvider().getCurrentTabModelFilter()
                            instanceof TabGroupModelFilter);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
index 8893edc..e780301 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
@@ -8,10 +8,12 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Color;
 import android.graphics.RectF;
 import android.os.Handler;
 import android.os.SystemClock;
 
+import androidx.annotation.ColorInt;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.supplier.Supplier;
@@ -50,6 +52,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
+import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.browser_ui.widget.animation.Interpolators;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.base.LocalizationUtils;
@@ -681,6 +684,25 @@
         return mOrientation;
     }
 
+    public @ColorInt int getBackgroundColor() {
+        if (TabUiFeatureUtilities.isTabStripFolioEnabled()) {
+            if (mIsIncognito) {
+                return Color.BLACK;
+            }
+            // @TODO(crbug.com/1373630): May change the color for night theme after finalizing the
+            // spec
+            return ChromeColors.getSurfaceColor(mContext, R.dimen.default_elevation_2);
+        } else if (TabUiFeatureUtilities.isTabStripDetachedEnabled()) {
+            if (mIsIncognito) {
+                // Use a non-dynamic dark background color for incognito, slightly greyer than
+                // Color.BLACK
+                return ChromeColors.getPrimaryBackgroundColor(mContext, mIsIncognito);
+            }
+            return ChromeColors.getSurfaceColor(mContext, R.dimen.default_elevation_0);
+        }
+        return Color.BLACK;
+    }
+
     /**
      * Updates all internal resources and dimensions.
      * @param context The current Android {@link Context}.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java
index b2ddc0b..06bc344e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java
@@ -7,6 +7,8 @@
 import android.content.Context;
 import android.os.Build;
 
+import androidx.annotation.ColorInt;
+
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.R;
@@ -29,6 +31,7 @@
  */
 @JNINamespace("android")
 public class TabStripSceneLayer extends SceneOverlayLayer {
+    private static boolean sTestFlag;
     private long mNativePtr;
     private final float mDpToPx;
     private SceneLayer mChildSceneLayer;
@@ -39,12 +42,19 @@
         mDpToPx = context.getResources().getDisplayMetrics().density;
     }
 
+    public static void setTestFlag(boolean testFlag) {
+        sTestFlag = testFlag;
+    }
+
     @Override
     protected void initializeNative() {
         if (mNativePtr == 0) {
             mNativePtr = TabStripSceneLayerJni.get().init(TabStripSceneLayer.this);
         }
-        assert mNativePtr != 0;
+        // Set flag for testing
+        if (!sTestFlag) {
+            assert mNativePtr != 0;
+        }
     }
 
     @Override
@@ -114,7 +124,8 @@
         final float width = layoutHelper.getWidth() * mDpToPx;
         final float height = layoutHelper.getHeight() * mDpToPx;
         TabStripSceneLayerJni.get().updateTabStripLayer(mNativePtr, TabStripSceneLayer.this, width,
-                height, yOffset * mDpToPx, shouldReaddBackground(layoutHelper.getOrientation()));
+                height, yOffset * mDpToPx, shouldReaddBackground(layoutHelper.getOrientation()),
+                layoutHelper.getBackgroundColor());
 
         updateStripScrim(layoutHelper.getStripScrim());
 
@@ -193,13 +204,14 @@
     }
 
     @NativeMethods
-    interface Natives {
+    public interface Natives {
         long init(TabStripSceneLayer caller);
         void beginBuildingFrame(
                 long nativeTabStripSceneLayer, TabStripSceneLayer caller, boolean visible);
         void finishBuildingFrame(long nativeTabStripSceneLayer, TabStripSceneLayer caller);
         void updateTabStripLayer(long nativeTabStripSceneLayer, TabStripSceneLayer caller,
-                float width, float height, float yOffset, boolean shouldReadBackground);
+                float width, float height, float yOffset, boolean shouldReadBackground,
+                @ColorInt int backgroundColor);
         void updateStripScrim(long nativeTabStripSceneLayer, TabStripSceneLayer caller, float x,
                 float y, float width, float height, int color, float alpha);
         void updateNewTabButton(long nativeTabStripSceneLayer, TabStripSceneLayer caller,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java
index bba9b4a..be4b127 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java
@@ -223,8 +223,10 @@
             }
 
             @Override
-            public void onCrash(Tab tab) {
-                if (tab == getTab() && SadTab.isShowing(tab)) showAndroidControls(false);
+            public void onContentChanged(Tab tab) {
+                if (tab.isShowingCustomView()) {
+                    showAndroidControls(false);
+                }
             }
 
             @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java
index 5267725..ec08eff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java
@@ -13,7 +13,6 @@
 import org.chromium.base.ObserverList;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
 import org.chromium.components.messages.MessageContainer;
 
@@ -43,9 +42,7 @@
     }
 
     private void updateMargins() {
-        if (mContainer.getVisibility() != View.VISIBLE
-                && ChromeFeatureList.isEnabled(
-                        ChromeFeatureList.MESSAGES_FOR_ANDROID_REDUCE_LAYOUT_CHANGES)) {
+        if (mContainer.getVisibility() != View.VISIBLE) {
             return;
         }
         CoordinatorLayout.LayoutParams params =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/ActivityTabStartupMetricsTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/ActivityTabStartupMetricsTracker.java
index de2c70f3..41d7876c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/ActivityTabStartupMetricsTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/ActivityTabStartupMetricsTracker.java
@@ -6,8 +6,6 @@
 
 import android.os.SystemClock;
 
-import org.chromium.base.ObserverList;
-import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.chrome.browser.paint_preview.StartupPaintPreviewHelper;
@@ -34,52 +32,11 @@
     private static final String FIRST_PAINT_OCCURRED_PRE_FOREGROUND_HISTOGRAM =
             "Startup.Android.Cold.FirstPaintOccurredPreForeground";
 
-    /** Observer for startup metrics. */
-    public interface Observer {
-        /**
-         * Called when the initial navigation upon startup is started. This will be fired at most
-         * once.
-         */
-        void onFirstNavigationStart();
-
-        /**
-         * Called when recording first visible content. This will be fired at most once.
-         */
-        void onFirstVisibleContent();
-
-        /**
-         * Called when recording first navigation commit. This will be fired at most once.
-         */
-        void onFirstNavigationCommit();
-
-        /**
-         * Called when recording first contentful paint. This will be fired at most once.
-         */
-        void onFirstContentfulPaint();
-    }
-
-    private static ObserverList<Observer> sObservers;
-
-    /** Adds an observer. */
-    public static boolean addObserver(Observer observer) {
-        ThreadUtils.assertOnUiThread();
-        if (sObservers == null) sObservers = new ObserverList<>();
-        return sObservers.addObserver(observer);
-    }
-
-    /** Removes an observer. */
-    public static boolean removeObserver(Observer observer) {
-        ThreadUtils.assertOnUiThread();
-        if (sObservers == null) return false;
-        return sObservers.removeObserver(observer);
-    }
-
     private class PageLoadMetricsObserverImpl implements PageLoadMetrics.Observer {
         private static final long NO_NAVIGATION_ID = -1;
 
         private long mNavigationId = NO_NAVIGATION_ID;
         private boolean mShouldRecordHistograms;
-        private boolean mInvokedOnFirstNavigationStart;
 
         @Override
         public void onNewNavigation(WebContents webContents, long navigationId,
@@ -88,17 +45,6 @@
 
             mNavigationId = navigationId;
             mShouldRecordHistograms = mShouldTrackStartupMetrics;
-
-            // Only notify observers of the initial navigation in the case where we will also record
-            // first contentful paint for this navigation.
-            if (!mInvokedOnFirstNavigationStart && mShouldRecordHistograms) {
-                if (sObservers != null) {
-                    for (Observer observer : sObservers) {
-                        observer.onFirstNavigationStart();
-                    }
-                }
-                mInvokedOnFirstNavigationStart = true;
-            }
         }
 
         @Override
@@ -192,12 +138,7 @@
                 };
         mPageLoadMetricsObserver = new PageLoadMetricsObserverImpl();
         PageLoadMetrics.addObserver(mPageLoadMetricsObserver, false);
-        mUmaUtilsObserver = new UmaUtils.Observer() {
-            @Override
-            public void onHasComeToForeground() {
-                registerHasComeToForeground();
-            }
-        };
+        mUmaUtilsObserver = this::registerHasComeToForeground;
         UmaUtils.addObserver(mUmaUtilsObserver);
     }
 
@@ -312,12 +253,6 @@
             }
             RecordHistogram.recordBooleanHistogram(
                     FIRST_COMMIT_OCCURRED_PRE_FOREGROUND_HISTOGRAM, false);
-
-            if (sObservers != null) {
-                for (Observer observer : sObservers) {
-                    observer.onFirstNavigationCommit();
-                }
-            }
         } else if (isTrackedPage && !UmaUtils.hasComeToForeground()
                 && !UmaUtils.hasComeToBackground()) {
             mRegisteredFirstCommitPreForeground = true;
@@ -351,12 +286,6 @@
             if (mHistogramSuffix.equals(UMA_HISTOGRAM_TABBED_SUFFIX)) {
                 recordVisibleContent(durationMs);
             }
-
-            if (sObservers != null) {
-                for (Observer observer : sObservers) {
-                    observer.onFirstContentfulPaint();
-                }
-            }
         }
         // This is the last navigation-related event we track, so clean up related state.
         mShouldTrackStartupMetrics = false;
@@ -377,12 +306,6 @@
         mFirstVisibleContentRecorded = true;
         RecordHistogram.recordMediumTimesHistogram(
                 "Startup.Android.Cold.TimeToFirstVisibleContent", durationMs);
-
-        if (sObservers != null) {
-            for (Observer observer : sObservers) {
-                observer.onFirstVisibleContent();
-            }
-        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillAddress.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillAddress.java
index 41e559cc..434ef44 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillAddress.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillAddress.java
@@ -34,10 +34,7 @@
     /** The pattern for a valid region code. */
     private static final String REGION_CODE_PATTERN = "^[A-Z]{2}$";
 
-    // Bit field values are identical to ProfileFields in payments_profile_comparator.h. Please also
-    // modify payments_profile_comparator.h after changing these bits since missing fields on both
-    // Android and Desktop are recorded in the same UMA metric:
-    // PaymentRequest.MissingShippingFields.
+    // Bit field values are identical to ProfileFields in payments_profile_comparator.h.
     @IntDef({CompletionStatus.COMPLETE, CompletionStatus.INVALID_RECIPIENT,
             CompletionStatus.INVALID_PHONE_NUMBER, CompletionStatus.INVALID_ADDRESS})
     @Retention(RetentionPolicy.SOURCE)
@@ -346,11 +343,6 @@
         return result;
     }
 
-    /** @return The missing fields of the shipping profile. */
-    public int getMissingFieldsOfShippingProfile() {
-        return checkAddressCompletionStatus(mProfile, mCheckType);
-    }
-
     private int calculateCompletenessScore() {
         int missingFields = checkAddressCompletionStatus(mProfile, mCheckType);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
index 4840b4aa..0cacc75 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
@@ -31,9 +31,7 @@
  */
 public class ContactEditor extends EditorBase<AutofillContact> {
     // Bit field values are identical to ProfileFields in payments_profile_comparator.h.
-    // Please also modify payments_profile_comparator.h after changing these bits since
-    // missing fields on both Android and Desktop are recorded in the same UMA metric:
-    // PaymentRequest.MissingContactFields.
+    // Please also modify payments_profile_comparator.h after changing these bits.
     public @interface CompletionStatus {}
     /** Can be sent to the merchant as-is without editing first. */
     public static final int COMPLETE = 0;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/ContactDetailsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/ContactDetailsSection.java
index 5a443af..21ea6fdb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/ContactDetailsSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/ContactDetailsSection.java
@@ -9,7 +9,6 @@
 
 import androidx.annotation.Nullable;
 
-import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.payments.AutofillAddress;
 import org.chromium.chrome.browser.payments.AutofillContact;
@@ -149,9 +148,6 @@
                     firstCompleteContactIndex != SectionInformation.NO_SELECTION);
         }
 
-        // Record all required and missing fields of the most complete suggestion.
-        recordMissingContactFields(uniqueContacts.isEmpty() ? null : uniqueContacts.get(0));
-
         updateItemsWithCollection(firstCompleteContactIndex, uniqueContacts);
     }
 
@@ -178,26 +174,4 @@
         }
         return null;
     }
-
-    // Bit field values are identical to ProfileFields from payments_profile_comparator.h
-    private void recordMissingContactFields(AutofillContact contact) {
-        int missingFields = 0;
-        if (mContactEditor.getRequestPayerName()
-                && (contact == null || TextUtils.isEmpty(contact.getPayerName()))) {
-            missingFields |= ContactEditor.INVALID_NAME;
-        }
-        if (mContactEditor.getRequestPayerPhone()
-                && (contact == null || TextUtils.isEmpty(contact.getPayerPhone()))) {
-            missingFields |= ContactEditor.INVALID_PHONE_NUMBER;
-        }
-        if (mContactEditor.getRequestPayerEmail()
-                && (contact == null || TextUtils.isEmpty(contact.getPayerEmail()))) {
-            missingFields |= ContactEditor.INVALID_EMAIL;
-        }
-
-        if (missingFields != 0) {
-            RecordHistogram.recordSparseHistogram(
-                    "PaymentRequest.MissingContactFields", missingFields);
-        }
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
index 1253e9b6..557eb71 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
@@ -13,7 +13,6 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Callback;
-import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
@@ -1182,20 +1181,6 @@
         mJourneyLogger.setNumberOfSuggestionsShown(
                 Section.SHIPPING_ADDRESS, addresses.size(), hasCompleteShippingAddress);
 
-        int missingFields;
-        if (addresses.isEmpty()) {
-            // All fields are missing.
-            missingFields = AutofillAddress.CompletionStatus.INVALID_RECIPIENT
-                    | AutofillAddress.CompletionStatus.INVALID_PHONE_NUMBER
-                    | AutofillAddress.CompletionStatus.INVALID_ADDRESS;
-        } else {
-            missingFields = addresses.get(0).getMissingFieldsOfShippingProfile();
-        }
-        if (missingFields != 0) {
-            RecordHistogram.recordSparseHistogram(
-                    "PaymentRequest.MissingShippingFields", missingFields);
-        }
-
         mShippingAddressesSection = new SectionInformation(
                 PaymentRequestUI.DataType.SHIPPING_ADDRESSES, firstCompleteAddressIndex, addresses);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/OverviewAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/OverviewAppMenuTest.java
index 09596325..cd46dc68 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/OverviewAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/OverviewAppMenuTest.java
@@ -61,7 +61,7 @@
             AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
         });
 
-        verifyTabSwitcherMenu();
+        verifyTabSwitcherMenu(false);
     }
 
     @Test
@@ -75,7 +75,7 @@
             AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
         });
 
-        verifyTabSwitcherMenuIncognito();
+        verifyTabSwitcherMenuIncognito(false);
     }
 
     @Test
@@ -83,17 +83,33 @@
     @Feature({"Browser", "Main"})
     @Features.
     EnableFeatures({ChromeFeatureList.START_SURFACE_ANDROID, ChromeFeatureList.TAB_GROUPS_ANDROID})
+    @Features.DisableFeatures({ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
     public void testAllMenuItemsWithStartSurface() throws Exception {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
         });
 
-        verifyTabSwitcherMenu();
+        verifyTabSwitcherMenu(false);
     }
 
     @Test
     @SmallTest
     @Feature({"Browser", "Main"})
+    @Features.EnableFeatures({ChromeFeatureList.START_SURFACE_ANDROID,
+            ChromeFeatureList.TAB_GROUPS_ANDROID, ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
+    public void
+    testAllMenuItemsWithStartSurfaceAndSelectTabs() throws Exception {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
+        });
+
+        verifyTabSwitcherMenu(true);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Browser", "Main"})
+    @Features.DisableFeatures({ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
     @Features.
     EnableFeatures({ChromeFeatureList.START_SURFACE_ANDROID, ChromeFeatureList.TAB_GROUPS_ANDROID})
     public void testIncognitoAllMenuItemsWithStartSurface() throws Exception {
@@ -102,7 +118,22 @@
             AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
         });
 
-        verifyTabSwitcherMenuIncognito();
+        verifyTabSwitcherMenuIncognito(false);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Browser", "Main"})
+    @Features.EnableFeatures({ChromeFeatureList.START_SURFACE_ANDROID,
+            ChromeFeatureList.TAB_GROUPS_ANDROID, ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
+    public void
+    testIncognitoAllMenuItemsWithStartSurfaceAndSelectTabs() throws Exception {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mActivityTestRule.getActivity().getTabModelSelector().selectModel(true);
+            AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
+        });
+
+        verifyTabSwitcherMenuIncognito(true);
     }
 
     @Test
@@ -168,6 +199,7 @@
     @Test
     @SmallTest
     @Feature({"Browser", "Main"})
+    @Features.DisableFeatures({ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
     @Features.
     EnableFeatures({ChromeFeatureList.START_SURFACE_ANDROID, ChromeFeatureList.TAB_GROUPS_ANDROID})
     public void testGroupTabsIsEnabledWithStartSurface() throws Exception {
@@ -177,9 +209,28 @@
 
         assertNotNull(AppMenuTestSupport.getMenuItemPropertyModel(
                 mActivityTestRule.getAppMenuCoordinator(), R.id.menu_group_tabs));
+        assertNull(AppMenuTestSupport.getMenuItemPropertyModel(
+                mActivityTestRule.getAppMenuCoordinator(), R.id.menu_select_tabs));
     }
 
-    private void verifyTabSwitcherMenu() {
+    @Test
+    @SmallTest
+    @Feature({"Browser", "Main"})
+    @Features.EnableFeatures({ChromeFeatureList.START_SURFACE_ANDROID,
+            ChromeFeatureList.TAB_GROUPS_ANDROID, ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
+    public void
+    testGroupTabsIsEnabledWithStartSurfaceAndSelectTabs() throws Exception {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
+        });
+
+        assertNull(AppMenuTestSupport.getMenuItemPropertyModel(
+                mActivityTestRule.getAppMenuCoordinator(), R.id.menu_group_tabs));
+        assertNotNull(AppMenuTestSupport.getMenuItemPropertyModel(
+                mActivityTestRule.getAppMenuCoordinator(), R.id.menu_select_tabs));
+    }
+
+    private void verifyTabSwitcherMenu(boolean selectTabs) {
         assertNotNull(AppMenuTestSupport.getMenuItemPropertyModel(
                 mActivityTestRule.getAppMenuCoordinator(), R.id.new_tab_menu_id));
         assertNotNull(AppMenuTestSupport.getMenuItemPropertyModel(
@@ -187,7 +238,11 @@
         assertNotNull(AppMenuTestSupport.getMenuItemPropertyModel(
                 mActivityTestRule.getAppMenuCoordinator(), R.id.close_all_tabs_menu_id));
         assertNotNull(AppMenuTestSupport.getMenuItemPropertyModel(
-                mActivityTestRule.getAppMenuCoordinator(), R.id.menu_group_tabs));
+                mActivityTestRule.getAppMenuCoordinator(),
+                selectTabs ? R.id.menu_select_tabs : R.id.menu_group_tabs));
+        assertNull(AppMenuTestSupport.getMenuItemPropertyModel(
+                mActivityTestRule.getAppMenuCoordinator(),
+                selectTabs ? R.id.menu_group_tabs : R.id.menu_select_tabs));
         assertNotNull(AppMenuTestSupport.getMenuItemPropertyModel(
                 mActivityTestRule.getAppMenuCoordinator(), R.id.preferences_id));
 
@@ -201,7 +256,7 @@
         assertEquals(5, menuItemsModelList.size());
     }
 
-    private void verifyTabSwitcherMenuIncognito() {
+    private void verifyTabSwitcherMenuIncognito(boolean selectTabs) {
         assertNotNull(AppMenuTestSupport.getMenuItemPropertyModel(
                 mActivityTestRule.getAppMenuCoordinator(), R.id.new_tab_menu_id));
         assertNotNull(AppMenuTestSupport.getMenuItemPropertyModel(
@@ -209,7 +264,11 @@
         assertNotNull(AppMenuTestSupport.getMenuItemPropertyModel(
                 mActivityTestRule.getAppMenuCoordinator(), R.id.close_all_incognito_tabs_menu_id));
         assertNotNull(AppMenuTestSupport.getMenuItemPropertyModel(
-                mActivityTestRule.getAppMenuCoordinator(), R.id.menu_group_tabs));
+                mActivityTestRule.getAppMenuCoordinator(),
+                selectTabs ? R.id.menu_select_tabs : R.id.menu_group_tabs));
+        assertNull(AppMenuTestSupport.getMenuItemPropertyModel(
+                mActivityTestRule.getAppMenuCoordinator(),
+                selectTabs ? R.id.menu_group_tabs : R.id.menu_select_tabs));
         assertNotNull(AppMenuTestSupport.getMenuItemPropertyModel(
                 mActivityTestRule.getAppMenuCoordinator(), R.id.preferences_id));
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTestUtils.java
index 06932f0..d4acbf8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTestUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTestUtils.java
@@ -17,14 +17,10 @@
 import org.chromium.chrome.browser.browser_controls.BrowserStateBrowserControlsVisibilityDelegate;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeActivityTestRule;
-import org.chromium.content_public.browser.GestureListenerManager;
-import org.chromium.content_public.browser.GestureStateListener;
 import org.chromium.content_public.browser.RenderCoordinates;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
-import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TouchCommon;
-import org.chromium.content_public.browser.test.util.WebContentsUtils;
 
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -58,10 +54,8 @@
             dragEndY = tempDragStartY;
         }
         long downTime = SystemClock.uptimeMillis();
-        TouchCommon.dragStart(testRule.getActivity(), dragX, dragStartY, downTime);
-        TouchCommon.dragTo(
+        TouchCommon.performDrag(
                 testRule.getActivity(), dragX, dragX, dragStartY, dragEndY, 100, downTime);
-        TouchCommon.dragEnd(testRule.getActivity(), dragX, dragEndY, downTime);
         waitForBrowserControlsPosition(testRule, expectedPosition);
     }
 
@@ -112,98 +106,34 @@
         final float initialVisibleContentOffset =
                 browserControlsStateProvider.getTopVisibleContentOffset();
 
-        browserControlsStateProvider.addObserver(new BrowserControlsStateProvider.Observer() {
-            @Override
-            public void onControlsOffsetChanged(int topOffset, int topControlsMinHeightOffset,
-                    int bottomOffset, int bottomControlsMinHeightOffset, boolean needsAnimate) {
-                if (browserControlsStateProvider.getTopVisibleContentOffset()
-                        != initialVisibleContentOffset) {
-                    contentMovedCallback.notifyCalled();
-                    browserControlsStateProvider.removeObserver(this);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            browserControlsStateProvider.addObserver(new BrowserControlsStateProvider.Observer() {
+                @Override
+                public void onControlsOffsetChanged(int topOffset, int topControlsMinHeightOffset,
+                        int bottomOffset, int bottomControlsMinHeightOffset, boolean needsAnimate) {
+                    if (browserControlsStateProvider.getTopVisibleContentOffset()
+                            != initialVisibleContentOffset) {
+                        contentMovedCallback.notifyCalled();
+                        browserControlsStateProvider.removeObserver(this);
+                    }
                 }
-            }
+            });
         });
 
         float dragX = 50f;
         float dragStartY = tab.getView().getHeight() - 50f;
 
-        WebContents webContents = tab.getWebContents();
-
-        final CallbackHelper scrollEndCallback = new CallbackHelper();
-        final CallbackHelper flingEndCallback = new CallbackHelper();
-        GestureStateListener scrollEndListener = new GestureStateListener() {
-            @Override
-            public void onScrollEnded(int scrollOffsetY, int scrollExtentY) {
-                scrollEndCallback.notifyCalled();
-            }
-
-            @Override
-            public void onFlingEndGesture(int scrollOffsetY, int scrollExtentY) {
-                flingEndCallback.notifyCalled();
-            }
-
-        };
-        GestureListenerManager gestureListenerManager =
-                WebContentsUtils.getGestureListenerManager(webContents);
-        gestureListenerManager.addListener(scrollEndListener);
-
         for (int i = 0; i < 10; i++) {
-            int numScrollEndCalled = scrollEndCallback.getCallCount();
-            int numFlingEndCalled = flingEndCallback.getCallCount();
             float dragEndY = dragStartY - browserControlsStateProvider.getTopControlsHeight();
 
             long downTime = SystemClock.uptimeMillis();
-            TouchCommon.dragStart(testRule.getActivity(), dragX, dragStartY, downTime);
-            TouchCommon.dragTo(
+            TouchCommon.performDrag(
                     testRule.getActivity(), dragX, dragX, dragStartY, dragEndY, 100, downTime);
-            TouchCommon.dragEnd(testRule.getActivity(), dragX, dragEndY, downTime);
 
             try {
                 contentMovedCallback.waitForCallback(0, 1, 500, TimeUnit.MILLISECONDS);
-
-                try {
-                    scrollEndCallback.waitForCallback(
-                            numScrollEndCalled, 1, 5000, TimeUnit.MILLISECONDS);
-                    flingEndCallback.waitForCallback(
-                            numFlingEndCalled, 1, 5000, TimeUnit.MILLISECONDS);
-                } catch (TimeoutException e) {
-                    Assert.fail("Didn't get expected ScrollEnd gestures");
-                }
-
-                try {
-                    flingEndCallback.waitForCallback(
-                            numFlingEndCalled, 1, 200, TimeUnit.MILLISECONDS);
-                } catch (TimeoutException e) {
-                    // Depending on timing - the above scroll may not have
-                    // generated a fling. If it did, it the fling end may
-                    // sometimes be called after the scroll end so wait a little
-                    // for it.
-                }
-
-                numFlingEndCalled = flingEndCallback.getCallCount();
-
                 scrollBrowserControls(testRule, false);
                 scrollBrowserControls(testRule, true);
-
-                // Make sure the gesture stream is finished before we hand back control.
-                try {
-                    scrollEndCallback.waitForCallback(
-                            numScrollEndCalled + 1, 2, 5000, TimeUnit.MILLISECONDS);
-                } catch (TimeoutException e) {
-                    Assert.fail("Didn't get expected ScrollEnd gestures");
-                }
-
-                try {
-                    flingEndCallback.waitForCallback(
-                            numFlingEndCalled, 2, 200, TimeUnit.MILLISECONDS);
-                } catch (TimeoutException e) {
-                    // Depending on timing - the above scrolls may not have
-                    // generated flings. If they did, the fling end may sometimes
-                    // be called after the scroll end so wait a little for it.
-                }
-
-                gestureListenerManager.removeListener(scrollEndListener);
-
                 return;
             } catch (TimeoutException e) {
                 // Ignore and retry
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingMultipleAddressesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingMultipleAddressesTest.java
index dc2db966..e12fa5a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingMultipleAddressesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingMultipleAddressesTest.java
@@ -11,7 +11,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
@@ -141,12 +140,6 @@
         // phone number), it ranks lower than Profile[0] since its completeness score is lower.
         Assert.assertTrue(mPaymentRequestTestRule.getShippingAddressSuggestionLabel(i++).contains(
                 "Marge Simpson"));
-
-        // Verify that no shipping fields is recorded since there is at least one complete
-        // suggestion.
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramTotalCountForTesting(
-                        "PaymentRequest.MissingShippingFields"));
     }
 
     /**
@@ -207,12 +200,6 @@
                 "Los Angeles"));
         Assert.assertFalse(mPaymentRequestTestRule.getShippingAddressSuggestionLabel(i).contains(
                 "Marge Simpson"));
-
-        // Verify that no missing fields is recorded for shipping since there is at least one
-        // complete suggestion.
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramTotalCountForTesting(
-                        "PaymentRequest.MissingShippingFields"));
     }
 
     /**
@@ -307,89 +294,5 @@
                 "Enter a valid address"));
         Assert.assertTrue(mPaymentRequestTestRule.getShippingAddressSuggestionLabel(i++).contains(
                 "More information required"));
-
-        // Verify that the missing fields of the most complete suggestion has been recorded.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.MissingShippingFields",
-                        AutofillAddress.CompletionStatus.INVALID_PHONE_NUMBER));
-    }
-
-    /**
-     * Make sure the shipping address bit is recorded in missing fields when an incomplete profile
-     * with missing address is the most complete one.
-     */
-    @Test
-    @MediumTest
-    @DisabledTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testMissingShippingAddressFieldRecorded() throws TimeoutException {
-        // Add a profile with invalid shipping address, and another one with both missing name and
-        // address.
-        mProfilesToAdd = new AutofillProfile[] {AUTOFILL_PROFILES[4], AUTOFILL_PROFILES[6]};
-        mCountsToSet = new int[] {5, 5};
-        mDatesToSet = new int[] {5000, 5000};
-
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickInShippingAddressAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-
-        Assert.assertEquals(2, mPaymentRequestTestRule.getNumberOfShippingAddressSuggestions());
-        // Verify that the missing fields of the most complete suggestion has been recorded.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.MissingShippingFields",
-                        AutofillAddress.CompletionStatus.INVALID_ADDRESS));
-    }
-
-    /**
-     * Make sure the name bit is recorded in missing fields when an incomplete profile with missing
-     * name is the most complete one.
-     */
-    @Test
-    @MediumTest
-    @DisabledTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testMissingNameFieldRecorded() throws TimeoutException {
-        // Add a profile with invalid shipping address, and another one with missing name.
-        mProfilesToAdd = new AutofillProfile[] {AUTOFILL_PROFILES[4], AUTOFILL_PROFILES[5]};
-        mCountsToSet = new int[] {5, 5};
-        mDatesToSet = new int[] {5000, 5000};
-
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickInShippingAddressAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-
-        Assert.assertEquals(2, mPaymentRequestTestRule.getNumberOfShippingAddressSuggestions());
-        // Verify that the missing fields of the most complete suggestion has been recorded.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.MissingShippingFields",
-                        AutofillAddress.CompletionStatus.INVALID_RECIPIENT));
-    }
-
-    /**
-     * Make sure all fields are recorded when no profile exists.
-     */
-    @Test
-    @MediumTest
-    @DisabledTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testAllMissingFieldsRecorded() throws TimeoutException {
-        // Don't add any profiles
-        mProfilesToAdd = new AutofillProfile[] {};
-        mCountsToSet = new int[] {};
-        mDatesToSet = new int[] {};
-
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
-
-        Assert.assertEquals(0, mPaymentRequestTestRule.getNumberOfShippingAddressSuggestions());
-        // Verify that the missing fields of the most complete suggestion has been recorded.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.MissingShippingFields",
-                        AutofillAddress.CompletionStatus.INVALID_RECIPIENT
-                                | AutofillAddress.CompletionStatus.INVALID_PHONE_NUMBER
-                                | AutofillAddress.CompletionStatus.INVALID_ADDRESS));
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMultipleContactDetailsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMultipleContactDetailsTest.java
index 52f53c4b..af7d3fdd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMultipleContactDetailsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMultipleContactDetailsTest.java
@@ -11,7 +11,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
@@ -164,12 +163,6 @@
                 mPaymentRequestTestRule.getContactDetailsSuggestionLabel(2));
         Assert.assertEquals("Homer Simpson\n555 123-4567\nEmail required",
                 mPaymentRequestTestRule.getContactDetailsSuggestionLabel(3));
-
-        // Verify that no record is logged since there is at least one complete suggested contact
-        // details.
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramTotalCountForTesting(
-                        "PaymentRequest.MissingContactFields"));
     }
 
     /**
@@ -198,11 +191,6 @@
                 mPaymentRequestTestRule.getContactDetailsSuggestionLabel(2));
         Assert.assertEquals("Marge Simpson\nMore information required",
                 mPaymentRequestTestRule.getContactDetailsSuggestionLabel(3));
-
-        // Verify that the missing fields of the most complete suggestion has been recorded.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.MissingContactFields", ContactEditor.INVALID_PHONE_NUMBER));
     }
 
     /**
@@ -284,28 +272,4 @@
         Assert.assertEquals("Lisa Simpson\n555 123-4567\nlisa@simpson.com",
                 mPaymentRequestTestRule.getContactDetailsSuggestionLabel(1));
     }
-
-    /**
-     * Make sure all fields are recorded when no profile exists.
-     */
-    @Test
-    @MediumTest
-    @Feature({"Payments"})
-    @DisabledTest(message = "https://crbug.com/1182644")
-    public void testContactDetailsAllMissingFieldsRecorded() throws TimeoutException {
-        // Don't add any profiles.
-        mProfilesToAdd = new AutofillProfile[] {};
-        mCountsToSet = new int[] {};
-        mDatesToSet = new int[] {};
-
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
-        Assert.assertEquals(0, mPaymentRequestTestRule.getNumberOfContactDetailSuggestions());
-
-        // Verify that all contact fields are recorded as missing when no suggestion exists.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.MissingContactFields",
-                        ContactEditor.INVALID_NAME | ContactEditor.INVALID_PHONE_NUMBER
-                                | ContactEditor.INVALID_EMAIL));
-    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java
index f738350b..888d090 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java
@@ -7,20 +7,27 @@
 import android.support.test.InstrumentationRegistry;
 import android.widget.Button;
 
+import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
+import org.junit.After;
 import org.junit.Assert;
-import org.junit.Before;
+import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.fullscreen.FullscreenManagerTestUtils;
+import org.chromium.chrome.browser.tab.TabUtils.LoadIfNeededCaller;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
 import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.net.test.util.TestWebServer;
@@ -31,15 +38,19 @@
  * Tests related to the sad tab logic.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
+@Batch(Batch.PER_CLASS)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class SadTabTest {
-    @Rule
-    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+    private static final String LONG_HTML_TEST_PAGE =
+            UrlUtils.encodeHtmlDataUri("<html><body style='height:100000px;'></body></html>");
 
-    @Before
-    public void setUp() throws InterruptedException {
-        mActivityTestRule.startMainActivityOnBlankPage();
-    }
+    @ClassRule
+    public static ChromeTabbedActivityTestRule sActivityTestRule =
+            new ChromeTabbedActivityTestRule();
+
+    @Rule
+    public BlankCTATabInitialStateRule mBlankCTATabInitialStateRule =
+            new BlankCTATabInitialStateRule(sActivityTestRule, false);
 
     private static boolean isShowingSadTab(Tab tab) {
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -50,6 +61,16 @@
         }
     }
 
+    @After
+    public void tearDown() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Tab tab = sActivityTestRule.getActivity().getActivityTab();
+            tab.show(TabSelectionType.FROM_USER, LoadIfNeededCaller.OTHER);
+            SadTab sadTab = SadTab.from(tab);
+            sadTab.removeIfPresent();
+        });
+    }
+
     /**
      * Verify that the sad tab is shown when the renderer crashes.
      */
@@ -57,7 +78,7 @@
     @SmallTest
     @Feature({"SadTab"})
     public void testSadTabShownWhenRendererProcessKilled() {
-        final Tab tab = mActivityTestRule.getActivity().getActivityTab();
+        final Tab tab = sActivityTestRule.getActivity().getActivityTab();
 
         Assert.assertFalse(isShowingSadTab(tab));
         simulateRendererKilled(tab, true);
@@ -72,7 +93,7 @@
     @SmallTest
     @Feature({"SadTab"})
     public void testSadTabNotShownWhenRendererProcessKilledInBackround() {
-        final Tab tab = mActivityTestRule.getActivity().getActivityTab();
+        final Tab tab = sActivityTestRule.getActivity().getActivityTab();
 
         Assert.assertFalse(isShowingSadTab(tab));
         simulateRendererKilled(tab, false);
@@ -86,12 +107,12 @@
     @SmallTest
     @Feature({"SadTab"})
     public void testSadTabReloadAfterKill() throws Throwable {
-        final Tab tab = mActivityTestRule.getActivity().getActivityTab();
+        final Tab tab = sActivityTestRule.getActivity().getActivityTab();
 
         TestWebServer webServer = TestWebServer.start();
         try {
             final String url1 = webServer.setEmptyResponse("/page1.html");
-            mActivityTestRule.loadUrl(url1);
+            sActivityTestRule.loadUrl(url1);
             Assert.assertFalse(tab.needsReload());
             simulateRendererKilled(tab, false);
             Assert.assertTrue(tab.needsReload());
@@ -107,16 +128,16 @@
     @SmallTest
     @Feature({"SadTab"})
     public void testSadTabNoReloadAfterLoad() throws Throwable {
-        final Tab tab = mActivityTestRule.getActivity().getActivityTab();
+        final Tab tab = sActivityTestRule.getActivity().getActivityTab();
 
         TestWebServer webServer = TestWebServer.start();
         try {
             final String url1 = webServer.setEmptyResponse("/page1.html");
             final String url2 = webServer.setEmptyResponse("/page2.html");
-            mActivityTestRule.loadUrl(url1);
+            sActivityTestRule.loadUrl(url1);
             Assert.assertFalse(tab.needsReload());
             simulateRendererKilled(tab, false);
-            mActivityTestRule.loadUrl(url2);
+            sActivityTestRule.loadUrl(url2);
             Assert.assertFalse(tab.needsReload());
         } finally {
             webServer.shutdown();
@@ -133,14 +154,14 @@
     @SmallTest
     @Feature({"SadTab"})
     public void testSadTabPageButtonText() throws IllegalArgumentException {
-        final Tab tab = mActivityTestRule.getActivity().getActivityTab();
+        final Tab tab = sActivityTestRule.getActivity().getActivityTab();
 
         Assert.assertFalse(isShowingSadTab(tab));
         simulateRendererKilled(tab, true);
         Assert.assertTrue(isShowingSadTab(tab));
         String actualText = getSadTabButton(tab).getText().toString();
         Assert.assertEquals("Expected the sad tab button to have the reload label",
-                mActivityTestRule.getActivity().getString(R.string.sad_tab_reload_label),
+                sActivityTestRule.getActivity().getString(R.string.sad_tab_reload_label),
                 actualText);
 
         reloadSadTab(tab);
@@ -150,9 +171,9 @@
         Assert.assertEquals(
                 "Expected the sad tab button to have the feedback label after the tab button "
                         + "crashes twice in a row.",
-                mActivityTestRule.getActivity().getString(R.string.sad_tab_send_feedback_label),
+                sActivityTestRule.getActivity().getString(R.string.sad_tab_send_feedback_label),
                 actualText);
-        mActivityTestRule.loadUrl("about:blank");
+        sActivityTestRule.loadUrl("about:blank");
         Assert.assertFalse(
                 "Expected about:blank to destroy the sad tab however the sad tab is still in "
                         + "view",
@@ -161,10 +182,25 @@
         actualText = getSadTabButton(tab).getText().toString();
         Assert.assertEquals(
                 "Expected the sad tab button to have the reload label after a successful load",
-                mActivityTestRule.getActivity().getString(R.string.sad_tab_reload_label),
+                sActivityTestRule.getActivity().getString(R.string.sad_tab_reload_label),
                 actualText);
     }
 
+    @Test
+    @MediumTest
+    @Feature({"SadTab"})
+    public void testSadTabBrowserControlsVisibility() {
+        TestThreadUtils.runOnUiThreadBlocking(
+                TabStateBrowserControlsVisibilityDelegate::disablePageLoadDelayForTests);
+        FullscreenManagerTestUtils.disableBrowserOverrides();
+        sActivityTestRule.loadUrl(LONG_HTML_TEST_PAGE);
+        FullscreenManagerTestUtils.waitForBrowserControlsToBeMoveable(
+                sActivityTestRule, sActivityTestRule.getActivity().getActivityTab());
+        FullscreenManagerTestUtils.scrollBrowserControls(sActivityTestRule, false);
+        simulateRendererKilled(sActivityTestRule.getActivity().getActivityTab(), true);
+        FullscreenManagerTestUtils.waitForBrowserControlsPosition(sActivityTestRule, 0);
+    }
+
     /**
      * Helper method that kills the renderer on a UI thread.
      */
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java
new file mode 100644
index 0000000..ddb1ad6
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java
@@ -0,0 +1,101 @@
+// 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.
+
+package org.chromium.chrome.browser.compositor.overlays.strip;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.supplier.Supplier;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.compositor.LayerTitleCache;
+import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
+import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
+import org.chromium.chrome.browser.compositor.scene_layer.TabStripSceneLayer;
+import org.chromium.chrome.browser.compositor.scene_layer.TabStripSceneLayerJni;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.browser_ui.styles.ChromeColors;
+
+/** Tests for {@link StripLayoutHelperManager}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Features.EnableFeatures({ChromeFeatureList.TAB_STRIP_REDESIGN})
+@Config(manifest = Config.NONE, qualifiers = "sw600dp")
+public class StripLayoutHelperManagerTest {
+    @Rule
+    public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor();
+    @Rule
+    public JniMocker mJniMocker = new JniMocker();
+    @Mock
+    private TabStripSceneLayer.Natives mTabStripSceneMock;
+    @Mock
+    private LayoutUpdateHost mUpdateHost;
+    @Mock
+    private LayoutRenderHost mRenderHost;
+    @Mock
+    private Supplier<LayerTitleCache> mLayerTitleCacheSupplier;
+    @Mock
+    private ActivityLifecycleDispatcher mLifecycleDispatcher;
+
+    private StripLayoutHelperManager mStripLayoutHelperManager;
+    private Context mContext;
+
+    @Before
+    public void beforeTest() {
+        MockitoAnnotations.initMocks(this);
+        mJniMocker.mock(TabStripSceneLayerJni.TEST_HOOKS, mTabStripSceneMock);
+        mContext = new ContextThemeWrapper(
+                ApplicationProvider.getApplicationContext(), R.style.Theme_BrowserUI_DayNight);
+        TabStripSceneLayer.setTestFlag(true);
+        initializeTest();
+    }
+
+    @After
+    public void tearDown() {
+        TabStripSceneLayer.setTestFlag(false);
+        TabUiFeatureUtilities.setTabStripRedesignEnableDetachedForTesting(false);
+        TabUiFeatureUtilities.setTabStripRedesignEnableFolioForTesting(false);
+    }
+
+    private void initializeTest() {
+        mStripLayoutHelperManager = new StripLayoutHelperManager(
+                mContext, mUpdateHost, mRenderHost, mLayerTitleCacheSupplier, mLifecycleDispatcher);
+    }
+
+    @Test
+    @Feature("Tab Strip Redesign")
+    public void testGetBackgroundColorDetached() {
+        TabUiFeatureUtilities.setTabStripRedesignEnableDetachedForTesting(true);
+        assertEquals(ChromeColors.getSurfaceColor(mContext, R.dimen.default_elevation_0),
+                mStripLayoutHelperManager.getBackgroundColor());
+    }
+
+    @Test
+    @Feature("Tab Strip Redesign")
+    public void testGetBackgroundColorFolio() {
+        TabUiFeatureUtilities.setTabStripRedesignEnableFolioForTesting(true);
+        mStripLayoutHelperManager.onContextChanged(mContext);
+        assertEquals(ChromeColors.getSurfaceColor(mContext, R.dimen.default_elevation_2),
+                mStripLayoutHelperManager.getBackgroundColor());
+    }
+}
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 305617d3..6cd3036 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -1456,7 +1456,7 @@
       // before crashpad is initialized. Please leave this check immediately
       // before the crashpad initialization; the amount of memory used at this
       // point is important to the test.
-      IMMEDIATE_CRASH();
+      base::ImmediateCrash();
     }
 #if BUILDFLAG(IS_ANDROID)
     crash_reporter::InitializeCrashpad(process_type.empty(), process_type);
diff --git a/chrome/app/framework.order b/chrome/app/framework.order
index 60f573a..1d6be61 100644
--- a/chrome/app/framework.order
+++ b/chrome/app/framework.order
@@ -19,7 +19,7 @@
 ___asan_default_options
 
 # Entry point from the app mode loader.
-_ChromeAppModeStart_v7
+_ChromeAppModeStart_v6
 
 # _ChromeMain must be listed last.  That's the whole point of this file.
 _ChromeMain
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 6dd4bd1df..6b0836f1 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -9877,6 +9877,10 @@
             Pages you view in this window won't appear in the browser history and they won't leave other traces, like cookies, on the computer after you close all open Guest windows. Any files you download will be preserved, however.
           </message>
         </if>
+        <message name="IDS_NEW_TAB_GUEST_SESSION_LEARN_MORE_ACCESSIBILITY_TEXT"
+            desc="Accessibility label for a link labeled 'Learn more' that links to a help article about browsing as a guest.">
+          Learn more about browsing as a guest
+        </message>
         <message name="IDS_NEW_TAB_TILE_GRID_ACCESSIBLE_DESCRIPTION"
                  desc="Help message for screenreader users, spoken when navigating to a tile grid for the first time.">
           Use left and right arrow keys to navigate.
diff --git a/chrome/app/generated_resources_grd/IDS_NEW_TAB_GUEST_SESSION_LEARN_MORE_ACCESSIBILITY_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_NEW_TAB_GUEST_SESSION_LEARN_MORE_ACCESSIBILITY_TEXT.png.sha1
new file mode 100644
index 0000000..e385641
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_NEW_TAB_GUEST_SESSION_LEARN_MORE_ACCESSIBILITY_TEXT.png.sha1
@@ -0,0 +1 @@
+9bedb88eaee93367bbb284b8eca365fb525050ac
\ No newline at end of file
diff --git a/chrome/app/shared_settings_strings.grdp b/chrome/app/shared_settings_strings.grdp
index b778eaad..9acf01d1 100644
--- a/chrome/app/shared_settings_strings.grdp
+++ b/chrome/app/shared_settings_strings.grdp
@@ -215,9 +215,18 @@
   <message name="IDS_SETTINGS_ENABLE_LOGGING_PREF" desc="The label of the checkbox to enable/disable crash and user metrics logging. This string does not need a Chromium counter-part because it will only be visible if Google-branded.">
     Help improve Chrome's features and performance
   </message>
-  <message name="IDS_SETTINGS_ENABLE_LOGGING_PREF_DESC" desc="The description of the checkbox to enable/disable crash and user metrics logging">
-    Automatically sends usage statistics and crash reports to Google
-  </message>
+  <if expr="is_fuchsia">
+    <then>
+      <message name="IDS_SETTINGS_ENABLE_LOGGING_PREF_DESC" desc="The description of the checkbox to enable/disable crash and user metrics logging">
+        Automatically sends usage statistics to Google. You can turn crash reports on or off in your device's settings.
+      </message>
+    </then>
+    <else>
+      <message name="IDS_SETTINGS_ENABLE_LOGGING_PREF_DESC" desc="The description of the checkbox to enable/disable crash and user metrics logging">
+        Automatically sends usage statistics and crash reports to Google
+      </message>
+    </else>
+  </if>
   <message name="IDS_SETTINGS_LINKDOCTOR_PREF" desc="The documentation string of the 'Use Link Doctor' preference to help with navigation errors.">
     Show suggestions for similar pages when a page can't be found
   </message>
diff --git a/chrome/app/vector_icons/download_in_progress.icon b/chrome/app/vector_icons/download_in_progress.icon
index 921f034..64f8ad5 100644
--- a/chrome/app/vector_icons/download_in_progress.icon
+++ b/chrome/app/vector_icons/download_in_progress.icon
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-CANVAS_DIMENSIONS, 20,
-R_MOVE_TO, 13.37f, 8.99f,
-R_LINE_TO, 1.32f, 1.32f,
-R_LINE_TO, -4.69f, 4.69f,
-R_LINE_TO, -4.69f, -4.69f,
-R_LINE_TO, 1.32f, -1.32f,
-R_LINE_TO, 2.37f, 2.42f,
-R_V_LINE_TO, -6.41f,
-R_H_LINE_TO, 2,
-R_V_LINE_TO, 6.41f,
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 11.37f, 6.99f,
+LINE_TO, 12.69f, 8.31f,
+LINE_TO, 8, 13,
+LINE_TO, 3.31f, 8.31f,
+LINE_TO, 4.63f, 6.99f,
+LINE_TO, 7, 9.41f,
+V_LINE_TO, 3,
+H_LINE_TO, 9,
+V_LINE_TO, 9.41f,
+LINE_TO, 11.37f, 6.99f,
 CLOSE
diff --git a/chrome/app/vector_icons/download_in_progress_touch.icon b/chrome/app/vector_icons/download_in_progress_touch.icon
index 9d8ea4f5..fb8d6c23 100644
--- a/chrome/app/vector_icons/download_in_progress_touch.icon
+++ b/chrome/app/vector_icons/download_in_progress_touch.icon
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-CANVAS_DIMENSIONS, 26,
-MOVE_TO, 16.95f, 12.06f,
-LINE_TO, 18.5f, 13.58f,
-LINE_TO, 13, 19,
-LINE_TO, 7.5f, 13.58f,
-LINE_TO, 9.05f, 12.06f,
-LINE_TO, 12, 15,
-V_LINE_TO, 7,
-H_LINE_TO, 14,
-V_LINE_TO, 15,
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 16.67f, 10.99f,
+LINE_TO, 18.5f, 12.75f,
+LINE_TO, 12, 19,
+LINE_TO, 5.5f, 12.75f,
+LINE_TO, 7.33f, 10.99f,
+LINE_TO, 10.82f, 14.38f,
+V_LINE_TO, 5,
+H_LINE_TO, 13.18f,
+V_LINE_TO, 14.38f,
+LINE_TO, 16.67f, 10.99f,
 CLOSE
diff --git a/chrome/app/vector_icons/download_toolbar_button.icon b/chrome/app/vector_icons/download_toolbar_button.icon
index 3bd8547..5310bac 100644
--- a/chrome/app/vector_icons/download_toolbar_button.icon
+++ b/chrome/app/vector_icons/download_toolbar_button.icon
@@ -2,26 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-CANVAS_DIMENSIONS, 20,
-R_MOVE_TO, 15.4f, 16.4f,
-R_V_LINE_TO, -2.7f,
-R_H_LINE_TO, 1.8f,
-R_V_LINE_TO, 2.7f,
-R_CUBIC_TO, 0, 0.99f, -0.81f, 1.8f, -1.8f, 1.8f,
-R_H_LINE_TO, -10.8f,
-R_CUBIC_TO, -0.99f, 0, -1.8f, -0.81f, -1.8f, -1.8f,
-R_V_LINE_TO, -2.7f,
-R_H_LINE_TO, 1.8f,
-R_V_LINE_TO, 2.7f,
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 14, 11,
+V_LINE_TO, 14,
+H_LINE_TO, 2,
+V_LINE_TO, 11,
+H_LINE_TO, 0,
+V_LINE_TO, 14,
+CUBIC_TO, 0, 15.1f, 0.9f, 16, 2, 16,
+H_LINE_TO, 14,
+CUBIC_TO, 15.1f, 16, 16, 15.1f, 16, 14,
+V_LINE_TO, 11,
+H_LINE_TO, 14,
 CLOSE,
-NEW_PATH,
-R_MOVE_TO, 13.37f, 8.99f,
-R_LINE_TO, 1.32f, 1.32f,
-R_LINE_TO, -4.69f, 4.69f,
-R_LINE_TO, -4.69f, -4.69f,
-R_LINE_TO, 1.32f, -1.32f,
-R_LINE_TO, 2.37f, 2.42f,
-R_V_LINE_TO, -7.41f,
-R_H_LINE_TO, 2,
-R_V_LINE_TO, 7.41f,
+MOVE_TO, 13, 7,
+LINE_TO, 11.59f, 5.59f,
+LINE_TO, 9, 8.17f,
+V_LINE_TO, 0,
+H_LINE_TO, 7,
+V_LINE_TO, 8.17f,
+LINE_TO, 4.41f, 5.59f,
+LINE_TO, 3, 7,
+LINE_TO, 8, 12,
+LINE_TO, 13, 7,
 CLOSE
diff --git a/chrome/app/vector_icons/download_toolbar_button_touch.icon b/chrome/app/vector_icons/download_toolbar_button_touch.icon
index 6c59a141..efaed61a 100644
--- a/chrome/app/vector_icons/download_toolbar_button_touch.icon
+++ b/chrome/app/vector_icons/download_toolbar_button_touch.icon
@@ -2,26 +2,28 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-CANVAS_DIMENSIONS, 26,
-MOVE_TO, 19.88f, 19,
-V_LINE_TO, 15,
-H_LINE_TO, 22,
-V_LINE_TO, 19,
-CUBIC_TO, 22, 20.21f, 21.04f, 21.25f, 19.88f, 21.25f,
-H_LINE_TO, 6.13f,
-CUBIC_TO, 4.96f, 21.25f, 4, 20.21f, 4, 19,
-V_LINE_TO, 15,
-H_LINE_TO, 6.13f,
-V_LINE_TO, 19,
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 18.88f, 18,
+V_LINE_TO, 14,
+H_LINE_TO, 21,
+V_LINE_TO, 18,
+CUBIC_TO, 21, 19.21f, 20.04f, 20.25f, 18.88f, 20.25f,
+H_LINE_TO, 5.13f,
+CUBIC_TO, 3.96f, 20.25f, 3, 19.21f, 3, 18,
+V_LINE_TO, 14,
+H_LINE_TO, 5.13f,
+V_LINE_TO, 18,
+H_LINE_TO, 18.88f,
 CLOSE,
 NEW_PATH,
-MOVE_TO, 16.95f, 10.06f,
-LINE_TO, 18.5f, 11.58f,
-LINE_TO, 13, 17,
-LINE_TO, 7.5f, 11.58f,
-LINE_TO, 9.05f, 10.06f,
-LINE_TO, 12, 13,
-V_LINE_TO, 4,
-H_LINE_TO, 14,
-V_LINE_TO, 13,
+MOVE_TO, 15.95f, 9.06f,
+LINE_TO, 17.5f, 10.58f,
+LINE_TO, 12, 16,
+LINE_TO, 6.5f, 10.58f,
+LINE_TO, 8.05f, 9.06f,
+LINE_TO, 11, 12,
+V_LINE_TO, 3,
+H_LINE_TO, 13,
+V_LINE_TO, 12,
+LINE_TO, 15.95f, 9.06f,
 CLOSE
diff --git a/chrome/app_shim/BUILD.gn b/chrome/app_shim/BUILD.gn
index 46fb84d7..a6f3f2f8 100644
--- a/chrome/app_shim/BUILD.gn
+++ b/chrome/app_shim/BUILD.gn
@@ -35,7 +35,6 @@
     "//content/public/browser",
     "//ipc",
     "//mojo/core/embedder",
-    "//mojo/core/embedder:features",
     "//ui/accelerated_widget_mac",
     "//ui/base",
   ]
diff --git a/chrome/app_shim/app_mode_loader_mac.mm b/chrome/app_shim/app_mode_loader_mac.mm
index c62e4a32..3e6a516 100644
--- a/chrome/app_shim/app_mode_loader_mac.mm
+++ b/chrome/app_shim/app_mode_loader_mac.mm
@@ -91,8 +91,6 @@
 
     // ** 3: Read the Chrome executable, Chrome framework, and Chrome framework
     // dylib paths.
-    app_mode::MojoIpczConfig mojo_ipcz_config =
-        app_mode::MojoIpczConfig::kUseCommandLineFeatures;
     base::FilePath executable_path;
     base::FilePath framework_path;
     base::FilePath framework_dylib_path;
@@ -100,46 +98,36 @@
             app_mode::kLaunchedByChromeFrameworkBundlePath) &&
         command_line.HasSwitch(app_mode::kLaunchedByChromeFrameworkDylibPath)) {
       // If Chrome launched this app shim, then it will specify the framework
-      // path and version, as well as flags to enable or disable MojoIpcz as
-      // needed. Do not populate `executable_path` (it is used to launch Chrome
-      // if Chrome is not running, which is inapplicable here).
+      // path and version. Do not populate `executable_path` (it is used to
+      // launch Chrome if Chrome is not running, which is inapplicable here).
       framework_path = command_line.GetSwitchValuePath(
           app_mode::kLaunchedByChromeFrameworkBundlePath);
       framework_dylib_path = command_line.GetSwitchValuePath(
           app_mode::kLaunchedByChromeFrameworkDylibPath);
     } else {
       // Otherwise, read the version from the symbolic link in the user data
-      // dir. If the version file does not exist, the version string will be
-      // empty and app_mode::GetChromeBundleInfo will default to the latest
-      // version, with MojoIpcz disabled.
-      app_mode::ChromeConnectionConfig config;
-      base::FilePath encoded_config;
+      // dir. If the version file does not exist, |cr_version_str| will be empty
+      // and app_mode::GetChromeBundleInfo will default to the latest version.
+      base::FilePath cr_version_str;
       base::ReadSymbolicLink(
           user_data_dir.Append(app_mode::kRunningChromeVersionSymlinkName),
-          &encoded_config);
-      if (!encoded_config.empty()) {
-        config =
-            app_mode::ChromeConnectionConfig::DecodeFromPath(encoded_config);
-        mojo_ipcz_config = config.is_mojo_ipcz_enabled
-                               ? app_mode::MojoIpczConfig::kEnabled
-                               : app_mode::MojoIpczConfig::kDisabled;
-      }
+          &cr_version_str);
       // If the version file does exist, it may have been left by a crashed
       // Chrome process. Ensure the process is still running.
-      if (!config.framework_version.empty()) {
+      if (!cr_version_str.empty()) {
         NSArray* existing_chrome = [NSRunningApplication
             runningApplicationsWithBundleIdentifier:cr_bundle_id];
         if ([existing_chrome count] == 0) {
           NSLog(@"Disregarding framework version from symlink");
-          config.framework_version.clear();
+          cr_version_str.clear();
         } else {
           NSLog(@"Framework version from symlink %s",
-                config.framework_version.c_str());
+                cr_version_str.value().c_str());
         }
       }
       if (!app_mode::GetChromeBundleInfo(
-              cr_bundle_path, config.framework_version.c_str(),
-              &executable_path, &framework_path, &framework_dylib_path)) {
+              cr_bundle_path, cr_version_str.value().c_str(), &executable_path,
+              &framework_path, &framework_dylib_path)) {
         NSLog(@"Couldn't ready Chrome bundle info");
         return kErrorReturnValue;
       }
@@ -208,7 +196,6 @@
       info.app_mode_url = app_mode_url.c_str();
       info.user_data_dir = plist_user_data_dir_utf8.c_str();
       info.profile_dir = profile_dir_utf8.c_str();
-      info.mojo_ipcz_config = mojo_ipcz_config;
       return ChromeAppModeStart(&info);
     }
 
diff --git a/chrome/app_shim/chrome_main_app_mode_mac.mm b/chrome/app_shim/chrome_main_app_mode_mac.mm
index e73c856..879976d 100644
--- a/chrome/app_shim/chrome_main_app_mode_mac.mm
+++ b/chrome/app_shim/chrome_main_app_mode_mac.mm
@@ -12,11 +12,9 @@
 
 #include "base/allocator/early_zone_registration_mac.h"
 #include "base/at_exit.h"
-#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/check.h"
 #include "base/command_line.h"
-#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/mac/bundle_locations.h"
@@ -36,7 +34,6 @@
 #include "chrome/common/mac/app_mode_common.h"
 #include "components/crash/core/app/crashpad.h"
 #include "mojo/core/embedder/embedder.h"
-#include "mojo/core/embedder/features.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
 #include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -136,27 +133,6 @@
     base::Thread* io_thread = new base::Thread("CrAppShimIO");
     io_thread->StartWithOptions(std::move(io_thread_options));
 
-    // It's necessary to initialize a FeatureList and call Mojo's InitFeatures()
-    // to ensure we're using the same IPC implementation as the browser.
-    auto feature_list = std::make_unique<base::FeatureList>();
-    if (info->mojo_ipcz_config ==
-        app_mode::MojoIpczConfig::kUseCommandLineFeatures) {
-      const auto& command_line = *base::CommandLine::ForCurrentProcess();
-      feature_list->InitializeFromCommandLine(
-          command_line.GetSwitchValueASCII(switches::kEnableFeatures),
-          command_line.GetSwitchValueASCII(switches::kDisableFeatures));
-
-    } else {
-      const bool mojo_ipcz_enabled =
-          info->mojo_ipcz_config == app_mode::MojoIpczConfig::kEnabled;
-      feature_list->RegisterExtraFeatureOverrides(
-          {{mojo::core::kMojoIpcz,
-            mojo_ipcz_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE
-                              : base::FeatureList::OVERRIDE_DISABLE_FEATURE}});
-    }
-    base::FeatureList::SetInstance(std::move(feature_list));
-    mojo::core::InitFeatures();
-
     // We're using an isolated Mojo connection between the browser and this
     // process, so this process must act as a broker.
     mojo::core::Configuration config;
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index c501302..314156e 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -6850,7 +6850,7 @@
       sources += [ "background/background_mode_manager_win.cc" ]
     }
     if (is_mac) {
-      sources += [ "background/background_mode_manager_mac.mm" ]
+      sources += [ "background/background_mode_manager_mac.cc" ]
     }
     if (is_chromeos_ash) {
       sources += [ "background/background_mode_manager_chromeos.cc" ]
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 80258d0..f67064a0 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -7145,6 +7145,11 @@
      flag_descriptions::kEnableShortcutCustomizationAppDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(features::kShortcutCustomizationApp)},
 
+    {"enable-input-device-settings-split",
+     flag_descriptions::kEnableInputDeviceSettingsSplitName,
+     flag_descriptions::kEnableInputDeviceSettingsSplitDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kShortcutCustomizationApp)},
+
     {"enable-shortcut-customization",
      flag_descriptions::kEnableShortcutCustomizationName,
      flag_descriptions::kEnableShortcutCustomizationDescription, kOsCrOS,
@@ -9195,13 +9200,6 @@
 #endif
 
 #if !BUILDFLAG(IS_ANDROID)
-    {"enable-tailored-security-desktop-notice",
-     flag_descriptions::kTailoredSecurityDesktopNoticeName,
-     flag_descriptions::kTailoredSecurityDesktopNoticeDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(safe_browsing::kTailoredSecurityDesktopNotice)},
-#endif
-
-#if !BUILDFLAG(IS_ANDROID)
     {"screen-ai", flag_descriptions::kScreenAIName,
      flag_descriptions::kScreenAIDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kScreenAI)},
@@ -9625,6 +9623,13 @@
      FEATURE_VALUE_TYPE(browser_ui::kRequestDesktopSiteExceptionsDowngrade)},
 #endif  // BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(IS_ANDROID)
+    {"request-desktop-site-zoom",
+     flag_descriptions::kRequestDesktopSiteZoomName,
+     flag_descriptions::kRequestDesktopSiteZoomDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(features::kRequestDesktopSiteZoom)},
+#endif  // BUILDFLAG(IS_ANDROID)
+
 #if !BUILDFLAG(IS_ANDROID)
     {"enable-web-hid-on-extension-service-worker",
      flag_descriptions::kEnableWebHidOnExtensionServiceWorkerName,
diff --git a/chrome/browser/android/chrome_backup_agent.cc b/chrome/browser/android/chrome_backup_agent.cc
index 8c845a2b..7ef0ab05 100644
--- a/chrome/browser/android/chrome_backup_agent.cc
+++ b/chrome/browser/android/chrome_backup_agent.cc
@@ -20,7 +20,7 @@
 
 // TODO(crbug.com/1305213): The data type toggles shouldn't be individually
 // listed here.
-static_assert(43 == syncer::GetNumModelTypes(),
+static_assert(44 == syncer::GetNumModelTypes(),
               "If the new type has a corresponding pref, add it here");
 const char* backed_up_preferences_[] = {
     autofill::prefs::kAutofillWalletImportEnabled,
@@ -34,6 +34,7 @@
     syncer::prefs::kSyncRequested,
     syncer::prefs::kSyncTabs,
     syncer::prefs::kSyncTypedUrls,
+    syncer::prefs::kSyncSavedTabGroups,
 };
 
 }  // namespace
diff --git a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
index cdad4a2..b44916e 100644
--- a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
@@ -49,12 +49,6 @@
     scrollable_strip_layer_->AddChild(new_tab_button_);
   }
 
-  // Using kLtGray as a temporary background color for TabStripRedesign
-  if (!base::FeatureList::IsEnabled(chrome::android::kTabStripRedesign)) {
-    tab_strip_layer_->SetBackgroundColor(SkColors::kBlack);
-  } else {
-    tab_strip_layer_->SetBackgroundColor(SkColors::kLtGray);
-  }
   tab_strip_layer_->SetIsDrawable(true);
   tab_strip_layer_->AddChild(scrollable_strip_layer_);
 
@@ -118,11 +112,13 @@
                                              jfloat width,
                                              jfloat height,
                                              jfloat y_offset,
-                                             jboolean should_readd_background) {
+                                             jboolean should_readd_background,
+                                             jint background_color) {
   gfx::RectF content(0, y_offset, width, height);
   layer()->SetPosition(gfx::PointF(0, y_offset));
   tab_strip_layer_->SetBounds(gfx::Size(width, height));
   scrollable_strip_layer_->SetBounds(gfx::Size(width, height));
+  tab_strip_layer_->SetBackgroundColor(SkColor4f::FromColor(background_color));
 
   // Content tree should not be affected by tab strip scene layer visibility.
   if (content_tree_)
@@ -155,7 +151,6 @@
     scrim_layer_->SetIsDrawable(false);
     return;
   }
-
   scrim_layer_->SetIsDrawable(true);
   // TODO(crbug/1308932): Remove FromColor and make all SkColor4f.
   scrim_layer_->SetBackgroundColor(SkColor4f::FromColor(color));
diff --git a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.h b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.h
index 5c1dbf6..ae173e8e5 100644
--- a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.h
+++ b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.h
@@ -53,7 +53,8 @@
                            jfloat width,
                            jfloat height,
                            jfloat y_offset,
-                           jboolean should_readd_background);
+                           jboolean should_readd_background,
+                           jint background_color);
 
   void UpdateStripScrim(JNIEnv* env,
                         const base::android::JavaParamRef<jobject>& jobj,
diff --git a/chrome/browser/apps/app_shim/DEPS b/chrome/browser/apps/app_shim/DEPS
index 4080ef6..9745612 100644
--- a/chrome/browser/apps/app_shim/DEPS
+++ b/chrome/browser/apps/app_shim/DEPS
@@ -1,8 +1,3 @@
-include_rules = [
-  "+mojo/core/embedder",
-  "+third_party/ipcz/include",
-]
-
 specific_include_rules = {
   "app_shim_listener_browsertest_mac.mm": [
     "+chrome/app_shim/app_shim_controller.h",
diff --git a/chrome/browser/apps/app_shim/app_shim_listener.mm b/chrome/browser/apps/app_shim/app_shim_listener.mm
index 190e09d..a6dae73 100644
--- a/chrome/browser/apps/app_shim/app_shim_listener.mm
+++ b/chrome/browser/apps/app_shim/app_shim_listener.mm
@@ -21,6 +21,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/mac/app_mode_common.h"
+#include "components/version_info/version_info.h"
 #include "content/public/browser/browser_task_traits.h"
 
 AppShimListener::AppShimListener() {}
@@ -80,16 +81,14 @@
       std::make_unique<apps::MachBootstrapAcceptor>(name_fragment, this);
   mach_acceptor_->Start();
 
-  // Create a symlink containing the current version string and a bit indicating
-  // whether or not the MojoIpcz feature is enabled. This allows the shim to
-  // load the same framework version as the currently running Chrome process,
-  // and it ensures that both processes are using the same IPC implementation.
+  // Create a symlink containing the current version string. This allows the
+  // shim to load the same framework version as the currently running Chrome
+  // process.
   base::FilePath version_path =
       user_data_dir.Append(app_mode::kRunningChromeVersionSymlinkName);
-  const auto config =
-      app_mode::ChromeConnectionConfig::GenerateForCurrentProcess();
   base::DeleteFile(version_path);
-  base::CreateSymbolicLink(config.EncodeAsPath(), version_path);
+  base::CreateSymbolicLink(base::FilePath(version_info::GetVersionNumber()),
+                           version_path);
 }
 
 void AppShimListener::OnClientConnected(mojo::PlatformChannelEndpoint endpoint,
diff --git a/chrome/browser/apps/app_shim/app_shim_listener_browsertest_mac.mm b/chrome/browser/apps/app_shim/app_shim_listener_browsertest_mac.mm
index 2b45ecc..4944661 100644
--- a/chrome/browser/apps/app_shim/app_shim_listener_browsertest_mac.mm
+++ b/chrome/browser/apps/app_shim/app_shim_listener_browsertest_mac.mm
@@ -10,7 +10,6 @@
 
 #include "base/bind.h"
 #include "base/check.h"
-#include "base/check_op.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/mac/foundation_util.h"
@@ -32,17 +31,14 @@
 #include "components/version_info/version_info.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
-#include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
-#include "mojo/public/cpp/system/handle.h"
 #include "mojo/public/cpp/system/isolated_connection.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/ipcz/include/ipcz/ipcz.h"
 
 // A test version of the AppShimController mojo client in chrome_main_app_mode.
 class TestShimClient : public chrome::mojom::AppShim {
@@ -92,48 +88,15 @@
     shim_receiver_.Bind(std::move(app_shim_receiver));
   }
 
-  mojo::ScopedMessagePipeHandle ConnectIcpzToShim(
-      mojo::PlatformChannelEndpoint endpoint) {
-    // ipcz does not support nodes connecting to themselves, as these tests
-    // normally do. Instead for ipcz we set up a secondary broker node and use
-    // that to connect back to Mojo's global ipcz node in this process,
-    // effectively simulating an external shim process.
-    //
-    // Note that ipcz handles and Mojo handles are interchangeable types with
-    // ipcz enabled, so we can use scoped Mojo handles to manage ipcz object
-    // lifetime here.
-    const IpczAPI& ipcz = mojo::core::GetIpczAPIForMojo();
-    IpczHandle node;
-    IpczResult result = ipcz.CreateNode(
-        &mojo::core::GetIpczDriverForMojo(), IPCZ_INVALID_DRIVER_HANDLE,
-        IPCZ_CREATE_NODE_AS_BROKER, nullptr, &node);
-    CHECK_EQ(IPCZ_RESULT_OK, result);
-    secondary_ipcz_broker_.reset(mojo::Handle{node});
-
-    // MojoIpcz reserves the first portal on each invitation connection for
-    // internal services. We discard it here since it's not needed.
-    IpczHandle portals[2];
-    result = ipcz.ConnectNode(
-        secondary_ipcz_broker_->value(),
-        mojo::core::CreateIpczTransportFromEndpoint(
-            std::move(endpoint),
-            {.local_is_broker = true, .remote_is_broker = true}),
-        /*num_initial_portals=*/2, IPCZ_CONNECT_NODE_TO_BROKER,
-        /*options=*/nullptr, portals);
-    CHECK_EQ(IPCZ_RESULT_OK, result);
-    ipcz.Close(portals[0], IPCZ_NO_FLAGS, nullptr);
-    return mojo::ScopedMessagePipeHandle(mojo::MessagePipeHandle(portals[1]));
-  }
-
   mojo::IsolatedConnection mojo_connection_;
-  mojo::ScopedHandle secondary_ipcz_broker_;
   mojo::Receiver<chrome::mojom::AppShim> shim_receiver_{this};
   mojo::Remote<chrome::mojom::AppShimHost> host_;
   mojo::PendingReceiver<chrome::mojom::AppShimHost> host_receiver_;
   mojo::Remote<chrome::mojom::AppShimHostBootstrap> host_bootstrap_;
 };
 
-TestShimClient::TestShimClient() {
+TestShimClient::TestShimClient()
+    : host_receiver_(host_.BindNewPipeAndPassReceiver()) {
   base::FilePath user_data_dir;
   CHECK(base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
 
@@ -142,32 +105,8 @@
        ".", base::MD5String(user_data_dir.value())});
   mojo::PlatformChannelEndpoint endpoint = ConnectToBrowser(name_fragment);
 
-  mojo::ScopedMessagePipeHandle message_pipe;
-  if (mojo::core::IsMojoIpczEnabled()) {
-    // With MojoIpcz, we need to set up a secondary node in order to simulate an
-    // external shim connection.
-    message_pipe = ConnectIcpzToShim(std::move(endpoint));
-
-    // It's important for the AppShimHost interface portals to be created on the
-    // secondary node too, since the fake shim passes the receiver endpoint back
-    // to the host in a reply over the primordial AppShim interface connecting
-    // the two nodes.
-    const IpczAPI& ipcz = mojo::core::GetIpczAPIForMojo();
-    IpczHandle remote, receiver;
-    const IpczResult result =
-        ipcz.OpenPortals(secondary_ipcz_broker_->value(), IPCZ_NO_FLAGS,
-                         nullptr, &remote, &receiver);
-    CHECK_EQ(IPCZ_RESULT_OK, result);
-    host_.Bind(mojo::PendingRemote<chrome::mojom::AppShimHost>(
-        mojo::ScopedMessagePipeHandle(mojo::MessagePipeHandle(remote)), 0));
-    host_receiver_ = mojo::PendingReceiver<chrome::mojom::AppShimHost>(
-        mojo::ScopedMessagePipeHandle(mojo::MessagePipeHandle(receiver)));
-  } else {
-    // Non-ipcz Mojo supports processes establishing IsolatedConnections to
-    // themselves.
-    message_pipe = mojo_connection_.Connect(std::move(endpoint));
-    host_receiver_ = host_.BindNewPipeAndPassReceiver();
-  }
+  mojo::ScopedMessagePipeHandle message_pipe =
+      mojo_connection_.Connect(std::move(endpoint));
   host_bootstrap_ = mojo::Remote<chrome::mojom::AppShimHostBootstrap>(
       mojo::PendingRemote<chrome::mojom::AppShimHostBootstrap>(
           std::move(message_pipe), 0));
@@ -330,10 +269,7 @@
 IN_PROC_BROWSER_TEST_F(AppShimListenerBrowserTestSymlink,
                        RunningChromeVersionCorrectlyWritten) {
   // Check that the RunningChromeVersion file is correctly written.
-  base::FilePath encoded_config;
-  EXPECT_TRUE(base::ReadSymbolicLink(version_path_, &encoded_config));
-  auto config =
-      app_mode::ChromeConnectionConfig::DecodeFromPath(encoded_config);
-  EXPECT_EQ(version_info::GetVersionNumber(), config.framework_version);
-  EXPECT_EQ(mojo::core::IsMojoIpczEnabled(), config.is_mojo_ipcz_enabled);
+  base::FilePath version;
+  EXPECT_TRUE(base::ReadSymbolicLink(version_path_, &version));
+  EXPECT_EQ(version_info::GetVersionNumber(), version.value());
 }
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator_util.h b/chrome/browser/ash/crosapi/browser_data_migrator_util.h
index 92282ae..07bf9c39 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator_util.h
+++ b/chrome/browser/ash/crosapi/browser_data_migrator_util.h
@@ -303,7 +303,7 @@
 };
 
 // List of data types in Sync Data that have to stay in Ash and Ash only.
-static_assert(43 == syncer::GetNumModelTypes(),
+static_assert(44 == syncer::GetNumModelTypes(),
               "If adding a new sync data type, update the lists below if"
               " you want to keep the new data type in Ash only.");
 constexpr syncer::ModelType kAshOnlySyncDataTypes[] = {
diff --git a/chrome/browser/ash/crosapi/keystore_service_ash.cc b/chrome/browser/ash/crosapi/keystore_service_ash.cc
index 6ff4659..fef6138 100644
--- a/chrome/browser/ash/crosapi/keystore_service_ash.cc
+++ b/chrome/browser/ash/crosapi/keystore_service_ash.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chromeos/crosapi/cpp/keystore_service_util.h"
 #include "chromeos/crosapi/mojom/keystore_error.mojom.h"
+#include "chromeos/crosapi/mojom/keystore_service.mojom-shared.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/cert/x509_certificate.h"
@@ -201,6 +202,7 @@
     mojom::KeystoreType type,
     const std::vector<uint8_t>& challenge,
     bool migrate,
+    mojom::KeystoreSigningAlgorithmName algorithm,
     ChallengeAttestationOnlyKeystoreCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!crosapi::mojom::IsKnownEnumValue(type)) {
@@ -210,6 +212,18 @@
     return;
   }
 
+  attestation::KeyType key_crypto_type;
+  switch (algorithm) {
+    // Use RSA by default for backwards compatibility.
+    case mojom::KeystoreSigningAlgorithmName::kUnknown:
+    case mojom::KeystoreSigningAlgorithmName::kRsassaPkcs115:
+      key_crypto_type = attestation::KEY_TYPE_RSA;
+      break;
+    case mojom::KeystoreSigningAlgorithmName::kEcdsa:
+      key_crypto_type = attestation::KEY_TYPE_ECC;
+      break;
+  }
+
   ash::attestation::AttestationKeyType key_type;
   switch (type) {
     case mojom::KeystoreType::kUser:
@@ -239,7 +253,7 @@
                      weak_factory_.GetWeakPtr(), std::move(callback),
                      challenge_key_ptr),
       std::string(challenge.begin(), challenge.end()),
-      /*register_key=*/migrate, ::attestation::KEY_TYPE_RSA, key_name_for_spkac,
+      /*register_key=*/migrate, key_crypto_type, key_name_for_spkac,
       /*signals=*/absl::nullopt);
 }
 
diff --git a/chrome/browser/ash/crosapi/keystore_service_ash.h b/chrome/browser/ash/crosapi/keystore_service_ash.h
index be42e96..7200d97 100644
--- a/chrome/browser/ash/crosapi/keystore_service_ash.h
+++ b/chrome/browser/ash/crosapi/keystore_service_ash.h
@@ -13,6 +13,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/platform_keys/platform_keys.h"
+#include "chromeos/crosapi/mojom/keystore_service.mojom-shared.h"
 #include "chromeos/crosapi/mojom/keystore_service.mojom.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -62,6 +63,7 @@
       mojom::KeystoreType type,
       const std::vector<uint8_t>& challenge,
       bool migrate,
+      mojom::KeystoreSigningAlgorithmName algorithm,
       ChallengeAttestationOnlyKeystoreCallback callback) override;
   void GetKeyStores(GetKeyStoresCallback callback) override;
   void SelectClientCertificates(
diff --git a/chrome/browser/ash/crosapi/keystore_service_ash_unittest.cc b/chrome/browser/ash/crosapi/keystore_service_ash_unittest.cc
index 57da59f1..e6211b6c 100644
--- a/chrome/browser/ash/crosapi/keystore_service_ash_unittest.cc
+++ b/chrome/browser/ash/crosapi/keystore_service_ash_unittest.cc
@@ -43,6 +43,7 @@
 
 using ::ash::platform_keys::MockKeyPermissionsService;
 using ::ash::platform_keys::MockPlatformKeysService;
+using ::attestation::KEY_TYPE_ECC;
 using ::attestation::KEY_TYPE_RSA;
 using ::base::test::RunOnceCallback;
 using ::chromeos::platform_keys::HashAlgorithm;
@@ -755,6 +756,7 @@
   CallbackObserver<mojom::ChallengeAttestationOnlyKeystoreResultPtr> observer;
   keystore_service_.ChallengeAttestationOnlyKeystore(
       mojom::KeystoreType::kUser, /*challenge=*/GetDataBin(), /*migrate=*/false,
+      mojom::KeystoreSigningAlgorithmName::kRsassaPkcs115,
       observer.GetCallback());
 
   ASSERT_TRUE(observer.result.has_value() && observer.result.value());
@@ -784,6 +786,7 @@
   CallbackObserver<mojom::ChallengeAttestationOnlyKeystoreResultPtr> observer;
   keystore_service_.ChallengeAttestationOnlyKeystore(
       mojom::KeystoreType::kUser, /*challenge=*/GetDataBin(), /*migrate=*/true,
+      mojom::KeystoreSigningAlgorithmName::kRsassaPkcs115,
       observer.GetCallback());
 
   ASSERT_TRUE(observer.result.has_value() && observer.result.value());
@@ -813,7 +816,8 @@
   CallbackObserver<mojom::ChallengeAttestationOnlyKeystoreResultPtr> observer;
   keystore_service_.ChallengeAttestationOnlyKeystore(
       mojom::KeystoreType::kDevice, /*challenge=*/GetDataBin(),
-      /*migrate=*/false, observer.GetCallback());
+      /*migrate=*/false, mojom::KeystoreSigningAlgorithmName::kRsassaPkcs115,
+      observer.GetCallback());
 
   ASSERT_TRUE(observer.result.has_value() && observer.result.value());
   ASSERT_TRUE(observer.result.value()->is_challenge_response());
@@ -842,7 +846,37 @@
   CallbackObserver<mojom::ChallengeAttestationOnlyKeystoreResultPtr> observer;
   keystore_service_.ChallengeAttestationOnlyKeystore(
       mojom::KeystoreType::kDevice, /*challenge=*/GetDataBin(),
-      /*migrate=*/true, observer.GetCallback());
+      /*migrate=*/true, mojom::KeystoreSigningAlgorithmName::kRsassaPkcs115,
+      observer.GetCallback());
+
+  ASSERT_TRUE(observer.result.has_value() && observer.result.value());
+  ASSERT_TRUE(observer.result.value()->is_challenge_response());
+  EXPECT_EQ(observer.result.value()->get_challenge_response(), GetDataBin());
+}
+
+TEST_F(KeystoreServiceAshTest, ChallengeUserEcdsaKeyMigrateSuccess) {
+  // Incoming challenge and outgoing challenge response are imitated with the
+  // same data blob. It is not realistic, but good enough for this test.
+
+  ash::attestation::MockTpmChallengeKey* challenge_key_ptr =
+      InjectMockChallengeKey();
+
+  EXPECT_CALL(
+      *challenge_key_ptr,
+      BuildResponse(ash::attestation::AttestationKeyType::KEY_USER,
+                    /*profile=*/_, /*callback=*/_, /*challenge=*/GetDataStr(),
+                    /*register_key=*/true,
+                    /*key_crypto_type=*/KEY_TYPE_ECC,
+                    /*key_name=*/std::string(),
+                    /*signals=*/_))
+      .WillOnce(RunOnceCallback<2>(
+          ash::attestation::TpmChallengeKeyResult::MakeChallengeResponse(
+              GetDataStr())));
+
+  CallbackObserver<mojom::ChallengeAttestationOnlyKeystoreResultPtr> observer;
+  keystore_service_.ChallengeAttestationOnlyKeystore(
+      mojom::KeystoreType::kUser, /*challenge=*/GetDataBin(), /*migrate=*/true,
+      mojom::KeystoreSigningAlgorithmName::kEcdsa, observer.GetCallback());
 
   ASSERT_TRUE(observer.result.has_value() && observer.result.value());
   ASSERT_TRUE(observer.result.value()->is_challenge_response());
@@ -869,7 +903,8 @@
   CallbackObserver<mojom::ChallengeAttestationOnlyKeystoreResultPtr> observer;
   keystore_service_.ChallengeAttestationOnlyKeystore(
       mojom::KeystoreType::kUser, /*challenge=*/GetDataBin(),
-      /*migrate=*/false, observer.GetCallback());
+      /*migrate=*/false, mojom::KeystoreSigningAlgorithmName::kRsassaPkcs115,
+      observer.GetCallback());
 
   ASSERT_TRUE(observer.result.has_value() && observer.result.value());
   ASSERT_TRUE(observer.result.value()->is_error_message());
diff --git a/chrome/browser/ash/file_manager/file_manager_jstest_base.cc b/chrome/browser/ash/file_manager/file_manager_jstest_base.cc
index aca16595..8c14534 100644
--- a/chrome/browser/ash/file_manager/file_manager_jstest_base.cc
+++ b/chrome/browser/ash/file_manager/file_manager_jstest_base.cc
@@ -9,6 +9,7 @@
 #include "ash/webui/file_manager/url_constants.h"
 #include "base/lazy_instance.h"
 #include "base/path_service.h"
+#include "chrome/browser/ash/file_manager/file_manager_string_util.h"
 #include "chrome/browser/ash/file_manager/file_manager_test_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -21,6 +22,7 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/file_manager/grit/file_manager_gen_resources_map.h"
 #include "ui/file_manager/grit/file_manager_resources_map.h"
+
 namespace {
 
 // WebUIProvider to attach the URLDataSource for the test URL during tests.
@@ -54,6 +56,11 @@
                                             kFileManagerGenResources,
                                             kFileManagerGenResourcesSize);
 
+    dict_ = GetFileManagerStrings();
+    AddFileManagerFeatureStrings("en-US", Profile::FromWebUI(web_ui), &dict_);
+    files_swa_source->AddLocalizedStrings(dict_);
+    files_swa_source->UseStringsJs();
+
     content::WebUIDataSource::Add(profile, files_swa_source);
 
     return std::make_unique<content::WebUIController>(web_ui);
@@ -80,7 +87,14 @@
 
     // TODO(crbug.com/1098685): Trusted Type remaining WebUI.
     source->DisableTrustedTypesCSP();
+
+    DCHECK(!dict_.empty()) << "The translation should be fully loaded";
+    source->AddLocalizedStrings(dict_);
+    source->UseStringsJs();
   }
+
+ private:
+  base::Value::Dict dict_;
 };
 
 base::LazyInstance<TestWebUIProvider>::DestructorAtExit test_webui_provider_ =
diff --git a/chrome/browser/ash/login/users/avatar/user_image_manager_browsertest.cc b/chrome/browser/ash/login/users/avatar/user_image_manager_browsertest.cc
index fbe2125..a26d345 100644
--- a/chrome/browser/ash/login/users/avatar/user_image_manager_browsertest.cc
+++ b/chrome/browser/ash/login/users/avatar/user_image_manager_browsertest.cc
@@ -11,6 +11,7 @@
 #include <string>
 #include <vector>
 
+#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
@@ -21,6 +22,7 @@
 #include "base/memory/ref_counted_memory.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -305,11 +307,14 @@
       controllable_http_response_;
 
   FakeGaiaMixin fake_gaia_{&mixin_host_};
+
+  base::test::ScopedFeatureList feature_list_;
 };
 
 class UserImageManagerTest : public UserImageManagerTestBase {
  public:
   UserImageManagerTest() {
+    feature_list_.InitAndDisableFeature(ash::features::kAvatarsCloudMigration);
     login_manager_mixin_.AppendRegularUsers(1);
     test_account_id1_ = login_manager_mixin_.users()[0].account_id;
   }
@@ -535,6 +540,7 @@
  protected:
   UserImageManagerPolicyTest()
       : owner_key_util_(new ownership::MockOwnerKeyUtil()) {
+    feature_list_.InitAndDisableFeature(ash::features::kAvatarsCloudMigration);
     login_manager_.AppendManagedUsers(1);
     enterprise_account_id_ = login_manager_.users()[0].account_id;
     cryptohome_id_ = cryptohome::CreateAccountIdentifierFromAccountId(
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_browsertest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_browsertest.cc
index 1c9d0a1..1af11b30 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_browsertest.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_browsertest.cc
@@ -3,7 +3,8 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
-#include "chrome/browser/ash/policy/reporting/metrics_reporting/metric_browsertest_utils.h"
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.h"
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.h"
 #include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
 #include "chrome/browser/ash/settings/stub_cros_settings_provider.h"
 #include "chrome/browser/chromeos/reporting/metric_default_utils.h"
@@ -14,6 +15,7 @@
 #include "components/reporting/proto/synced/record.pb.h"
 #include "components/reporting/proto/synced/record_constants.pb.h"
 #include "content/public/test/browser_test.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash::reporting {
 
@@ -39,11 +41,119 @@
 using ::reporting::Record;
 using ::testing::Eq;
 
+// Is the given record about info metric? If yes, return the underlying
+// MetricData object.
+absl::optional<MetricData> IsRecordInfo(const Record& record) {
+  if (record.destination() != Destination::INFO_METRIC) {
+    return absl::nullopt;
+  }
+
+  MetricData record_data;
+  EXPECT_TRUE(record_data.ParseFromString(record.data()));
+  EXPECT_TRUE(record_data.has_info_data());
+  return record_data;
+}
+
+// Assert info in a record and returns the underlying MetricData object.
+MetricData AssertInfo(Priority priority, const Record& record) {
+  EXPECT_THAT(priority, Eq(Priority::SLOW_BATCH));
+  EXPECT_THAT(record.destination(), Eq(Destination::INFO_METRIC));
+  MetricData record_data;
+  EXPECT_TRUE(record_data.ParseFromString(record.data()));
+  EXPECT_TRUE(record_data.has_timestamp_ms());
+  EXPECT_TRUE(record_data.has_info_data());
+  return record_data;
+}
+
+}  // namespace
+
+// ---- CPU ----
+
+class CpuInfoSamplerBrowserTest : public policy::DevicePolicyCrosBrowserTest {
+ public:
+  CpuInfoSamplerBrowserTest(const CpuInfoSamplerBrowserTest&) = delete;
+  CpuInfoSamplerBrowserTest& operator=(const CpuInfoSamplerBrowserTest&) =
+      delete;
+
+ protected:
+  CpuInfoSamplerBrowserTest() = default;
+  ~CpuInfoSamplerBrowserTest() override = default;
+
+  void SetUpOnMainThread() override {
+    policy::DevicePolicyCrosBrowserTest::SetUpOnMainThread();
+    scoped_testing_cros_settings_.device_settings()->SetBoolean(
+        kReportDeviceCpuInfo, true);
+  }
+
+  // Is the given record about memory info metric?
+  static bool IsRecordCpuInfo(const Record& record) {
+    auto record_data = IsRecordInfo(record);
+    return record_data.has_value() &&
+           record_data.value().info_data().has_cpu_info();
+  }
+
+  // Gets next enqueued memory info record. This is useful in excluding
+  // other types of records from being examined.
+  static std::tuple<Priority, Record> GetNextEnqueuedCpuInfoRecord(
+      MissiveClientTestObserver* observer) {
+    Priority priority;
+    Record record;
+    do {
+      // If no record is enqueued, this line would time out when the loop
+      // is entered for the first time.
+      std::tie(priority, record) = observer->GetNextEnqueuedRecord();
+    } while (!IsRecordCpuInfo(record));
+
+    return std::make_tuple(priority, record);
+  }
+
+ private:
+  CrosHealthdInfoMetricsHelper cros_healthd_info_metrics_helper_;
+  ScopedTestingCrosSettings scoped_testing_cros_settings_;
+};
+
+IN_PROC_BROWSER_TEST_F(CpuInfoSamplerBrowserTest, KeylockerUnsupported) {
+  auto cpu_result = ::reporting::test::CreateCpuResult(nullptr);
+  ash::cros_healthd::FakeCrosHealthd::Get()
+      ->SetProbeTelemetryInfoResponseForTesting(cpu_result);
+  MissiveClientTestObserver observer(Destination::INFO_METRIC);
+  auto [priority, record] = GetNextEnqueuedCpuInfoRecord(&observer);
+  auto info_data = AssertInfo(priority, record).info_data();
+  ASSERT_TRUE(info_data.cpu_info().has_keylocker_info());
+  EXPECT_FALSE(info_data.cpu_info().keylocker_info().configured());
+  EXPECT_FALSE(info_data.cpu_info().keylocker_info().supported());
+}
+
+IN_PROC_BROWSER_TEST_F(CpuInfoSamplerBrowserTest, KeylockerConfigured) {
+  auto cpu_result = ::reporting::test::CreateCpuResult(
+      ::reporting::test::CreateKeylockerInfo(true));
+  ash::cros_healthd::FakeCrosHealthd::Get()
+      ->SetProbeTelemetryInfoResponseForTesting(cpu_result);
+  MissiveClientTestObserver observer(Destination::INFO_METRIC);
+  auto [priority, record] = GetNextEnqueuedCpuInfoRecord(&observer);
+  auto info_data = AssertInfo(priority, record).info_data();
+  ASSERT_TRUE(info_data.cpu_info().has_keylocker_info());
+  EXPECT_TRUE(info_data.cpu_info().keylocker_info().configured());
+  EXPECT_TRUE(info_data.cpu_info().keylocker_info().supported());
+}
+
+// ---- Memory ----
+
+// Memory constants.
+static constexpr int64_t kTmeMaxKeys = 2;
+static constexpr int64_t kTmeKeysLength = 4;
+
 class MemoryInfoSamplerBrowserTest
-    : public policy::DevicePolicyCrosBrowserTest {
+    : public policy::DevicePolicyCrosBrowserTest,
+      public testing::WithParamInterface<
+          ::reporting::test::MemoryInfoTestCase> {
+ public:
+  MemoryInfoSamplerBrowserTest(const MemoryInfoSamplerBrowserTest&) = delete;
+  MemoryInfoSamplerBrowserTest& operator=(const MemoryInfoSamplerBrowserTest&) =
+      delete;
+
  protected:
   MemoryInfoSamplerBrowserTest() = default;
-
   ~MemoryInfoSamplerBrowserTest() override = default;
 
   void SetUpOnMainThread() override {
@@ -52,18 +162,11 @@
         kReportDeviceMemoryInfo, true);
   }
 
+  // Is the given record about memory info metric?
   static bool IsRecordMemoryInfo(const Record& record) {
-    if (record.destination() != Destination::INFO_METRIC) {
-      return false;
-    }
-
-    MetricData record_data;
-    EXPECT_TRUE(record_data.ParseFromString(record.data()));
-    if (!record_data.has_info_data()) {
-      return false;
-    }
-
-    return record_data.info_data().has_memory_info();
+    auto record_data = IsRecordInfo(record);
+    return record_data.has_value() &&
+           record_data.value().info_data().has_memory_info();
   }
 
   // Gets next enqueued memory info record. This is useful in excluding
@@ -83,37 +186,8 @@
 
   static void AssertMemoryInfo(MissiveClientTestObserver* observer) {
     auto [priority, record] = GetNextEnqueuedMemoryInfoRecord(observer);
-    EXPECT_THAT(priority, Eq(Priority::SLOW_BATCH));
-    EXPECT_THAT(record.destination(), Eq(Destination::INFO_METRIC));
-    MetricData record_data;
-    ASSERT_TRUE(record_data.ParseFromString(record.data()));
-    EXPECT_TRUE(record_data.has_timestamp_ms());
-    EXPECT_FALSE(record_data.has_telemetry_data());
-    ASSERT_TRUE(record_data.has_info_data());
-
-    const auto& info_data = record_data.info_data();
-    ASSERT_TRUE(info_data.has_memory_info());
-    EXPECT_THAT(info_data.memory_info().tme_info().max_keys(), Eq(0));
-  }
-
-  cros_healthd::MemoryEncryptionInfoPtr CreateMemoryEncryptionInfo(
-      cros_healthd::EncryptionState encryption_state,
-      int64_t max_keys,
-      int64_t key_length,
-      cros_healthd::CryptoAlgorithm encryption_algorithm) {
-    return cros_healthd::MemoryEncryptionInfo::New(
-        encryption_state, max_keys, key_length, encryption_algorithm);
-  }
-
-  cros_healthd::TelemetryInfoPtr CreateMemoryResult(
-      cros_healthd::MemoryEncryptionInfoPtr memory_encryption_info) {
-    auto telemetry_info = cros_healthd::TelemetryInfo::New();
-    telemetry_info->memory_result =
-        cros_healthd::MemoryResult::NewMemoryInfo(cros_healthd::MemoryInfo::New(
-            /*total_memory=*/0, /*free_memory=*/0, /*available_memory=*/0,
-            /*page_faults_since_last_boot=*/0,
-            std::move(memory_encryption_info)));
-    return telemetry_info;
+    MetricData record_data = AssertInfo(priority, record);
+    ::reporting::test::AssertMemoryInfo(record_data, GetParam());
   }
 
  private:
@@ -121,10 +195,12 @@
   ScopedTestingCrosSettings scoped_testing_cros_settings_;
 };
 
-IN_PROC_BROWSER_TEST_F(MemoryInfoSamplerBrowserTest, ReportMemoryInfo) {
-  auto memory_result = CreateMemoryResult(
-      CreateMemoryEncryptionInfo(cros_healthd::EncryptionState::kUnknown, 0, 0,
-                                 cros_healthd::CryptoAlgorithm::kUnknown));
+IN_PROC_BROWSER_TEST_P(MemoryInfoSamplerBrowserTest, ReportMemoryInfo) {
+  const auto& test_case = GetParam();
+  auto memory_result = ::reporting::test::CreateMemoryResult(
+      ::reporting::test::CreateMemoryEncryptionInfo(
+          test_case.healthd_encryption_state, test_case.max_keys,
+          test_case.key_length, test_case.healthd_encryption_algorithm));
 
   ash::cros_healthd::FakeCrosHealthd::Get()
       ->SetProbeTelemetryInfoResponseForTesting(memory_result);
@@ -132,6 +208,21 @@
   AssertMemoryInfo(&observer);
 }
 
-}  // namespace
+INSTANTIATE_TEST_SUITE_P(
+    MemoryInfoSamplerBrowserTests,
+    MemoryInfoSamplerBrowserTest,
+    testing::ValuesIn<::reporting::test::MemoryInfoTestCase>({
+        {"UnknownEncryptionState", cros_healthd::EncryptionState::kUnknown,
+         ::reporting::MEMORY_ENCRYPTION_STATE_UNKNOWN,
+         cros_healthd::CryptoAlgorithm::kUnknown,
+         ::reporting::MEMORY_ENCRYPTION_ALGORITHM_UNKNOWN, 0, 0},
+        {"KeyValuesSet", cros_healthd::EncryptionState::kUnknown,
+         ::reporting::MEMORY_ENCRYPTION_STATE_UNKNOWN,
+         cros_healthd::CryptoAlgorithm::kUnknown,
+         ::reporting::MEMORY_ENCRYPTION_ALGORITHM_UNKNOWN, kTmeMaxKeys,
+         kTmeKeysLength},
+    }),
+    [](const testing::TestParamInfo<MemoryInfoSamplerBrowserTest::ParamType>&
+           info) { return info.param.test_name; });
 
 }  // namespace ash::reporting
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.cc
new file mode 100644
index 0000000..ad0f9a3
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.cc
@@ -0,0 +1,70 @@
+// 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/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace reporting::test {
+
+// ------- CPU -------
+
+cros_healthd::KeylockerInfoPtr CreateKeylockerInfo(bool configured) {
+  return cros_healthd::KeylockerInfo::New(configured);
+}
+
+cros_healthd::TelemetryInfoPtr CreateCpuResult(
+    cros_healthd::KeylockerInfoPtr keylocker_info) {
+  auto telemetry_info = cros_healthd::TelemetryInfo::New();
+  telemetry_info->cpu_result =
+      cros_healthd::CpuResult::NewCpuInfo(cros_healthd::CpuInfo::New(
+          /*num_total_threads=*/0,
+          /*architecture=*/cros_healthd::CpuArchitectureEnum::kX86_64,
+          /*physical_cpus=*/std::vector<cros_healthd::PhysicalCpuInfoPtr>(),
+          /*temperature_channels=*/
+          std::vector<cros_healthd::CpuTemperatureChannelPtr>(),
+          /*keylocker_info=*/std::move(keylocker_info)));
+
+  return telemetry_info;
+}
+
+// ------- memory --------
+
+cros_healthd::MemoryEncryptionInfoPtr CreateMemoryEncryptionInfo(
+    cros_healthd::EncryptionState encryption_state,
+    int64_t max_keys,
+    int64_t key_length,
+    cros_healthd::CryptoAlgorithm encryption_algorithm) {
+  return cros_healthd::MemoryEncryptionInfo::New(
+      encryption_state, max_keys, key_length, encryption_algorithm);
+}
+
+cros_healthd::TelemetryInfoPtr CreateMemoryResult(
+    cros_healthd::MemoryEncryptionInfoPtr memory_encryption_info) {
+  auto telemetry_info = cros_healthd::TelemetryInfo::New();
+  telemetry_info->memory_result =
+      cros_healthd::MemoryResult::NewMemoryInfo(cros_healthd::MemoryInfo::New(
+          /*total_memory=*/0, /*free_memory=*/0, /*available_memory=*/0,
+          /*page_faults_since_last_boot=*/0,
+          std::move(memory_encryption_info)));
+  return telemetry_info;
+}
+
+void AssertMemoryInfo(const MetricData& result,
+                      const MemoryInfoTestCase& test_case) {
+  EXPECT_FALSE(result.has_telemetry_data());
+  ASSERT_TRUE(result.has_info_data());
+  const auto& info_data = result.info_data();
+  ASSERT_TRUE(info_data.has_memory_info());
+  ASSERT_TRUE(info_data.memory_info().has_tme_info());
+
+  const auto& tme_info = info_data.memory_info().tme_info();
+  EXPECT_EQ(tme_info.encryption_state(), test_case.reporting_encryption_state);
+  EXPECT_EQ(tme_info.encryption_algorithm(),
+            test_case.reporting_encryption_algorithm);
+  EXPECT_EQ(tme_info.max_keys(), test_case.max_keys);
+  EXPECT_EQ(tme_info.key_length(), test_case.key_length);
+}
+
+}  // namespace reporting::test
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.h b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.h
new file mode 100644
index 0000000..83a165d
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.h
@@ -0,0 +1,50 @@
+// 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.
+
+// Some utilities that can be used for info metric unit tests and browser tests.
+
+#ifndef CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_CROS_HEALTHD_INFO_METRIC_SAMPLER_TEST_UTILS_H_
+#define CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_CROS_HEALTHD_INFO_METRIC_SAMPLER_TEST_UTILS_H_
+
+#include <string>
+
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.h"
+
+namespace reporting::test {
+
+namespace cros_healthd = ::ash::cros_healthd::mojom;
+
+// ------- CPU --------
+cros_healthd::KeylockerInfoPtr CreateKeylockerInfo(bool configured);
+cros_healthd::TelemetryInfoPtr CreateCpuResult(
+    cros_healthd::KeylockerInfoPtr keylocker_info);
+
+// ------- memory --------
+
+struct MemoryInfoTestCase {
+  std::string test_name;
+  cros_healthd::EncryptionState healthd_encryption_state;
+  reporting::MemoryEncryptionState reporting_encryption_state;
+  cros_healthd::CryptoAlgorithm healthd_encryption_algorithm;
+  reporting::MemoryEncryptionAlgorithm reporting_encryption_algorithm;
+  int64_t max_keys;
+  int64_t key_length;
+};
+
+// Create a memory encryption info for memory tests.
+cros_healthd::MemoryEncryptionInfoPtr CreateMemoryEncryptionInfo(
+    cros_healthd::EncryptionState encryption_state,
+    int64_t max_keys,
+    int64_t key_length,
+    cros_healthd::CryptoAlgorithm encryption_algorithm);
+
+// Create a memory test result.
+cros_healthd::TelemetryInfoPtr CreateMemoryResult(
+    cros_healthd::MemoryEncryptionInfoPtr memory_encryption_info);
+
+void AssertMemoryInfo(const MetricData& result,
+                      const MemoryInfoTestCase& test_case);
+}  // namespace reporting::test
+
+#endif  // CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_CROS_HEALTHD_INFO_METRIC_SAMPLER_TEST_UTILS_H_
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc
index 75e81df..602c1a13 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc
@@ -7,6 +7,7 @@
 
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.h"
 #include "chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.h"
 #include "components/reporting/util/test_support_callbacks.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -25,19 +26,9 @@
   std::vector<reporting::ThunderboltSecurityLevel> reporting_security_levels;
 };
 
-struct MemoryEncryptionTestCase {
-  std::string test_name;
-  cros_healthd::EncryptionState healthd_encryption_state;
-  reporting::MemoryEncryptionState reporting_encryption_state;
-  cros_healthd::CryptoAlgorithm healthd_encryption_algorithm;
-  reporting::MemoryEncryptionAlgorithm reporting_encryption_algorithm;
-  int64_t max_keys;
-  int64_t key_length;
-};
-
 // Memory constants.
-constexpr int64_t kTmeMaxKeys = 2;
-constexpr int64_t kTmeKeysLength = 4;
+static constexpr int64_t kTmeMaxKeys = 2;
+static constexpr int64_t kTmeKeysLength = 4;
 
 // Boot Performance constants.
 constexpr int64_t kBootUpSeconds = 5054;
@@ -47,25 +38,6 @@
 constexpr char kShutdownReason[] = "user-request";
 constexpr char kShutdownReasonNotApplicable[] = "N/A";
 
-cros_healthd::KeylockerInfoPtr CreateKeylockerInfo(bool configured) {
-  return cros_healthd::KeylockerInfo::New(configured);
-}
-
-cros_healthd::TelemetryInfoPtr CreateCpuResult(
-    cros_healthd::KeylockerInfoPtr keylocker_info) {
-  auto telemetry_info = cros_healthd::TelemetryInfo::New();
-  telemetry_info->cpu_result =
-      cros_healthd::CpuResult::NewCpuInfo(cros_healthd::CpuInfo::New(
-          /*num_total_threads=*/0,
-          /*architecture=*/cros_healthd::CpuArchitectureEnum::kX86_64,
-          /*physical_cpus=*/std::vector<cros_healthd::PhysicalCpuInfoPtr>(),
-          /*temperature_channels=*/
-          std::vector<cros_healthd::CpuTemperatureChannelPtr>(),
-          /*keylocker_info=*/std::move(keylocker_info)));
-
-  return telemetry_info;
-}
-
 cros_healthd::TelemetryInfoPtr CreateUsbBusResult(
     std::vector<cros_healthd::BusDevicePtr> usb_devices) {
   auto telemetry_info = cros_healthd::TelemetryInfo::New();
@@ -115,26 +87,6 @@
   return telemetry_info;
 }
 
-cros_healthd::MemoryEncryptionInfoPtr CreateMemoryEncryptionInfo(
-    cros_healthd::EncryptionState encryption_state,
-    int64_t max_keys,
-    int64_t key_length,
-    cros_healthd::CryptoAlgorithm encryption_algorithm) {
-  return cros_healthd::MemoryEncryptionInfo::New(
-      encryption_state, max_keys, key_length, encryption_algorithm);
-}
-
-cros_healthd::TelemetryInfoPtr CreateMemoryResult(
-    cros_healthd::MemoryEncryptionInfoPtr memory_encryption_info) {
-  auto telemetry_info = cros_healthd::TelemetryInfo::New();
-  telemetry_info->memory_result =
-      cros_healthd::MemoryResult::NewMemoryInfo(cros_healthd::MemoryInfo::New(
-          /*total_memory=*/0, /*free_memory=*/0, /*available_memory=*/0,
-          /*page_faults_since_last_boot=*/0,
-          std::move(memory_encryption_info)));
-  return telemetry_info;
-}
-
 cros_healthd::TelemetryInfoPtr CreateBootPerformanceResult(
     int64_t boot_up_seconds,
     int64_t boot_up_timestamp_seconds,
@@ -260,9 +212,9 @@
     : public CrosHealthdMetricSamplerTest,
       public testing::WithParamInterface<TbtTestCase> {};
 
-class CrosHealthdMetricSamplerMemoryEncryptionTest
+class CrosHealthdMetricSamplerMemoryInfoTest
     : public CrosHealthdMetricSamplerTest,
-      public testing::WithParamInterface<MemoryEncryptionTestCase> {};
+      public testing::WithParamInterface<MemoryInfoTestCase> {};
 
 TEST_F(CrosHealthdMetricSamplerTest, TestUsbTelemetryMultipleEntries) {
   // Max value for 8-bit unsigned integer
@@ -405,9 +357,8 @@
   EXPECT_EQ(usb_telemetry.firmware_version(), kFirmwareVersion);
 }
 
-TEST_P(CrosHealthdMetricSamplerMemoryEncryptionTest,
-       TestMemoryEncryptionReporting) {
-  const MemoryEncryptionTestCase& test_case = GetParam();
+TEST_P(CrosHealthdMetricSamplerMemoryInfoTest, TestMemoryInfoeporting) {
+  const auto& test_case = GetParam();
   const absl::optional<MetricData> optional_result = CollectData(
       CreateMemoryResult(CreateMemoryEncryptionInfo(
           test_case.healthd_encryption_state, test_case.max_keys,
@@ -417,17 +368,7 @@
 
   ASSERT_TRUE(optional_result.has_value());
   const MetricData& result = optional_result.value();
-
-  ASSERT_TRUE(result.has_info_data());
-  ASSERT_TRUE(result.info_data().has_memory_info());
-  ASSERT_TRUE(result.info_data().memory_info().has_tme_info());
-
-  const auto& tme_info = result.info_data().memory_info().tme_info();
-  EXPECT_EQ(tme_info.encryption_state(), test_case.reporting_encryption_state);
-  EXPECT_EQ(tme_info.encryption_algorithm(),
-            test_case.reporting_encryption_algorithm);
-  EXPECT_EQ(tme_info.max_keys(), test_case.max_keys);
-  EXPECT_EQ(tme_info.key_length(), test_case.key_length);
+  AssertMemoryInfo(result, test_case);
 }
 
 TEST_P(CrosHealthdMetricSamplerTbtTest, TestTbtSecurityLevels) {
@@ -1096,9 +1037,9 @@
            info) { return info.param.test_name; });
 
 INSTANTIATE_TEST_SUITE_P(
-    CrosHealthdMetricSamplerMemoryEncryptionTests,
-    CrosHealthdMetricSamplerMemoryEncryptionTest,
-    testing::ValuesIn<MemoryEncryptionTestCase>({
+    CrosHealthdMetricSamplerMemoryInfoTests,
+    CrosHealthdMetricSamplerMemoryInfoTest,
+    testing::ValuesIn<MemoryInfoTestCase>({
         {"UnknownEncryptionState", cros_healthd::EncryptionState::kUnknown,
          ::reporting::MEMORY_ENCRYPTION_STATE_UNKNOWN,
          cros_healthd::CryptoAlgorithm::kUnknown,
@@ -1137,7 +1078,7 @@
          kTmeKeysLength},
     }),
     [](const testing::TestParamInfo<
-        CrosHealthdMetricSamplerMemoryEncryptionTest::ParamType>& info) {
+        CrosHealthdMetricSamplerMemoryInfoTest::ParamType>& info) {
       return info.param.test_name;
     });
 
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc
index c5996b00..4db50912 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc
@@ -18,9 +18,9 @@
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "components/reporting/metrics/configured_sampler.h"
 #include "components/reporting/metrics/event_driven_telemetry_sampler_pool.h"
-#include "components/reporting/metrics/fake_metric_report_queue.h"
-#include "components/reporting/metrics/fake_reporting_settings.h"
-#include "components/reporting/metrics/fake_sampler.h"
+#include "components/reporting/metrics/fakes/fake_metric_report_queue.h"
+#include "components/reporting/metrics/fakes/fake_reporting_settings.h"
+#include "components/reporting/metrics/fakes/fake_sampler.h"
 #include "components/reporting/metrics/metric_data_collector.h"
 #include "components/reporting/metrics/metric_event_observer_manager.h"
 #include "components/reporting/metrics/metric_report_queue.h"
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler_unittest.cc
index 358deb4..63a4b9e6 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler_unittest.cc
@@ -23,7 +23,7 @@
 #include "chromeos/ash/components/network/tether_constants.h"
 #include "chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.h"
 #include "chromeos/login/login_state/login_state.h"
-#include "components/reporting/metrics/fake_sampler.h"
+#include "components/reporting/metrics/fakes/fake_sampler.h"
 #include "components/reporting/proto/synced/metric_data.pb.h"
 #include "components/reporting/util/test_support_callbacks.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/background/background_mode_manager.cc b/chrome/browser/background/background_mode_manager.cc
index cbf9b5d..9894507 100644
--- a/chrome/browser/background/background_mode_manager.cc
+++ b/chrome/browser/background/background_mode_manager.cc
@@ -24,7 +24,6 @@
 #include "base/metrics/user_metrics.h"
 #include "base/one_shot_event.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -372,11 +371,6 @@
 
 // static
 void BackgroundModeManager::RegisterPrefs(PrefRegistrySimple* registry) {
-#if BUILDFLAG(IS_MAC)
-  registry->RegisterBooleanPref(prefs::kUserRemovedLoginItem, false);
-  registry->RegisterBooleanPref(prefs::kChromeCreatedLoginItem, false);
-  registry->RegisterBooleanPref(prefs::kMigratedLoginItemPref, false);
-#endif
   registry->RegisterBooleanPref(prefs::kBackgroundModeEnabled, true);
 }
 
@@ -874,6 +868,8 @@
   EnableLaunchOnStartup(*launch_on_startup_enabled_);
 }
 
+namespace {
+
 // Gets the image for the status tray icon, at the correct size for the current
 // platform and display settings.
 gfx::ImageSkia GetStatusTrayIcon() {
@@ -906,6 +902,8 @@
 #endif
 }
 
+}  // namespace
+
 void BackgroundModeManager::CreateStatusTrayIcon() {
   // Only need status icons on windows/linux. ChromeOS doesn't allow exiting
   // Chrome and Mac can use the dock icon instead.
diff --git a/chrome/browser/background/background_mode_manager_mac.cc b/chrome/browser/background/background_mode_manager_mac.cc
new file mode 100644
index 0000000..71669968
--- /dev/null
+++ b/chrome/browser/background/background_mode_manager_mac.cc
@@ -0,0 +1,25 @@
+// Copyright 2011 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/background/background_mode_manager.h"
+
+#include "base/task/sequenced_task_runner.h"
+#include "base/task/thread_pool.h"
+
+void BackgroundModeManager::EnableLaunchOnStartup(bool should_launch) {
+  // The Mac does not support forcing a launch on startup.
+}
+
+void BackgroundModeManager::DisplayClientInstalledNotification(
+    const std::u16string& name) {
+  // TODO(http://crbug.com/74970): Display a platform-appropriate notification
+  // here.
+}
+
+scoped_refptr<base::SequencedTaskRunner>
+BackgroundModeManager::CreateTaskRunner() {
+  return base::ThreadPool::CreateSequencedTaskRunner(
+      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+       base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
+}
diff --git a/chrome/browser/background/background_mode_manager_mac.mm b/chrome/browser/background/background_mode_manager_mac.mm
deleted file mode 100644
index 53aea51e..0000000
--- a/chrome/browser/background/background_mode_manager_mac.mm
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright 2011 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/mac/mac_util.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/task/thread_pool.h"
-#include "base/threading/scoped_blocking_call.h"
-#include "chrome/browser/background/background_mode_manager.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
-#include "components/prefs/pref_service.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-
-using content::BrowserThread;
-
-namespace {
-void SetUserRemovedLoginItemPrefOnUIThread() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  PrefService* service = g_browser_process->local_state();
-  service->SetBoolean(prefs::kUserRemovedLoginItem, true);
-}
-
-void SetCreatedLoginItemPrefOnUIThread() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  PrefService* service = g_browser_process->local_state();
-  service->SetBoolean(prefs::kChromeCreatedLoginItem, true);
-}
-
-void DisableLaunchOnStartupOnWorkerThread() {
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::MAY_BLOCK);
-  // If the LoginItem is not hidden, it means it's user created, so don't
-  // delete it.
-  bool is_hidden = false;
-  if (base::mac::CheckLoginItemStatus(&is_hidden) && is_hidden)
-    base::mac::RemoveFromLoginItems();
-}
-
-void CheckForUserRemovedLoginItemOnWorkerThread() {
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::MAY_BLOCK);
-  if (!base::mac::CheckLoginItemStatus(NULL)) {
-    // There's no LoginItem, so set the kUserRemovedLoginItem pref.
-    content::GetUIThreadTaskRunner({})->PostTask(
-        FROM_HERE, base::BindOnce(SetUserRemovedLoginItemPrefOnUIThread));
-  }
-}
-
-void EnableLaunchOnStartupOnWorkerThread(bool need_migration) {
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::MAY_BLOCK);
-  if (need_migration) {
-    // This is the first time running Chrome since the kChromeCreatedLoginItem
-    // pref was added. Initialize the status of this pref based on whether
-    // there is already a hidden login item.
-    bool is_hidden = false;
-    if (base::mac::CheckLoginItemStatus(&is_hidden)) {
-      if (is_hidden) {
-      // We already have a hidden login item, so set the kChromeCreatedLoginItem
-      // flag.
-      content::GetUIThreadTaskRunner({})->PostTask(
-          FROM_HERE, base::BindOnce(SetCreatedLoginItemPrefOnUIThread));
-      }
-      // LoginItem already exists - just exit.
-      return;
-    }
-  }
-
-  // Check if Chrome is already a Login Item - if not, create one.
-  if (!base::mac::CheckLoginItemStatus(NULL)) {
-    // Call back to the UI thread to set our preference so we know that Chrome
-    // created the login item (which means we are allowed to delete it later).
-    // There's a race condition here if the user disables launch on startup
-    // before our callback is run, but the user can manually disable
-    // "Open At Login" via the dock if this happens.
-    base::mac::AddToLoginItems(true);  // Hide on startup.
-    content::GetUIThreadTaskRunner({})->PostTask(
-        FROM_HERE, base::BindOnce(SetCreatedLoginItemPrefOnUIThread));
-  }
-}
-
-}  // namespace
-
-void BackgroundModeManager::EnableLaunchOnStartup(bool should_launch) {
-  // LoginItems are associated with an executable, not with a specific
-  // user-data-dir, so only mess with the LoginItem when running with the
-  // default user-data-dir. So if a user is running multiple instances of
-  // Chrome with different user-data-dirs, they won't conflict in their
-  // use of LoginItems.
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kUserDataDir))
-    return;
-
-  // There are a few cases we need to handle:
-  //
-  // 1) Chrome is transitioning to "launch on startup" state, and there's no
-  // login item currently. We create a new item if the kUserRemovedLoginItem
-  // and kChromeCreatedLoginItem flags are already false, and set the
-  // kChromeCreatedLoginItem flag to true. If kChromeCreatedLoginItem is
-  // already set (meaning that we created a login item that has since been
-  // deleted) then we will set the kUserRemovedLoginItem so we do not create
-  // login items in the future.
-  //
-  // 2) Chrome is transitioning to the "do not launch on startup" state. If
-  // the kChromeCreatedLoginItem flag is false, we do nothing. Otherwise, we
-  // will delete the login item if it's present, and not we will set
-  // kUserRemovedLoginItem to true to prevent future login items from being
-  // created.
-  if (should_launch) {
-    PrefService* service = g_browser_process->local_state();
-    // If the user removed the login item, don't ever create another one.
-    if (service->GetBoolean(prefs::kUserRemovedLoginItem))
-      return;
-
-    if (service->GetBoolean(prefs::kChromeCreatedLoginItem)) {
-      DCHECK(service->GetBoolean(prefs::kMigratedLoginItemPref));
-      // If we previously created a login item, we don't need to create
-      // a new one - just check to see if the user removed it so we don't
-      // ever create another one.
-      task_runner_->PostTask(
-          FROM_HERE,
-          base::BindOnce(CheckForUserRemovedLoginItemOnWorkerThread));
-    } else {
-      bool need_migration = !service->GetBoolean(
-          prefs::kMigratedLoginItemPref);
-      service->SetBoolean(prefs::kMigratedLoginItemPref, true);
-      task_runner_->PostTask(
-          FROM_HERE,
-          base::BindOnce(EnableLaunchOnStartupOnWorkerThread, need_migration));
-    }
-  } else {
-    PrefService* service = g_browser_process->local_state();
-    // If Chrome didn't create any login items, just exit.
-    if (!service->GetBoolean(prefs::kChromeCreatedLoginItem))
-      return;
-
-    // Clear the pref now that we're removing the login item.
-    service->ClearPref(prefs::kChromeCreatedLoginItem);
-
-    // If the user removed our login item, note this so we don't ever create
-    // another one.
-    task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(CheckForUserRemovedLoginItemOnWorkerThread));
-
-    // Call to the File thread to remove the login item since it requires
-    // accessing the disk.
-    task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(DisableLaunchOnStartupOnWorkerThread));
-  }
-}
-
-void BackgroundModeManager::DisplayClientInstalledNotification(
-    const std::u16string& name) {
-  // TODO(atwilson): Display a platform-appropriate notification here.
-  // http://crbug.com/74970
-}
-
-scoped_refptr<base::SequencedTaskRunner>
-BackgroundModeManager::CreateTaskRunner() {
-  return base::ThreadPool::CreateSequencedTaskRunner(
-      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-       base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
-}
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 693db42..7b37750b 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -356,7 +356,7 @@
 void HandleTestParameters(const base::CommandLine& command_line) {
   // This parameter causes a null pointer crash (crash reporter trigger).
   if (command_line.HasSwitch(switches::kBrowserCrashTest)) {
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   }
 }
 
diff --git a/chrome/browser/chrome_browser_main_extra_parts_ozone.cc b/chrome/browser/chrome_browser_main_extra_parts_ozone.cc
index b125b46..b46245a5 100644
--- a/chrome/browser/chrome_browser_main_extra_parts_ozone.cc
+++ b/chrome/browser/chrome_browser_main_extra_parts_ozone.cc
@@ -8,6 +8,8 @@
 #include "base/callback.h"
 #include "base/logging.h"
 #include "chrome/browser/lifetime/application_lifetime_desktop.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 #include "ui/ozone/public/ozone_platform.h"
 
 ChromeBrowserMainExtraPartsOzone::ChromeBrowserMainExtraPartsOzone() = default;
@@ -24,7 +26,8 @@
     LOG(FATAL) << "Browser failed to shutdown.";
   });
   ui::OzonePlatform::GetInstance()->PostCreateMainMessageLoop(
-      std::move(shutdown_cb));
+      std::move(shutdown_cb),
+      content::GetUIThreadTaskRunner({content::BrowserTaskType::kUserInput}));
 }
 
 void ChromeBrowserMainExtraPartsOzone::PostMainMessageLoopRun() {
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 156cc8b3..0d6f2dc 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -27,6 +27,7 @@
 #include "base/path_service.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -2365,17 +2366,25 @@
 
 bool ChromeContentBrowserClient::ShouldUrlUseApplicationIsolationLevel(
     content::BrowserContext* browser_context,
-    const GURL& url) {
+    const GURL& url,
+    bool origin_matches_flag) {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-  if (base::FeatureList::IsEnabled(features::kIsolatedWebApps)) {
-    // TODO(crbug.com/1363756): Remove the GetStorageIsolationKey call.
-    Profile* profile = Profile::FromBrowserContext(browser_context);
-    return url.SchemeIs(chrome::kIsolatedAppScheme) ||
-           !!web_app::GetStorageIsolationKey(profile->GetPrefs(),
-                                             url::Origin::Create(url));
+  if (!base::FeatureList::IsEnabled(features::kIsolatedWebApps)) {
+    return false;
   }
-#endif
+
+  if (url.SchemeIs(chrome::kIsolatedAppScheme)) {
+    return true;
+  }
+
+  // TODO(crbug.com/1363756): Remove the GetStorageIsolationKey call.
+  return origin_matches_flag &&
+         !!web_app::GetStorageIsolationKey(
+             Profile::FromBrowserContext(browser_context)->GetPrefs(),
+             url::Origin::Create(url));
+#else
   return false;
+#endif
 }
 
 bool ChromeContentBrowserClient::IsIsolatedContextAllowedForUrl(
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 633b2a20..13d97332 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -16,6 +16,7 @@
 #include "base/containers/flat_map.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/strings/string_piece_forward.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "base/values.h"
@@ -235,7 +236,8 @@
       override;
   bool ShouldUrlUseApplicationIsolationLevel(
       content::BrowserContext* browser_context,
-      const GURL& url) override;
+      const GURL& url,
+      bool origin_matches_flag) override;
   bool IsIsolatedContextAllowedForUrl(content::BrowserContext* browser_context,
                                       const GURL& lock_url) override;
   bool IsIsolatedWebAppsDeveloperModeAllowed(
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc
index b544e84..1f30dd4 100644
--- a/chrome/browser/chrome_content_browser_client_unittest.cc
+++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/field_trial.h"
 #include "base/run_loop.h"
+#include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/gtest_util.h"
 #include "base/test/scoped_feature_list.h"
@@ -95,9 +96,11 @@
 #include "third_party/blink/public/common/features.h"
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
-using content::BrowsingDataFilterBuilder;
-using testing::_;
-using testing::NotNull;
+using ::content::BrowsingDataFilterBuilder;
+using ::testing::_;
+using ::testing::IsFalse;
+using ::testing::IsTrue;
+using ::testing::NotNull;
 
 class ChromeContentBrowserClientTest : public testing::Test {
  public:
@@ -818,7 +821,8 @@
   EXPECT_EQ(CreateDefaultStoragePartitionConfig(), config);
   EXPECT_FALSE(
       test_content_browser_client.ShouldUrlUseApplicationIsolationLevel(
-          &profile_, GURL(kHttpsScope)));
+          &profile_, GURL(kHttpsScope),
+          /*origin_matches_flag=*/false));
 }
 
 TEST_F(ChromeContentBrowserClientStoragePartitionTest,
@@ -833,7 +837,47 @@
   EXPECT_EQ(CreateDefaultStoragePartitionConfig(), config);
   EXPECT_FALSE(
       test_content_browser_client.ShouldUrlUseApplicationIsolationLevel(
-          &profile_, GURL(kHttpsScope)));
+          &profile_, GURL(kHttpsScope),
+          /*origin_matches_flag=*/false));
+}
+
+TEST_F(ChromeContentBrowserClientStoragePartitionTest,
+       EnableIsolatedLevelForIsolatedAppSchemeWhenIsolatedAppFeatureIsEnabled) {
+  TestChromeContentBrowserClient test_content_browser_client;
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(features::kIsolatedWebApps);
+
+  EXPECT_THAT(
+      test_content_browser_client.ShouldUrlUseApplicationIsolationLevel(
+          &profile_, GURL(kIsolatedAppScope), /*origin_matches_flag=*/false),
+      IsTrue());
+}
+
+TEST_F(
+    ChromeContentBrowserClientStoragePartitionTest,
+    DoNotEnableIsolatedLevelForIsolatedAppSchemeWhenIsolatedAppFeatureIsDisabled) {
+  TestChromeContentBrowserClient test_content_browser_client;
+
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(features::kIsolatedWebApps);
+
+  EXPECT_THAT(test_content_browser_client.ShouldUrlUseApplicationIsolationLevel(
+                  &profile_, GURL(kIsolatedAppScope),
+                  /*origin_matches_flag=*/false),
+              IsFalse());
+}
+
+TEST_F(ChromeContentBrowserClientStoragePartitionTest,
+       DoNotEnableIsolatedLevelForNonIsolatedApp) {
+  TestChromeContentBrowserClient test_content_browser_client;
+
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(features::kIsolatedWebApps);
+
+  EXPECT_THAT(test_content_browser_client.ShouldUrlUseApplicationIsolationLevel(
+                  &profile_, GURL(kHttpsScope),
+                  /*origin_matches_flag=*/false),
+              IsFalse());
 }
 
 TEST_F(ChromeContentBrowserClientStoragePartitionTest,
@@ -856,7 +900,8 @@
       /*in_memory=*/false);
   EXPECT_EQ(expected_config, config);
   EXPECT_TRUE(test_content_browser_client.ShouldUrlUseApplicationIsolationLevel(
-      &profile_, GURL(kHttpsScope)));
+      &profile_, GURL(kHttpsScope),
+      /*origin_matches_flag=*/true));
 }
 
 TEST_F(ChromeContentBrowserClientStoragePartitionTest,
@@ -869,7 +914,8 @@
   EXPECT_EQ(CreateDefaultStoragePartitionConfig(), config);
   EXPECT_FALSE(
       test_content_browser_client.ShouldUrlUseApplicationIsolationLevel(
-          &profile_, GURL(kIsolatedAppScope)));
+          &profile_, GURL(kIsolatedAppScope),
+          /*origin_matches_flag=*/false));
 }
 
 TEST_F(ChromeContentBrowserClientStoragePartitionTest,
@@ -892,7 +938,8 @@
       /*in_memory=*/false);
   EXPECT_EQ(expected_config, config);
   EXPECT_TRUE(test_content_browser_client.ShouldUrlUseApplicationIsolationLevel(
-      &profile_, GURL(kIsolatedAppScope)));
+      &profile_, GURL(kIsolatedAppScope),
+      /*origin_matches_flag=*/false));
 }
 
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 267d65c..ade6a3ef 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1732,6 +1732,8 @@
     "../ash/policy/reporting/arc_app_install_policy_data_helper_unittest.cc",
     "../ash/policy/reporting/arc_app_install_policy_data_unittest.cc",
     "../ash/policy/reporting/metrics_reporting/audio/audio_events_observer_unittest.cc",
+    "../ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.cc",
+    "../ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.h",
     "../ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc",
     "../ash/policy/reporting/metrics_reporting/cros_reporting_settings_unittest.cc",
     "../ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc",
@@ -2154,7 +2156,7 @@
     "//components/pref_registry",
     "//components/prefs:test_support",
     "//components/renderer_context_menu",
-    "//components/reporting/metrics:test_support",
+    "//components/reporting/metrics/fakes:test_support",
     "//components/reporting/proto:metric_data_proto",
     "//components/reporting/storage:missive_storage_module",
     "//components/resources",
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index bb0a104..8bfece9 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -616,6 +616,8 @@
       return chromeos::WindowStateType::kPrimarySnapped;
     case api::autotest_private::WMEventType::WM_EVENT_TYPE_WMEVENTSNAPRIGHT:
       return chromeos::WindowStateType::kSecondarySnapped;
+    case api::autotest_private::WMEventType::WM_EVENT_TYPE_WMEVENTFLOAT:
+      return chromeos::WindowStateType::kFloated;
     default:
       NOTREACHED();
       return chromeos::WindowStateType::kNormal;
@@ -636,6 +638,8 @@
       return ash::WMEventType::WM_EVENT_SNAP_PRIMARY;
     case api::autotest_private::WMEventType::WM_EVENT_TYPE_WMEVENTSNAPRIGHT:
       return ash::WMEventType::WM_EVENT_SNAP_SECONDARY;
+    case api::autotest_private::WMEventType::WM_EVENT_TYPE_WMEVENTFLOAT:
+      return ash::WMEventType::WM_EVENT_FLOAT;
     default:
       NOTREACHED();
       return ash::WMEventType::WM_EVENT_NORMAL;
@@ -666,6 +670,8 @@
           WINDOW_STATE_TYPE_RIGHTSNAPPED;
     case chromeos::WindowStateType::kPip:
       return api::autotest_private::WindowStateType::WINDOW_STATE_TYPE_PIP;
+    case chromeos::WindowStateType::kFloated:
+      return api::autotest_private::WindowStateType::WINDOW_STATE_TYPE_FLOATED;
     default:
       NOTREACHED();
       return api::autotest_private::WindowStateType::WINDOW_STATE_TYPE_NONE;
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
index 93c98f4..5a74ea6d 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -31,7 +31,7 @@
 #if DCHECK_IS_ON()
   return "whitelist for device-local accounts";
 #else
-  IMMEDIATE_CRASH();
+  base::ImmediateCrash();
 #endif
 }
 
diff --git a/chrome/browser/chromeos/extensions/signin_screen_policy_provider.cc b/chrome/browser/chromeos/extensions/signin_screen_policy_provider.cc
index ef378ee..66cf1526 100644
--- a/chrome/browser/chromeos/extensions/signin_screen_policy_provider.cc
+++ b/chrome/browser/chromeos/extensions/signin_screen_policy_provider.cc
@@ -36,7 +36,7 @@
 #if DCHECK_IS_ON()
   return "Guard for sign-in screen";
 #else
-  IMMEDIATE_CRASH();
+  base::ImmediateCrash();
 #endif
 }
 
diff --git a/chrome/browser/chromeos/reporting/metric_reporting_manager_lacros_unittest.cc b/chrome/browser/chromeos/reporting/metric_reporting_manager_lacros_unittest.cc
index 4dcddcfd..e0267c0 100644
--- a/chrome/browser/chromeos/reporting/metric_reporting_manager_lacros_unittest.cc
+++ b/chrome/browser/chromeos/reporting/metric_reporting_manager_lacros_unittest.cc
@@ -16,7 +16,7 @@
 #include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/crosapi/mojom/device_settings_service.mojom.h"
 #include "components/policy/policy_constants.h"
-#include "components/reporting/metrics/fake_metric_report_queue.h"
+#include "components/reporting/metrics/fakes/fake_metric_report_queue.h"
 #include "components/reporting/metrics/metric_data_collector.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/devtools/devtools_browser_context_manager.cc b/chrome/browser/devtools/devtools_browser_context_manager.cc
index 338bd94a..b3a8751 100644
--- a/chrome/browser/devtools/devtools_browser_context_manager.cc
+++ b/chrome/browser/devtools/devtools_browser_context_manager.cc
@@ -95,7 +95,7 @@
   // If no browsers are opened - dispose right away.
   if (!has_opened_browser) {
     StopObservingProfileIfAny(profile);
-    ProfileDestroyer::DestroyProfileWhenAppropriateWithTimeout(
+    ProfileDestroyer::DestroyOTRProfileWhenAppropriateWithTimeout(
         profile, base::Seconds(kDestroyProfileTimeoutSeconds));
     std::move(callback).Run(true, "");
     return;
@@ -141,7 +141,7 @@
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(
-          &ProfileDestroyer::DestroyProfileWhenAppropriateWithTimeout,
+          &ProfileDestroyer::DestroyOTRProfileWhenAppropriateWithTimeout,
           base::Unretained(browser->profile()),
           base::Seconds(kDestroyProfileTimeoutSeconds)));
 
diff --git a/chrome/browser/extensions/api/debugger/debugger_apitest.cc b/chrome/browser/extensions/api/debugger/debugger_apitest.cc
index 9e22e81..dee9449 100644
--- a/chrome/browser/extensions/api/debugger/debugger_apitest.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_apitest.cc
@@ -496,7 +496,7 @@
   }
 
   void TearDownOnMainThread() override {
-    ProfileDestroyer::DestroyProfileWhenAppropriate(otr_profile_);
+    ProfileDestroyer::DestroyOTRProfileWhenAppropriate(otr_profile_);
     DebuggerApiTest::TearDownOnMainThread();
   }
 
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index d08c2caf..9c73cc7 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -2212,13 +2212,11 @@
   ash::ProfileHelper::SetAlwaysReturnPrimaryUserForTesting(true);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   ProfileManager* profile_manager = g_browser_process->profile_manager();
-  Profile* temp_profile =
-      Profile::CreateProfile(
-          profile_manager->user_data_dir().AppendASCII("profile"), nullptr,
-          Profile::CreateMode::CREATE_MODE_SYNCHRONOUS)
-          .release();
+  std::unique_ptr<Profile> temp_profile = Profile::CreateProfile(
+      profile_manager->user_data_dir().AppendASCII("profile"), nullptr,
+      Profile::CreateMode::CREATE_MODE_SYNCHRONOUS);
   // Create a WebRequestAPI instance that we can control the lifetime of.
-  auto api = std::make_unique<WebRequestAPI>(temp_profile);
+  auto api = std::make_unique<WebRequestAPI>(temp_profile.get());
   // Make sure we are proxying for |temp_profile|.
   api->ForceProxyForTesting();
   temp_profile->GetDefaultStoragePartition()->FlushNetworkInterfaceForTesting();
@@ -2226,7 +2224,7 @@
   mojo::Remote<network::mojom::URLLoaderFactory> factory;
   auto pending_receiver = factory.BindNewPipeAndPassReceiver();
   auto temp_web_contents =
-      WebContents::Create(WebContents::CreateParams(temp_profile));
+      WebContents::Create(WebContents::CreateParams(temp_profile.get()));
   content::RenderFrameHost* frame = temp_web_contents->GetPrimaryMainFrame();
   EXPECT_TRUE(api->MaybeProxyURLLoaderFactory(
       frame->GetProcess()->GetBrowserContext(), frame,
@@ -2265,7 +2263,8 @@
   // the ThreadPool here to avoid the posts coming from it.
   content::RunAllTasksUntilIdle();
 
-  ProfileDestroyer::DestroyProfileWhenAppropriate(temp_profile);
+  ProfileDestroyer::DestroyOriginalProfileWhenAppropriate(
+      std::move(temp_profile));
   client.Unbind();
   api.reset();
 }
diff --git a/chrome/browser/extensions/install_signer.cc b/chrome/browser/extensions/install_signer.cc
index 1223843..8b16712 100644
--- a/chrome/browser/extensions/install_signer.cc
+++ b/chrome/browser/extensions/install_signer.cc
@@ -11,14 +11,11 @@
 #include <utility>
 
 #include "base/base64.h"
-#include "base/bind.h"
 #include "base/command_line.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
-#include "base/lazy_instance.h"
-#include "base/metrics/histogram_macros.h"
+#include "base/json/values_util.h"
 #include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
@@ -68,15 +65,15 @@
 const char kBackendUrl[] =
     "https://www.googleapis.com/chromewebstore/v1.1/items/verify";
 
-const char kPublicKeyPEM[] =                                            \
-    "-----BEGIN PUBLIC KEY-----"                                        \
-    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj/u/XDdjlDyw7gHEtaaa"  \
-    "sZ9GdG8WOKAyJzXd8HFrDtz2Jcuy7er7MtWvHgNDA0bwpznbI5YdZeV4UfCEsA4S"  \
-    "rA5b3MnWTHwA1bgbiDM+L9rrqvcadcKuOlTeN48Q0ijmhHlNFbTzvT9W0zw/GKv8"  \
-    "LgXAHggxtmHQ/Z9PP2QNF5O8rUHHSL4AJ6hNcEKSBVSmbbjeVm4gSXDuED5r0nwx"  \
-    "vRtupDxGYp8IZpP5KlExqNu1nbkPc+igCTIB6XsqijagzxewUHCdovmkb2JNtskx"  \
-    "/PMIEv+TvWIx2BzqGp71gSh/dV7SJ3rClvWd2xj8dtxG8FfAWDTIIi0qZXWn2Qhi"  \
-    "zQIDAQAB"                                                          \
+const char kPublicKeyPEM[] =
+    "-----BEGIN PUBLIC KEY-----"
+    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj/u/XDdjlDyw7gHEtaaa"
+    "sZ9GdG8WOKAyJzXd8HFrDtz2Jcuy7er7MtWvHgNDA0bwpznbI5YdZeV4UfCEsA4S"
+    "rA5b3MnWTHwA1bgbiDM+L9rrqvcadcKuOlTeN48Q0ijmhHlNFbTzvT9W0zw/GKv8"
+    "LgXAHggxtmHQ/Z9PP2QNF5O8rUHHSL4AJ6hNcEKSBVSmbbjeVm4gSXDuED5r0nwx"
+    "vRtupDxGYp8IZpP5KlExqNu1nbkPc+igCTIB6XsqijagzxewUHCdovmkb2JNtskx"
+    "/PMIEv+TvWIx2BzqGp71gSh/dV7SJ3rClvWd2xj8dtxG8FfAWDTIIi0qZXWn2Qhi"
+    "zQIDAQAB"
     "-----END PUBLIC KEY-----";
 
 GURL GetBackendUrl() {
@@ -112,7 +109,7 @@
   if (input.length() != 10)
     return false;
   for (int i = 0; i < 10; i++) {
-    if (i == 4 ||  i == 7) {
+    if (i == 4 || i == 7) {
       if (input[i] != '-')
         return false;
     } else if (!base::IsAsciiDigit(input[i])) {
@@ -122,106 +119,92 @@
   return true;
 }
 
-// Sets the value of |key| in |dictionary| to be a list with the contents of
-// |ids|.
-void SetExtensionIdSet(base::DictionaryValue* dictionary,
-                       const char* key,
-                       const ExtensionIdSet& ids) {
-  auto id_list = std::make_unique<base::ListValue>();
-  for (auto i = ids.begin(); i != ids.end(); ++i)
-    id_list->Append(*i);
-  dictionary->Set(key, std::move(id_list));
+// Helper for serialization of ExtensionIdSets to/from a base::Value::List.
+[[nodiscard]] base::Value::List ExtensionIdSetToList(
+    const ExtensionIdSet& ids) {
+  base::Value::List id_list;
+  base::ranges::for_each(ids,
+                         [&id_list](const auto& id) { id_list.Append(id); });
+  return id_list;
 }
 
-// Tries to fetch a list of strings from |dictionay| for |key|, and inserts
-// them into |ids|. The return value indicates success/failure. Note: on
-// failure, |ids| might contain partial results, for instance if some of the
-// members of the list were not strings.
-bool GetExtensionIdSet(const base::DictionaryValue& dictionary,
-                       const char* key,
-                       ExtensionIdSet* ids) {
-  const base::ListValue* id_list = nullptr;
-  if (!dictionary.GetList(key, &id_list))
-    return false;
-  for (const auto& entry : id_list->GetList()) {
-    if (!entry.is_string()) {
-      return false;
-    }
-    ids->insert(entry.GetString());
+[[nodiscard]] absl::optional<ExtensionIdSet> ExtensionIdSetFromList(
+    const base::Value::List& list) {
+  ExtensionIdSet ids;
+  for (const base::Value& value : list) {
+    if (!value.is_string())
+      return absl::nullopt;
+    ids.insert(value.GetString());
   }
-  return true;
+  return ids;
 }
 
 }  // namespace
 
 namespace extensions {
 
-InstallSignature::InstallSignature() {
-}
+InstallSignature::InstallSignature() = default;
 InstallSignature::InstallSignature(const InstallSignature& other) = default;
-InstallSignature::~InstallSignature() {
-}
+InstallSignature::~InstallSignature() = default;
 
-void InstallSignature::ToValue(base::DictionaryValue* value) const {
-  CHECK(value);
-
-  value->SetIntKey(kSignatureFormatVersionKey, kSignatureFormatVersion);
-  SetExtensionIdSet(value, kIdsKey, ids);
-  SetExtensionIdSet(value, kInvalidIdsKey, invalid_ids);
-  value->SetStringKey(kExpireDateKey, expire_date);
+base::Value::Dict InstallSignature::ToDict() const {
+  base::Value::Dict dict;
+  dict.Set(kSignatureFormatVersionKey, kSignatureFormatVersion);
+  dict.Set(kIdsKey, ExtensionIdSetToList(ids));
+  dict.Set(kInvalidIdsKey, ExtensionIdSetToList(invalid_ids));
+  dict.Set(kExpireDateKey, expire_date);
   std::string salt_base64;
   std::string signature_base64;
   base::Base64Encode(salt, &salt_base64);
   base::Base64Encode(signature, &signature_base64);
-  value->SetStringKey(kSaltKey, salt_base64);
-  value->SetStringKey(kSignatureKey, signature_base64);
-  value->SetStringKey(kTimestampKey,
-                      base::NumberToString(timestamp.ToInternalValue()));
+  dict.Set(kSaltKey, salt_base64);
+  dict.Set(kSignatureKey, signature_base64);
+  dict.Set(kTimestampKey, base::TimeToValue(timestamp));
+  return dict;
 }
 
 // static
-std::unique_ptr<InstallSignature> InstallSignature::FromValue(
-    const base::DictionaryValue& value) {
-  std::unique_ptr<InstallSignature> result(new InstallSignature);
+std::unique_ptr<InstallSignature> InstallSignature::FromDict(
+    const base::Value::Dict& dict) {
+  std::unique_ptr<InstallSignature> result =
+      std::make_unique<InstallSignature>();
 
   // For now we don't want to support any backwards compability, but in the
   // future if we do, we would want to put the migration code here.
-  absl::optional<int> format_version =
-      value.FindIntKey(kSignatureFormatVersionKey);
-  if (format_version != kSignatureFormatVersion) {
-    result.reset();
-    return result;
-  }
+  if (dict.FindInt(kSignatureFormatVersionKey) != kSignatureFormatVersion)
+    return nullptr;
 
-  const base::Value::Dict& dict = value.GetDict();
-  const std::string* expire_date = dict.FindString(kExpireDateKey);
-  const std::string* salt_base64 = dict.FindString(kSaltKey);
-  const std::string* signature_base64 = dict.FindString(kSignatureKey);
+  base::raw_ptr<const std::string> expire_date =
+      dict.FindString(kExpireDateKey);
+  base::raw_ptr<const std::string> salt_base64 = dict.FindString(kSaltKey);
+  base::raw_ptr<const std::string> signature_base64 =
+      dict.FindString(kSignatureKey);
   if (!expire_date || !salt_base64 || !signature_base64 ||
       !base::Base64Decode(*salt_base64, &result->salt) ||
-      !base::Base64Decode(*signature_base64, &result->signature)) {
-    result.reset();
-    return result;
-  }
+      !base::Base64Decode(*signature_base64, &result->signature))
+    return nullptr;
+
   result->expire_date = *expire_date;
 
   // Note: earlier versions of the code did not write out a timestamp value
   // so older entries will not necessarily have this.
-  if (const base::Value* timestamp = value.FindKey(kTimestampKey)) {
-    int64_t timestamp_value = 0;
-    if (!timestamp->is_string() ||
-        !base::StringToInt64(timestamp->GetString(), &timestamp_value)) {
-      result.reset();
-      return result;
-    }
-    result->timestamp = base::Time::FromInternalValue(timestamp_value);
-  }
+  result->timestamp =
+      base::ValueToTime(dict.Find(kTimestampKey)).value_or(base::Time());
 
-  if (!GetExtensionIdSet(value, kIdsKey, &result->ids) ||
-      !GetExtensionIdSet(value, kInvalidIdsKey, &result->invalid_ids)) {
-    result.reset();
-    return result;
-  }
+  base::raw_ptr<const base::Value::List> ids_list = dict.FindList(kIdsKey);
+  base::raw_ptr<const base::Value::List> invalid_ids_list =
+      dict.FindList(kInvalidIdsKey);
+  if (!ids_list || !invalid_ids_list)
+    return nullptr;
+
+  absl::optional<ExtensionIdSet> ids = ExtensionIdSetFromList(*ids_list);
+  absl::optional<ExtensionIdSet> invalid_ids =
+      ExtensionIdSetFromList(*invalid_ids_list);
+  if (!ids || !invalid_ids)
+    return nullptr;
+
+  result->ids = ids.value();
+  result->invalid_ids = invalid_ids.value();
 
   return result;
 }
@@ -231,8 +214,7 @@
     const ExtensionIdSet& ids)
     : ids_(ids), url_loader_factory_(std::move(url_loader_factory)) {}
 
-InstallSigner::~InstallSigner() {
-}
+InstallSigner::~InstallSigner() = default;
 
 // static
 bool InstallSigner::VerifySignature(const InstallSignature& signature) {
@@ -421,10 +403,9 @@
     expire_date = *maybe_expire_date;
   }
 
-  bool fields_success =
-      protocol_version == 1 && !signature_base64.empty() &&
-      ValidateExpireDateFormat(expire_date) &&
-      base::Base64Decode(signature_base64, &signature);
+  bool fields_success = protocol_version == 1 && !signature_base64.empty() &&
+                        ValidateExpireDateFormat(expire_date) &&
+                        base::Base64Decode(signature_base64, &signature);
   if (!fields_success) {
     ReportErrorViaCallback();
     return;
@@ -470,5 +451,4 @@
     std::move(callback_).Run(std::move(result));
 }
 
-
 }  // namespace extensions
diff --git a/chrome/browser/extensions/install_signer.h b/chrome/browser/extensions/install_signer.h
index a867d4e7..64e896e4 100644
--- a/chrome/browser/extensions/install_signer.h
+++ b/chrome/browser/extensions/install_signer.h
@@ -10,14 +10,11 @@
 #include <string>
 #include <vector>
 
-#include "base/callback.h"
+#include "base/functional/callback.h"
 #include "base/time/time.h"
+#include "base/values.h"
 #include "extensions/common/extension_id.h"
 
-namespace base {
-class DictionaryValue;
-}
-
 namespace network {
 class SimpleURLLoader;
 class SharedURLLoaderFactory;
@@ -50,11 +47,11 @@
   InstallSignature(const InstallSignature& other);
   ~InstallSignature();
 
-  // Helper methods for serialization to/from a base::DictionaryValue.
-  void ToValue(base::DictionaryValue* value) const;
+  // Helper methods for serialization to/from a base::Value::Dict.
+  [[nodiscard]] base::Value::Dict ToDict() const;
 
-  static std::unique_ptr<InstallSignature> FromValue(
-      const base::DictionaryValue& value);
+  static std::unique_ptr<InstallSignature> FromDict(
+      const base::Value::Dict& dict);
 };
 
 // Objects of this class encapsulate an operation to get a signature proving
diff --git a/chrome/browser/extensions/install_verifier.cc b/chrome/browser/extensions/install_verifier.cc
index dda18efd..4871612 100644
--- a/chrome/browser/extensions/install_verifier.cc
+++ b/chrome/browser/extensions/install_verifier.cc
@@ -15,8 +15,10 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/one_shot_event.h"
+#include "base/ranges/algorithm.h"
 #include "base/stl_util.h"
 #include "base/trace_event/trace_event.h"
+#include "base/values.h"
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/extension_management.h"
@@ -115,7 +117,7 @@
                                  content::BrowserContext* context)
     : prefs_(prefs), context_(context), bootstrap_check_complete_(false) {}
 
-InstallVerifier::~InstallVerifier() {}
+InstallVerifier::~InstallVerifier() = default;
 
 // static
 InstallVerifier* InstallVerifier::Get(
@@ -145,17 +147,14 @@
 void InstallVerifier::Init() {
   TRACE_EVENT0("browser,startup", "extensions::InstallVerifier::Init");
 
-  const base::DictionaryValue* pref = prefs_->GetInstallSignature();
-  if (pref) {
-    std::unique_ptr<InstallSignature> signature_from_prefs =
-        InstallSignature::FromValue(*pref);
-    if (signature_from_prefs.get()) {
-      if (!InstallSigner::VerifySignature(*signature_from_prefs)) {
-        DVLOG(1) << "Init - ignoring invalid signature";
-      } else {
-        signature_ = std::move(signature_from_prefs);
-        GarbageCollect();
-      }
+  std::unique_ptr<InstallSignature> signature_from_prefs =
+      InstallSignature::FromDict(prefs_->GetInstallSignature());
+  if (signature_from_prefs.get()) {
+    if (!InstallSigner::VerifySignature(*signature_from_prefs)) {
+      DVLOG(1) << "Init - ignoring invalid signature";
+    } else {
+      signature_ = std::move(signature_from_prefs);
+      GarbageCollect();
     }
   }
 
@@ -231,16 +230,12 @@
   if (!signature_.get() || !ShouldFetchSignature())
     return;
 
-  bool found_any = false;
-  for (auto i = ids.begin(); i != ids.end(); ++i) {
-    if (base::Contains(signature_->ids, *i) ||
-        base::Contains(signature_->invalid_ids, *i)) {
-      found_any = true;
-      break;
-    }
-  }
-  if (!found_any)
+  if (base::ranges::any_of(ids, [this](const std::string& id) {
+        return base::Contains(signature_->ids, id) ||
+               base::Contains(signature_->invalid_ids, id);
+      })) {
     return;
+  }
 
   std::unique_ptr<InstallVerifier::PendingOperation> operation(
       new InstallVerifier::PendingOperation(InstallVerifier::REMOVE));
@@ -321,16 +316,14 @@
 InstallVerifier::PendingOperation::PendingOperation(OperationType type)
     : type(type) {}
 
-InstallVerifier::PendingOperation::~PendingOperation() {
-}
+InstallVerifier::PendingOperation::~PendingOperation() = default;
 
 ExtensionIdSet InstallVerifier::GetExtensionsToVerify() const {
   ExtensionIdSet result;
   std::unique_ptr<ExtensionSet> extensions =
       ExtensionRegistry::Get(context_)->GenerateInstalledExtensionsSet();
   for (ExtensionSet::const_iterator iter = extensions->begin();
-       iter != extensions->end();
-       ++iter) {
+       iter != extensions->end(); ++iter) {
     if (NeedsVerification(**iter, context_))
       result.insert((*iter)->id());
   }
@@ -338,25 +331,15 @@
 }
 
 void InstallVerifier::MaybeBootstrapSelf() {
-  bool needs_bootstrap = false;
-
   ExtensionIdSet extension_ids = GetExtensionsToVerify();
-  if (signature_.get() == nullptr && ShouldFetchSignature()) {
-    needs_bootstrap = true;
-  } else {
-    for (auto iter = extension_ids.begin(); iter != extension_ids.end();
-         ++iter) {
-      if (!IsKnownId(*iter)) {
-        needs_bootstrap = true;
-        break;
-      }
-    }
-  }
-
-  if (needs_bootstrap)
+  if ((signature_.get() == nullptr && ShouldFetchSignature()) ||
+      base::ranges::any_of(extension_ids, [this](const std::string& id) {
+        return !IsKnownId(id);
+      })) {
     AddMany(extension_ids, ADD_ALL_BOOTSTRAP);
-  else
+  } else {
     bootstrap_check_complete_ = true;
+  }
 }
 
 void InstallVerifier::OnVerificationComplete(bool success, OperationType type) {
@@ -370,8 +353,7 @@
         const ExtensionSet& disabled_extensions =
             ExtensionRegistry::Get(context_)->disabled_extensions();
         for (ExtensionSet::const_iterator iter = disabled_extensions.begin();
-             iter != disabled_extensions.end();
-             ++iter) {
+             iter != disabled_extensions.end(); ++iter) {
           int disable_reasons = prefs_->GetDisableReasons((*iter)->id());
           if (disable_reasons & disable_reason::DISABLE_NOT_VERIFIED &&
               !MustRemainDisabled(iter->get(), nullptr, nullptr)) {
@@ -404,8 +386,8 @@
                    signature_->invalid_ids.end());
   ExtensionIdList all_ids;
   prefs_->GetExtensions(&all_ids);
-  for (ExtensionIdList::const_iterator i = all_ids.begin();
-       i != all_ids.end(); ++i) {
+  for (ExtensionIdList::const_iterator i = all_ids.begin(); i != all_ids.end();
+       ++i) {
     auto found = leftovers.find(*i);
     if (found != leftovers.end())
       leftovers.erase(found);
@@ -434,9 +416,9 @@
     ids_to_sign.insert(signature_->ids.begin(), signature_->ids.end());
   }
   if (operation.type == InstallVerifier::REMOVE) {
-    for (auto i = operation.ids.begin(); i != operation.ids.end(); ++i) {
-      if (base::Contains(ids_to_sign, *i))
-        ids_to_sign.erase(*i);
+    for (const std::string& id : operation.ids) {
+      if (base::Contains(ids_to_sign, id))
+        ids_to_sign.erase(id);
     }
   } else {  // All other operation types are some form of "ADD".
     ids_to_sign.insert(operation.ids.begin(), operation.ids.end());
@@ -457,14 +439,13 @@
     DVLOG(1) << "SaveToPrefs - saving NULL";
     prefs_->SetInstallSignature(nullptr);
   } else {
-    base::DictionaryValue pref;
-    signature_->ToValue(&pref);
+    base::Value::Dict pref = signature_->ToDict();
     if (VLOG_IS_ON(1)) {
       DVLOG(1) << "SaveToPrefs - saving";
 
       DCHECK(InstallSigner::VerifySignature(*signature_));
       std::unique_ptr<InstallSignature> rehydrated =
-          InstallSignature::FromValue(pref);
+          InstallSignature::FromDict(pref);
       DCHECK(InstallSigner::VerifySignature(*rehydrated));
     }
     prefs_->SetInstallSignature(&pref);
@@ -484,8 +465,8 @@
 
     if (!provisional_.empty()) {
       // Update |provisional_| to remove ids that were successfully signed.
-      provisional_ = base::STLSetDifference<ExtensionIdSet>(
-          provisional_, signature_->ids);
+      provisional_ =
+          base::STLSetDifference<ExtensionIdSet>(provisional_, signature_->ids);
     }
   }
 
diff --git a/chrome/browser/extensions/standard_management_policy_provider.cc b/chrome/browser/extensions/standard_management_policy_provider.cc
index 9c614910..f20685a 100644
--- a/chrome/browser/extensions/standard_management_policy_provider.cc
+++ b/chrome/browser/extensions/standard_management_policy_provider.cc
@@ -76,7 +76,7 @@
 #if DCHECK_IS_ON()
   return "extension management policy controlled settings";
 #else
-  IMMEDIATE_CRASH();
+  base::ImmediateCrash();
 #endif
 }
 
diff --git a/chrome/browser/external_protocol/external_protocol_handler_browsertest.cc b/chrome/browser/external_protocol/external_protocol_handler_browsertest.cc
index 56be3292..de9d828 100644
--- a/chrome/browser/external_protocol/external_protocol_handler_browsertest.cc
+++ b/chrome/browser/external_protocol/external_protocol_handler_browsertest.cc
@@ -49,10 +49,10 @@
   // additional security check and ask the user, without displaying a message in
   // the console. Adopt Linux's behavior for the purpose of this test suite.
   void AllowCustomProtocol() {
-    base::ListValue allow_list;
+    base::Value::List allow_list;
     allow_list.Append("custom:*");
     browser()->profile()->GetPrefs()->Set(policy::policy_prefs::kUrlAllowlist,
-                                          allow_list);
+                                          base::Value(std::move(allow_list)));
   }
 
   content::RenderFrameHost* CreateIFrame(content::RenderFrameHost* document,
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java
index 2df54ae..cab50d3 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java
@@ -273,7 +273,7 @@
     }
 
     private int getVisibleArea(View childView) {
-        Rect rect = new Rect();
+        Rect rect = new Rect(0, 0, childView.getWidth(), childView.getHeight());
         if (!mRootView.getChildVisibleRect(childView, rect, null)) return 0;
         return rect.width() * rect.height();
     }
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTrackerTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTrackerTest.java
index b9bc09b..993186b 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTrackerTest.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTrackerTest.java
@@ -152,6 +152,14 @@
 
     @Test
     @SmallTest
+    public void testGetChildVisibleRectCalledWithChildRect() {
+        mockViewDimensions(mChildA, 10, 10);
+        mTracker.isViewVisible(mChildA, 0.66f);
+        verify(mParentView).getChildVisibleRect(eq(mChildA), eq(new Rect(0, 0, 10, 10)), eq(null));
+    }
+
+    @Test
+    @SmallTest
     public void testIsItemCoveringViewport_JustEnough() {
         mockViewDimensions(mChildA, 100, 100);
         mockGetChildVisibleRect(mChildA, 0, 0, 100, 26);
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 90f10a6..e324462 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2507,6 +2507,11 @@
     "expiry_milestone": 119
   },
   {
+    "name": "enable-input-device-settings-split",
+    "owners": ["dpad", "zentaro", "cros-peripherals@google.com"],
+    "expiry_milestone": 120
+  },
+  {
     "name": "enable-input-event-logging",
     "owners": [ "hcutts", "chromeos-tango@google.com" ],
     // Used to attach input device debugging information to feedback reports.
@@ -3143,11 +3148,6 @@
     "expiry_milestone": 90
   },
   {
-    "name": "enable-tailored-security-desktop-notice",
-    "owners": ["jacastro@chromium.org", "chrome-counter-abuse-alerts@google.com"],
-    "expiry_milestone": 107
-  },
-  {
     "name": "enable-tailored-security-integration",
     "owners": ["joemerramos", "ajuma", "bling-flags@google.com"],
     "expiry_milestone": 114
@@ -5980,6 +5980,11 @@
     "expiry_milestone": 96
   },
   {
+    "name": "request-desktop-site-zoom",
+    "owners": [ "aishwaryarj@google.com", "twellington", "clank-app-team@google.com" ],
+    "expiry_milestone": 112
+  },
+  {
     "name": "restore-session-from-cache",
     "owners": [ "justincohen", "gambard", "bling-flags@google.com" ],
     // Needed for manual testing of native session restore flow on iOS.
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 022da6bd..271956a 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1090,6 +1090,11 @@
 extern const char kEnableShortcutCustomizationDescription[] =
     "Enable customization of shortcuts in the new shortcuts app.";
 
+const char kEnableInputDeviceSettingsSplitName[] =
+    "Enable input device settings split";
+const char kEnableInputDeviceSettingsSplitDescription[] =
+    "Enable input device settings to be split per-device.";
+
 const char kExperimentalRgbKeyboardPatternsName[] =
     "Enable experimental RGB Keyboard patterns support";
 const char kExperimentalRgbKeyboardPatternsDescription[] =
@@ -2899,13 +2904,6 @@
 const char kTabSearchFuzzySearchDescription[] =
     "Enable fuzzy search for Tab Search.";
 
-const char kTailoredSecurityDesktopNoticeName[] =
-    "Dialogs to notify the user of Safe Browsing Enhanced Protection";
-const char kTailoredSecurityDesktopNoticeDescription[] =
-    "Enable the use of dialogs to notify the user of Safe Browsing Enhanced "
-    "Protection within Chrome when they enable or disable Enhanced Protection "
-    "on their Account.";
-
 const char kTextInShelfName[] = "Internal test: text in shelf";
 const char kTextInShelfDescription[] =
     "Extend text in shelf timeout to learn about user education";
@@ -3969,6 +3967,11 @@
     " On tablets with small screens a mobile site will be requested by "
     "default.";
 
+const char kRequestDesktopSiteZoomName[] =
+    "Default zoom for request desktop site on Android.";
+const char kRequestDesktopSiteZoomDescription[] =
+    "Apply default page zoom on the desktop version of websites.";
+
 const char kRevokeNotificationsPermissionIfDisabledOnAppLevelName[] =
     "Revoke site-level notification permission on Android";
 const char kRevokeNotificationsPermissionIfDisabledOnAppLevelDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index bdff541..8ec115f 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -725,6 +725,9 @@
 extern const char kEnableShortcutCustomizationName[];
 extern const char kEnableShortcutCustomizationDescription[];
 
+extern const char kEnableInputDeviceSettingsSplitName[];
+extern const char kEnableInputDeviceSettingsSplitDescription[];
+
 extern const char kExperimentalRgbKeyboardPatternsName[];
 extern const char kExperimentalRgbKeyboardPatternsDescription[];
 
@@ -1647,9 +1650,6 @@
 extern const char kFoldableJankFixAndroidName[];
 extern const char kFoldableJankFixAndroidDescription[];
 
-extern const char kTailoredSecurityDesktopNoticeName[];
-extern const char kTailoredSecurityDesktopNoticeDescription[];
-
 extern const char kTextBasedAudioDescriptionName[];
 extern const char kTextBasedAudioDescriptionDescription[];
 
@@ -2273,6 +2273,9 @@
 extern const char kRequestDesktopSiteForTabletsName[];
 extern const char kRequestDesktopSiteForTabletsDescription[];
 
+extern const char kRequestDesktopSiteZoomName[];
+extern const char kRequestDesktopSiteZoomDescription[];
+
 extern const char kRevokeNotificationsPermissionIfDisabledOnAppLevelName[];
 extern const char
     kRevokeNotificationsPermissionIfDisabledOnAppLevelDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index f17b9754..185054c 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -355,7 +355,6 @@
     &messages::kMessagesForAndroidChromeSurvey,
     &messages::kMessagesForAndroidInfrastructure,
     &messages::kMessagesForAndroidReaderMode,
-    &messages::kMessagesForAndroidReduceLayoutChanges,
     &messages::kMessagesForAndroidSaveCard,
     &offline_pages::kOfflineIndicatorFeature,
     &offline_pages::kOfflinePagesCTFeature,  // See crbug.com/620421.
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 68de27c0..933d51f 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
@@ -413,8 +413,6 @@
     public static final String MESSAGES_FOR_ANDROID_PERMISSION_UPDATE =
             "MessagesForAndroidPermissionUpdate";
     public static final String MESSAGES_FOR_ANDROID_READER_MODE = "MessagesForAndroidReaderMode";
-    public static final String MESSAGES_FOR_ANDROID_REDUCE_LAYOUT_CHANGES =
-            "MessagesForAndroidReduceLayoutChanges";
     public static final String MESSAGES_FOR_ANDROID_SAVE_CARD = "MessagesForAndroidSaveCard";
     public static final String MESSAGES_FOR_ANDROID_STACKING_ANIMATION =
             "MessagesForAndroidStackingAnimation";
diff --git a/chrome/browser/k_anonymity_service/k_anonymity_trust_token_getter.cc b/chrome/browser/k_anonymity_service/k_anonymity_trust_token_getter.cc
index 1fc7318..da9f0ba 100644
--- a/chrome/browser/k_anonymity_service/k_anonymity_trust_token_getter.cc
+++ b/chrome/browser/k_anonymity_service/k_anonymity_trust_token_getter.cc
@@ -60,7 +60,8 @@
 // callbacks are supposed to be on that thread.). For some reason this is does
 // not seem to be true sometimes on Android. So we forward the call to this
 // function to the correct thread to avoid a crash from a race condition.
-void CheckThreadAndMaybeForward(base::OnceClosure callback) {
+// Returns true if we are on the correct thread and should continue executing.
+bool CheckThreadAndMaybeForward(base::OnceClosure callback) {
   if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
     // Only dump the first time, but always forward the call.
     static std::atomic<bool> dumped = false;
@@ -69,7 +70,9 @@
     }
     content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
                                                  std::move(callback));
+    return false;
   }
+  return true;
 }
 
 }  // namespace
@@ -548,9 +551,11 @@
 void KAnonymityTrustTokenGetter::FailAllCallbacks() {
   // TODO(crbug.com/1376858): Remove this check once it is no longer needed.
   // Ensure we are in the right thread.
-  CheckThreadAndMaybeForward(
-      base::BindOnce(&KAnonymityTrustTokenGetter::FailAllCallbacks,
-                     weak_ptr_factory_.GetWeakPtr()));
+  if (!CheckThreadAndMaybeForward(
+          base::BindOnce(&KAnonymityTrustTokenGetter::FailAllCallbacks,
+                         weak_ptr_factory_.GetWeakPtr()))) {
+    return;
+  }
 
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   while (!pending_callbacks_.empty())
@@ -560,9 +565,11 @@
 void KAnonymityTrustTokenGetter::CompleteOneRequest() {
   // TODO(crbug.com/1376858): Remove this check once it is no longer needed.
   // Ensure we are in the right thread.
-  CheckThreadAndMaybeForward(
-      base::BindOnce(&KAnonymityTrustTokenGetter::CompleteOneRequest,
-                     weak_ptr_factory_.GetWeakPtr()));
+  if (!CheckThreadAndMaybeForward(
+          base::BindOnce(&KAnonymityTrustTokenGetter::CompleteOneRequest,
+                         weak_ptr_factory_.GetWeakPtr()))) {
+    return;
+  }
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK(!pending_callbacks_.empty());
   RecordTrustTokenGetterAction(
diff --git a/chrome/browser/loader/signed_exchange_policy_browsertest.cc b/chrome/browser/loader/signed_exchange_policy_browsertest.cc
index ef8841d..c6f0b9b 100644
--- a/chrome/browser/loader/signed_exchange_policy_browsertest.cc
+++ b/chrome/browser/loader/signed_exchange_policy_browsertest.cc
@@ -73,12 +73,12 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
   EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
 
-  base::ListValue blocklist;
+  base::Value::List blocklist;
   blocklist.Append("test.example.org");
   policy::PolicyMap policies;
   policies.Set(policy::key::kURLBlocklist, policy::POLICY_LEVEL_MANDATORY,
                policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
-               blocklist.Clone(), nullptr);
+               base::Value(std::move(blocklist)), nullptr);
 
 #if BUILDFLAG(IS_CHROMEOS)
   policy::SetEnterpriseUsersProfileDefaults(&policies);
diff --git a/chrome/browser/media/offscreen_tab.cc b/chrome/browser/media/offscreen_tab.cc
index 32a1c3ac..bdbef88 100644
--- a/chrome/browser/media/offscreen_tab.cc
+++ b/chrome/browser/media/offscreen_tab.cc
@@ -139,7 +139,7 @@
   DVLOG(1) << "Destroying OffscreenTab for start_url=" << start_url_.spec();
   if (otr_profile_) {
     otr_profile_->RemoveObserver(this);
-    ProfileDestroyer::DestroyProfileWhenAppropriate(otr_profile_);
+    ProfileDestroyer::DestroyOTRProfileWhenAppropriate(otr_profile_);
   }
 }
 
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 67fc2412..c6fc5783 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -775,6 +775,15 @@
     "ash.launcher.suggested_content_info_dismissed";
 #endif
 
+#if BUILDFLAG(ENABLE_BACKGROUND_MODE) && BUILDFLAG(IS_MAC)
+// Deprecated 11/2022.
+const char kUserRemovedLoginItem[] = "background_mode.user_removed_login_item";
+const char kChromeCreatedLoginItem[] =
+    "background_mode.chrome_created_login_item";
+const char kMigratedLoginItemPref[] =
+    "background_mode.migrated_login_item_pref";
+#endif
+
 // Register local state used only for migration (clearing or moving to a new
 // key).
 void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
@@ -837,6 +846,13 @@
   // Deprecated 09/2022
   registry->RegisterDictionaryPref(kUsersLastInputMethod);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+// Deprecated 11/2022.
+#if BUILDFLAG(ENABLE_BACKGROUND_MODE) && BUILDFLAG(IS_MAC)
+  registry->RegisterBooleanPref(kUserRemovedLoginItem, false);
+  registry->RegisterBooleanPref(kChromeCreatedLoginItem, false);
+  registry->RegisterBooleanPref(kMigratedLoginItemPref, false);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 }
 
 // Register prefs used only for migration (clearing or moving to a new key).
@@ -1743,6 +1759,13 @@
   local_state->ClearPref(kUsersLastInputMethod);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(ENABLE_BACKGROUND_MODE) && BUILDFLAG(IS_MAC)
+  // Added 11/2022.
+  local_state->ClearPref(kUserRemovedLoginItem);
+  local_state->ClearPref(kChromeCreatedLoginItem);
+  local_state->ClearPref(kMigratedLoginItemPref);
+#endif
+
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS
 
diff --git a/chrome/browser/printing/print_browsertest.cc b/chrome/browser/printing/print_browsertest.cc
index be2a27e..4bea23534 100644
--- a/chrome/browser/printing/print_browsertest.cc
+++ b/chrome/browser/printing/print_browsertest.cc
@@ -3001,11 +3001,17 @@
   ASSERT_TRUE(web_contents);
   SetUpPrintViewManager(web_contents);
 
-  // There are no callbacks for tracking in-browser printing.  The only
-  // message is to wait for the one print job to be destroyed to ensure
-  // printing finished cleanly before completing the test.  So only need
-  // to override the number of expected messages for OOP.
-  if (GetParam() != PrintBackendFeatureVariation::kInBrowserProcess) {
+  if (GetParam() == PrintBackendFeatureVariation::kInBrowserProcess) {
+    // There are no callbacks for print stages with in-browser printing.  So
+    // the print job is started, but that fails, and there is no capturing of
+    // that result.
+    // The rest of the test sequence for this is:
+    // - An error dialog is shown.
+    // - Wait for the one print job to be destroyed, to ensure printing
+    //   finished cleanly before completing the test.
+    // This results in a total of 2 calls.
+    SetNumExpectedMessages(/*num=*/2);
+  } else {
     // The test sequence for this is:
     // - A print job is started, but that fails.
     // - An error dialog is shown.
@@ -3018,11 +3024,7 @@
   PrintAfterPreviewIsReadyAndLoaded();
 
   EXPECT_EQ(start_printing_result(), mojom::ResultCode::kFailed);
-  // TODO(crbug.com/1375018)  Update once in-browser failure is fixed to notify
-  // user of error.
-  EXPECT_EQ(
-      error_dialog_shown_count(),
-      GetParam() == PrintBackendFeatureVariation::kInBrowserProcess ? 0u : 1u);
+  EXPECT_EQ(error_dialog_shown_count(), 1u);
   EXPECT_EQ(print_job_destruction_count(), 1);
 }
 
@@ -3304,8 +3306,8 @@
   EXPECT_EQ(print_job_destruction_count(), 1);
 }
 
-// TODO(crbug.com/1375007): Very flaky on Mac and slightly on Linux.
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+// TODO(crbug.com/1375007): Slightly flaky on Linux.
+#if BUILDFLAG(IS_LINUX)
 #define MAYBE_StartBasicPrintCancel DISABLED_StartBasicPrintCancel
 #else
 #define MAYBE_StartBasicPrintCancel StartBasicPrintCancel
@@ -3367,10 +3369,12 @@
     // pipeline, so the test sequence for this is:
     // - Gets default settings.
     // - Asks user for settings.
+    // - A print job is started, but that fails.  There is no override to
+    //   this notice directly.  This does cause an error dialog to be shown.
     // - Wait for the one print job to be destroyed, to ensure printing
     //   finished cleanly before completing the test.
-    // This results in a total of 3 calls.
-    SetNumExpectedMessages(/*num=*/3);
+    // This results in a total of 4 calls.
+    SetNumExpectedMessages(/*num=*/4);
   } else {
     // The test sequence for this is:
     // - Gets default settings.
@@ -3388,11 +3392,7 @@
   WaitUntilCallbackReceived();
 
   EXPECT_EQ(start_printing_result(), mojom::ResultCode::kFailed);
-  // TODO(crbug.com/1375018)  Update once in-browser failure is fixed to notify
-  // user of error.
-  EXPECT_EQ(
-      error_dialog_shown_count(),
-      GetParam() == PrintBackendFeatureVariation::kInBrowserProcess ? 0u : 1u);
+  EXPECT_EQ(error_dialog_shown_count(), 1u);
   EXPECT_EQ(print_job_destruction_count(), 1);
 }
 
diff --git a/chrome/browser/printing/print_job_worker_oop.cc b/chrome/browser/printing/print_job_worker_oop.cc
index 1ae89aaf..921f463 100644
--- a/chrome/browser/printing/print_job_worker_oop.cc
+++ b/chrome/browser/printing/print_job_worker_oop.cc
@@ -10,7 +10,6 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/printing/print_backend_service_manager.h"
-#include "chrome/browser/printing/print_error_dialog.h"
 #include "chrome/browser/printing/print_job.h"
 #include "chrome/services/printing/public/mojom/print_backend_service.mojom.h"
 #include "components/device_event_log/device_event_log.h"
@@ -409,7 +408,6 @@
     uma_result = PrintOopResult::kCanceled;
   }
   base::UmaHistogramEnumeration(kPrintOopPrintResultHistogramName, uma_result);
-  ShowPrintErrorDialog();
 
   // Initiate rest of regular failure handling.
   task_runner()->PostTask(FROM_HERE,
diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc
index 97870bf..33e41e3 100644
--- a/chrome/browser/printing/print_view_manager_base.cc
+++ b/chrome/browser/printing/print_view_manager_base.cc
@@ -764,6 +764,11 @@
 }
 
 void PrintViewManagerBase::OnFailed() {
+#if !BUILDFLAG(IS_ANDROID)  // Android does not implement this function.
+  if (!canceling_job_)
+    ShowPrintErrorDialog();
+#endif
+
   TerminatePrintJob(true);
 }
 
@@ -871,6 +876,8 @@
     return;
 
   if (cancel) {
+    canceling_job_ = true;
+
     // We don't need the metafile data anymore because the printing is canceled.
     print_job_->Cancel();
     quit_inner_loop_.Reset();
diff --git a/chrome/browser/printing/print_view_manager_base.h b/chrome/browser/printing/print_view_manager_base.h
index a12011e..8ce84a1 100644
--- a/chrome/browser/printing/print_view_manager_base.h
+++ b/chrome/browser/printing/print_view_manager_base.h
@@ -324,6 +324,9 @@
   // Indication of success of the print job.
   bool printing_succeeded_ = false;
 
+  // Indication that the job is getting canceled.
+  bool canceling_job_ = false;
+
   // Set while running an inner message loop inside RenderAllMissingPagesNow().
   // This means we are _blocking_ until all the necessary pages have been
   // rendered or the print settings are being loaded.
diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h
index bb7826c..b23d522 100644
--- a/chrome/browser/profiles/profile.h
+++ b/chrome/browser/profiles/profile.h
@@ -488,6 +488,8 @@
 
   virtual void RecordPrimaryMainFrameNavigation() = 0;
 
+  base::WeakPtr<Profile> GetWeakPtr();
+
  protected:
   // Creates an OffTheRecordProfile which points to this Profile.
   static std::unique_ptr<Profile> CreateOffTheRecordProfile(
@@ -506,8 +508,7 @@
 
  private:
   friend class ProfileDestroyer;
-
-  base::WeakPtr<Profile> GetWeakPtr();
+  friend class OTRProfileDestroyer;
 
   // Created on the UI thread, and returned by GetResourceContext(), but
   // otherwise lives on and is destroyed on the IO thread.
diff --git a/chrome/browser/profiles/profile_android.cc b/chrome/browser/profiles/profile_android.cc
index 6d44689..2dd384f 100644
--- a/chrome/browser/profiles/profile_android.cc
+++ b/chrome/browser/profiles/profile_android.cc
@@ -71,9 +71,12 @@
 
 void ProfileAndroid::DestroyWhenAppropriate(JNIEnv* env,
                                             const JavaParamRef<jobject>& obj) {
+  CHECK(profile_->IsOffTheRecord())
+      << "Only OTR profiles can be destroyed from Java as regular profiles are "
+         "owned by the C++ ProfileManager.";
   // Don't delete the Profile directly because the corresponding
   // RenderViewHost might not be deleted yet.
-  ProfileDestroyer::DestroyProfileWhenAppropriate(profile_);
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(profile_);
 }
 
 base::android::ScopedJavaLocalRef<jobject> ProfileAndroid::GetOriginalProfile(
diff --git a/chrome/browser/profiles/profile_browsertest.cc b/chrome/browser/profiles/profile_browsertest.cc
index 7efc9b3..69846360 100644
--- a/chrome/browser/profiles/profile_browsertest.cc
+++ b/chrome/browser/profiles/profile_browsertest.cc
@@ -853,7 +853,8 @@
   watcher1.Watch(otr_profile1);
   watcher2.Watch(otr_profile2);
 
-  ProfileDestroyer::DestroyProfileWhenAppropriate(regular_profile.release());
+  ProfileDestroyer::DestroyOriginalProfileWhenAppropriate(
+      std::move(regular_profile));
 
   EXPECT_TRUE(watcher1.destroyed());
   EXPECT_TRUE(watcher2.destroyed());
@@ -883,7 +884,7 @@
       incognito_browser->profile()->GetIOTaskRunner();
 
   // Request the destruction of one OTR profile:
-  ProfileDestroyer::DestroyProfileWhenAppropriate(otr_profile[1]);
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(otr_profile[1]);
   EXPECT_FALSE(watcher[0].destroyed());
   EXPECT_TRUE(watcher[1].destroyed());
   EXPECT_FALSE(watcher[2].destroyed());
@@ -908,7 +909,7 @@
   EXPECT_FALSE(watcher[2].destroyed());
 
   // Cleanup
-  ProfileDestroyer::DestroyProfileWhenAppropriate(otr_profile[2]);
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(otr_profile[2]);
   watcher[2].WaitForDestruction();
 }
 
@@ -965,7 +966,7 @@
       regular_profile, ProfileKeepAliveOrigin::kOffTheRecordProfile));
 
   // Destroy the OTR profile. *Now* the regular Profile should get deleted.
-  ProfileDestroyer::DestroyProfileWhenAppropriate(otr_profile);
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(otr_profile);
   otr_watcher.WaitForDestruction();
   regular_watcher.WaitForDestruction();
 
diff --git a/chrome/browser/profiles/profile_destroyer.cc b/chrome/browser/profiles/profile_destroyer.cc
index 3079db2..73a34a4 100644
--- a/chrome/browser/profiles/profile_destroyer.cc
+++ b/chrome/browser/profiles/profile_destroyer.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/profiles/profile_destroyer.h"
 
 #include <memory>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/feature_list.h"
@@ -59,17 +60,113 @@
 
 }  // namespace
 
-// static
-void ProfileDestroyer::DestroyProfileWhenAppropriate(Profile* profile) {
-  DestroyProfileWhenAppropriateWithTimeout(profile,
-                                           base::Seconds(kTimerDelaySeconds));
-}
+class OTRProfileDestroyer : public ProfileDestroyer {
+ public:
+  OTRProfileDestroyer(Profile* profile, base::TimeDelta timeout)
+      : ProfileDestroyer(profile, timeout), profile_(profile->GetWeakPtr()) {}
+  ~OTRProfileDestroyer() override = default;
+
+ protected:
+  Profile* GetProfile() override { return profile_.get(); }
+
+  void DoDestroyUnderlyingProfile() override {
+    Profile* profile = profile_.get();
+    if (!profile)
+      return;
+    ProfileDestroyer::DestroyOffTheRecordProfileNow(profile);
+  }
+
+  void RetryDestroyUnderlyingProfile() override {
+    Profile* profile = profile_.get();
+    if (!profile)
+      return;
+    ProfileDestroyer::DestroyOTRProfileWhenAppropriateWithTimeout(profile,
+                                                                  timeout());
+  }
+
+ private:
+  base::WeakPtr<Profile> profile_;
+};
+
+class OriginalProfileDestroyer : public ProfileDestroyer {
+ public:
+  OriginalProfileDestroyer(std::unique_ptr<Profile> profile,
+                           base::TimeDelta timeout)
+      : ProfileDestroyer(profile.get(), timeout),
+        profile_(std::move(profile)) {}
+  ~OriginalProfileDestroyer() override = default;
+
+ protected:
+  Profile* GetProfile() override { return profile_.get(); }
+
+  void DoDestroyUnderlyingProfile() override {
+    DCHECK(profile_);
+    ProfileDestroyer::DestroyOriginalProfileNow(std::move(profile_));
+  }
+
+  void RetryDestroyUnderlyingProfile() override {
+    DCHECK(profile_);
+    ProfileDestroyer::DestroyOriginalProfileWhenAppropriateWithTimeout(
+        std::move(profile_), timeout());
+  }
+
+ private:
+  std::unique_ptr<Profile> profile_;
+};
 
 // static
-void ProfileDestroyer::DestroyProfileWhenAppropriateWithTimeout(
+void ProfileDestroyer::DestroyOriginalProfileWhenAppropriate(
+    std::unique_ptr<Profile> profile) {
+  DestroyOriginalProfileWhenAppropriateWithTimeout(
+      std::move(profile), base::Seconds(kTimerDelaySeconds));
+}
+
+void ProfileDestroyer::DestroyOriginalProfileWhenAppropriateWithTimeout(
+    std::unique_ptr<Profile> profile,
+    base::TimeDelta timeout) {
+  DCHECK(profile);
+  DCHECK_EQ(profile.get(), profile->GetOriginalProfile());
+  DCHECK(!HasPendingDestroyerForProfile(profile.get()));
+
+  TRACE_EVENT(
+      "shutdown",
+      "ProfileDestroyer::DestroyOriginalProfileWhenAppropriateWithTimeout",
+      [&](perfetto::EventContext ctx) {
+        auto* proto = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>()
+                          ->set_chrome_profile_destroyer();
+        proto->set_profile_ptr(reinterpret_cast<uint64_t>(profile.get()));
+        proto->set_is_off_the_record(profile->IsOffTheRecord());
+      });
+
+  profile->MaybeSendDestroyedNotification();
+
+  HostSet profile_hosts;
+  GetHostsForProfile(&profile_hosts, profile.get());
+  for (Profile* otr_profile : GetDependentProfiles(profile.get())) {
+    GetHostsForProfile(&profile_hosts, otr_profile);
+  }
+
+  OriginalProfileDestroyer* profile_destroyer =
+      new OriginalProfileDestroyer(std::move(profile), timeout);
+  profile_destroyer->Start(profile_hosts);
+}
+
+void ProfileDestroyer::DestroyOTRProfileWhenAppropriate(Profile* profile) {
+  DestroyOTRProfileWhenAppropriateWithTimeout(
+      profile, base::Seconds(kTimerDelaySeconds));
+}
+
+void ProfileDestroyer::DestroyOTRProfileWhenAppropriateWithTimeout(
     Profile* profile,
     base::TimeDelta timeout) {
-  TRACE_EVENT("shutdown", "ProfileDestroyer::DestroyProfileWhenAppropriate",
+  DCHECK(profile);
+  DCHECK_NE(profile, profile->GetOriginalProfile());
+
+  if (HasPendingDestroyerForProfile(profile))
+    return;
+
+  TRACE_EVENT("shutdown",
+              "ProfileDestroyer::DestroyOTRProfileWhenAppropriateWithTimeout",
               [&](perfetto::EventContext ctx) {
                 auto* proto =
                     ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>()
@@ -77,42 +174,21 @@
                 proto->set_profile_ptr(reinterpret_cast<uint64_t>(profile));
                 proto->set_is_off_the_record(profile->IsOffTheRecord());
               });
+
   profile->MaybeSendDestroyedNotification();
 
-  // Profiles may have DestroyProfileWhenAppropriate() called before their
-  // RenderProcessHosts are gone. When this happens, we need to defer their
-  // deletion.
-  //
-  // TODO(arthursonzogni): Explore adding a ScopedProfileKeepAlive in
-  // RenderProcessHost. This would remove the need of a ProfileDestroyer waiting
-  // for RenderProcessHost deletion. It will make Chrome more stable and fix
-  // some UAF bugs.
-  //
-  // Note: The original profile waits for both its own RenderProcessHost and its
-  // OffTheRecord Profiles's RenderProcessHosts. It is slightly safer. OTR
-  // profiles holds a ScopedProfileKeepAlive on their parent and are deleted
-  // first, so this seems unnecessary, but ScopedProfileKeepAlive logic is
-  // ignored during shutdown and by the System Profile do not either.
   HostSet profile_hosts;
   GetHostsForProfile(&profile_hosts, profile);
-  for (Profile* otr_profile : GetDependentProfiles(profile)) {
-    GetHostsForProfile(&profile_hosts, otr_profile);
-  }
-
-  if (!profile_hosts.empty()) {
-    // The instance will destroy itself once all (non-spare) render process
-    // hosts referring to it are properly terminated. This happens in the two
-    // "final" state: Retry() and Timeout().
-    new ProfileDestroyer(profile, profile_hosts, timeout);
-    return;
-  }
-
-  DestroyProfileNow(profile);
+  OTRProfileDestroyer* profile_destroyer =
+      new OTRProfileDestroyer(profile, timeout);
+  profile_destroyer->Start(profile_hosts);
 }
 
 // static
 void ProfileDestroyer::DestroyPendingProfilesForShutdown() {
   while (!PendingDestroyers().empty()) {
+    TRACE_EVENT("shutdown",
+                "ProfileDestroyer::DestroyPendingProfilesForShutdown");
     ProfileDestroyer* destroyer = *(PendingDestroyers().begin());
     // Destroys `destroyer`and removes it from `PendingDestroyers()`:
     destroyer->Timeout();
@@ -139,15 +215,8 @@
 }
 
 // static
-void ProfileDestroyer::DestroyProfileNow(Profile* profile) {
-  if (profile->IsOffTheRecord())
-    DestroyOffTheRecordProfileNow(profile);
-  else
-    DestroyOriginalProfileNow(profile);
-}
-
-// static
-void ProfileDestroyer::DestroyOriginalProfileNow(Profile* profile) {
+void ProfileDestroyer::DestroyOriginalProfileNow(
+    std::unique_ptr<Profile> profile) {
   DCHECK(profile);
   DCHECK(!profile->IsOffTheRecord());
   TRACE_EVENT("shutdown", "ProfileDestroyer::DestroyOriginalProfileNow",
@@ -155,7 +224,8 @@
                 auto* proto =
                     ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>()
                         ->set_chrome_profile_destroyer();
-                proto->set_profile_ptr(reinterpret_cast<uint64_t>(profile));
+                proto->set_profile_ptr(
+                    reinterpret_cast<uint64_t>(profile.get()));
               });
 
   // With DestroyProfileOnBrowserClose and --single-process, we need to clean up
@@ -164,7 +234,7 @@
   if (base::FeatureList::IsEnabled(features::kDestroyProfileOnBrowserClose) &&
       content::RenderProcessHost::run_renderer_in_process()) {
     HostSet rph;
-    GetHostsForProfile(&rph, profile, /*include_spare_rph=*/true);
+    GetHostsForProfile(&rph, profile.get(), /*include_spare_rph=*/true);
     if (!rph.empty()) {
       content::RenderProcessHost::ShutDownInProcessRenderer();
     }
@@ -173,11 +243,11 @@
 #if DCHECK_IS_ON()
   // Save the raw pointers of profile and dependent profile for DCHECKing on
   // later.
-  void* profile_ptr = profile;
-  std::vector<Profile*> dependent_profile = GetDependentProfiles(profile);
+  void* profile_ptr = profile.get();
+  std::vector<Profile*> dependent_profile = GetDependentProfiles(profile.get());
 #endif  // DCHECK_IS_ON()
 
-  delete profile;
+  profile.reset();
 
 #if DCHECK_IS_ON()
   // Count the number of hosts that have dangling pointers to the freed Profile
@@ -210,12 +280,12 @@
 #endif  // DCHECK_IS_ON()
 }
 
-ProfileDestroyer::ProfileDestroyer(Profile* profile,
-                                   const HostSet& hosts,
-                                   base::TimeDelta timeout)
-    : profile_(profile->GetWeakPtr()),
-      timeout_(timeout),
-      profile_ptr_(reinterpret_cast<uint64_t>(profile)) {
+ProfileDestroyer::ProfileDestroyer(Profile* profile, base::TimeDelta timeout)
+    : timeout_(timeout), profile_ptr_(reinterpret_cast<uint64_t>(profile)) {
+  PendingDestroyers().insert(this);
+}
+
+void ProfileDestroyer::Start(const HostSet& hosts) {
   TRACE_EVENT("shutdown", "ProfileDestroyer::ProfileDestroyer",
               [&](perfetto::EventContext ctx) {
                 auto* proto =
@@ -224,15 +294,19 @@
                 proto->set_profile_ptr(profile_ptr_);
                 proto->set_host_count_at_creation(hosts.size());
               });
-  DCHECK(!hosts.empty());
-  PendingDestroyers().insert(this);
+
   for (auto* host : hosts)
     observations_.AddObservation(host);
-  DCHECK(observations_.IsObservingAnySource());
+
+  if (!observations_.IsObservingAnySource()) {
+    // No renderer process to wait for. Destroy profile now.
+    Timeout();
+    return;
+  }
 
   // We don't want to wait for RenderProcessHost to be destroyed longer than
   // timeout.
-  timer_.Start(FROM_HERE, timeout,
+  timer_.Start(FROM_HERE, timeout_,
                base::BindOnce(&ProfileDestroyer::Timeout,
                               weak_ptr_factory_.GetWeakPtr()));
 }
@@ -292,20 +366,6 @@
       base::BindOnce(&ProfileDestroyer::Retry, weak_ptr_factory_.GetWeakPtr()));
 }
 
-void ProfileDestroyer::Timeout() {
-  if (Profile* profile = profile_.get()) {
-    DestroyProfileNow(profile);
-  }
-  delete this;  // Final state.
-}
-
-void ProfileDestroyer::Retry() {
-  if (Profile* profile = profile_.get()) {
-    DestroyProfileWhenAppropriateWithTimeout(profile, timeout_);
-  }
-  delete this;  // Final state.
-}
-
 // static
 void ProfileDestroyer::GetHostsForProfile(HostSet* out,
                                           void* profile_ptr,
@@ -335,3 +395,34 @@
     out->insert(render_process_host);
   }
 }
+
+void ProfileDestroyer::Timeout() {
+  DCHECK(!is_prepared_for_destruction_);
+  is_prepared_for_destruction_ = true;
+
+  // Destroying the profile destroys remote hosts, so it is important to keep
+  // |this| alive while the underlying profile is destroyed as otherwise the
+  // destructor will crash on line CHECK(!observations_.IsObservingAnySource()).
+  DoDestroyUnderlyingProfile();
+
+  delete this;  // Final state.
+}
+
+void ProfileDestroyer::ProfileDestroyer::Retry() {
+  DCHECK(!is_prepared_for_destruction_);
+  is_prepared_for_destruction_ = true;
+
+  RetryDestroyUnderlyingProfile();
+
+  delete this;  // Final state.
+}
+
+// static
+bool ProfileDestroyer::HasPendingDestroyerForProfile(const Profile* profile) {
+  for (ProfileDestroyer* destroyer : PendingDestroyers()) {
+    if (destroyer->GetProfile() == profile &&
+        !destroyer->is_prepared_for_destruction())
+      return true;
+  }
+  return false;
+}
diff --git a/chrome/browser/profiles/profile_destroyer.h b/chrome/browser/profiles/profile_destroyer.h
index aae4a053..e646fe9 100644
--- a/chrome/browser/profiles/profile_destroyer.h
+++ b/chrome/browser/profiles/profile_destroyer.h
@@ -7,10 +7,12 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <set>
 
 #include "base/memory/ref_counted.h"
 #include "base/scoped_multi_source_observation.h"
+#include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_process_host_observer.h"
@@ -19,15 +21,22 @@
 class Profile;
 class ProfileImpl;
 
-// We use this class to destroy the off the record profile so that we can make
-// sure it gets done asynchronously after all render process hosts are gone.
+// We use this class to destroy the profiles so that we can make sure it gets
+// done asynchronously after all render process hosts are gone.
 class ProfileDestroyer : public content::RenderProcessHostObserver {
  public:
-  // Destroys the given profile either instantly, or after a short delay waiting
-  // for dependent renderer process hosts to destroy.
-  // Ownership of the profile is passed to profile destroyer and the profile
-  // should not be used after this call.
-  static void DestroyProfileWhenAppropriate(Profile* profile);
+  // Destroys the given original profile either instantly, or after a short
+  // delay waiting for dependent renderer process hosts to destroy.
+  static void DestroyOriginalProfileWhenAppropriate(
+      std::unique_ptr<Profile> profile);
+
+  // Destroys the given off-the-record profile either instantly, or after a
+  // short delay waiting for dependent renderer process hosts to destroy.
+  // `profile` should not be used after this call.
+  //
+  // OTR profiles are owned by their parent profile - the parent profile is
+  // responsible for actually destroying the object.
+  static void DestroyOTRProfileWhenAppropriate(Profile* profile);
 
   // Force destroy all the profiles pending deletion. This is called by the
   // ProfileManager during shutdown.
@@ -36,6 +45,55 @@
   ProfileDestroyer(const ProfileDestroyer&) = delete;
   ProfileDestroyer& operator=(const ProfileDestroyer&) = delete;
 
+ protected:
+  using HostSet = std::set<content::RenderProcessHost*>;
+
+  // Similar to DestroyOriginalProfileWhenAppropriate(), but with an explicit
+  // timeout to wait for the renderer host to be destroyed.
+  static void DestroyOriginalProfileWhenAppropriateWithTimeout(
+      std::unique_ptr<Profile> profile,
+      base::TimeDelta timeout);
+
+  // Similar to DestroyOTRProfileWhenAppropriate(), but with an explicit
+  // timeout to wait for the renderer host to be destroyed.
+  static void DestroyOTRProfileWhenAppropriateWithTimeout(
+      Profile* profile,
+      base::TimeDelta timeout);
+
+  // Destroys an Original (non-off-the-record) profile immediately.
+  static void DestroyOriginalProfileNow(std::unique_ptr<Profile> profile);
+
+  // Destroys an OffTheRecord profile immediately and removes it from all
+  // pending destroyers.
+  static void DestroyOffTheRecordProfileNow(Profile* profile);
+
+  // Fetch the list of render process hosts that still point to |profile_ptr|.
+  // |profile_ptr| is a void* because the Profile object may be freed. Only
+  // pointer comparison is allowed, it will never be dereferenced as a Profile.
+  //
+  // If |include_spare_rph| is true, include spare render process hosts in the
+  // output.
+  static void GetHostsForProfile(HostSet* out,
+                                 void* profile_ptr,
+                                 bool include_spare_rph = false);
+
+  // Returns true if there is already a pending destroyer for |profile|.
+  static bool HasPendingDestroyerForProfile(const Profile* profile);
+
+  ProfileDestroyer(Profile* profile, base::TimeDelta timeout);
+  ~ProfileDestroyer() override;
+
+  const base::TimeDelta& timeout() const { return timeout_; }
+
+  // Returns the underlying profile that should be destructed.
+  virtual Profile* GetProfile() = 0;
+
+  // Destroys the underlying profile.
+  virtual void DoDestroyUnderlyingProfile() = 0;
+
+  // Retries the destruction of the underlying profile using the same timeout.
+  virtual void RetryDestroyUnderlyingProfile() = 0;
+
  private:
   friend class ProfileImpl;
   friend class base::RefCounted<ProfileDestroyer>;
@@ -43,18 +101,14 @@
   // For custom timeout, see DestroyProfileWhenAppropriateWithTimeout.
   friend class DevToolsBrowserContextManager;
 
-  using HostSet = std::set<content::RenderProcessHost*>;
+  // Starts monitoring the |hosts| and the timeout. If |hosts| is empty, then
+  // this function will delete the profile now.
+  void Start(const HostSet& hosts);
 
-  // Same as DestroyProfileWhenAppropriate, but configures how long to wait
-  // for render process hosts to be destroyed. Intended for testing/automation
-  // scenarios, where default timeout is too short.
-  static void DestroyProfileWhenAppropriateWithTimeout(Profile* profile,
-                                                       base::TimeDelta timeout);
-
-  ProfileDestroyer(Profile* profile,
-                   const HostSet& hosts,
-                   base::TimeDelta timeout);
-  ~ProfileDestroyer() override;
+  // Returns true when this profile destroyer was scheduled for destruction;
+  bool is_prepared_for_destruction() const {
+    return is_prepared_for_destruction_;
+  }
 
   // content::RenderProcessHostObserver override.
   void RenderProcessHostDestroyed(content::RenderProcessHost* host) override;
@@ -67,26 +121,6 @@
   void Retry();
   void Timeout();
 
-  // Fetch the list of render process hosts that still point to |profile_ptr|.
-  // |profile_ptr| is a void* because the Profile object may be freed. Only
-  // pointer comparison is allowed, it will never be dereferenced as a Profile.
-  //
-  // If |include_spare_rph| is true, include spare render process hosts in the
-  // output.
-  static void GetHostsForProfile(HostSet* out,
-                                 void* profile_ptr,
-                                 bool include_spare_rph = false);
-
-  // Destroys a profile immediately.
-  static void DestroyProfileNow(Profile* profile);
-
-  // Destroys an Original (non-off-the-record) profile immediately.
-  static void DestroyOriginalProfileNow(Profile* profile);
-
-  // Destroys an OffTheRecord profile immediately and removes it from all
-  // pending destroyers.
-  static void DestroyOffTheRecordProfileNow(Profile* profile);
-
   // We don't want to wait forever, so we have a cancellation timer.
   base::OneShotTimer timer_;
 
@@ -94,28 +128,11 @@
                                      content::RenderProcessHostObserver>
       observations_{this};
 
-  // The profile being destroyed.
-  //
-  // Note: Ownership model of the Profile is not consistent. As a result, this
-  // variable sometimes represent ownership over the Profile, but sometimes
-  // this is just a weak reference, and the Profile might be destroyed outside
-  // of the ProfileDestroyer.
-  //
-  // [Regular profile]
-  // Owned by the ProfileManager. Ownership is transferred.
-  //
-  // [OTR profile]
-  // Owned by the original profile. Owner is NOT transferred. This is a weak
-  // pointer. Deleting the original Profile will delete its OTR profile under
-  // the hood.
-  //
-  // [Independent profile]
-  // It depends on the component. Most likely, the ownership is transferred.
-  base::WeakPtr<Profile> profile_;
-
   // Force-destruction timeout.
   const base::TimeDelta timeout_;
 
+  bool is_prepared_for_destruction_ = false;
+
   // The initial value of |profile_| stored as uint64_t for traces. It is useful
   // for use in the destructor, because at the end, |profile_| is nullptr.
   const uint64_t profile_ptr_;
diff --git a/chrome/browser/profiles/profile_destroyer_unittest.cc b/chrome/browser/profiles/profile_destroyer_unittest.cc
index d9730ed..8d83cb35 100644
--- a/chrome/browser/profiles/profile_destroyer_unittest.cc
+++ b/chrome/browser/profiles/profile_destroyer_unittest.cc
@@ -205,7 +205,7 @@
 
   // No profile are destroyed, because of the RenderProcessHosts.
   StopKeepingAliveOriginalProfile();
-  ProfileDestroyer::DestroyProfileWhenAppropriate(OtrProfile(0));
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(OtrProfile(0));
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(original_profile());
   EXPECT_TRUE(OtrProfile(0));
@@ -238,7 +238,7 @@
   EXPECT_TRUE(OtrProfile(0));
 
   // Ask for destruction of OTR profile, and expect immediate destruction.
-  ProfileDestroyer::DestroyProfileWhenAppropriate(OtrProfile(0));
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(OtrProfile(0));
   EXPECT_FALSE(OtrProfile(0));
 }
 
@@ -254,7 +254,7 @@
       CreatedRendererProcessHost(OtrProfile(0));
 
   // Ask for destruction of OTR profile, but expect it to be delayed.
-  ProfileDestroyer::DestroyProfileWhenAppropriate(OtrProfile(0));
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(OtrProfile(0));
   EXPECT_TRUE(OtrProfile(0));
 
   // Destroy the first pending render process host, and expect it not to destroy
@@ -270,29 +270,6 @@
   EXPECT_FALSE(OtrProfile(0));
 }
 
-// Regression test for:
-// https://crbug.com/1337388#c11
-TEST_P(ProfileDestroyerTest,
-       DestructionRequestedTwiceWhileDelayedOriginalProfile) {
-  if (!IsScopedProfileKeepAliveSupported())
-    return;
-  CreateOriginalProfile();
-
-  content::RenderProcessHost* render_process_host =
-      CreatedRendererProcessHost(original_profile());
-  StopKeepingAliveOriginalProfile();
-
-  EXPECT_TRUE(original_profile());
-  ProfileDestroyer::DestroyProfileWhenAppropriate(original_profile());
-  EXPECT_TRUE(original_profile());
-  ProfileDestroyer::DestroyProfileWhenAppropriate(original_profile());
-  EXPECT_TRUE(original_profile());
-
-  render_process_host->Cleanup();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(original_profile());
-}
-
 TEST_P(ProfileDestroyerTest, RenderProcessAddedAfterDestroyRequested) {
   if (!IsScopedProfileKeepAliveSupported())
     return;
@@ -302,8 +279,6 @@
       CreatedRendererProcessHost(original_profile());
   StopKeepingAliveOriginalProfile();
 
-  ProfileDestroyer::DestroyProfileWhenAppropriate(original_profile());
-
   EXPECT_TRUE(original_profile());
   content::RenderProcessHost* render_process_host_2 =
       CreatedRendererProcessHost(original_profile());
@@ -329,9 +304,9 @@
   content::RenderProcessHost* render_process_host =
       CreatedRendererProcessHost(OtrProfile(0));
 
-  ProfileDestroyer::DestroyProfileWhenAppropriate(OtrProfile(0));
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(OtrProfile(0));
   EXPECT_TRUE(OtrProfile(0));
-  ProfileDestroyer::DestroyProfileWhenAppropriate(OtrProfile(0));
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(OtrProfile(0));
   EXPECT_TRUE(OtrProfile(0));
 
   render_process_host->Cleanup();
@@ -359,8 +334,8 @@
 
   // Ask for the destruction of two of them. The destruction is delayed, because
   // they are kept alive by two RenderProcessHost depending on them.
-  ProfileDestroyer::DestroyProfileWhenAppropriate(OtrProfile(0));
-  ProfileDestroyer::DestroyProfileWhenAppropriate(OtrProfile(1));
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(OtrProfile(0));
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(OtrProfile(1));
   EXPECT_TRUE(original_profile());
   EXPECT_TRUE(OtrProfile(0));
   EXPECT_TRUE(OtrProfile(1));
@@ -391,7 +366,7 @@
   EXPECT_TRUE(OtrProfile(2));
 
   // Allow the deletion of the last OTR profile:
-  ProfileDestroyer::DestroyProfileWhenAppropriate(OtrProfile(2));
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(OtrProfile(2));
   EXPECT_TRUE(original_profile());
   EXPECT_FALSE(OtrProfile(0));
   EXPECT_FALSE(OtrProfile(1));
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 7167530..5965ecdd 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -514,6 +514,21 @@
     std::move(callback).Run(profile);
 }
 
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+void ClearPrimaryAccountForProfile(
+    base::WeakPtr<Profile> weak_profile,
+    signin_metrics::ProfileSignout signout_source_metric,
+    signin_metrics::SignoutDelete signout_delete_metric) {
+  Profile* profile = weak_profile.get();
+  if (!profile)
+    return;
+
+  IdentityManagerFactory::GetForProfile(profile)
+      ->GetPrimaryAccountMutator()
+      ->ClearPrimaryAccount(signout_source_metric, signout_delete_metric);
+}
+#endif
+
 }  // namespace
 
 ProfileManager::ProfileManager(const base::FilePath& user_data_dir)
@@ -1774,7 +1789,8 @@
   DCHECK(owned_profile_);
   DCHECK_EQ(owned_profile_.get(), unowned_profile_);
   unowned_profile_ = nullptr;
-  ProfileDestroyer::DestroyProfileWhenAppropriate(owned_profile_.release());
+  ProfileDestroyer::DestroyOriginalProfileWhenAppropriate(
+      std::move(owned_profile_));
 }
 
 // static
@@ -2284,16 +2300,10 @@
               << !entry->CanBeManaged();
       if (signin_util::IsForceSigninEnabled() && could_be_managed_status &&
           !entry->CanBeManaged()) {
-        auto* account_mutator = identity_manager->GetPrimaryAccountMutator();
-
-        // GetPrimaryAccountMutator() returns nullptr on ChromeOS only.
-        DCHECK(account_mutator);
         content::GetUIThreadTaskRunner({})->PostTask(
             FROM_HERE,
             base::BindOnce(
-                base::IgnoreResult(
-                    &signin::PrimaryAccountMutator::ClearPrimaryAccount),
-                base::Unretained(account_mutator),
+                &ClearPrimaryAccountForProfile, profile->GetWeakPtr(),
                 signin_metrics::AUTHENTICATION_FAILED_WITH_FORCE_SIGNIN,
                 signin_metrics::SignoutDelete::kIgnoreMetric));
       }
diff --git a/chrome/browser/resources/access_code_cast/access_code_cast.ts b/chrome/browser/resources/access_code_cast/access_code_cast.ts
index fc22a371..16429ae05 100644
--- a/chrome/browser/resources/access_code_cast/access_code_cast.ts
+++ b/chrome/browser/resources/access_code_cast/access_code_cast.ts
@@ -12,7 +12,7 @@
 
 import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
-import {isWindows} from 'chrome://resources/js/cr.m.js';
+import {isWindows} from 'chrome://resources/js/platform.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {PluralStringProxyImpl} from 'chrome://resources/js/plural_string_proxy.js';
diff --git a/chrome/browser/resources/bluetooth_internals/BUILD.gn b/chrome/browser/resources/bluetooth_internals/BUILD.gn
index 1c71413..a8d85f75 100644
--- a/chrome/browser/resources/bluetooth_internals/BUILD.gn
+++ b/chrome/browser/resources/bluetooth_internals/BUILD.gn
@@ -43,6 +43,7 @@
   "object_fieldset.html",
   "service_list_item.html",
   "snackbar.html",
+  "value_control.html",
 ]
 
 # Files generated by html_to_wrapper
diff --git a/chrome/browser/resources/bluetooth_internals/characteristic_list_item.html b/chrome/browser/resources/bluetooth_internals/characteristic_list_item.html
index 8565af67..ffcd9de 100644
--- a/chrome/browser/resources/bluetooth_internals/characteristic_list_item.html
+++ b/chrome/browser/resources/bluetooth_internals/characteristic_list_item.html
@@ -4,24 +4,11 @@
   }
 
   .info-container > h4,
-  .info-container > div {
+  .info-container > object-field-set,
+  .info-container > value-control {
     margin: 1em;
   }
 
-  /* Value Control */
-  .value-control > div {
-    display: flex;
-    margin: 4px 0;
-  }
-
-  .value-control > div > input {
-    flex-grow: 1;
-  }
-
-  .value-control > div:nth-of-type(2) {
-    justify-content: flex-end;
-  }
-
   button.show-all-properties {
     background-image: linear-gradient(#ededed, #ededed 38%, #dedede);
     border: 1px solid rgba(0, 0, 0, 0.25);
@@ -59,12 +46,6 @@
     /* OVERRIDE */
     transition: border-color 200ms;
   }
-
-  h3 {
-    font-weight: normal;
-    line-height: 1;
-    user-select: none;
-  }
 </style>
 <expandable-list-item>
   <div slot="brief-content">
@@ -76,8 +57,9 @@
     <object-field-set class="characteristics" show-all></object-field-set>
     <h4>Properties<button class="show-all-properties">Show All</button></h4>
     <object-field-set class="properties"></object-field-set>
+    <h4>Value</h4>
+    <value-control hidden></value-control>
     <h4>Descriptors</h4>
     <descriptor-list></descriptor-list>
-    <h4>Value</h4>
   </div>
 </expandable-list-item>
diff --git a/chrome/browser/resources/bluetooth_internals/characteristic_list_item.js b/chrome/browser/resources/bluetooth_internals/characteristic_list_item.js
index acddb77..374c9fb 100644
--- a/chrome/browser/resources/bluetooth_internals/characteristic_list_item.js
+++ b/chrome/browser/resources/bluetooth_internals/characteristic_list_item.js
@@ -56,8 +56,6 @@
     this.deviceAddress_ = '';
     /** @private {string} */
     this.serviceId_ = '';
-    /** @private {!ValueControl} */
-    this.valueControl_ = new ValueControl();
   }
 
   connectedCallback() {
@@ -131,18 +129,15 @@
     });
     propertiesFieldSet.hidden = false;
 
-    this.valueControl_.load({
+    const valueControl = this.shadowRoot.querySelector('value-control');
+    valueControl.dataset.options = JSON.stringify({
       deviceAddress: this.deviceAddress_,
       serviceId: this.serviceId_,
       characteristicId: this.info.id,
       properties: this.info.properties,
     });
-    this.valueControl_.setValue(this.info.lastKnownValue);
-
-    const infoDiv = this.shadowRoot.querySelector('.info-container');
-    infoDiv.insertBefore(
-        this.valueControl_,
-        this.shadowRoot.querySelector('characteristic-list'));
+    valueControl.dataset.value = JSON.stringify(this.info.lastKnownValue);
+    valueControl.hidden = false;
   }
 }
 
diff --git a/chrome/browser/resources/bluetooth_internals/descriptor_list_item.html b/chrome/browser/resources/bluetooth_internals/descriptor_list_item.html
index 248f0697..d523f153 100644
--- a/chrome/browser/resources/bluetooth_internals/descriptor_list_item.html
+++ b/chrome/browser/resources/bluetooth_internals/descriptor_list_item.html
@@ -4,29 +4,10 @@
   }
 
   .info-container > h4,
-  .info-container > div {
+  .info-container > object-field-set,
+  .info-container > value-control {
     margin: 1em;
   }
-
-  /* Value Control */
-  .value-control > div {
-    display: flex;
-    margin: 4px 0;
-  }
-
-  .value-control > div > input {
-    flex-grow: 1;
-  }
-
-  .value-control > div:nth-of-type(2) {
-    justify-content: flex-end;
-  }
-
-  h3 {
-    font-weight: normal;
-    line-height: 1;
-    user-select: none;
-  }
 </style>
 <expandable-list-item>
   <div slot="brief-content">
@@ -37,5 +18,6 @@
     <h4>Descriptor Info</h4>
     <object-field-set show-all hidden></object-field-set>
     <h4>Value</h4>
+    <value-control hidden></value-control>
   </div>
 </expandable-list-item>
diff --git a/chrome/browser/resources/bluetooth_internals/descriptor_list_item.js b/chrome/browser/resources/bluetooth_internals/descriptor_list_item.js
index d23aead..cc08312 100644
--- a/chrome/browser/resources/bluetooth_internals/descriptor_list_item.js
+++ b/chrome/browser/resources/bluetooth_internals/descriptor_list_item.js
@@ -4,12 +4,12 @@
 
 import './expandable_list_item.js';
 import './object_fieldset.js';
+import './value_control.js';
 
 import {CustomElement} from 'chrome://resources/js/custom_element.js';
 
 import {getTemplate} from './descriptor_list_item.html.js';
 import {DescriptorInfo} from './device.mojom-webui.js';
-import {ValueControl} from './value_control.js';
 
 /** Property names for the DescriptorInfo fieldset */
 const INFO_PROPERTY_NAMES = {
@@ -33,9 +33,6 @@
     this.serviceId_ = '';
     /** @private {string} */
     this.characteristicId_ = '';
-
-    /** @private {!ValueControl} */
-    this.valueControl_ = new ValueControl();
   }
 
   connectedCallback() {
@@ -55,21 +52,20 @@
     });
     fieldSet.hidden = false;
 
-    this.valueControl_.load({
+    const valueControl = this.shadowRoot.querySelector('value-control');
+    valueControl.dataset.options = JSON.stringify({
       deviceAddress: this.deviceAddress_,
       serviceId: this.serviceId_,
       characteristicId: this.characteristicId_,
       descriptorId: this.info.id,
     });
+    valueControl.hidden = false;
 
     const descriptorHeaderValue =
         this.shadowRoot.querySelector('.header-value');
     descriptorHeaderValue.textContent = this.info.uuid.uuid;
 
     const infoDiv = this.shadowRoot.querySelector('.info-container');
-    infoDiv.insertBefore(
-        this.valueControl_,
-        this.shadowRoot.querySelector('characteristic-list'));
   }
 }
 
diff --git a/chrome/browser/resources/bluetooth_internals/service_list_item.html b/chrome/browser/resources/bluetooth_internals/service_list_item.html
index f07c4be6..cfdfd64 100644
--- a/chrome/browser/resources/bluetooth_internals/service_list_item.html
+++ b/chrome/browser/resources/bluetooth_internals/service_list_item.html
@@ -4,7 +4,7 @@
   }
 
   .info-container > h4,
-  .info-container > div {
+  .info-container > object-field-set {
     margin: 1em;
   }
 
diff --git a/chrome/browser/resources/bluetooth_internals/value_control.html b/chrome/browser/resources/bluetooth_internals/value_control.html
new file mode 100644
index 0000000..0915df3
--- /dev/null
+++ b/chrome/browser/resources/bluetooth_internals/value_control.html
@@ -0,0 +1,37 @@
+<style>
+  :host {
+    display: block;
+  }
+
+  div {
+    display: flex;
+    margin: 4px 0;
+  }
+
+  div > input {
+    flex-grow: 1;
+  }
+
+  div:nth-of-type(2) {
+    justify-content: flex-end;
+  }
+
+  h3 {
+    font-weight: normal;
+    line-height: 1;
+    user-select: none;
+  }
+</style>
+<h3>Value cannot be read or written</h3>
+<div>
+  <input></input>
+  <select>
+    <option value="Hexadecimal">Hexadecimal</option>
+    <option value="UTF-8">UTF-8</option>
+    <option value="Decimal">Decimal</option>
+  </select>
+</div>
+<div>
+  <button class="read">Read</button>
+  <button class="write">Write</button>
+</div>
diff --git a/chrome/browser/resources/bluetooth_internals/value_control.js b/chrome/browser/resources/bluetooth_internals/value_control.js
index 9cfd6c7f..bb5b66b 100644
--- a/chrome/browser/resources/bluetooth_internals/value_control.js
+++ b/chrome/browser/resources/bluetooth_internals/value_control.js
@@ -7,11 +7,12 @@
  */
 
 import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
-import {define as crUiDefine} from 'chrome://resources/js/cr/ui.js';
+import {CustomElement} from 'chrome://resources/js/custom_element.js';
 
 import {GattResult, Property} from './device.mojom-webui.js';
 import {connectToDevice} from './device_broker.js';
 import {showSnackbar, SnackbarType} from './snackbar.js';
+import {getTemplate} from './value_control.html.js';
 
 /**
  * @typedef {{
@@ -205,19 +206,21 @@
  * @constructor
  * @extends {HTMLDivElement}
  */
-export const ValueControl = crUiDefine('div');
+export class ValueControlElement extends CustomElement {
+  static get is() {
+    return 'value-control';
+  }
 
-ValueControl.prototype = {
-  __proto__: HTMLDivElement.prototype,
+  static get template() {
+    return getTemplate();
+  }
 
-  /**
-   * Decorates the element as a ValueControl. Creates the layout for the value
-   * control by creating a text input, select element, and two buttons for
-   * read/write requests. Event handlers are attached and references to these
-   * elements are stored for later use.
-   */
-  decorate() {
-    this.classList.add('value-control');
+  static get observedAttributes() {
+    return ['data-value', 'data-options'];
+  }
+
+  constructor() {
+    super();
 
     /** @private {!Value} */
     this.value_ = new Value([]);
@@ -231,11 +234,21 @@
     this.descriptorId_ = null;
     /** @private {number} */
     this.properties_ = Number.MAX_SAFE_INTEGER;
+    /** @private {!HTMLInputElement} */
+    this.valueInput_ = this.shadowRoot.querySelector('input');
+    /** @private {!HTMLSelectElement} */
+    this.typeSelect_ = this.shadowRoot.querySelector('select');
+    /** @private {!HTMLButtonElement} */
+    this.writeBtn_ = this.shadowRoot.querySelector('button.write');
+    /** @private {!HTMLButtonElement} */
+    this.readBtn_ = this.shadowRoot.querySelector('button.read');
+    /** @private {!HTMLElement} */
+    this.unavailableMessage_ = this.shadowRoot.querySelector('h3');
+  }
 
-    this.unavailableMessage_ = document.createElement('h3');
-    this.unavailableMessage_.textContent = 'Value cannot be read or written.';
+  connectedCallback() {
+    this.classList.add('value-control');
 
-    this.valueInput_ = document.createElement('input');
     this.valueInput_.addEventListener('change', function() {
       try {
         this.value_.setAs(this.typeSelect_.value, this.valueInput_.value);
@@ -244,57 +257,43 @@
       }
     }.bind(this));
 
-    this.typeSelect_ = document.createElement('select');
-
-    Object.keys(ValueDataType).forEach(function(key) {
-      const type = ValueDataType[key];
-      const option = document.createElement('option');
-      option.value = type;
-      option.text = type;
-      this.typeSelect_.add(option);
-    }, this);
-
     this.typeSelect_.addEventListener('change', this.redraw.bind(this));
 
-    const inputDiv = document.createElement('div');
-    inputDiv.appendChild(this.valueInput_);
-    inputDiv.appendChild(this.typeSelect_);
-
-    this.readBtn_ = document.createElement('button');
-    this.readBtn_.textContent = 'Read';
     this.readBtn_.addEventListener('click', this.readValue_.bind(this));
 
-    this.writeBtn_ = document.createElement('button');
-    this.writeBtn_.textContent = 'Write';
     this.writeBtn_.addEventListener('click', this.writeValue_.bind(this));
 
-    const buttonsDiv = document.createElement('div');
-    buttonsDiv.appendChild(this.readBtn_);
-    buttonsDiv.appendChild(this.writeBtn_);
-
-    this.appendChild(this.unavailableMessage_);
-    this.appendChild(inputDiv);
-    this.appendChild(buttonsDiv);
-  },
+    this.redraw();
+  }
 
   /**
    * Sets the settings used by the value control and redraws the control to
    * match the read/write settings in |options.properties|. If properties
    * are not provided, no restrictions on reading/writing are applied.
-   * @param {!ValueLoadOptions} options
    */
-  load(options) {
-    this.deviceAddress_ = options.deviceAddress;
-    this.serviceId_ = options.serviceId;
-    this.characteristicId_ = options.characteristicId;
-    this.descriptorId_ = options.descriptorId;
+  attributeChangedCallback(name, oldValue, newValue) {
+    assert(name === 'data-value' || name === 'data-options');
 
-    if (options.properties) {
-      this.properties_ = options.properties;
+    if (oldValue === newValue) {
+      return;
+    }
+
+    if (name === 'data-options') {
+      const options = JSON.parse(newValue);
+      this.deviceAddress_ = options.deviceAddress;
+      this.serviceId_ = options.serviceId;
+      this.characteristicId_ = options.characteristicId;
+      this.descriptorId_ = options.descriptorId;
+
+      if (options.properties) {
+        this.properties_ = options.properties;
+      }
+    } else {
+      this.value_.setArray(JSON.parse(newValue));
     }
 
     this.redraw();
-  },
+  }
 
   /**
    * Redraws the value control with updated layout depending on the
@@ -315,7 +314,7 @@
     }
 
     this.valueInput_.value = this.value_.getAs(this.typeSelect_.value);
-  },
+  }
 
   /**
    * Sets the value of the control.
@@ -324,7 +323,7 @@
   setValue(value) {
     this.value_.setArray(value);
     this.redraw();
-  },
+  }
 
   /**
    * Gets an error string describing the given |result| code.
@@ -337,7 +336,7 @@
     return Object.keys(GattResult).find(function(key) {
       return GattResult[key] === result;
     });
-  },
+  }
 
   /**
    * Called when the read button is pressed. Connects to the device and
@@ -374,7 +373,7 @@
               this.deviceAddress_ + ': ' + errorString, SnackbarType.ERROR,
               'Retry', this.readValue_.bind(this));
         }.bind(this));
-  },
+  }
 
   /**
    * Called when the write button is pressed. Connects to the device and
@@ -412,5 +411,7 @@
               this.deviceAddress_ + ': ' + errorString, SnackbarType.ERROR,
               'Retry', this.writeValue_.bind(this));
         }.bind(this));
-  },
-};
+  }
+}
+
+customElements.define('value-control', ValueControlElement);
diff --git a/chrome/browser/resources/bookmarks/command_manager.ts b/chrome/browser/resources/bookmarks/command_manager.ts
index 04cbc3b..c7035b0 100644
--- a/chrome/browser/resources/bookmarks/command_manager.ts
+++ b/chrome/browser/resources/bookmarks/command_manager.ts
@@ -21,7 +21,7 @@
 import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 import {getToastManager} from 'chrome://resources/cr_elements/cr_toast/cr_toast_manager.js';
 import {assert, assertNotReached} from 'chrome://resources/js/assert_ts.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {KeyboardShortcutList} from 'chrome://resources/js/keyboard_shortcut_list.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
diff --git a/chrome/browser/resources/bookmarks/item.ts b/chrome/browser/resources/bookmarks/item.ts
index ce8691f..7dc6d33 100644
--- a/chrome/browser/resources/bookmarks/item.ts
+++ b/chrome/browser/resources/bookmarks/item.ts
@@ -10,7 +10,7 @@
 
 import {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import {assert} from 'chrome://resources/js/assert_ts.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {focusWithoutInk} from 'chrome://resources/js/focus_without_ink.js';
 import {getFaviconForPageURL} from 'chrome://resources/js/icon.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
diff --git a/chrome/browser/resources/bookmarks/list.ts b/chrome/browser/resources/bookmarks/list.ts
index dc040692..3ae3147 100644
--- a/chrome/browser/resources/bookmarks/list.ts
+++ b/chrome/browser/resources/bookmarks/list.ts
@@ -10,7 +10,7 @@
 
 import {getInstance as getAnnouncerInstance} from 'chrome://resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
 import {assert} from 'chrome://resources/js/assert_ts.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.js';
 import {ListPropertyUpdateMixin} from 'chrome://resources/cr_elements/list_property_update_mixin.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/common/testing/fake_settings_private.js b/chrome/browser/resources/chromeos/accessibility/common/testing/fake_settings_private.js
index 87311dc8..01b903b 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/testing/fake_settings_private.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/testing/fake_settings_private.js
@@ -69,7 +69,9 @@
     assertEquals(Array.isArray(value), Array.isArray(pref.value));
 
     if (this.failNextSetPref_) {
-      callback(false);
+      if (callback) {
+        callback(false);
+      }
       this.failNextSetPref_ = false;
       return;
     }
@@ -77,8 +79,9 @@
 
     const changed = JSON.stringify(pref.value) !== JSON.stringify(value);
     pref.value = deepCopy(value);
-    callback(true);
-
+    if (callback) {
+      callback(true);
+    }
     // Like chrome.settingsPrivate, send a notification when prefs change.
     if (changed) {
       this.sendPrefChanges([{key, value}]);
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_enhanced_voices_test.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_enhanced_voices_test.js
index 3346a21..2c6cc9a 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_enhanced_voices_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_enhanced_voices_test.js
@@ -45,7 +45,7 @@
   // Sets the policy to allow or disallow the network voices.
   setEnhancedNetworkVoicesPolicy(allowed) {
     chrome.settingsPrivate.setPref(
-        PrefsManager.ENHANCED_VOICES_POLICY_KEY, allowed, '', () => {});
+        PrefsManager.ENHANCED_VOICES_POLICY_KEY, allowed);
   }
 };
 
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_mouse_selection_test.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_mouse_selection_test.js
index 1b25937..221d217 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_mouse_selection_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_mouse_selection_test.js
@@ -330,7 +330,7 @@
       // Sometimes we get "Select-to-speak button" and sometimes
       // "Select-to-speak". Either is acceptable.
       this.assertEqualsCollapseWhitespace(
-          utterance.replace(/button/, ''), 'Select-to-speak');
+          utterance.replace(/button/, '').toLowerCase(), 'select-to-speak');
     })]);
 
     focusRingsCallback = this.newCallback((focusRings) => {
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_prefs_test.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_prefs_test.js
index cc47426..7914515 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_prefs_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_prefs_test.js
@@ -42,7 +42,6 @@
   // This must be done before setting STS rate and pitch for tests to work
   // properly.
   setGlobalRateAndPitch(rate, pitch) {
-    const unused = () => {};
     chrome.settingsPrivate.setPref('settings.tts.speech_rate', rate);
     chrome.settingsPrivate.setPref('settings.tts.speech_pitch', pitch);
   }
diff --git a/chrome/browser/resources/components/components.ts b/chrome/browser/resources/components/components.ts
index 3a65d972..0252672 100644
--- a/chrome/browser/resources/components/components.ts
+++ b/chrome/browser/resources/components/components.ts
@@ -5,8 +5,9 @@
 import './strings.m.js';
 
 import {assert} from 'chrome://resources/js/assert_ts.js';
-import {addWebUIListener, isChromeOS, sendWithPromise} from 'chrome://resources/js/cr.m.js';
+import {addWebUIListener, sendWithPromise} from 'chrome://resources/js/cr.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {isChromeOS} from 'chrome://resources/js/platform.js';
 import {$} from 'chrome://resources/js/util.js';
 
 declare global {
diff --git a/chrome/browser/resources/extensions/shortcut_util.ts b/chrome/browser/resources/extensions/shortcut_util.ts
index 30a0708..5a392fd7 100644
--- a/chrome/browser/resources/extensions/shortcut_util.ts
+++ b/chrome/browser/resources/extensions/shortcut_util.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {assertNotReached} from 'chrome://resources/js/assert_ts.js';
-import {isChromeOS, isMac} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS, isMac} from 'chrome://resources/js/platform.js';
 
 
 export enum Key {
diff --git a/chrome/browser/resources/ntp4/context_menu_handler.js b/chrome/browser/resources/ntp4/context_menu_handler.js
index e562b36d..b8f557b 100644
--- a/chrome/browser/resources/ntp4/context_menu_handler.js
+++ b/chrome/browser/resources/ntp4/context_menu_handler.js
@@ -11,7 +11,6 @@
 import {assertInstanceof} from 'chrome://resources/js/assert.js';
 import {NativeEventTarget as EventTarget} from 'chrome://resources/js/cr/event_target.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.js';
-import {isWindows, isLinux, isMac, isLacros} from 'chrome://resources/js/cr.m.js';
 import {dispatchPropertyChange} from 'chrome://resources/js/cr_deprecated.js';
 import {decorate} from 'chrome://resources/js/cr/ui.js';
 
@@ -112,7 +111,10 @@
     // On windows we might hide the menu in a right mouse button up and if
     // that is the case we wait some short period before we allow the menu
     // to be shown again.
-    this.hideTimestamp_ = isWindows ? Date.now() : 0;
+    this.hideTimestamp_ = 0;
+    // <if expr="is_win">
+    this.hideTimestamp_ = Date.now();
+    // </if>
 
     const ev = new Event('hide');
     ev.element = originalContextElement;
@@ -177,13 +179,14 @@
       case 'mousedown':
         if (!this.menu.contains(e.target)) {
           this.hideMenu();
-          if (e.button === 0 /* Left button */ &&
-              (isLinux || isMac || isLacros)) {
+          // <if expr="is_linux or is_macosx or chromeos_lacros">
+          if (e.button === 0 /* Left button */) {
             // Emulate Mac and Linux, which swallow native 'mousedown' events
             // that close menus.
             e.preventDefault();
             e.stopPropagation();
           }
+          // </if>
         } else {
           e.preventDefault();
         }
diff --git a/chrome/browser/resources/ntp4/guest_tab.html b/chrome/browser/resources/ntp4/guest_tab.html
index a89226c..9f9a9f63 100644
--- a/chrome/browser/resources/ntp4/guest_tab.html
+++ b/chrome/browser/resources/ntp4/guest_tab.html
@@ -10,7 +10,8 @@
 <div class="content">
   <h1>$i18n{guestTabHeading}</h1>
   <p>$i18n{guestTabDescription}</p>
-  <a class="learn-more-button" href="$i18n{learnMoreLink}">$i18n{learnMore}</a>
+  <a class="learn-more-button" href="$i18n{learnMoreLink}"
+      aria-label="$i18nPolymer{learnMoreA11yLabel}">$i18n{learnMore}</a>
 </div>
 </body>
 </html>
diff --git a/chrome/browser/resources/ntp4/incognito_tab.html b/chrome/browser/resources/ntp4/incognito_tab.html
index 8664461..6fa36bd 100644
--- a/chrome/browser/resources/ntp4/incognito_tab.html
+++ b/chrome/browser/resources/ntp4/incognito_tab.html
@@ -25,7 +25,8 @@
   <p id="subtitle">
     <span>$i18n{incognitoTabDescription}</span>
     <a class="learn-more-button"
-        href="$i18n{learnMoreLink}">$i18n{learnMore}</a>
+        href="$i18n{learnMoreLink}"
+        aria-label="$i18nPolymer{learnMoreA11yLabel}">$i18n{learnMore}</a>
   </p>
   <div id="bulletpoints-wrapper">
     <div class="bulletpoints first">$i18nRaw{incognitoTabFeatures}</div>
@@ -46,7 +47,8 @@
                aria-label="$i18n{cookieControlsTitle}"
                $i18n{cookieControlsToggleChecked} dark></cr-toggle>
   </div>
-  <a class="learn-more-button" href="$i18n{learnMoreLink}">$i18n{learnMore}</a>
+  <a class="learn-more-button" href="$i18n{learnMoreLink}"
+      aria-label="$i18nPolymer{learnMoreA11yLabel}">$i18n{learnMore}</a>
 </div>
 <script type="module" src="incognito_tab.js"></script>
 <!-- Lazy-load cr_elements to avoid performance penalty introduced by loading Polymer -->
diff --git a/chrome/browser/resources/ntp4/menu_button.js b/chrome/browser/resources/ntp4/menu_button.js
index 9837ff2f..c9e0a4f 100644
--- a/chrome/browser/resources/ntp4/menu_button.js
+++ b/chrome/browser/resources/ntp4/menu_button.js
@@ -8,7 +8,7 @@
 // patterns. Use Web Components in any new code.
 
 import {assert} from 'chrome://resources/js/assert.js';
-import {isWindows} from 'chrome://resources/js/cr.m.js';
+import {isWindows} from 'chrome://resources/js/platform.js';
 import {decorate, define as crUiDefine} from 'chrome://resources/js/cr/ui.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.js';
 
diff --git a/chrome/browser/resources/ntp4/tile_page.js b/chrome/browser/resources/ntp4/tile_page.js
index c4079e67..de79a1a 100644
--- a/chrome/browser/resources/ntp4/tile_page.js
+++ b/chrome/browser/resources/ntp4/tile_page.js
@@ -4,7 +4,6 @@
 
 import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
 import {dispatchSimpleEvent} from 'chrome://resources/js/cr_deprecated.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
 import {toCssPx} from 'chrome://resources/js/cr/ui.js';
 import {DragWrapper, DragWrapperDelegate} from 'chrome://resources/js/drag_wrapper.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.js';
@@ -1012,9 +1011,9 @@
     fadeDistance = Math.min(leftMargin, fadeDistance);
     // On Skia we don't use any fade because it works very poorly. See
     // http://crbug.com/99373
-    if (!isMac) {
-      fadeDistance = 1;
-    }
+    // <if expr="not is_macosx">
+    fadeDistance = 1;
+    // </if>
     const gradient = '-webkit-linear-gradient(left,' +
         'transparent, ' +
         'transparent ' + (leftMargin - fadeDistance) + 'px, ' +
diff --git a/chrome/browser/resources/predictors/predictors.ts b/chrome/browser/resources/predictors/predictors.ts
index cd941415..b6b76a2 100644
--- a/chrome/browser/resources/predictors/predictors.ts
+++ b/chrome/browser/resources/predictors/predictors.ts
@@ -6,7 +6,7 @@
 import './resource_prefetch_predictor.js';
 import 'chrome://resources/cr_elements/cr_tab_box/cr_tab_box.js';
 
-import {isWindows} from 'chrome://resources/js/cr.m.js';
+import {isWindows} from 'chrome://resources/js/platform.js';
 
 if (isWindows) {
   document.documentElement.setAttribute('os', 'win');
diff --git a/chrome/browser/resources/print_preview/data/local_parsers.ts b/chrome/browser/resources/print_preview/data/local_parsers.ts
index 8a139bc4..fff7f09 100644
--- a/chrome/browser/resources/print_preview/data/local_parsers.ts
+++ b/chrome/browser/resources/print_preview/data/local_parsers.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {assertNotReached} from 'chrome://resources/js/assert_ts.js';
-import {isChromeOS, isLacros} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS, isLacros} from 'chrome://resources/js/platform.js';
 
 import {Destination, DestinationOptionalParams, DestinationOrigin, PrinterType} from './destination.js';
 // <if expr="is_chromeos">
diff --git a/chrome/browser/resources/print_preview/ui/app.ts b/chrome/browser/resources/print_preview/ui/app.ts
index e74a6dc8..08a81015 100644
--- a/chrome/browser/resources/print_preview/ui/app.ts
+++ b/chrome/browser/resources/print_preview/ui/app.ts
@@ -9,7 +9,7 @@
 import './sidebar.js';
 
 import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
-import {isMac, isWindows} from 'chrome://resources/js/cr.m.js';
+import {isMac, isWindows} from 'chrome://resources/js/platform.js';
 import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.js';
 import {hasKeyModifiers} from 'chrome://resources/js/util.js';
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.ts b/chrome/browser/resources/settings/languages_page/languages_page.ts
index ab18cf3f..253de15 100644
--- a/chrome/browser/resources/settings/languages_page/languages_page.ts
+++ b/chrome/browser/resources/settings/languages_page/languages_page.ts
@@ -32,7 +32,7 @@
 import {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 import {assert} from 'chrome://resources/js/assert_ts.js';
-import {isWindows} from 'chrome://resources/js/cr.m.js';
+import {isWindows} from 'chrome://resources/js/platform.js';
 import {focusWithoutInk} from 'chrome://resources/js/focus_without_ink.js';
 import {I18nMixin, I18nMixinInterface} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {DomRepeatEvent, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/browser/resources/settings/people_page/people_page.ts b/chrome/browser/resources/settings/people_page/people_page.ts
index 747c2a83..310c90e 100644
--- a/chrome/browser/resources/settings/people_page/people_page.ts
+++ b/chrome/browser/resources/settings/people_page/people_page.ts
@@ -26,7 +26,7 @@
 import {convertImageSequenceToPng} from 'chrome://resources/ash/common/cr_picture/png.js';
 // </if>
 import {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
-import {isChromeOS} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS} from 'chrome://resources/js/platform.js';
 import {focusWithoutInk} from 'chrome://resources/js/focus_without_ink.js';
 import {getImage} from 'chrome://resources/js/icon.js';
 import {WebUiListenerMixin, WebUiListenerMixinInterface} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
diff --git a/chrome/browser/resources/side_panel/bookmarks/icons.html b/chrome/browser/resources/side_panel/bookmarks/icons.html
index 418df9c..ff017567 100644
--- a/chrome/browser/resources/side_panel/bookmarks/icons.html
+++ b/chrome/browser/resources/side_panel/bookmarks/icons.html
@@ -1,6 +1,9 @@
 <iron-iconset-svg name="bookmarks" size="16">
   <svg>
     <defs>
+      <g id="add-tab">
+        <path fill-rule="evenodd" d="M8 1.333A6.67 6.67 0 0 0 1.333 8 6.67 6.67 0 0 0 8 14.667 6.67 6.67 0 0 0 14.666 8 6.67 6.67 0 0 0 8 1.333Zm-.667 3.334v2.666H4.666v1.334h2.667v2.666h1.333V8.667h2.667V7.333H8.666V4.667H7.333ZM2.666 8A5.34 5.34 0 0 0 8 13.333 5.34 5.34 0 0 0 13.333 8 5.34 5.34 0 0 0 8 2.667 5.34 5.34 0 0 0 2.666 8Z"></path>
+      </g>
       <g id="check" viewBox="0 0 24 24">
         <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"></path>
       </g>
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html
index 1bf32836..d7767a0 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html
@@ -1,7 +1,7 @@
 <style include="cr-icons cr-hidden-style">
   :host {
     --back-button-size: 14px;
-    --label-border-color: var(--google-grey-300);
+    --border-color: var(--google-grey-300);
     --label-selected-color: var(--google-blue-50);
     --price-tracking-chip-background: var(--google-green-50);
     --price-tracking-discount-color: var(--google-green-800);
@@ -10,7 +10,7 @@
 
   @media (prefers-color-scheme: dark) {
     :host {
-      --label-border-color: var(--google-grey-700);
+      --border-color: var(--google-grey-700);
       --price-tracking-chip-background: var(--google-green-900);
       --price-tracking-discount-color: white;
       --price-tracking-original-color: var(--google-green-200);
@@ -46,6 +46,11 @@
     text-transform: uppercase;
   }
 
+  .bookmarks {
+    height: inherit;
+    overflow-y: auto;
+  }
+
   .chip-discounted {
     --power-bookmark-chip-background: var(--price-tracking-chip-background);
   }
@@ -53,6 +58,7 @@
   .column {
     display: flex;
     flex-direction: column;
+    height: 100%;
   }
 
   .dropdown-item {
@@ -67,6 +73,16 @@
     margin: 14px;
   }
 
+  .footer {
+    border-top: 1px solid var(--border-color);
+  }
+
+  .footer-button {
+    gap: 8px;
+    margin: 8px;
+    padding: 6px;
+  }
+
   .icon-arrow-back {
     -webkit-mask-image: var(--cr-icon-image);
     -webkit-mask-position: center;
@@ -84,7 +100,7 @@
   .label {
     align-items: center;
     background-color: transparent;
-    border: 1px solid var(--label-border-color);
+    border: 1px solid var(--border-color);
     border-radius: 4px;
     color: var(--cr-secondary-text-color);
     display: flex;
@@ -147,32 +163,40 @@
         aria-label="$i18n{sortMenuA11yLabel}"
         on-click="onShowSortMenuClicked_"></cr-icon-button>
   </div>
-  <template is="dom-repeat" items="[[shownBookmarks_]]">
-    <power-bookmark-row id="bookmark-[[item.id]]" bookmark="[[item]]"
-        description="[[getBookmarkDescription_(item, descriptions_.*)]]"
-        compact="[[compact_]]"
-        on-row-clicked="onRowClicked_">
-      <div slot="extra-content-container">
-        <template is="dom-if"
-            if="[[isPriceTracked_(item, showPriceTracking_)]]" restamp>
-          <template is="dom-if" if="[[showDiscountedPrice_(item)]]" restamp>
-            <power-bookmark-chip class="chip-discounted">
-              <iron-icon icon="bookmarks:price-tracking"
-                  class="icon-discounted"></iron-icon>
-              <div class="price-discounted">[[getCurrentPrice_(item)]]</div>
-              <div class="price-original">[[getPreviousPrice_(item)]]</div>
-            </power-bookmark-chip>
+  <div class="bookmarks">
+    <template is="dom-repeat" items="[[shownBookmarks_]]">
+      <power-bookmark-row id="bookmark-[[item.id]]" bookmark="[[item]]"
+          description="[[getBookmarkDescription_(item, descriptions_.*)]]"
+          compact="[[compact_]]"
+          on-row-clicked="onRowClicked_">
+        <div slot="extra-content-container">
+          <template is="dom-if"
+              if="[[isPriceTracked_(item, showPriceTracking_)]]" restamp>
+            <template is="dom-if" if="[[showDiscountedPrice_(item)]]" restamp>
+              <power-bookmark-chip class="chip-discounted">
+                <iron-icon icon="bookmarks:price-tracking"
+                    class="icon-discounted"></iron-icon>
+                <div class="price-discounted">[[getCurrentPrice_(item)]]</div>
+                <div class="price-original">[[getPreviousPrice_(item)]]</div>
+              </power-bookmark-chip>
+            </template>
+            <template is="dom-if" if="[[!showDiscountedPrice_(item)]]" restamp>
+              <power-bookmark-chip>
+                <iron-icon icon="bookmarks:price-tracking"></iron-icon>
+                <div>[[getCurrentPrice_(item)]]</div>
+              </power-bookmark-chip>
+            </template>
           </template>
-          <template is="dom-if" if="[[!showDiscountedPrice_(item)]]" restamp>
-            <power-bookmark-chip>
-              <iron-icon icon="bookmarks:price-tracking"></iron-icon>
-              <div>[[getCurrentPrice_(item)]]</div>
-            </power-bookmark-chip>
-          </template>
-        </template>
-      </div>
-    </power-bookmark-row>
-  </template>
+        </div>
+      </power-bookmark-row>
+    </template>
+  </div>
+  <div class="footer">
+    <cr-button class="footer-button" on-click="onAddTabClicked_">
+      <iron-icon icon="bookmarks:add-tab" class="label-icon"></iron-icon>
+      $i18n{addCurrentTab}
+    </cr-button>
+  </div>
 </div>
 
 <cr-action-menu id="sortMenu">
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
index 7c29f35..9a2d7ed 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
@@ -8,6 +8,7 @@
 import './power_bookmark_chip.js';
 import './power_bookmark_row.js';
 import '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import '//resources/cr_elements/cr_button/cr_button.js';
 import '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import '//resources/cr_elements/icons.html.js';
 
@@ -348,6 +349,10 @@
     this.activeSortIndex_ = event.model.index;
   }
 
+  private onAddTabClicked_() {
+    // TODO: Implement this
+  }
+
   /**
    * Whether the given price-tracked bookmark should display as if discounted.
    */
diff --git a/chrome/browser/resources/whats_new/whats_new_app.ts b/chrome/browser/resources/whats_new/whats_new_app.ts
index abaeb0e..e18dd49 100644
--- a/chrome/browser/resources/whats_new/whats_new_app.ts
+++ b/chrome/browser/resources/whats_new/whats_new_app.ts
@@ -7,7 +7,7 @@
 
 import {ClickInfo, Command} from 'chrome://resources/js/browser_command/browser_command.mojom-webui.js';
 import {BrowserCommandProxy} from 'chrome://resources/js/browser_command/browser_command_proxy.js';
-import {isChromeOS} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS} from 'chrome://resources/js/platform.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
index 43172fb2..9533653 100644
--- a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
@@ -69,20 +69,17 @@
   extension_info->set_install_time_msec(
       extension_prefs.GetInstallTime(extension.id()).ToJavaTime());
 
-  const base::DictionaryValue* signature =
-      extension_prefs.GetInstallSignature();
-  if (signature) {
-    std::unique_ptr<extensions::InstallSignature> signature_from_prefs =
-        extensions::InstallSignature::FromValue(*signature);
-    if (signature_from_prefs) {
-      if (base::Contains(signature_from_prefs->ids, extension_id)) {
-        extension_info->set_has_signature_validation(true);
-        extension_info->set_signature_is_valid(true);
-      } else if (base::Contains(signature_from_prefs->invalid_ids,
-                                extension_id)) {
-        extension_info->set_has_signature_validation(true);
-        extension_info->set_signature_is_valid(false);
-      }
+  std::unique_ptr<extensions::InstallSignature> signature_from_prefs =
+      extensions::InstallSignature::FromDict(
+          extension_prefs.GetInstallSignature());
+  if (signature_from_prefs) {
+    if (base::Contains(signature_from_prefs->ids, extension_id)) {
+      extension_info->set_has_signature_validation(true);
+      extension_info->set_signature_is_valid(true);
+    } else if (base::Contains(signature_from_prefs->invalid_ids,
+                              extension_id)) {
+      extension_info->set_has_signature_validation(true);
+      extension_info->set_signature_is_valid(false);
     }
   }
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
index 30c4f34..48264ca 100644
--- a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
@@ -110,8 +110,7 @@
 
 void ExtensionTestingProfile::SetInstallSignature(
     extensions::InstallSignature signature) {
-  base::DictionaryValue signature_dict;
-  signature.ToValue(&signature_dict);
+  base::Value::Dict signature_dict = signature.ToDict();
   extension_prefs_->SetInstallSignature(&signature_dict);
 }
 
@@ -130,7 +129,7 @@
     SAFE_BROWSING_AND_EXTENDED_REPORTING,
   };
 
-  ExtensionDataCollectionTest() : profile_number_() {}
+  ExtensionDataCollectionTest() = default;
 
   void SetUp() override {
     testing::Test::SetUp();
@@ -188,7 +187,7 @@
   std::unique_ptr<TestingProfileManager> profile_manager_;
 
  private:
-  int profile_number_;
+  int profile_number_ = 0;
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   ash::ScopedCrosSettingsTestHelper cros_settings_test_helper_;
diff --git a/chrome/browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc b/chrome/browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc
index 2e99c13..1c97e3a 100644
--- a/chrome/browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc
+++ b/chrome/browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc
@@ -98,7 +98,7 @@
 
   // Kill the OTR profile. The signal collection resumes.
   EXPECT_CALL(segmentation_platform_service_, EnableMetrics(true)).Times(1);
-  ProfileDestroyer::DestroyProfileWhenAppropriate(profile1_otr1);
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(profile1_otr1);
 
   // Start again another OTR profile. The signal collection should stop.
   EXPECT_CALL(segmentation_platform_service_, EnableMetrics(false)).Times(1);
@@ -119,13 +119,13 @@
   // Start killing the OTR profiles one by one. The signal collection resumes
   // only after the last one is killed.
   EXPECT_CALL(segmentation_platform_service_, EnableMetrics(_)).Times(0);
-  ProfileDestroyer::DestroyProfileWhenAppropriate(profile1_otr2_secondary);
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(profile1_otr2_secondary);
 
   EXPECT_CALL(segmentation_platform_service_, EnableMetrics(_)).Times(0);
-  ProfileDestroyer::DestroyProfileWhenAppropriate(profile1_otr2);
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(profile1_otr2);
 
   EXPECT_CALL(segmentation_platform_service_, EnableMetrics(true)).Times(1);
-  ProfileDestroyer::DestroyProfileWhenAppropriate(profile2_otr1);
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(profile2_otr1);
 
   // Cleanup profiles.
   testing_profile_manager_->DeleteTestingProfile(kProfile1);
@@ -145,7 +145,7 @@
 
   // Kill the OTR profile. The signal collection resumes.
   EXPECT_CALL(segmentation_platform_service_, EnableMetrics(true)).Times(1);
-  ProfileDestroyer::DestroyProfileWhenAppropriate(otr);
+  ProfileDestroyer::DestroyOTRProfileWhenAppropriate(otr);
 
   // Cleanup profiles.
   EXPECT_CALL(segmentation_platform_service_, EnableMetrics(_)).Times(0);
diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
index a004774..e1692f33 100644
--- a/chrome/browser/supervised_user/supervised_user_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_service.cc
@@ -763,7 +763,7 @@
 #if DCHECK_IS_ON()
   return "Supervised User Service";
 #else
-  IMMEDIATE_CRASH();
+  base::ImmediateCrash();
 #endif
 }
 
diff --git a/chrome/browser/sync/sync_service_factory_unittest.cc b/chrome/browser/sync/sync_service_factory_unittest.cc
index 33714acf..a7deb37 100644
--- a/chrome/browser/sync/sync_service_factory_unittest.cc
+++ b/chrome/browser/sync/sync_service_factory_unittest.cc
@@ -84,7 +84,7 @@
 
   // Returns the collection of default datatypes.
   syncer::ModelTypeSet DefaultDatatypes() {
-    static_assert(43 == syncer::GetNumModelTypes(),
+    static_assert(44 == syncer::GetNumModelTypes(),
                   "When adding a new type, you probably want to add it here as "
                   "well (assuming it is already enabled).");
 
diff --git a/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc b/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
index 01bc140d..77fbc17 100644
--- a/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
@@ -28,7 +28,7 @@
 namespace {
 
 syncer::ModelTypeSet AllowedTypesInStandaloneTransportMode() {
-  static_assert(43 == syncer::GetNumModelTypes(),
+  static_assert(44 == syncer::GetNumModelTypes(),
                 "Add new types below if they run in transport mode");
   // Only some special allowlisted types (and control types) are allowed in
   // standalone transport mode.
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java
index 679c7dfb..7f69561 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.toolbar;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import org.chromium.base.Callback;
@@ -25,6 +26,7 @@
 import org.chromium.components.profile_metrics.BrowserProfileType;
 import org.chromium.content_public.browser.LoadCommittedDetails;
 import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsObserver;
 import org.chromium.content_public.common.ContentUrlConstants;
 import org.chromium.ui.base.PageTransition;
@@ -44,6 +46,8 @@
     private final ObservableSupplierImpl<Boolean> mBackPressChangedSupplier =
             new ObservableSupplierImpl<>();
     private Tab mOldTab;
+    @Nullable
+    private WebContentsObserver mWebContentsBackPressObserver;
     private final Callback<BottomControlsCoordinator> mBottomControlsCoordinatorAvailableCallback;
 
     /**
@@ -177,11 +181,64 @@
             mActivityTabSupplier.removeObserver(mOnActivityTabCallback);
             mBottomControlsCoordinatorSupplier.removeObserver(
                     mBottomControlsCoordinatorAvailableCallback);
+            if (mWebContentsBackPressObserver != null) mWebContentsBackPressObserver.destroy();
         }
     }
 
     private void onActivityTabChanged(@Nullable Tab tab) {
-        final WebContentsObserver webContentsObserver = new WebContentsObserver() {
+        final TabObserver tabObserver = new EmptyTabObserver() {
+            @Override
+            public void webContentsWillSwap(Tab tab) {
+                if (tab.getWebContents() != null) {
+                    removeWebContentsBackPressObserver(tab.getWebContents());
+                }
+                onBackPressedChanged();
+            }
+
+            @Override
+            public void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad) {
+                if (tab.getWebContents() != null) {
+                    addWebContentsBackPressObserver(tab.getWebContents());
+                }
+                onBackPressedChanged();
+            }
+
+            @Override
+            public void onDestroyed(Tab tab) {
+                if (tab.getWebContents() != null) {
+                    removeWebContentsBackPressObserver(tab.getWebContents());
+                }
+                onBackPressedChanged();
+            }
+
+            @Override
+            public void onContentChanged(Tab tab) {
+                if (tab.getWebContents() != null) {
+                    addWebContentsBackPressObserver(tab.getWebContents());
+                }
+                onBackPressedChanged();
+            }
+        };
+
+        if (mOldTab != null) {
+            mOldTab.removeObserver(tabObserver);
+            if (mOldTab.getWebContents() != null) {
+                removeWebContentsBackPressObserver(mOldTab.getWebContents());
+            }
+        }
+        if (tab != null) {
+            if (tab.getWebContents() != null) {
+                addWebContentsBackPressObserver(tab.getWebContents());
+            }
+            tab.addObserver(tabObserver);
+            mOldTab = tab;
+        }
+        onBackPressedChanged();
+    }
+
+    private void addWebContentsBackPressObserver(@NonNull WebContents webContents) {
+        if (mWebContentsBackPressObserver != null) mWebContentsBackPressObserver.destroy();
+        mWebContentsBackPressObserver = new WebContentsObserver(webContents) {
             @Override
             public void navigationEntryCommitted(LoadCommittedDetails details) {
                 onBackPressedChanged();
@@ -202,55 +259,11 @@
                 onBackPressedChanged();
             }
         };
+    }
 
-        final TabObserver mTabObserver = new EmptyTabObserver() {
-            @Override
-            public void webContentsWillSwap(Tab tab) {
-                if (tab.getWebContents() != null) {
-                    tab.getWebContents().removeObserver(webContentsObserver);
-                }
-                onBackPressedChanged();
-            }
-
-            @Override
-            public void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad) {
-                if (tab.getWebContents() != null) {
-                    tab.getWebContents().addObserver(webContentsObserver);
-                }
-                onBackPressedChanged();
-            }
-
-            @Override
-            public void onDestroyed(Tab tab) {
-                if (tab.getWebContents() != null) {
-                    tab.getWebContents().removeObserver(webContentsObserver);
-                }
-                onBackPressedChanged();
-            }
-
-            @Override
-            public void onContentChanged(Tab tab) {
-                if (tab.getWebContents() != null) {
-                    tab.getWebContents().addObserver(webContentsObserver);
-                }
-                onBackPressedChanged();
-            }
-        };
-
-        if (mOldTab != null) {
-            mOldTab.removeObserver(mTabObserver);
-            if (mOldTab.getWebContents() != null) {
-                mOldTab.getWebContents().removeObserver(webContentsObserver);
-            }
-        }
-        if (tab != null) {
-            if (tab.getWebContents() != null) {
-                tab.getWebContents().addObserver(webContentsObserver);
-            }
-            tab.addObserver(mTabObserver);
-            mOldTab = tab;
-        }
-        onBackPressedChanged();
+    private void removeWebContentsBackPressObserver(@NonNull WebContents webContents) {
+        webContents.removeObserver(mWebContentsBackPressObserver);
+        if (mWebContentsBackPressObserver != null) mWebContentsBackPressObserver.destroy();
     }
 
     private void onBottomControlsCoordinatorAvailable(
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/settings/AdaptiveToolbarPreferenceFragmentTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/settings/AdaptiveToolbarPreferenceFragmentTest.java
index 2384198..fe48819f 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/settings/AdaptiveToolbarPreferenceFragmentTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/settings/AdaptiveToolbarPreferenceFragmentTest.java
@@ -34,6 +34,7 @@
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.SettingsFeatureList;
 import org.chromium.components.browser_ui.widget.RadioButtonWithDescription;
 
 /**
@@ -42,7 +43,8 @@
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 @EnableFeatures({ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR_CUSTOMIZATION_V2})
-@DisableFeatures({ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR})
+@DisableFeatures({ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR,
+        SettingsFeatureList.HIGHLIGHT_MANAGED_PREF_DISCLAIMER_ANDROID})
 public class AdaptiveToolbarPreferenceFragmentTest {
     @Rule
     public TestRule mProcessor = new Features.JUnitProcessor();
@@ -170,4 +172,4 @@
         Assert.assertTrue(
                 "Buttons except " + buttonTitle + " should be unchecked.", isRestUnchecked(type));
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/browser/ui/app_list/app_list_sort_browsertest.cc b/chrome/browser/ui/app_list/app_list_sort_browsertest.cc
index 543eef5b..27e4cdd 100644
--- a/chrome/browser/ui/app_list/app_list_sort_browsertest.cc
+++ b/chrome/browser/ui/app_list/app_list_sort_browsertest.cc
@@ -854,6 +854,7 @@
   // Transition to clamshell mode - verify that the bubble launcher does not
   // have undo toast, and that the order of apps is still sorted.
   ash::ShellTestApi().SetTabletModeEnabledForTest(false);
+  WaitForAppListTransitionAnimation();
 
   ash::AcceleratorController::Get()->PerformActionIfEnabled(
       ash::TOGGLE_APP_LIST, {});
@@ -1071,6 +1072,7 @@
             std::vector<std::string>({app1_id_, app2_id_, app3_id_}));
 
   ash::ShellTestApi().SetTabletModeEnabledForTest(false);
+  WaitForAppListTransitionAnimation();
   ash::AcceleratorController::Get()->PerformActionIfEnabled(
       ash::TOGGLE_APP_LIST, {});
   app_list_test_api_.WaitForBubbleWindow(/*wait_for_opening_animation=*/true);
diff --git a/chrome/browser/ui/app_list/search/app_service_app_result.cc b/chrome/browser/ui/app_list/search/app_service_app_result.cc
index 06f670e2..a6e4018 100644
--- a/chrome/browser/ui/app_list/search/app_service_app_result.cc
+++ b/chrome/browser/ui/app_list/search/app_service_app_result.cc
@@ -270,11 +270,6 @@
         LargeIconServiceFactory::GetForBrowserContext(profile);
     UpdateContinueReadingFavicon(/*continue_to_google_server=*/true);
   }
-
-  // Set these values to make sure that the chip will show up
-  // in the proper position.
-  SetDisplayIndex(ash::SearchResultDisplayIndex::kFirstIndex);
-  SetDisplayType(ash::SearchResultDisplayType::kChip);
 }
 
 void AppServiceAppResult::UpdateContinueReadingFavicon(
diff --git a/chrome/browser/ui/app_list/search/chrome_search_result.cc b/chrome/browser/ui/app_list/search/chrome_search_result.cc
index a441c62..53257ea 100644
--- a/chrome/browser/ui/app_list/search/chrome_search_result.cc
+++ b/chrome/browser/ui/app_list/search/chrome_search_result.cc
@@ -154,16 +154,6 @@
   SetSearchResultMetadata();
 }
 
-void ChromeSearchResult::SetDisplayIndex(DisplayIndex display_index) {
-  metadata_->display_index = display_index;
-  SetSearchResultMetadata();
-}
-
-void ChromeSearchResult::SetPositionPriority(float position_priority) {
-  metadata_->position_priority = position_priority;
-  SetSearchResultMetadata();
-}
-
 void ChromeSearchResult::SetIsOmniboxSearch(bool is_omnibox_search) {
   metadata_->is_omnibox_search = is_omnibox_search;
   SetSearchResultMetadata();
diff --git a/chrome/browser/ui/app_list/search/chrome_search_result.h b/chrome/browser/ui/app_list/search/chrome_search_result.h
index 5de33fc..c32f661 100644
--- a/chrome/browser/ui/app_list/search/chrome_search_result.h
+++ b/chrome/browser/ui/app_list/search/chrome_search_result.h
@@ -41,7 +41,6 @@
   using Tags = ash::SearchResultTags;
   using Action = ash::SearchResultAction;
   using Actions = ash::SearchResultActions;
-  using DisplayIndex = ash::SearchResultDisplayIndex;
   using IconInfo = ash::SearchResultIconInfo;
   using IconShape = ash::SearchResultIconShape;
   using TextItem = ash::SearchResultTextItem;
@@ -96,8 +95,6 @@
     return metadata_->result_type;
   }
   MetricsType metrics_type() const { return metadata_->metrics_type; }
-  DisplayIndex display_index() const { return metadata_->display_index; }
-  float position_priority() const { return metadata_->position_priority; }
   const Actions& actions() const { return metadata_->actions; }
   double display_score() const { return metadata_->display_score; }
   bool is_recommendation() const { return metadata_->is_recommendation; }
@@ -131,8 +128,6 @@
   void SetDisplayType(DisplayType display_type);
   void SetResultType(ResultType result_type);
   void SetMetricsType(MetricsType metrics_type);
-  void SetDisplayIndex(DisplayIndex display_index);
-  void SetPositionPriority(float position_priority);
   void SetDisplayScore(double display_score);
   void SetActions(const Actions& actions);
   void SetIsOmniboxSearch(bool is_omnibox_search);
diff --git a/chrome/browser/ui/app_list/search/help_app_search_browsertest.cc b/chrome/browser/ui/app_list/search/help_app_search_browsertest.cc
index 8db26f3..b624f29 100644
--- a/chrome/browser/ui/app_list/search/help_app_search_browsertest.cc
+++ b/chrome/browser/ui/app_list/search/help_app_search_browsertest.cc
@@ -72,6 +72,15 @@
     results_waiter.Wait();
   }
 
+  // Returns the first published continue section result.
+  const ChromeSearchResult* FindLeadingContinueSectionResult() {
+    for (const auto* result : PublishedResults()) {
+      if (result->display_type() == ash::SearchResultDisplayType::kContinue)
+        return result;
+    }
+    return nullptr;
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -86,21 +95,34 @@
 
     AppListClientImpl::GetInstance()->UpdateProfile();
 
+    app_list::SearchController* search_controller =
+        AppListClientImpl::GetInstance()->search_controller();
+
     // For release note chips to show in productivity launcher, continue section
     // needs to be visible, which will only be the case if other continue
     // results exist - create search provider to inject continue section
     // results.
-    auto continue_section_provider =
-        std::make_unique<TestContinueFilesSearchProvider>();
-    continue_section_provider_ = continue_section_provider.get();
-    app_list::SearchController* search_controller =
-        AppListClientImpl::GetInstance()->search_controller();
-    size_t group_id = search_controller->AddGroup(10);
-    search_controller->AddProvider(group_id,
-                                   std::move(continue_section_provider));
+    // Adds providers for both local and drive files to test that results
+    // provided by help app are ranked before both types of files.
+    auto local_continue_section_provider =
+        std::make_unique<TestContinueFilesSearchProvider>(
+            /*for_drive_files=*/false);
+    local_continue_section_provider_ = local_continue_section_provider.get();
+    EXPECT_EQ(1u, search_controller->ReplaceProvidersForResultTypeForTest(
+                      local_continue_section_provider_->ResultType(),
+                      std::move(local_continue_section_provider)));
+
+    auto drive_continue_section_provider =
+        std::make_unique<TestContinueFilesSearchProvider>(
+            /*for_drive_files=*/true);
+    drive_continue_section_provider_ = drive_continue_section_provider.get();
+    EXPECT_EQ(1u, search_controller->ReplaceProvidersForResultTypeForTest(
+                      drive_continue_section_provider_->ResultType(),
+                      std::move(drive_continue_section_provider)));
   }
 
-  TestContinueFilesSearchProvider* continue_section_provider_ = nullptr;
+  TestContinueFilesSearchProvider* local_continue_section_provider_ = nullptr;
+  TestContinueFilesSearchProvider* drive_continue_section_provider_ = nullptr;
 };
 
 // Test that Help App shows up as Release notes if pref shows we have some times
@@ -119,11 +141,28 @@
   EXPECT_EQ(result->title(), l10n_util::GetStringUTF16(
                                  IDS_HELP_APP_WHATS_NEW_CONTINUE_TASK_TITLE));
   EXPECT_EQ(result->metrics_type(), ash::HELP_APP_UPDATES);
-  // Displayed in first position.
-  EXPECT_EQ(result->position_priority(), 1.0f);
   EXPECT_EQ(result->display_type(), DisplayType::kContinue);
 }
 
+// Tests that Help App release notes chip is shown before other continue section
+// items.
+IN_PROC_BROWSER_TEST_F(HelpAppSearchBrowserTest, ReleaseNoteChipRankedFirst) {
+  ash::SystemWebAppManager::GetForTest(GetProfile())
+      ->InstallSystemAppsForTesting();
+  GetProfile()->GetPrefs()->SetInteger(
+      prefs::kReleaseNotesSuggestionChipTimesLeftToShow, 3);
+
+  local_continue_section_provider_->set_count(5);
+  drive_continue_section_provider_->set_count(5);
+
+  ShowAppListAndWaitForHelpAppZeroStateResults();
+
+  auto* result = FindResult("help-app://updates");
+  ASSERT_TRUE(result);
+  EXPECT_EQ(result->display_type(), DisplayType::kContinue);
+  EXPECT_EQ(FindLeadingContinueSectionResult(), result);
+}
+
 // Test that the number of times the suggestion chip should show decreases when
 // the chip is shown.
 IN_PROC_BROWSER_TEST_F(HelpAppSearchBrowserTest,
@@ -135,8 +174,8 @@
   ash::AppListTestApi app_list_test_api;
   app_list_test_api.SetContinueSectionPrivacyNoticeAccepted();
 
-  if (continue_section_provider_)
-    continue_section_provider_->set_count(10);
+  local_continue_section_provider_->set_count(5);
+  drive_continue_section_provider_->set_count(5);
 
   ShowAppListAndWaitForHelpAppZeroStateResults();
 
@@ -160,8 +199,8 @@
   ash::AppListTestApi app_list_test_api;
   app_list_test_api.SetContinueSectionPrivacyNoticeAccepted();
 
-  if (continue_section_provider_)
-    continue_section_provider_->set_count(10);
+  local_continue_section_provider_->set_count(5);
+  drive_continue_section_provider_->set_count(5);
 
   SearchResultsChangedWaiter results_waiter(
       GetClient()->search_controller(),
@@ -258,8 +297,6 @@
 
   EXPECT_EQ(base::UTF16ToASCII(result->title()), "Fix connection problems");
   EXPECT_EQ(base::UTF16ToASCII(result->details()), "Help");
-  // No priority for position.
-  EXPECT_EQ(result->position_priority(), 0);
   EXPECT_EQ(result->display_type(), DisplayType::kList);
 
   // Open the search result. This should open the help app at the expected url
@@ -310,8 +347,6 @@
   ASSERT_TRUE(result);
   // Has regular app name as title.
   EXPECT_EQ(base::UTF16ToASCII(result->title()), "Explore");
-  // No priority for position.
-  EXPECT_EQ(result->position_priority(), 0);
 }
 
 IN_PROC_BROWSER_TEST_P(HelpAppSwaSearchBrowserTest, Launch) {
diff --git a/chrome/browser/ui/app_list/search/help_app_zero_state_provider.cc b/chrome/browser/ui/app_list/search/help_app_zero_state_provider.cc
index 4b7729bd..35472d0 100644
--- a/chrome/browser/ui/app_list/search/help_app_zero_state_provider.cc
+++ b/chrome/browser/ui/app_list/search/help_app_zero_state_provider.cc
@@ -83,10 +83,6 @@
   SetTitle(title);
   if (!details.empty())
     SetDetails(details);
-  // Show this in the first position, in front of any other chips that may be
-  // also claiming the first slot.
-  SetDisplayIndex(DisplayIndex::kFirstIndex);
-  SetPositionPriority(1.0f);
   SetResultType(ResultType::kZeroStateHelpApp);
   SetDisplayType(display_type);
   // Some chips have different metrics types.
diff --git a/chrome/browser/ui/app_list/search/mixer.cc b/chrome/browser/ui/app_list/search/mixer.cc
index 1f5069a8..66f3843 100644
--- a/chrome/browser/ui/app_list/search/mixer.cc
+++ b/chrome/browser/ui/app_list/search/mixer.cc
@@ -59,14 +59,6 @@
     : result(result), score(score) {}
 
 bool Mixer::SortData::operator<(const SortData& other) const {
-  // This data precedes (less than) |other| if it has specified display index or
-  // higher score.
-  ash::SearchResultDisplayIndex index1 = result->display_index();
-  ash::SearchResultDisplayIndex index2 = other.result->display_index();
-  // The |kUndefined| index is larger than other specified indexes.
-  if (index1 != index2)
-    return index1 < index2;
-
   return score > other.score;
 }
 
diff --git a/chrome/browser/ui/app_list/search/search_controller.h b/chrome/browser/ui/app_list/search/search_controller.h
index c901473..e5390c0 100644
--- a/chrome/browser/ui/app_list/search/search_controller.h
+++ b/chrome/browser/ui/app_list/search/search_controller.h
@@ -91,6 +91,15 @@
   virtual void AddProvider(size_t group_id,
                            std::unique_ptr<SearchProvider> provider) = 0;
 
+  // Removes, and deletes registered search providers that provide results for
+  // `result_type` and adds a new "test" provider.
+  // No-op if no providers for `result_type` were previously registered.
+  // Expects that `provider` provides results for `result_type`.
+  // Returns number of providers removed from the provider list.
+  virtual size_t ReplaceProvidersForResultTypeForTest(
+      ash::AppListSearchResultType result_type,
+      std::unique_ptr<SearchProvider> provider) = 0;
+
   // Update the controller with the given results. Used only if the categorical
   // search feature flag is enabled.
   virtual void SetResults(const SearchProvider* provider, Results results) = 0;
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl.cc b/chrome/browser/ui/app_list/search/search_controller_impl.cc
index a1a2f25..3480d09e 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_impl.cc
@@ -142,6 +142,13 @@
   providers_.emplace_back(std::move(provider));
 }
 
+size_t SearchControllerImpl::ReplaceProvidersForResultTypeForTest(
+    ash::AppListSearchResultType result_type,
+    std::unique_ptr<SearchProvider> provider) {
+  NOTIMPLEMENTED();
+  return 0u;
+}
+
 void SearchControllerImpl::SetResults(const SearchProvider* provider,
                                       Results results) {
   // TODO(crbug.com/1199206): Clean up this function as
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl.h b/chrome/browser/ui/app_list/search/search_controller_impl.h
index 1e0b82d..4318c284 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl.h
+++ b/chrome/browser/ui/app_list/search/search_controller_impl.h
@@ -66,6 +66,9 @@
   size_t AddGroup(size_t max_results) override;
   void AddProvider(size_t group_id,
                    std::unique_ptr<SearchProvider> provider) override;
+  size_t ReplaceProvidersForResultTypeForTest(
+      ash::AppListSearchResultType result_type,
+      std::unique_ptr<SearchProvider> provider) override;
   void SetResults(const SearchProvider* provider, Results results) override;
   void Publish() override;
   ChromeSearchResult* FindSearchResult(const std::string& result_id) override;
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl_new.cc b/chrome/browser/ui/app_list/search/search_controller_impl_new.cc
index f11bf98d..06ff3f2b1 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl_new.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_impl_new.cc
@@ -12,6 +12,7 @@
 #include "ash/public/cpp/app_list/app_list_types.h"
 #include "ash/public/cpp/tablet_mode.h"
 #include "base/bind.h"
+#include "base/containers/cxx20_erase.h"
 #include "base/metrics/metrics_hashes.h"
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
@@ -216,6 +217,27 @@
   providers_.emplace_back(std::move(provider));
 }
 
+size_t SearchControllerImplNew::ReplaceProvidersForResultTypeForTest(
+    ash::AppListSearchResultType result_type,
+    std::unique_ptr<SearchProvider> new_provider) {
+  DCHECK_EQ(result_type, new_provider->ResultType());
+
+  size_t removed_providers = base::EraseIf(
+      providers_, [&](const std::unique_ptr<SearchProvider>& provider) {
+        return provider->ResultType() == result_type;
+      });
+  if (!removed_providers)
+    return 0u;
+  DCHECK_EQ(1u, removed_providers);
+
+  if (ash::IsZeroStateResultType(result_type))
+    total_zero_state_blockers_ -= removed_providers;
+
+  // Note that `group_id` is not used by this search controller implementation.
+  AddProvider(/*group_id=*/-1, std::move(new_provider));
+  return removed_providers;
+}
+
 void SearchControllerImplNew::SetResults(const SearchProvider* provider,
                                          Results results) {
   // Re-post onto the UI sequence if not called from there.
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl_new.h b/chrome/browser/ui/app_list/search/search_controller_impl_new.h
index 984ff63b..e649c69 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl_new.h
+++ b/chrome/browser/ui/app_list/search/search_controller_impl_new.h
@@ -66,6 +66,9 @@
   size_t AddGroup(size_t max_results) override;
   void AddProvider(size_t group_id,
                    std::unique_ptr<SearchProvider> provider) override;
+  size_t ReplaceProvidersForResultTypeForTest(
+      ash::AppListSearchResultType result_type,
+      std::unique_ptr<SearchProvider> provider) override;
   void SetResults(const SearchProvider* provider, Results results) override;
   void Publish() override;
   ChromeSearchResult* FindSearchResult(const std::string& result_id) override;
diff --git a/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.cc b/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.cc
index cb0f7fb..aa198bb2 100644
--- a/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.cc
@@ -23,13 +23,16 @@
 // Fake continue section search results.
 class TestContinueSectionSearchResult : public ChromeSearchResult {
  public:
-  explicit TestContinueSectionSearchResult(const std::string& id) {
+  explicit TestContinueSectionSearchResult(const std::string& id,
+                                           bool is_drive_result) {
     set_id(id);
     SetTitle(base::UTF8ToUTF16(id));
     SetDisplayType(DisplayType::kContinue);
     SetCategory(Category::kFiles);
-    SetResultType(ResultType::kZeroStateFile);
-    SetMetricsType(ash::ZERO_STATE_FILE);
+    SetResultType(is_drive_result ? ResultType::kZeroStateDrive
+                                  : ResultType::kZeroStateFile);
+    SetMetricsType(is_drive_result ? ash::ZERO_STATE_DRIVE
+                                   : ash::ZERO_STATE_FILE);
   }
 
   TestContinueSectionSearchResult(const TestContinueSectionSearchResult&) =
@@ -45,14 +48,18 @@
 
 }  // namespace
 
-TestContinueFilesSearchProvider::TestContinueFilesSearchProvider() = default;
+TestContinueFilesSearchProvider::TestContinueFilesSearchProvider(
+    bool for_drive_files)
+    : for_drive_files_(for_drive_files) {}
 
 TestContinueFilesSearchProvider::~TestContinueFilesSearchProvider() = default;
 
 void TestContinueFilesSearchProvider::StartZeroState() {
-  auto create_result = [](int index) -> std::unique_ptr<ChromeSearchResult> {
-    const std::string id = base::StringPrintf("continue_task_%d", index);
-    return std::make_unique<TestContinueSectionSearchResult>(id);
+  auto create_result = [&](int index) -> std::unique_ptr<ChromeSearchResult> {
+    const std::string id =
+        base::StringPrintf("continue_task_%d_%d", for_drive_files_, index);
+    return std::make_unique<TestContinueSectionSearchResult>(id,
+                                                             for_drive_files_);
   };
 
   std::vector<std::unique_ptr<ChromeSearchResult>> results;
@@ -64,7 +71,8 @@
 
 ash::AppListSearchResultType TestContinueFilesSearchProvider::ResultType()
     const {
-  return ResultType::kUnknown;
+  return for_drive_files_ ? ResultType::kZeroStateDrive
+                          : ResultType::kZeroStateFile;
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.h b/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.h
index 9a7b612..7b3445d8 100644
--- a/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.h
+++ b/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.h
@@ -11,10 +11,10 @@
 
 // Test specific search provider that publishes certain number of continue
 // section file results. Published results will have ID/title in format
-// "continue_task_<index>".
+// "continue_task_<for_drive_files>_<index>".
 class TestContinueFilesSearchProvider : public SearchProvider {
  public:
-  TestContinueFilesSearchProvider();
+  explicit TestContinueFilesSearchProvider(bool for_drive_files);
 
   TestContinueFilesSearchProvider(const TestContinueFilesSearchProvider&) =
       delete;
@@ -30,6 +30,7 @@
   void set_count(size_t count) { count_ = count; }
 
  private:
+  const bool for_drive_files_;
   // Number of results returned by the test provider.
   size_t count_ = 0;
 };
diff --git a/chrome/browser/ui/app_list/search/test/test_search_controller.cc b/chrome/browser/ui/app_list/search/test/test_search_controller.cc
index daaac6e6..eef5db0 100644
--- a/chrome/browser/ui/app_list/search/test/test_search_controller.cc
+++ b/chrome/browser/ui/app_list/search/test/test_search_controller.cc
@@ -53,6 +53,13 @@
   provider_->set_controller(this);
 }
 
+size_t TestSearchController::ReplaceProvidersForResultTypeForTest(
+    ash::AppListSearchResultType result_type,
+    std::unique_ptr<SearchProvider> provider) {
+  NOTREACHED();
+  return 0u;
+}
+
 void TestSearchController::SetResults(const SearchProvider* provider,
                                       Results results) {
   last_results_ = std::move(results);
diff --git a/chrome/browser/ui/app_list/search/test/test_search_controller.h b/chrome/browser/ui/app_list/search/test/test_search_controller.h
index fd6c4da..fc24c3a5 100644
--- a/chrome/browser/ui/app_list/search/test/test_search_controller.h
+++ b/chrome/browser/ui/app_list/search/test/test_search_controller.h
@@ -37,6 +37,9 @@
   size_t AddGroup(size_t max_results) override;
   void AddProvider(size_t group_id,
                    std::unique_ptr<SearchProvider> provider) override;
+  size_t ReplaceProvidersForResultTypeForTest(
+      ash::AppListSearchResultType result_type,
+      std::unique_ptr<SearchProvider> provider) override;
   void SetResults(const SearchProvider* provider, Results results) override;
   void Publish() override;
   ChromeSearchResult* FindSearchResult(const std::string& result_id) override;
diff --git a/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc b/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
index f74761c..3e18ed6a 100644
--- a/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
@@ -11,7 +11,7 @@
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/system/message_center/unified_message_center_bubble.h"
-#include "ash/system/message_center/unified_message_center_view.h"
+#include "ash/system/notification_center/notification_center_view.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "base/command_line.h"
@@ -107,17 +107,17 @@
 // Returns the view for the specified |notification|.
 message_center::MessageView* FindViewForNotification(
     const message_center::Notification* notification) {
-  UnifiedMessageListView* unified_message_list_view =
+  NotificationListView* notification_list_view =
       FindStatusAreaWidget()
           ->unified_system_tray()
           ->message_center_bubble()
-          ->message_center_view()
-          ->message_list_view();
+          ->notification_center_view()
+          ->notification_list_view();
 
   // TODO(crbug/1335196): `FindDescendentsOfClass` returning empty list for
-  // `UnifiedMessageCenterView` even when `MessageView`s exist. Need to
+  // `NotificationCenterView` even when `MessageView`s exist. Need to
   // investigate and resolve.
-  return unified_message_list_view->GetMessageViewForNotificationId(
+  return notification_list_view->GetMessageViewForNotificationId(
       notification->id());
 }
 
diff --git a/chrome/browser/ui/ash/calendar/calendar_keyed_service.cc b/chrome/browser/ui/ash/calendar/calendar_keyed_service.cc
index 8cedf63..d985ffcb 100644
--- a/chrome/browser/ui/ash/calendar/calendar_keyed_service.cc
+++ b/chrome/browser/ui/ash/calendar/calendar_keyed_service.cc
@@ -46,7 +46,11 @@
         policy {
           cookies_allowed: NO
           setting: "This feature cannot be disabled in settings."
-          policy_exception_justification: "Not implemented."
+          chrome_policy {
+              CalendarIntegrationEnabled {
+                CalendarIntegrationEnabled: true
+              }
+          }
         })");
 
 }  // namespace
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 88572ad..7eca80fe 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -636,9 +636,9 @@
     g_browser_process->background_printing_manager()
         ->DeletePreviewContentsForBrowserContext(profile_);
 #endif
-      // An incognito profile is no longer needed, this indirectly frees
-      // its cache and cookies once it gets destroyed at the appropriate time.
-      ProfileDestroyer::DestroyProfileWhenAppropriate(profile_);
+    // An incognito profile is no longer needed, this indirectly frees
+    // its cache and cookies once it gets destroyed at the appropriate time.
+    ProfileDestroyer::DestroyOTRProfileWhenAppropriate(profile_);
   }
 
   // There may be pending file dialogs, we need to tell them that we've gone
diff --git a/chrome/browser/ui/media_router/presentation_receiver_window_controller.cc b/chrome/browser/ui/media_router/presentation_receiver_window_controller.cc
index 45a27e0..c76254da 100644
--- a/chrome/browser/ui/media_router/presentation_receiver_window_controller.cc
+++ b/chrome/browser/ui/media_router/presentation_receiver_window_controller.cc
@@ -53,7 +53,7 @@
 
   if (otr_profile_) {
     otr_profile_observation_.Reset();
-    ProfileDestroyer::DestroyProfileWhenAppropriate(otr_profile_);
+    ProfileDestroyer::DestroyOTRProfileWhenAppropriate(otr_profile_);
   }
 }
 
diff --git a/chrome/browser/ui/views/bubble_anchor_util_views.cc b/chrome/browser/ui/views/bubble_anchor_util_views.cc
index 98a8fff..f65939f 100644
--- a/chrome/browser/ui/views/bubble_anchor_util_views.cc
+++ b/chrome/browser/ui/views/bubble_anchor_util_views.cc
@@ -23,7 +23,11 @@
                                                    Anchor anchor) {
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
   if (anchor == kLocationBar &&
-      browser_view->GetLocationBarView()->ShouldHideContentSettingImage()) {
+      browser_view->GetLocationBarView()->chip_controller() &&
+      browser_view->GetLocationBarView()
+          ->chip_controller()
+          ->chip()
+          ->GetVisible()) {
     return {browser_view->GetLocationBarView()->chip_controller()->chip(),
             browser_view->GetLocationBarView()->chip_controller()->chip(),
             views::BubbleBorder::TOP_LEFT};
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.cc
index c0a5052..6e000f1 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.cc
@@ -25,12 +25,11 @@
 
 namespace {
 
-std::unique_ptr<base::ListValue> ToListValue(
-    const std::vector<std::string>& permissions) {
+base::Value::List ToListValue(const std::vector<std::string>& permissions) {
   extensions::ListBuilder builder;
   for (const std::string& permission : permissions)
     builder.Append(permission);
-  return builder.Build();
+  return builder.BuildList();
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/views/hats/hats_next_web_dialog.cc b/chrome/browser/ui/views/hats/hats_next_web_dialog.cc
index f1561f1..cf85a66 100644
--- a/chrome/browser/ui/views/hats/hats_next_web_dialog.cc
+++ b/chrome/browser/ui/views/hats/hats_next_web_dialog.cc
@@ -209,7 +209,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (otr_profile_) {
     otr_profile_->RemoveObserver(this);
-    ProfileDestroyer::DestroyProfileWhenAppropriate(otr_profile_);
+    ProfileDestroyer::DestroyOTRProfileWhenAppropriate(otr_profile_);
   }
   auto* service = HatsServiceFactory::GetForProfile(browser_->profile(), false);
   DCHECK(service);
@@ -241,7 +241,7 @@
   // The HaTS backend service accepts a list of preferred languages, although
   // only the application locale is provided here to ensure that the survey
   // matches the native UI language.
-  base::ListValue language_list;
+  base::Value::List language_list;
   language_list.Append(g_browser_process->GetApplicationLocale());
 
   std::string language_list_json;
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_controller.cc b/chrome/browser/ui/views/page_action/page_action_icon_controller.cc
index 798cd60..e7e5373 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_controller.cc
+++ b/chrome/browser/ui/views/page_action/page_action_icon_controller.cc
@@ -274,7 +274,7 @@
       return page_action.first;
     }
   }
-  IMMEDIATE_CRASH();
+  base::ImmediateCrash();
 }
 
 void PageActionIconController::UpdateAll() {
diff --git a/chrome/browser/ui/views/payments/payment_request_missing_fields_metrics_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_missing_fields_metrics_browsertest.cc
deleted file mode 100644
index f2cd6628..0000000
--- a/chrome/browser/ui/views/payments/payment_request_missing_fields_metrics_browsertest.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/test/metrics/histogram_tester.h"
-#include "chrome/browser/ui/views/payments/payment_request_browsertest_base.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/validation.h"
-#include "components/payments/core/journey_logger.h"
-#include "components/payments/core/payments_profile_comparator.h"
-#include "content/public/test/browser_test.h"
-
-namespace payments {
-
-using PaymentRequestMissingFieldsMetricsTest = PaymentRequestBrowserTestBase;
-
-// Tests that proper UMA metrics are logged when shipping section is incomplete.
-IN_PROC_BROWSER_TEST_F(PaymentRequestMissingFieldsMetricsTest,
-                       TestIncompleteShippingProfile) {
-  // Installs two apps so that the Payment Request UI will be shown.
-  std::string a_method_name;
-  InstallPaymentApp("a.com", "payment_request_success_responder.js",
-                    &a_method_name);
-  std::string b_method_name;
-  InstallPaymentApp("b.com", "payment_request_success_responder.js",
-                    &b_method_name);
-
-  NavigateTo("/payment_request_shipping_address_instance_test.html");
-  base::HistogramTester histogram_tester;
-
-  // Add an incomplete profile. The profile has email address only.
-  AddAutofillProfile(autofill::test::GetIncompleteProfile2());
-
-  // Show a Payment Request.
-  InvokePaymentRequestUIWithJs(
-      content::JsReplace("buyWithMethods([{supportedMethods:$1}"
-                         ", {supportedMethods:$2}]);",
-                         a_method_name, b_method_name));
-
-  // Navigate away to abort the Payment Request and trigger the logs.
-  NavigateTo("/payment_request_email_test.html");
-
-  // Make sure the correct events were logged.
-  int32_t expected_event_bits =
-      JourneyLogger::EVENT_SHOWN | JourneyLogger::EVENT_USER_ABORTED |
-      JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT |
-      JourneyLogger::EVENT_REQUEST_SHIPPING |
-      JourneyLogger::EVENT_REQUEST_METHOD_OTHER |
-      JourneyLogger::EVENT_AVAILABLE_METHOD_OTHER |
-      JourneyLogger::EVENT_NEEDS_COMPLETION_SHIPPING;
-  histogram_tester.ExpectBucketCount("PaymentRequest.Events",
-                                     expected_event_bits, 1);
-
-  // Since the incomplete profile has email address only, the rest of the bits
-  // should be logged in MissingShippingFields.
-  int32_t expected_missing_shipping_bits = PaymentsProfileComparator::kName |
-                                           PaymentsProfileComparator::kPhone |
-                                           PaymentsProfileComparator::kAddress;
-  histogram_tester.ExpectBucketCount("PaymentRequest.MissingShippingFields",
-                                     expected_missing_shipping_bits, 1);
-}
-
-// Tests that proper UMA metrics are logged when contacts section is incomplete.
-IN_PROC_BROWSER_TEST_F(PaymentRequestMissingFieldsMetricsTest,
-                       TestIncompleteContactDetails) {
-  // Installs two apps so that the Payment Request UI will be shown.
-  std::string a_method_name;
-  InstallPaymentApp("a.com", "payment_request_success_responder.js",
-                    &a_method_name);
-  std::string b_method_name;
-  InstallPaymentApp("b.com", "payment_request_success_responder.js",
-                    &b_method_name);
-
-  NavigateTo("/payment_request_contact_details_test.html");
-  base::HistogramTester histogram_tester;
-
-  // Add an incomplete profile. The profile has email address only.
-  AddAutofillProfile(autofill::test::GetIncompleteProfile2());
-
-  // Show a Payment Request.
-  InvokePaymentRequestUIWithJs(
-      content::JsReplace("buyWithMethods([{supportedMethods:$1}"
-                         ", {supportedMethods:$2}]);",
-                         a_method_name, b_method_name));
-
-  // Navigate away to abort the Payment Request and trigger the logs.
-  NavigateTo("/payment_request_email_test.html");
-
-  // Make sure the correct events were logged. EVENT_NEEDS_COMPLETION_PAYMENT is
-  // set since billing address of the card is incomplete.
-  int32_t expected_event_bits =
-      JourneyLogger::EVENT_SHOWN | JourneyLogger::EVENT_USER_ABORTED |
-      JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT |
-      JourneyLogger::EVENT_REQUEST_PAYER_NAME |
-      JourneyLogger::EVENT_REQUEST_PAYER_EMAIL |
-      JourneyLogger::EVENT_REQUEST_PAYER_PHONE |
-      JourneyLogger::EVENT_REQUEST_METHOD_OTHER |
-      JourneyLogger::EVENT_AVAILABLE_METHOD_OTHER |
-      JourneyLogger::EVENT_NEEDS_COMPLETION_CONTACT_INFO;
-  histogram_tester.ExpectBucketCount("PaymentRequest.Events",
-                                     expected_event_bits, 1);
-
-  // Since the incomplete profile has email address only, the rest of the bits
-  // should be logged in MissingContactFields.
-  int32_t expected_missing_contact_bits =
-      PaymentsProfileComparator::kName | PaymentsProfileComparator::kPhone;
-  histogram_tester.ExpectBucketCount("PaymentRequest.MissingContactFields",
-                                     expected_missing_contact_bits, 1);
-
-  // Even though the profile is incomplete, there should be no log for missing
-  // shipping fields since shipping was not required.
-  histogram_tester.ExpectTotalCount("PaymentRequest.MissingShippingFields", 0);
-}
-
-}  // namespace payments
diff --git a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
index 9efc724c..f5ec944 100644
--- a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
+++ b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
@@ -308,6 +308,8 @@
   }
 
   replacements["learnMoreLink"] = kLearnMoreIncognitoUrl;
+  replacements["learnMoreA11yLabel"] = l10n_util::GetStringUTF8(
+      IDS_INCOGNITO_TAB_LEARN_MORE_ACCESSIBILITY_LABEL);
   replacements["title"] = l10n_util::GetStringUTF8(
       base::FeatureList::IsEnabled(
           features::kUpdateHistoryEntryPointsInIncognito)
@@ -394,6 +396,10 @@
   localized_strings.Set("learnMore",
                         l10n_util::GetStringUTF16(guest_tab_link_ids));
   localized_strings.Set("learnMoreLink", guest_tab_link);
+  localized_strings.Set(
+      "learnMoreA11yLabel",
+      l10n_util::GetStringUTF16(
+          IDS_NEW_TAB_GUEST_SESSION_LEARN_MORE_ACCESSIBILITY_TEXT));
 
   const std::string& app_locale = g_browser_process->GetApplicationLocale();
   webui::SetLoadTimeDataDefaults(app_locale, &localized_strings);
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
index a2de591..ee500c1 100644
--- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -105,6 +105,8 @@
              types.Has(syncer::UserSelectableType::kPreferences));
   result.Set("readingListSynced",
              types.Has(syncer::UserSelectableType::kReadingList));
+  result.Set("savedTabGroupsSynced",
+             types.Has(syncer::UserSelectableType::kSavedTabGroups));
   result.Set("tabsSynced", types.Has(syncer::UserSelectableType::kTabs));
   result.Set("themesSynced", types.Has(syncer::UserSelectableType::kThemes));
   result.Set("typedUrlsSynced",
@@ -155,6 +157,8 @@
                    types.Has(syncer::UserSelectableType::kPreferences));
   ExpectHasBoolKey(dictionary, "readingListSynced",
                    types.Has(syncer::UserSelectableType::kReadingList));
+  ExpectHasBoolKey(dictionary, "savedTabGroupsSynced",
+                   types.Has(syncer::UserSelectableType::kSavedTabGroups));
   ExpectHasBoolKey(dictionary, "tabsSynced",
                    types.Has(syncer::UserSelectableType::kTabs));
   ExpectHasBoolKey(dictionary, "themesSynced",
diff --git a/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc b/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc
index d78cded..0ca3d125 100644
--- a/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc
+++ b/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc
@@ -64,6 +64,7 @@
       {"sortAlphabetically", IDS_BOOKMARKS_SORT_ALPHABETICALLY},
       {"sortReverseAlphabetically", IDS_BOOKMARKS_SORT_REVERSE_ALPHABETICALLY},
       {"sortMenuA11yLabel", IDS_BOOKMARKS_SORT_MENU_A11Y_LABEL},
+      {"addCurrentTab", IDS_READ_LATER_ADD_CURRENT_TAB},
   };
   for (const auto& str : kLocalizedStrings)
     webui::AddLocalizedString(source, str.name, str.id);
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index a73242f..59da6d5 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -361,8 +361,6 @@
     "//components/webapps/services/web_app_origin_association:lib",
     "//components/webapps/services/web_app_origin_association:service",
     "//content/public/browser",
-    "//mojo/core/embedder",
-    "//mojo/core/embedder:features",
     "//mojo/public/cpp/bindings",
     "//net",
     "//services/data_decoder/public/cpp",
diff --git a/chrome/browser/web_applications/DEPS b/chrome/browser/web_applications/DEPS
index 89357b54..5e66e02 100644
--- a/chrome/browser/web_applications/DEPS
+++ b/chrome/browser/web_applications/DEPS
@@ -4,7 +4,6 @@
   "+components/services/storage/indexed_db/locks",
   "+components/web_package",
   "+components/webapps/services/web_app_origin_association",
-  "+mojo/core/embedder",
   "+mojo/public/cpp/bindings",
   "+services/network/public/cpp",
   "+third_party/blink/public/common",
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_browsertest.cc
index ccfbee61..a41ae97 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_browsertest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_browsertest.cc
@@ -63,8 +63,6 @@
   return web_app;
 }
 
-}  // namespace
-
 class IsolatedWebAppURLLoaderFactoryBrowserTest : public InProcessBrowserTest {
  public:
   explicit IsolatedWebAppURLLoaderFactoryBrowserTest(
@@ -126,13 +124,35 @@
     }
   }
 
+  Browser* CreateAppWindow() {
+    AppId app_id = GenerateAppId(
+        /*manifest_id=*/"", url::Origin::Create(GURL{kPrimaryUrl}).GetURL());
+
+    return Browser::Create(Browser::CreateParams::CreateForApp(
+        GenerateApplicationNameFromAppId(app_id),
+        /*trusted_source=*/true, gfx::Rect(), browser()->profile(),
+        /*user_gesture=*/true));
+  }
+
+  content::WebContents* AttachWebContents(Browser* app_window) {
+    app_window->tab_strip_model()->AppendWebContents(
+        content::WebContents::Create(
+            content::WebContents::CreateParams(app_window->profile())),
+        /*foreground=*/true);
+
+    return app_window->tab_strip_model()->GetActiveWebContents();
+  }
+
   void NavigateAndWaitForTitle(const GURL& url,
                                const std::u16string& page_title) {
-    content::TitleWatcher title_watcher(
-        browser()->tab_strip_model()->GetActiveWebContents(), page_title);
+    Browser* app_window = CreateAppWindow();
+    content::TitleWatcher title_watcher(AttachWebContents(app_window),
+                                        page_title);
 
-    content::RenderFrameHost* render_frame_host =
-        ui_test_utils::NavigateToURL(browser(), GURL(kPrimaryUrl));
+    content::RenderFrameHost* render_frame_host = NavigateToURLWithDisposition(
+        app_window, url, WindowOpenDisposition::CURRENT_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+
     ASSERT_THAT(render_frame_host, NotNull());
 
     EXPECT_THAT(title_watcher.WaitAndGetTitle(), Eq(page_title));
@@ -141,15 +161,19 @@
 
   void NavigateAndWaitForError(const GURL& url,
                                const std::string& error_messsage) {
+    Browser* app_window = CreateAppWindow();
+
     content::WebContentsConsoleObserver console_observer(
-        browser()->tab_strip_model()->GetActiveWebContents());
+        AttachWebContents(app_window));
     console_observer.SetFilter(base::BindRepeating(
         [](const content::WebContentsConsoleObserver::Message& message) {
           return message.log_level == blink::mojom::ConsoleMessageLevel::kError;
         }));
 
-    content::RenderFrameHost* render_frame_host =
-        ui_test_utils::NavigateToURL(browser(), url);
+    content::RenderFrameHost* render_frame_host = NavigateToURLWithDisposition(
+        app_window, url, WindowOpenDisposition::CURRENT_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+
     ASSERT_THAT(render_frame_host, NotNull());
 
     ASSERT_TRUE(console_observer.Wait());
@@ -215,13 +239,17 @@
   builder.AddExchange(kPrimaryUrl,
                       {{":status", "200"}, {"content-type", "text/html"}},
                       R"(
-    <script>
-      fetch('data.txt')
-        .then(res => res.text())
-        .then(data => { console.log(data); document.title = data; })
-        .catch(err => console.error(err));
-    </script>)");
-  builder.AddExchange(kPrimaryUrl + "/data.txt",
+    <script type="text/javascript" src="/script.js"></script>
+)");
+  builder.AddExchange(kPrimaryUrl + "/script.js",
+                      {{":status", "200"}, {"content-type", "text/javascript"}},
+                      R"(
+fetch('title.txt')
+  .then(res => res.text())
+  .then(data => { console.log(data); document.title = data; })
+  .catch(err => console.error(err));
+)");
+  builder.AddExchange(kPrimaryUrl + "/title.txt",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "some data");
   base::FilePath bundle_path = SignAndWriteBundleToDisk(builder.CreateBundle());
@@ -276,53 +304,5 @@
       "non-existing");
 }
 
-class IsolatedWebAppURLLoaderFactoryNonExistingBundlesBrowserTest
-    : public IsolatedWebAppURLLoaderFactoryBrowserTest,
-      public testing::WithParamInterface<std::pair<GURL, std::string>> {
- public:
-  IsolatedWebAppURLLoaderFactoryNonExistingBundlesBrowserTest()
-      : url_(std::get<0>(GetParam())),
-        error_message_(std::get<1>(GetParam())) {}
-
- protected:
-  GURL url_;
-  std::string error_message_;
-};
-
-IN_PROC_BROWSER_TEST_P(
-    IsolatedWebAppURLLoaderFactoryNonExistingBundlesBrowserTest,
-    NonExistingBundle) {
-  std::unique_ptr<WebApp> iwa = CreateIsolatedWebApp(
-      GURL(kPrimaryUrl),
-      IsolationData{IsolationData::InstalledBundle{
-          .path =
-              base::FilePath(FILE_PATH_LITERAL("/this/path/does/not/exist"))}});
-  RegisterWebApp(std::move(iwa));
-
-  NavigateAndWaitForError(url_, error_message_);
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    IsolatedWebAppURLLoaderFactoryNonExistingBundlesBrowserTest,
-    ::testing::Values(
-        std::make_pair(
-            GURL("isolated-app://foo"),
-            "The host of isolated-app:// URLs must be a valid Signed Web "
-            "Bundle ID (got foo): The signed web bundle ID must be exactly 56 "
-            "characters long, but was 3 characters long."),
-        std::make_pair(
-            GURL("isolated-app://"
-                 "9erugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic"),
-            "The host of isolated-app:// URLs must be a valid Signed Web "
-            "Bundle ID (got "
-            "9erugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic): The "
-            "signed web bundle ID must only contain lowercase ASCII characters "
-            "and digits between 2 and 7 (without any padding)."),
-        std::make_pair(
-            GURL("isolated-app://"
-                 "aerugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic"),
-            "Failed to read response from Signed Web Bundle: Failed to parse "
-            "integrity block: FILE_ERROR_NOT_FOUND")));
-
+}  // namespace
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/os_integration/web_app_shortcut_mac.mm b/chrome/browser/web_applications/os_integration/web_app_shortcut_mac.mm
index 94590a2..f0cdba3a 100644
--- a/chrome/browser/web_applications/os_integration/web_app_shortcut_mac.mm
+++ b/chrome/browser/web_applications/os_integration/web_app_shortcut_mac.mm
@@ -14,12 +14,10 @@
 #include <string>
 #include <utility>
 
-#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
-#include "base/feature_list.h"
 #include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -61,8 +59,6 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_switches.h"
-#include "mojo/core/embedder/embedder.h"
-#include "mojo/core/embedder/features.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image_family.h"
@@ -443,17 +439,6 @@
     if (launched_after_rebuild)
       command_line.AppendSwitch(app_mode::kLaunchedAfterRebuild);
 
-    // The shim must use the same Mojo implementation as this browser. Since
-    // feature parameters and field trials are otherwise not passed to shim
-    // processes, we use feature override switches to ensure Mojo parity.
-    if (mojo::core::IsMojoIpczEnabled()) {
-      command_line.AppendSwitchASCII(switches::kEnableFeatures,
-                                     mojo::core::kMojoIpcz.name);
-    } else {
-      command_line.AppendSwitchASCII(switches::kDisableFeatures,
-                                     mojo::core::kMojoIpcz.name);
-    }
-
     // Launch without activating (NSWorkspaceLaunchWithoutActivation).
     base::scoped_nsobject<NSRunningApplication> app(
         base::mac::OpenApplicationWithPath(
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 67e4ef1..265bedc 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1667390101-69fc5ef43375aa1d851c6b73e2cd659dd5befba7.profdata
+chrome-linux-main-1667411991-2f5eab0e58f8b49ce45e62a0aff1397fbb8558c1.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index c4be34eb..5aa94723 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1667390101-1e7e8b1d29fb48c57163421e2671dfd4cad813a5.profdata
+chrome-mac-arm-main-1667411991-49674c078bb2d3d8eff24c037bf14e6d3d2e9d01.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index a889df25..6c724cac 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1667401199-3470cbe5d668f73b099cbe9bad1f59cee0793fd7.profdata
+chrome-win32-main-1667411991-55111278c8b50cd1b45ca818611379bcea385c22.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 2e532dc..769d851 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1667401199-c0f6ac7aa08b8b51fca4b565777e0e0f9ed33e21.profdata
+chrome-win64-main-1667411991-b47da5fce0e9716b40eab2c4a7a6aaf53945ed3a.profdata
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 6f2601c5..d4b8297 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -686,11 +686,8 @@
 
     deps = [
       ":constants",
-      "//components/version_info",
-      "//mojo/core/embedder",
+      "//base",
     ]
-
-    public_deps = [ "//base" ]
   }
 }
 
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index e55cc0e9..49b3e4da 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -519,7 +519,8 @@
     "channel": "stable",
     "contexts": ["webui"],
     "matches": [
-      "chrome://file-manager/*"
+      "chrome://file-manager/*",
+      "chrome://webui-test/*"
     ]
   }],
   "fileManagerPrivateInternal": [{
@@ -531,7 +532,8 @@
     "channel": "stable",
     "contexts": ["webui"],
     "matches": [
-      "chrome://file-manager/*"
+      "chrome://file-manager/*",
+      "chrome://webui-test/*"
     ]
   }],
   "fileSystemProvider": [{
@@ -1004,8 +1006,7 @@
     "contexts": ["webui"],
     "matches": [
       "chrome://bookmarks/*",
-      "chrome://tab-strip.top-chrome/*",
-      "chrome://file-manager/*"
+      "chrome://tab-strip.top-chrome/*"
     ]
   }, {
     "channel": "stable",
diff --git a/chrome/common/extensions/api/autotest_private.idl b/chrome/common/extensions/api/autotest_private.idl
index 40622cb..cb0c8102 100644
--- a/chrome/common/extensions/api/autotest_private.idl
+++ b/chrome/common/extensions/api/autotest_private.idl
@@ -78,7 +78,8 @@
     Fullscreen,
     LeftSnapped,
     RightSnapped,
-    PIP
+    PIP,
+    Floated
   };
 
   // A subset of WM event types in ash::WMEventType. We may add more in the
@@ -89,7 +90,8 @@
     WMEventMinimize,
     WMEventFullscreen,
     WMEventSnapLeft,
-    WMEventSnapRight
+    WMEventSnapRight,
+    WMEventFloat
   };
 
   // Display orientation type.
diff --git a/chrome/common/mac/app_mode_common.h b/chrome/common/mac/app_mode_common.h
index e1a19f0..40b6e15 100644
--- a/chrome/common/mac/app_mode_common.h
+++ b/chrome/common/mac/app_mode_common.h
@@ -6,9 +6,7 @@
 #define CHROME_COMMON_MAC_APP_MODE_COMMON_H_
 
 #include <CoreServices/CoreServices.h>
-#include <string>
 
-#include "base/files/file_path.h"
 #include "base/strings/stringize_macros.h"
 
 #ifdef __OBJC__
@@ -23,7 +21,7 @@
 // The version of the ChromeAppModeInfo struct below. If the format of the
 // struct ever changes, be sure to update the APP_SHIM_VERSION_NUMBER here and
 // the corresponding line in //chrome/app/framework.order .
-#define APP_SHIM_VERSION_NUMBER 7
+#define APP_SHIM_VERSION_NUMBER 6
 
 // All the other macro magic to make APP_SHIM_VERSION_NUMBER usable.
 #define APP_MODE_CONCAT(a, b) a##b
@@ -43,9 +41,8 @@
 // of the full profile directory path.
 extern const char kAppShimBootstrapNameFragment[];
 
-// A symlink used to store the version string of the currently running Chrome,
-// along with any other necessary configuration. The shim will read this to
-// determine which version of the framework to load.
+// A symlink used to store the version string of the currently running Chrome.
+// The shim will read this to determine which version of the framework to load.
 extern const char kRunningChromeVersionSymlinkName[];
 
 // The process ID of the Chrome process that launched the app shim.
@@ -156,19 +153,6 @@
 // Bundle ID of the Chrome browser bundle.
 extern NSString* const kShortcutBrowserBundleIDPlaceholder;
 
-// Indicates the MojoIpcz feature configuration for a launched shim process.
-enum class MojoIpczConfig {
-  // MojoIpcz is enabled.
-  kEnabled,
-
-  // MojoIpcz is disabled.
-  kDisabled,
-
-  // The MojoIpcz configuration should be determined by feature flags on the
-  // CommandLine once parsed by the shim.
-  kUseCommandLineFeatures,
-};
-
 // The structure used to pass information from the app mode loader to the
 // (browser) framework via the entry point ChromeAppModeStart_vN.
 //
@@ -213,29 +197,6 @@
 
   // Directory of the profile associated with the app, as UTF-8.
   const char* profile_dir;
-
-  // Indicates whether MojoIpcz must be enabled in the shim.
-  MojoIpczConfig mojo_ipcz_config;
-};
-
-// Conveys the configuration for a connection to be established between a shim
-// process and a running Chrome process.
-struct ChromeConnectionConfig {
-  // The version of the Chromium framework to use.
-  std::string framework_version;
-
-  // Indicates whether or not the MojoIpcz feature must be enabled.
-  bool is_mojo_ipcz_enabled;
-
-  // Returns a new configuration appropriate for the calling Chrome process to
-  // encode and convey to a shim.
-  static ChromeConnectionConfig GenerateForCurrentProcess();
-
-  // Generates a path value which encodes the contents of this structure.
-  base::FilePath EncodeAsPath() const;
-
-  // Parses a path value into a configuration.
-  static ChromeConnectionConfig DecodeFromPath(const base::FilePath& path);
 };
 
 }  // namespace app_mode
diff --git a/chrome/common/mac/app_mode_common.mm b/chrome/common/mac/app_mode_common.mm
index 3896207..a90636f 100644
--- a/chrome/common/mac/app_mode_common.mm
+++ b/chrome/common/mac/app_mode_common.mm
@@ -7,12 +7,6 @@
 #import <Foundation/Foundation.h>
 #include <type_traits>
 
-#include "base/check.h"
-#include "base/strings/strcat.h"
-#include "base/strings/string_split.h"
-#include "components/version_info/version_info.h"
-#include "mojo/core/embedder/embedder.h"
-
 namespace app_mode {
 
 const char kAppShimBootstrapNameFragment[] = "apps";
@@ -81,39 +75,8 @@
         offsetof(ChromeAppModeInfo, app_mode_name) == 0x30 &&
         offsetof(ChromeAppModeInfo, app_mode_url) == 0x38 &&
         offsetof(ChromeAppModeInfo, user_data_dir) == 0x40 &&
-        offsetof(ChromeAppModeInfo, profile_dir) == 0x48 &&
-        offsetof(ChromeAppModeInfo, mojo_ipcz_config) == 0x50,
+        offsetof(ChromeAppModeInfo, profile_dir) == 0x48,
     "ChromeAppModeInfo layout has changed; bump the APP_SHIM_VERSION_NUMBER "
     "in chrome/common/mac/app_mode_common.h. (And fix this static_assert.)");
 
-// static
-ChromeConnectionConfig ChromeConnectionConfig::GenerateForCurrentProcess() {
-  return {
-      .framework_version = version_info::GetVersionNumber(),
-      .is_mojo_ipcz_enabled = mojo::core::IsMojoIpczEnabled(),
-  };
-}
-
-base::FilePath ChromeConnectionConfig::EncodeAsPath() const {
-  return base::FilePath(
-      base::StrCat({framework_version, ":", is_mojo_ipcz_enabled ? "1" : "0"}));
-}
-
-// static
-ChromeConnectionConfig ChromeConnectionConfig::DecodeFromPath(
-    const base::FilePath& path) {
-  DCHECK(!path.empty());
-  const std::vector<std::string> parts = base::SplitString(
-      path.value(), ":", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
-  DCHECK(!parts.empty());
-  if (parts.size() == 1) {
-    // Assume MojoIpcz is disabled for path values which predate the
-    // introduction of the MojoIpcz bit.
-    return {.framework_version = parts[0], .is_mojo_ipcz_enabled = false};
-  }
-
-  return {.framework_version = parts[0],
-          .is_mojo_ipcz_enabled = parts[1] == "1"};
-}
-
 }  // namespace app_mode
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index feb8b24..e7086097 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2804,20 +2804,6 @@
 const char kMaxConnectionsPerProxy[] = "net.max_connections_per_proxy";
 
 #if BUILDFLAG(IS_MAC)
-// Set to true if the user removed our login item so we should not create a new
-// one when uninstalling background apps.
-const char kUserRemovedLoginItem[] = "background_mode.user_removed_login_item";
-
-// Set to true if Chrome already created a login item, so there's no need to
-// create another one.
-const char kChromeCreatedLoginItem[] =
-    "background_mode.chrome_created_login_item";
-
-// Set to true once we've initialized kChromeCreatedLoginItem for the first
-// time.
-const char kMigratedLoginItemPref[] =
-    "background_mode.migrated_login_item_pref";
-
 // A boolean that tracks whether to show a notification when trying to quit
 // while there are apps running.
 const char kNotifyWhenAppsKeepChromeAlive[] =
diff --git a/chrome/services/speech/soda/BUILD.gn b/chrome/services/speech/soda/BUILD.gn
index b2cc992..76d83e98 100644
--- a/chrome/services/speech/soda/BUILD.gn
+++ b/chrome/services/speech/soda/BUILD.gn
@@ -41,7 +41,7 @@
 source_set("unit_tests") {
   testonly = true
 
-  if (enable_soda) {
+  if (enable_soda_integration_tests) {
     sources = [ "soda_client_unittest.cc" ]
 
     if (is_mac) {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index f43c303..17d68a9f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2644,8 +2644,6 @@
         "//chrome/browser/renderer_host:history_swiper",
         "//components/onc",
         "//components/remote_cocoa/app_shim",
-        "//mojo/core/embedder",
-        "//third_party/ipcz/src:ipcz_chromium",
         "//third_party/ocmock",
         "//ui/accelerated_widget_mac",
       ]
@@ -2658,7 +2656,7 @@
       data_deps += [ "//chrome:packed_resources" ]
     }
 
-    if (enable_soda) {
+    if (enable_soda_integration_tests) {
       assert(enable_speech_service)
       sources +=
           [ "../browser/speech/speech_recognition_service_browsertest.cc" ]
@@ -3431,7 +3429,6 @@
         "../browser/ui/views/payments/payment_request_completion_status_metrics_browsertest.cc",
         "../browser/ui/views/payments/payment_request_data_url_browsertest.cc",
         "../browser/ui/views/payments/payment_request_journey_logger_browsertest.cc",
-        "../browser/ui/views/payments/payment_request_missing_fields_metrics_browsertest.cc",
         "../browser/ui/views/payments/payment_request_no_update_with_browsertest.cc",
         "../browser/ui/views/payments/payment_request_payment_app_browsertest.cc",
         "../browser/ui/views/payments/payment_request_payment_response_browsertest.cc",
@@ -3926,6 +3923,8 @@
         "../browser/ash/policy/networking/policy_certs_browsertest.cc",
         "../browser/ash/policy/reporting/metrics_reporting/audio/audio_events_observer_browsertest.cc",
         "../browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_browsertest.cc",
+        "../browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.cc",
+        "../browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.h",
         "../browser/ash/policy/reporting/metrics_reporting/metric_browsertest_utils.cc",
         "../browser/ash/policy/reporting/metrics_reporting/metric_browsertest_utils.h",
         "../browser/ash/policy/reporting/metrics_reporting/network/network_events_observer_browsertest.cc",
@@ -6392,7 +6391,7 @@
     ]
   }
 
-  if (enable_soda) {
+  if (enable_soda_integration_tests) {
     deps += [ "//chrome/services/speech/soda:unit_tests" ]
   }
 
@@ -7721,7 +7720,7 @@
       "//components/account_manager_core",
       "//components/account_manager_core:test_support",
       "//components/reporting/metrics:metrics_data_collection",
-      "//components/reporting/metrics:test_support",
+      "//components/reporting/metrics/fakes:test_support",
     ]
   }
 
diff --git a/chrome/test/android/browsertests_apk/android_browsertests_jni_onload.cc b/chrome/test/android/browsertests_apk/android_browsertests_jni_onload.cc
index 94d7e6c..3d5b14e3 100644
--- a/chrome/test/android/browsertests_apk/android_browsertests_jni_onload.cc
+++ b/chrome/test/android/browsertests_apk/android_browsertests_jni_onload.cc
@@ -5,44 +5,11 @@
 #include <memory>
 
 #include "base/android/jni_android.h"
-#include "base/android/jni_utils.h"
-#include "base/android/library_loader/library_loader_hooks.h"
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/no_destructor.h"
 #include "chrome/app/android/chrome_jni_onload.h"
 #include "chrome/test/base/chrome_test_launcher.h"
-#include "chrome/utility/chrome_content_utility_client.h"
 #include "content/public/app/content_jni_onload.h"
 #include "content/public/app/content_main.h"
-#include "content/public/common/content_switches.h"
 #include "content/public/test/nested_message_pump_android.h"
-#include "content/public/test/network_service_test_helper.h"
-#include "services/network/public/mojom/network_service.mojom.h"
-
-namespace {
-bool NativeInit(base::android::LibraryProcessType) {
-  static base::NoDestructor<content::NetworkServiceTestHelper>
-      network_service_test_helper;
-
-  // Setup a working test environment for the network service in case it's used.
-  // Only create this object in the utility process, so that its members don't
-  // interfere with other test objects in the browser process.
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->GetSwitchValueASCII(switches::kProcessType) ==
-          switches::kUtilityProcess &&
-      command_line->GetSwitchValueASCII(switches::kUtilitySubType) ==
-          network::mojom::NetworkService::Name_) {
-    ChromeContentUtilityClient::SetNetworkBinderCreationCallback(base::BindOnce(
-        [](content::NetworkServiceTestHelper* helper,
-           service_manager::BinderRegistry* registry) {
-          helper->RegisterNetworkBinders(registry);
-        },
-        network_service_test_helper.get()));
-  }
-  return true;
-}
-}  // namespace
 
 // This is called by the VM when the shared library is first loaded.
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
@@ -62,7 +29,6 @@
       });
 
   content::SetContentMainDelegate(new ChromeTestChromeMainDelegate());
-  base::android::SetNativeInitializationHook(NativeInit);
 
   return JNI_VERSION_1_4;
 }
diff --git a/chrome/test/data/pdf/fullscreen_test.ts b/chrome/test/data/pdf/fullscreen_test.ts
index c62d13d..4f01e06d 100644
--- a/chrome/test/data/pdf/fullscreen_test.ts
+++ b/chrome/test/data/pdf/fullscreen_test.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {FittingType, PdfScriptingApi} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/pdf/viewport_test.ts b/chrome/test/data/pdf/viewport_test.ts
index 1fad1ff..7b8930c 100644
--- a/chrome/test/data/pdf/viewport_test.ts
+++ b/chrome/test/data/pdf/viewport_test.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {FittingType, PAGE_SHADOW, SwipeDirection, Viewport} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 
 import {createMockUnseasonedPdfPluginForTest, getZoomableViewport, MockDocumentDimensions, MockElement, MockSizer, MockUnseasonedPdfPluginElement, MockViewportChangedCallback} from './test_util.js';
 
diff --git a/chrome/test/data/webui/bluetooth_internals/bluetooth_internals_test.js b/chrome/test/data/webui/bluetooth_internals/bluetooth_internals_test.js
index e446324..3a764ca9 100644
--- a/chrome/test/data/webui/bluetooth_internals/bluetooth_internals_test.js
+++ b/chrome/test/data/webui/bluetooth_internals/bluetooth_internals_test.js
@@ -9,7 +9,7 @@
 import {connectedDevices} from 'chrome://bluetooth-internals/device_broker.js';
 import {dismissSnackbar, getSnackbarStateForTest, showSnackbar} from 'chrome://bluetooth-internals/snackbar.js';
 import {UUID} from 'chrome://bluetooth-internals/uuid.mojom-webui.js';
-import {ValueControl, ValueDataType} from 'chrome://bluetooth-internals/value_control.js';
+import {ValueDataType} from 'chrome://bluetooth-internals/value_control.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {$} from 'chrome://resources/js/util.js';
@@ -603,60 +603,73 @@
   let valueControl = null;
 
   setup(function() {
-    valueControl = new ValueControl();
-    valueControl.load(device1.address, service1.id, characteristic1);
-    valueControl.typeSelect_.value = ValueDataType.HEXADECIMAL;
+    document.body.innerHTML = window.trustedTypes.emptyHTML;
+    valueControl = document.createElement('value-control');
+    document.body.appendChild(valueControl);
+    valueControl.dataset.options = JSON.stringify({
+      deviceAddress: device1.address,
+      serviceId: service1.id,
+      characteristicId: characteristic1,
+    });
+    valueControl.shadowRoot.querySelector('select').value =
+        ValueDataType.HEXADECIMAL;
   });
 
   test('ValueControl_SetValue_Hexadecimal_EmptyArray', function() {
-    valueControl.setValue([]);
-    assertEquals('', valueControl.valueInput_.value);
+    valueControl.dataset.value = JSON.stringify([]);
+    assertEquals('', valueControl.shadowRoot.querySelector('input').value);
   });
 
   test('ValueControl_SetValue_Hexadecimal_OneValue', function() {
-    valueControl.setValue([aCode]);
-    assertEquals('0x61', valueControl.valueInput_.value);
+    valueControl.dataset.value = JSON.stringify([aCode]);
+    assertEquals('0x61', valueControl.shadowRoot.querySelector('input').value);
   });
 
   test('ValueControl_SetValue_Hexadecimal_ThreeValues', function() {
-    valueControl.setValue([aCode, bCode, cCode]);
-    assertEquals('0x616263', valueControl.valueInput_.value);
+    valueControl.dataset.value = JSON.stringify([aCode, bCode, cCode]);
+    assertEquals(
+        '0x616263', valueControl.shadowRoot.querySelector('input').value);
   });
 
   test('ValueControl_SetValue_UTF8_EmptyArray', function() {
-    valueControl.typeSelect_.value = ValueDataType.UTF8;
-    valueControl.setValue([]);
-    assertEquals('', valueControl.valueInput_.value);
+    valueControl.shadowRoot.querySelector('select').value = ValueDataType.UTF8;
+    valueControl.dataset.value = JSON.stringify([]);
+    assertEquals('', valueControl.shadowRoot.querySelector('input').value);
   });
 
   test('ValueControl_SetValue_UTF8_OneValue', function() {
-    valueControl.typeSelect_.value = ValueDataType.UTF8;
-    valueControl.setValue([aCode]);
-    assertEquals('a', valueControl.valueInput_.value);
+    valueControl.shadowRoot.querySelector('select').value = ValueDataType.UTF8;
+    valueControl.dataset.value = JSON.stringify([aCode]);
+    assertEquals('a', valueControl.shadowRoot.querySelector('input').value);
   });
 
   test('ValueControl_SetValue_UTF8_ThreeValues', function() {
-    valueControl.typeSelect_.value = ValueDataType.UTF8;
-    valueControl.setValue([aCode, bCode, cCode]);
-    assertEquals('abc', valueControl.valueInput_.value);
+    valueControl.shadowRoot.querySelector('select').value = ValueDataType.UTF8;
+    valueControl.dataset.value = JSON.stringify([aCode, bCode, cCode]);
+    assertEquals('abc', valueControl.shadowRoot.querySelector('input').value);
   });
 
   test('ValueControl_SetValue_Decimal_EmptyArray', function() {
-    valueControl.typeSelect_.value = ValueDataType.DECIMAL;
-    valueControl.setValue([]);
-    assertEquals('', valueControl.valueInput_.value);
+    valueControl.shadowRoot.querySelector('select').value =
+        ValueDataType.DECIMAL;
+    valueControl.dataset.value = JSON.stringify([]);
+    assertEquals('', valueControl.shadowRoot.querySelector('input').value);
   });
 
   test('ValueControl_SetValue_Decimal_OneValue', function() {
-    valueControl.typeSelect_.value = ValueDataType.DECIMAL;
-    valueControl.setValue([aCode]);
-    assertEquals(String(aCode), valueControl.valueInput_.value);
+    valueControl.shadowRoot.querySelector('select').value =
+        ValueDataType.DECIMAL;
+    valueControl.dataset.value = JSON.stringify([aCode]);
+    assertEquals(
+        String(aCode), valueControl.shadowRoot.querySelector('input').value);
   });
 
   test('ValueControl_SetValue_Decimal_ThreeValues', function() {
-    valueControl.typeSelect_.value = ValueDataType.DECIMAL;
-    valueControl.setValue([aCode, bCode, cCode]);
-    assertEquals('97-98-99', valueControl.valueInput_.value);
+    valueControl.shadowRoot.querySelector('select').value =
+        ValueDataType.DECIMAL;
+    valueControl.dataset.value = JSON.stringify([aCode, bCode, cCode]);
+    assertEquals(
+        '97-98-99', valueControl.shadowRoot.querySelector('input').value);
   });
 
   test('ValueControl_ConvertValue_Hexadecimal_EmptyString', function() {
@@ -676,25 +689,27 @@
   });
 
   test('ValueControl_ConvertValue_UTF8_EmptyString', function() {
-    valueControl.typeSelect_.value = ValueDataType.UTF8;
+    valueControl.shadowRoot.querySelector('select').value = ValueDataType.UTF8;
     valueControl.value_.setAs(ValueDataType.UTF8, '');
     assertEquals(0, valueControl.value_.getArray().length);
   });
 
   test('ValueControl_ConvertValue_UTF8_ThreeValues', function() {
-    valueControl.typeSelect_.value = ValueDataType.UTF8;
+    valueControl.shadowRoot.querySelector('select').value = ValueDataType.UTF8;
     valueControl.value_.setAs(ValueDataType.UTF8, 'abc');
     assertDeepEquals([aCode, bCode, cCode], valueControl.value_.getArray());
   });
 
   test('ValueControl_ConvertValue_Decimal_EmptyString', function() {
-    valueControl.typeSelect_.value = ValueDataType.DECIMAL;
+    valueControl.shadowRoot.querySelector('select').value =
+        ValueDataType.DECIMAL;
     valueControl.value_.setAs(ValueDataType.DECIMAL, '');
     assertEquals(0, valueControl.value_.getArray().length);
   });
 
   test('ValueControl_ConvertValue_Decimal_ThreeValues_Fail', function() {
-    valueControl.typeSelect_.value = ValueDataType.DECIMAL;
+    valueControl.shadowRoot.querySelector('select').value =
+        ValueDataType.DECIMAL;
 
     assertThrows(function() {
       valueControl.value_.setAs(ValueDataType.DECIMAL, '97-+-99' /* a-+-c */);
@@ -702,7 +717,8 @@
   });
 
   test('ValueControl_ConvertValue_Decimal_ThreeValues', function() {
-    valueControl.typeSelect_.value = ValueDataType.DECIMAL;
+    valueControl.shadowRoot.querySelector('select').value =
+        ValueDataType.DECIMAL;
     valueControl.value_.setAs(ValueDataType.DECIMAL, '97-98-99' /* abc */);
     assertDeepEquals([aCode, bCode, cCode], valueControl.value_.getArray());
   });
diff --git a/chrome/test/data/webui/bookmarks/app_test.js b/chrome/test/data/webui/bookmarks/app_test.js
index b278a2f..effb0ae 100644
--- a/chrome/test/data/webui/bookmarks/app_test.js
+++ b/chrome/test/data/webui/bookmarks/app_test.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {HIDE_FOCUS_RING_ATTRIBUTE, LOCAL_STORAGE_FOLDER_STATE_KEY, LOCAL_STORAGE_TREE_WIDTH_KEY} from 'chrome://bookmarks/bookmarks.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {down, keyDownOn, pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/bookmarks/command_manager_test.js b/chrome/test/data/webui/bookmarks/command_manager_test.js
index b6084dc..06eaf44 100644
--- a/chrome/test/data/webui/bookmarks/command_manager_test.js
+++ b/chrome/test/data/webui/bookmarks/command_manager_test.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {BookmarksCommandManagerElement, Command, createBookmark, DialogFocusManager, getDisplayedList, MenuSource, selectFolder} from 'chrome://bookmarks/bookmarks.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/bookmarks/list_focus_test.js b/chrome/test/data/webui/bookmarks/list_focus_test.js
index 42d6824..e6eea14 100644
--- a/chrome/test/data/webui/bookmarks/list_focus_test.js
+++ b/chrome/test/data/webui/bookmarks/list_focus_test.js
@@ -4,7 +4,7 @@
 
 import {Command} from 'chrome://bookmarks/bookmarks.js';
 import {assert} from 'chrome://resources/js/assert.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/bookmarks/test_util.ts b/chrome/test/data/webui/bookmarks/test_util.ts
index 0ecc0af7..fdc62312 100644
--- a/chrome/test/data/webui/bookmarks/test_util.ts
+++ b/chrome/test/data/webui/bookmarks/test_util.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {BookmarksFolderNodeElement, FolderOpenState, NodeMap, normalizeNodes} from 'chrome://bookmarks/bookmarks.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 /**
diff --git a/chrome/test/data/webui/bookmarks/toolbar_test.ts b/chrome/test/data/webui/bookmarks/toolbar_test.ts
index d43cf91..4e33c86 100644
--- a/chrome/test/data/webui/bookmarks/toolbar_test.ts
+++ b/chrome/test/data/webui/bookmarks/toolbar_test.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {BookmarksToolbarElement, Command} from 'chrome://bookmarks/bookmarks.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/chromeos/ash_common/cr_policy_indicator_behavior_tests.ts b/chrome/test/data/webui/chromeos/ash_common/cr_policy_indicator_behavior_tests.ts
index 69d9c687..c9873785 100644
--- a/chrome/test/data/webui/chromeos/ash_common/cr_policy_indicator_behavior_tests.ts
+++ b/chrome/test/data/webui/chromeos/ash_common/cr_policy_indicator_behavior_tests.ts
@@ -8,7 +8,7 @@
 import './cr_policy_strings.js';
 
 import {CrPolicyIndicatorBehavior, CrPolicyIndicatorType} from 'chrome://resources/ash/common/cr_policy_indicator_behavior.js';
-import {isChromeOS} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS} from 'chrome://resources/js/platform.js';
 import {mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/chromeos/diagnostics/diagnostics_browsertest.js b/chrome/test/data/webui/chromeos/diagnostics/diagnostics_browsertest.js
index c03df8e..29e7cfb 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/diagnostics_browsertest.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/diagnostics_browsertest.js
@@ -86,7 +86,7 @@
 
 tests.forEach(([testName, module, condition, caseName]) => {
   const className =
-      `DiagnosticsApp${condition ? `with${condition}` : ''}_${testName}`;
+      `DiagnosticsApp${condition ? `With${condition}` : ''}_${testName}`;
 
   let classToExtend = DiagnosticsApp;
   if (condition === 'Input') {
diff --git a/chrome/test/data/webui/cr_components/most_visited_test.ts b/chrome/test/data/webui/cr_components/most_visited_test.ts
index c9042f1..e8a8a65 100644
--- a/chrome/test/data/webui/cr_components/most_visited_test.ts
+++ b/chrome/test/data/webui/cr_components/most_visited_test.ts
@@ -11,7 +11,7 @@
 import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {TextDirection} from 'chrome://resources/mojo/mojo/public/mojom/base/text_direction.mojom-webui.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_action_menu_test.ts b/chrome/test/data/webui/cr_elements/cr_action_menu_test.ts
index 898bca2..042bbfc 100644
--- a/chrome/test/data/webui/cr_elements/cr_action_menu_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_action_menu_test.ts
@@ -7,7 +7,7 @@
 
 import {AnchorAlignment, CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
 import {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
-import {isMac, isWindows} from 'chrome://resources/js/cr.m.js';
+import {isMac, isWindows} from 'chrome://resources/js/platform.js';
 import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
diff --git a/chrome/test/data/webui/cr_elements/find_shortcut_mixin_test.ts b/chrome/test/data/webui/cr_elements/find_shortcut_mixin_test.ts
index 5bf22fd..6fe2125d 100644
--- a/chrome/test/data/webui/cr_elements/find_shortcut_mixin_test.ts
+++ b/chrome/test/data/webui/cr_elements/find_shortcut_mixin_test.ts
@@ -7,7 +7,7 @@
 
 import {FindShortcutManager, FindShortcutMixin} from 'chrome://resources/cr_elements/find_shortcut_mixin.js';
 import {assert} from 'chrome://resources/js/assert.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/downloads/manager_tests.ts b/chrome/test/data/webui/downloads/manager_tests.ts
index 7f49e8ca..4206846 100644
--- a/chrome/test/data/webui/downloads/manager_tests.ts
+++ b/chrome/test/data/webui/downloads/manager_tests.ts
@@ -5,7 +5,7 @@
 import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {BrowserProxy, CrToastManagerElement, DangerType, DownloadsManagerElement, loadTimeData, PageRemote, States} from 'chrome://downloads/downloads.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertLT, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/history/history_list_focus_test.ts b/chrome/test/data/webui/history/history_list_focus_test.ts
index d0343fe..ee704b0a 100644
--- a/chrome/test/data/webui/history/history_list_focus_test.ts
+++ b/chrome/test/data/webui/history/history_list_focus_test.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {BrowserServiceImpl, ensureLazyLoaded, HistoryAppElement, HistoryEntry, HistoryListElement} from 'chrome://history/history.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/history/history_list_test.ts b/chrome/test/data/webui/history/history_list_test.ts
index 5e96aaa..6ab05fda 100644
--- a/chrome/test/data/webui/history/history_list_test.ts
+++ b/chrome/test/data/webui/history/history_list_test.ts
@@ -3,7 +3,8 @@
 // found in the LICENSE file.
 
 import {BrowserServiceImpl, CrDialogElement, ensureLazyLoaded, HistoryAppElement, HistoryEntry, HistoryItemElement, HistoryListElement, HistoryToolbarElement} from 'chrome://history/history.js';
-import {isMac, webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
+import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertGT, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/history/history_toolbar_focus_test.ts b/chrome/test/data/webui/history/history_toolbar_focus_test.ts
index 708cfcb3..5961425c 100644
--- a/chrome/test/data/webui/history/history_toolbar_focus_test.ts
+++ b/chrome/test/data/webui/history/history_toolbar_focus_test.ts
@@ -5,7 +5,7 @@
 import 'chrome://history/history.js';
 
 import {BrowserServiceImpl, HistoryAppElement} from 'chrome://history/history.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/inline_login/inline_login_test.ts b/chrome/test/data/webui/inline_login/inline_login_test.ts
index 7127b112..f7ff105 100644
--- a/chrome/test/data/webui/inline_login/inline_login_test.ts
+++ b/chrome/test/data/webui/inline_login/inline_login_test.ts
@@ -7,7 +7,8 @@
 import {InlineLoginAppElement, View} from 'chrome://chrome-signin/inline_login_app.js';
 import {InlineLoginBrowserProxyImpl} from 'chrome://chrome-signin/inline_login_browser_proxy.js';
 import {assert} from 'chrome://resources/js/assert.js';
-import {isChromeOS, webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
+import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS} from 'chrome://resources/js/platform.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/js/icon_test.ts b/chrome/test/data/webui/js/icon_test.ts
index 0c781fe..1ca9b76d 100644
--- a/chrome/test/data/webui/js/icon_test.ts
+++ b/chrome/test/data/webui/js/icon_test.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {isChromeOS, isLacros, isLinux, isMac, isWindows} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS, isLacros, isLinux, isMac, isWindows} from 'chrome://resources/js/platform.js';
 import {getFavicon, getFaviconForPageURL, getFileIconUrl} from 'chrome://resources/js/icon.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
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 9373b5c6..0c57c11 100644
--- a/chrome/test/data/webui/new_tab_page/app_test.ts
+++ b/chrome/test/data/webui/new_tab_page/app_test.ts
@@ -8,7 +8,7 @@
 import {$$, AppElement, BackgroundManager, BrowserCommandProxy, CustomizeDialogPage, NewTabPageProxy, NtpElement, VoiceAction, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote, PageInterface} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {Command, CommandHandlerRemote} from 'chrome://resources/js/browser_command/browser_command.mojom-webui.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/print_preview/key_event_test.ts b/chrome/test/data/webui/print_preview/key_event_test.ts
index 0c1c9295..f53ed94 100644
--- a/chrome/test/data/webui/print_preview/key_event_test.ts
+++ b/chrome/test/data/webui/print_preview/key_event_test.ts
@@ -4,7 +4,7 @@
 
 import {NativeLayerImpl, PluginProxyImpl, PrintPreviewAppElement} from 'chrome://print/print_preview.js';
 import {assert} from 'chrome://resources/js/assert.js';
-import {isChromeOS, isLacros, isMac, isWindows} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS, isLacros, isMac, isWindows} from 'chrome://resources/js/platform.js';
 import {keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/print_preview/link_container_test.ts b/chrome/test/data/webui/print_preview/link_container_test.ts
index 39cda440..c33b629 100644
--- a/chrome/test/data/webui/print_preview/link_container_test.ts
+++ b/chrome/test/data/webui/print_preview/link_container_test.ts
@@ -4,7 +4,7 @@
 
 import {Destination, DestinationOrigin, PrintPreviewLinkContainerElement} from 'chrome://print/print_preview.js';
 import {assert} from 'chrome://resources/js/assert.js';
-import {isWindows} from 'chrome://resources/js/cr.m.js';
+import {isWindows} from 'chrome://resources/js/platform.js';
 
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/settings/basic_page_test.ts b/chrome/test/data/webui/settings/basic_page_test.ts
index 57c7dc5..0bab3e4f 100644
--- a/chrome/test/data/webui/settings/basic_page_test.ts
+++ b/chrome/test/data/webui/settings/basic_page_test.ts
@@ -8,7 +8,8 @@
 import 'chrome://settings/settings.js';
 import 'chrome://settings/lazy_load.js';
 
-import {isChromeOS, isLacros, webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS, isLacros} from 'chrome://resources/js/platform.js';
+import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {CrSettingsPrefs, MetricsBrowserProxyImpl, pageVisibility, PerformanceBrowserProxyImpl, PrivacyGuideBrowserProxy, PrivacyGuideBrowserProxyImpl, PrivacyGuideInteractions, Router, routes, SettingsBasicPageElement, SettingsIdleLoadElement, SettingsPrefsElement, SettingsSectionElement, StatusAction, SyncStatus} from 'chrome://settings/settings.js';
diff --git a/chrome/test/data/webui/settings/chromeos/fake_language_settings_private.js b/chrome/test/data/webui/settings/chromeos/fake_language_settings_private.js
index 0cc14bbd..ba086aa 100644
--- a/chrome/test/data/webui/settings/chromeos/fake_language_settings_private.js
+++ b/chrome/test/data/webui/settings/chromeos/fake_language_settings_private.js
@@ -8,7 +8,6 @@
  */
 
 import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
-import {isChromeOS} from 'chrome://resources/js/cr.m.js';
 
 import {FakeChromeEvent} from '../../fake_chrome_event.js';
 import {TestBrowserProxy} from '../../test_browser_proxy.js';
@@ -274,10 +273,8 @@
     languages.push(languageCode);
     languageCodes = languages.join(',');
     this.settingsPrefs_.set('prefs.intl.accept_languages.value', languageCodes);
-    if (isChromeOS) {
-      this.settingsPrefs_.set(
-          'prefs.settings.language.preferred_languages.value', languageCodes);
-    }
+    this.settingsPrefs_.set(
+        'prefs.settings.language.preferred_languages.value', languageCodes);
   }
 
   /**
@@ -295,10 +292,8 @@
     languages.splice(index, 1);
     languageCodes = languages.join(',');
     this.settingsPrefs_.set('prefs.intl.accept_languages.value', languageCodes);
-    if (isChromeOS) {
-      this.settingsPrefs_.set(
-          'prefs.settings.language.preferred_languages.value', languageCodes);
-    }
+    this.settingsPrefs_.set(
+        'prefs.settings.language.preferred_languages.value', languageCodes);
   }
 
   /**
@@ -366,10 +361,8 @@
 
     languageCodes = languages.join(',');
     this.settingsPrefs_.set('prefs.intl.accept_languages.value', languageCodes);
-    if (isChromeOS) {
-      this.settingsPrefs_.set(
-          'prefs.settings.language.preferred_languages.value', languageCodes);
-    }
+    this.settingsPrefs_.set(
+        'prefs.settings.language.preferred_languages.value', languageCodes);
   }
 
   /**
@@ -433,9 +426,6 @@
    *     callback
    */
   getInputMethodLists(callback) {
-    if (!isChromeOS) {
-      assertNotReached();
-    }
     callback({
       componentExtensionImes:
           /** @type {!Array<!chrome.languageSettingsPrivate.InputMethod>} */ (
@@ -450,7 +440,6 @@
    * @param {string} inputMethodId
    */
   addInputMethod(inputMethodId) {
-    assert(isChromeOS);
     const inputMethod = this.componentExtensionImes.find(function(ime) {
       return ime.id === inputMethodId;
     });
@@ -468,7 +457,6 @@
    * @param {string} inputMethodId
    */
   removeInputMethod(inputMethodId) {
-    assert(isChromeOS);
     const inputMethod = this.componentExtensionImes.find(function(ime) {
       return ime.id === inputMethodId;
     });
@@ -562,44 +550,42 @@
       type: chrome.settingsPrivate.PrefType.STRING,
       value: 'en-US',
     },
-  ];
-  if (isChromeOS) {
-    fakePrefs.push({
+    {
       key: 'settings.language.preferred_languages',
       type: chrome.settingsPrivate.PrefType.STRING,
       value: 'en-US,sw',
-    });
-    fakePrefs.push({
+    },
+    {
       key: 'settings.language.preload_engines',
       type: chrome.settingsPrivate.PrefType.STRING,
       value: '_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:us::eng,' +
           '_comp_ime_fgoepimhcoialccpbmpnnblemnepkkaoxkb:us:dvorak:eng',
-    });
-    fakePrefs.push({
+    },
+    {
       key: 'settings.language.enabled_extension_imes',
       type: chrome.settingsPrivate.PrefType.STRING,
       value: '',
-    });
-    fakePrefs.push({
+    },
+    {
       key: 'settings.language.ime_menu_activated',
       type: chrome.settingsPrivate.PrefType.BOOLEAN,
       value: false,
-    });
-    fakePrefs.push({
+    },
+    {
       key: 'settings.language.allowed_input_methods',
       type: chrome.settingsPrivate.PrefType.LIST,
       value: [],
-    });
-    fakePrefs.push({
+    },
+    {
       key: 'ash.shortcut_reminders.last_used_ime_dismissed',
       type: chrome.settingsPrivate.PrefType.BOOLEAN,
       value: false,
-    });
-    fakePrefs.push({
+    },
+    {
       key: 'ash.shortcut_reminders.next_ime_dismissed',
       type: chrome.settingsPrivate.PrefType.BOOLEAN,
       value: false,
-    });
-  }
+    },
+  ];
   return fakePrefs;
 }
diff --git a/chrome/test/data/webui/settings/languages_page_tests.ts b/chrome/test/data/webui/settings/languages_page_tests.ts
index b293c9bf..6df7cb1 100644
--- a/chrome/test/data/webui/settings/languages_page_tests.ts
+++ b/chrome/test/data/webui/settings/languages_page_tests.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // clang-format off
-import {isWindows} from 'chrome://resources/js/cr.m.js';
+import {isWindows} from 'chrome://resources/js/platform.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/settings/languages_tests.ts b/chrome/test/data/webui/settings/languages_tests.ts
index 16cf2420..7e44e9f3 100644
--- a/chrome/test/data/webui/settings/languages_tests.ts
+++ b/chrome/test/data/webui/settings/languages_tests.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // clang-format off
-import {isWindows} from 'chrome://resources/js/cr.m.js';
+import {isWindows} from 'chrome://resources/js/platform.js';
 import {LanguageHelper, LanguagesBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {CrSettingsPrefs} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/settings/passwords_export_dialog_test.ts b/chrome/test/data/webui/settings/passwords_export_dialog_test.ts
index 57c5f1f..3212094 100644
--- a/chrome/test/data/webui/settings/passwords_export_dialog_test.ts
+++ b/chrome/test/data/webui/settings/passwords_export_dialog_test.ts
@@ -7,7 +7,7 @@
 // clang-format off
 import 'chrome://settings/lazy_load.js';
 
-import {isChromeOS, isLacros} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS, isLacros} from 'chrome://resources/js/platform.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {CrDialogElement, PasswordsExportDialogElement} from 'chrome://settings/lazy_load.js';
 import {PasswordManagerImpl} from 'chrome://settings/settings.js';
diff --git a/chrome/test/data/webui/settings/passwords_section_test.ts b/chrome/test/data/webui/settings/passwords_section_test.ts
index 9c2062a6..9983402 100644
--- a/chrome/test/data/webui/settings/passwords_section_test.ts
+++ b/chrome/test/data/webui/settings/passwords_section_test.ts
@@ -7,7 +7,8 @@
 // clang-format off
 import 'chrome://settings/lazy_load.js';
 
-import {isMac, isWindows, isChromeOS, isLacros, webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
+import {isMac, isWindows, isChromeOS, isLacros} from 'chrome://resources/js/platform.js';
+import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {PasswordsSectionElement} from 'chrome://settings/lazy_load.js';
diff --git a/chrome/test/data/webui/settings/site_details_tests.ts b/chrome/test/data/webui/settings/site_details_tests.ts
index 388b04b..1b57cc8 100644
--- a/chrome/test/data/webui/settings/site_details_tests.ts
+++ b/chrome/test/data/webui/settings/site_details_tests.ts
@@ -3,7 +3,8 @@
 // found in the LICENSE file.
 
 // clang-format off
-import {isChromeOS, webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS} from 'chrome://resources/js/platform.js';
+import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {listenOnce} from 'chrome://resources/js/util.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {ChooserType, ContentSetting, ContentSettingsTypes, SiteDetailsElement, SiteSettingSource, SiteSettingsPrefsBrowserProxyImpl, WebsiteUsageBrowserProxy, WebsiteUsageBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
diff --git a/chrome/test/data/webui/tab_strip/drag_manager_test.ts b/chrome/test/data/webui/tab_strip/drag_manager_test.ts
index 9414a65..f0422a7 100644
--- a/chrome/test/data/webui/tab_strip/drag_manager_test.ts
+++ b/chrome/test/data/webui/tab_strip/drag_manager_test.ts
@@ -1,7 +1,7 @@
 // Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-import {isChromeOS} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS} from 'chrome://resources/js/platform.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {DragManager, DragManagerDelegate, PLACEHOLDER_GROUP_ID, PLACEHOLDER_TAB_ID} from 'chrome://tab-strip.top-chrome/drag_manager.js';
 import {TabElement} from 'chrome://tab-strip.top-chrome/tab.js';
diff --git a/chrome/test/data/webui/whats_new/whats_new_app_test.ts b/chrome/test/data/webui/whats_new/whats_new_app_test.ts
index 1477ee89..720ed09a 100644
--- a/chrome/test/data/webui/whats_new/whats_new_app_test.ts
+++ b/chrome/test/data/webui/whats_new/whats_new_app_test.ts
@@ -6,7 +6,7 @@
 
 import {CommandHandlerRemote} from 'chrome://resources/js/browser_command/browser_command.mojom-webui.js';
 import {BrowserCommandProxy} from 'chrome://resources/js/browser_command/browser_command_proxy.js';
-import {isChromeOS} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS} from 'chrome://resources/js/platform.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
diff --git a/chrome/updater/run_all_unittests.cc b/chrome/updater/run_all_unittests.cc
index ecffbb2..2f24036 100644
--- a/chrome/updater/run_all_unittests.cc
+++ b/chrome/updater/run_all_unittests.cc
@@ -164,6 +164,8 @@
   MaybeIncreaseTestTimeouts(argc, argv);
 
 #if BUILDFLAG(IS_WIN)
+  updater::test::MaybeExcludePathsFromWindowsDefender();
+
   std::cerr << "Process priority: " << base::Process::Current().GetPriority()
             << std::endl;
   std::cerr << updater::GetUACState() << std::endl;
diff --git a/chrome/updater/unittest_util.cc b/chrome/updater/unittest_util.cc
index b7df85b..5ec1b38 100644
--- a/chrome/updater/unittest_util.cc
+++ b/chrome/updater/unittest_util.cc
@@ -7,12 +7,17 @@
 #include <string>
 #include <utility>
 
+#include "base/base_paths.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/path_service.h"
 #include "base/process/kill.h"
+#include "base/process/launch.h"
 #include "base/process/process_iterator.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/policy/manager.h"
@@ -80,4 +85,41 @@
   return Local::DeleteDirsIfEmpty(file_path->DirName());
 }
 
+#if BUILDFLAG(IS_WIN)
+void MaybeExcludePathsFromWindowsDefender() {
+  constexpr char kTestLauncherExcludePathsFromWindowDefender[] =
+      "exclude-paths-from-win-defender";
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(kTestLauncherExcludePathsFromWindowDefender))
+    return;
+
+  base::FilePath program_files;
+  base::FilePath program_files_x86;
+  base::FilePath local_app_data;
+  if (!base::PathService::Get(base::DIR_PROGRAM_FILES, &program_files) ||
+      !base::PathService::Get(base::DIR_PROGRAM_FILESX86, &program_files_x86) ||
+      !base::PathService::Get(base::DIR_LOCAL_APP_DATA, &local_app_data)) {
+    return;
+  }
+
+  const auto quote_path_value = [](const base::FilePath& path) {
+    return base::StrCat({L"'", path.value(), L"'"});
+  };
+  const std::wstring cmdline =
+      base::StrCat({L"PowerShell.exe Add-MpPreference -ExclusionPath ",
+                    base::JoinString({quote_path_value(program_files),
+                                      quote_path_value(program_files_x86),
+                                      quote_path_value(local_app_data)},
+                                     L", ")});
+
+  base::LaunchOptions options;
+  options.start_hidden = true;
+  options.wait = true;
+  VLOG(1) << "Running: " << cmdline;
+  base::Process process = base::LaunchProcess(cmdline, options);
+  LOG_IF(ERROR, !process.IsValid())
+      << "Failed to disable Windows Defender: " << cmdline;
+}
+#endif  // BUILDFLAG(IS_WIN)
+
 }  // namespace updater::test
diff --git a/chrome/updater/unittest_util.h b/chrome/updater/unittest_util.h
index bd5600d..4ab54676 100644
--- a/chrome/updater/unittest_util.h
+++ b/chrome/updater/unittest_util.h
@@ -58,6 +58,12 @@
 bool DeleteFileAndEmptyParentDirectories(
     const absl::optional<base::FilePath>& file_path);
 
+#if BUILDFLAG(IS_WIN)
+// Change Windows Defender settings to skip scanning the paths used by the
+// updater if test runs with the flag `exclude-paths-from-win-defender`.
+void MaybeExcludePathsFromWindowsDefender();
+#endif
+
 }  // namespace updater::test
 
 #endif  // CHROME_UPDATER_UNITTEST_UTIL_H_
diff --git a/chromecast/cast_core/runtime/browser/cast_runtime_content_browser_client.cc b/chromecast/cast_core/runtime/browser/cast_runtime_content_browser_client.cc
index efadbde..3b69110e 100644
--- a/chromecast/cast_core/runtime/browser/cast_runtime_content_browser_client.cc
+++ b/chromecast/cast_core/runtime/browser/cast_runtime_content_browser_client.cc
@@ -151,7 +151,14 @@
 
 void CastRuntimeContentBrowserClient::InitializeCoreComponents(
     CastWebService* web_service) {
-  app_dispatcher_ = RuntimeApplicationDispatcher::Create(*this, web_service);
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  std::string runtime_id =
+      command_line->GetSwitchValueASCII(cast::core::kCastCoreRuntimeIdSwitch);
+  std::string runtime_service_path =
+      command_line->GetSwitchValueASCII(cast::core::kRuntimeServicePathSwitch);
+
+  app_dispatcher_ = std::make_unique<RuntimeServiceImpl>(
+      *this, *web_service, runtime_id, runtime_service_path);
 }
 
 }  // namespace chromecast
diff --git a/chromecast/cast_core/runtime/browser/runtime_application_base.cc b/chromecast/cast_core/runtime/browser/runtime_application_base.cc
index 52ff8d2..53388ff 100644
--- a/chromecast/cast_core/runtime/browser/runtime_application_base.cc
+++ b/chromecast/cast_core/runtime/browser/runtime_application_base.cc
@@ -7,7 +7,8 @@
 #include "base/notreached.h"
 #include "base/ranges/algorithm.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "chromecast/browser/cast_web_service.h"
+#include "chromecast/browser/cast_content_window.h"
+#include "chromecast/browser/cast_web_contents.h"
 #include "chromecast/browser/visibility_types.h"
 #include "chromecast/common/feature_constants.h"
 
@@ -33,14 +34,11 @@
 RuntimeApplicationBase::RuntimeApplicationBase(
     std::string cast_session_id,
     cast::common::ApplicationConfig app_config,
-    mojom::RendererType renderer_type_used,
-    CastWebService* web_service)
+    mojom::RendererType renderer_type)
     : cast_session_id_(std::move(cast_session_id)),
       app_config_(std::move(app_config)),
-      renderer_type_(renderer_type_used),
-      web_service_(web_service),
+      renderer_type_(renderer_type),
       task_runner_(base::SequencedTaskRunnerHandle::Get()) {
-  DCHECK(web_service_);
   DCHECK(task_runner_);
 }
 
@@ -68,13 +66,15 @@
 
 void RuntimeApplicationBase::Load(StatusCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(delegate_->GetWebContents());
 
   is_application_running_ = true;
-  cast_web_view_ = CreateCastWebView();
   if (cached_mojom_rules_) {
     // Apply cached URL rewrite rules before anything is done with the page.
-    cast_web_view_->cast_web_contents()->SetUrlRewriteRules(
-        std::move(cached_mojom_rules_));
+    auto* cast_web_contents =
+        CastWebContents::FromWebContents(delegate_->GetWebContents());
+    DCHECK(cast_web_contents);
+    cast_web_contents->SetUrlRewriteRules(std::move(cached_mojom_rules_));
   }
 
   LOG(INFO) << "Loaded application: " << *this;
@@ -213,9 +213,13 @@
 
 void RuntimeApplicationBase::LoadPage(const GURL& url) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(delegate_->GetWebContents());
+  auto* cast_web_contents =
+      CastWebContents::FromWebContents(delegate_->GetWebContents());
+  DCHECK(cast_web_contents);
 
-  cast_web_contents()->AddRendererFeatures(GetRendererFeatures());
-  cast_web_contents()->SetAppProperties(
+  cast_web_contents->AddRendererFeatures(GetRendererFeatures());
+  cast_web_contents->SetAppProperties(
       config().app_id(), GetCastSessionId(), GetIsAudioOnly(), url,
       GetEnforceFeaturePermissions(), GetFeaturePermissions(),
       GetAdditionalFeaturePermissionOrigins());
@@ -224,56 +228,48 @@
   // created. This way users won't see the progressive UI updates as the page is
   // formed and styles are applied. The actual window will be created in
   // OnApplicationStarted when application is fully launched.
-  cast_web_contents()->LoadUrl(url);
+  cast_web_contents->LoadUrl(url);
 
   // This needs to be called to get the PageState::LOADED event as it's fully
   // loaded.
-  cast_web_contents()->SetWebVisibilityAndPaint(false);
+  cast_web_contents->SetWebVisibilityAndPaint(false);
 }
 
 void RuntimeApplicationBase::OnPageLoaded() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DLOG(INFO) << "Page loaded: " << *this;
 
-  cast_web_view_->window()->AddObserver(this);
-  cast_web_view_->window()->EnableTouchInput(touch_input_ ==
-                                             cast::common::TouchInput::ENABLED);
+  DCHECK(delegate_->GetCastContentWindow());
+  delegate_->GetCastContentWindow()->AddObserver(this);
+  delegate_->GetCastContentWindow()->EnableTouchInput(
+      touch_input_ == cast::common::TouchInput::ENABLED);
 
   // Create the window and show the web view.
   if (visibility_ == cast::common::Visibility::FULL_SCREEN) {
     LOG(INFO) << "Loading page in full screen: " << *this;
-    cast_web_view_->window()->GrantScreenAccess();
-    cast_web_view_->window()->CreateWindow(mojom::ZOrder::APP,
-                                           VisibilityPriority::STICKY_ACTIVITY);
+    delegate_->GetCastContentWindow()->GrantScreenAccess();
+    delegate_->GetCastContentWindow()->CreateWindow(
+        mojom::ZOrder::APP, VisibilityPriority::STICKY_ACTIVITY);
   } else {
     LOG(INFO) << "Loading page in background: " << *this;
-    cast_web_view_->window()->CreateWindow(mojom::ZOrder::APP,
-                                           VisibilityPriority::HIDDEN);
+    delegate_->GetCastContentWindow()->CreateWindow(mojom::ZOrder::APP,
+                                                    VisibilityPriority::HIDDEN);
   }
 
   delegate().NotifyApplicationStarted();
 }
 
-CastWebView::Scoped RuntimeApplicationBase::CreateCastWebView() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  mojom::CastWebViewParamsPtr params = mojom::CastWebViewParams::New();
-  params->renderer_type = renderer_type_;
-  params->handle_inner_contents = true;
-  params->session_id = GetCastSessionId();
-  params->is_remote_control_mode = GetIsRemoteControlMode();
-  params->activity_id =
-      params->is_remote_control_mode ? params->session_id : config().app_id();
-  params->enabled_for_dev = GetEnabledForDev();
-  return web_service_->CreateWebViewInternal(std::move(params));
-}
-
 void RuntimeApplicationBase::SetUrlRewriteRules(
     url_rewrite::mojom::UrlRequestRewriteRulesPtr mojom_rules) {
-  if (!cast_web_view_) {
+  if (!delegate_->GetWebContents()) {
     cached_mojom_rules_ = std::move(mojom_rules);
     return;
   }
-  cast_web_contents()->SetUrlRewriteRules(std::move(mojom_rules));
+
+  auto* cast_web_contents =
+      CastWebContents::FromWebContents(delegate_->GetWebContents());
+  DCHECK(cast_web_contents);
+  cast_web_contents->SetUrlRewriteRules(std::move(mojom_rules));
 }
 
 void RuntimeApplicationBase::SetMediaState(
@@ -288,24 +284,27 @@
             << cast::common::MediaState::Type_Name(media_state_) << ", "
             << *this;
 
-  if (!cast_web_view_ || !cast_web_view_->cast_web_contents()) {
+  if (!delegate_->GetWebContents()) {
     return;
   }
 
+  auto* cast_web_contents =
+      CastWebContents::FromWebContents(delegate_->GetWebContents());
+  DCHECK(cast_web_contents);
   switch (media_state_) {
     case cast::common::MediaState::LOAD_BLOCKED:
-      cast_web_view_->cast_web_contents()->BlockMediaLoading(true);
-      cast_web_view_->cast_web_contents()->BlockMediaStarting(true);
+      cast_web_contents->BlockMediaLoading(true);
+      cast_web_contents->BlockMediaStarting(true);
       break;
 
     case cast::common::MediaState::START_BLOCKED:
-      cast_web_view_->cast_web_contents()->BlockMediaLoading(false);
-      cast_web_view_->cast_web_contents()->BlockMediaStarting(true);
+      cast_web_contents->BlockMediaLoading(false);
+      cast_web_contents->BlockMediaStarting(true);
       break;
 
     case cast::common::MediaState::UNBLOCKED:
-      cast_web_view_->cast_web_contents()->BlockMediaLoading(false);
-      cast_web_view_->cast_web_contents()->BlockMediaStarting(false);
+      cast_web_contents->BlockMediaLoading(false);
+      cast_web_contents->BlockMediaStarting(false);
       break;
 
     default:
@@ -326,20 +325,21 @@
             << cast::common::Visibility::Type_Name(visibility_) << ", "
             << *this;
 
-  if (!cast_web_view_ || !cast_web_view_->window()) {
+  if (!delegate_->GetCastContentWindow()) {
     return;
   }
 
   switch (visibility_) {
     case cast::common::Visibility::FULL_SCREEN:
-      cast_web_view_->window()->RequestVisibility(
+      delegate_->GetCastContentWindow()->RequestVisibility(
           VisibilityPriority::STICKY_ACTIVITY);
-      cast_web_view_->window()->GrantScreenAccess();
+      delegate_->GetCastContentWindow()->GrantScreenAccess();
       break;
 
     case cast::common::Visibility::HIDDEN:
-      cast_web_view_->window()->RequestVisibility(VisibilityPriority::HIDDEN);
-      cast_web_view_->window()->RevokeScreenAccess();
+      delegate_->GetCastContentWindow()->RequestVisibility(
+          VisibilityPriority::HIDDEN);
+      delegate_->GetCastContentWindow()->RevokeScreenAccess();
       break;
 
     default:
@@ -360,18 +360,22 @@
             << cast::common::TouchInput::Type_Name(touch_input_) << ", "
             << *this;
 
-  if (!cast_web_view_ || !cast_web_view_->window()) {
+  if (!delegate_->GetCastContentWindow()) {
     return;
   }
 
-  cast_web_view_->window()->EnableTouchInput(touch_input_ ==
-                                             cast::common::TouchInput::ENABLED);
+  delegate_->GetCastContentWindow()->EnableTouchInput(
+      touch_input_ == cast::common::TouchInput::ENABLED);
 }
 
 bool RuntimeApplicationBase::IsApplicationRunning() const {
   return is_application_running_;
 }
 
+mojom::RendererType RuntimeApplicationBase::GetRendererType() const {
+  return renderer_type_;
+}
+
 void RuntimeApplicationBase::StopApplication(
     cast::common::StopReason::Type stop_reason,
     int32_t net_error_code) {
@@ -382,11 +386,15 @@
   }
   is_application_running_ = false;
 
-  if (cast_web_view_) {
-    cast_web_contents()->ClosePage();
+  if (delegate_->GetWebContents()) {
+    auto* cast_web_contents =
+        CastWebContents::FromWebContents(delegate_->GetWebContents());
+    DCHECK(cast_web_contents);
+    cast_web_contents->ClosePage();
+
     // Check if window is still available as page might have been closed before.
-    if (cast_web_view_->window()) {
-      cast_web_view_->window()->RemoveObserver(this);
+    if (delegate_->GetCastContentWindow()) {
+      delegate_->GetCastContentWindow()->RemoveObserver(this);
     }
   }
 
@@ -399,17 +407,21 @@
 
 void RuntimeApplicationBase::OnVisibilityChange(
     VisibilityType visibility_type) {
+  DCHECK(delegate_->GetWebContents());
+  auto* cast_web_contents =
+      CastWebContents::FromWebContents(delegate_->GetWebContents());
+  DCHECK(cast_web_contents);
   switch (visibility_type) {
     case VisibilityType::FULL_SCREEN:
     case VisibilityType::PARTIAL_OUT:
     case VisibilityType::TRANSIENTLY_HIDDEN:
       LOG(INFO) << "Application is visible now: " << *this;
-      cast_web_contents()->SetWebVisibilityAndPaint(true);
+      cast_web_contents->SetWebVisibilityAndPaint(true);
       break;
 
     default:
       LOG(INFO) << "Application is hidden now: " << *this;
-      cast_web_contents()->SetWebVisibilityAndPaint(false);
+      cast_web_contents->SetWebVisibilityAndPaint(false);
       break;
   }
 }
diff --git a/chromecast/cast_core/runtime/browser/runtime_application_base.h b/chromecast/cast_core/runtime/browser/runtime_application_base.h
index 0932fcf3..bdec1b4 100644
--- a/chromecast/cast_core/runtime/browser/runtime_application_base.h
+++ b/chromecast/cast_core/runtime/browser/runtime_application_base.h
@@ -12,9 +12,8 @@
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
 #include "chromecast/browser/cast_content_window.h"
-#include "chromecast/browser/cast_web_view.h"
+#include "chromecast/browser/mojom/cast_web_service.mojom.h"
 #include "components/cast_receiver/browser/public/runtime_application.h"
-#include "components/cast_receiver/common/public/status.h"
 #include "components/url_rewrite/mojom/url_request_rewrite.mojom.h"
 #include "third_party/cast_core/public/src/proto/common/application_config.pb.h"
 #include "third_party/cast_core/public/src/proto/common/application_state.pb.h"
@@ -27,7 +26,6 @@
 namespace chromecast {
 
 class CastWebContents;
-class CastWebService;
 class MessagePortService;
 
 // This class is for sharing code between Web and streaming RuntimeApplication
@@ -65,6 +63,14 @@
     // Creates a new platform-specific WebUIControllerFactory.
     virtual std::unique_ptr<content::WebUIControllerFactory>
     CreateWebUIControllerFactory(std::vector<std::string> hosts) = 0;
+
+    // Returns the WebContents this application should use.
+    virtual content::WebContents* GetWebContents() = 0;
+
+    // Returns the CastContentWindow associated with this application.
+    //
+    // TODO(crbug.com/1359559): Remove this function.
+    virtual CastContentWindow* GetCastContentWindow() = 0;
   };
 
   ~RuntimeApplicationBase() override;
@@ -94,6 +100,21 @@
   // Sets touch input.
   void SetTouchInput(cast::common::TouchInput::Type touch_input);
 
+  // Returns if current session is enabled for dev.
+  //
+  // TODO(crbug.com/1359587): Remove this function.
+  bool GetEnabledForDev() const;
+
+  // Returns if remote control mode is enabled.
+  //
+  // TODO(crbug.com/1359587): Remove this function.
+  bool GetIsRemoteControlMode() const;
+
+  // Returns the type of Renderer to be used for this application.
+  //
+  // TODO(crbug.com/1359587): Remove this function.
+  mojom::RendererType GetRendererType() const;
+
   // Called to launch the application. The |callback| will be called indicating
   // if the operation succeeded or not.
   virtual void Launch(StatusCallback callback) = 0;
@@ -112,8 +133,7 @@
   // |web_service| is expected to exist for the lifetime of this instance.
   RuntimeApplicationBase(std::string cast_session_id,
                          cast::common::ApplicationConfig app_config,
-                         mojom::RendererType renderer_type_used,
-                         CastWebService* web_service);
+                         mojom::RendererType renderer_type);
 
   // Stops the running application. Must be called before destruction of any
   // instance of the implementing object.
@@ -124,11 +144,6 @@
     return task_runner_;
   }
 
-  CastWebContents* cast_web_contents() {
-    DCHECK(cast_web_view_);
-    return cast_web_view_->cast_web_contents();
-  }
-
   Delegate& delegate() { return *delegate_; }
 
   // NOTE: This field is empty until after Load() is called.
@@ -140,9 +155,6 @@
   // Returns if app is audio only.
   bool GetIsAudioOnly() const;
 
-  // Returns if remote control mode is enabled.
-  bool GetIsRemoteControlMode() const;
-
   // Returns if feature permissions are enforced.
   bool GetEnforceFeaturePermissions() const;
 
@@ -152,9 +164,6 @@
   // Returns additional feature permission origins.
   std::vector<std::string> GetAdditionalFeaturePermissionOrigins() const;
 
-  // Returns if current session is enabled for dev.
-  bool GetEnabledForDev() const;
-
   // Loads the page at the given |url| in the CastWebContents.
   void LoadPage(const GURL& url);
 
@@ -165,15 +174,12 @@
   // CastContentWindow::Observer implementation:
   void OnVisibilityChange(VisibilityType visibility_type) override;
 
-  // Creates the root CastWebView for this Cast session.
-  CastWebView::Scoped CreateCastWebView();
-
   const std::string cast_session_id_;
   const cast::common::ApplicationConfig app_config_;
+
   // Renderer type used by this application.
   mojom::RendererType renderer_type_;
-  // The |web_service_| used to create |cast_web_view_|.
-  CastWebService* const web_service_;
+
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
   base::raw_ptr<Delegate> delegate_{nullptr};
@@ -181,10 +187,6 @@
   // SetUrlRewriteRules is called.
   url_rewrite::mojom::UrlRequestRewriteRulesPtr cached_mojom_rules_{nullptr};
 
-  // The WebView associated with the window in which the Cast application is
-  // displayed.
-  CastWebView::Scoped cast_web_view_;
-
   // Flags whether the application is running or stopped.
   bool is_application_running_ = false;
 
diff --git a/chromecast/cast_core/runtime/browser/runtime_application_dispatcher.h b/chromecast/cast_core/runtime/browser/runtime_application_dispatcher.h
index a418a65..fe7e2482 100644
--- a/chromecast/cast_core/runtime/browser/runtime_application_dispatcher.h
+++ b/chromecast/cast_core/runtime/browser/runtime_application_dispatcher.h
@@ -5,25 +5,12 @@
 #ifndef CHROMECAST_CAST_CORE_RUNTIME_BROWSER_RUNTIME_APPLICATION_DISPATCHER_H_
 #define CHROMECAST_CAST_CORE_RUNTIME_BROWSER_RUNTIME_APPLICATION_DISPATCHER_H_
 
-#include <memory>
-
 #include "components/cast_receiver/common/public/status.h"
 
-namespace cast_receiver {
-class ApplicationClient;
-}
-
 namespace chromecast {
 
-class CastWebService;
-
 class RuntimeApplicationDispatcher {
  public:
-  // Creates an instance of |RuntimeApplicationDispatcher|.
-  static std::unique_ptr<RuntimeApplicationDispatcher> Create(
-      cast_receiver::ApplicationClient& application_client,
-      CastWebService* web_service);
-
   // |application_client| is expected to persist for the lifetime of this
   // instance.
   virtual ~RuntimeApplicationDispatcher();
diff --git a/chromecast/cast_core/runtime/browser/runtime_application_dispatcher_base.h b/chromecast/cast_core/runtime/browser/runtime_application_dispatcher_base.h
index 20cbf6c..2783f2b 100644
--- a/chromecast/cast_core/runtime/browser/runtime_application_dispatcher_base.h
+++ b/chromecast/cast_core/runtime/browser/runtime_application_dispatcher_base.h
@@ -24,11 +24,10 @@
 template <typename TRuntimeApplicationPlatform>
 class RuntimeApplicationDispatcherBase : public RuntimeApplicationDispatcher {
  public:
-  // |application_client| and |web_service| are expected to persist for the
-  // lifetime of this instance.
-  RuntimeApplicationDispatcherBase(
-      cast_receiver::ApplicationClient& application_client,
-      CastWebService* web_service);
+  // |application_client| is expected to persist for the lifetime of this
+  // instance.
+  explicit RuntimeApplicationDispatcherBase(
+      cast_receiver::ApplicationClient& application_client);
   ~RuntimeApplicationDispatcherBase() override = default;
 
  protected:
@@ -55,12 +54,9 @@
     return *application_client_;
   }
 
-  CastWebService& cast_web_service() { return *web_service_; }
-
  private:
   SEQUENCE_CHECKER(sequence_checker_);
   base::raw_ref<cast_receiver::ApplicationClient> const application_client_;
-  base::raw_ptr<CastWebService> const web_service_;
 
   base::flat_map<std::string, std::unique_ptr<TRuntimeApplicationPlatform>>
       loaded_apps_;
@@ -69,11 +65,8 @@
 template <typename TRuntimeApplicationPlatform>
 RuntimeApplicationDispatcherBase<TRuntimeApplicationPlatform>::
     RuntimeApplicationDispatcherBase(
-        cast_receiver::ApplicationClient& application_client,
-        CastWebService* web_service)
-    : application_client_(application_client), web_service_(web_service) {
-  DCHECK(web_service_);
-}
+        cast_receiver::ApplicationClient& application_client)
+    : application_client_(application_client) {}
 
 template <typename TRuntimeApplicationPlatform>
 TRuntimeApplicationPlatform*
@@ -86,10 +79,10 @@
   std::unique_ptr<RuntimeApplicationBase> app;
   if (openscreen::cast::IsCastStreamingReceiverAppId(app_config.app_id())) {
     app = std::make_unique<StreamingRuntimeApplication>(
-        session_id, std::move(app_config), web_service_, *application_client_);
+        session_id, std::move(app_config), *application_client_);
   } else {
-    app = std::make_unique<WebRuntimeApplication>(
-        session_id, std::move(app_config), web_service_);
+    app = std::make_unique<WebRuntimeApplication>(session_id,
+                                                  std::move(app_config));
   }
 
   // TODO(b/232140331): Call this only when foreground app changes.
diff --git a/chromecast/cast_core/runtime/browser/runtime_application_service_impl.cc b/chromecast/cast_core/runtime/browser/runtime_application_service_impl.cc
index 9773b055..7cddacdce 100644
--- a/chromecast/cast_core/runtime/browser/runtime_application_service_impl.cc
+++ b/chromecast/cast_core/runtime/browser/runtime_application_service_impl.cc
@@ -5,18 +5,23 @@
 #include "chromecast/cast_core/runtime/browser/runtime_application_service_impl.h"
 
 #include "base/task/bind_post_task.h"
+#include "chromecast/browser/cast_web_service.h"
+#include "chromecast/browser/cast_web_view.h"
 #include "chromecast/cast_core/grpc/grpc_status_or.h"
 #include "chromecast/cast_core/runtime/browser/grpc_webui_controller_factory.h"
 #include "chromecast/cast_core/runtime/browser/message_port_service_grpc.h"
+#include "chromecast/cast_core/runtime/browser/runtime_application_base.h"
 #include "chromecast/cast_core/runtime/browser/url_rewrite/url_request_rewrite_type_converters.h"
 
 namespace chromecast {
 
 RuntimeApplicationServiceImpl::RuntimeApplicationServiceImpl(
     std::unique_ptr<RuntimeApplicationBase> runtime_application,
-    scoped_refptr<base::SequencedTaskRunner> task_runner)
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    CastWebService& web_service)
     : runtime_application_(std::move(runtime_application)),
-      task_runner_(std::move(task_runner)) {
+      task_runner_(std::move(task_runner)),
+      web_service_(web_service) {
   DCHECK(runtime_application_);
   DCHECK(task_runner_);
 
@@ -84,6 +89,7 @@
           request.url_rewrite_rules());
   runtime_application_->SetUrlRewriteRules(std::move(mojom_rules));
 
+  cast_web_view_ = CreateCastWebView();
   runtime_application_->Load(std::move(callback));
 }
 
@@ -148,6 +154,21 @@
   }
 }
 
+CastWebView::Scoped RuntimeApplicationServiceImpl::CreateCastWebView() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  mojom::CastWebViewParamsPtr params = mojom::CastWebViewParams::New();
+  params->renderer_type = runtime_application_->GetRendererType();
+  params->handle_inner_contents = true;
+  params->session_id = runtime_application_->GetCastSessionId();
+  params->is_remote_control_mode =
+      runtime_application_->GetIsRemoteControlMode();
+  params->activity_id = params->is_remote_control_mode
+                            ? params->session_id
+                            : runtime_application_->GetAppId();
+  params->enabled_for_dev = runtime_application_->GetEnabledForDev();
+  return web_service_->CreateWebViewInternal(std::move(params));
+}
+
 void RuntimeApplicationServiceImpl::HandleSetUrlRewriteRules(
     cast::v2::SetUrlRewriteRulesRequest request,
     cast::v2::RuntimeApplicationServiceHandler::SetUrlRewriteRules::Reactor*
@@ -294,6 +315,22 @@
                                                       &core_app_stub_.value());
 }
 
+content::WebContents* RuntimeApplicationServiceImpl::GetWebContents() {
+  if (!cast_web_view_) {
+    return nullptr;
+  }
+
+  return cast_web_view_->web_contents();
+}
+
+CastContentWindow* RuntimeApplicationServiceImpl::GetCastContentWindow() {
+  if (!cast_web_view_) {
+    return nullptr;
+  }
+
+  return cast_web_view_->window();
+}
+
 void RuntimeApplicationServiceImpl::OnAllBindingsReceived(
     GetAllBindingsCallback callback,
     cast::utils::GrpcStatusOr<cast::bindings::GetAllResponse> response_or) {
diff --git a/chromecast/cast_core/runtime/browser/runtime_application_service_impl.h b/chromecast/cast_core/runtime/browser/runtime_application_service_impl.h
index 2b711f3e..c2d8856 100644
--- a/chromecast/cast_core/runtime/browser/runtime_application_service_impl.h
+++ b/chromecast/cast_core/runtime/browser/runtime_application_service_impl.h
@@ -9,6 +9,8 @@
 #include <string>
 #include <vector>
 
+#include "chromecast/browser/cast_content_window.h"
+#include "chromecast/browser/cast_web_view.h"
 #include "chromecast/cast_core/grpc/grpc_server.h"
 #include "chromecast/cast_core/runtime/browser/runtime_application_base.h"
 #include "components/cast_receiver/browser/public/runtime_application.h"
@@ -22,12 +24,15 @@
 #include "third_party/cast_core/public/src/proto/web/message_channel.pb.h"
 
 namespace content {
+class WebContents;
 class WebUIControllerFactory;
 }
 
 namespace chromecast {
 
+class CastContentWindow;
 class MessagePortService;
+class RuntimeApplicationBase;
 
 class RuntimeApplicationServiceImpl : public RuntimeApplicationBase::Delegate {
  public:
@@ -35,11 +40,10 @@
 
   RuntimeApplicationServiceImpl(
       std::unique_ptr<RuntimeApplicationBase> runtime_application,
-      scoped_refptr<base::SequencedTaskRunner> task_runner);
+      scoped_refptr<base::SequencedTaskRunner> task_runner,
+      CastWebService& web_service);
   ~RuntimeApplicationServiceImpl() override;
 
-  const std::string& app_id() const { return runtime_application_->GetAppId(); }
-
   void Load(const cast::runtime::LoadApplicationRequest& request,
             StatusCallback callback);
   void Launch(const cast::runtime::LaunchApplicationRequest& request,
@@ -47,7 +51,9 @@
   void Stop(const cast::runtime::StopApplicationRequest& request,
             StatusCallback callback);
 
-  // RuntimeApplicationBase::Delegate implementation:
+  const std::string& app_id() { return runtime_application_->GetAppId(); }
+
+  // RuntimeApplication::Delegate implementation:
   void NotifyApplicationStarted() override;
   void NotifyApplicationStopped(cast::common::StopReason::Type stop_reason,
                                 int32_t net_error_code) override;
@@ -56,9 +62,14 @@
   std::unique_ptr<MessagePortService> CreateMessagePortService() override;
   std::unique_ptr<content::WebUIControllerFactory> CreateWebUIControllerFactory(
       std::vector<std::string> hosts) override;
+  content::WebContents* GetWebContents() override;
+  CastContentWindow* GetCastContentWindow() override;
 
  private:
-  // RuntimeApplicationBase handlers:
+  // Creates the root CastWebView for this Cast session.
+  CastWebView::Scoped CreateCastWebView();
+
+  // RuntimeApplicationService handlers:
   void HandleSetUrlRewriteRules(
       cast::v2::SetUrlRewriteRulesRequest request,
       cast::v2::RuntimeApplicationServiceHandler::SetUrlRewriteRules::Reactor*
@@ -88,6 +99,12 @@
   std::unique_ptr<RuntimeApplicationBase> const runtime_application_;
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
+  base::raw_ref<CastWebService> web_service_;
+
+  // The WebView associated with the window in which the Cast application is
+  // displayed.
+  CastWebView::Scoped cast_web_view_;
+
   absl::optional<cast::utils::GrpcServer> grpc_server_;
   absl::optional<cast::v2::CoreApplicationServiceStub> core_app_stub_;
   absl::optional<cast::v2::CoreMessagePortApplicationServiceStub>
diff --git a/chromecast/cast_core/runtime/browser/runtime_service_impl.cc b/chromecast/cast_core/runtime/browser/runtime_service_impl.cc
index c4a998f6..7531dd1 100644
--- a/chromecast/cast_core/runtime/browser/runtime_service_impl.cc
+++ b/chromecast/cast_core/runtime/browser/runtime_service_impl.cc
@@ -25,30 +25,16 @@
 
 }  // namespace
 
-std::unique_ptr<RuntimeApplicationDispatcher>
-RuntimeApplicationDispatcher::Create(
-    cast_receiver::ApplicationClient& application_client,
-    CastWebService* web_service) {
-  auto* command_line = base::CommandLine::ForCurrentProcess();
-  std::string runtime_id =
-      command_line->GetSwitchValueASCII(cast::core::kCastCoreRuntimeIdSwitch);
-  std::string runtime_service_path =
-      command_line->GetSwitchValueASCII(cast::core::kRuntimeServicePathSwitch);
-
-  LOG(INFO) << "gRPC platform created";
-  return std::make_unique<RuntimeServiceImpl>(application_client, web_service,
-                                              runtime_id, runtime_service_path);
-}
-
 RuntimeServiceImpl::RuntimeServiceImpl(
     cast_receiver::ApplicationClient& application_client,
-    CastWebService* web_service,
+    CastWebService& web_service,
     std::string runtime_id,
     std::string runtime_service_endpoint)
-    : Base(application_client, web_service),
+    : Base(application_client),
       runtime_id_(std::move(runtime_id)),
       runtime_service_endpoint_(std::move(runtime_service_endpoint)),
       task_runner_(base::SequencedTaskRunnerHandle::Get()),
+      web_service_(web_service),
       metrics_recorder_(this) {
   heartbeat_timer_.SetTaskRunner(task_runner_);
 }
@@ -166,11 +152,13 @@
       request.cast_session_id(), request.application_config(),
       base::BindOnce(
           [](scoped_refptr<base::SequencedTaskRunner> task_runner,
+             CastWebService& web_service,
              std::unique_ptr<RuntimeApplicationBase> runtime_application) {
             return std::make_unique<RuntimeApplicationServiceImpl>(
-                std::move(runtime_application), std::move(task_runner));
+                std::move(runtime_application), std::move(task_runner),
+                web_service);
           },
-          task_runner_));
+          task_runner_, std::ref(*web_service_)));
   platform_app->Load(
       request,
       base::BindPostTask(
diff --git a/chromecast/cast_core/runtime/browser/runtime_service_impl.h b/chromecast/cast_core/runtime/browser/runtime_service_impl.h
index 90d5262..b03fb2db 100644
--- a/chromecast/cast_core/runtime/browser/runtime_service_impl.h
+++ b/chromecast/cast_core/runtime/browser/runtime_service_impl.h
@@ -32,10 +32,10 @@
     : public RuntimeApplicationDispatcherBase<RuntimeApplicationServiceImpl>,
       public CastRuntimeMetricsRecorder::EventBuilderFactory {
  public:
-  // |application_client| is expected to persist for the lifetime of this
-  // instance.
+  // |application_client| and |web_service| are expected to persist for the
+  // lifetime of this instance.
   RuntimeServiceImpl(cast_receiver::ApplicationClient& application_client,
-                     CastWebService* web_service,
+                     CastWebService& web_service,
                      std::string runtime_id,
                      std::string runtime_service_endpoint);
   ~RuntimeServiceImpl() override;
@@ -108,6 +108,8 @@
   SEQUENCE_CHECKER(sequence_checker_);
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
+  base::raw_ref<CastWebService> const web_service_;
+
   // Allows metrics, histogram, action recording, which can be reported by
   // CastRuntimeMetricsRecorderService if Cast Core starts it.
   CastRuntimeMetricsRecorder metrics_recorder_;
diff --git a/chromecast/cast_core/runtime/browser/streaming_runtime_application.cc b/chromecast/cast_core/runtime/browser/streaming_runtime_application.cc
index 45e823b..354006c 100644
--- a/chromecast/cast_core/runtime/browser/streaming_runtime_application.cc
+++ b/chromecast/cast_core/runtime/browser/streaming_runtime_application.cc
@@ -34,12 +34,10 @@
 StreamingRuntimeApplication::StreamingRuntimeApplication(
     std::string cast_session_id,
     cast::common::ApplicationConfig app_config,
-    CastWebService* web_service,
     cast_receiver::ApplicationClient& application_client)
     : RuntimeApplicationBase(std::move(cast_session_id),
                              std::move(app_config),
-                             mojom::RendererType::MOJO_RENDERER,
-                             web_service),
+                             mojom::RendererType::MOJO_RENDERER),
       application_client_(application_client) {}
 
 StreamingRuntimeApplication::~StreamingRuntimeApplication() {
@@ -98,7 +96,7 @@
   // Initialize the streaming receiver.
   receiver_session_client_ = std::make_unique<StreamingReceiverSessionClient>(
       task_runner(), application_client_->GetNetworkContextGetter(),
-      std::move(server_port), cast_web_contents()->web_contents(), this,
+      std::move(server_port), delegate().GetWebContents(), this,
       /* supports_audio= */ config().app_id() !=
           openscreen::cast::GetIosAppStreamingAudioVideoAppId(),
       /* supports_video= */ true);
diff --git a/chromecast/cast_core/runtime/browser/streaming_runtime_application.h b/chromecast/cast_core/runtime/browser/streaming_runtime_application.h
index cf9ea9f6..cfa92c4 100644
--- a/chromecast/cast_core/runtime/browser/streaming_runtime_application.h
+++ b/chromecast/cast_core/runtime/browser/streaming_runtime_application.h
@@ -26,7 +26,6 @@
   StreamingRuntimeApplication(
       std::string cast_session_id,
       cast::common::ApplicationConfig app_config,
-      CastWebService* web_service,
       cast_receiver::ApplicationClient& application_client);
   ~StreamingRuntimeApplication() override;
 
diff --git a/chromecast/cast_core/runtime/browser/web_runtime_application.cc b/chromecast/cast_core/runtime/browser/web_runtime_application.cc
index f58f29e..6e66b1a1 100644
--- a/chromecast/cast_core/runtime/browser/web_runtime_application.cc
+++ b/chromecast/cast_core/runtime/browser/web_runtime_application.cc
@@ -20,12 +20,10 @@
 
 WebRuntimeApplication::WebRuntimeApplication(
     std::string cast_session_id,
-    cast::common::ApplicationConfig config,
-    CastWebService* web_service)
+    cast::common::ApplicationConfig config)
     : RuntimeApplicationBase(std::move(cast_session_id),
                              std::move(config),
-                             mojom::RendererType::MOJO_RENDERER,
-                             web_service),
+                             mojom::RendererType::MOJO_RENDERER),
       app_url_(RuntimeApplicationBase::config().cast_web_app_config().url()) {}
 
 WebRuntimeApplication::~WebRuntimeApplication() {
@@ -73,7 +71,8 @@
 
   CastWebContents* inner_cast_contents =
       CastWebContents::FromWebContents(inner_web_contents);
-  CastWebContents* outer_cast_contents = cast_web_contents();
+  CastWebContents* outer_cast_contents =
+      CastWebContents::FromWebContents(delegate().GetWebContents());
   DCHECK(inner_cast_contents);
   DCHECK(outer_cast_contents);
 
@@ -125,15 +124,14 @@
     return;
   }
 
-  content::WebContentsObserver::Observe(cast_web_contents()->web_contents());
-  cast_receiver::PageStateObserver::Observe(
-      cast_web_contents()->web_contents());
+  content::WebContentsObserver::Observe(delegate().GetWebContents());
+  cast_receiver::PageStateObserver::Observe(delegate().GetWebContents());
   bindings_manager_ = std::make_unique<BindingsManagerWebRuntime>(
       *this, delegate().CreateMessagePortService());
   for (auto& binding : bindings) {
     bindings_manager_->AddBinding(std::move(binding));
   }
-  bindings_manager_->ConfigureWebContents(cast_web_contents()->web_contents());
+  bindings_manager_->ConfigureWebContents(delegate().GetWebContents());
 
   // Application is initialized now - we can load the URL.
   LoadPage(app_url_);
diff --git a/chromecast/cast_core/runtime/browser/web_runtime_application.h b/chromecast/cast_core/runtime/browser/web_runtime_application.h
index 43f4d46..5e69bf5 100644
--- a/chromecast/cast_core/runtime/browser/web_runtime_application.h
+++ b/chromecast/cast_core/runtime/browser/web_runtime_application.h
@@ -15,7 +15,6 @@
 namespace chromecast {
 
 class BindingsManagerWebRuntime;
-class CastWebService;
 
 class WebRuntimeApplication final : public RuntimeApplicationBase,
                                     public content::WebContentsObserver,
@@ -24,8 +23,7 @@
  public:
   // |web_service| is expected to exist for the lifetime of this instance.
   WebRuntimeApplication(std::string cast_session_id,
-                        cast::common::ApplicationConfig app_config,
-                        CastWebService* web_service);
+                        cast::common::ApplicationConfig app_config);
   ~WebRuntimeApplication() override;
 
  private:
diff --git a/chromeos/ash/components/dbus/private_computing/BUILD.gn b/chromeos/ash/components/dbus/private_computing/BUILD.gn
new file mode 100644
index 0000000..f53fb60
--- /dev/null
+++ b/chromeos/ash/components/dbus/private_computing/BUILD.gn
@@ -0,0 +1,35 @@
+# 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("//third_party/protobuf/proto_library.gni")
+
+assert(is_chromeos_ash, "Non-ChromeOS builds cannot depend on //chromeos/ash")
+
+component("private_computing") {
+  defines = [ "IS_PRIVATE_COMPUTING_IMPL" ]
+
+  public_deps = [
+    ":private_computing_proto",
+    "//chromeos/ash/components/dbus",
+    "//chromeos/dbus/common",
+  ]
+
+  deps = [
+    "//base",
+    "//dbus",
+  ]
+
+  sources = [
+    "fake_private_computing_client.cc",
+    "fake_private_computing_client.h",
+    "private_computing_client.cc",
+    "private_computing_client.h",
+  ]
+}
+
+proto_library("private_computing_proto") {
+  sources = [ "//third_party/cros_system_api/dbus/private_computing/private_computing_service.proto" ]
+
+  proto_out_dir = "chromeos/ash/components/dbus/private_computing"
+}
diff --git a/chromeos/ash/components/dbus/private_computing/OWNERS b/chromeos/ash/components/dbus/private_computing/OWNERS
new file mode 100644
index 0000000..2b7b640
--- /dev/null
+++ b/chromeos/ash/components/dbus/private_computing/OWNERS
@@ -0,0 +1,3 @@
+hirthanan@google.com
+qianwan@google.com
+nsinghal@google.com
diff --git a/chromeos/ash/components/dbus/private_computing/fake_private_computing_client.cc b/chromeos/ash/components/dbus/private_computing/fake_private_computing_client.cc
new file mode 100644
index 0000000..0bc9ce1b
--- /dev/null
+++ b/chromeos/ash/components/dbus/private_computing/fake_private_computing_client.cc
@@ -0,0 +1,51 @@
+// 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/ash/components/dbus/private_computing/fake_private_computing_client.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "chromeos/ash/components/dbus/private_computing/private_computing_service.pb.h"
+
+namespace ash {
+
+FakePrivateComputingClient::FakePrivateComputingClient() = default;
+
+FakePrivateComputingClient::~FakePrivateComputingClient() = default;
+
+void FakePrivateComputingClient::SaveLastPingDatesStatus(
+    const private_computing::SaveStatusRequest& request,
+    SaveStatusCallback callback) {
+  private_computing::SaveStatusResponse response;
+  if (save_status_response_.has_value())
+    response = save_status_response_.value();
+  std::move(callback).Run(response);
+}
+
+void FakePrivateComputingClient::GetLastPingDatesStatus(
+    GetStatusCallback callback) {
+  private_computing::GetStatusResponse response;
+  if (get_status_response_.has_value())
+    response = get_status_response_.value();
+  std::move(callback).Run(response);
+}
+
+PrivateComputingClient::TestInterface*
+FakePrivateComputingClient::GetTestInterface() {
+  return this;
+}
+
+void FakePrivateComputingClient::SetSaveLastPingDatesStatusResponse(
+    private_computing::SaveStatusResponse response) {
+  save_status_response_ = response;
+}
+
+void FakePrivateComputingClient::SetGetLastPingDatesStatusResponse(
+    private_computing::GetStatusResponse response) {
+  get_status_response_ = response;
+}
+
+}  // namespace ash
diff --git a/chromeos/ash/components/dbus/private_computing/fake_private_computing_client.h b/chromeos/ash/components/dbus/private_computing/fake_private_computing_client.h
new file mode 100644
index 0000000..3dd4594
--- /dev/null
+++ b/chromeos/ash/components/dbus/private_computing/fake_private_computing_client.h
@@ -0,0 +1,48 @@
+// 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 CHROMEOS_ASH_COMPONENTS_DBUS_PRIVATE_COMPUTING_FAKE_PRIVATE_COMPUTING_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_PRIVATE_COMPUTING_FAKE_PRIVATE_COMPUTING_CLIENT_H_
+
+#include <string>
+
+#include "chromeos/ash/components/dbus/private_computing/private_computing_client.h"
+#include "chromeos/ash/components/dbus/private_computing/private_computing_service.pb.h"
+#include "dbus/object_proxy.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace ash {
+
+class COMPONENT_EXPORT(PRIVATE_COMPUTING) FakePrivateComputingClient
+    : public PrivateComputingClient,
+      public PrivateComputingClient::TestInterface {
+ public:
+  FakePrivateComputingClient();
+  FakePrivateComputingClient(const FakePrivateComputingClient&) = delete;
+  FakePrivateComputingClient& operator=(const FakePrivateComputingClient&) =
+      delete;
+  ~FakePrivateComputingClient() override;
+
+  // PrivateComputingClient implementation:
+  void SaveLastPingDatesStatus(
+      const private_computing::SaveStatusRequest& request,
+      SaveStatusCallback callback) override;
+  void GetLastPingDatesStatus(GetStatusCallback callback) override;
+
+  PrivateComputingClient::TestInterface* GetTestInterface() override;
+
+  // PrivateComputingClient::TestInterface implementation:
+  void SetSaveLastPingDatesStatusResponse(
+      private_computing::SaveStatusResponse response) override;
+  void SetGetLastPingDatesStatusResponse(
+      private_computing::GetStatusResponse response) override;
+
+ private:
+  absl::optional<private_computing::SaveStatusResponse> save_status_response_;
+  absl::optional<private_computing::GetStatusResponse> get_status_response_;
+};
+
+}  // namespace ash
+
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_PRIVATE_COMPUTING_FAKE_PRIVATE_COMPUTING_CLIENT_H_
diff --git a/chromeos/ash/components/dbus/private_computing/private_computing_client.cc b/chromeos/ash/components/dbus/private_computing/private_computing_client.cc
new file mode 100644
index 0000000..1fb2aad3
--- /dev/null
+++ b/chromeos/ash/components/dbus/private_computing/private_computing_client.cc
@@ -0,0 +1,178 @@
+// Copyright 2012 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/ash/components/dbus/private_computing/private_computing_client.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/strcat.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chromeos/ash/components/dbus/private_computing/fake_private_computing_client.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "dbus/object_proxy.h"
+#include "third_party/cros_system_api/dbus/private_computing/dbus-constants.h"
+
+namespace ash {
+namespace {
+
+PrivateComputingClient* g_instance = nullptr;
+
+const char kDbusCallFailure[] = "Failed to call private computing.";
+const char kProtoMessageParsingFailure[] =
+    "Failed to parse response message from private computing.";
+
+// Tries to parse a proto message from |response| into |proto| and returns null
+// if successful. If |response| is nullptr or the message cannot be parsed it
+// will return an appropriate error message.
+const char* DeserializeProto(dbus::Response* response,
+                             google::protobuf::MessageLite* proto) {
+  if (!response)
+    return kDbusCallFailure;
+
+  dbus::MessageReader reader(response);
+  if (!reader.PopArrayOfBytesAsProto(proto))
+    return kProtoMessageParsingFailure;
+
+  return nullptr;
+}
+
+// "Real" implementation of PrivateComputingClient talking to the
+// PrivateComputing daemon on the Chrome OS side.
+class PrivateComputingClientImpl : public PrivateComputingClient {
+ public:
+  PrivateComputingClientImpl() = default;
+
+  PrivateComputingClientImpl(const PrivateComputingClientImpl&) = delete;
+  PrivateComputingClientImpl& operator=(const PrivateComputingClientImpl&) =
+      delete;
+
+  ~PrivateComputingClientImpl() override = default;
+
+  void Init(dbus::Bus* bus) {
+    proxy_ = bus->GetObjectProxy(
+        private_computing::kPrivateComputingServiceName,
+        dbus::ObjectPath(private_computing::kPrivateComputingServicePath));
+  }
+
+  // PrivateComputingClient:
+  void SaveLastPingDatesStatus(
+      const private_computing::SaveStatusRequest& request,
+      SaveStatusCallback callback) override {
+    dbus::MethodCall method_call(private_computing::kPrivateComputingInterface,
+                                 private_computing::kSaveLastPingDatesStatus);
+    dbus::MessageWriter writer(&method_call);
+
+    if (!writer.AppendProtoAsArrayOfBytes(request)) {
+      private_computing::SaveStatusResponse response;
+      response.set_error_message(
+          base::StrCat({"Failure to call d-bus method: ",
+                        private_computing::kSaveLastPingDatesStatus}));
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(callback), response));
+      return;
+    }
+
+    proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(
+            &PrivateComputingClientImpl::HandleSaveLastPingDatesStatusResponse,
+            weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
+  void GetLastPingDatesStatus(GetStatusCallback callback) override {
+    dbus::MethodCall method_call(private_computing::kPrivateComputingInterface,
+                                 private_computing::kGetLastPingDatesStatus);
+    dbus::MessageWriter writer(&method_call);
+    proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(
+            &PrivateComputingClientImpl::HandleGetLastPingDatesStatusResponse,
+            weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
+  TestInterface* GetTestInterface() override { return nullptr; }
+
+ private:
+  // Handle a DBus response from the private computing chromeos daemon,
+  // invoking the callback that the method was originally called with the
+  // success response.
+  void HandleSaveLastPingDatesStatusResponse(SaveStatusCallback callback,
+                                             dbus::Response* response) {
+    private_computing::SaveStatusResponse response_proto;
+    const char* error_message = DeserializeProto(response, &response_proto);
+    if (error_message) {
+      LOG(ERROR)
+          << "Response from SaveLastPingDatesStatus contains error message "
+          << error_message;
+      response_proto.set_error_message(error_message);
+    }
+
+    std::move(callback).Run(response_proto);
+  }
+
+  // Handle a DBus response from the private computing chromeos daemon,
+  // invoking the callback that the method was originally called with the
+  // success response.
+  void HandleGetLastPingDatesStatusResponse(GetStatusCallback callback,
+                                            dbus::Response* response) {
+    private_computing::GetStatusResponse response_proto;
+    const char* error_message = DeserializeProto(response, &response_proto);
+    if (error_message) {
+      LOG(ERROR)
+          << "Response from GetLastPingDatesStatus contains error message "
+          << error_message;
+      response_proto.set_error_message(error_message);
+    }
+
+    std::move(callback).Run(response_proto);
+  }
+
+  raw_ptr<dbus::ObjectProxy> proxy_ = nullptr;
+
+  // Note: This should remain the last member so that it will be destroyed
+  // first, invalidating its weak pointers, before the other members are
+  // destroyed.
+  base::WeakPtrFactory<PrivateComputingClientImpl> weak_ptr_factory_{this};
+};
+
+}  // namespace
+
+PrivateComputingClient::PrivateComputingClient() {
+  DCHECK(!g_instance);
+  g_instance = this;
+}
+
+PrivateComputingClient::~PrivateComputingClient() {
+  DCHECK_EQ(this, g_instance);
+  g_instance = nullptr;
+}
+
+// static
+void PrivateComputingClient::Initialize(dbus::Bus* bus) {
+  DCHECK(bus);
+  (new PrivateComputingClientImpl())->Init(bus);
+}
+
+// static
+void PrivateComputingClient::InitializeFake() {
+  new FakePrivateComputingClient();
+}
+
+// static
+void PrivateComputingClient::Shutdown() {
+  DCHECK(g_instance);
+  delete g_instance;
+}
+
+// static
+PrivateComputingClient* PrivateComputingClient::Get() {
+  return g_instance;
+}
+
+}  // namespace ash
diff --git a/chromeos/ash/components/dbus/private_computing/private_computing_client.h b/chromeos/ash/components/dbus/private_computing/private_computing_client.h
new file mode 100644
index 0000000..69fd313
--- /dev/null
+++ b/chromeos/ash/components/dbus/private_computing/private_computing_client.h
@@ -0,0 +1,91 @@
+// 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 CHROMEOS_ASH_COMPONENTS_DBUS_PRIVATE_COMPUTING_PRIVATE_COMPUTING_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_PRIVATE_COMPUTING_PRIVATE_COMPUTING_CLIENT_H_
+
+#include "base/component_export.h"
+#include "base/functional/callback_forward.h"
+#include "chromeos/ash/components/dbus/private_computing/private_computing_service.pb.h"
+#include "dbus/object_proxy.h"
+
+namespace dbus {
+class Bus;
+}  // namespace dbus
+
+namespace ash {
+
+// PrivateComputingClient is used to communicate with the Chrome OS Private
+// Computing DBus daemon.
+// For example, this client will communicate over DBus to write to the
+// preserved file - last active dates, which the chronos browser user normally
+// doesn't have access to.
+class COMPONENT_EXPORT(PRIVATE_COMPUTING) PrivateComputingClient {
+ public:
+  using SaveStatusCallback = base::OnceCallback<void(
+      const private_computing::SaveStatusResponse response)>;
+  using GetStatusCallback = base::OnceCallback<void(
+      const private_computing::GetStatusResponse response)>;
+
+  using SaveStatusCall =
+      base::RepeatingCallback<void(const private_computing::SaveStatusRequest,
+                                   SaveStatusCallback)>;
+  using GetStatusCall = base::RepeatingCallback<void(GetStatusCallback)>;
+
+  // Interface with testing functionality. Accessed through
+  // GetTestInterface(), only implemented in the fake implementation.
+  class TestInterface {
+   public:
+    // Sets SaveStatusResponse proto.
+    virtual void SetSaveLastPingDatesStatusResponse(
+        private_computing::SaveStatusResponse response) = 0;
+
+    // Sets GetStatusResponse proto.
+    virtual void SetGetLastPingDatesStatusResponse(
+        private_computing::GetStatusResponse response) = 0;
+
+   protected:
+    virtual ~TestInterface() = default;
+  };
+
+  PrivateComputingClient(const PrivateComputingClient&) = delete;
+  PrivateComputingClient& operator=(const PrivateComputingClient&) = delete;
+
+  // Creates and initializes the global instance. |bus| must not be null.
+  static void Initialize(dbus::Bus* bus);
+
+  // Creates and initializes a fake global instance if not already created.
+  static void InitializeFake();
+
+  // Destroys the global instance which must have been initialized.
+  static void Shutdown();
+
+  // Returns the global instance if initialized. May return null.
+  static PrivateComputingClient* Get();
+
+  // SaveLastPingDatesStatus requests the private computing DBus daemon to
+  // write to the preserved file path to store last ping UTC dates by use
+  // case. Returns a proto representing whether operation was successful.
+  virtual void SaveLastPingDatesStatus(
+      const private_computing::SaveStatusRequest& request,
+      SaveStatusCallback callback) = 0;
+
+  // GetLastPingDatesStatus requests the private computing DBus daemon to
+  // retrieve the last ping UTC dates for each use case.
+  // Returns a response proto containing the last ping UTC dates or a string
+  // representing the error.
+  virtual void GetLastPingDatesStatus(GetStatusCallback callback) = 0;
+
+  // Returns an interface for testing (fake only), or returns nullptr.
+  virtual TestInterface* GetTestInterface() = 0;
+
+ protected:
+  // Initialize/Shutdown should be used instead.
+  PrivateComputingClient();
+  virtual ~PrivateComputingClient();
+};
+
+}  // namespace ash
+
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_PRIVATE_COMPUTING_PRIVATE_COMPUTING_CLIENT_H_
diff --git a/chromeos/ash/services/federated/public/cpp/service_connection.cc b/chromeos/ash/services/federated/public/cpp/service_connection.cc
index 1ef066b..e522856 100644
--- a/chromeos/ash/services/federated/public/cpp/service_connection.cc
+++ b/chromeos/ash/services/federated/public/cpp/service_connection.cc
@@ -19,9 +19,6 @@
 namespace federated {
 
 namespace {
-BASE_FEATURE(kShouldFederatedScheduleTasks,
-             "ShouldFederatedScheduleTasks",
-             base::FEATURE_DISABLED_BY_DEFAULT);
 
 ServiceConnection* g_fake_service_connection_for_testing = nullptr;
 
@@ -99,10 +96,6 @@
       platform_channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD(),
       base::BindOnce(&ServiceConnectionImpl::OnBootstrapMojoConnectionResponse,
                      base::Unretained(this)));
-
-  if (base::FeatureList::IsEnabled(kShouldFederatedScheduleTasks)) {
-    federated_service_->StartScheduling();
-  }
 }
 
 void ServiceConnectionImpl::OnMojoDisconnect() {
diff --git a/chromeos/crosapi/mojom/keystore_service.mojom b/chromeos/crosapi/mojom/keystore_service.mojom
index b98dcf3..dd7064ad 100644
--- a/chromeos/crosapi/mojom/keystore_service.mojom
+++ b/chromeos/crosapi/mojom/keystore_service.mojom
@@ -62,7 +62,7 @@
 // The name of a WebCrypto signing algorithm.
 [Stable, Extensible]
 enum KeystoreSigningAlgorithmName {
-  kUnknown = 0,
+  [Default] kUnknown = 0,
   kRsassaPkcs115 = 1,
   kEcdsa = 2,
 };
@@ -238,7 +238,8 @@
   // certificate with the migrated key.
   [MinVersion=15]
   ChallengeAttestationOnlyKeystore@20(
-      KeystoreType type, array<uint8> challenge, bool migrate) =>
+      KeystoreType type, array<uint8> challenge, bool migrate,
+      [MinVersion=17] KeystoreSigningAlgorithmName algorithm) =>
       (ChallengeAttestationOnlyKeystoreResult result);
 
   // Returns the keystores available to the client. These are used as inputs to
diff --git a/chromeos/dbus/power/power_manager_client.cc b/chromeos/dbus/power/power_manager_client.cc
index 6e4b6d6..adf9bc3 100644
--- a/chromeos/dbus/power/power_manager_client.cc
+++ b/chromeos/dbus/power/power_manager_client.cc
@@ -381,6 +381,8 @@
                       const std::string& description) override {
     POWER_LOG(USER) << "RequestRestart: " << reason << " (" << description
                     << ")";
+    for (auto& observer : observers_)
+      observer.RestartRequested(reason);
     dbus::MethodCall method_call(power_manager::kPowerManagerInterface,
                                  power_manager::kRequestRestartMethod);
     dbus::MessageWriter writer(&method_call);
diff --git a/chromeos/dbus/power/power_manager_client.h b/chromeos/dbus/power/power_manager_client.h
index 8a72c257..a0d02c7 100644
--- a/chromeos/dbus/power/power_manager_client.h
+++ b/chromeos/dbus/power/power_manager_client.h
@@ -23,6 +23,7 @@
 #include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
 #include "chromeos/dbus/power_manager/suspend.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/cros_system_api/dbus/power_manager/dbus-constants.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
 namespace base {
@@ -158,6 +159,11 @@
     // observer is ready for suspend.
     virtual void DarkSuspendImminent() {}
 
+    // Called when the browser is about to request system restart. Restart is
+    // deferred until all observers' implementations of this method have
+    // finished running.
+    virtual void RestartRequested(power_manager::RequestRestartReason reason) {}
+
     // Called when the browser is about to request shutdown. Shutdown is
     // deferred until all observers' implementations of this method have
     // finished running.
diff --git a/chromeos/dbus/power/power_manager_client_unittest.cc b/chromeos/dbus/power/power_manager_client_unittest.cc
index 5a1bf1a..8eac9f02 100644
--- a/chromeos/dbus/power/power_manager_client_unittest.cc
+++ b/chromeos/dbus/power/power_manager_client_unittest.cc
@@ -25,6 +25,7 @@
 #include "dbus/object_path.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/power_manager/dbus-constants.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
 using ::testing::_;
@@ -77,6 +78,15 @@
   return true;
 }
 
+// Matcher that verifies that a dbus::MethodCall has member |method_name|.
+MATCHER_P(IsRequestRestart, method_name, "") {
+  if (arg->GetMember() != method_name) {
+    *result_listener << "has member " << arg->GetMember();
+    return false;
+  }
+  return true;
+}
+
 // Runs |callback| with |response|. Needed due to ResponseCallback expecting a
 // bare pointer rather than an std::unique_ptr.
 void RunResponseCallback(dbus::ObjectProxy::ResponseCallback callback,
@@ -99,6 +109,7 @@
   int num_suspend_imminent() const { return num_suspend_imminent_; }
   int num_suspend_done() const { return num_suspend_done_; }
   int num_dark_suspend_imminent() const { return num_dark_suspend_imminent_; }
+  int num_restart_requested() const { return num_restart_requested_; }
   const base::UnguessableToken& block_suspend_token() const {
     return block_suspend_token_;
   }
@@ -147,15 +158,19 @@
   void AmbientColorChanged(const int32_t color_temperature) override {
     ambient_color_temperature_ = color_temperature;
   }
+  void RestartRequested(power_manager::RequestRestartReason reason) override {
+    num_restart_requested_++;
+  }
 
  private:
   PowerManagerClient* client_;  // Not owned.
 
-  // Number of times SuspendImminent(), SuspendDone(), and DarkSuspendImminent()
-  // have been called.
+  // Number of times SuspendImminent(), SuspendDone(), DarkSuspendImminent() and
+  // RestartRequested() have been called.
   int num_suspend_imminent_ = 0;
   int num_suspend_done_ = 0;
   int num_dark_suspend_imminent_ = 0;
+  int num_restart_requested_ = 0;
 
   // Should SuspendImminent() and DarkSuspendImminent() call |client_|'s
   // BlockSuspend() method?
@@ -703,4 +718,17 @@
   base::PowerMonitor::RemovePowerThermalObserver(&observer);
 }
 
+// Test that |RequestRestart| calls |RestartRequested| method for observers.
+TEST_F(PowerManagerClientTest, ObserverCalledAfterRequestRestart) {
+  TestObserver observer(client_);
+  EXPECT_CALL(*proxy_.get(),
+              DoCallMethod(IsRequestRestart("RequestRestart"), _, _));
+  EXPECT_EQ(0, observer.num_restart_requested());
+
+  client_->RequestRestart(
+      power_manager::RequestRestartReason::REQUEST_RESTART_OTHER,
+      "test restart");
+  EXPECT_EQ(1, observer.num_restart_requested());
+}
+
 }  // namespace chromeos
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index 9a7deb0..c65f7c6 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -326,6 +326,13 @@
   "login.ChangePassword.auth_factor_experiment_on",
   "login.ChangePassword.auth_factor_experiment_off",
 
+  # https://crbug.com/1379756
+  "inputs.PhysicalKeyboardGrammarCheck",
+  "inputs.PhysicalKeyboardGrammarCheck.lacros",
+  "inputs.VirtualKeyboardTypingUserMode.guest",
+  "inputs.VirtualKeyboardGlideTyping.clamshell_a11y_docked_lacros",
+  "inputs.VirtualKeyboardGlideTyping.clamshell_a11y_floating_lacros",
+
   # READ COMMENT AT TOP BEFORE ADDING NEW TESTS HERE.
 ]
 
diff --git a/components/BUILD.gn b/components/BUILD.gn
index f34ac5c..8c2f612 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -171,6 +171,7 @@
     "//components/memory_pressure:unit_tests",
     "//components/metrics:unit_tests",
     "//components/metrics/demographics:unit_tests",
+    "//components/named_mojo_ipc_server:unit_tests",
     "//components/navigation_metrics:unit_tests",
     "//components/net_log:unit_tests",
     "//components/network_session_configurator/browser:unit_tests",
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index fc18be6e..906a14eb 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -305,6 +305,8 @@
     "payments/full_card_request.h",
     "payments/iban_save_manager.cc",
     "payments/iban_save_manager.h",
+    "payments/iban_save_strike_database.cc",
+    "payments/iban_save_strike_database.h",
     "payments/legal_message_line.cc",
     "payments/legal_message_line.h",
     "payments/offer_notification_handler.cc",
diff --git a/components/autofill/core/browser/payments/iban_save_manager.cc b/components/autofill/core/browser/payments/iban_save_manager.cc
index 4f84883..7cda1476 100644
--- a/components/autofill/core/browser/payments/iban_save_manager.cc
+++ b/components/autofill/core/browser/payments/iban_save_manager.cc
@@ -5,12 +5,15 @@
 #include "components/autofill/core/browser/payments/iban_save_manager.h"
 
 #include "components/autofill/core/browser/data_model/iban.h"
+#include "components/autofill/core/browser/payments/iban_save_strike_database.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 
 namespace autofill {
 
-IBANSaveManager::IBANSaveManager(PersonalDataManager* personal_data_manager)
-    : personal_data_manager_(personal_data_manager) {}
+IBANSaveManager::IBANSaveManager(AutofillClient* client)
+    : personal_data_manager_(client->GetPersonalDataManager()),
+      iban_save_strike_database_(std::make_unique<IBANSaveStrikeDatabase>(
+          client->GetStrikeDatabase())) {}
 
 IBANSaveManager::~IBANSaveManager() = default;
 
@@ -20,10 +23,19 @@
     return false;
 
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-  iban_save_candidate_ = iban_import_candidate.value();
   // TODO(crbug.com/1349109): Display the IBAN save prompt, and pass in a
   // callback to OnUserDidDecideOnLocalSave() that will be called when the user
   // makes a decision on the IBAN save prompt.
+  // If the max strikes limit has been reached, do not show the IBAN save
+  // prompt.
+  if (iban_save_strike_database_->ShouldBlockFeature(
+          base::UTF16ToUTF8((*iban_import_candidate).value()))) {
+    return false;
+  }
+
+  // No conditions to abort offering IBAN save early were met, so show the IBAN
+  // save prompt.
+  iban_save_candidate_ = iban_import_candidate.value();
   return true;
 #else
   // IBAN save prompts do not currently exist on mobile.
@@ -43,12 +55,16 @@
 
   switch (user_decision) {
     case AutofillClient::SaveIBANOfferUserDecision::kAccepted:
-      // TODO(crbug.com/1349109): Clear all IBANSave strikes for this IBAN.
+      // Clear all IBANSave strikes for this IBAN, so that if it's later removed
+      // the strike count starts over with respect to re-saving it.
+      iban_save_strike_database_->ClearStrikes(
+          base::UTF16ToUTF8(iban_save_candidate_.value()));
       personal_data_manager_->OnAcceptedLocalIBANSave(iban_save_candidate_);
       break;
     case AutofillClient::SaveIBANOfferUserDecision::kIgnored:
     case AutofillClient::SaveIBANOfferUserDecision::kDeclined:
-      // TODO(crbug.com/1349109): Add IBANSave strike for this IBAN.
+      iban_save_strike_database_->AddStrike(
+          base::UTF16ToUTF8(iban_save_candidate_.value()));
       break;
   }
 }
diff --git a/components/autofill/core/browser/payments/iban_save_manager.h b/components/autofill/core/browser/payments/iban_save_manager.h
index 6b937f0..4d722ff 100644
--- a/components/autofill/core/browser/payments/iban_save_manager.h
+++ b/components/autofill/core/browser/payments/iban_save_manager.h
@@ -13,13 +13,14 @@
 
 namespace autofill {
 
+class IBANSaveStrikeDatabase;
 class PersonalDataManager;
 
 // Decides whether an IBAN local save should be offered and handles the workflow
 // for local saves.
 class IBANSaveManager {
  public:
-  explicit IBANSaveManager(PersonalDataManager* personal_data_manager);
+  explicit IBANSaveManager(AutofillClient* client);
   IBANSaveManager(const IBANSaveManager&) = delete;
   IBANSaveManager& operator=(const IBANSaveManager&) = delete;
   virtual ~IBANSaveManager();
@@ -35,8 +36,6 @@
     OnUserDidDecideOnLocalSave(user_decision, nickname);
   }
 
-  IBAN get_iban_save_candidate_for_testing() { return iban_save_candidate_; }
-
  private:
   // Called once the user makes a decision with respect to the local IBAN
   // offer-to-save-prompt. `nickname` is the nickname for the IBAN, which should
@@ -54,6 +53,9 @@
   // imported IBAN is not empty.
   IBAN iban_save_candidate_;
 
+  // StrikeDatabase used to check whether to offer to save the IBAN or not.
+  std::unique_ptr<IBANSaveStrikeDatabase> iban_save_strike_database_;
+
   base::WeakPtrFactory<IBANSaveManager> weak_ptr_factory_{this};
 };
 
diff --git a/components/autofill/core/browser/payments/iban_save_manager_unittest.cc b/components/autofill/core/browser/payments/iban_save_manager_unittest.cc
index e1dddee9..7ab5553 100644
--- a/components/autofill/core/browser/payments/iban_save_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/iban_save_manager_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/test/task_environment.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/iban.h"
+#include "components/autofill/core/browser/payments/iban_save_strike_database.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/autofill/core/common/autofill_prefs.h"
@@ -21,6 +22,10 @@
  public:
   IBANSaveManagerTest() {
     autofill_client_.SetPrefs(test::PrefServiceForTesting());
+    std::unique_ptr<TestStrikeDatabase> test_strike_database =
+        std::make_unique<TestStrikeDatabase>();
+    strike_database_ = test_strike_database.get();
+    autofill_client_.set_test_strike_database(std::move(test_strike_database));
     prefs::SetAutofillIBANEnabled(autofill_client_.GetPrefs(), true);
     personal_data().Init(/*profile_database=*/nullptr,
                          /*account_database=*/nullptr,
@@ -31,7 +36,7 @@
                          /*strike_database=*/nullptr,
                          /*image_fetcher=*/nullptr,
                          /*is_off_the_record=*/false);
-    iban_save_manager_ = std::make_unique<IBANSaveManager>(&personal_data());
+    iban_save_manager_ = std::make_unique<IBANSaveManager>(&autofill_client_);
   }
 
   void OnUserDidDecideOnLocalSave(
@@ -53,6 +58,7 @@
       std::make_unique<TestPersonalDataManager>()};
 
   std::unique_ptr<IBANSaveManager> iban_save_manager_;
+  raw_ptr<TestStrikeDatabase> strike_database_;
 };
 
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
@@ -124,6 +130,107 @@
   EXPECT_TRUE(personal_data().GetIBANs().empty());
 }
 
+TEST_F(IBANSaveManagerTest, LocallySaveIBAN_NotEnoughStrikesShouldOfferToSave) {
+  IBAN iban(base::GenerateGUID());
+  const std::u16string iban_value = u"DE91 1000 0000 0123 4567 89";
+  iban.set_value(iban_value);
+
+  IBANSaveStrikeDatabase iban_save_strike_database =
+      IBANSaveStrikeDatabase(strike_database_);
+
+  iban_save_strike_database.AddStrike(base::UTF16ToUTF8(iban_value));
+
+  // Verify `iban_value` has been successfully added to the strike database.
+  EXPECT_EQ(
+      1, iban_save_strike_database.GetStrikes(base::UTF16ToUTF8(iban_value)));
+  EXPECT_TRUE(iban_save_manager_->AttemptToOfferIBANLocalSave(iban));
+}
+
+TEST_F(IBANSaveManagerTest, LocallySaveIBAN_MaxStrikesShouldNotOfferToSave) {
+  IBAN iban(base::GenerateGUID());
+  const std::u16string iban_value = u"DE91 1000 0000 0123 4567 89";
+  iban.set_value(iban_value);
+
+  IBANSaveStrikeDatabase iban_save_strike_database =
+      IBANSaveStrikeDatabase(strike_database_);
+
+  for (int i = 0; i < iban_save_strike_database.GetMaxStrikesLimit(); ++i) {
+    iban_save_strike_database.AddStrike(base::UTF16ToUTF8(iban_value));
+  }
+  EXPECT_EQ(
+      iban_save_strike_database.GetMaxStrikesLimit(),
+      iban_save_strike_database.GetStrikes(base::UTF16ToUTF8(iban_value)));
+
+  EXPECT_FALSE(iban_save_manager_->AttemptToOfferIBANLocalSave(iban));
+}
+
+TEST_F(IBANSaveManagerTest, OnUserDidDecideOnLocalSave_Accepted_ClearsStrikes) {
+  IBAN iban(base::GenerateGUID());
+  const std::u16string iban_value = u"DE91 1000 0000 0123 4567 89";
+  iban.set_value(iban_value);
+  iban_save_manager_->AttemptToOfferIBANLocalSave(iban);
+
+  IBANSaveStrikeDatabase iban_save_strike_database =
+      IBANSaveStrikeDatabase(strike_database_);
+
+  iban_save_strike_database.AddStrike(base::UTF16ToUTF8(iban_value));
+
+  // Verify `iban_value` has been successfully added to the strike database.
+  EXPECT_EQ(
+      1, iban_save_strike_database.GetStrikes(base::UTF16ToUTF8(iban_value)));
+  OnUserDidDecideOnLocalSave(
+      AutofillClient::SaveIBANOfferUserDecision::kAccepted,
+      u"My teacher's IBAN");
+
+  // Verify `iban_value` has been cleared in the strike database.
+  EXPECT_EQ(
+      0, iban_save_strike_database.GetStrikes(base::UTF16ToUTF8(iban_value)));
+}
+
+TEST_F(IBANSaveManagerTest, OnUserDidDecideOnLocalSave_Declined_AddsStrike) {
+  IBAN iban(base::GenerateGUID());
+  const std::u16string iban_value = u"DE91 1000 0000 0123 4567 89";
+  iban.set_value(iban_value);
+  iban_save_manager_->AttemptToOfferIBANLocalSave(iban);
+
+  IBANSaveStrikeDatabase iban_save_strike_database =
+      IBANSaveStrikeDatabase(strike_database_);
+
+  // Verify `iban_value` has been successfully added to the strike database.
+  EXPECT_EQ(
+      0, iban_save_strike_database.GetStrikes(base::UTF16ToUTF8(iban_value)));
+
+  OnUserDidDecideOnLocalSave(
+      AutofillClient::SaveIBANOfferUserDecision::kDeclined,
+      u"My teacher's IBAN");
+
+  // Verify `iban_value` has been added to the strike database.
+  EXPECT_EQ(
+      1, iban_save_strike_database.GetStrikes(base::UTF16ToUTF8(iban_value)));
+}
+
+TEST_F(IBANSaveManagerTest, OnUserDidDecideOnLocalSave_Ignored_AddsStrike) {
+  IBAN iban(base::GenerateGUID());
+  const std::u16string iban_value = u"DE91 1000 0000 0123 4567 89";
+  iban.set_value(iban_value);
+  iban_save_manager_->AttemptToOfferIBANLocalSave(iban);
+
+  IBANSaveStrikeDatabase iban_save_strike_database =
+      IBANSaveStrikeDatabase(strike_database_);
+
+  // Verify `iban_value` has been successfully added to the strike database.
+  EXPECT_EQ(
+      0, iban_save_strike_database.GetStrikes(base::UTF16ToUTF8(iban_value)));
+
+  OnUserDidDecideOnLocalSave(
+      AutofillClient::SaveIBANOfferUserDecision::kDeclined,
+      u"My teacher's IBAN");
+
+  // Verify `iban_value` has been added to the strike database.
+  EXPECT_EQ(
+      1, iban_save_strike_database.GetStrikes(base::UTF16ToUTF8(iban_value)));
+}
+
 #endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/iban_save_strike_database.cc b/components/autofill/core/browser/payments/iban_save_strike_database.cc
new file mode 100644
index 0000000..4ce2d2a
--- /dev/null
+++ b/components/autofill/core/browser/payments/iban_save_strike_database.cc
@@ -0,0 +1,34 @@
+// 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/autofill/core/browser/payments/iban_save_strike_database.h"
+
+#include "components/autofill/core/browser/proto/strike_data.pb.h"
+
+namespace autofill {
+
+IBANSaveStrikeDatabase::IBANSaveStrikeDatabase(StrikeDatabase* strike_database)
+    : StrikeDatabaseIntegratorBase(strike_database) {
+  RemoveExpiredStrikes();
+}
+
+std::string IBANSaveStrikeDatabase::GetProjectPrefix() const {
+  return "IBANSave";
+}
+
+int IBANSaveStrikeDatabase::GetMaxStrikesLimit() const {
+  return 3;
+}
+
+absl::optional<base::TimeDelta> IBANSaveStrikeDatabase::GetExpiryTimeDelta()
+    const {
+  // Expiry time is 6 months.
+  return base::Days(183);
+}
+
+bool IBANSaveStrikeDatabase::UniqueIdsRequired() const {
+  return true;
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/payments/iban_save_strike_database.h b/components/autofill/core/browser/payments/iban_save_strike_database.h
new file mode 100644
index 0000000..562fe62
--- /dev/null
+++ b/components/autofill/core/browser/payments/iban_save_strike_database.h
@@ -0,0 +1,27 @@
+// 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_AUTOFILL_CORE_BROWSER_PAYMENTS_IBAN_SAVE_STRIKE_DATABASE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_IBAN_SAVE_STRIKE_DATABASE_H_
+
+#include "components/autofill/core/browser/strike_database.h"
+#include "components/autofill/core/browser/strike_database_integrator_base.h"
+
+namespace autofill {
+
+// Implementation of StrikeDatabaseIntegratorBase for IBAN save strike check.
+// Owned by IBANSaveManager.
+class IBANSaveStrikeDatabase : public StrikeDatabaseIntegratorBase {
+ public:
+  explicit IBANSaveStrikeDatabase(StrikeDatabase* strike_database);
+
+  std::string GetProjectPrefix() const override;
+  int GetMaxStrikesLimit() const override;
+  absl::optional<base::TimeDelta> GetExpiryTimeDelta() const override;
+  bool UniqueIdsRequired() const override;
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_IBAN_SAVE_STRIKE_DATABASE_H_
diff --git a/components/browser_ui/settings/android/BUILD.gn b/components/browser_ui/settings/android/BUILD.gn
index ac37dfb..a43f92e 100644
--- a/components/browser_ui/settings/android/BUILD.gn
+++ b/components/browser_ui/settings/android/BUILD.gn
@@ -73,7 +73,7 @@
     "java/res/layout/button_preference_button.xml",
     "java/res/layout/button_preference_layout.xml",
     "java/res/layout/checkable_image_view_widget.xml",
-    "java/res/layout/chrome_base_preference.xml",
+    "java/res/layout/chrome_managed_preference.xml",
     "java/res/layout/clickable_spans_text_message_preference_layout.xml",
     "java/res/layout/image_button_widget.xml",
     "java/res/layout/long_summary_text_message_preference.xml",
@@ -93,12 +93,16 @@
 android_library("unit_device_javatests") {
   testonly = true
   sources = [
-    "widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreferenceTest.java",
-    "widget/java/src/org/chromium/components/browser_ui/settings/ChromeImageViewPreferenceTest.java",
-    "widget/java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtilsTest.java",
+    "widget/javatests/src/org/chromium/components/browser_ui/settings/ChromeBasePreferenceTest.java",
+    "widget/javatests/src/org/chromium/components/browser_ui/settings/ChromeImageViewPreferenceTest.java",
+    "widget/javatests/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreferenceTest.java",
+    "widget/javatests/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtilsTest.java",
+    "widget/javatests/src/org/chromium/components/browser_ui/settings/Matchers.java",
   ]
   deps = [
     ":java",
+    ":java_resources",
+    ":java_test_resources",
     ":test_support_java",
     "//base:base_java",
     "//base:base_java_test_support",
@@ -113,17 +117,19 @@
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/hamcrest:hamcrest_java",
     "//third_party/junit",
+    "//third_party/mockito:mockito_java",
     "//ui/android:ui_java_test_support",
     "//ui/android:ui_no_recycler_view_java",
   ]
+  resources_package = "org.chromium.components.browser_ui.settings.test"
 }
 
 android_library("test_support_java") {
   testonly = true
 
   sources = [
-    "java/src/org/chromium/components/browser_ui/settings/BlankUiTestActivitySettingsTestRule.java",
-    "widget/java/src/org/chromium/components/browser_ui/settings/PlaceholderSettingsForTest.java",
+    "widget/javatests/src/org/chromium/components/browser_ui/settings/BlankUiTestActivitySettingsTestRule.java",
+    "widget/javatests/src/org/chromium/components/browser_ui/settings/PlaceholderSettingsForTest.java",
   ]
 
   deps = [
@@ -137,6 +143,20 @@
   ]
 }
 
+android_resources("java_test_resources") {
+  testonly = true
+  sources = [
+    "widget/javatests/res/layout/chrome_managed_preference_with_custom_layout.xml",
+    "widget/javatests/res/xml/test_chrome_base_preference_screen.xml",
+    "widget/javatests/res/xml/test_chrome_switch_preference_screen.xml",
+  ]
+  deps = [
+    ":java_resources",
+    "//third_party/androidx:androidx_preference_preference_java",
+    "//ui/android:ui_java_resources",
+  ]
+}
+
 generate_jni("settings_jni_headers") {
   sources = _jni_sources
 }
diff --git a/components/browser_ui/settings/android/java/res/layout/chrome_base_preference.xml b/components/browser_ui/settings/android/java/res/layout/chrome_managed_preference.xml
similarity index 92%
rename from components/browser_ui/settings/android/java/res/layout/chrome_base_preference.xml
rename to components/browser_ui/settings/android/java/res/layout/chrome_managed_preference.xml
index d951d3c..e5b221b4 100644
--- a/components/browser_ui/settings/android/java/res/layout/chrome_base_preference.xml
+++ b/components/browser_ui/settings/android/java/res/layout/chrome_managed_preference.xml
@@ -81,4 +81,11 @@
 
     </RelativeLayout>
 
+    <LinearLayout android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="64dp"
+        android:gravity="center_vertical|end"
+        android:orientation="vertical" />
+
 </LinearLayout>
diff --git a/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtils.java b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtils.java
index 9ab38c3db..ca91c47 100644
--- a/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtils.java
+++ b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtils.java
@@ -5,13 +5,16 @@
 package org.chromium.components.browser_ui.settings;
 
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
+import android.util.AttributeSet;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.annotation.DrawableRes;
+import androidx.annotation.LayoutRes;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
@@ -119,13 +122,13 @@
      *
      * @param delegate The delegate that controls whether the preference is managed. May be null,
      *         then this method does nothing.
-     * @param preference The Preference that is being initialized
+     * @param preference The Preference that is being initialized.
      */
     public static void initPreference(
             @Nullable ManagedPreferenceDelegate delegate, Preference preference) {
         if (delegate == null) return;
 
-        if (shouldApplyManagedIcon(delegate, preference)) {
+        if (!(preference instanceof ChromeImageViewPreference)) {
             preference.setIcon(getManagedIconDrawable(delegate, preference));
         }
 
@@ -156,6 +159,14 @@
             @Nullable ManagedPreferenceDelegate delegate, Preference preference, View view) {
         if (delegate == null) return;
 
+        // If disclaimer highlighting is enabled, perform binding taking into account that a
+        // disclaimer view may be available.
+        if (SettingsFeatureList.isEnabled(
+                    SettingsFeatureList.HIGHLIGHT_MANAGED_PREF_DISCLAIMER_ANDROID)) {
+            ManagedPreferencesUtils.onBindViewToChromeManagedPreference(delegate, preference, view);
+            return;
+        }
+
         if (delegate.isPreferenceClickDisabledByPolicy(preference)) {
             ViewUtils.setEnabledRecursive(view, false);
         }
@@ -177,11 +188,11 @@
      * @param preference The ChromeBasePreference that owns the view.
      * @param view The View that was bound to the ChromeBasePreference.
      */
-    public static void onBindViewToChromeBasePreference(
-            @Nullable ManagedPreferenceDelegate delegate, ChromeBasePreference preference,
-            View view) {
+    private static void onBindViewToChromeManagedPreference(
+            @Nullable ManagedPreferenceDelegate delegate, Preference preference, View view) {
         assert SettingsFeatureList.isEnabled(
                 SettingsFeatureList.HIGHLIGHT_MANAGED_PREF_DISCLAIMER_ANDROID);
+        assert delegate != null;
         if (delegate == null) return;
 
         if (delegate.isPreferenceClickDisabledByPolicy(preference)) {
@@ -202,6 +213,8 @@
         //                          by a custodian.
         if (view.findViewById(R.id.managed_disclaimer_text) != null
                 && delegate.isPreferenceControlledByPolicy(preference)) {
+            // Hide the icon since it will be shown on the highlighted managed disclaimer.
+            hideManagedIcon(preference, view);
             setSummaryWithHighlightedManagedInfo(preference.getContext(), descriptionText, view);
         } else {
             CharSequence managedDisclaimerText = getManagedDisclaimerText(delegate, preference);
@@ -277,6 +290,29 @@
     }
 
     /**
+     * Use {@code chrome_managed_preference} as the preference layout if a custom layout has not
+     * been set. That situation happens for example in the Sync and Google service preferences in
+     * the Main Settings menu, that define their own layouts and use this class to leverage icon
+     * tinting. Also, those preferences don't need to be managed, so there is no need to change
+     * their layouts to include the managed disclaimer.
+     * @param context The context for a given preference.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     * @return The layout resource to be used by a managed preference.
+     */
+    public static @LayoutRes int getLayoutResourceForPreference(
+            Context context, AttributeSet attrs) {
+        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Preference);
+
+        // Take the custom layout defined via either {@code Preference_layout} or
+        // {@code Preference_android_layout}. If neither is set, use
+        // {@code chrome_managed_preference} as fallback.
+        @LayoutRes
+        int fallback = a.getResourceId(
+                R.styleable.Preference_android_layout, R.layout.chrome_managed_preference);
+        return a.getResourceId(R.styleable.Preference_layout, fallback);
+    }
+
+    /**
      * @param descriptionText A description or a state for a given preference.
      * @param managedDisclaimerText The text the indicates that a preference is managed.
      * @param view The view corresponding to a given preference.
@@ -320,6 +356,24 @@
     }
 
     /**
+     * Hide the managed icon, to be used when the preference defines a custom layout and is managed
+     * by policy. In that case, the icon will be shown on the managed disclaimer view.
+     * @param preference The {@link Preference} that is being show to the user for a given
+     *         preference.
+     * @param view The view corresponding to a given preference.
+     */
+    private static void hideManagedIcon(Preference preference, View view) {
+        final ImageView imageView = (ImageView) view.findViewById(android.R.id.icon);
+        if (imageView != null) {
+            imageView.setVisibility(View.GONE);
+        }
+        final View imageFrame = view.findViewById(R.id.icon_frame);
+        if (imageFrame != null) {
+            imageFrame.setVisibility(View.GONE);
+        }
+    }
+
+    /**
      * @param delegate The delegate that controls whether the preference is managed. If null,
      *         then the preference is not managed.
      * @param preference The {@link Preference} that is being shown to the user.
@@ -396,28 +450,4 @@
         managedDisclaimerView.setVisibility(View.VISIBLE);
         managedDisclaimerView.setEnabled(true);
     }
-
-    /**
-     * @param delegate The delegate that controls whether the preference is managed. May be null,
-     *         then this method does nothing.
-     * @param preference The Preference that is being initialized
-     * @return Whether the preference's {@code icon} view should show a special managed icon.
-     */
-    private static boolean shouldApplyManagedIcon(
-            @Nullable ManagedPreferenceDelegate delegate, Preference preference) {
-        // Never replace the icon for {@link ChromeImageViewPreference}.
-        if (preference instanceof ChromeImageViewPreference) return false;
-
-        // Preferences managed by a custodian use the legacy UI that doesn't highlight the managed
-        // disclaimer, and thus should show the managed icon beside the title and summary.
-        // TODO(crbug.com/1378293): Apply highlighted managed disclaimer for preferences managed
-        //                          by a custodian.
-        if (delegate.isPreferenceControlledByCustodian(preference)) return true;
-
-        // For preferences controlled by policy, show the managed icon beside the title/summary
-        // only for the legacy UI. For the UI that highlights managed disclaimers, the icon will
-        // be shown next to the disclaimer text, hide it from the preference's view.
-        return !SettingsFeatureList.isEnabled(
-                SettingsFeatureList.HIGHLIGHT_MANAGED_PREF_DISCLAIMER_ANDROID);
-    }
 }
diff --git a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreference.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreference.java
index 765738c..f0d17e3 100644
--- a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreference.java
+++ b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreference.java
@@ -11,7 +11,6 @@
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 
-import androidx.annotation.LayoutRes;
 import androidx.annotation.Nullable;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceViewHolder;
@@ -58,20 +57,8 @@
 
         if (SettingsFeatureList.isEnabled(
                     SettingsFeatureList.HIGHLIGHT_MANAGED_PREF_DISCLAIMER_ANDROID)) {
-            // Use {@code chrome_base_preference.xml} as the preference layout if a custom layout
-            // has not been set. That situation happens for example in the Sync and Google service
-            // preferences in the Main Settings menu, that define their own layouts and use this
-            // class to leverage icon tinting. Also, those preferences don't need to be managed, so
-            // there is no need to change their layouts to include the managed disclaimer.
-            final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Preference);
-
-            // Take the custom layout defined via either {@code Preference_layout} or
-            // {@code Preference_android_layout}. If neither is set, use
-            // {@code chrome_base_preference} as fallback.
-            @LayoutRes
-            int fallback = a.getResourceId(
-                    R.styleable.Preference_android_layout, R.layout.chrome_base_preference);
-            setLayoutResource(a.getResourceId(R.styleable.Preference_layout, fallback));
+            setLayoutResource(
+                    ManagedPreferencesUtils.getLayoutResourceForPreference(context, attrs));
         }
 
         setSingleLineTitle(false);
@@ -99,14 +86,7 @@
             icon.setColorFilter(mIconTint.getDefaultColor(), PorterDuff.Mode.SRC_IN);
         }
 
-        if (SettingsFeatureList.isEnabled(
-                    SettingsFeatureList.HIGHLIGHT_MANAGED_PREF_DISCLAIMER_ANDROID)) {
-            ManagedPreferencesUtils.onBindViewToChromeBasePreference(
-                    mManagedPrefDelegate, this, holder.itemView);
-        } else {
-            ManagedPreferencesUtils.onBindViewToPreference(
-                    mManagedPrefDelegate, this, holder.itemView);
-        }
+        ManagedPreferencesUtils.onBindViewToPreference(mManagedPrefDelegate, this, holder.itemView);
 
         if (mDividerAllowedAbove != null) {
             holder.setDividerAllowedAbove(mDividerAllowedAbove);
diff --git a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreferenceTest.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreferenceTest.java
deleted file mode 100644
index cb9f208..0000000
--- a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreferenceTest.java
+++ /dev/null
@@ -1,222 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.browser_ui.settings;
-
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-
-import static org.hamcrest.Matchers.allOf;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.stringContainsInOrder;
-
-import android.app.Activity;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.preference.PreferenceFragmentCompat;
-import androidx.preference.PreferenceScreen;
-import androidx.test.espresso.ViewInteraction;
-import androidx.test.filters.SmallTest;
-
-import com.google.common.collect.ImmutableList;
-
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.hamcrest.TypeSafeMatcher;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.FeatureList;
-import org.chromium.base.test.params.ParameterAnnotations;
-import org.chromium.base.test.params.ParameterSet;
-import org.chromium.base.test.params.ParameterizedRunner;
-import org.chromium.base.test.util.Batch;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Tests of {@link ChromeBasePreference}.
- */
-@RunWith(ParameterizedRunner.class)
-@Batch(Batch.PER_CLASS)
-public class ChromeBasePreferenceTest {
-    @ClassRule
-    public static final DisableAnimationsTestRule disableAnimationsRule =
-            new DisableAnimationsTestRule();
-    @Rule
-    public final BlankUiTestActivitySettingsTestRule mSettingsRule =
-            new BlankUiTestActivitySettingsTestRule();
-
-    private static final String TITLE = "Preference Title";
-    private static final String SUMMARY = "This is a summary.";
-
-    private Activity mActivity;
-    private PreferenceFragmentCompat mPreferenceFragment;
-    private PreferenceScreen mPreferenceScreen;
-
-    private boolean mEnableHighlightManagedPrefDisclaimerAndroid;
-
-    public ChromeBasePreferenceTest(boolean enableHighlightManagedPrefDisclaimerAndroid) {
-        mEnableHighlightManagedPrefDisclaimerAndroid = enableHighlightManagedPrefDisclaimerAndroid;
-    }
-
-    @Before
-    public void setUp() {
-        FeatureList.TestValues testValuesOverride = new FeatureList.TestValues();
-        testValuesOverride.addFeatureFlagOverride(
-                SettingsFeatureList.HIGHLIGHT_MANAGED_PREF_DISCLAIMER_ANDROID,
-                mEnableHighlightManagedPrefDisclaimerAndroid);
-        FeatureList.setTestValues(testValuesOverride);
-
-        mSettingsRule.launchPreference(PlaceholderSettingsForTest.class);
-        mActivity = mSettingsRule.getActivity();
-        mPreferenceFragment = mSettingsRule.getPreferenceFragment();
-        mPreferenceScreen = mSettingsRule.getPreferenceScreen();
-    }
-
-    @Test
-    @SmallTest
-    public void testUnmanagedPreference() {
-        ChromeBasePreference preference = new ChromeBasePreference(mActivity);
-        preference.setTitle(TITLE);
-        preference.setSummary(SUMMARY);
-        preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.UNMANAGED_DELEGATE);
-        mPreferenceScreen.addPreference(preference);
-
-        Assert.assertTrue(preference.isEnabled());
-
-        getTitleView().check(matches(allOf(withText(TITLE), isDisplayed())));
-        getSummaryView().check(matches(allOf(withText(SUMMARY), isDisplayed())));
-        getIconView().check(matches(not(isDisplayed())));
-    }
-
-    @Test
-    @SmallTest
-    public void testPolicyManagedPreferenceWithoutSummary() {
-        ChromeBasePreference preference = new ChromeBasePreference(mActivity);
-        preference.setTitle(TITLE);
-        preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.POLICY_DELEGATE);
-        mPreferenceScreen.addPreference(preference);
-
-        Assert.assertFalse(preference.isEnabled());
-
-        getTitleView().check(matches(allOf(withText(TITLE), isDisplayed())));
-        if (mEnableHighlightManagedPrefDisclaimerAndroid) {
-            getManagedDisclaimerView().check(
-                    matches(allOf(withText(R.string.managed_by_your_organization),
-                            hasDrawableStart(), isDisplayed())));
-            getIconView().check(matches(not(isDisplayed())));
-        } else {
-            getSummaryView().check(
-                    matches(allOf(withText(R.string.managed_by_your_organization), isDisplayed())));
-            getIconView().check(matches(isDisplayed()));
-        }
-    }
-
-    @Test
-    @SmallTest
-    public void testPolicyManagedPreferenceWithSummary() {
-        ChromeBasePreference preference = new ChromeBasePreference(mActivity);
-        preference.setTitle(TITLE);
-        preference.setSummary(SUMMARY);
-        preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.POLICY_DELEGATE);
-        mPreferenceScreen.addPreference(preference);
-
-        Assert.assertFalse(preference.isEnabled());
-
-        getTitleView().check(matches(allOf(withText(TITLE), isDisplayed())));
-        if (mEnableHighlightManagedPrefDisclaimerAndroid) {
-            getSummaryView().check(matches(allOf(withText(SUMMARY), isDisplayed())));
-            getManagedDisclaimerView().check(
-                    matches(allOf(withText(R.string.managed_by_your_organization),
-                            hasDrawableStart(), isDisplayed())));
-            getIconView().check(matches(not(isDisplayed())));
-        } else {
-            List<String> expectedSummaryContains = ImmutableList.of(
-                    SUMMARY, mActivity.getString(R.string.managed_by_your_organization));
-            getSummaryView().check(matches(allOf(
-                    withText(stringContainsInOrder(expectedSummaryContains)), isDisplayed())));
-            getIconView().check(matches(isDisplayed()));
-        }
-    }
-
-    @Test
-    @SmallTest
-    public void testSingleCustodianManagedPreference() {
-        ChromeBasePreference preference = new ChromeBasePreference(mActivity);
-        preference.setTitle(TITLE);
-        preference.setManagedPreferenceDelegate(
-                ManagedPreferencesUtilsTest.SINGLE_CUSTODIAN_DELEGATE);
-        mPreferenceScreen.addPreference(preference);
-
-        Assert.assertFalse(preference.isEnabled());
-
-        getTitleView().check(matches(allOf(withText(TITLE), isDisplayed())));
-        getSummaryView().check(
-                matches(allOf(withText(R.string.managed_by_your_parent), isDisplayed())));
-        getIconView().check(matches(isDisplayed()));
-    }
-
-    @Test
-    @SmallTest
-    public void testMultipleCustodianManagedPreference() {
-        ChromeBasePreference preference = new ChromeBasePreference(mActivity);
-        preference.setTitle(TITLE);
-        preference.setManagedPreferenceDelegate(
-                ManagedPreferencesUtilsTest.MULTI_CUSTODIAN_DELEGATE);
-        mPreferenceScreen.addPreference(preference);
-
-        Assert.assertFalse(preference.isEnabled());
-
-        getTitleView().check(matches(allOf(withText(TITLE), isDisplayed())));
-        getSummaryView().check(
-                matches(allOf(withText(R.string.managed_by_your_parents), isDisplayed())));
-        getIconView().check(matches(isDisplayed()));
-    }
-
-    private ViewInteraction getTitleView() {
-        return onView(withId(android.R.id.title));
-    }
-
-    private ViewInteraction getSummaryView() {
-        return onView(withId(android.R.id.summary));
-    }
-
-    private ViewInteraction getManagedDisclaimerView() {
-        return onView(withId(R.id.managed_disclaimer_text));
-    }
-
-    private ViewInteraction getIconView() {
-        return onView(withId(android.R.id.icon));
-    }
-
-    private static Matcher<View> hasDrawableStart() {
-        return new TypeSafeMatcher<View>() {
-            @Override
-            protected boolean matchesSafely(View view) {
-                return ((TextView) view).getCompoundDrawablesRelative()[0] != null;
-            }
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("Expected TextView to define android:drawableStart");
-            }
-        };
-    }
-
-    @ParameterAnnotations.ClassParameter
-    private static List<ParameterSet> sClassParams = Arrays.asList(
-            new ParameterSet().value(true).name("EnableHighlightManagedPrefDisclaimerAndroid"),
-            new ParameterSet().value(false).name("DisableHighlightManagedPrefDisclaimerAndroid"));
-}
diff --git a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreference.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreference.java
index a33ab5f..5f4537a 100644
--- a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreference.java
+++ b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreference.java
@@ -27,11 +27,16 @@
     private Integer mBackgroundColorRes;
 
     public ChromeSwitchPreference(Context context) {
-        super(context);
+        this(context, null);
     }
 
     public ChromeSwitchPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
+        if (SettingsFeatureList.isEnabled(
+                    SettingsFeatureList.HIGHLIGHT_MANAGED_PREF_DISCLAIMER_ANDROID)) {
+            setLayoutResource(
+                    ManagedPreferencesUtils.getLayoutResourceForPreference(context, attrs));
+        }
     }
 
     /**
@@ -59,7 +64,8 @@
 
         mView = holder.itemView;
         updateBackground();
-        ManagedPreferencesUtils.onBindViewToPreference(mManagedPrefDelegate, this, mView);
+
+        ManagedPreferencesUtils.onBindViewToPreference(mManagedPrefDelegate, this, holder.itemView);
     }
 
     @Override
diff --git a/components/browser_ui/settings/android/widget/javatests/res/layout/chrome_managed_preference_with_custom_layout.xml b/components/browser_ui/settings/android/widget/javatests/res/layout/chrome_managed_preference_with_custom_layout.xml
new file mode 100644
index 0000000..94488c1
--- /dev/null
+++ b/components/browser_ui/settings/android/widget/javatests/res/layout/chrome_managed_preference_with_custom_layout.xml
@@ -0,0 +1,55 @@
+<?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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:orientation="horizontal">
+
+    <FrameLayout
+        android:id="@android:id/icon_frame"
+        android:layout_width="56dp"
+        android:layout_height="40dp"
+        android:gravity="start">
+        <ImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            tools:ignore="ContentDescription" />
+    </FrameLayout>
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignStart="@android:id/title"
+            android:layout_below="@android:id/title" />
+
+    </RelativeLayout>
+
+    <LinearLayout android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="64dp"
+        android:gravity="center_vertical"
+        android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/components/browser_ui/settings/android/widget/javatests/res/xml/test_chrome_base_preference_screen.xml b/components/browser_ui/settings/android/widget/javatests/res/xml/test_chrome_base_preference_screen.xml
new file mode 100644
index 0000000..a0c4d5c
--- /dev/null
+++ b/components/browser_ui/settings/android/widget/javatests/res/xml/test_chrome_base_preference_screen.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:key="preference_with_custom_layout"
+        android:layout="@layout/chrome_managed_preference_with_custom_layout"/>
+
+</PreferenceScreen>
diff --git a/components/browser_ui/settings/android/widget/javatests/res/xml/test_chrome_switch_preference_screen.xml b/components/browser_ui/settings/android/widget/javatests/res/xml/test_chrome_switch_preference_screen.xml
new file mode 100644
index 0000000..94f3e04
--- /dev/null
+++ b/components/browser_ui/settings/android/widget/javatests/res/xml/test_chrome_switch_preference_screen.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
+        android:key="preference_with_custom_layout"
+        android:layout="@layout/chrome_managed_preference_with_custom_layout"/>
+
+</PreferenceScreen>
diff --git a/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/BlankUiTestActivitySettingsTestRule.java b/components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/BlankUiTestActivitySettingsTestRule.java
similarity index 100%
rename from components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/BlankUiTestActivitySettingsTestRule.java
rename to components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/BlankUiTestActivitySettingsTestRule.java
diff --git a/components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/ChromeBasePreferenceTest.java b/components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/ChromeBasePreferenceTest.java
new file mode 100644
index 0000000..5ad6bbc
--- /dev/null
+++ b/components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/ChromeBasePreferenceTest.java
@@ -0,0 +1,319 @@
+// Copyright 2020 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.browser_ui.settings;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.stringContainsInOrder;
+
+import android.app.Activity;
+
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+import androidx.test.filters.LargeTest;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.FeatureList;
+import org.chromium.base.test.params.BaseJUnit4RunnerDelegate;
+import org.chromium.base.test.params.ParameterAnnotations;
+import org.chromium.base.test.params.ParameterSet;
+import org.chromium.base.test.params.ParameterizedRunner;
+import org.chromium.base.test.util.Batch;
+import org.chromium.components.browser_ui.settings.test.R;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.test.util.DisableAnimationsTestRule;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests of {@link ChromeBasePreference}.
+ */
+@RunWith(ParameterizedRunner.class)
+@ParameterAnnotations.UseRunnerDelegate(BaseJUnit4RunnerDelegate.class)
+@Batch(Batch.PER_CLASS)
+public class ChromeBasePreferenceTest {
+    @ClassRule
+    public static final DisableAnimationsTestRule disableAnimationsRule =
+            new DisableAnimationsTestRule();
+    @Rule
+    public final BlankUiTestActivitySettingsTestRule mSettingsRule =
+            new BlankUiTestActivitySettingsTestRule();
+
+    private static final String TITLE = "Preference Title";
+    private static final String SUMMARY = "This is a summary.";
+
+    private Activity mActivity;
+    private PreferenceScreen mPreferenceScreen;
+
+    private boolean mEnableHighlightManagedPrefDisclaimerAndroid;
+
+    @ParameterAnnotations.ClassParameter
+    private static List<ParameterSet> sClassParams = Arrays.asList(
+            new ParameterSet().value(true).name("EnableHighlightManagedPrefDisclaimerAndroid"),
+            new ParameterSet().value(false).name("DisableHighlightManagedPrefDisclaimerAndroid"));
+
+    public ChromeBasePreferenceTest(boolean enableHighlightManagedPrefDisclaimerAndroid) {
+        mEnableHighlightManagedPrefDisclaimerAndroid = enableHighlightManagedPrefDisclaimerAndroid;
+    }
+
+    @Before
+    public void setUp() {
+        FeatureList.TestValues testValuesOverride = new FeatureList.TestValues();
+        testValuesOverride.addFeatureFlagOverride(
+                SettingsFeatureList.HIGHLIGHT_MANAGED_PREF_DISCLAIMER_ANDROID,
+                mEnableHighlightManagedPrefDisclaimerAndroid);
+        FeatureList.setTestValues(testValuesOverride);
+
+        mSettingsRule.launchPreference(PlaceholderSettingsForTest.class);
+        mActivity = mSettingsRule.getActivity();
+        mPreferenceScreen = mSettingsRule.getPreferenceScreen();
+    }
+
+    @After
+    public void tearDown() {
+        FeatureList.setTestValues(null);
+    }
+
+    @Test
+    @LargeTest
+    public void testUnmanagedPreference() {
+        ChromeBasePreference preference = new ChromeBasePreference(mActivity);
+        preference.setTitle(TITLE);
+        preference.setSummary(SUMMARY);
+        preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.UNMANAGED_DELEGATE);
+        mPreferenceScreen.addPreference(preference);
+
+        Assert.assertTrue(preference.isEnabled());
+
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        onView(withId(android.R.id.summary))
+                .check(matches(allOf(withText(SUMMARY), isDisplayed())));
+        if (mEnableHighlightManagedPrefDisclaimerAndroid) {
+            onView(withId(R.id.managed_disclaimer_text)).check(matches(not(isDisplayed())));
+        } else {
+            onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+        }
+        onView(withId(android.R.id.icon)).check(matches(not(isDisplayed())));
+    }
+
+    @Test
+    @LargeTest
+    public void testPolicyManagedPreferenceWithoutSummary() {
+        ChromeBasePreference preference = new ChromeBasePreference(mActivity);
+        preference.setTitle(TITLE);
+        preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.POLICY_DELEGATE);
+        mPreferenceScreen.addPreference(preference);
+
+        Assert.assertFalse(preference.isEnabled());
+
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        if (mEnableHighlightManagedPrefDisclaimerAndroid) {
+            onView(withId(android.R.id.summary)).check(matches(not(isDisplayed())));
+            onView(withId(R.id.managed_disclaimer_text))
+                    .check(matches(allOf(withText(R.string.managed_by_your_organization),
+                            Matchers.hasDrawableStart(), isDisplayed())));
+            onView(withId(android.R.id.icon)).check(matches(not(isDisplayed())));
+        } else {
+            onView(withId(android.R.id.summary))
+                    .check(matches(
+                            allOf(withText(R.string.managed_by_your_organization), isDisplayed())));
+            onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+            onView(withId(android.R.id.icon)).check(matches(isDisplayed()));
+        }
+    }
+
+    @Test
+    @LargeTest
+    public void testPolicyManagedPreferenceWithSummary() {
+        ChromeBasePreference preference = new ChromeBasePreference(mActivity);
+        preference.setTitle(TITLE);
+        preference.setSummary(SUMMARY);
+        preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.POLICY_DELEGATE);
+        mPreferenceScreen.addPreference(preference);
+        Assert.assertFalse(preference.isEnabled());
+
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        if (mEnableHighlightManagedPrefDisclaimerAndroid) {
+            onView(withId(android.R.id.summary))
+                    .check(matches(allOf(withText(SUMMARY), isDisplayed())));
+            onView(withId(R.id.managed_disclaimer_text))
+                    .check(matches(allOf(withText(R.string.managed_by_your_organization),
+                            Matchers.hasDrawableStart(), isDisplayed())));
+            onView(withId(android.R.id.icon)).check(matches(not(isDisplayed())));
+        } else {
+            onView(withId(android.R.id.summary))
+                    .check(matches(allOf(
+                            withText(stringContainsInOrder(ImmutableList.of(SUMMARY,
+                                    mActivity.getString(R.string.managed_by_your_organization)))),
+                            isDisplayed())));
+            onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+            onView(withId(android.R.id.icon)).check(matches(isDisplayed()));
+        }
+    }
+
+    @Test
+    @LargeTest
+    public void testSingleCustodianManagedPreference() {
+        ChromeBasePreference preference = new ChromeBasePreference(mActivity);
+        preference.setTitle(TITLE);
+        preference.setManagedPreferenceDelegate(
+                ManagedPreferencesUtilsTest.SINGLE_CUSTODIAN_DELEGATE);
+        mPreferenceScreen.addPreference(preference);
+
+        Assert.assertFalse(preference.isEnabled());
+
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        onView(withId(android.R.id.summary))
+                .check(matches(allOf(withText(R.string.managed_by_your_parent), isDisplayed())));
+        if (mEnableHighlightManagedPrefDisclaimerAndroid) {
+            onView(withId(R.id.managed_disclaimer_text)).check(matches(not(isDisplayed())));
+        } else {
+            onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+        }
+        onView(withId(android.R.id.icon)).check(matches(isDisplayed()));
+    }
+
+    @Test
+    @LargeTest
+    public void testMultipleCustodianManagedPreference() {
+        ChromeBasePreference preference = new ChromeBasePreference(mActivity);
+        preference.setTitle(TITLE);
+        preference.setManagedPreferenceDelegate(
+                ManagedPreferencesUtilsTest.MULTI_CUSTODIAN_DELEGATE);
+        mPreferenceScreen.addPreference(preference);
+
+        Assert.assertFalse(preference.isEnabled());
+
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        onView(withId(android.R.id.summary))
+                .check(matches(allOf(withText(R.string.managed_by_your_parents), isDisplayed())));
+        if (mEnableHighlightManagedPrefDisclaimerAndroid) {
+            onView(withId(R.id.managed_disclaimer_text)).check(matches(not(isDisplayed())));
+        } else {
+            onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+        }
+        onView(withId(android.R.id.icon)).check(matches(isDisplayed()));
+    }
+
+    @Test
+    @LargeTest
+    public void testUnmanagedPreferenceWithCustomLayout() throws Exception {
+        PreferenceFragmentCompat fragment = mSettingsRule.getPreferenceFragment();
+        SettingsUtils.addPreferencesFromResource(
+                fragment, R.xml.test_chrome_base_preference_screen);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ChromeBasePreference preference =
+                    fragment.findPreference("preference_with_custom_layout");
+            preference.setTitle(TITLE);
+            preference.setSummary(SUMMARY);
+            preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.UNMANAGED_DELEGATE);
+        });
+
+        ChromeBasePreference preference = fragment.findPreference("preference_with_custom_layout");
+        Assert.assertEquals(preference.getLayoutResource(),
+                R.layout.chrome_managed_preference_with_custom_layout);
+        Assert.assertTrue(preference.isEnabled());
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        onView(withId(android.R.id.summary))
+                .check(matches(allOf(withText(SUMMARY), isDisplayed())));
+        onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+        onView(withId(android.R.id.icon)).check(matches(not(isDisplayed())));
+    }
+
+    @Test
+    @LargeTest
+    public void testPolicyManagedPreferenceWithSummaryAndCustomLayout() {
+        PreferenceFragmentCompat fragment = mSettingsRule.getPreferenceFragment();
+        SettingsUtils.addPreferencesFromResource(
+                fragment, R.xml.test_chrome_base_preference_screen);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ChromeBasePreference preference =
+                    fragment.findPreference("preference_with_custom_layout");
+            preference.setTitle(TITLE);
+            preference.setSummary(SUMMARY);
+            preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.POLICY_DELEGATE);
+        });
+
+        ChromeBasePreference preference = fragment.findPreference("preference_with_custom_layout");
+        Assert.assertEquals(preference.getLayoutResource(),
+                R.layout.chrome_managed_preference_with_custom_layout);
+        Assert.assertFalse(preference.isEnabled());
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        onView(withId(android.R.id.summary))
+                .check(matches(
+                        allOf(withText(stringContainsInOrder(ImmutableList.of(SUMMARY,
+                                      mActivity.getString(R.string.managed_by_your_organization)))),
+                                isDisplayed())));
+        onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+        onView(withId(android.R.id.icon)).check(matches(isDisplayed()));
+    }
+
+    @Test
+    @LargeTest
+    public void testPolicyManagedPreferenceWithoutSummaryWithCustomLayout() {
+        PreferenceFragmentCompat fragment = mSettingsRule.getPreferenceFragment();
+        SettingsUtils.addPreferencesFromResource(
+                fragment, R.xml.test_chrome_base_preference_screen);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ChromeBasePreference preference =
+                    fragment.findPreference("preference_with_custom_layout");
+            preference.setTitle(TITLE);
+            preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.POLICY_DELEGATE);
+        });
+
+        ChromeBasePreference preference = fragment.findPreference("preference_with_custom_layout");
+        Assert.assertEquals(preference.getLayoutResource(),
+                R.layout.chrome_managed_preference_with_custom_layout);
+        Assert.assertFalse(preference.isEnabled());
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        onView(withId(android.R.id.summary))
+                .check(matches(
+                        allOf(withText(R.string.managed_by_your_organization), isDisplayed())));
+        onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+        onView(withId(android.R.id.icon)).check(matches(isDisplayed()));
+    }
+
+    @Test
+    @LargeTest
+    public void testSingleCustodianManagedPreferenceWithCustomLayout() {
+        PreferenceFragmentCompat fragment = mSettingsRule.getPreferenceFragment();
+        SettingsUtils.addPreferencesFromResource(
+                fragment, R.xml.test_chrome_base_preference_screen);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ChromeBasePreference preference =
+                    fragment.findPreference("preference_with_custom_layout");
+            preference.setTitle(TITLE);
+            preference.setManagedPreferenceDelegate(
+                    ManagedPreferencesUtilsTest.SINGLE_CUSTODIAN_DELEGATE);
+        });
+
+        ChromeBasePreference preference = fragment.findPreference("preference_with_custom_layout");
+        Assert.assertEquals(preference.getLayoutResource(),
+                R.layout.chrome_managed_preference_with_custom_layout);
+        Assert.assertFalse(preference.isEnabled());
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        onView(withId(android.R.id.summary))
+                .check(matches(allOf(withText(R.string.managed_by_your_parent), isDisplayed())));
+        onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+        onView(withId(android.R.id.icon)).check(matches(isDisplayed()));
+    }
+}
diff --git a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeImageViewPreferenceTest.java b/components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/ChromeImageViewPreferenceTest.java
similarity index 100%
rename from components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeImageViewPreferenceTest.java
rename to components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/ChromeImageViewPreferenceTest.java
diff --git a/components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreferenceTest.java b/components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreferenceTest.java
new file mode 100644
index 0000000..2a4f471
--- /dev/null
+++ b/components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreferenceTest.java
@@ -0,0 +1,334 @@
+// 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.
+
+package org.chromium.components.browser_ui.settings;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.stringContainsInOrder;
+
+import android.app.Activity;
+
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+import androidx.test.filters.LargeTest;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.FeatureList;
+import org.chromium.base.test.params.BaseJUnit4RunnerDelegate;
+import org.chromium.base.test.params.ParameterAnnotations;
+import org.chromium.base.test.params.ParameterSet;
+import org.chromium.base.test.params.ParameterizedRunner;
+import org.chromium.base.test.util.Batch;
+import org.chromium.components.browser_ui.settings.test.R;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.test.util.DisableAnimationsTestRule;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests of {@link ChromeSwitchPreference}.
+ */
+@RunWith(ParameterizedRunner.class)
+@ParameterAnnotations.UseRunnerDelegate(BaseJUnit4RunnerDelegate.class)
+@Batch(Batch.PER_CLASS)
+public class ChromeSwitchPreferenceTest {
+    @ClassRule
+    public static final DisableAnimationsTestRule disableAnimationsRule =
+            new DisableAnimationsTestRule();
+    @Rule
+    public final BlankUiTestActivitySettingsTestRule mSettingsRule =
+            new BlankUiTestActivitySettingsTestRule();
+
+    private static final String TITLE = "Preference Title";
+    private static final String SUMMARY = "This is a summary.";
+
+    private Activity mActivity;
+    private PreferenceScreen mPreferenceScreen;
+
+    private boolean mEnableHighlightManagedPrefDisclaimerAndroid;
+
+    @ParameterAnnotations.ClassParameter
+    private static List<ParameterSet> sClassParams = Arrays.asList(
+            new ParameterSet().value(true).name("EnableHighlightManagedPrefDisclaimerAndroid"),
+            new ParameterSet().value(false).name("DisableHighlightManagedPrefDisclaimerAndroid"));
+
+    public ChromeSwitchPreferenceTest(boolean enableHighlightManagedPrefDisclaimerAndroid) {
+        mEnableHighlightManagedPrefDisclaimerAndroid = enableHighlightManagedPrefDisclaimerAndroid;
+    }
+
+    @Before
+    public void setUp() {
+        FeatureList.TestValues testValuesOverride = new FeatureList.TestValues();
+        testValuesOverride.addFeatureFlagOverride(
+                SettingsFeatureList.HIGHLIGHT_MANAGED_PREF_DISCLAIMER_ANDROID,
+                mEnableHighlightManagedPrefDisclaimerAndroid);
+        FeatureList.setTestValues(testValuesOverride);
+
+        mSettingsRule.launchPreference(PlaceholderSettingsForTest.class);
+        mActivity = mSettingsRule.getActivity();
+        mPreferenceScreen = mSettingsRule.getPreferenceScreen();
+    }
+
+    @After
+    public void tearDown() {
+        FeatureList.setTestValues(null);
+    }
+
+    @Test
+    @LargeTest
+    public void testUnmanagedPreference() {
+        ChromeSwitchPreference preference = new ChromeSwitchPreference(mActivity);
+        preference.setTitle(TITLE);
+        preference.setSummary(SUMMARY);
+        preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.UNMANAGED_DELEGATE);
+        mPreferenceScreen.addPreference(preference);
+
+        Assert.assertTrue(preference.isEnabled());
+
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        onView(withId(android.R.id.summary))
+                .check(matches(allOf(withText(SUMMARY), isDisplayed())));
+        if (mEnableHighlightManagedPrefDisclaimerAndroid) {
+            onView(withId(R.id.managed_disclaimer_text)).check(matches(not(isDisplayed())));
+        } else {
+            onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+        }
+        onView(withId(android.R.id.icon)).check(matches(not(isDisplayed())));
+        onView(withId(R.id.switchWidget)).check(matches(allOf(isEnabled(), isDisplayed())));
+    }
+
+    @Test
+    @LargeTest
+    public void testPolicyManagedPreferenceWithoutSummary() {
+        ChromeSwitchPreference preference = new ChromeSwitchPreference(mActivity);
+        preference.setTitle(TITLE);
+        preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.POLICY_DELEGATE);
+        mPreferenceScreen.addPreference(preference);
+
+        Assert.assertFalse(preference.isEnabled());
+
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        if (mEnableHighlightManagedPrefDisclaimerAndroid) {
+            onView(withId(android.R.id.summary)).check(matches(not(isDisplayed())));
+            onView(withId(R.id.managed_disclaimer_text))
+                    .check(matches(allOf(withText(R.string.managed_by_your_organization),
+                            Matchers.hasDrawableStart(), isDisplayed())));
+            onView(withId(android.R.id.icon)).check(matches(not(isDisplayed())));
+        } else {
+            onView(withId(android.R.id.summary))
+                    .check(matches(
+                            allOf(withText(R.string.managed_by_your_organization), isDisplayed())));
+            onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+            onView(withId(android.R.id.icon)).check(matches(isDisplayed()));
+        }
+        onView(withId(R.id.switchWidget)).check(matches(allOf(not(isEnabled()), isDisplayed())));
+    }
+
+    @Test
+    @LargeTest
+    public void testPolicyManagedPreferenceWithSummary() {
+        ChromeSwitchPreference preference = new ChromeSwitchPreference(mActivity);
+        preference.setTitle(TITLE);
+        preference.setSummary(SUMMARY);
+        preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.POLICY_DELEGATE);
+        mPreferenceScreen.addPreference(preference);
+
+        Assert.assertFalse(preference.isEnabled());
+
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        if (mEnableHighlightManagedPrefDisclaimerAndroid) {
+            onView(withId(android.R.id.summary))
+                    .check(matches(allOf(withText(SUMMARY), isDisplayed())));
+            onView(withId(R.id.managed_disclaimer_text))
+                    .check(matches(allOf(withText(R.string.managed_by_your_organization),
+                            Matchers.hasDrawableStart(), isDisplayed())));
+            onView(withId(android.R.id.icon)).check(matches(not(isDisplayed())));
+        } else {
+            onView(withId(android.R.id.summary))
+                    .check(matches(allOf(
+                            withText(stringContainsInOrder(ImmutableList.of(SUMMARY,
+                                    mActivity.getString(R.string.managed_by_your_organization)))),
+                            isDisplayed())));
+            onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+            onView(withId(android.R.id.icon)).check(matches(isDisplayed()));
+        }
+        onView(withId(R.id.switchWidget)).check(matches(allOf(not(isEnabled()), isDisplayed())));
+    }
+
+    @Test
+    @LargeTest
+    public void testSingleCustodianManagedPreference() {
+        ChromeSwitchPreference preference = new ChromeSwitchPreference(mActivity);
+        preference.setTitle(TITLE);
+        preference.setManagedPreferenceDelegate(
+                ManagedPreferencesUtilsTest.SINGLE_CUSTODIAN_DELEGATE);
+        mPreferenceScreen.addPreference(preference);
+
+        Assert.assertFalse(preference.isEnabled());
+
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        onView(withId(android.R.id.summary))
+                .check(matches(allOf(withText(R.string.managed_by_your_parent), isDisplayed())));
+        if (mEnableHighlightManagedPrefDisclaimerAndroid) {
+            onView(withId(R.id.managed_disclaimer_text)).check(matches(not(isDisplayed())));
+        } else {
+            onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+        }
+        onView(withId(android.R.id.icon)).check(matches(isDisplayed()));
+        onView(withId(R.id.switchWidget)).check(matches(allOf(not(isEnabled()), isDisplayed())));
+    }
+
+    @Test
+    @LargeTest
+    public void testMultipleCustodianManagedPreference() {
+        ChromeSwitchPreference preference = new ChromeSwitchPreference(mActivity);
+        preference.setTitle(TITLE);
+        preference.setManagedPreferenceDelegate(
+                ManagedPreferencesUtilsTest.MULTI_CUSTODIAN_DELEGATE);
+        mPreferenceScreen.addPreference(preference);
+
+        Assert.assertFalse(preference.isEnabled());
+
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        onView(withId(android.R.id.summary))
+                .check(matches(allOf(withText(R.string.managed_by_your_parents), isDisplayed())));
+        if (mEnableHighlightManagedPrefDisclaimerAndroid) {
+            onView(withId(R.id.managed_disclaimer_text)).check(matches(not(isDisplayed())));
+        } else {
+            onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+        }
+        onView(withId(android.R.id.icon)).check(matches(isDisplayed()));
+        onView(withId(R.id.switchWidget)).check(matches(allOf(not(isEnabled()), isDisplayed())));
+    }
+
+    @Test
+    @LargeTest
+    public void testUnmanagedPreferenceWithCustomLayout() throws Exception {
+        PreferenceFragmentCompat fragment = mSettingsRule.getPreferenceFragment();
+        SettingsUtils.addPreferencesFromResource(
+                fragment, R.xml.test_chrome_switch_preference_screen);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ChromeSwitchPreference preference =
+                    fragment.findPreference("preference_with_custom_layout");
+            preference.setTitle(TITLE);
+            preference.setSummary(SUMMARY);
+            preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.UNMANAGED_DELEGATE);
+        });
+
+        ChromeSwitchPreference preference =
+                fragment.findPreference("preference_with_custom_layout");
+        Assert.assertEquals(preference.getLayoutResource(),
+                R.layout.chrome_managed_preference_with_custom_layout);
+        Assert.assertTrue(preference.isEnabled());
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        onView(withId(android.R.id.summary))
+                .check(matches(allOf(withText(SUMMARY), isDisplayed())));
+        onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+        onView(withId(android.R.id.icon)).check(matches(not(isDisplayed())));
+        onView(withId(R.id.switchWidget)).check(matches(allOf(isEnabled(), isDisplayed())));
+    }
+
+    @Test
+    @LargeTest
+    public void testPolicyManagedPreferenceWithSummaryAndCustomLayout() {
+        PreferenceFragmentCompat fragment = mSettingsRule.getPreferenceFragment();
+        SettingsUtils.addPreferencesFromResource(
+                fragment, R.xml.test_chrome_switch_preference_screen);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ChromeSwitchPreference preference =
+                    fragment.findPreference("preference_with_custom_layout");
+            preference.setTitle(TITLE);
+            preference.setSummary(SUMMARY);
+            preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.POLICY_DELEGATE);
+        });
+
+        ChromeSwitchPreference preference =
+                fragment.findPreference("preference_with_custom_layout");
+        Assert.assertEquals(preference.getLayoutResource(),
+                R.layout.chrome_managed_preference_with_custom_layout);
+        Assert.assertFalse(preference.isEnabled());
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        onView(withId(android.R.id.summary))
+                .check(matches(
+                        allOf(withText(stringContainsInOrder(ImmutableList.of(SUMMARY,
+                                      mActivity.getString(R.string.managed_by_your_organization)))),
+                                isDisplayed())));
+        onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+        onView(withId(android.R.id.icon)).check(matches(isDisplayed()));
+        onView(withId(R.id.switchWidget)).check(matches(allOf(not(isEnabled()), isDisplayed())));
+    }
+
+    @Test
+    @LargeTest
+    public void testPolicyManagedPreferenceWithoutSummaryWithCustomLayout() {
+        PreferenceFragmentCompat fragment = mSettingsRule.getPreferenceFragment();
+        SettingsUtils.addPreferencesFromResource(
+                fragment, R.xml.test_chrome_switch_preference_screen);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ChromeSwitchPreference preference =
+                    fragment.findPreference("preference_with_custom_layout");
+            preference.setTitle(TITLE);
+            preference.setManagedPreferenceDelegate(ManagedPreferencesUtilsTest.POLICY_DELEGATE);
+        });
+
+        ChromeSwitchPreference preference =
+                fragment.findPreference("preference_with_custom_layout");
+        Assert.assertEquals(preference.getLayoutResource(),
+                R.layout.chrome_managed_preference_with_custom_layout);
+        Assert.assertFalse(preference.isEnabled());
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        onView(withId(android.R.id.summary))
+                .check(matches(
+                        allOf(withText(R.string.managed_by_your_organization), isDisplayed())));
+        onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+        onView(withId(android.R.id.icon)).check(matches(isDisplayed()));
+        onView(withId(R.id.switchWidget)).check(matches(allOf(not(isEnabled()), isDisplayed())));
+    }
+
+    @Test
+    @LargeTest
+    public void testSingleCustodianManagedPreferenceWithCustomLayout() {
+        PreferenceFragmentCompat fragment = mSettingsRule.getPreferenceFragment();
+        SettingsUtils.addPreferencesFromResource(
+                fragment, R.xml.test_chrome_switch_preference_screen);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ChromeSwitchPreference preference =
+                    fragment.findPreference("preference_with_custom_layout");
+            preference.setTitle(TITLE);
+            preference.setManagedPreferenceDelegate(
+                    ManagedPreferencesUtilsTest.SINGLE_CUSTODIAN_DELEGATE);
+        });
+
+        ChromeSwitchPreference preference =
+                fragment.findPreference("preference_with_custom_layout");
+        Assert.assertEquals(preference.getLayoutResource(),
+                R.layout.chrome_managed_preference_with_custom_layout);
+        Assert.assertFalse(preference.isEnabled());
+        onView(withId(android.R.id.title)).check(matches(allOf(withText(TITLE), isDisplayed())));
+        onView(withId(android.R.id.summary))
+                .check(matches(allOf(withText(R.string.managed_by_your_parent), isDisplayed())));
+        onView(withId(R.id.managed_disclaimer_text)).check(doesNotExist());
+        onView(withId(android.R.id.icon)).check(matches(isDisplayed()));
+        onView(withId(R.id.switchWidget)).check(matches(allOf(not(isEnabled()), isDisplayed())));
+    }
+}
diff --git a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtilsTest.java b/components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtilsTest.java
similarity index 100%
rename from components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtilsTest.java
rename to components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtilsTest.java
diff --git a/components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/Matchers.java b/components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/Matchers.java
new file mode 100644
index 0000000..61bb9e8
--- /dev/null
+++ b/components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/Matchers.java
@@ -0,0 +1,33 @@
+// 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.
+
+package org.chromium.components.browser_ui.settings;
+
+import android.view.View;
+import android.widget.TextView;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+/** Contains assert conditions for preference components. */
+public class Matchers {
+    /**
+     * Returns a matcher that checks if a {@link TextView} has a {@link Drawable} set via {@code
+     * android:drawableStart}.
+     */
+    public static Matcher<View> hasDrawableStart() {
+        return new TypeSafeMatcher<View>() {
+            @Override
+            protected boolean matchesSafely(View view) {
+                return ((TextView) view).getCompoundDrawablesRelative()[0] != null;
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("Expected TextView to define android:drawableStart");
+            }
+        };
+    }
+}
diff --git a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/PlaceholderSettingsForTest.java b/components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/PlaceholderSettingsForTest.java
similarity index 100%
rename from components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/PlaceholderSettingsForTest.java
rename to components/browser_ui/settings/android/widget/javatests/src/org/chromium/components/browser_ui/settings/PlaceholderSettingsForTest.java
diff --git a/components/client_hints/browser/client_hints.cc b/components/client_hints/browser/client_hints.cc
index 5c4f03e..608ca57 100644
--- a/components/client_hints/browser/client_hints.cc
+++ b/components/client_hints/browser/client_hints.cc
@@ -140,7 +140,8 @@
              url, ContentSettingsType::JAVASCRIPT) != CONTENT_SETTING_BLOCK;
 }
 
-bool ClientHints::AreThirdPartyCookiesBlocked(const GURL& url) {
+bool ClientHints::AreThirdPartyCookiesBlocked(const GURL& url,
+                                              content::RenderFrameHost* rfh) {
   return settings_map_->GetContentSetting(
              url, url, ContentSettingsType::COOKIES) == CONTENT_SETTING_BLOCK ||
          cookie_settings_->ShouldBlockThirdPartyCookies();
diff --git a/components/client_hints/browser/client_hints.h b/components/client_hints/browser/client_hints.h
index d750289..affaa44 100644
--- a/components/client_hints/browser/client_hints.h
+++ b/components/client_hints/browser/client_hints.h
@@ -49,7 +49,8 @@
   bool IsJavaScriptAllowed(const GURL& url,
                            content::RenderFrameHost* parent_rfh) override;
 
-  bool AreThirdPartyCookiesBlocked(const GURL& url) override;
+  bool AreThirdPartyCookiesBlocked(const GURL& url,
+                                   content::RenderFrameHost* rfh) override;
 
   blink::UserAgentMetadata GetUserAgentMetadata() override;
 
diff --git a/components/client_hints/browser/in_memory_client_hints_controller_delegate.cc b/components/client_hints/browser/in_memory_client_hints_controller_delegate.cc
index 0c46b67..944a5a79 100644
--- a/components/client_hints/browser/in_memory_client_hints_controller_delegate.cc
+++ b/components/client_hints/browser/in_memory_client_hints_controller_delegate.cc
@@ -91,7 +91,8 @@
 }
 
 bool InMemoryClientHintsControllerDelegate::AreThirdPartyCookiesBlocked(
-    const GURL& url) {
+    const GURL& url,
+    content::RenderFrameHost* rfh) {
   return are_third_party_cookies_blocked_callback_.Run(url);
 }
 
diff --git a/components/client_hints/browser/in_memory_client_hints_controller_delegate.h b/components/client_hints/browser/in_memory_client_hints_controller_delegate.h
index 4422b42..9cd1720f 100644
--- a/components/client_hints/browser/in_memory_client_hints_controller_delegate.h
+++ b/components/client_hints/browser/in_memory_client_hints_controller_delegate.h
@@ -63,7 +63,8 @@
   network::NetworkQualityTracker* GetNetworkQualityTracker() override;
   bool IsJavaScriptAllowed(const GURL& url,
                            content::RenderFrameHost* parent_rfh) override;
-  bool AreThirdPartyCookiesBlocked(const GURL& url) override;
+  bool AreThirdPartyCookiesBlocked(const GURL& url,
+                                   content::RenderFrameHost* rfh) override;
   blink::UserAgentMetadata GetUserAgentMetadata() override;
   void SetMostRecentMainFrameViewportSize(
       const gfx::Size& viewport_size) override;
diff --git a/components/client_update_protocol/BUILD.gn b/components/client_update_protocol/BUILD.gn
index a1a75164..8e496c2 100644
--- a/components/client_update_protocol/BUILD.gn
+++ b/components/client_update_protocol/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//testing/libfuzzer/fuzzer_test.gni")
+
 static_library("client_update_protocol") {
   sources = [
     "ecdsa.cc",
@@ -25,3 +27,11 @@
     "//testing/gtest",
   ]
 }
+
+fuzzer_test("ecdsa_fuzzer") {
+  sources = [ "ecdsa_fuzzer.cc" ]
+  deps = [
+    ":client_update_protocol",
+    "//base",
+  ]
+}
diff --git a/components/client_update_protocol/ecdsa_fuzzer.cc b/components/client_update_protocol/ecdsa_fuzzer.cc
new file mode 100644
index 0000000..b473184
--- /dev/null
+++ b/components/client_update_protocol/ecdsa_fuzzer.cc
@@ -0,0 +1,38 @@
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <stddef.h>
+#include <stdint.h>
+#include <memory>
+
+#include "base/strings/string_piece.h"
+#include "components/client_update_protocol/ecdsa.h"
+
+namespace client_update_protocol {
+
+constexpr uint8_t kKeyPubBytes[] = {
+    0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+    0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03,
+    0x42, 0x00, 0x04, 0x59, 0x65, 0x1F, 0x1D, 0x36, 0x33, 0x81, 0xE1, 0xB2,
+    0xD3, 0x78, 0x4B, 0xE1, 0x7B, 0x8D, 0x07, 0x33, 0x55, 0x4F, 0x0D, 0x67,
+    0x1C, 0x33, 0xD2, 0xFE, 0x78, 0x02, 0xB6, 0xD2, 0xDF, 0x2F, 0x45, 0x1F,
+    0x49, 0xBA, 0xD2, 0xE6, 0x67, 0x4E, 0x4D, 0xA9, 0x77, 0xB3, 0x34, 0x12,
+    0xEB, 0x6D, 0xC0, 0xDC, 0x86, 0xE7, 0xBE, 0xF7, 0x09, 0x56, 0x77, 0x2A,
+    0xF3, 0xE8, 0x4E, 0x96, 0xAB, 0xAB, 0x12};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp(data, size);
+  base::StringPiece public_key = {reinterpret_cast<const char*>(kKeyPubBytes),
+                                  sizeof(kKeyPubBytes)};
+  std::unique_ptr<client_update_protocol::Ecdsa> cup =
+      client_update_protocol::Ecdsa::Create(6, public_key);
+  cup->SignRequest(fdp.ConsumeRandomLengthString());
+  cup->ValidateResponse(fdp.ConsumeRandomLengthString(),
+                        fdp.ConsumeRandomLengthString());
+  return 0;
+}
+
+}  // namespace client_update_protocol
diff --git a/components/crash/core/app/crashpad.cc b/components/crash/core/app/crashpad.cc
index afa205b..9ad09dfe 100644
--- a/components/crash/core/app/crashpad.cc
+++ b/components/crash/core/app/crashpad.cc
@@ -59,8 +59,8 @@
                    const char* prefix_end,
                    const char* buf_end) {
   // This simulates that a CHECK(false) was done at file:line instead of here.
-  // This is used instead of IMMEDIATE_CRASH() to give better error messages
-  // locally (printed stack for one).
+  // This is used instead of base::ImmediateCrash() to give better error
+  // messages locally (printed stack for one).
   logging::CheckError::Check(file, line, "false").stream() << prefix_end;
 }
 
diff --git a/components/flags_ui/resources/flags.ts b/components/flags_ui/resources/flags.ts
index feba287..751c697 100644
--- a/components/flags_ui/resources/flags.ts
+++ b/components/flags_ui/resources/flags.ts
@@ -10,9 +10,10 @@
 import './strings.m.js';
 
 import {assert} from 'chrome://resources/js/assert_ts.js';
-import {isIOS, sendWithPromise} from 'chrome://resources/js/cr.m.js';
+import {sendWithPromise} from 'chrome://resources/js/cr.m.js';
 import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {isIOS} from 'chrome://resources/js/platform.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {$} from 'chrome://resources/js/util.js';
 
diff --git a/components/heap_profiling/in_process/heap_profiler_controller.cc b/components/heap_profiling/in_process/heap_profiler_controller.cc
index 1ebc8cef..deabeae9b 100644
--- a/components/heap_profiling/in_process/heap_profiler_controller.cc
+++ b/components/heap_profiling/in_process/heap_profiler_controller.cc
@@ -13,6 +13,7 @@
 #include "base/bind.h"
 #include "base/check.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/numerics/clamped_math.h"
 #include "base/profiler/module_cache.h"
 #include "base/rand_util.h"
 #include "base/sampling_heap_profiler/sampling_heap_profiler.h"
@@ -272,6 +273,7 @@
 
   SampleMap merged_samples = MergeSamples(samples);
 
+  base::ClampedNumeric<uint64_t> total_sampled_bytes;
   for (auto& pair : merged_samples) {
     const Sample& sample = pair.first;
     const SampleValue& value = pair.second;
@@ -288,9 +290,17 @@
     // do not have a meaningful timestamp.
     profile_builder.OnSampleCompleted(std::move(frames), base::TimeTicks(),
                                       value.total, value.count);
+
+    total_sampled_bytes += value.total;
   }
 
   profile_builder.OnProfileCompleted(base::TimeDelta(), base::TimeDelta());
+
+  constexpr int kBytesPerMB = 1024 * 1024;
+  base::UmaHistogramMemoryLargeMB(
+      ProcessHistogramName("HeapProfiling.InProcess.TotalSampledMemory",
+                           process_type),
+      base::ClampDiv(total_sampled_bytes, kBytesPerMB));
 }
 
 }  // namespace heap_profiling
diff --git a/components/messages/android/messages_feature.cc b/components/messages/android/messages_feature.cc
index 7bc6905..d75474e9 100644
--- a/components/messages/android/messages_feature.cc
+++ b/components/messages/android/messages_feature.cc
@@ -108,10 +108,6 @@
     kMessagesForAndroidUpdatePassword_UseFollowupButtonText{
         &kMessagesForAndroidUpdatePassword, "use_followup_button_text", false};
 
-BASE_FEATURE(kMessagesForAndroidReduceLayoutChanges,
-             "MessagesForAndroidReduceLayoutChanges",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 bool IsAdsBlockedMessagesUiEnabled() {
   return base::FeatureList::IsEnabled(kMessagesForAndroidInfrastructure) &&
          base::FeatureList::IsEnabled(kMessagesForAndroidAdsBlocked);
diff --git a/components/messages/android/messages_feature.h b/components/messages/android/messages_feature.h
index bddb9fd..73fd450a 100644
--- a/components/messages/android/messages_feature.h
+++ b/components/messages/android/messages_feature.h
@@ -66,10 +66,6 @@
 // Infobars infrastructure.
 BASE_DECLARE_FEATURE(kMessagesForAndroidUpdatePassword);
 
-// Feature that controls whether we always update layout parameters or only
-// while the message container is visible.
-BASE_DECLARE_FEATURE(kMessagesForAndroidReduceLayoutChanges);
-
 bool IsAdsBlockedMessagesUiEnabled();
 
 bool IsNearOomReductionMessagesUiEnabled();
diff --git a/components/named_mojo_ipc_server/BUILD.gn b/components/named_mojo_ipc_server/BUILD.gn
new file mode 100644
index 0000000..ad671f1
--- /dev/null
+++ b/components/named_mojo_ipc_server/BUILD.gn
@@ -0,0 +1,82 @@
+# Copyright 2021 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("testing_mojom") {
+  testonly = true
+  sources = [ "testing.test-mojom" ]
+  deps = [ "//mojo/public/mojom/base" ]
+}
+
+static_library("named_mojo_ipc_server") {
+  sources = [
+    "ipc_server.h",
+    "named_mojo_ipc_server.cc",
+    "named_mojo_ipc_server.h",
+    "named_mojo_ipc_util.cc",
+    "named_mojo_ipc_util.h",
+    "named_mojo_server_endpoint_connector.h",
+  ]
+  deps = [
+    "//base",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/platform",
+    "//mojo/public/cpp/system",
+  ]
+  if (is_linux) {
+    sources += [
+      "named_mojo_server_endpoint_connector_linux.cc",
+      "named_mojo_server_endpoint_connector_linux.h",
+    ]
+  } else if (is_win) {
+    sources += [
+      "named_mojo_server_endpoint_connector_win.cc",
+      "named_mojo_server_endpoint_connector_win.h",
+    ]
+  } else {
+    sources += [ "named_mojo_server_endpoint_connector_unsupported.cc" ]
+  }
+}
+
+static_library("test_support") {
+  testonly = true
+
+  sources = [
+    "fake_ipc_server.cc",
+    "fake_ipc_server.h",
+    "named_mojo_ipc_test_util.cc",
+    "named_mojo_ipc_test_util.h",
+  ]
+  deps = [
+    ":named_mojo_ipc_server",
+    ":testing_mojom",
+    "//base",
+    "//mojo/public/cpp/platform",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = []
+
+  # Disable MojoIpcServerTest on unsupported platforms (i.e. Mac).
+  if (is_linux || is_win) {
+    sources += [ "named_mojo_ipc_server_unittest.cc" ]
+  }
+  deps = [
+    ":named_mojo_ipc_server",
+    ":test_support",
+    ":testing_mojom",
+    "//base",
+    "//base/test:test_support",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/platform",
+    "//mojo/public/cpp/system",
+    "//remoting/host/mojom",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/chrome/common/mac/DEPS b/components/named_mojo_ipc_server/DEPS
similarity index 71%
rename from chrome/common/mac/DEPS
rename to components/named_mojo_ipc_server/DEPS
index 9243dcd6..6278491 100644
--- a/chrome/common/mac/DEPS
+++ b/components/named_mojo_ipc_server/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
+  "+mojo/public",
   "+mojo/core/embedder",
 ]
diff --git a/components/named_mojo_ipc_server/OWNERS b/components/named_mojo_ipc_server/OWNERS
new file mode 100644
index 0000000..836c509b
--- /dev/null
+++ b/components/named_mojo_ipc_server/OWNERS
@@ -0,0 +1,4 @@
+yuweih@chromium.org
+noahrose@google.com
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/components/named_mojo_ipc_server/README.md b/components/named_mojo_ipc_server/README.md
new file mode 100644
index 0000000..5d5fbab
--- /dev/null
+++ b/components/named_mojo_ipc_server/README.md
@@ -0,0 +1,63 @@
+# Named Mojo IPC Server
+
+This component provides a helper that uses a mojo::NamedPlatformChannel to
+manage multiple concurrent IPCs. Clients that connect to the
+NamedPlatformChannel are sent invitations to join an isolated IPC graph
+suitable only for direct IPC between the two processes.
+
+## Caveats
+
+The isolated connections managed by NamedMojoIpcServer can only be used to
+connect two nodes which have been initialized as Mojo brokers. That is,
+the `is_broker_process` field in the configuration passed to `mojo::core::Init`
+must be set to true. This assumes that these isolated connections effectively
+only go between two standalone (i.e. non-Chrome) processes, or between some
+standalone process and the browser process; but never between two existing
+Chrome processes, or between a standalone process and one of Chrome's child
+processes.
+
+A restriction of isolated connections is that Mojo remotes, receivers, or
+message pipes cannot be passed outside of the boundary of the process pairs.
+For example, the server cannot be used broker connections between two clients.
+
+## Example usage
+
+In the server process:
+```cpp
+class MyInterfaceImpl: public mojom::MyInterface {
+  void Start() {
+    server_.set_disconnect_handler(
+        base::BindRepeating(&MyInterfaceImpl::OnDisconnected, this));
+    server_.StartServer();
+  }
+
+  void OnDisconnected() {
+    LOG(INFO) << "Receiver disconnected: " << server_.current_receiver();
+  }
+
+  // mojom::MyInterface Implementation.
+  void DoWork() override {
+    // Do something...
+
+    // If you want to close the connection:
+    server_.Close(server_.current_receiver());
+  }
+
+  MojoIpcServer<mojom::MyInterface> server_{"my_server_name", this};
+};
+```
+
+In the client:
+```cpp
+void ConnectToServer() {
+mojo::PlatformChannelEndpoint endpoint =
+  mojo::NamedPlatformChannel::ConnectToServer("my_server_name");
+
+std::unique_ptr<mojo::IsolatedConnection> connection =
+    std::make_unique<mojo::IsolatedConnection>();
+
+mojo::Remote<mojom::MyInterface> remote(
+  mojo::PendingRemote<mojom::MyInterface>(
+    connection->Connect(std::move(endpoint)), 0));
+}
+```
diff --git a/remoting/host/mojo_ipc/fake_ipc_server.cc b/components/named_mojo_ipc_server/fake_ipc_server.cc
similarity index 87%
rename from remoting/host/mojo_ipc/fake_ipc_server.cc
rename to components/named_mojo_ipc_server/fake_ipc_server.cc
index 51ad7d6e..0563f717 100644
--- a/remoting/host/mojo_ipc/fake_ipc_server.cc
+++ b/components/named_mojo_ipc_server/fake_ipc_server.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "remoting/host/mojo_ipc/fake_ipc_server.h"
+#include "components/named_mojo_ipc_server/fake_ipc_server.h"
 
-namespace remoting {
+namespace named_mojo_ipc_server {
 
 FakeIpcServer::TestState::TestState() = default;
 
@@ -38,4 +38,4 @@
   return test_state_->current_peer_pid;
 }
 
-}  // namespace remoting
+}  // namespace named_mojo_ipc_server
diff --git a/remoting/host/mojo_ipc/fake_ipc_server.h b/components/named_mojo_ipc_server/fake_ipc_server.h
similarity index 77%
rename from remoting/host/mojo_ipc/fake_ipc_server.h
rename to components/named_mojo_ipc_server/fake_ipc_server.h
index bab4ec57..75c5edfd 100644
--- a/remoting/host/mojo_ipc/fake_ipc_server.h
+++ b/components/named_mojo_ipc_server/fake_ipc_server.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef REMOTING_HOST_MOJO_IPC_FAKE_IPC_SERVER_H_
-#define REMOTING_HOST_MOJO_IPC_FAKE_IPC_SERVER_H_
+#ifndef COMPONENTS_NAMED_MOJO_IPC_SERVER_FAKE_IPC_SERVER_H_
+#define COMPONENTS_NAMED_MOJO_IPC_SERVER_FAKE_IPC_SERVER_H_
 
 #include "base/memory/raw_ptr.h"
-#include "remoting/host/mojo_ipc/ipc_server.h"
+#include "components/named_mojo_ipc_server/ipc_server.h"
 
-namespace remoting {
+namespace named_mojo_ipc_server {
 
 class FakeIpcServer final : public IpcServer {
  public:
@@ -40,6 +40,6 @@
   raw_ptr<TestState> test_state_;
 };
 
-}  // namespace remoting
+}  // namespace named_mojo_ipc_server
 
-#endif  // REMOTING_HOST_MOJO_IPC_FAKE_IPC_SERVER_H_
+#endif  // COMPONENTS_NAMED_MOJO_IPC_SERVER_FAKE_IPC_SERVER_H_
diff --git a/remoting/host/mojo_ipc/ipc_server.h b/components/named_mojo_ipc_server/ipc_server.h
similarity index 84%
rename from remoting/host/mojo_ipc/ipc_server.h
rename to components/named_mojo_ipc_server/ipc_server.h
index 6f36503d..1f9b49ef 100644
--- a/remoting/host/mojo_ipc/ipc_server.h
+++ b/components/named_mojo_ipc_server/ipc_server.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef REMOTING_HOST_MOJO_IPC_IPC_SERVER_H_
-#define REMOTING_HOST_MOJO_IPC_IPC_SERVER_H_
+#ifndef COMPONENTS_NAMED_MOJO_IPC_SERVER_IPC_SERVER_H_
+#define COMPONENTS_NAMED_MOJO_IPC_SERVER_IPC_SERVER_H_
 
 #include "base/callback.h"
 #include "base/process/process_handle.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 
-namespace remoting {
+namespace named_mojo_ipc_server {
 
 // An interface for MojoIpcServer to allow mocking in unittests.
 class IpcServer {
@@ -41,6 +41,6 @@
   virtual base::ProcessId current_peer_pid() const = 0;
 };
 
-}  // namespace remoting
+}  // namespace named_mojo_ipc_server
 
-#endif  // REMOTING_HOST_MOJO_IPC_IPC_SERVER_H_
+#endif  // COMPONENTS_NAMED_MOJO_IPC_SERVER_IPC_SERVER_H_
diff --git a/remoting/host/mojo_ipc/mojo_ipc_server.cc b/components/named_mojo_ipc_server/named_mojo_ipc_server.cc
similarity index 78%
rename from remoting/host/mojo_ipc/mojo_ipc_server.cc
rename to components/named_mojo_ipc_server/named_mojo_ipc_server.cc
index 441e3e7..e032a488 100644
--- a/remoting/host/mojo_ipc/mojo_ipc_server.cc
+++ b/components/named_mojo_ipc_server/named_mojo_ipc_server.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 "remoting/host/mojo_ipc/mojo_ipc_server.h"
+#include "components/named_mojo_ipc_server/named_mojo_ipc_server.h"
 
 #include <memory>
 
@@ -14,14 +14,14 @@
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
-#include "remoting/host/mojo_ipc/mojo_server_endpoint_connector.h"
+#include "components/named_mojo_ipc_server/named_mojo_server_endpoint_connector.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "base/strings/stringprintf.h"
 #include "base/win/win_util.h"
 #endif  // BUILDFLAG(IS_WIN)
 
-namespace remoting {
+namespace named_mojo_ipc_server {
 
 namespace {
 
@@ -55,7 +55,7 @@
 
 }  // namespace
 
-MojoIpcServerBase::MojoIpcServerBase(
+NamedMojoIpcServerBase::NamedMojoIpcServerBase(
     const mojo::NamedPlatformChannel::ServerName& server_name,
     IsTrustedMojoEndpointCallback is_trusted_endpoint_callback)
     : server_name_(server_name),
@@ -64,22 +64,22 @@
       base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
 }
 
-MojoIpcServerBase::~MojoIpcServerBase() {
+NamedMojoIpcServerBase::~NamedMojoIpcServerBase() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
-void MojoIpcServerBase::StartServer() {
+void NamedMojoIpcServerBase::StartServer() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (server_started_) {
     return;
   }
-  endpoint_connector_ = MojoServerEndpointConnector::Create(this);
+  endpoint_connector_ = NamedMojoServerEndpointConnector::Create(this);
   server_started_ = true;
   SendInvitation();
 }
 
-void MojoIpcServerBase::StopServer() {
+void NamedMojoIpcServerBase::StopServer() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (!server_started_) {
@@ -91,7 +91,7 @@
   active_connections_.clear();
 }
 
-void MojoIpcServerBase::Close(mojo::ReceiverId id) {
+void NamedMojoIpcServerBase::Close(mojo::ReceiverId id) {
   UntrackMessagePipe(id);
   auto it = active_connections_.find(id);
   if (it != active_connections_.end()) {
@@ -99,24 +99,24 @@
   }
 }
 
-void MojoIpcServerBase::SendInvitation() {
+void NamedMojoIpcServerBase::SendInvitation() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   io_sequence_->PostTaskAndReplyWithResult(
       FROM_HERE,
       base::BindOnce(&CreateServerEndpointOnIoSequence, server_name_),
-      base::BindOnce(&MojoIpcServerBase::OnServerEndpointCreated,
+      base::BindOnce(&NamedMojoIpcServerBase::OnServerEndpointCreated,
                      weak_factory_.GetWeakPtr()));
 }
 
-void MojoIpcServerBase::OnIpcDisconnected() {
+void NamedMojoIpcServerBase::OnIpcDisconnected() {
   if (disconnect_handler_) {
     disconnect_handler_.Run();
   }
   Close(current_receiver());
 }
 
-void MojoIpcServerBase::OnServerEndpointCreated(
+void NamedMojoIpcServerBase::OnServerEndpointCreated(
     mojo::PlatformChannelServerEndpoint endpoint) {
   if (!server_started_) {
     // A server endpoint might be created on |io_sequence_| after StopServer()
@@ -133,7 +133,7 @@
   endpoint_connector_->Connect(std::move(endpoint));
 }
 
-void MojoIpcServerBase::OnServerEndpointConnected(
+void NamedMojoIpcServerBase::OnServerEndpointConnected(
     std::unique_ptr<mojo::IsolatedConnection> connection,
     mojo::ScopedMessagePipeHandle message_pipe,
     base::ProcessId peer_pid) {
@@ -148,10 +148,10 @@
   SendInvitation();
 }
 
-void MojoIpcServerBase::OnServerEndpointConnectionFailed() {
-  resent_invitation_on_error_timer_.Start(FROM_HERE,
-                                          kResentInvitationOnErrorDelay, this,
-                                          &MojoIpcServerBase::SendInvitation);
+void NamedMojoIpcServerBase::OnServerEndpointConnectionFailed() {
+  resent_invitation_on_error_timer_.Start(
+      FROM_HERE, kResentInvitationOnErrorDelay, this,
+      &NamedMojoIpcServerBase::SendInvitation);
 }
 
-}  // namespace remoting
+}  // namespace named_mojo_ipc_server
diff --git a/remoting/host/mojo_ipc/mojo_ipc_server.h b/components/named_mojo_ipc_server/named_mojo_ipc_server.h
similarity index 77%
rename from remoting/host/mojo_ipc/mojo_ipc_server.h
rename to components/named_mojo_ipc_server/named_mojo_ipc_server.h
index a9150a4c..f025716 100644
--- a/remoting/host/mojo_ipc/mojo_ipc_server.h
+++ b/components/named_mojo_ipc_server/named_mojo_ipc_server.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 REMOTING_HOST_MOJO_IPC_MOJO_IPC_SERVER_H_
-#define REMOTING_HOST_MOJO_IPC_MOJO_IPC_SERVER_H_
+#ifndef COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_IPC_SERVER_H_
+#define COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_IPC_SERVER_H_
 
 #include <memory>
 #include <utility>
@@ -18,25 +18,26 @@
 #include "base/sequence_checker.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/timer/timer.h"
+#include "components/named_mojo_ipc_server/ipc_server.h"
+#include "components/named_mojo_ipc_server/named_mojo_server_endpoint_connector.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel_server_endpoint.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
-#include "remoting/host/mojo_ipc/ipc_server.h"
-#include "remoting/host/mojo_ipc/mojo_server_endpoint_connector.h"
 
 namespace mojo {
 class IsolatedConnection;
 }
 
-namespace remoting {
+namespace named_mojo_ipc_server {
 
 // Template-less base class to keep implementations in the .cc file. For usage,
 // see MojoIpcServer.
-class MojoIpcServerBase : public IpcServer,
-                          public MojoServerEndpointConnector::Delegate {
+class NamedMojoIpcServerBase
+    : public IpcServer,
+      public NamedMojoServerEndpointConnector::Delegate {
  public:
   using IsTrustedMojoEndpointCallback =
       base::RepeatingCallback<bool(base::ProcessId)>;
@@ -60,9 +61,10 @@
   }
 
  protected:
-  MojoIpcServerBase(const mojo::NamedPlatformChannel::ServerName& server_name,
-                    IsTrustedMojoEndpointCallback is_trusted_endpoint_callback);
-  ~MojoIpcServerBase() override;
+  NamedMojoIpcServerBase(
+      const mojo::NamedPlatformChannel::ServerName& server_name,
+      IsTrustedMojoEndpointCallback is_trusted_endpoint_callback);
+  ~NamedMojoIpcServerBase() override;
 
   void SendInvitation();
 
@@ -102,13 +104,13 @@
   // A task runner to run blocking jobs.
   scoped_refptr<base::SequencedTaskRunner> io_sequence_;
 
-  std::unique_ptr<MojoServerEndpointConnector> endpoint_connector_;
+  std::unique_ptr<NamedMojoServerEndpointConnector> endpoint_connector_;
   ActiveConnectionMap active_connections_;
   base::OneShotTimer resent_invitation_on_error_timer_;
 
   base::RepeatingClosure on_invitation_sent_callback_for_testing_;
 
-  base::WeakPtrFactory<MojoIpcServerBase> weak_factory_{this};
+  base::WeakPtrFactory<NamedMojoIpcServerBase> weak_factory_{this};
 };
 
 // A helper that uses a NamedPlatformChannel to send out mojo invitations and
@@ -146,24 +148,25 @@
 //         base::BindRepeating(&MyInterfaceImpl::IsTrustedMojoEndpoint)};
 //   };
 template <typename Interface>
-class MojoIpcServer final : public MojoIpcServerBase {
+class NamedMojoIpcServer final : public NamedMojoIpcServerBase {
  public:
   // server_name: The server name to start the NamedPlatformChannel.
   // is_trusted_endpoint_callback: A predicate which returns true if the process
   // referred to by the caller PID is a trusted mojo endpoint.
-  MojoIpcServer(const mojo::NamedPlatformChannel::ServerName& server_name,
-                Interface* interface_impl,
-                IsTrustedMojoEndpointCallback is_trusted_endpoint_callback)
-      : MojoIpcServerBase(server_name, std::move(is_trusted_endpoint_callback)),
+  NamedMojoIpcServer(const mojo::NamedPlatformChannel::ServerName& server_name,
+                     Interface* interface_impl,
+                     IsTrustedMojoEndpointCallback is_trusted_endpoint_callback)
+      : NamedMojoIpcServerBase(server_name,
+                               std::move(is_trusted_endpoint_callback)),
         interface_impl_(interface_impl) {
     receiver_set_.set_disconnect_handler(base::BindRepeating(
-        &MojoIpcServer::OnIpcDisconnected, base::Unretained(this)));
+        &NamedMojoIpcServer::OnIpcDisconnected, base::Unretained(this)));
   }
 
-  ~MojoIpcServer() override = default;
+  ~NamedMojoIpcServer() override = default;
 
-  MojoIpcServer(const MojoIpcServer&) = delete;
-  MojoIpcServer& operator=(const MojoIpcServer&) = delete;
+  NamedMojoIpcServer(const NamedMojoIpcServer&) = delete;
+  NamedMojoIpcServer& operator=(const NamedMojoIpcServer&) = delete;
 
   void set_disconnect_handler(base::RepeatingClosure handler) override {
     disconnect_handler_ = handler;
@@ -178,7 +181,7 @@
   }
 
  private:
-  // MojoIpcServerBase implementation.
+  // NamedMojoIpcServerBase implementation.
   mojo::ReceiverId TrackMessagePipe(mojo::ScopedMessagePipeHandle message_pipe,
                                     base::ProcessId peer_pid) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -200,6 +203,6 @@
   mojo::ReceiverSet<Interface, base::ProcessId> receiver_set_;
 };
 
-}  // namespace remoting
+}  // namespace named_mojo_ipc_server
 
-#endif  // REMOTING_HOST_MOJO_IPC_MOJO_IPC_SERVER_H_
+#endif  // COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_IPC_SERVER_H_
diff --git a/remoting/host/mojo_ipc/mojo_ipc_server_unittest.cc b/components/named_mojo_ipc_server/named_mojo_ipc_server_unittest.cc
similarity index 76%
rename from remoting/host/mojo_ipc/mojo_ipc_server_unittest.cc
rename to components/named_mojo_ipc_server/named_mojo_ipc_server_unittest.cc
index 11f0d91..c8c1c91 100644
--- a/remoting/host/mojo_ipc/mojo_ipc_server_unittest.cc
+++ b/components/named_mojo_ipc_server/named_mojo_ipc_server_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 "remoting/host/mojo_ipc/mojo_ipc_server.h"
+#include "components/named_mojo_ipc_server/named_mojo_ipc_server.h"
 
 #include <memory>
 #include <string>
@@ -19,7 +19,11 @@
 #include "base/test/task_environment.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread.h"
 #include "base/time/time.h"
+#include "components/named_mojo_ipc_server/named_mojo_ipc_test_util.h"
+#include "components/named_mojo_ipc_server/testing.test-mojom.h"
+#include "mojo/core/embedder/scoped_ipc_support.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -27,12 +31,10 @@
 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
 #include "mojo/public/cpp/system/isolated_connection.h"
 #include "mojo/public/cpp/system/message_pipe.h"
-#include "remoting/host/mojo_ipc/mojo_ipc_test_util.h"
-#include "remoting/host/mojom/testing.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace remoting {
+namespace named_mojo_ipc_server {
 
 namespace {
 
@@ -55,10 +57,10 @@
 
 }  // namespace
 
-class MojoIpcServerTest : public testing::Test, public test::mojom::Echo {
+class NamedMojoIpcServerTest : public testing::Test, public test::mojom::Echo {
  public:
-  MojoIpcServerTest();
-  ~MojoIpcServerTest() override;
+  NamedMojoIpcServerTest();
+  ~NamedMojoIpcServerTest() override;
 
   void SetUp() override;
 
@@ -68,7 +70,9 @@
       mojo::IsolatedConnection& client_connection);
   void WaitForInvitationSent();
 
-  std::unique_ptr<MojoIpcServer<test::mojom::Echo>> ipc_server_;
+  base::Thread ipc_thread_{"ipc!"};
+  std::unique_ptr<NamedMojoIpcServer<test::mojom::Echo>> ipc_server_;
+
   mojo::ReceiverId last_echo_string_receiver_id_ = 0u;
 
   // If this is set, EchoString() will run this callback instead of responding
@@ -81,50 +85,58 @@
 
   void OnInvitationSent();
 
+  std::unique_ptr<mojo::core::ScopedIPCSupport> ipc_support_;
   mojo::NamedPlatformChannel::ServerName test_server_name_;
 
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::MainThreadType::IO};
 
-  // Run loops that wait for MojoIpcServerBase::ObserverForTesting methods to
-  // be called.
+  // Run loops that wait for NamedMojoIpcServerBase::ObserverForTesting methods
+  // to be called.
   std::unique_ptr<base::RunLoop> on_invitation_sent_run_loop_;
 };
 
-MojoIpcServerTest::MojoIpcServerTest() {
+NamedMojoIpcServerTest::NamedMojoIpcServerTest() {
+  ipc_thread_.StartWithOptions(
+      base::Thread::Options(base::MessagePumpType::IO, 0));
+  ipc_support_ = std::make_unique<mojo::core::ScopedIPCSupport>(
+      ipc_thread_.task_runner(),
+      mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN);
+
   test_server_name_ = test::GenerateRandomServerName();
-  ipc_server_ = std::make_unique<MojoIpcServer<test::mojom::Echo>>(
+  ipc_server_ = std::make_unique<NamedMojoIpcServer<test::mojom::Echo>>(
       test_server_name_, this,
       base::BindRepeating([](base::ProcessId) { return true; }));
   ipc_server_->set_on_invitation_sent_callback_for_testing(base::BindRepeating(
-      &MojoIpcServerTest::OnInvitationSent, base::Unretained(this)));
+      &NamedMojoIpcServerTest::OnInvitationSent, base::Unretained(this)));
   on_invitation_sent_run_loop_ = std::make_unique<base::RunLoop>();
 }
 
-MojoIpcServerTest::~MojoIpcServerTest() = default;
+NamedMojoIpcServerTest::~NamedMojoIpcServerTest() = default;
 
-void MojoIpcServerTest::SetUp() {
+void NamedMojoIpcServerTest::SetUp() {
   ipc_server_->StartServer();
   WaitForInvitationSent();
 }
 
-mojo::PlatformChannelEndpoint MojoIpcServerTest::ConnectToTestServer() {
+mojo::PlatformChannelEndpoint NamedMojoIpcServerTest::ConnectToTestServer() {
   return mojo::NamedPlatformChannel::ConnectToServer(test_server_name_);
 }
 
-mojo::Remote<test::mojom::Echo> MojoIpcServerTest::ConnectAndCreateEchoRemote(
+mojo::Remote<test::mojom::Echo>
+NamedMojoIpcServerTest::ConnectAndCreateEchoRemote(
     mojo::IsolatedConnection& client_connection) {
   return mojo::Remote<test::mojom::Echo>(mojo::PendingRemote<test::mojom::Echo>(
       client_connection.Connect(ConnectToTestServer()), /* version= */ 0));
 }
 
-void MojoIpcServerTest::WaitForInvitationSent() {
+void NamedMojoIpcServerTest::WaitForInvitationSent() {
   on_invitation_sent_run_loop_->Run();
   on_invitation_sent_run_loop_ = std::make_unique<base::RunLoop>();
 }
 
-void MojoIpcServerTest::EchoString(const std::string& input,
-                                   EchoStringCallback callback) {
+void NamedMojoIpcServerTest::EchoString(const std::string& input,
+                                        EchoStringCallback callback) {
   if (echo_string_handler_) {
     echo_string_handler_.Run(input, std::move(callback));
     return;
@@ -135,17 +147,17 @@
   ASSERT_EQ(base::GetCurrentProcId(), ipc_server_->current_peer_pid());
 }
 
-void MojoIpcServerTest::OnInvitationSent() {
+void NamedMojoIpcServerTest::OnInvitationSent() {
   on_invitation_sent_run_loop_->Quit();
 }
 
-TEST_F(MojoIpcServerTest, SendEcho) {
+TEST_F(NamedMojoIpcServerTest, SendEcho) {
   mojo::IsolatedConnection client_connection;
   auto echo_remote = ConnectAndCreateEchoRemote(client_connection);
   SendEchoAndVerifyResponse(echo_remote);
 }
 
-TEST_F(MojoIpcServerTest, DeleteMojoServer_NoLingeringInvitations) {
+TEST_F(NamedMojoIpcServerTest, DeleteNamedMojoServer_NoLingeringInvitations) {
   ipc_server_.reset();
   base::RunLoop().RunUntilIdle();
   // For posix, the socket doesn't seem to be closed immediately after the
@@ -157,7 +169,7 @@
   ASSERT_FALSE(endpoint.is_valid());
 }
 
-TEST_F(MojoIpcServerTest, DisconnectHandler) {
+TEST_F(NamedMojoIpcServerTest, DisconnectHandler) {
   base::RunLoop disconnect_run_loop;
   base::MockCallback<base::RepeatingClosure> disconnect_handler;
   EXPECT_CALL(disconnect_handler, Run()).WillOnce([&]() {
@@ -177,7 +189,7 @@
   disconnect_run_loop.Run();
 }
 
-TEST_F(MojoIpcServerTest, DeleteMojoServer_RemoteDisconnected) {
+TEST_F(NamedMojoIpcServerTest, DeleteNamedMojoServer_RemoteDisconnected) {
   mojo::IsolatedConnection client_connection;
   auto echo_remote = ConnectAndCreateEchoRemote(client_connection);
   SendEchoAndVerifyResponse(echo_remote);
@@ -188,7 +200,7 @@
   disconnect_run_loop.Run();
 }
 
-TEST_F(MojoIpcServerTest, StopServer_RemoteDisconnected) {
+TEST_F(NamedMojoIpcServerTest, StopServer_RemoteDisconnected) {
   mojo::IsolatedConnection client_connection;
   auto echo_remote = ConnectAndCreateEchoRemote(client_connection);
   SendEchoAndVerifyResponse(echo_remote);
@@ -199,7 +211,7 @@
   disconnect_run_loop.Run();
 }
 
-TEST_F(MojoIpcServerTest, CloseReceiver_RemoteDisconnected) {
+TEST_F(NamedMojoIpcServerTest, CloseReceiver_RemoteDisconnected) {
   mojo::IsolatedConnection client_connection;
   auto echo_remote = ConnectAndCreateEchoRemote(client_connection);
   SendEchoAndVerifyResponse(echo_remote);
@@ -212,13 +224,13 @@
   ASSERT_EQ(0u, ipc_server_->GetNumberOfActiveConnectionsForTesting());
 }
 
-TEST_F(MojoIpcServerTest, CloseNonexistentReceiver_NoCrash) {
+TEST_F(NamedMojoIpcServerTest, CloseNonexistentReceiver_NoCrash) {
   ASSERT_EQ(0u, ipc_server_->GetNumberOfActiveConnectionsForTesting());
   ipc_server_->Close(1u);
   ASSERT_EQ(0u, ipc_server_->GetNumberOfActiveConnectionsForTesting());
 }
 
-TEST_F(MojoIpcServerTest, RemoteDisconnected_ConnectionRemoved) {
+TEST_F(NamedMojoIpcServerTest, RemoteDisconnected_ConnectionRemoved) {
   mojo::IsolatedConnection client_connection;
   auto echo_remote = ConnectAndCreateEchoRemote(client_connection);
   SendEchoAndVerifyResponse(echo_remote);
@@ -231,7 +243,8 @@
   ASSERT_EQ(0u, ipc_server_->GetNumberOfActiveConnectionsForTesting());
 }
 
-TEST_F(MojoIpcServerTest, RemoteDisconnectedBeforeBound_NewInvitationIsSent) {
+TEST_F(NamedMojoIpcServerTest,
+       RemoteDisconnectedBeforeBound_NewInvitationIsSent) {
   mojo::IsolatedConnection client_connection;
   auto handle = client_connection.Connect(ConnectToTestServer());
   base::SequencedTaskRunnerHandle::Get()->PostTask(
@@ -239,13 +252,13 @@
   WaitForInvitationSent();
 }
 
-TEST_F(MojoIpcServerTest, RemoteConnectsAndHangs_NewInvitationIsSent) {
+TEST_F(NamedMojoIpcServerTest, RemoteConnectsAndHangs_NewInvitationIsSent) {
   mojo::IsolatedConnection client_connection;
   auto handle = client_connection.Connect(ConnectToTestServer());
   WaitForInvitationSent();
 }
 
-TEST_F(MojoIpcServerTest, ParallelIpcs) {
+TEST_F(NamedMojoIpcServerTest, ParallelIpcs) {
   base::RunLoop echo_response_run_loop;
   base::MockCallback<EchoStringCallback> echo_mock_callback;
   EXPECT_CALL(echo_mock_callback, Run(_))
@@ -278,4 +291,4 @@
   echo_response_run_loop.Run();
 }
 
-}  // namespace remoting
+}  // namespace named_mojo_ipc_server
diff --git a/remoting/host/mojo_ipc/mojo_ipc_test_util.cc b/components/named_mojo_ipc_server/named_mojo_ipc_test_util.cc
similarity index 87%
rename from remoting/host/mojo_ipc/mojo_ipc_test_util.cc
rename to components/named_mojo_ipc_server/named_mojo_ipc_test_util.cc
index a6d3fe1..be0843e 100644
--- a/remoting/host/mojo_ipc/mojo_ipc_test_util.cc
+++ b/components/named_mojo_ipc_server/named_mojo_ipc_test_util.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 "remoting/host/mojo_ipc/mojo_ipc_test_util.h"
+#include "components/named_mojo_ipc_server/named_mojo_ipc_test_util.h"
 
 #include <inttypes.h>
 
@@ -13,7 +13,7 @@
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 
-namespace remoting {
+namespace named_mojo_ipc_server {
 namespace test {
 
 mojo::NamedPlatformChannel::ServerName GenerateRandomServerName() {
@@ -34,4 +34,4 @@
 }
 
 }  // namespace test
-}  // namespace remoting
+}  // namespace named_mojo_ipc_server
diff --git a/components/named_mojo_ipc_server/named_mojo_ipc_test_util.h b/components/named_mojo_ipc_server/named_mojo_ipc_test_util.h
new file mode 100644
index 0000000..0dbe05e
--- /dev/null
+++ b/components/named_mojo_ipc_server/named_mojo_ipc_test_util.h
@@ -0,0 +1,17 @@
+// Copyright 2021 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_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_IPC_TEST_UTIL_H_
+#define COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_IPC_TEST_UTIL_H_
+
+#include "mojo/public/cpp/platform/named_platform_channel.h"
+
+namespace named_mojo_ipc_server::test {
+
+// Generates a random server name for unittests.
+mojo::NamedPlatformChannel::ServerName GenerateRandomServerName();
+
+}  // namespace named_mojo_ipc_server::test
+
+#endif  // COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_IPC_TEST_UTIL_H_
diff --git a/remoting/host/mojo_ipc/mojo_ipc_util.cc b/components/named_mojo_ipc_server/named_mojo_ipc_util.cc
similarity index 88%
rename from remoting/host/mojo_ipc/mojo_ipc_util.cc
rename to components/named_mojo_ipc_server/named_mojo_ipc_util.cc
index 5424c5b..63b6e60 100644
--- a/remoting/host/mojo_ipc/mojo_ipc_util.cc
+++ b/components/named_mojo_ipc_server/named_mojo_ipc_util.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 "remoting/host/mojo_ipc/mojo_ipc_util.h"
+#include "components/named_mojo_ipc_server/named_mojo_ipc_util.h"
 
 #include <string>
 
@@ -14,7 +14,7 @@
 #include "base/logging.h"
 #endif
 
-namespace remoting {
+namespace named_mojo_ipc_server {
 
 mojo::NamedPlatformChannel::ServerName
 WorkingDirectoryIndependentServerNameFromUTF8(base::StringPiece name) {
@@ -35,4 +35,4 @@
   return mojo::NamedPlatformChannel::ServerNameFromUTF8(name);
 }
 
-}  // namespace remoting
+}  // namespace named_mojo_ipc_server
diff --git a/remoting/host/mojo_ipc/mojo_ipc_util.h b/components/named_mojo_ipc_server/named_mojo_ipc_util.h
similarity index 66%
rename from remoting/host/mojo_ipc/mojo_ipc_util.h
rename to components/named_mojo_ipc_server/named_mojo_ipc_util.h
index c0bb18a2..9cf58c25 100644
--- a/remoting/host/mojo_ipc/mojo_ipc_util.h
+++ b/components/named_mojo_ipc_server/named_mojo_ipc_util.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef REMOTING_HOST_MOJO_IPC_MOJO_IPC_UTIL_H_
-#define REMOTING_HOST_MOJO_IPC_MOJO_IPC_UTIL_H_
+#ifndef COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_IPC_UTIL_H_
+#define COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_IPC_UTIL_H_
 
 #include "base/strings/string_piece_forward.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 
-namespace remoting {
+namespace named_mojo_ipc_server {
 
 // Creates a server name that is independent to the working directory, i.e.
 // it resolves to the same channel no matter which working directory you are
@@ -16,6 +16,6 @@
 mojo::NamedPlatformChannel::ServerName
 WorkingDirectoryIndependentServerNameFromUTF8(base::StringPiece name);
 
-}  // namespace remoting
+}  // namespace named_mojo_ipc_server
 
-#endif  // REMOTING_HOST_MOJO_IPC_MOJO_IPC_UTIL_H_
+#endif  // COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_IPC_UTIL_H_
diff --git a/remoting/host/mojo_ipc/mojo_server_endpoint_connector.h b/components/named_mojo_ipc_server/named_mojo_server_endpoint_connector.h
similarity index 78%
rename from remoting/host/mojo_ipc/mojo_server_endpoint_connector.h
rename to components/named_mojo_ipc_server/named_mojo_server_endpoint_connector.h
index 0e143d04..96c8dda 100644
--- a/remoting/host/mojo_ipc/mojo_server_endpoint_connector.h
+++ b/components/named_mojo_ipc_server/named_mojo_server_endpoint_connector.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 REMOTING_HOST_MOJO_IPC_MOJO_SERVER_ENDPOINT_CONNECTOR_H_
-#define REMOTING_HOST_MOJO_IPC_MOJO_SERVER_ENDPOINT_CONNECTOR_H_
+#ifndef COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_SERVER_ENDPOINT_CONNECTOR_H_
+#define COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_SERVER_ENDPOINT_CONNECTOR_H_
 
 #include <memory>
 
@@ -12,7 +12,7 @@
 #include "mojo/public/cpp/system/isolated_connection.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 
-namespace remoting {
+namespace named_mojo_ipc_server {
 
 // Interface to allow platform-specific implementations to establish connection
 // between the server endpoint and the client. mojo::IsolatedConnection can
@@ -24,7 +24,7 @@
 // 2. Observing the client process' PID without passing it via IPC, which
 //    wouldn't be feasible with the alternative approach, since mojo doesn't
 //    expose the underlying socket/named pipe.
-class MojoServerEndpointConnector {
+class NamedMojoServerEndpointConnector {
  public:
   class Delegate {
    public:
@@ -45,19 +45,19 @@
 
   // Creates the platform-specific MojoServerEndpointConnector. |delegate| must
   // outlives the created object.
-  static std::unique_ptr<MojoServerEndpointConnector> Create(
+  static std::unique_ptr<NamedMojoServerEndpointConnector> Create(
       Delegate* delegate);
 
-  virtual ~MojoServerEndpointConnector() = default;
+  virtual ~NamedMojoServerEndpointConnector() = default;
 
   // Connects to |server_endpoint|; invokes the delegate when it's connected or
   // failed to connect. Note that only one pending server endpoint is allowed.
   virtual void Connect(mojo::PlatformChannelServerEndpoint server_endpoint) = 0;
 
  protected:
-  MojoServerEndpointConnector() = default;
+  NamedMojoServerEndpointConnector() = default;
 };
 
-}  // namespace remoting
+}  // namespace named_mojo_ipc_server
 
-#endif  // REMOTING_HOST_MOJO_IPC_MOJO_SERVER_ENDPOINT_CONNECTOR_H_
+#endif  // COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_SERVER_ENDPOINT_CONNECTOR_H_
diff --git a/remoting/host/mojo_ipc/mojo_server_endpoint_connector_linux.cc b/components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_linux.cc
similarity index 75%
rename from remoting/host/mojo_ipc/mojo_server_endpoint_connector_linux.cc
rename to components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_linux.cc
index 95e17c9..7a82c31 100644
--- a/remoting/host/mojo_ipc/mojo_server_endpoint_connector_linux.cc
+++ b/components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_linux.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 "remoting/host/mojo_ipc/mojo_server_endpoint_connector_linux.h"
+#include "components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_linux.h"
 
 #include <sys/socket.h>
 
@@ -13,19 +13,20 @@
 #include "base/logging.h"
 #include "mojo/public/cpp/platform/socket_utils_posix.h"
 
-namespace remoting {
+namespace named_mojo_ipc_server {
 
-MojoServerEndpointConnectorLinux::MojoServerEndpointConnectorLinux(
+NamedMojoServerEndpointConnectorLinux::NamedMojoServerEndpointConnectorLinux(
     Delegate* delegate)
     : delegate_(delegate) {
   DCHECK(delegate_);
 }
 
-MojoServerEndpointConnectorLinux::~MojoServerEndpointConnectorLinux() {
+NamedMojoServerEndpointConnectorLinux::
+    ~NamedMojoServerEndpointConnectorLinux() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
-void MojoServerEndpointConnectorLinux::Connect(
+void NamedMojoServerEndpointConnectorLinux::Connect(
     mojo::PlatformChannelServerEndpoint server_endpoint) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(server_endpoint.is_valid());
@@ -35,11 +36,11 @@
   read_watcher_controller_ = base::FileDescriptorWatcher::WatchReadable(
       pending_server_endpoint_.platform_handle().GetFD().get(),
       base::BindRepeating(
-          &MojoServerEndpointConnectorLinux::OnFileCanReadWithoutBlocking,
+          &NamedMojoServerEndpointConnectorLinux::OnFileCanReadWithoutBlocking,
           weak_factory_.GetWeakPtr()));
 }
 
-void MojoServerEndpointConnectorLinux::OnFileCanReadWithoutBlocking() {
+void NamedMojoServerEndpointConnectorLinux::OnFileCanReadWithoutBlocking() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   int fd = pending_server_endpoint_.platform_handle().GetFD().get();
@@ -82,9 +83,9 @@
 }
 
 // static
-std::unique_ptr<MojoServerEndpointConnector>
-MojoServerEndpointConnector::Create(Delegate* delegate) {
-  return std::make_unique<MojoServerEndpointConnectorLinux>(delegate);
+std::unique_ptr<NamedMojoServerEndpointConnector>
+NamedMojoServerEndpointConnector::Create(Delegate* delegate) {
+  return std::make_unique<NamedMojoServerEndpointConnectorLinux>(delegate);
 }
 
-}  // namespace remoting
+}  // namespace named_mojo_ipc_server
diff --git a/components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_linux.h b/components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_linux.h
new file mode 100644
index 0000000..cff39a7
--- /dev/null
+++ b/components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_linux.h
@@ -0,0 +1,52 @@
+// Copyright 2021 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_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_SERVER_ENDPOINT_CONNECTOR_LINUX_H_
+#define COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_SERVER_ENDPOINT_CONNECTOR_LINUX_H_
+
+#include "base/files/file_descriptor_watcher_posix.h"
+#include "base/memory/raw_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/thread_annotations.h"
+#include "components/named_mojo_ipc_server/named_mojo_server_endpoint_connector.h"
+
+namespace named_mojo_ipc_server {
+
+// Linux implementation for MojoServerEndpointConnector.
+class NamedMojoServerEndpointConnectorLinux final
+    : public NamedMojoServerEndpointConnector {
+ public:
+  explicit NamedMojoServerEndpointConnectorLinux(Delegate* delegate);
+  NamedMojoServerEndpointConnectorLinux(
+      const NamedMojoServerEndpointConnectorLinux&) = delete;
+  NamedMojoServerEndpointConnectorLinux& operator=(
+      const NamedMojoServerEndpointConnectorLinux&) = delete;
+  ~NamedMojoServerEndpointConnectorLinux() override;
+
+  // NamedMojoServerEndpointConnector implementation.
+  void Connect(mojo::PlatformChannelServerEndpoint server_endpoint) override;
+
+ private:
+  void OnFileCanReadWithoutBlocking();
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  raw_ptr<Delegate> delegate_ GUARDED_BY_CONTEXT(sequence_checker_);
+
+  // These are only valid/non-null when there is a pending connection.
+  // Note that `pending_server_endpoint_` must outlive
+  // `read_watcher_controller_`; otherwise a bad file descriptor error will
+  // occur at destruction.
+  mojo::PlatformChannelServerEndpoint pending_server_endpoint_
+      GUARDED_BY_CONTEXT(sequence_checker_);
+  std::unique_ptr<base::FileDescriptorWatcher::Controller>
+      read_watcher_controller_ GUARDED_BY_CONTEXT(sequence_checker_);
+
+  base::WeakPtrFactory<NamedMojoServerEndpointConnectorLinux> weak_factory_{
+      this};
+};
+
+}  // namespace named_mojo_ipc_server
+
+#endif  // COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_SERVER_ENDPOINT_CONNECTOR_LINUX_H_
diff --git a/components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_unsupported.cc b/components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_unsupported.cc
new file mode 100644
index 0000000..8a7817a
--- /dev/null
+++ b/components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_unsupported.cc
@@ -0,0 +1,18 @@
+// Copyright 2021 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/named_mojo_ipc_server/named_mojo_server_endpoint_connector.h"
+
+namespace named_mojo_ipc_server {
+
+// static
+// Dummy implementation that returns nullptr for unsupported platforms, i.e.
+// Mac.
+// TODO(yuweih): Implement NamedMojoServerEndpointConnector for Mac.
+std::unique_ptr<NamedMojoServerEndpointConnector>
+NamedMojoServerEndpointConnector::Create(Delegate* delegate) {
+  return nullptr;
+}
+
+}  // namespace named_mojo_ipc_server
diff --git a/remoting/host/mojo_ipc/mojo_server_endpoint_connector_win.cc b/components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_win.cc
similarity index 80%
rename from remoting/host/mojo_ipc/mojo_server_endpoint_connector_win.cc
rename to components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_win.cc
index 99f4c58..22df805 100644
--- a/remoting/host/mojo_ipc/mojo_server_endpoint_connector_win.cc
+++ b/components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_win.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 "remoting/host/mojo_ipc/mojo_server_endpoint_connector_win.h"
+#include "components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_win.h"
 
 #include <string.h>
 #include <windows.h>
@@ -23,9 +23,9 @@
 #include "mojo/public/cpp/platform/platform_handle.h"
 #include "mojo/public/cpp/system/isolated_connection.h"
 
-namespace remoting {
+namespace named_mojo_ipc_server {
 
-MojoServerEndpointConnectorWin::MojoServerEndpointConnectorWin(
+NamedMojoServerEndpointConnectorWin::NamedMojoServerEndpointConnectorWin(
     Delegate* delegate)
     : delegate_(delegate),
       client_connected_event_(base::WaitableEvent::ResetPolicy::MANUAL,
@@ -33,11 +33,11 @@
   DCHECK(delegate_);
 }
 
-MojoServerEndpointConnectorWin::~MojoServerEndpointConnectorWin() {
+NamedMojoServerEndpointConnectorWin::~NamedMojoServerEndpointConnectorWin() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
-void MojoServerEndpointConnectorWin::Connect(
+void NamedMojoServerEndpointConnectorWin::Connect(
     mojo::PlatformChannelServerEndpoint server_endpoint) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(server_endpoint.is_valid());
@@ -68,7 +68,7 @@
       client_connection_watcher_.StartWatching(
           &client_connected_event_,
           base::BindOnce(
-              &MojoServerEndpointConnectorWin::OnConnectedEventSignaled,
+              &NamedMojoServerEndpointConnectorWin::OnConnectedEventSignaled,
               base::Unretained(this)),
           base::SequencedTaskRunnerHandle::Get());
       return;
@@ -79,7 +79,7 @@
   }
 }
 
-void MojoServerEndpointConnectorWin::OnConnectedEventSignaled(
+void NamedMojoServerEndpointConnectorWin::OnConnectedEventSignaled(
     base::WaitableEvent* event) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(&client_connected_event_, event);
@@ -87,7 +87,7 @@
   OnReady();
 }
 
-void MojoServerEndpointConnectorWin::OnReady() {
+void NamedMojoServerEndpointConnectorWin::OnReady() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   base::ProcessId peer_pid;
@@ -111,7 +111,7 @@
                                        std::move(message_pipe), peer_pid);
 }
 
-void MojoServerEndpointConnectorWin::OnError() {
+void NamedMojoServerEndpointConnectorWin::OnError() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   ResetConnectionObjects();
@@ -119,12 +119,12 @@
 }
 
 // static
-std::unique_ptr<MojoServerEndpointConnector>
-MojoServerEndpointConnector::Create(Delegate* delegate) {
-  return std::make_unique<MojoServerEndpointConnectorWin>(delegate);
+std::unique_ptr<NamedMojoServerEndpointConnector>
+NamedMojoServerEndpointConnector::Create(Delegate* delegate) {
+  return std::make_unique<NamedMojoServerEndpointConnectorWin>(delegate);
 }
 
-void MojoServerEndpointConnectorWin::ResetConnectionObjects() {
+void NamedMojoServerEndpointConnectorWin::ResetConnectionObjects() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   client_connection_watcher_.StopWatching();
@@ -132,4 +132,4 @@
   pending_named_pipe_handle_.Close();
 }
 
-}  // namespace remoting
+}  // namespace named_mojo_ipc_server
diff --git a/remoting/host/mojo_ipc/mojo_server_endpoint_connector_win.h b/components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_win.h
similarity index 60%
rename from remoting/host/mojo_ipc/mojo_server_endpoint_connector_win.h
rename to components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_win.h
index ea03888..abb24184 100644
--- a/remoting/host/mojo_ipc/mojo_server_endpoint_connector_win.h
+++ b/components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_win.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 REMOTING_HOST_MOJO_IPC_MOJO_SERVER_ENDPOINT_CONNECTOR_WIN_H_
-#define REMOTING_HOST_MOJO_IPC_MOJO_SERVER_ENDPOINT_CONNECTOR_WIN_H_
+#ifndef COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_SERVER_ENDPOINT_CONNECTOR_WIN_H_
+#define COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_SERVER_ENDPOINT_CONNECTOR_WIN_H_
 
 #include <windows.h>
 
@@ -14,20 +14,20 @@
 #include "base/thread_annotations.h"
 #include "base/win/scoped_handle.h"
 #include "base/win/windows_types.h"
-#include "remoting/host/mojo_ipc/mojo_server_endpoint_connector.h"
+#include "components/named_mojo_ipc_server/named_mojo_server_endpoint_connector.h"
 
-namespace remoting {
+namespace named_mojo_ipc_server {
 
-// Windows implementation for MojoServerEndpointConnector.
-class MojoServerEndpointConnectorWin final
-    : public MojoServerEndpointConnector {
+// Windows implementation for NamedMojoServerEndpointConnector.
+class NamedMojoServerEndpointConnectorWin final
+    : public NamedMojoServerEndpointConnector {
  public:
-  explicit MojoServerEndpointConnectorWin(Delegate* delegate);
-  MojoServerEndpointConnectorWin(const MojoServerEndpointConnectorWin&) =
-      delete;
-  MojoServerEndpointConnectorWin& operator=(
-      const MojoServerEndpointConnectorWin&) = delete;
-  ~MojoServerEndpointConnectorWin() override;
+  explicit NamedMojoServerEndpointConnectorWin(Delegate* delegate);
+  NamedMojoServerEndpointConnectorWin(
+      const NamedMojoServerEndpointConnectorWin&) = delete;
+  NamedMojoServerEndpointConnectorWin& operator=(
+      const NamedMojoServerEndpointConnectorWin&) = delete;
+  ~NamedMojoServerEndpointConnectorWin() override;
 
   void Connect(mojo::PlatformChannelServerEndpoint server_endpoint) override;
 
@@ -58,6 +58,6 @@
   OVERLAPPED connect_overlapped_ GUARDED_BY_CONTEXT(sequence_checker_);
 };
 
-}  // namespace remoting
+}  // namespace named_mojo_ipc_server
 
-#endif  // REMOTING_HOST_MOJO_IPC_MOJO_SERVER_ENDPOINT_CONNECTOR_WIN_H_
+#endif  // COMPONENTS_NAMED_MOJO_IPC_SERVER_NAMED_MOJO_SERVER_ENDPOINT_CONNECTOR_WIN_H_
diff --git a/remoting/host/mojom/testing.mojom b/components/named_mojo_ipc_server/testing.test-mojom
similarity index 86%
rename from remoting/host/mojom/testing.mojom
rename to components/named_mojo_ipc_server/testing.test-mojom
index 89400ea..4ec1760 100644
--- a/remoting/host/mojom/testing.mojom
+++ b/components/named_mojo_ipc_server/testing.test-mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-module remoting.test.mojom;
+module named_mojo_ipc_server.test.mojom;
 
 // A simple mojo interface for testing.
 interface Echo {
diff --git a/components/new_or_sad_tab_strings.grdp b/components/new_or_sad_tab_strings.grdp
index 6c8a867..b61151d 100644
--- a/components/new_or_sad_tab_strings.grdp
+++ b/components/new_or_sad_tab_strings.grdp
@@ -119,6 +119,10 @@
                desc="Title of the new incognito tab page, not to be confused with the action of opening a new incognito tab.">
         New Incognito Tab
       </message>
+      <message name="IDS_INCOGNITO_TAB_LEARN_MORE_ACCESSIBILITY_LABEL"
+          desc="Accessibility label for a link labeled 'Learn more' that links to a help article about Incognito mode.">
+        Learn more about Incognito
+      </message>
       <if expr="not is_android">
         <message name="IDS_NEW_TAB_OTR_HEADING"
                  desc="Heading used when a person opens an OTR window">
diff --git a/components/new_or_sad_tab_strings_grdp/IDS_INCOGNITO_TAB_LEARN_MORE_ACCESSIBILITY_LABEL.png.sha1 b/components/new_or_sad_tab_strings_grdp/IDS_INCOGNITO_TAB_LEARN_MORE_ACCESSIBILITY_LABEL.png.sha1
new file mode 100644
index 0000000..87ca16c
--- /dev/null
+++ b/components/new_or_sad_tab_strings_grdp/IDS_INCOGNITO_TAB_LEARN_MORE_ACCESSIBILITY_LABEL.png.sha1
@@ -0,0 +1 @@
+f50a6ef53d98a6aa2597c8da8c0b1008753b3315
\ No newline at end of file
diff --git a/components/offline_pages/core/prefetch/tasks/download_archives_task_unittest.cc b/components/offline_pages/core/prefetch/tasks/download_archives_task_unittest.cc
index 1ba5a25..d3937ee 100644
--- a/components/offline_pages/core/prefetch/tasks/download_archives_task_unittest.cc
+++ b/components/offline_pages/core/prefetch/tasks/download_archives_task_unittest.cc
@@ -27,7 +27,7 @@
 
 const int64_t kSmallArchiveSize = 1LL * 1024 * 1024;
 const int64_t kLargeArchiveSize =
-    2 * PrefetchDownloaderQuota::GetMaxDailyQuotaBytes() / 3;
+    2 * PrefetchDownloaderQuota::kDefaultMaxDailyQuotaBytes / 3;
 
 const PrefetchItem* FindPrefetchItemByOfflineId(
     const std::set<PrefetchItem>& items,
diff --git a/components/openscreen_platform/logging.cc b/components/openscreen_platform/logging.cc
index d94fa94..bfca9fd6 100644
--- a/components/openscreen_platform/logging.cc
+++ b/components/openscreen_platform/logging.cc
@@ -49,7 +49,7 @@
 
 void Break() {
 #if defined(OFFICIAL_BUILD) && defined(NDEBUG)
-  IMMEDIATE_CRASH();
+  base::ImmediateCrash();
 #else
   // Chrome's base::debug::BreakDebugger is not properly annotated as
   // [[noreturn]], so we abort instead. This may need to be revisited
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc
index 5e3f0d60..155c7ebe 100644
--- a/components/payments/content/payment_request_state.cc
+++ b/components/payments/content/payment_request_state.cc
@@ -562,10 +562,6 @@
       profile_comparator()->IsShippingComplete(shipping_profiles_[0])) {
     selected_shipping_profile_ = shipping_profiles()[0];
   }
-  // Record the missing required fields (if any) of the most complete shipping
-  // profile.
-  profile_comparator()->RecordMissingFieldsOfShippingProfile(
-      shipping_profiles().empty() ? nullptr : shipping_profiles()[0]);
   UpdateIsReadyToPayAndNotifyObservers();
 }
 
@@ -657,11 +653,6 @@
       profile_comparator()->IsContactInfoComplete(contact_profiles_[0]))
     selected_contact_profile_ = contact_profiles()[0];
 
-  // Record the missing required fields (if any) of the most complete contact
-  // profile.
-  profile_comparator()->RecordMissingFieldsOfContactProfile(
-      contact_profiles().empty() ? nullptr : contact_profiles()[0]);
-
   // Sort apps.
   PaymentApp::SortApps(&available_apps_);
 
diff --git a/components/payments/core/payments_profile_comparator.cc b/components/payments/core/payments_profile_comparator.cc
index 4d9294012..6c125be 100644
--- a/components/payments/core/payments_profile_comparator.cc
+++ b/components/payments/core/payments_profile_comparator.cc
@@ -178,36 +178,6 @@
            GetRequiredProfileFieldsForShipping());
 }
 
-void PaymentsProfileComparator::RecordMissingFieldsOfShippingProfile(
-    const autofill::AutofillProfile* profile) const {
-  // We should not record anything when no shipping fields is required.
-  if (GetRequiredProfileFieldsForShipping() == kNone)
-    return;
-
-  // Record any required fields that are missing.
-  PaymentsProfileComparator::ProfileFields missing_fields =
-      GetMissingProfileFields(profile) & GetRequiredProfileFieldsForShipping();
-  if (missing_fields != kNone) {
-    base::UmaHistogramSparse("PaymentRequest.MissingShippingFields",
-                             missing_fields);
-  }
-}
-
-void PaymentsProfileComparator::RecordMissingFieldsOfContactProfile(
-    const autofill::AutofillProfile* profile) const {
-  // We should not record anything when no contact fields is required.
-  if (GetRequiredProfileFieldsForContact() == kNone)
-    return;
-
-  // Record any required fields that are missing.
-  PaymentsProfileComparator::ProfileFields missing_fields =
-      GetMissingProfileFields(profile) & GetRequiredProfileFieldsForContact();
-  if (missing_fields != kNone) {
-    base::UmaHistogramSparse("PaymentRequest.MissingContactFields",
-                             missing_fields);
-  }
-}
-
 std::u16string PaymentsProfileComparator::GetStringForMissingContactFields(
     const autofill::AutofillProfile& profile) const {
   return GetStringForMissingFields(GetMissingProfileFields(&profile) &
diff --git a/components/payments/core/payments_profile_comparator.h b/components/payments/core/payments_profile_comparator.h
index bff48b3..3f75881 100644
--- a/components/payments/core/payments_profile_comparator.h
+++ b/components/payments/core/payments_profile_comparator.h
@@ -33,9 +33,7 @@
  public:
   // Bitmask of potentially-required fields used in evaluating completeness. Bit
   // field values are identical to CompletionStatus in AutofillAddress.java and
-  // ContactEditor.java. Please also modify java files after changing these bits
-  // since missing fields on both Android and Desktop are recorded in the same
-  // UMA metric: PaymentRequest.Missing[Shipping|Contact]Fields.
+  // ContactEditor.java.
   using ProfileFields = uint32_t;
   const static ProfileFields kNone = 0;
   const static ProfileFields kName = 1 << 0;
@@ -112,11 +110,6 @@
   std::u16string GetTitleForMissingShippingFields(
       const autofill::AutofillProfile& profile) const;
 
-  void RecordMissingFieldsOfShippingProfile(
-      const autofill::AutofillProfile* profile) const;
-  void RecordMissingFieldsOfContactProfile(
-      const autofill::AutofillProfile* profile) const;
-
   // Clears the cached evaluation result for |profile|. Must be called when a
   // profile is modified and saved during the course of a PaymentRequest.
   virtual void Invalidate(const autofill::AutofillProfile& profile);
diff --git a/components/reporting/metrics/BUILD.gn b/components/reporting/metrics/BUILD.gn
index 8ea9c07..617d1897 100644
--- a/components/reporting/metrics/BUILD.gn
+++ b/components/reporting/metrics/BUILD.gn
@@ -31,29 +31,6 @@
   ]
 }
 
-static_library("test_support") {
-  testonly = true
-  sources = [
-    "fake_event_driven_telemetry_sampler_pool.cc",
-    "fake_event_driven_telemetry_sampler_pool.h",
-    "fake_metric_report_queue.cc",
-    "fake_metric_report_queue.h",
-    "fake_reporting_settings.cc",
-    "fake_reporting_settings.h",
-    "fake_sampler.cc",
-    "fake_sampler.h",
-  ]
-  deps = [
-    ":metrics_data_collection",
-    "//base",
-    "//components/reporting/client:report_queue",
-    "//components/reporting/proto:metric_data_proto",
-    "//components/reporting/proto:record_constants",
-    "//components/reporting/util:status",
-    "//testing/gtest",
-  ]
-}
-
 source_set("unit_tests") {
   testonly = true
   sources = [
@@ -67,11 +44,11 @@
   ]
   deps = [
     ":metrics_data_collection",
-    ":test_support",
     "//base",
     "//base/test:test_support",
     "//components/reporting/client:report_queue",
     "//components/reporting/client:test_support",
+    "//components/reporting/metrics/fakes:test_support",
     "//components/reporting/proto:metric_data_proto",
     "//components/reporting/proto:record_constants",
     "//components/reporting/util:status",
diff --git a/components/reporting/metrics/configured_sampler_unittest.cc b/components/reporting/metrics/configured_sampler_unittest.cc
index db786fc..cf916b3 100644
--- a/components/reporting/metrics/configured_sampler_unittest.cc
+++ b/components/reporting/metrics/configured_sampler_unittest.cc
@@ -10,8 +10,8 @@
 
 #include "base/strings/string_piece.h"
 #include "base/test/task_environment.h"
-#include "components/reporting/metrics/fake_reporting_settings.h"
-#include "components/reporting/metrics/fake_sampler.h"
+#include "components/reporting/metrics/fakes/fake_reporting_settings.h"
+#include "components/reporting/metrics/fakes/fake_sampler.h"
 #include "components/reporting/metrics/sampler.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/reporting/metrics/fakes/BUILD.gn b/components/reporting/metrics/fakes/BUILD.gn
new file mode 100644
index 0000000..474d722f
--- /dev/null
+++ b/components/reporting/metrics/fakes/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2021 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("test_support") {
+  testonly = true
+  sources = [
+    "fake_event_driven_telemetry_sampler_pool.cc",
+    "fake_event_driven_telemetry_sampler_pool.h",
+    "fake_metric_report_queue.cc",
+    "fake_metric_report_queue.h",
+    "fake_reporting_settings.cc",
+    "fake_reporting_settings.h",
+    "fake_sampler.cc",
+    "fake_sampler.h",
+  ]
+  deps = [
+    "//base",
+    "//components/reporting/metrics:metrics_data_collection",
+    "//components/reporting/client:report_queue",
+    "//components/reporting/proto:metric_data_proto",
+    "//components/reporting/proto:record_constants",
+    "//components/reporting/util:status",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/reporting/metrics/fake_event_driven_telemetry_sampler_pool.cc b/components/reporting/metrics/fakes/fake_event_driven_telemetry_sampler_pool.cc
similarity index 91%
rename from components/reporting/metrics/fake_event_driven_telemetry_sampler_pool.cc
rename to components/reporting/metrics/fakes/fake_event_driven_telemetry_sampler_pool.cc
index da09b51..2263813 100644
--- a/components/reporting/metrics/fake_event_driven_telemetry_sampler_pool.cc
+++ b/components/reporting/metrics/fakes/fake_event_driven_telemetry_sampler_pool.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 "components/reporting/metrics/fake_event_driven_telemetry_sampler_pool.h"
+#include "components/reporting/metrics/fakes/fake_event_driven_telemetry_sampler_pool.h"
 
 #include <vector>
 
diff --git a/components/reporting/metrics/fake_event_driven_telemetry_sampler_pool.h b/components/reporting/metrics/fakes/fake_event_driven_telemetry_sampler_pool.h
similarity index 82%
rename from components/reporting/metrics/fake_event_driven_telemetry_sampler_pool.h
rename to components/reporting/metrics/fakes/fake_event_driven_telemetry_sampler_pool.h
index 0d2856d4..bf46837 100644
--- a/components/reporting/metrics/fake_event_driven_telemetry_sampler_pool.h
+++ b/components/reporting/metrics/fakes/fake_event_driven_telemetry_sampler_pool.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 COMPONENTS_REPORTING_METRICS_FAKE_EVENT_DRIVEN_TELEMETRY_SAMPLER_POOL_H_
-#define COMPONENTS_REPORTING_METRICS_FAKE_EVENT_DRIVEN_TELEMETRY_SAMPLER_POOL_H_
+#ifndef COMPONENTS_REPORTING_METRICS_FAKES_FAKE_EVENT_DRIVEN_TELEMETRY_SAMPLER_POOL_H_
+#define COMPONENTS_REPORTING_METRICS_FAKES_FAKE_EVENT_DRIVEN_TELEMETRY_SAMPLER_POOL_H_
 
 #include <vector>
 
@@ -40,4 +40,4 @@
 
 }  // namespace reporting::test
 
-#endif  // COMPONENTS_REPORTING_METRICS_FAKE_EVENT_DRIVEN_TELEMETRY_SAMPLER_POOL_H_
+#endif  // COMPONENTS_REPORTING_METRICS_FAKES_FAKE_EVENT_DRIVEN_TELEMETRY_SAMPLER_POOL_H_
diff --git a/components/reporting/metrics/fake_metric_report_queue.cc b/components/reporting/metrics/fakes/fake_metric_report_queue.cc
similarity index 96%
rename from components/reporting/metrics/fake_metric_report_queue.cc
rename to components/reporting/metrics/fakes/fake_metric_report_queue.cc
index ac4a78fe..d693bc588 100644
--- a/components/reporting/metrics/fake_metric_report_queue.cc
+++ b/components/reporting/metrics/fakes/fake_metric_report_queue.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 "components/reporting/metrics/fake_metric_report_queue.h"
+#include "components/reporting/metrics/fakes/fake_metric_report_queue.h"
 
 #include <memory>
 #include <vector>
diff --git a/components/reporting/metrics/fake_metric_report_queue.h b/components/reporting/metrics/fakes/fake_metric_report_queue.h
similarity index 86%
rename from components/reporting/metrics/fake_metric_report_queue.h
rename to components/reporting/metrics/fakes/fake_metric_report_queue.h
index d8ecab3..ac38a769 100644
--- a/components/reporting/metrics/fake_metric_report_queue.h
+++ b/components/reporting/metrics/fakes/fake_metric_report_queue.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 COMPONENTS_REPORTING_METRICS_FAKE_METRIC_REPORT_QUEUE_H_
-#define COMPONENTS_REPORTING_METRICS_FAKE_METRIC_REPORT_QUEUE_H_
+#ifndef COMPONENTS_REPORTING_METRICS_FAKES_FAKE_METRIC_REPORT_QUEUE_H_
+#define COMPONENTS_REPORTING_METRICS_FAKES_FAKE_METRIC_REPORT_QUEUE_H_
 
 #include <memory>
 #include <vector>
@@ -48,4 +48,4 @@
 }  // namespace test
 }  // namespace reporting
 
-#endif  // COMPONENTS_REPORTING_METRICS_FAKE_METRIC_REPORT_QUEUE_H_
+#endif  // COMPONENTS_REPORTING_METRICS_FAKES_FAKE_METRIC_REPORT_QUEUE_H_
diff --git a/components/reporting/metrics/fake_reporting_settings.cc b/components/reporting/metrics/fakes/fake_reporting_settings.cc
similarity index 96%
rename from components/reporting/metrics/fake_reporting_settings.cc
rename to components/reporting/metrics/fakes/fake_reporting_settings.cc
index 6a166d9..e7d80ea7 100644
--- a/components/reporting/metrics/fake_reporting_settings.cc
+++ b/components/reporting/metrics/fakes/fake_reporting_settings.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 "components/reporting/metrics/fake_reporting_settings.h"
+#include "components/reporting/metrics/fakes/fake_reporting_settings.h"
 
 #include "base/containers/contains.h"
 #include "base/run_loop.h"
diff --git a/components/reporting/metrics/fake_reporting_settings.h b/components/reporting/metrics/fakes/fake_reporting_settings.h
similarity index 88%
rename from components/reporting/metrics/fake_reporting_settings.h
rename to components/reporting/metrics/fakes/fake_reporting_settings.h
index 97a6bf3..5d5378b 100644
--- a/components/reporting/metrics/fake_reporting_settings.h
+++ b/components/reporting/metrics/fakes/fake_reporting_settings.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 COMPONENTS_REPORTING_METRICS_FAKE_REPORTING_SETTINGS_H_
-#define COMPONENTS_REPORTING_METRICS_FAKE_REPORTING_SETTINGS_H_
+#ifndef COMPONENTS_REPORTING_METRICS_FAKES_FAKE_REPORTING_SETTINGS_H_
+#define COMPONENTS_REPORTING_METRICS_FAKES_FAKE_REPORTING_SETTINGS_H_
 
 #include <memory>
 #include <string>
@@ -56,4 +56,4 @@
 }  // namespace test
 }  // namespace reporting
 
-#endif  // COMPONENTS_REPORTING_METRICS_FAKE_REPORTING_SETTINGS_H_
+#endif  // COMPONENTS_REPORTING_METRICS_FAKES_FAKE_REPORTING_SETTINGS_H_
diff --git a/components/reporting/metrics/fake_sampler.cc b/components/reporting/metrics/fakes/fake_sampler.cc
similarity index 96%
rename from components/reporting/metrics/fake_sampler.cc
rename to components/reporting/metrics/fakes/fake_sampler.cc
index 1fb3108..638337a7 100644
--- a/components/reporting/metrics/fake_sampler.cc
+++ b/components/reporting/metrics/fakes/fake_sampler.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 "components/reporting/metrics/fake_sampler.h"
+#include "components/reporting/metrics/fakes/fake_sampler.h"
 
 #include <utility>
 
diff --git a/components/reporting/metrics/fake_sampler.h b/components/reporting/metrics/fakes/fake_sampler.h
similarity index 90%
rename from components/reporting/metrics/fake_sampler.h
rename to components/reporting/metrics/fakes/fake_sampler.h
index 1dd0ac3..14114a9 100644
--- a/components/reporting/metrics/fake_sampler.h
+++ b/components/reporting/metrics/fakes/fake_sampler.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 COMPONENTS_REPORTING_METRICS_FAKE_SAMPLER_H_
-#define COMPONENTS_REPORTING_METRICS_FAKE_SAMPLER_H_
+#ifndef COMPONENTS_REPORTING_METRICS_FAKES_FAKE_SAMPLER_H_
+#define COMPONENTS_REPORTING_METRICS_FAKES_FAKE_SAMPLER_H_
 
 #include "components/reporting/metrics/sampler.h"
 #include "components/reporting/proto/synced/metric_data.pb.h"
@@ -77,4 +77,4 @@
 }  // namespace test
 }  // namespace reporting
 
-#endif  // COMPONENTS_REPORTING_METRICS_FAKE_SAMPLER_H_
+#endif  // COMPONENTS_REPORTING_METRICS_FAKES_FAKE_SAMPLER_H_
diff --git a/components/reporting/metrics/metric_data_collector_unittest.cc b/components/reporting/metrics/metric_data_collector_unittest.cc
index a3a0596..344af078b6f6 100644
--- a/components/reporting/metrics/metric_data_collector_unittest.cc
+++ b/components/reporting/metrics/metric_data_collector_unittest.cc
@@ -13,10 +13,10 @@
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
-#include "components/reporting/metrics/fake_event_driven_telemetry_sampler_pool.h"
-#include "components/reporting/metrics/fake_metric_report_queue.h"
-#include "components/reporting/metrics/fake_reporting_settings.h"
-#include "components/reporting/metrics/fake_sampler.h"
+#include "components/reporting/metrics/fakes/fake_event_driven_telemetry_sampler_pool.h"
+#include "components/reporting/metrics/fakes/fake_metric_report_queue.h"
+#include "components/reporting/metrics/fakes/fake_reporting_settings.h"
+#include "components/reporting/metrics/fakes/fake_sampler.h"
 #include "components/reporting/metrics/metric_report_queue.h"
 #include "components/reporting/proto/synced/metric_data.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/components/reporting/metrics/metric_event_observer_manager_unittest.cc b/components/reporting/metrics/metric_event_observer_manager_unittest.cc
index bac09f1..005b5ad3 100644
--- a/components/reporting/metrics/metric_event_observer_manager_unittest.cc
+++ b/components/reporting/metrics/metric_event_observer_manager_unittest.cc
@@ -12,10 +12,10 @@
 
 #include "base/test/task_environment.h"
 #include "components/reporting/metrics/configured_sampler.h"
-#include "components/reporting/metrics/fake_event_driven_telemetry_sampler_pool.h"
-#include "components/reporting/metrics/fake_metric_report_queue.h"
-#include "components/reporting/metrics/fake_reporting_settings.h"
-#include "components/reporting/metrics/fake_sampler.h"
+#include "components/reporting/metrics/fakes/fake_event_driven_telemetry_sampler_pool.h"
+#include "components/reporting/metrics/fakes/fake_metric_report_queue.h"
+#include "components/reporting/metrics/fakes/fake_reporting_settings.h"
+#include "components/reporting/metrics/fakes/fake_sampler.h"
 #include "components/reporting/metrics/metric_report_queue.h"
 #include "components/reporting/proto/synced/metric_data.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/components/reporting/metrics/metric_rate_controller_unittest.cc b/components/reporting/metrics/metric_rate_controller_unittest.cc
index 04f9acb..7d8f8ed 100644
--- a/components/reporting/metrics/metric_rate_controller_unittest.cc
+++ b/components/reporting/metrics/metric_rate_controller_unittest.cc
@@ -10,7 +10,7 @@
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
-#include "components/reporting/metrics/fake_reporting_settings.h"
+#include "components/reporting/metrics/fakes/fake_reporting_settings.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace reporting {
diff --git a/components/reporting/metrics/metric_report_queue_unittest.cc b/components/reporting/metrics/metric_report_queue_unittest.cc
index 9ccb2f4f..8515e8b 100644
--- a/components/reporting/metrics/metric_report_queue_unittest.cc
+++ b/components/reporting/metrics/metric_report_queue_unittest.cc
@@ -14,7 +14,7 @@
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "components/reporting/client/mock_report_queue.h"
-#include "components/reporting/metrics/fake_reporting_settings.h"
+#include "components/reporting/metrics/fakes/fake_reporting_settings.h"
 #include "components/reporting/proto/synced/metric_data.pb.h"
 #include "components/reporting/proto/synced/record_constants.pb.h"
 #include "components/reporting/util/status.h"
diff --git a/components/reporting/metrics/metric_reporting_controller_unittest.cc b/components/reporting/metrics/metric_reporting_controller_unittest.cc
index b152d8e..c5545ea 100644
--- a/components/reporting/metrics/metric_reporting_controller_unittest.cc
+++ b/components/reporting/metrics/metric_reporting_controller_unittest.cc
@@ -10,7 +10,7 @@
 #include "base/callback_forward.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
-#include "components/reporting/metrics/fake_reporting_settings.h"
+#include "components/reporting/metrics/fakes/fake_reporting_settings.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace reporting {
diff --git a/components/reporting/metrics/multi_samplers_collector_unittest.cc b/components/reporting/metrics/multi_samplers_collector_unittest.cc
index 20b38cf..667e4dea 100644
--- a/components/reporting/metrics/multi_samplers_collector_unittest.cc
+++ b/components/reporting/metrics/multi_samplers_collector_unittest.cc
@@ -7,7 +7,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
-#include "components/reporting/metrics/fake_sampler.h"
+#include "components/reporting/metrics/fakes/fake_sampler.h"
 #include "components/reporting/metrics/multi_samplers_collector.h"
 #include "components/reporting/proto/synced/metric_data.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/reporting/storage/BUILD.gn b/components/reporting/storage/BUILD.gn
index 0c79a50..f77cee0d 100644
--- a/components/reporting/storage/BUILD.gn
+++ b/components/reporting/storage/BUILD.gn
@@ -50,6 +50,7 @@
     "//components/reporting/proto:record_proto",
     "//components/reporting/resources:resource_interface",
     "//components/reporting/util:file",
+    "//components/reporting/util:refcounted_closure_list",
     "//components/reporting/util:status",
     "//components/reporting/util:status_macros",
     "//components/reporting/util:task_runner_context",
diff --git a/components/reporting/storage/storage.cc b/components/reporting/storage/storage.cc
index 701db8d7..289a16f 100644
--- a/components/reporting/storage/storage.cc
+++ b/components/reporting/storage/storage.cc
@@ -8,6 +8,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/barrier_closure.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/callback_list.h"
@@ -943,4 +944,25 @@
 StatusOr<std::string> Storage::GetPipelineId() {
   return pipeline_id_in_storage_->GetPipelineId();
 }
+
+void Storage::RegisterCompletionCallback(base::OnceClosure callback) {
+  // Although this is an asynchronous action, note that Storage cannot be
+  // destructed until the callback is registered - StorageQueue is held by added
+  // reference here. Thus, the callback being registered is guaranteed
+  // to be called when the Storage is being destructed.
+  DCHECK(callback);
+  sequenced_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::OnceClosure callback, scoped_refptr<Storage> self) {
+            DCHECK_CALLED_ON_VALID_SEQUENCE(self->sequence_checker_);
+            const base::RepeatingClosure queue_callback =
+                base::BarrierClosure(self->queues_.size(), std::move(callback));
+            for (auto& queue : self->queues_) {
+              // Copy the callback as base::OnceClosure.
+              queue.second->RegisterCompletionCallback(queue_callback);
+            }
+          },
+          std::move(callback), base::WrapRefCounted(this)));
+}
 }  // namespace reporting
diff --git a/components/reporting/storage/storage.h b/components/reporting/storage/storage.h
index 2ce0d09..76c20e5 100644
--- a/components/reporting/storage/storage.h
+++ b/components/reporting/storage/storage.h
@@ -85,6 +85,11 @@
   // Returns the pipeline ID if possible. Otherwise, returns error Status.
   StatusOr<std::string> GetPipelineId();
 
+  // Registers completion notification callback. Thread-safe.
+  // All registered callbacks are called when all queues destructions come
+  // to their completion and the Storage is destructed as well.
+  void RegisterCompletionCallback(base::OnceClosure callback);
+
  protected:
   virtual ~Storage();
 
diff --git a/components/reporting/storage/storage_queue.cc b/components/reporting/storage/storage_queue.cc
index 9567376..f3989e19 100644
--- a/components/reporting/storage/storage_queue.cc
+++ b/components/reporting/storage/storage_queue.cc
@@ -17,6 +17,7 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/callback_list.h"
 #include "base/containers/adapters.h"
 #include "base/containers/flat_set.h"
 #include "base/files/file.h"
@@ -46,6 +47,7 @@
 #include "components/reporting/storage/storage_configuration.h"
 #include "components/reporting/storage/storage_uploader_interface.h"
 #include "components/reporting/util/file.h"
+#include "components/reporting/util/refcounted_closure_list.h"
 #include "components/reporting/util/status.h"
 #include "components/reporting/util/status_macros.h"
 #include "components/reporting/util/statusor.h"
@@ -151,6 +153,8 @@
     scoped_refptr<CompressionModule> compression_module)
     : base::RefCountedDeleteOnSequence<StorageQueue>(sequenced_task_runner),
       sequenced_task_runner_(sequenced_task_runner),
+      completion_closure_list_(
+          base::MakeRefCounted<RefCountedClosureList>(sequenced_task_runner)),
       low_priority_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
           {base::TaskPriority::BEST_EFFORT, base::MayBlock()})),
       options_(options),
@@ -331,9 +335,9 @@
                    GetFileSequenceIdFromPath(full_name));
   RETURN_IF_ERROR(SetGenerationId(full_name));
 
-  auto file_or_status = SingleFile::Create(full_name, file_info.GetSize(),
-                                           options_.memory_resource(),
-                                           options_.disk_space_resource());
+  auto file_or_status = SingleFile::Create(
+      full_name, file_info.GetSize(), options_.memory_resource(),
+      options_.disk_space_resource(), completion_closure_list_);
   if (!file_or_status.ok()) {
     return file_or_status.status();
   }
@@ -477,7 +481,7 @@
                 .AddExtensionASCII(base::NumberToString(generation_id_))
                 .AddExtensionASCII(base::NumberToString(next_sequencing_id_)),
             /*size=*/0, options_.memory_resource(),
-            options_.disk_space_resource()));
+            options_.disk_space_resource(), completion_closure_list_));
     next_sequencing_id_ = 0;
     auto insert_result = files_.emplace(next_sequencing_id_, file);
     DCHECK(insert_result.second);
@@ -508,7 +512,7 @@
               .AddExtensionASCII(base::NumberToString(generation_id_))
               .AddExtensionASCII(base::NumberToString(next_sequencing_id_)),
           /*size=*/0, options_.memory_resource(),
-          options_.disk_space_resource()));
+          options_.disk_space_resource(), completion_closure_list_));
   RETURN_IF_ERROR(new_file->Open(/*read_only=*/false));
   auto insert_result = files_.emplace(next_sequencing_id_, new_file);
   if (!insert_result.second) {
@@ -615,7 +619,7 @@
               .Append(METADATA_NAME)
               .AddExtensionASCII(base::NumberToString(next_sequencing_id_)),
           /*size=*/0, options_.memory_resource(),
-          options_.disk_space_resource()));
+          options_.disk_space_resource(), completion_closure_list_));
   RETURN_IF_ERROR(meta_file->Open(/*read_only=*/false));
 
   // The space for this following Append is being reserved in
@@ -655,8 +659,6 @@
                                                   meta_file->name()}));
   }
   meta_file->Close();
-  // Switch the latest metafile.
-  meta_file_ = std::move(meta_file);
   // Asynchronously delete all earlier metafiles. Do not wait for this to
   // happen.
   low_priority_task_runner_->PostTask(
@@ -673,7 +675,8 @@
   ASSIGN_OR_RETURN(
       scoped_refptr<SingleFile> meta_file,
       SingleFile::Create(meta_file_path, size, options_.memory_resource(),
-                         options_.disk_space_resource()));
+                         options_.disk_space_resource(),
+                         completion_closure_list_));
   RETURN_IF_ERROR(meta_file->Open(/*read_only=*/true));
   // Metadata file format is:
   // - generation id (8 bytes)
@@ -726,7 +729,7 @@
     // the latest sequencing id.
     last_record_digest_.emplace(read_result.ValueOrDie());
   }
-  meta_file_ = std::move(meta_file);
+  meta_file->Close();
   // Store used metadata file.
   used_files_set->emplace(meta_file_path);
   return Status::StatusOK();
@@ -1974,8 +1977,28 @@
 }
 
 void StorageQueue::ReleaseAllFileInstances() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(storage_queue_sequence_checker_);
+  // Close files explicitly, because they might be still referred by contexts.
+  for (auto& file : files_) {
+    file.second->Close();
+  }
   files_.clear();
-  meta_file_.reset();
+}
+
+void StorageQueue::RegisterCompletionCallback(base::OnceClosure callback) {
+  // Although this is an asynchronous action, note that `StorageQueue` cannot be
+  // destructed until the callback is registered - `StorageQueue` is held by
+  // the added reference here. Thus, the callback being registered is guaranteed
+  // to be called only when `StorageQueue` is being destructed.
+  DCHECK(callback);
+  sequenced_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::OnceClosure callback, scoped_refptr<StorageQueue> self) {
+            self->completion_closure_list_->RegisterCompletionCallback(
+                std::move(callback));
+          },
+          std::move(callback), base::WrapRefCounted(this)));
 }
 
 void StorageQueue::TestInjectErrorsForOperation(
@@ -1992,7 +2015,8 @@
     const base::FilePath& filename,
     int64_t size,
     scoped_refptr<ResourceInterface> memory_resource,
-    scoped_refptr<ResourceInterface> disk_space_resource) {
+    scoped_refptr<ResourceInterface> disk_space_resource,
+    scoped_refptr<RefCountedClosureList> completion_closure_list) {
   if (!disk_space_resource->Reserve(size)) {
     LOG(WARNING) << "Disk space exceeded adding file "
                  << filename.MaybeAsASCII();
@@ -2003,25 +2027,32 @@
   }
   // Cannot use base::MakeRefCounted, since the constructor is private.
   return scoped_refptr<StorageQueue::SingleFile>(
-      new SingleFile(filename, size, memory_resource, disk_space_resource));
+      new SingleFile(filename, size, memory_resource, disk_space_resource,
+                     completion_closure_list));
 }
 
 StorageQueue::SingleFile::SingleFile(
     const base::FilePath& filename,
     int64_t size,
     scoped_refptr<ResourceInterface> memory_resource,
-    scoped_refptr<ResourceInterface> disk_space_resource)
-    : filename_(filename),
+    scoped_refptr<ResourceInterface> disk_space_resource,
+    scoped_refptr<RefCountedClosureList> completion_closure_list)
+    : completion_closure_list_(completion_closure_list),
+      filename_(filename),
       size_(size),
       memory_resource_(memory_resource),
-      disk_space_resource_(disk_space_resource) {}
+      disk_space_resource_(disk_space_resource) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
 
 StorageQueue::SingleFile::~SingleFile() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   disk_space_resource_->Discard(size_);
   Close();
 }
 
 Status StorageQueue::SingleFile::Open(bool read_only) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (handle_) {
     DCHECK_EQ(is_readonly(), read_only);
     // TODO(b/157943192): Restart auto-closing timer.
@@ -2050,10 +2081,14 @@
 }
 
 void StorageQueue::SingleFile::Close() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   is_readonly_ = absl::nullopt;
   if (buffer_) {
     buffer_.reset();
+  }
+  if (buffer_size_ > 0) {
     memory_resource_->Discard(buffer_size_);
+    buffer_size_ = 0;
   }
   if (!handle_) {
     // TODO(b/157943192): Restart auto-closing timer.
@@ -2063,6 +2098,7 @@
 }
 
 void StorageQueue::SingleFile::DeleteWarnIfFailed() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!handle_);
   disk_space_resource_->Discard(size_);
   size_ = 0;
@@ -2074,6 +2110,7 @@
     uint32_t size,
     size_t max_buffer_size,
     bool expect_readonly) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!handle_) {
     return Status(error::UNAVAILABLE, base::StrCat({"File not open ", name()}));
   }
@@ -2162,6 +2199,7 @@
 }
 
 StatusOr<uint32_t> StorageQueue::SingleFile::Append(base::StringPiece data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!handle_) {
     return Status(error::UNAVAILABLE, base::StrCat({"File not open ", name()}));
   }
diff --git a/components/reporting/storage/storage_queue.h b/components/reporting/storage/storage_queue.h
index 78a72951..16e199f 100644
--- a/components/reporting/storage/storage_queue.h
+++ b/components/reporting/storage/storage_queue.h
@@ -9,8 +9,10 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/callback.h"
+#include "base/callback_list.h"
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/feature_list.h"
@@ -30,6 +32,7 @@
 #include "components/reporting/proto/synced/record.pb.h"
 #include "components/reporting/storage/storage_configuration.h"
 #include "components/reporting/storage/storage_uploader_interface.h"
+#include "components/reporting/util/refcounted_closure_list.h"
 #include "components/reporting/util/status.h"
 #include "components/reporting/util/statusor.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -128,6 +131,11 @@
   void AssignDegradationQueues(
       const std::vector<scoped_refptr<StorageQueue>>& degradation_queues);
 
+  // Registers completion notification callback. Thread-safe.
+  // All registered callbacks are called when the queue destruction comes
+  // to its completion.
+  void RegisterCompletionCallback(base::OnceClosure callback);
+
   // Test only: makes specified records fail on specified operation kind.
   void TestInjectErrorsForOperation(
       const test::StorageQueueOperationKind operation_kind,
@@ -165,7 +173,8 @@
         const base::FilePath& filename,
         int64_t size,
         scoped_refptr<ResourceInterface> memory_resource,
-        scoped_refptr<ResourceInterface> disk_space_resource);
+        scoped_refptr<ResourceInterface> disk_space_resource,
+        scoped_refptr<RefCountedClosureList> completion_closure_list);
 
     Status Open(bool read_only);  // No-op if already opened.
     void Close();                 // No-op if not opened.
@@ -207,7 +216,14 @@
     SingleFile(const base::FilePath& filename,
                int64_t size,
                scoped_refptr<ResourceInterface> memory_resource,
-               scoped_refptr<ResourceInterface> disk_space_resource);
+               scoped_refptr<ResourceInterface> disk_space_resource,
+               scoped_refptr<RefCountedClosureList> completion_closure_list);
+
+    SEQUENCE_CHECKER(sequence_checker_);
+
+    // Completion closure list reference. Dropped last, when `ReadContext` is
+    // destructed.
+    const scoped_refptr<RefCountedClosureList> completion_closure_list_;
 
     // Flag (valid for opened file only): true if file was opened for reading
     // only, false otherwise.
@@ -218,8 +234,8 @@
 
     std::unique_ptr<base::File> handle_;  // Set only when opened/created.
 
-    scoped_refptr<ResourceInterface> memory_resource_;
-    scoped_refptr<ResourceInterface> disk_space_resource_;
+    const scoped_refptr<ResourceInterface> memory_resource_;
+    const scoped_refptr<ResourceInterface> disk_space_resource_;
 
     // When reading the file, this is the buffer and data positions.
     // If the data is read sequentially, buffered portions are reused
@@ -387,6 +403,10 @@
   const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
   SEQUENCE_CHECKER(storage_queue_sequence_checker_);
 
+  // Completion closure list reference. Dropped when `StorageQueue` is
+  // destructed.
+  const scoped_refptr<RefCountedClosureList> completion_closure_list_;
+
   // Dedicated sequence task runner for low priority actions (which make
   // no impact on the main activity - e.g., deletion of the outdated metafiles).
   // Serializeing them should reduce their impact.
@@ -438,9 +458,6 @@
   // that cannot be filled in and is uploaded as such.
   absl::optional<int64_t> first_unconfirmed_sequencing_id_;
 
-  // Latest metafile. May be null.
-  scoped_refptr<SingleFile> meta_file_;
-
   // Ordered map of the files by ascending sequencing id.
   std::map<int64_t, scoped_refptr<SingleFile>> files_;
 
diff --git a/components/reporting/storage/storage_queue_stress_test.cc b/components/reporting/storage/storage_queue_stress_test.cc
index b01d03b..0b2a2c4 100644
--- a/components/reporting/storage/storage_queue_stress_test.cc
+++ b/components/reporting/storage/storage_queue_stress_test.cc
@@ -132,14 +132,7 @@
     options_.set_directory(base::FilePath(location_.GetPath()));
   }
 
-  void TearDown() override {
-    ResetTestStorageQueue();
-    // Make sure all memory is deallocated.
-    EXPECT_THAT(options_.memory_resource()->GetUsed(), Eq(0u));
-    // Make sure all disk is not reserved (files remain, but Storage is not
-    // responsible for them anymore).
-    EXPECT_THAT(options_.disk_space_resource()->GetUsed(), Eq(0u));
-  }
+  void TearDown() override { ResetTestStorageQueue(); }
 
   void CreateTestStorageQueueOrDie(const QueueOptions& options) {
     ASSERT_FALSE(storage_queue_) << "StorageQueue already assigned";
@@ -167,12 +160,22 @@
   }
 
   void ResetTestStorageQueue() {
-    // Let everything ongoing to finish.
+    if (storage_queue_) {
+      // StorageQueue is destructed on thread, wait for it to finish.
+      test::TestCallbackAutoWaiter waiter;
+      storage_queue_->RegisterCompletionCallback(base::BindOnce(
+          &test::TestCallbackAutoWaiter::Signal, base::Unretained(&waiter)));
+      storage_queue_.reset();
+    }
+    // Let remaining asynchronous activity finish.
+    // TODO(b/254418902): The next line is not logically necessary, but for
+    // unknown reason the tests becomes flaky without it, keeping it for now.
     task_environment_.RunUntilIdle();
-    storage_queue_.reset();
-    // StorageQueue is destructed on a thread,
-    // so we need to wait for it to destruct.
-    task_environment_.RunUntilIdle();
+    // Make sure all memory is deallocated.
+    EXPECT_THAT(options_.memory_resource()->GetUsed(), Eq(0u));
+    // Make sure all disk is not reserved (files remain, but Storage is not
+    // responsible for them anymore).
+    EXPECT_THAT(options_.disk_space_resource()->GetUsed(), Eq(0u));
   }
 
   void AsyncStartTestUploader(
diff --git a/components/reporting/storage/storage_queue_unittest.cc b/components/reporting/storage/storage_queue_unittest.cc
index 8fd048c1..e3dbac09 100644
--- a/components/reporting/storage/storage_queue_unittest.cc
+++ b/components/reporting/storage/storage_queue_unittest.cc
@@ -105,11 +105,6 @@
 
   void TearDown() override {
     ResetTestStorageQueue();
-    // Make sure all memory is deallocated.
-    EXPECT_THAT(options_.memory_resource()->GetUsed(), Eq(0u));
-    // Make sure all disk is not reserved (files remain, but Storage is not
-    // responsible for them anymore).
-    EXPECT_THAT(options_.disk_space_resource()->GetUsed(), Eq(0u));
     // Log next uploader id for possible verification.
     LOG(ERROR) << "Next uploader id=" << next_uploader_id.load();
   }
@@ -623,12 +618,22 @@
   }
 
   void ResetTestStorageQueue() {
-    // Let everything ongoing to finish.
+    if (storage_queue_) {
+      // StorageQueue is destructed on thread, wait for it to finish.
+      test::TestCallbackAutoWaiter waiter;
+      storage_queue_->RegisterCompletionCallback(base::BindOnce(
+          &test::TestCallbackAutoWaiter::Signal, base::Unretained(&waiter)));
+      storage_queue_.reset();
+    }
+    // Let remaining asynchronous activity finish.
+    // TODO(b/254418902): The next line is not logically necessary, but for
+    // unknown reason the tests becomes flaky without it, keeping it for now.
     task_environment_.RunUntilIdle();
-    storage_queue_.reset();
-    // StorageQueue is destructed on a thread,
-    // so we need to wait for it to destruct.
-    task_environment_.RunUntilIdle();
+    // Make sure all memory is deallocated.
+    EXPECT_THAT(options_.memory_resource()->GetUsed(), Eq(0u));
+    // Make sure all disk is not reserved (files remain, but Storage is not
+    // responsible for them anymore).
+    EXPECT_THAT(options_.disk_space_resource()->GetUsed(), Eq(0u));
     // Handle and reject INIT_RESUME (optionally), in case the queue is
     // recreated.
     EXPECT_CALL(set_mock_uploader_expectations_,
diff --git a/components/reporting/storage/storage_unittest.cc b/components/reporting/storage/storage_unittest.cc
index 612cf64a..993db8c4 100644
--- a/components/reporting/storage/storage_unittest.cc
+++ b/components/reporting/storage/storage_unittest.cc
@@ -823,17 +823,21 @@
   }
 
   void ResetTestStorage() {
-    // Let asynchronous activity finish.
-    task_environment_.RunUntilIdle();
     if (storage_) {
+      // StorageQueue comprising Storage are destructed on threads, wait for
+      // them to finish.
+      test::TestCallbackAutoWaiter waiter;
+      storage_->RegisterCompletionCallback(base::BindOnce(
+          &test::TestCallbackAutoWaiter::Signal, base::Unretained(&waiter)));
       storage_.reset();
-      // StorageQueue is destructed on a thread,
-      // so we need to wait for all queues to destruct.
-      task_environment_.RunUntilIdle();
     }
     // Key has already been loaded, no need to redo it next time
     // (unless explicitly requested).
     expect_to_need_key_ = false;
+    // Let remaining asynchronous activity finish.
+    // TODO(b/254418902): The next line is not logically necessary, but for
+    // unknown reason the tests becomes flaky without it, keeping it for now.
+    task_environment_.RunUntilIdle();
     // Make sure all memory is deallocated.
     EXPECT_THAT(options_.memory_resource()->GetUsed(), Eq(0u));
     // Make sure all disk is not reserved (files remain, but Storage is not
diff --git a/components/reporting/util/BUILD.gn b/components/reporting/util/BUILD.gn
index b84eaa4b..625d8edc 100644
--- a/components/reporting/util/BUILD.gn
+++ b/components/reporting/util/BUILD.gn
@@ -76,6 +76,15 @@
   ]
 }
 
+source_set("refcounted_closure_list") {
+  sources = [
+    "refcounted_closure_list.cc",
+    "refcounted_closure_list.h",
+  ]
+
+  deps = [ "//base" ]
+}
+
 # All unit tests are built as part of the //components:components_unittests
 # target.
 source_set("unit_tests") {
@@ -83,6 +92,7 @@
   sources = [
     "disconnectable_client_unittest.cc",
     "file_unittest.cc",
+    "refcounted_closure_list_unittest.cc",
     "status_macros_unittest.cc",
     "status_unittest.cc",
     "statusor_unittest.cc",
@@ -90,6 +100,7 @@
   deps = [
     ":disconnectable_client",
     ":file",
+    ":refcounted_closure_list",
     ":status",
     ":status_macros",
     ":task_runner_context",
diff --git a/components/reporting/util/refcounted_closure_list.cc b/components/reporting/util/refcounted_closure_list.cc
new file mode 100644
index 0000000..a679e4f2
--- /dev/null
+++ b/components/reporting/util/refcounted_closure_list.cc
@@ -0,0 +1,44 @@
+// 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/reporting/util/refcounted_closure_list.h"
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/callback_list.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+
+namespace reporting {
+
+RefCountedClosureList::RefCountedClosureList(
+    scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner)
+    : base::RefCountedDeleteOnSequence<RefCountedClosureList>(
+          sequenced_task_runner),
+      sequenced_task_runner_(sequenced_task_runner) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+RefCountedClosureList::~RefCountedClosureList() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Send notification to all registered closures.
+  callbacks_.Notify();
+  DCHECK(callbacks_.empty());
+  callback_subscriptions_.clear();
+}
+
+void RefCountedClosureList::RegisterCompletionCallback(
+    base::OnceClosure callback) {
+  DCHECK(callback);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  callback_subscriptions_.emplace_back(callbacks_.Add(std::move(callback)));
+}
+
+}  // namespace reporting
diff --git a/components/reporting/util/refcounted_closure_list.h b/components/reporting/util/refcounted_closure_list.h
new file mode 100644
index 0000000..2a8f1ad
--- /dev/null
+++ b/components/reporting/util/refcounted_closure_list.h
@@ -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.
+
+#ifndef COMPONENTS_REPORTING_UTIL_REFCOUNTED_CLOSURE_LIST_H_
+#define COMPONENTS_REPORTING_UTIL_REFCOUNTED_CLOSURE_LIST_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/callback_list.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/thread_annotations.h"
+
+namespace reporting {
+
+// Thread-safe refcounted closure class, combining `BarrierClosure` and
+// `OnceClosureList`, but allowing to take and drop references at arbitrary
+// time. When the last reference is dropped, `OnceClosureList` is notified.
+class RefCountedClosureList
+    : public base::RefCountedDeleteOnSequence<RefCountedClosureList> {
+ public:
+  explicit RefCountedClosureList(
+      scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner);
+  RefCountedClosureList(const RefCountedClosureList&) = delete;
+  RefCountedClosureList& operator=(const RefCountedClosureList&) = delete;
+
+  // Registers completion notification callback.
+  void RegisterCompletionCallback(base::OnceClosure callback);
+
+ private:
+  friend class base::RefCountedDeleteOnSequence<RefCountedClosureList>;
+  friend class base::DeleteHelper<RefCountedClosureList>;
+
+  ~RefCountedClosureList();
+
+  scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  // List of all completion closures (protected by |sequenced_task_runner_|).
+  base::OnceClosureList callbacks_ GUARDED_BY_CONTEXT(sequence_checker_);
+  std::vector<base::CallbackListSubscription> callback_subscriptions_
+      GUARDED_BY_CONTEXT(sequence_checker_);
+};
+
+}  // namespace reporting
+
+#endif  // COMPONENTS_REPORTING_UTIL_REFCOUNTED_CLOSURE_LIST_H_
diff --git a/components/reporting/util/refcounted_closure_list_unittest.cc b/components/reporting/util/refcounted_closure_list_unittest.cc
new file mode 100644
index 0000000..66e51c51
--- /dev/null
+++ b/components/reporting/util/refcounted_closure_list_unittest.cc
@@ -0,0 +1,79 @@
+// 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 <atomic>
+#include <memory>
+
+#include "base/callback.h"
+#include "base/rand_util.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/task/thread_pool.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/reporting/util/refcounted_closure_list.h"
+#include "components/reporting/util/test_support_callbacks.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::Eq;
+
+namespace reporting {
+namespace {
+
+class RefCountedClosureListTest : public ::testing::Test {
+ protected:
+  class Worker {
+   public:
+    Worker(std::atomic<size_t>* count,
+           const scoped_refptr<RefCountedClosureList> callback)
+        : count_(count), callback_(callback) {}
+    Worker(const Worker&) = delete;
+    Worker& operator=(const Worker&) = delete;
+    ~Worker() = default;  // Here we drop the reference to closure list!
+
+    void Run() { (*count_)--; }
+
+   private:
+    std::atomic<size_t>* const count_;
+    const scoped_refptr<RefCountedClosureList> callback_;
+  };
+
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+};
+
+TEST_F(RefCountedClosureListTest, BasicUsageTest) {
+  static constexpr size_t kNumIterations = 50;
+  static constexpr size_t kNumTasksFactor = 3;
+  for (size_t i = 0; i < kNumIterations; ++i) {
+    const size_t num_tasks = i * kNumTasksFactor + 1;
+    std::atomic<size_t> count{num_tasks};
+    {
+      test::TestCallbackAutoWaiter waiter;
+      const auto closure_list = base::MakeRefCounted<RefCountedClosureList>(
+          base::SequencedTaskRunnerHandle::Get());
+      closure_list->RegisterCompletionCallback(base::BindOnce(
+          &test::TestCallbackAutoWaiter::Signal, base::Unretained(&waiter)));
+      for (size_t t = 0; t < num_tasks; ++t) {
+        auto worker = std::make_unique<Worker>(&count, closure_list);
+        base::ThreadPool::PostDelayedTask(
+            FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
+            base::BindOnce(&Worker::Run, std::move(worker)),
+            base::Seconds(1.0 + base::RandDouble()));
+      }
+      ASSERT_THAT(count.load(), Eq(num_tasks));
+
+      // Forward time to trigger workers to run.
+      task_environment_.FastForwardBy(base::Seconds(2));
+      // Wait for the signal.
+    }
+    ASSERT_THAT(count.load(), Eq(0u));
+  }
+}
+
+}  // namespace
+}  // namespace reporting
diff --git a/components/services/storage/test_api/test_api.cc b/components/services/storage/test_api/test_api.cc
index b16fb51..db75b5e 100644
--- a/components/services/storage/test_api/test_api.cc
+++ b/components/services/storage/test_api/test_api.cc
@@ -60,7 +60,7 @@
   }
 
   // mojom::TestApi implementation:
-  void CrashNow() override { IMMEDIATE_CRASH(); }
+  void CrashNow() override { base::ImmediateCrash(); }
 
   void ForceLeveldbDatabaseCompaction(
       const std::string& name,
diff --git a/components/soda/BUILD.gn b/components/soda/BUILD.gn
index adaaba7..30eaba1 100644
--- a/components/soda/BUILD.gn
+++ b/components/soda/BUILD.gn
@@ -10,7 +10,7 @@
 
 buildflag_header("buildflags") {
   header = "buildflags.h"
-  flags = [ "ENABLE_SODA=$enable_soda" ]
+  flags = [ "ENABLE_SODA_INTEGRATION_TESTS=$enable_soda_integration_tests" ]
 }
 
 component("soda") {
diff --git a/components/soda/buildflags.gni b/components/soda/buildflags.gni
index d037f858..d96925b 100644
--- a/components/soda/buildflags.gni
+++ b/components/soda/buildflags.gni
@@ -4,5 +4,9 @@
 
 declare_args() {
   # Used to enable the Speech On-Device API (SODA) in unit and browser tests.
-  enable_soda = false
+  # This affects production code on some platforms and should not be enabled in
+  # official builds.
+  enable_soda_integration_tests = false
 }
+
+assert(!is_official_build || !enable_soda_integration_tests)
diff --git a/components/sync/base/model_type.cc b/components/sync/base/model_type.cc
index c0eaae1..05177cf 100644
--- a/components/sync/base/model_type.cc
+++ b/components/sync/base/model_type.cc
@@ -176,6 +176,9 @@
     {CONTACT_INFO, "CONTACT_INFO", "contact_info", "Contact Info",
      sync_pb::EntitySpecifics::kContactInfoFieldNumber,
      ModelTypeForHistograms::kContactInfo},
+    {SAVED_TAB_GROUP, "SAVED_TAB_GROUP", "saved_tab_group", "Saved Tab Group",
+     sync_pb::EntitySpecifics::kSavedTabGroupFieldNumber,
+     ModelTypeForHistograms::kSavedTabGroups},
     // ---- Proxy types ----
     {PROXY_TABS, "", "", "Proxy tabs", -1, ModelTypeForHistograms::kProxyTabs},
     // ---- Control Types ----
@@ -187,7 +190,7 @@
 static_assert(std::size(kModelTypeInfoMap) == GetNumModelTypes(),
               "kModelTypeInfoMap should have GetNumModelTypes() elements");
 
-static_assert(43 == syncer::GetNumModelTypes(),
+static_assert(44 == syncer::GetNumModelTypes(),
               "When adding a new type, update enum SyncModelTypes in enums.xml "
               "and suffix SyncModelType in histograms.xml.");
 
@@ -324,6 +327,9 @@
     case SEGMENTATION:
       specifics->mutable_segmentation();
       break;
+    case SAVED_TAB_GROUP:
+      specifics->mutable_saved_tab_group();
+      break;
   }
 }
 
@@ -354,7 +360,7 @@
 }
 
 ModelType GetModelTypeFromSpecifics(const sync_pb::EntitySpecifics& specifics) {
-  static_assert(43 == syncer::GetNumModelTypes(),
+  static_assert(44 == syncer::GetNumModelTypes(),
                 "When adding new protocol types, the following type lookup "
                 "logic must be updated.");
   if (specifics.has_bookmark())
@@ -439,6 +445,8 @@
     return AUTOFILL_WALLET_USAGE;
   if (specifics.has_segmentation())
     return SEGMENTATION;
+  if (specifics.has_saved_tab_group())
+    return SAVED_TAB_GROUP;
 
   // This client version doesn't understand |specifics|.
   DVLOG(1) << "Unknown datatype in sync proto.";
@@ -446,7 +454,7 @@
 }
 
 ModelTypeSet EncryptableUserTypes() {
-  static_assert(43 == syncer::GetNumModelTypes(),
+  static_assert(44 == syncer::GetNumModelTypes(),
                 "If adding an unencryptable type, remove from "
                 "encryptable_user_types below.");
   ModelTypeSet encryptable_user_types = UserTypes();
diff --git a/components/sync/base/model_type.h b/components/sync/base/model_type.h
index 0e2e0149..e11889b 100644
--- a/components/sync/base/model_type.h
+++ b/components/sync/base/model_type.h
@@ -140,6 +140,9 @@
   PRINTERS_AUTHORIZATION_SERVERS,
   // Contact information from the Google Address Storage.
   CONTACT_INFO,
+  // A tab group saved by a user. Currently only supported on desktop platforms
+  // (Linux, Mac, Windows, ChromeOS).
+  SAVED_TAB_GROUP,
 
   // Proxy types are excluded from the sync protocol, but are still considered
   // real user types. By convention, we prefix them with 'PROXY_' to distinguish
@@ -241,7 +244,8 @@
   kContactInfo = 53,
   kAutofillWalletUsage = 54,
   kSegmentation = 55,
-  kMaxValue = kSegmentation
+  kSavedTabGroups = 56,
+  kMaxValue = kSavedTabGroups
 };
 
 // Used to mark the type of EntitySpecifics that has no actual data.
@@ -258,14 +262,14 @@
   return ModelTypeSet(
       BOOKMARKS, PREFERENCES, PASSWORDS, AUTOFILL_PROFILE, AUTOFILL,
       AUTOFILL_WALLET_DATA, AUTOFILL_WALLET_METADATA, AUTOFILL_WALLET_OFFER,
-      AUTOFILL_WALLET_USAGE, THEMES, TYPED_URLS, EXTENSIONS,
-      SEARCH_ENGINES, SESSIONS, APPS, APP_SETTINGS, EXTENSION_SETTINGS,
+      AUTOFILL_WALLET_USAGE, THEMES, TYPED_URLS, EXTENSIONS, SEARCH_ENGINES,
+      SESSIONS, APPS, APP_SETTINGS, EXTENSION_SETTINGS,
       HISTORY_DELETE_DIRECTIVES, DICTIONARY, DEVICE_INFO, PRIORITY_PREFERENCES,
       SUPERVISED_USER_SETTINGS, APP_LIST, ARC_PACKAGE, PRINTERS, READING_LIST,
       USER_EVENTS, NIGORI, USER_CONSENTS, SEND_TAB_TO_SELF, SECURITY_EVENTS,
       WEB_APPS, WIFI_CONFIGURATIONS, OS_PREFERENCES, OS_PRIORITY_PREFERENCES,
       SHARING_MESSAGE, WORKSPACE_DESK, HISTORY, PRINTERS_AUTHORIZATION_SERVERS,
-      CONTACT_INFO);
+      CONTACT_INFO, SAVED_TAB_GROUP);
 }
 
 // These are the normal user-controlled types. This is to distinguish from
diff --git a/components/sync/base/pref_names.h b/components/sync/base/pref_names.h
index fdd7c64..79a9a49e 100644
--- a/components/sync/base/pref_names.h
+++ b/components/sync/base/pref_names.h
@@ -54,6 +54,7 @@
 inline constexpr char kSyncThemes[] = "sync.themes";
 inline constexpr char kSyncTypedUrls[] = "sync.typed_urls";
 inline constexpr char kSyncWifiConfigurations[] = "sync.wifi_configurations";
+inline constexpr char kSyncSavedTabGroups[] = "sync.saved_tab_groups";
 
 // Boolean used by enterprise configuration management in order to lock down
 // sync.
diff --git a/components/sync/base/sync_prefs.cc b/components/sync/base/sync_prefs.cc
index 7fdba40..7826566 100644
--- a/components/sync/base/sync_prefs.cc
+++ b/components/sync/base/sync_prefs.cc
@@ -311,6 +311,8 @@
       return prefs::kSyncTabs;
     case UserSelectableType::kWifiConfigurations:
       return prefs::kSyncWifiConfigurations;
+    case UserSelectableType::kSavedTabGroups:
+      return prefs::kSyncSavedTabGroups;
   }
   NOTREACHED();
   return nullptr;
diff --git a/components/sync/base/user_selectable_type.cc b/components/sync/base/user_selectable_type.cc
index 75292ab..e3bc65a 100644
--- a/components/sync/base/user_selectable_type.cc
+++ b/components/sync/base/user_selectable_type.cc
@@ -34,9 +34,10 @@
 constexpr char kReadingListTypeName[] = "readingList";
 constexpr char kTabsTypeName[] = "tabs";
 constexpr char kWifiConfigurationsTypeName[] = "wifiConfigurations";
+constexpr char kSavedTabGroupsTypeName[] = "savedTabGroups";
 
 UserSelectableTypeInfo GetUserSelectableTypeInfo(UserSelectableType type) {
-  static_assert(43 == syncer::GetNumModelTypes(),
+  static_assert(44 == syncer::GetNumModelTypes(),
                 "Almost always when adding a new ModelType, you must tie it to "
                 "a UserSelectableType below (new or existing) so the user can "
                 "disable syncing of that data. Today you must also update the "
@@ -97,6 +98,8 @@
               WIFI_CONFIGURATIONS,
               {WIFI_CONFIGURATIONS}};
 #endif
+    case UserSelectableType::kSavedTabGroups:
+      return {kSavedTabGroupsTypeName, SAVED_TAB_GROUP, {SAVED_TAB_GROUP}};
   }
   NOTREACHED();
   return {nullptr, UNSPECIFIED, {}};
@@ -169,6 +172,9 @@
   if (type == kWifiConfigurationsTypeName) {
     return UserSelectableType::kWifiConfigurations;
   }
+  if (type == kSavedTabGroupsTypeName) {
+    return UserSelectableType::kSavedTabGroups;
+  }
   return absl::nullopt;
 }
 
diff --git a/components/sync/base/user_selectable_type.h b/components/sync/base/user_selectable_type.h
index 14ad601..e26f44ea 100644
--- a/components/sync/base/user_selectable_type.h
+++ b/components/sync/base/user_selectable_type.h
@@ -35,7 +35,8 @@
   kReadingList,
   kTabs,
   kWifiConfigurations,
-  kLastType = kWifiConfigurations
+  kSavedTabGroups,
+  kLastType = kSavedTabGroups
 };
 
 using UserSelectableTypeSet = base::EnumSet<UserSelectableType,
diff --git a/components/sync/driver/resources/sync_index.ts b/components/sync/driver/resources/sync_index.ts
index 2782ea4e..328d680 100644
--- a/components/sync/driver/resources/sync_index.ts
+++ b/components/sync/driver/resources/sync_index.ts
@@ -17,15 +17,14 @@
 import './invalidations.js';
 
 import {assert} from 'chrome://resources/js/assert_ts.js';
-import {isWindows} from 'chrome://resources/js/cr.m.js';
 
 // Allow platform specific CSS rules.
 //
 // TODO(akalin): BMM and options page does something similar, too.
 // Move this to util.js.
-if (isWindows) {
-  document.documentElement.setAttribute('os', 'win');
-}
+// <if expr="is_win">
+document.documentElement.setAttribute('os', 'win');
+// </if>
 
 const tabBox = document.querySelector('cr-tab-box');
 assert(tabBox);
diff --git a/components/sync/driver/sync_user_settings_impl.cc b/components/sync/driver/sync_user_settings_impl.cc
index 3c1b2c87..d1ad7d8 100644
--- a/components/sync/driver/sync_user_settings_impl.cc
+++ b/components/sync/driver/sync_user_settings_impl.cc
@@ -268,7 +268,7 @@
 #endif
   types.RetainAll(registered_model_types_);
 
-  static_assert(43 == GetNumModelTypes(),
+  static_assert(44 == GetNumModelTypes(),
                 "If adding a new sync data type, update the list below below if"
                 " you want to disable the new data type for local sync.");
   types.PutAll(ControlTypes());
diff --git a/components/sync/engine/cycle/data_type_tracker.cc b/components/sync/engine/cycle/data_type_tracker.cc
index 7002fd1fa..9e5f7c1 100644
--- a/components/sync/engine/cycle/data_type_tracker.cc
+++ b/components/sync/engine/cycle/data_type_tracker.cc
@@ -13,6 +13,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
 #include "components/sync/base/features.h"
+#include "components/sync/base/model_type.h"
 #include "components/sync/engine/polling_constants.h"
 #include "components/sync/protocol/data_type_progress_marker.pb.h"
 
@@ -53,6 +54,7 @@
       return kVeryBigLocalChangeNudgeDelay;
     case SESSIONS:
     case HISTORY:
+    case SAVED_TAB_GROUP:
       // Sessions is the type that causes the most commit traffic. It gets a
       // custom nudge delay, tuned for a reasonable trade-off between traffic
       // and freshness.
@@ -161,6 +163,7 @@
     case OS_PRIORITY_PREFERENCES:
     case WORKSPACE_DESK:
     case NIGORI:
+    case SAVED_TAB_GROUP:
     case PROXY_TABS:
       return false;
     case UNSPECIFIED:
diff --git a/components/sync/protocol/proto_value_conversions_unittest.cc b/components/sync/protocol/proto_value_conversions_unittest.cc
index 2a7008b7..8116f23 100644
--- a/components/sync/protocol/proto_value_conversions_unittest.cc
+++ b/components/sync/protocol/proto_value_conversions_unittest.cc
@@ -65,7 +65,7 @@
 
 DEFINE_SPECIFICS_TO_VALUE_TEST(encrypted)
 
-static_assert(43 == syncer::GetNumModelTypes(),
+static_assert(44 == syncer::GetNumModelTypes(),
               "When adding a new field, add a DEFINE_SPECIFICS_TO_VALUE_TEST "
               "for your field below, and optionally a test for the specific "
               "conversions.");
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index 8ac463b2..09c680f 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -534,7 +534,7 @@
 }
 
 VISIT_PROTO_FIELDS(const sync_pb::EntitySpecifics& proto) {
-  static_assert(43 == GetNumModelTypes(),
+  static_assert(44 == GetNumModelTypes(),
                 "When adding a new protocol type, you will likely need to add "
                 "it here as well.");
   VISIT(encrypted);
diff --git a/components/url_formatter/BUILD.gn b/components/url_formatter/BUILD.gn
index f22b420..1ea38702 100644
--- a/components/url_formatter/BUILD.gn
+++ b/components/url_formatter/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//testing/libfuzzer/fuzzer_test.gni")
+
 if (is_android) {
   import("//build/config/android/rules.gni")
 }
@@ -84,3 +86,13 @@
     "//url",
   ]
 }
+
+fuzzer_test("url_formatter_fixer_fuzzer") {
+  sources = [ "url_fixer_fuzzer.cc" ]
+  deps = [
+    ":url_formatter",
+    "//base",
+    "//base:i18n",
+    "//url",
+  ]
+}
diff --git a/components/url_formatter/url_fixer_fuzzer.cc b/components/url_formatter/url_fixer_fuzzer.cc
new file mode 100644
index 0000000..59571d1
--- /dev/null
+++ b/components/url_formatter/url_fixer_fuzzer.cc
@@ -0,0 +1,79 @@
+// 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 <stdint.h>
+
+#include <string>
+#include <tuple>
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "base/at_exit.h"
+#include "base/check.h"
+#include "base/files/file_path.h"
+#include "base/i18n/icu_util.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "components/url_formatter/url_fixer.h"
+#include "url/third_party/mozilla/url_parse.h"
+
+namespace {
+
+// Performs initialization and holds state that's shared across all runs.
+class Environment {
+ public:
+  Environment() {
+    CHECK(base::i18n::InitializeICU());
+    logging::SetMinLogLevel(logging::LOG_FATAL);
+  }
+
+ private:
+  base::AtExitManager at_exit_manager_;
+};
+
+base::FilePath GenerateFuzzedFilePath(FuzzedDataProvider& provider) {
+  const std::string raw_string = provider.ConsumeRandomLengthString();
+#if BUILDFLAG(IS_WIN)
+  return base::FilePath(base::UTF8ToWide(raw_string));
+#else
+  return base::FilePath(raw_string);
+#endif
+}
+
+}  // namespace
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  static Environment env;
+  FuzzedDataProvider provider(data, size);
+
+  switch (provider.ConsumeIntegralInRange<int>(0, 3)) {
+    case 0: {
+      std::string text = provider.ConsumeRandomLengthString();
+      url::Parsed parts;
+      std::ignore = url_formatter::SegmentURL(text, &parts);
+      break;
+    }
+    case 1: {
+      std::u16string text =
+          base::UTF8ToUTF16(provider.ConsumeRandomLengthString());
+      url::Parsed parts;
+      std::ignore = url_formatter::SegmentURL(text, &parts);
+      break;
+    }
+    case 2: {
+      std::ignore =
+          url_formatter::FixupURL(provider.ConsumeRandomLengthString(),
+                                  provider.ConsumeRandomLengthString());
+      break;
+    }
+    case 3: {
+      std::ignore = url_formatter::FixupRelativeFile(
+          GenerateFuzzedFilePath(provider), GenerateFuzzedFilePath(provider));
+      break;
+    }
+  }
+
+  return 0;
+}
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index 8f3d6b39..587cf9e 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -235,7 +235,7 @@
 // render pass, instead of SkiaOutputDeviceBufferQueue itself.
 BASE_FEATURE(kRendererAllocatesImages,
              "RendererAllocatesImages",
-#if BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
              base::FEATURE_ENABLED_BY_DEFAULT
 #else
              base::FEATURE_DISABLED_BY_DEFAULT
diff --git a/components/viz/common/resources/bitmap_allocation.cc b/components/viz/common/resources/bitmap_allocation.cc
index 5ca9241..cf015f6 100644
--- a/components/viz/common/resources/bitmap_allocation.cc
+++ b/components/viz/common/resources/bitmap_allocation.cc
@@ -43,7 +43,7 @@
 
 base::MappedReadOnlyRegion AllocateSharedBitmap(const gfx::Size& size,
                                                 ResourceFormat format) {
-  DCHECK(IsBitmapFormatSupported(format));
+  DCHECK(IsBitmapFormatSupported(format)) << "(format = " << format << ")";
   size_t bytes = 0;
   if (!ResourceSizes::MaybeSizeInBytes(size, format, &bytes)) {
     DLOG(ERROR) << "AllocateMappedBitmap with size that overflows";
diff --git a/components/viz/service/display_embedder/buffer_queue.cc b/components/viz/service/display_embedder/buffer_queue.cc
index 2b760087..53c8116 100644
--- a/components/viz/service/display_embedder/buffer_queue.cc
+++ b/components/viz/service/display_embedder/buffer_queue.cc
@@ -135,13 +135,14 @@
   DCHECK(format_);
   const ResourceFormat format = GetResourceFormat(format_.value());
 
+  constexpr uint32_t usage = gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
+                             gpu::SHARED_IMAGE_USAGE_DISPLAY_WRITE |
+                             gpu::SHARED_IMAGE_USAGE_SCANOUT;
+
   available_buffers_.reserve(available_buffers_.size() + n);
   for (size_t i = 0; i < n; ++i) {
     const gpu::Mailbox mailbox = skia_output_surface_->CreateSharedImage(
-        format, size_, color_space_,
-        gpu::SHARED_IMAGE_USAGE_SCANOUT | gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
-            gpu::SHARED_IMAGE_USAGE_DISPLAY_WRITE,
-        surface_handle_);
+        format, size_, color_space_, usage, surface_handle_);
     DCHECK(!mailbox.IsZero());
 
     available_buffers_.push_back(
diff --git a/components/viz/service/display_embedder/output_presenter_fuchsia.cc b/components/viz/service/display_embedder/output_presenter_fuchsia.cc
index 3e38f37..442a4f5 100644
--- a/components/viz/service/display_embedder/output_presenter_fuchsia.cc
+++ b/components/viz/service/display_embedder/output_presenter_fuchsia.cc
@@ -186,6 +186,8 @@
 OutputPresenterFuchsia::AllocateImages(gfx::ColorSpace color_space,
                                        gfx::Size image_size,
                                        size_t num_images) {
+  DCHECK(!features::ShouldRendererAllocateImages());
+
   // Fuchsia allocates images in batches and does not support allocating and
   // releasing images on demand.
   CHECK_NE(num_images, 1u);
@@ -193,7 +195,6 @@
   // Create PresenterImageFuchsia for each buffer in the collection.
   constexpr uint32_t image_usage = gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
                                    gpu::SHARED_IMAGE_USAGE_DISPLAY_WRITE |
-                                   gpu::SHARED_IMAGE_USAGE_RASTER |
                                    gpu::SHARED_IMAGE_USAGE_SCANOUT;
 
   std::vector<std::unique_ptr<OutputPresenter::Image>> images;
@@ -284,19 +285,42 @@
     return;
   }
 
-  next_frame_->overlays.emplace_back();
-  auto& overlay = next_frame_->overlays.back();
-  overlay.pixmap = std::move(pixmap);
-  overlay.overlay_plane_data = gfx::OverlayPlaneData(
-      overlay_plane_candidate.plane_z_order,
-      absl::get<gfx::OverlayTransform>(overlay_plane_candidate.transform),
-      overlay_plane_candidate.display_rect, overlay_plane_candidate.uv_rect,
-      !overlay_plane_candidate.is_opaque,
-      gfx::ToRoundedRect(overlay_plane_candidate.damage_rect),
-      overlay_plane_candidate.opacity, overlay_plane_candidate.priority_hint,
-      overlay_plane_candidate.rounded_corners,
-      overlay_plane_candidate.color_space,
-      overlay_plane_candidate.hdr_metadata);
+  if (overlay_plane_candidate.is_root_render_pass) {
+    DCHECK(features::ShouldRendererAllocateImages());
+    DCHECK(!next_frame_->native_pixmap);
+    next_frame_->native_pixmap = std::move(pixmap);
+
+    // Pass the acquire fence to system compositor if one exists.
+    gfx::GpuFenceHandle acqire_fence = access->TakeAcquireFence();
+    if (!acqire_fence.is_null())
+      next_frame_->acquire_fences.push_back(std::move(acqire_fence));
+
+    // Create and pass a release fence to the system compositor too.
+    gpu::ExternalSemaphore semaphore =
+        gpu::ExternalSemaphore::Create(dependency_->GetVulkanContextProvider());
+    DCHECK(semaphore.is_valid());
+    auto release_fence = semaphore.TakeSemaphoreHandle().ToGpuFenceHandle();
+    next_frame_->release_fences.push_back(release_fence.Clone());
+
+    // The release fence is signaled when the primary plane buffer can be
+    // reused, rather than after it's first presented, so added as release fence
+    // for the current access directly.
+    access->SetReleaseFence(std::move(release_fence));
+  } else {
+    next_frame_->overlays.emplace_back();
+    auto& overlay = next_frame_->overlays.back();
+    overlay.pixmap = std::move(pixmap);
+    overlay.overlay_plane_data = gfx::OverlayPlaneData(
+        overlay_plane_candidate.plane_z_order,
+        absl::get<gfx::OverlayTransform>(overlay_plane_candidate.transform),
+        overlay_plane_candidate.display_rect, overlay_plane_candidate.uv_rect,
+        !overlay_plane_candidate.is_opaque,
+        gfx::ToRoundedRect(overlay_plane_candidate.damage_rect),
+        overlay_plane_candidate.opacity, overlay_plane_candidate.priority_hint,
+        overlay_plane_candidate.rounded_corners,
+        overlay_plane_candidate.color_space,
+        overlay_plane_candidate.hdr_metadata);
+  }
 }
 
 void OutputPresenterFuchsia::PresentNextFrame() {
diff --git a/components/webcrypto/PLAN.md b/components/webcrypto/PLAN.md
index 228a030..512b0e0 100644
--- a/components/webcrypto/PLAN.md
+++ b/components/webcrypto/PLAN.md
@@ -5,9 +5,6 @@
 
 ## Code Changes
 
-* Code health, especially breaking dependencies on deprecated base::Value
-  APIs (https://crbug.com/1312496) and migrating indexed loops to range loops
-  where it makes sense (https://crbug.com/1313075)
 * Investigate whether CryptoThreadPool is necessary, whether it is necessary
   for all operations, and whether it can be use some existing more generic
   thread pool rather than requiring a dedicated worker thread
diff --git a/components/webcrypto/algorithms/aes_cbc_unittest.cc b/components/webcrypto/algorithms/aes_cbc_unittest.cc
index 697591d..834d55f7 100644
--- a/components/webcrypto/algorithms/aes_cbc_unittest.cc
+++ b/components/webcrypto/algorithms/aes_cbc_unittest.cc
@@ -443,11 +443,11 @@
 // If key_ops is specified but empty, no key usages are allowed for the key.
 TEST_F(WebCryptoAesCbcTest, ImportKeyJwkEmptyKeyOps) {
   blink::WebCryptoKey key;
-  base::DictionaryValue dict;
-  dict.SetString("kty", "oct");
-  dict.SetBoolean("ext", false);
-  dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
-  dict.SetKey("key_ops", base::ListValue());
+  base::Value::Dict dict;
+  dict.Set("kty", "oct");
+  dict.Set("ext", false);
+  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
+  dict.Set("key_ops", base::Value::List());
 
   // The JWK does not contain encrypt usages.
   EXPECT_EQ(Status::ErrorJwkKeyopsInconsistent(),
@@ -465,9 +465,9 @@
 // If key_ops is missing, then any key usages can be specified.
 TEST_F(WebCryptoAesCbcTest, ImportKeyJwkNoKeyOps) {
   blink::WebCryptoKey key;
-  base::DictionaryValue dict;
-  dict.SetString("kty", "oct");
-  dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
+  base::Value::Dict dict;
+  dict.Set("kty", "oct");
+  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
 
   EXPECT_EQ(Status::Success(),
             ImportKeyJwkFromDict(
@@ -485,11 +485,11 @@
 
 TEST_F(WebCryptoAesCbcTest, ImportKeyJwkKeyOpsEncryptDecrypt) {
   blink::WebCryptoKey key;
-  base::DictionaryValue dict;
-  dict.SetString("kty", "oct");
-  dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
+  base::Value::Dict dict;
+  dict.Set("kty", "oct");
+  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
   base::Value* key_ops =
-      dict.SetKey("key_ops", base::Value(base::Value::Type::LIST));
+      dict.Set("key_ops", base::Value(base::Value::Type::LIST));
 
   key_ops->Append("encrypt");
 
@@ -523,12 +523,12 @@
 // Test failure if input usage is NOT a strict subset of the JWK usage.
 TEST_F(WebCryptoAesCbcTest, ImportKeyJwkKeyOpsNotSuperset) {
   blink::WebCryptoKey key;
-  base::DictionaryValue dict;
-  dict.SetString("kty", "oct");
-  dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
-  base::ListValue key_ops;
+  base::Value::Dict dict;
+  dict.Set("kty", "oct");
+  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
+  base::Value::List key_ops;
   key_ops.Append("encrypt");
-  dict.SetKey("key_ops", std::move(key_ops));
+  dict.Set("key_ops", std::move(key_ops));
 
   EXPECT_EQ(
       Status::ErrorJwkKeyopsInconsistent(),
@@ -540,13 +540,13 @@
 
 TEST_F(WebCryptoAesCbcTest, ImportKeyJwkUseEnc) {
   blink::WebCryptoKey key;
-  base::DictionaryValue dict;
-  dict.SetString("kty", "oct");
-  dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
+  base::Value::Dict dict;
+  dict.Set("kty", "oct");
+  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
 
   // Test JWK composite use 'enc' usage
-  dict.SetString("alg", "A128CBC");
-  dict.SetString("use", "enc");
+  dict.Set("alg", "A128CBC");
+  dict.Set("use", "enc");
   EXPECT_EQ(
       Status::Success(),
       ImportKeyJwkFromDict(
@@ -599,13 +599,13 @@
 TEST_F(WebCryptoAesCbcTest, ImportJwkKeyOpsLacksUsages) {
   blink::WebCryptoKey key;
 
-  base::DictionaryValue dict;
-  dict.SetString("kty", "oct");
-  dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
+  base::Value::Dict dict;
+  dict.Set("kty", "oct");
+  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
 
-  base::ListValue key_ops;
+  base::Value::List key_ops;
   key_ops.Append("foo");
-  dict.SetKey("key_ops", std::move(key_ops));
+  dict.Set("key_ops", std::move(key_ops));
   EXPECT_EQ(Status::ErrorJwkKeyopsInconsistent(),
             ImportKeyJwkFromDict(
                 dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
diff --git a/components/webcrypto/algorithms/ecdsa_unittest.cc b/components/webcrypto/algorithms/ecdsa_unittest.cc
index 48124eb..c9e181c 100644
--- a/components/webcrypto/algorithms/ecdsa_unittest.cc
+++ b/components/webcrypto/algorithms/ecdsa_unittest.cc
@@ -100,11 +100,10 @@
       ReadJsonTestFileAsList("ec_private_keys.json");
   const base::Value& key_value = private_keys[0];
   ASSERT_TRUE(key_value.is_dict());
-  const base::DictionaryValue* key_dict =
-      &base::Value::AsDictionaryValue(key_value);
+  const base::Value::Dict& key_dict = key_value.GetDict();
   blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(key_dict);
-  const base::DictionaryValue* key_jwk;
-  ASSERT_TRUE(key_dict->GetDictionary("jwk", &key_jwk));
+  const base::Value::Dict* key_jwk = key_dict.FindDict("jwk");
+  ASSERT_TRUE(key_jwk);
 
   blink::WebCryptoKey private_key;
   ASSERT_EQ(
@@ -115,14 +114,12 @@
   // Erase the "d" member so the private key JWK can be used to import the
   // public key (WebCrypto doesn't provide a mechanism for importing a public
   // key given a private key).
-  std::unique_ptr<base::DictionaryValue> key_jwk_copy =
-      base::DictionaryValue::From(
-          base::Value::ToUniquePtrValue(key_jwk->Clone()));
-  key_jwk_copy->RemoveKey("d");
+  base::Value::Dict key_jwk_copy = key_jwk->Clone();
+  key_jwk_copy.Remove("d");
   blink::WebCryptoKey public_key;
   ASSERT_EQ(
       Status::Success(),
-      ImportKeyJwkFromDict(*key_jwk_copy, CreateEcdsaImportAlgorithm(curve),
+      ImportKeyJwkFromDict(key_jwk_copy, CreateEcdsaImportAlgorithm(curve),
                            true, blink::kWebCryptoKeyUsageVerify, &public_key));
 
   // Sign twice
@@ -155,7 +152,7 @@
 // determines what usages to use for the key.
 blink::WebCryptoKeyUsageMask GetExpectedUsagesForKeyImport(
     blink::WebCryptoKeyFormat key_format,
-    const base::DictionaryValue* test) {
+    const base::Value::Dict& test) {
   blink::WebCryptoKeyUsageMask kPublicUsages = blink::kWebCryptoKeyUsageVerify;
   blink::WebCryptoKeyUsageMask kPrivateUsages = blink::kWebCryptoKeyUsageSign;
 
@@ -166,10 +163,10 @@
     case blink::kWebCryptoKeyFormatPkcs8:
       return kPrivateUsages;
     case blink::kWebCryptoKeyFormatJwk: {
-      const base::DictionaryValue* key = nullptr;
-      if (!test->GetDictionary("key", &key))
+      const base::Value::Dict* key = test.FindDict("key");
+      if (!key)
         ADD_FAILURE() << "Missing key property";
-      return key->FindKey("d") ? kPrivateUsages : kPublicUsages;
+      return key->contains("d") ? kPrivateUsages : kPublicUsages;
     }
   }
 
@@ -185,21 +182,20 @@
     SCOPED_TRACE(&test_value - &tests[0]);
 
     ASSERT_TRUE(test_value.is_dict());
-    const base::DictionaryValue* test =
-        &base::Value::AsDictionaryValue(test_value);
+    const base::Value::Dict& test = test_value.GetDict();
 
     blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(test);
     blink::WebCryptoKeyFormat key_format = GetKeyFormatFromJsonTestCase(test);
     std::vector<uint8_t> key_data =
         GetKeyDataFromJsonTestCase(test, key_format);
-    std::string expected_error;
-    ASSERT_TRUE(test->GetString("error", &expected_error));
+    const std::string* expected_error = test.FindString("error");
+    ASSERT_TRUE(expected_error);
 
     blink::WebCryptoKey key;
     Status status =
         ImportKey(key_format, key_data, CreateEcdsaImportAlgorithm(curve), true,
                   GetExpectedUsagesForKeyImport(key_format, test), &key);
-    ASSERT_EQ(expected_error, StatusToString(status));
+    ASSERT_EQ(*expected_error, StatusToString(status));
   }
 }
 
@@ -214,15 +210,14 @@
     SCOPED_TRACE(&test_value - &tests[0]);
 
     ASSERT_TRUE(test_value.is_dict());
-    const base::DictionaryValue* test =
-        &base::Value::AsDictionaryValue(test_value);
+    const base::Value::Dict& test = test_value.GetDict();
 
     blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(test);
-    const base::DictionaryValue* jwk_dict;
-    EXPECT_TRUE(test->GetDictionary("jwk", &jwk_dict));
+    const base::Value::Dict* jwk_dict = test.FindDict("jwk");
+    ASSERT_TRUE(jwk_dict);
     std::vector<uint8_t> jwk_bytes = MakeJsonVector(*jwk_dict);
     std::vector<uint8_t> pkcs8_bytes = GetBytesFromHexString(
-        test, test->FindKey("exported_pkcs8") ? "exported_pkcs8" : "pkcs8");
+        test, test.contains("exported_pkcs8") ? "exported_pkcs8" : "pkcs8");
 
     // -------------------------------------------------
     // Test from JWK, and then export to {JWK, PKCS8}
@@ -270,7 +265,7 @@
     // where the publicKey was missing, it will be synthesized and written back
     // during export).
     std::vector<uint8_t> pkcs8_input_bytes = GetBytesFromHexString(
-        test, test->FindKey("original_pkcs8") ? "original_pkcs8" : "pkcs8");
+        test, test.contains("original_pkcs8") ? "original_pkcs8" : "pkcs8");
     base::span<const uint8_t> pkcs8_input_data(
         pkcs8_input_bytes.empty() ? pkcs8_bytes : pkcs8_input_bytes);
 
diff --git a/components/webcrypto/algorithms/sha_unittest.cc b/components/webcrypto/algorithms/sha_unittest.cc
index a1014668..f7fd536 100644
--- a/components/webcrypto/algorithms/sha_unittest.cc
+++ b/components/webcrypto/algorithms/sha_unittest.cc
@@ -26,8 +26,7 @@
   for (const auto& test_value : tests) {
     SCOPED_TRACE(&test_value - &tests[0]);
     ASSERT_TRUE(test_value.is_dict());
-    const base::DictionaryValue* test =
-        &base::Value::AsDictionaryValue(test_value);
+    const base::Value::Dict& test = test_value.GetDict();
 
     blink::WebCryptoAlgorithm test_algorithm =
         GetDigestAlgorithm(test, "algorithm");
diff --git a/components/webcrypto/algorithms/test_helpers.cc b/components/webcrypto/algorithms/test_helpers.cc
index 680f6ac0..eb41aa59 100644
--- a/components/webcrypto/algorithms/test_helpers.cc
+++ b/components/webcrypto/algorithms/test_helpers.cc
@@ -138,10 +138,6 @@
   return bytes;
 }
 
-std::vector<uint8_t> MakeJsonVector(const base::DictionaryValue& value) {
-  return MakeJsonVector(base::ValueView(value));
-}
-
 std::vector<uint8_t> MakeJsonVector(const base::ValueView& value) {
   std::string json;
   bool ok = base::JSONWriter::Write(value, &json);
@@ -157,14 +153,9 @@
   return std::move(*result).TakeList();
 }
 
-std::vector<uint8_t> GetBytesFromHexString(const base::Value* dict,
+std::vector<uint8_t> GetBytesFromHexString(const base::Value::Dict& dict,
                                            const std::string& property_name) {
-  if (!dict->is_dict()) {
-    ADD_FAILURE() << "Value is not a dictionary";
-    return std::vector<uint8_t>();
-  }
-
-  const std::string* hex_string = dict->FindStringPath(property_name);
+  const std::string* hex_string = dict.FindStringByDottedPath(property_name);
   if (!hex_string) {
     ADD_FAILURE() << "Couldn't get string property: " << property_name;
     return std::vector<uint8_t>();
@@ -173,10 +164,10 @@
   return HexStringToBytes(*hex_string);
 }
 
-blink::WebCryptoAlgorithm GetDigestAlgorithm(const base::DictionaryValue* dict,
+blink::WebCryptoAlgorithm GetDigestAlgorithm(const base::Value::Dict& dict,
                                              const char* property_name) {
-  std::string algorithm_name;
-  if (!dict->GetString(property_name, &algorithm_name)) {
+  const std::string* algorithm_name = dict.FindString(property_name);
+  if (!algorithm_name) {
     ADD_FAILURE() << "Couldn't get string property: " << property_name;
     return blink::WebCryptoAlgorithm::CreateNull();
   }
@@ -192,7 +183,7 @@
   };
 
   for (auto mapping : kDigestNameToId) {
-    if (mapping.name == algorithm_name)
+    if (mapping.name == *algorithm_name)
       return CreateAlgorithm(mapping.id);
   }
 
@@ -321,15 +312,6 @@
                    algorithm, extractable, usages, key);
 }
 
-Status ImportKeyJwkFromDict(const base::DictionaryValue& dict,
-                            const blink::WebCryptoAlgorithm& algorithm,
-                            bool extractable,
-                            blink::WebCryptoKeyUsageMask usages,
-                            blink::WebCryptoKey* key) {
-  return ImportKeyJwkFromDict(base::ValueView(dict), algorithm, extractable,
-                              usages, key);
-}
-
 absl::optional<base::Value::Dict> GetJwkDictionary(
     const std::vector<uint8_t>& json) {
   base::StringPiece json_string(reinterpret_cast<const char*>(json.data()),
@@ -409,9 +391,9 @@
     return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(k) failed";
   if (!base::EqualsCaseInsensitiveASCII(
           base::HexEncode(k_value.data(), k_value.size()), k_expected_hex)) {
-    return ::testing::AssertionFailure() << "Expected 'k' to be "
-                                         << k_expected_hex
-                                         << " but found something different";
+    return ::testing::AssertionFailure()
+           << "Expected 'k' to be " << k_expected_hex
+           << " but found something different";
   }
 
   return VerifyJwk(dict.value(), "oct", alg_expected, use_mask_expected);
@@ -449,9 +431,9 @@
     return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(e) failed";
   if (!base::EqualsCaseInsensitiveASCII(
           base::HexEncode(e_value.data(), e_value.size()), e_expected_hex)) {
-    return ::testing::AssertionFailure() << "Expected 'e' to be "
-                                         << e_expected_hex
-                                         << " but found something different";
+    return ::testing::AssertionFailure()
+           << "Expected 'e' to be " << e_expected_hex
+           << " but found something different";
   }
 
   return VerifyJwk(dict.value(), "RSA", alg_expected, use_mask_expected);
@@ -550,41 +532,41 @@
 }
 
 blink::WebCryptoKeyFormat GetKeyFormatFromJsonTestCase(
-    const base::DictionaryValue* test) {
-  std::string format;
-  EXPECT_TRUE(test->GetString("key_format", &format));
-  if (format == "jwk")
+    const base::Value::Dict& test) {
+  const std::string* format = test.FindString("key_format");
+  CHECK(format);
+  if (*format == "jwk")
     return blink::kWebCryptoKeyFormatJwk;
-  else if (format == "pkcs8")
+  else if (*format == "pkcs8")
     return blink::kWebCryptoKeyFormatPkcs8;
-  else if (format == "spki")
+  else if (*format == "spki")
     return blink::kWebCryptoKeyFormatSpki;
-  else if (format == "raw")
+  else if (*format == "raw")
     return blink::kWebCryptoKeyFormatRaw;
 
-  ADD_FAILURE() << "Unrecognized key format: " << format;
+  ADD_FAILURE() << "Unrecognized key format: " << *format;
   return blink::kWebCryptoKeyFormatRaw;
 }
 
 std::vector<uint8_t> GetKeyDataFromJsonTestCase(
-    const base::DictionaryValue* test,
+    const base::Value::Dict& test,
     blink::WebCryptoKeyFormat key_format) {
   if (key_format == blink::kWebCryptoKeyFormatJwk) {
-    const base::DictionaryValue* json;
-    EXPECT_TRUE(test->GetDictionary("key", &json));
+    const base::Value::Dict* json = test.FindDict("key");
+    EXPECT_TRUE(json);
     return MakeJsonVector(*json);
   }
   return GetBytesFromHexString(test, "key");
 }
 
 blink::WebCryptoNamedCurve GetCurveNameFromDictionary(
-    const base::DictionaryValue* dict) {
-  std::string curve_str;
-  if (!dict->GetString("crv", &curve_str)) {
+    const base::Value::Dict& dict) {
+  const std::string* curve_str = dict.FindString("crv");
+  if (!curve_str) {
     ADD_FAILURE() << "Missing crv parameter";
     return blink::kWebCryptoNamedCurveP384;
   }
-  return CurveNameToCurve(curve_str);
+  return CurveNameToCurve(*curve_str);
 }
 
 blink::WebCryptoNamedCurve CurveNameToCurve(const std::string& name) {
diff --git a/components/webcrypto/algorithms/test_helpers.h b/components/webcrypto/algorithms/test_helpers.h
index 1a5c09f..56dfe3d2 100644
--- a/components/webcrypto/algorithms/test_helpers.h
+++ b/components/webcrypto/algorithms/test_helpers.h
@@ -67,10 +67,6 @@
 
 std::vector<uint8_t> HexStringToBytes(const std::string& hex);
 
-// Deprecated; do not add new uses of this function, since base::DictionaryValue
-// itself is deprecated.
-std::vector<uint8_t> MakeJsonVector(const base::DictionaryValue& dict);
-
 // Serialize |value| to json, then return that json as a byte vector.
 std::vector<uint8_t> MakeJsonVector(const base::ValueView& value);
 
@@ -86,13 +82,13 @@
 // (which can include periods for nested dictionaries). Interprets the
 // string as a hex encoded string and converts it to a bytes list.
 //
-// Returns empty vector on failure or if |dict| is not a dictionary.
-std::vector<uint8_t> GetBytesFromHexString(const base::Value* dict,
+// Returns empty vector on failure.
+std::vector<uint8_t> GetBytesFromHexString(const base::Value::Dict& dict,
                                            const std::string& property_name);
 
 // Reads a string property with path "property_name" and converts it to a
-// WebCryptoAlgorith. Returns null algorithm on failure.
-blink::WebCryptoAlgorithm GetDigestAlgorithm(const base::DictionaryValue* dict,
+// WebCryptoAlgorithm. Returns null algorithm on failure.
+blink::WebCryptoAlgorithm GetDigestAlgorithm(const base::Value::Dict& dict,
                                              const char* property_name);
 
 // Returns true if any of the vectors in the input list have identical content.
@@ -134,15 +130,6 @@
                             blink::WebCryptoKeyUsageMask usages,
                             blink::WebCryptoKey* key);
 
-// Obsolete compatibility overload, do not add new uses. This is only present
-// because base::DictionaryValue requires explicit conversion to
-// base::ValueView.
-Status ImportKeyJwkFromDict(const base::DictionaryValue& dict,
-                            const blink::WebCryptoAlgorithm& algorithm,
-                            bool extractable,
-                            blink::WebCryptoKeyUsageMask usages,
-                            blink::WebCryptoKey* key);
-
 // Parses a vector of JSON into a dictionary.
 absl::optional<base::Value::Dict> GetJwkDictionary(
     const std::vector<uint8_t>& json);
@@ -193,17 +180,17 @@
 // Reads a key format string as used in some JSON test files and converts it to
 // a WebCryptoKeyFormat.
 blink::WebCryptoKeyFormat GetKeyFormatFromJsonTestCase(
-    const base::DictionaryValue* test);
+    const base::Value::Dict& test);
 
 // Extracts the key data bytes from |test| as used insome JSON test files.
 std::vector<uint8_t> GetKeyDataFromJsonTestCase(
-    const base::DictionaryValue* test,
+    const base::Value::Dict& test,
     blink::WebCryptoKeyFormat key_format);
 
 // Reads the "crv" string from a JSON test case and returns it as a
 // WebCryptoNamedCurve.
 blink::WebCryptoNamedCurve GetCurveNameFromDictionary(
-    const base::DictionaryValue* dict);
+    const base::Value::Dict& dict);
 
 blink::WebCryptoNamedCurve CurveNameToCurve(const std::string& name);
 
diff --git a/components/zucchini/buffer_source_unittest.cc b/components/zucchini/buffer_source_unittest.cc
index 5d10068..3b877f7c 100644
--- a/components/zucchini/buffer_source_unittest.cc
+++ b/components/zucchini/buffer_source_unittest.cc
@@ -279,7 +279,8 @@
   EXPECT_EQ(good(0x10000000, 5U), run("80 80 80 80 01"));
   EXPECT_EQ(good(0x10204081, 5U), run("81 81 81 81 01"));
   EXPECT_EQ(good(0x7FFFFFFF, 5U), run("FF FF FF FF 07"));
-  EXPECT_EQ(good(-static_cast<int32_t>(0x80000000), 5U), run("80 80 80 80 08"));
+  // Signed 0x80000000 is already negative.
+  EXPECT_EQ(good(static_cast<int32_t>(0x80000000), 5U), run("80 80 80 80 08"));
   EXPECT_EQ(good(-0x1, 5U), run("FF FF FF FF 0F"));  // Redundant code.
   EXPECT_EQ(kBad, run("FF FF FF FF 80"));            // Too long / out of data.
   EXPECT_EQ(good(0x0FFFFFFF, 5U), run("FF FF FF FF 10"));   // "1" discarded.
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index c98128e2..4850b2cf 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -655,6 +655,8 @@
     "buckets/bucket_manager.h",
     "buckets/bucket_manager_host.cc",
     "buckets/bucket_manager_host.h",
+    "buckets/bucket_utils.cc",
+    "buckets/bucket_utils.h",
     "byte_stream.cc",
     "byte_stream.h",
     "cache_storage/background_fetch_cache_entry_handler_impl.cc",
diff --git a/content/browser/android/content_feature_list.cc b/content/browser/android/content_feature_list.cc
index 56350d7..b3806463 100644
--- a/content/browser/android/content_feature_list.cc
+++ b/content/browser/android/content_feature_list.cc
@@ -36,6 +36,7 @@
     &features::kReduceGpuPriorityOnBackground,
     &features::kRequestDesktopSiteAdditions,
     &features::kRequestDesktopSiteExceptions,
+    &features::kRequestDesktopSiteZoom,
     &features::kTouchDragAndContextMenu,
     &features::kWebAuthConditionalUI,
     &features::kWebBluetoothNewPermissionsBackend,
diff --git a/content/browser/buckets/bucket_manager_host.cc b/content/browser/buckets/bucket_manager_host.cc
index 8608a7c..d9eee899 100644
--- a/content/browser/buckets/bucket_manager_host.cc
+++ b/content/browser/buckets/bucket_manager_host.cc
@@ -9,6 +9,7 @@
 #include "base/types/pass_key.h"
 #include "components/services/storage/public/cpp/buckets/bucket_info.h"
 #include "content/browser/buckets/bucket_manager.h"
+#include "content/browser/buckets/bucket_utils.h"
 #include "content/browser/storage_partition_impl.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/common/permissions/permission_utils.h"
@@ -16,33 +17,6 @@
 
 namespace content {
 
-namespace {
-
-bool IsValidBucketName(const std::string& name) {
-  // Details on bucket name validation and reasoning explained in
-  // https://github.com/WICG/storage-buckets/blob/gh-pages/explainer.md
-  if (name.empty() || name.length() >= 64)
-    return false;
-
-  // The name must only contain characters in a restricted set.
-  for (char ch : name) {
-    if (base::IsAsciiLower(ch))
-      continue;
-    if (base::IsAsciiDigit(ch))
-      continue;
-    if (ch == '_' || ch == '-')
-      continue;
-    return false;
-  }
-
-  // The first character in the name is more restricted.
-  if (name[0] == '_' || name[0] == '-')
-    return false;
-  return true;
-}
-
-}  // namespace
-
 BucketManagerHost::BucketManagerHost(BucketManager* manager,
                                      const blink::StorageKey& storage_key)
     : manager_(manager), storage_key_(storage_key) {
diff --git a/content/browser/buckets/bucket_utils.cc b/content/browser/buckets/bucket_utils.cc
new file mode 100644
index 0000000..ee122fa
--- /dev/null
+++ b/content/browser/buckets/bucket_utils.cc
@@ -0,0 +1,34 @@
+// 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 "content/browser/buckets/bucket_utils.h"
+
+#include "base/strings/string_util.h"
+
+namespace content {
+
+bool IsValidBucketName(const std::string& name) {
+  // Details on bucket name validation and reasoning explained in
+  // https://github.com/WICG/storage-buckets/blob/gh-pages/explainer.md
+  if (name.empty() || name.length() >= 64)
+    return false;
+
+  // The name must only contain characters in a restricted set.
+  for (char ch : name) {
+    if (base::IsAsciiLower(ch))
+      continue;
+    if (base::IsAsciiDigit(ch))
+      continue;
+    if (ch == '_' || ch == '-')
+      continue;
+    return false;
+  }
+
+  // The first character in the name is more restricted.
+  if (name[0] == '_' || name[0] == '-')
+    return false;
+  return true;
+}
+
+}  // namespace content
diff --git a/content/browser/buckets/bucket_utils.h b/content/browser/buckets/bucket_utils.h
new file mode 100644
index 0000000..9073e85
--- /dev/null
+++ b/content/browser/buckets/bucket_utils.h
@@ -0,0 +1,16 @@
+// 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 CONTENT_BROWSER_BUCKETS_BUCKET_UTILS_H_
+#define CONTENT_BROWSER_BUCKETS_BUCKET_UTILS_H_
+
+#include "content/common/content_export.h"
+
+#include <string>
+
+namespace content {
+CONTENT_EXPORT bool IsValidBucketName(const std::string& name);
+}
+
+#endif  // CONTENT_BROWSER_BUCKETS_BUCKET_UTILS_H_
diff --git a/content/browser/buckets/bucket_utils_unittest.cc b/content/browser/buckets/bucket_utils_unittest.cc
new file mode 100644
index 0000000..0af8cfa
--- /dev/null
+++ b/content/browser/buckets/bucket_utils_unittest.cc
@@ -0,0 +1,30 @@
+// 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 "content/browser/buckets/bucket_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+TEST(BucketUtilsTest, IsValidBucketName) {
+  struct {
+    std::string bucket_name;
+    bool expected_validity;
+  } test_cases[] = {
+      {"", false},
+      {"StringThatIsLongerThan64Characters1234567890123456789012345678901",
+       false},
+      {"-ShouldNotStartWithDash", false},
+      {"_ShouldNotStartWithUnderscore", false},
+      {"ShouldNotContainNonAsciiCharacters!", false},
+      {"ShouldNotContainUpperCases", false},
+      {"should-be-valid-123", true},
+  };
+
+  for (auto& testCase : test_cases) {
+    EXPECT_EQ(IsValidBucketName(testCase.bucket_name),
+              testCase.expected_validity);
+  }
+}
+}  // namespace content
diff --git a/content/browser/client_hints/client_hints.cc b/content/browser/client_hints/client_hints.cc
index b5769c4..b063e8b7 100644
--- a/content/browser/client_hints/client_hints.cc
+++ b/content/browser/client_hints/client_hints.cc
@@ -527,7 +527,8 @@
     // third-party cookies are blocked, so that we don't reveal any more user
     // data than is allowed by the cookie settings.
     if (outermost_main_frame_origin.IsSameOriginWith(current_origin) ||
-        !delegate->AreThirdPartyCookiesBlocked(current_origin.GetURL())) {
+        !delegate->AreThirdPartyCookiesBlocked(current_origin.GetURL(),
+                                               current)) {
       blink::EnabledClientHints current_url_hints;
       delegate->GetAllowedClientHintsFromSource(current_origin,
                                                 &current_url_hints);
@@ -565,7 +566,8 @@
           origin)) {
     // If third-party cookeis are blocked, we will not persist the
     // Sec-CH-UA-Reduced client hint in a third-party context.
-    if (delegate->AreThirdPartyCookiesBlocked(origin.GetURL())) {
+    if (delegate->AreThirdPartyCookiesBlocked(
+            origin.GetURL(), frame_tree_node->current_frame_host())) {
       accept_ch->clear();
       return;
     }
diff --git a/content/browser/content_security_policy_browsertest.cc b/content/browser/content_security_policy_browsertest.cc
index 727a766..778ac22 100644
--- a/content/browser/content_security_policy_browsertest.cc
+++ b/content/browser/content_security_policy_browsertest.cc
@@ -292,9 +292,11 @@
 
 class IsolatedWebAppContentBrowserClient : public ContentBrowserClient {
  public:
-  bool ShouldUrlUseApplicationIsolationLevel(BrowserContext* browser_context,
-                                             const GURL& url) override {
-    return true;
+  bool ShouldUrlUseApplicationIsolationLevel(
+      BrowserContext* browser_context,
+      const GURL& url,
+      bool origin_matches_flag) override {
+    return origin_matches_flag;
   }
 };
 
diff --git a/content/browser/direct_sockets/direct_sockets_test_utils.cc b/content/browser/direct_sockets/direct_sockets_test_utils.cc
index 1100de5..0ca92a0 100644
--- a/content/browser/direct_sockets/direct_sockets_test_utils.cc
+++ b/content/browser/direct_sockets/direct_sockets_test_utils.cc
@@ -208,8 +208,9 @@
 
 bool IsolatedWebAppContentBrowserClient::ShouldUrlUseApplicationIsolationLevel(
     BrowserContext* browser_context,
-    const GURL& url) {
-  return true;
+    const GURL& url,
+    bool origin_matches_flag) {
+  return origin_matches_flag;
 }
 
 absl::optional<blink::ParsedPermissionsPolicy>
diff --git a/content/browser/direct_sockets/direct_sockets_test_utils.h b/content/browser/direct_sockets/direct_sockets_test_utils.h
index 1a35723e..698a58a02 100644
--- a/content/browser/direct_sockets/direct_sockets_test_utils.h
+++ b/content/browser/direct_sockets/direct_sockets_test_utils.h
@@ -198,7 +198,8 @@
 class IsolatedWebAppContentBrowserClient : public ContentBrowserClient {
  public:
   bool ShouldUrlUseApplicationIsolationLevel(BrowserContext* browser_context,
-                                             const GURL& url) override;
+                                             const GURL& url,
+                                             bool origin_matches_flag) override;
 
   absl::optional<blink::ParsedPermissionsPolicy>
   GetPermissionsPolicyForIsolatedWebApp(
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index 8eba8be4..1dfa639 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -2977,6 +2977,74 @@
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
+                       RunAdAuctionInvalidDirectFromSellerSignalsInvalidURL) {
+  ASSERT_TRUE(NavigateToURL(shell(), https_server_->GetURL("a.test", "/echo")));
+
+  EXPECT_EQ(
+      "TypeError: Failed to execute 'runAdAuction' on 'Navigator': "
+      "directFromSellerSignals 'https://invalid^&' for AuctionAdConfig with "
+      "seller 'https://test.com' cannot be resolved to a valid URL.",
+      RunAuctionAndWait(R"({
+      seller: 'https://test.com',
+      decisionLogicUrl: 'https://test.com',
+      directFromSellerSignals: 'https://invalid^&'
+  })"));
+  WaitForAccessObserved({});
+}
+
+IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
+                       RunAdAuctionInvalidDirectFromSellerSignalsNotHttps) {
+  ASSERT_TRUE(NavigateToURL(shell(), https_server_->GetURL("a.test", "/echo")));
+
+  EXPECT_EQ(
+      "TypeError: Failed to execute 'runAdAuction' on 'Navigator': "
+      "directFromSellerSignals 'http://test.com/signals' for AuctionAdConfig "
+      "with seller 'https://test.com' must match seller origin; only https "
+      "scheme is supported.",
+      RunAuctionAndWait(R"({
+      seller: 'https://test.com',
+      decisionLogicUrl: 'https://test.com',
+      directFromSellerSignals: 'http://test.com/signals'
+  })"));
+  WaitForAccessObserved({});
+}
+
+IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
+                       RunAdAuctionInvalidDirectFromSellerSignalsWrongOrigin) {
+  ASSERT_TRUE(NavigateToURL(shell(), https_server_->GetURL("a.test", "/echo")));
+
+  EXPECT_EQ(
+      "TypeError: Failed to execute 'runAdAuction' on 'Navigator': "
+      "directFromSellerSignals 'https://test2.com/signals' for AuctionAdConfig "
+      "with seller 'https://test.com' must match seller origin; only https "
+      "scheme is supported.",
+      RunAuctionAndWait(R"({
+      seller: 'https://test.com',
+      decisionLogicUrl: 'https://test.com',
+      directFromSellerSignals: 'https://test2.com/signals'
+  })"));
+  WaitForAccessObserved({});
+}
+
+IN_PROC_BROWSER_TEST_F(
+    InterestGroupBrowserTest,
+    RunAdAuctionInvalidDirectFromSellerSignalsHasQueryString) {
+  ASSERT_TRUE(NavigateToURL(shell(), https_server_->GetURL("a.test", "/echo")));
+
+  EXPECT_EQ(
+      "TypeError: Failed to execute 'runAdAuction' on 'Navigator': "
+      "directFromSellerSignals 'https://test.com/signals?shouldntBeHere' for "
+      "AuctionAdConfig with seller 'https://test.com' URL prefix must not have "
+      "a query string.",
+      RunAuctionAndWait(R"({
+      seller: 'https://test.com',
+      decisionLogicUrl: 'https://test.com',
+      directFromSellerSignals: 'https://test.com/signals?shouldntBeHere'
+  })"));
+  WaitForAccessObserved({});
+}
+
+IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
                        RunAdAuctionBuyersNoInterestGroup) {
   GURL test_url = https_server_->GetURL("a.test", "/echo");
   ASSERT_TRUE(NavigateToURL(shell(), test_url));
diff --git a/content/browser/network_service_instance_impl.cc b/content/browser/network_service_instance_impl.cc
index d17c5e4c..a9488dab 100644
--- a/content/browser/network_service_instance_impl.cc
+++ b/content/browser/network_service_instance_impl.cc
@@ -179,7 +179,7 @@
       //
       // These cases are handled internally and so this case should never be
       // hit. It is undefined behavior to proceed in this case so CHECK here.
-      IMMEDIATE_CRASH();
+      base::ImmediateCrash();
     case SandboxGrantResult::kFailedToCreateDataDirectory:
       // A failure to create the `data_directory` is fatal, and the
       // `unsandboxed_data_path` should be used.
diff --git a/content/browser/preloading/prefetch/prefetch_container.cc b/content/browser/preloading/prefetch/prefetch_container.cc
index a1b529a..6ee7ba0 100644
--- a/content/browser/preloading/prefetch/prefetch_container.cc
+++ b/content/browser/preloading/prefetch/prefetch_container.cc
@@ -73,9 +73,9 @@
       prefetch_status_.value_or(PrefetchStatus::kPrefetchNotStarted)));
   builder.SetLinkClicked(navigated_to_);
 
-  if (data_length_) {
-    builder.SetDataLength(
-        ukm::GetExponentialBucketMinForBytes(data_length_.value()));
+  if (prefetch_response_sizes_) {
+    builder.SetDataLength(ukm::GetExponentialBucketMinForBytes(
+        prefetch_response_sizes_->encoded_data_length));
   }
 
   if (fetch_duration_) {
@@ -246,8 +246,13 @@
 void PrefetchContainer::UpdatePrefetchRequestMetrics(
     const absl::optional<network::URLLoaderCompletionStatus>& completion_status,
     const network::mojom::URLResponseHead* head) {
-  if (completion_status)
-    data_length_ = completion_status->encoded_data_length;
+  if (completion_status) {
+    prefetch_response_sizes_ = {
+        .encoded_data_length = completion_status->encoded_data_length,
+        .encoded_body_length = completion_status->encoded_body_length,
+        .decoded_body_length = completion_status->decoded_body_length,
+    };
+  }
 
   if (head)
     header_latency_ =
diff --git a/content/browser/preloading/prefetch/prefetch_container.h b/content/browser/preloading/prefetch/prefetch_container.h
index 49c56a8..d1f8dc4 100644
--- a/content/browser/preloading/prefetch/prefetch_container.h
+++ b/content/browser/preloading/prefetch/prefetch_container.h
@@ -36,6 +36,17 @@
 class PrefetchedMainframeResponseContainer;
 class ProxyLookupClientImpl;
 
+// Holds the relevant size information of the prefetched response. The struct is
+// installed onto `PrefetchContainer`, and gets passed into
+// `PrefetchFromStringURLLoader` to notify the associated `URLLoaderClient` of
+// the actual size of the response, as `PrefetchFromStringURLLoader` is not
+// aware of the prefetched request.
+struct PrefetchResponseSizes {
+  int64_t encoded_data_length;
+  int64_t encoded_body_length;
+  int64_t decoded_body_length;
+};
+
 // This class contains the state for a request to prefetch a specific URL.
 class CONTENT_EXPORT PrefetchContainer {
  public:
@@ -182,6 +193,11 @@
     return devtools_observer_;
   }
 
+  const absl::optional<PrefetchResponseSizes>& GetPrefetchResponseSizes()
+      const {
+    return prefetch_response_sizes_;
+  }
+
  protected:
   friend class PrefetchContainerTest;
 
@@ -247,8 +263,8 @@
 
   ukm::SourceId ukm_source_id_;
 
-  // The size of the prefetched response.
-  absl::optional<int> data_length_;
+  // The sizes information of the prefetched response.
+  absl::optional<PrefetchResponseSizes> prefetch_response_sizes_;
 
   // The amount  of time it took for the prefetch to complete.
   absl::optional<base::TimeDelta> fetch_duration_;
diff --git a/content/browser/preloading/prefetch/prefetch_from_string_url_loader.cc b/content/browser/preloading/prefetch/prefetch_from_string_url_loader.cc
index 9834fbc..2d19fadc 100644
--- a/content/browser/preloading/prefetch/prefetch_from_string_url_loader.cc
+++ b/content/browser/preloading/prefetch/prefetch_from_string_url_loader.cc
@@ -10,6 +10,7 @@
 #include "base/check_op.h"
 #include "base/memory/weak_ptr.h"
 #include "base/notreached.h"
+#include "content/browser/preloading/prefetch/prefetch_container.h"
 #include "content/browser/preloading/prefetch/prefetched_mainframe_response_container.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -26,11 +27,13 @@
 
 PrefetchFromStringURLLoader::PrefetchFromStringURLLoader(
     std::unique_ptr<PrefetchedMainframeResponseContainer> response,
+    const absl::optional<PrefetchResponseSizes>& response_sizes,
     const network::ResourceRequest& tentative_resource_request)
     : head_(response->ReleaseHead()),
       body_buffer_(
           base::MakeRefCounted<net::StringIOBuffer>(response->ReleaseBody())),
-      bytes_of_raw_data_to_transfer_(body_buffer_->size()) {}
+      bytes_of_raw_data_to_transfer_(body_buffer_->size()),
+      response_sizes_(response_sizes) {}
 
 PrefetchFromStringURLLoader::~PrefetchFromStringURLLoader() = default;
 
@@ -139,7 +142,13 @@
 }
 
 void PrefetchFromStringURLLoader::Finish(int error) {
-  client_->OnComplete(network::URLLoaderCompletionStatus(error));
+  network::URLLoaderCompletionStatus status(error);
+  if (response_sizes_) {
+    status.encoded_data_length = response_sizes_->encoded_data_length;
+    status.encoded_body_length = response_sizes_->encoded_body_length;
+    status.decoded_body_length = response_sizes_->decoded_body_length;
+  }
+  client_->OnComplete(status);
   handle_watcher_.reset();
   producer_handle_.reset();
   client_.reset();
diff --git a/content/browser/preloading/prefetch/prefetch_from_string_url_loader.h b/content/browser/preloading/prefetch/prefetch_from_string_url_loader.h
index f0fa6c6..83f3a97 100644
--- a/content/browser/preloading/prefetch/prefetch_from_string_url_loader.h
+++ b/content/browser/preloading/prefetch/prefetch_from_string_url_loader.h
@@ -28,11 +28,13 @@
 namespace content {
 
 class PrefetchedMainframeResponseContainer;
+struct PrefetchResponseSizes;
 
 class PrefetchFromStringURLLoader : public network::mojom::URLLoader {
  public:
   PrefetchFromStringURLLoader(
       std::unique_ptr<PrefetchedMainframeResponseContainer> prefetched_response,
+      const absl::optional<PrefetchResponseSizes>& response_sizes,
       const network::ResourceRequest& tenative_resource_request);
   ~PrefetchFromStringURLLoader() override;
 
@@ -99,9 +101,11 @@
   mojo::ScopedDataPipeProducerHandle producer_handle_;
   std::unique_ptr<mojo::SimpleWatcher> handle_watcher_;
 
+  const absl::optional<PrefetchResponseSizes>& response_sizes_;
+
   base::WeakPtrFactory<PrefetchFromStringURLLoader> weak_ptr_factory_{this};
 };
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_FROM_STRING_URL_LOADER_H_
\ No newline at end of file
+#endif  // CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_FROM_STRING_URL_LOADER_H_
diff --git a/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.cc b/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.cc
index c657f7a..a28042c 100644
--- a/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.cc
+++ b/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.cc
@@ -229,6 +229,7 @@
   std::unique_ptr<PrefetchFromStringURLLoader> url_loader =
       std::make_unique<PrefetchFromStringURLLoader>(
           prefetch_container->ReleasePrefetchedResponse(),
+          prefetch_container->GetPrefetchResponseSizes(),
           tenative_resource_request);
   scoped_refptr<SingleRequestURLLoaderFactory>
       single_request_url_loader_factory =
diff --git a/content/browser/preloading/prerender/prerender_browsertest.cc b/content/browser/preloading/prerender/prerender_browsertest.cc
index 2243df4..4e776be0 100644
--- a/content/browser/preloading/prerender/prerender_browsertest.cc
+++ b/content/browser/preloading/prerender/prerender_browsertest.cc
@@ -5803,7 +5803,7 @@
         GetPrerenderedMainFrameHost(host_id)->GetProcess();
     ScopedAllowRendererCrashes allow_renderer_crashes(process);
 #if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_X86_FAMILY)
-    // On x86 and x86_64 Android, IMMEDIATE_CRASH() macro used in
+    // On x86 and x86_64 Android, base::ImmediateCrash() macro used in
     // ChildProcessHostImpl::CrashHungProcess() called from ForceCrash()
     // does not seem to work as expected. (See https://crbug.com/1211655)
     // We have no other ForceCrash() call sites on other than Linux and CrOS.
@@ -5925,8 +5925,9 @@
     mojo::Remote<storage::mojom::TestApi> test_api;
     StoragePartitionImpl::GetStorageServiceForTesting()->BindTestApi(
         test_api.BindNewPipeAndPassReceiver().PassPipe());
-    // On x86 and x86_64 Android, IMMEDIATE_CRASH() macro used in CrashNow()
-    // does not seem to work as expected. (See https://crbug.com/1211655)
+    // On x86 and x86_64 Android, base::ImmediateCrash() macro used in
+    // CrashNow() does not seem to work as expected. (See
+    // https://crbug.com/1211655)
     test_api->CrashNow();
     loop.Run();
   }
diff --git a/content/browser/private_aggregation/private_aggregation_host.cc b/content/browser/private_aggregation/private_aggregation_host.cc
index 84bc62b..f25b92a 100644
--- a/content/browser/private_aggregation/private_aggregation_host.cc
+++ b/content/browser/private_aggregation/private_aggregation_host.cc
@@ -13,6 +13,7 @@
 #include "base/check.h"
 #include "base/command_line.h"
 #include "base/guid.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/rand_util.h"
 #include "base/ranges/algorithm.h"
 #include "base/time/time.h"
@@ -44,6 +45,13 @@
          base::RandDouble() * base::Minutes(50);
 }
 
+void RecordSendHistogramReportResultHistogram(
+    PrivateAggregationHost::SendHistogramReportResult result) {
+  base::UmaHistogramEnumeration(
+      "PrivacySandbox.PrivateAggregation.Host.SendHistogramReportResult",
+      result);
+}
+
 }  // namespace
 
 struct PrivateAggregationHost::ReceiverContext {
@@ -97,6 +105,8 @@
   if (!GetContentClient()->browser()->IsPrivateAggregationAllowed(
           &*browser_context_, receiver_set_.current_context().top_frame_origin,
           reporting_origin)) {
+    RecordSendHistogramReportResultHistogram(
+        SendHistogramReportResult::kApiDisabledInSettings);
     return;
   }
 
@@ -111,6 +121,8 @@
     // TODO(crbug.com/1323324): Add histograms for monitoring failures here,
     // possibly broken out by failure reason.
     mojo::ReportBadMessage("Too many contributions");
+    RecordSendHistogramReportResultHistogram(
+        SendHistogramReportResult::kTooManyContributions);
     return;
   }
 
@@ -150,6 +162,8 @@
   if (!debug_mode_details->debug_key.is_null()) {
     if (!debug_mode_details->is_enabled) {
       mojo::ReportBadMessage("Debug key present but debug mode is not enabled");
+      RecordSendHistogramReportResultHistogram(
+          SendHistogramReportResult::kDebugKeyPresentWithoutDebugMode);
       return;
     }
     debug_key = debug_mode_details->debug_key->value;
@@ -163,6 +177,8 @@
     // TODO(crbug.com/1323324): Add histograms for monitoring failures here,
     // possibly broken out by failure reason.
     mojo::ReportBadMessage("Invalid report request parameters");
+    RecordSendHistogramReportResultHistogram(
+        SendHistogramReportResult::kReportRequestCreationFailed);
     return;
   }
 
@@ -176,6 +192,7 @@
 
   on_report_request_received_.Run(std::move(report_request.value()),
                                   std::move(budget_key.value()));
+  RecordSendHistogramReportResultHistogram(SendHistogramReportResult::kSuccess);
 }
 
 }  // namespace content
diff --git a/content/browser/private_aggregation/private_aggregation_host.h b/content/browser/private_aggregation/private_aggregation_host.h
index 036ee18..a184b40 100644
--- a/content/browser/private_aggregation/private_aggregation_host.h
+++ b/content/browser/private_aggregation/private_aggregation_host.h
@@ -31,6 +31,17 @@
 class CONTENT_EXPORT PrivateAggregationHost
     : public mojom::PrivateAggregationHost {
  public:
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  enum class SendHistogramReportResult {
+    kSuccess = 0,
+    kApiDisabledInSettings = 1,
+    kTooManyContributions = 2,
+    kDebugKeyPresentWithoutDebugMode = 3,
+    kReportRequestCreationFailed = 4,
+    kMaxValue = kReportRequestCreationFailed,
+  };
+
   // Version string for the reports generated by this API.
   static constexpr char kApiReportVersion[] = "0.1";
 
diff --git a/content/browser/private_aggregation/private_aggregation_host_unittest.cc b/content/browser/private_aggregation/private_aggregation_host_unittest.cc
index 12f0d95..ddf2438 100644
--- a/content/browser/private_aggregation/private_aggregation_host_unittest.cc
+++ b/content/browser/private_aggregation/private_aggregation_host_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/command_line.h"
 #include "base/guid.h"
 #include "base/test/gmock_move_support.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
 #include "base/time/clock.h"
@@ -42,6 +43,9 @@
 using testing::Invoke;
 using testing::Property;
 
+constexpr char kSendHistogramReportResultHistogram[] =
+    "PrivacySandbox.PrivateAggregation.Host.SendHistogramReportResult";
+
 class PrivateAggregationHostTest : public testing::Test {
  public:
   PrivateAggregationHostTest() = default;
@@ -68,6 +72,8 @@
 
 TEST_F(PrivateAggregationHostTest,
        SendHistogramReport_ReportRequestHasCorrectMembers) {
+  base::HistogramTester histogram;
+
   const url::Origin kExampleOrigin =
       url::Origin::Create(GURL("https://example.com"));
   const url::Origin kMainFrameOrigin =
@@ -126,9 +132,15 @@
 
   EXPECT_TRUE(aggregation_service::ReportRequestsEqual(
       validated_request.value(), expected_request.value()));
+
+  histogram.ExpectUniqueSample(
+      kSendHistogramReportResultHistogram,
+      PrivateAggregationHost::SendHistogramReportResult::kSuccess, 1);
 }
 
 TEST_F(PrivateAggregationHostTest, ApiDiffers_RequestUpdatesCorrectly) {
+  base::HistogramTester histogram;
+
   const url::Origin kExampleOrigin =
       url::Origin::Create(GURL("https://example.com"));
   const url::Origin kMainFrameOrigin =
@@ -172,9 +184,15 @@
   EXPECT_EQ(validated_requests[0]->shared_info().api_identifier, "fledge");
   EXPECT_EQ(validated_requests[1]->shared_info().api_identifier,
             "shared-storage");
+
+  histogram.ExpectUniqueSample(
+      kSendHistogramReportResultHistogram,
+      PrivateAggregationHost::SendHistogramReportResult::kSuccess, 2);
 }
 
 TEST_F(PrivateAggregationHostTest, DebugModeDetails_ReflectedInReport) {
+  base::HistogramTester histogram;
+
   const url::Origin kExampleOrigin =
       url::Origin::Create(GURL("https://example.com"));
   const url::Origin kMainFrameOrigin =
@@ -182,8 +200,8 @@
 
   std::vector<mojom::DebugModeDetailsPtr> debug_mode_details_args;
   debug_mode_details_args.push_back(mojom::DebugModeDetails::New());
-  debug_mode_details_args.push_back(
-      mojom::DebugModeDetails::New(/*is_enabled=*/true, /*debug_key=*/nullptr));
+  debug_mode_details_args.push_back(mojom::DebugModeDetails::New(
+      /*is_enabled=*/true, /*debug_key=*/nullptr));
   debug_mode_details_args.push_back(mojom::DebugModeDetails::New(
       /*is_enabled=*/true,
       /*debug_key=*/mojom::DebugKey::New(/*value=*/1234u)));
@@ -227,10 +245,16 @@
   EXPECT_EQ(validated_requests[0]->debug_key(), absl::nullopt);
   EXPECT_EQ(validated_requests[1]->debug_key(), absl::nullopt);
   EXPECT_EQ(validated_requests[2]->debug_key(), 1234u);
+
+  histogram.ExpectUniqueSample(
+      kSendHistogramReportResultHistogram,
+      PrivateAggregationHost::SendHistogramReportResult::kSuccess, 3);
 }
 
 TEST_F(PrivateAggregationHostTest,
        MultipleReceievers_SendHistogramReportCallsRoutedCorrectly) {
+  base::HistogramTester histogram;
+
   const url::Origin kExampleOriginA =
       url::Origin::Create(GURL("https://a.example"));
   const url::Origin kExampleOriginB =
@@ -305,9 +329,15 @@
     remote.FlushForTesting();
     EXPECT_TRUE(remote.is_connected());
   }
+
+  histogram.ExpectUniqueSample(
+      kSendHistogramReportResultHistogram,
+      PrivateAggregationHost::SendHistogramReportResult::kSuccess, 2);
 }
 
 TEST_F(PrivateAggregationHostTest, BindUntrustworthyOriginReceiver_Fails) {
+  base::HistogramTester histogram;
+
   const url::Origin kInsecureOrigin =
       url::Origin::Create(GURL("http://example.com"));
   const url::Origin kOpaqueOrigin;
@@ -324,8 +354,8 @@
                                       PrivateAggregationBudgetKey::Api::kFledge,
                                       remote_2.BindNewPipeAndPassReceiver()));
 
-  // Attempt to send a message to an unconnected remote. The request should not
-  // be processed.
+  // Attempt to send a message to an unconnected remote. The request should
+  // not be processed.
   EXPECT_CALL(mock_callback_, Run(_, _)).Times(0);
   std::vector<mojom::AggregatableReportHistogramContributionPtr> contributions;
   contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
@@ -340,6 +370,8 @@
   remote_2.FlushForTesting();
   EXPECT_FALSE(remote_1.is_connected());
   EXPECT_FALSE(remote_2.is_connected());
+
+  histogram.ExpectTotalCount(kSendHistogramReportResultHistogram, 0);
 }
 
 TEST_F(PrivateAggregationHostTest, InvalidRequest_Rejected) {
@@ -376,21 +408,52 @@
           /*bucket=*/123, /*value=*/456));
 
   EXPECT_CALL(mock_callback_, Run(_, _)).Times(0);
-  remote->SendHistogramReport(std::move(negative_contributions),
-                              mojom::AggregationServiceMode::kDefault,
-                              mojom::DebugModeDetails::New());
-  remote->SendHistogramReport(std::move(too_many_contributions),
-                              mojom::AggregationServiceMode::kDefault,
-                              mojom::DebugModeDetails::New());
-  remote->SendHistogramReport(
-      std::move(valid_contributions), mojom::AggregationServiceMode::kDefault,
-      // Debug mode must be enabled for a debug key to be set.
-      mojom::DebugModeDetails::New(
-          /*is_enabled=*/false, /*debug_key=*/mojom::DebugKey::New(1234u)));
-  remote.FlushForTesting();
+
+  {
+    base::HistogramTester histogram;
+    remote->SendHistogramReport(std::move(negative_contributions),
+                                mojom::AggregationServiceMode::kDefault,
+                                mojom::DebugModeDetails::New());
+    remote.FlushForTesting();
+    histogram.ExpectUniqueSample(
+        kSendHistogramReportResultHistogram,
+        PrivateAggregationHost::SendHistogramReportResult::
+            kReportRequestCreationFailed,
+        1);
+  }
+  {
+    base::HistogramTester histogram;
+
+    remote->SendHistogramReport(std::move(too_many_contributions),
+                                mojom::AggregationServiceMode::kDefault,
+                                mojom::DebugModeDetails::New());
+    remote.FlushForTesting();
+    histogram.ExpectUniqueSample(
+        kSendHistogramReportResultHistogram,
+        PrivateAggregationHost::SendHistogramReportResult::
+            kTooManyContributions,
+        1);
+  }
+  {
+    base::HistogramTester histogram;
+
+    remote->SendHistogramReport(
+        std::move(valid_contributions), mojom::AggregationServiceMode::kDefault,
+        // Debug mode must be enabled for a debug key to be set.
+        mojom::DebugModeDetails::New(
+            /*is_enabled=*/false, /*debug_key=*/mojom::DebugKey::New(1234u)));
+    remote.FlushForTesting();
+    histogram.ExpectUniqueSample(
+        kSendHistogramReportResultHistogram,
+        PrivateAggregationHost::SendHistogramReportResult::
+            kDebugKeyPresentWithoutDebugMode,
+        1);
+  }
 }
 
 TEST_F(PrivateAggregationHostTest, PrivateAggregationAllowed_RequestSucceeds) {
+  base::HistogramTester histogram;
+
   MockPrivateAggregationContentBrowserClient browser_client;
   ScopedContentBrowserClientSetting setting(&browser_client);
 
@@ -419,9 +482,15 @@
 
   remote.FlushForTesting();
   EXPECT_TRUE(remote.is_connected());
+
+  histogram.ExpectUniqueSample(
+      kSendHistogramReportResultHistogram,
+      PrivateAggregationHost::SendHistogramReportResult::kSuccess, 1);
 }
 
 TEST_F(PrivateAggregationHostTest, PrivateAggregationDisallowed_RequestFails) {
+  base::HistogramTester histogram;
+
   MockPrivateAggregationContentBrowserClient browser_client;
   ScopedContentBrowserClientSetting setting(&browser_client);
 
@@ -450,6 +519,11 @@
 
   remote.FlushForTesting();
   EXPECT_TRUE(remote.is_connected());
+
+  histogram.ExpectUniqueSample(
+      kSendHistogramReportResultHistogram,
+      PrivateAggregationHost::SendHistogramReportResult::kApiDisabledInSettings,
+      1);
 }
 
 class PrivateAggregationHostDeveloperModeTest
diff --git a/content/browser/renderer_host/isolated_web_app_throttle_browsertest.cc b/content/browser/renderer_host/isolated_web_app_throttle_browsertest.cc
index ff4e8e1..72cbd5de 100644
--- a/content/browser/renderer_host/isolated_web_app_throttle_browsertest.cc
+++ b/content/browser/renderer_host/isolated_web_app_throttle_browsertest.cc
@@ -42,9 +42,11 @@
     app_origin_ = url::Origin::Create(app_url);
   }
 
-  bool ShouldUrlUseApplicationIsolationLevel(BrowserContext* browser_context,
-                                             const GURL& url) override {
-    return true;
+  bool ShouldUrlUseApplicationIsolationLevel(
+      BrowserContext* browser_context,
+      const GURL& url,
+      bool origin_matches_flag) override {
+    return origin_matches_flag;
   }
 
  private:
diff --git a/content/browser/renderer_host/isolated_web_app_throttle_unittest.cc b/content/browser/renderer_host/isolated_web_app_throttle_unittest.cc
index 25838f53..9c1aacee 100644
--- a/content/browser/renderer_host/isolated_web_app_throttle_unittest.cc
+++ b/content/browser/renderer_host/isolated_web_app_throttle_unittest.cc
@@ -41,9 +41,11 @@
 
 class IsolatedWebAppContentBrowserClient : public ContentBrowserClient {
  public:
-  bool ShouldUrlUseApplicationIsolationLevel(BrowserContext* browser_context,
-                                             const GURL& url) override {
-    return true;
+  bool ShouldUrlUseApplicationIsolationLevel(
+      BrowserContext* browser_context,
+      const GURL& url,
+      bool origin_matches_flag) override {
+    return origin_matches_flag;
   }
 
   bool HandleExternalProtocol(
diff --git a/content/browser/renderer_host/navigation_request_unittest.cc b/content/browser/renderer_host/navigation_request_unittest.cc
index 9caa0c0..84a3d69 100644
--- a/content/browser/renderer_host/navigation_request_unittest.cc
+++ b/content/browser/renderer_host/navigation_request_unittest.cc
@@ -84,9 +84,7 @@
     contents()->GetPrimaryMainFrame()->InitializeRenderFrameIfNeeded();
   }
 
-  void TearDown() override {
-    RenderViewHostImplTestHarness::TearDown();
-  }
+  void TearDown() override { RenderViewHostImplTestHarness::TearDown(); }
 
   void CancelDeferredNavigation(
       NavigationThrottle::ThrottleCheckResult result) {
@@ -772,9 +770,11 @@
     SetBrowserClientForTesting(old_client_);
   }
 
-  bool ShouldUrlUseApplicationIsolationLevel(BrowserContext* browser_context,
-                                             const GURL& url) override {
-    return true;
+  bool ShouldUrlUseApplicationIsolationLevel(
+      BrowserContext* browser_context,
+      const GURL& url,
+      bool origin_matches_flag) override {
+    return origin_matches_flag;
   }
 
  private:
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index 7ad248e..90369e1 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -5379,9 +5379,11 @@
  public:
   IsolatedWebAppContentBrowserClient() = default;
 
-  bool ShouldUrlUseApplicationIsolationLevel(BrowserContext* browser_context,
-                                             const GURL& url) override {
-    return true;
+  bool ShouldUrlUseApplicationIsolationLevel(
+      BrowserContext* browser_context,
+      const GURL& url,
+      bool origin_matches_flag) override {
+    return origin_matches_flag;
   }
 };
 
diff --git a/content/browser/site_isolation_policy_unittest.cc b/content/browser/site_isolation_policy_unittest.cc
index 56b88ef3..b5846f1 100644
--- a/content/browser/site_isolation_policy_unittest.cc
+++ b/content/browser/site_isolation_policy_unittest.cc
@@ -69,9 +69,11 @@
 
 class ApplicationIsolationEnablingBrowserClient : public ContentBrowserClient {
  public:
-  bool ShouldUrlUseApplicationIsolationLevel(BrowserContext* browser_context,
-                                             const GURL& url) override {
-    return true;
+  bool ShouldUrlUseApplicationIsolationLevel(
+      BrowserContext* browser_context,
+      const GURL& url,
+      bool origin_matches_flag) override {
+    return origin_matches_flag || url.SchemeIs("isolated-app");
   }
 };
 
@@ -155,4 +157,28 @@
       /*browser_context=*/nullptr, GURL("https://app.com:443")));
 }
 
+TEST_F(
+    SiteIsolationPolicyIsolatedApplicationTest,
+    ShouldSchemeUseApplicationIsolationLevelOverridesIsolatedAppOriginsFlag) {
+  base::CommandLine::ForCurrentProcess()->RemoveSwitch(
+      switches::kIsolatedAppOrigins);
+
+  // For the format of isolated app identifier see
+  // https://github.com/WICG/isolated-web-apps/blob/main/Scheme.md
+  EXPECT_TRUE(SiteIsolationPolicy::ShouldUrlUseApplicationIsolationLevel(
+      /*browser_context=*/nullptr,
+      GURL(
+          R"(isolated-app://aerugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic)")));
+}
+
+TEST_F(
+    SiteIsolationPolicyIsolatedApplicationTest,
+    ShouldSchemeUseApplicationIsolationLevelIsDisableForNonIsolatedAppScheme) {
+  base::CommandLine::ForCurrentProcess()->RemoveSwitch(
+      switches::kIsolatedAppOrigins);
+
+  EXPECT_FALSE(SiteIsolationPolicy::ShouldUrlUseApplicationIsolationLevel(
+      /*browser_context=*/nullptr, GURL("http://example.com")));
+}
+
 }  // namespace content
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index 4cf227d3..0fe5ba4 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -343,8 +343,7 @@
   // the function body unique by adding a log line, so it doesn't get merged
   // with other functions by link time optimizations (ICF).
   NOINLINE void CrashHungProcess() override {
-    LOG(ERROR) << "Crashing because hung";
-    IMMEDIATE_CRASH();
+    LOG(FATAL) << "Crashing because hung";
   }
 
   void RunServiceDeprecated(
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
index 06b3d93d..fee3d1f 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
@@ -1580,6 +1580,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "https://crbug.com/1181596")
     public void test_iframeAriaHidden() {
         performHtmlTest("iframe-aria-hidden.html");
     }
@@ -1599,6 +1600,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "https://crbug.com/1379850")
     public void test_iframeEmptyPositioned() {
         performHtmlTest("iframe-empty-positioned.html");
     }
diff --git a/content/public/browser/client_hints_controller_delegate.h b/content/public/browser/client_hints_controller_delegate.h
index 777b4fc3..5e1c77a8 100644
--- a/content/public/browser/client_hints_controller_delegate.h
+++ b/content/public/browser/client_hints_controller_delegate.h
@@ -47,9 +47,10 @@
   virtual bool IsJavaScriptAllowed(const GURL& url,
                                    RenderFrameHost* parent_rfh) = 0;
 
-  // Returns true iff cookies are blocked for the URL or third-party cookies
+  // Returns true iff cookies are blocked for the URL/RFH or third-party cookies
   // are disabled in the user agent.
-  virtual bool AreThirdPartyCookiesBlocked(const GURL& url) = 0;
+  virtual bool AreThirdPartyCookiesBlocked(const GURL& url,
+                                           RenderFrameHost* rfh) = 0;
 
   virtual blink::UserAgentMetadata GetUserAgentMetadata() = 0;
 
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index d42a393..2dc33cb 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -13,6 +13,7 @@
 #include "base/guid.h"
 #include "base/no_destructor.h"
 #include "base/notreached.h"
+#include "base/strings/string_piece.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/values.h"
@@ -324,7 +325,8 @@
 
 bool ContentBrowserClient::ShouldUrlUseApplicationIsolationLevel(
     BrowserContext* browser_context,
-    const GURL& url) {
+    const GURL& url,
+    bool origin_matches_flag) {
   return false;
 }
 
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index c50ff632..4c6f5c3c 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -627,10 +627,13 @@
 
   // Returns true if the given URL needs be loaded with the "isolated
   // application" isolation level. COOP/COEP headers must also be properly set
-  // in order to enable the application isolation level.
+  // in order to enable the application isolation level. `origin_matches_flag`
+  // specifies whether the URL's origin is allowed to use application isolation
+  // according to the content-level `kIsolatedAppOrigins` switch.
   virtual bool ShouldUrlUseApplicationIsolationLevel(
       BrowserContext* browser_context,
-      const GURL& url);
+      const GURL& url,
+      bool origin_matches_flag);
 
   // Allows the embedder to enable access to Isolated Context Web APIs for the
   // given |lock_url| -- the URL to which the renderer process is locked.
diff --git a/content/public/browser/site_isolation_policy.cc b/content/public/browser/site_isolation_policy.cc
index e6cd07c4..6ecb54a 100644
--- a/content/public/browser/site_isolation_policy.cc
+++ b/content/public/browser/site_isolation_policy.cc
@@ -337,9 +337,8 @@
   bool origin_matches_flag = g_disable_flag_caching_for_tests
                                  ? CreateIsolatedAppOriginSet().contains(origin)
                                  : GetIsolatedAppOriginSet().contains(origin);
-  return origin_matches_flag &&
-         GetContentClient()->browser()->ShouldUrlUseApplicationIsolationLevel(
-             browser_context, url);
+  return GetContentClient()->browser()->ShouldUrlUseApplicationIsolationLevel(
+      browser_context, url, origin_matches_flag);
 }
 
 // static
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 91dbf73..f49d248 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -1381,6 +1381,12 @@
              "RequestDesktopSiteExceptions",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Request Desktop Site zoom for Android. Apply a pre-defined page zoom level
+// when desktop user agent is used.
+BASE_FEATURE(kRequestDesktopSiteZoom,
+             "RequestDesktopSiteZoom",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Screen Capture API support for Android
 BASE_FEATURE(kUserMediaScreenCapturing,
              "UserMediaScreenCapturing",
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 6a9574aa..5d7a04a 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -291,6 +291,7 @@
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kOnDemandAccessibilityEvents);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kRequestDesktopSiteAdditions);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kRequestDesktopSiteExceptions);
+CONTENT_EXPORT BASE_DECLARE_FEATURE(kRequestDesktopSiteZoom);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kUserMediaScreenCapturing);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kWarmUpNetworkProcess);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebNfc);
diff --git a/content/public/test/audio_service_test_helper.cc b/content/public/test/audio_service_test_helper.cc
index abe4ee68..c55d7f84 100644
--- a/content/public/test/audio_service_test_helper.cc
+++ b/content/public/test/audio_service_test_helper.cc
@@ -43,21 +43,20 @@
 };
 
 AudioServiceTestHelper::AudioServiceTestHelper()
-    : testing_api_(new TestingApi) {
-  if (base::FeatureList::IsEnabled(features::kAudioServiceOutOfProcess)) {
-    audio::Service::SetTestingApiBinderForTesting(
-        base::BindRepeating(&AudioServiceTestHelper::BindTestingApiReceiver,
-                            base::Unretained(this)));
-  }
+    : testing_api_(std::make_unique<TestingApi>()) {
+  audio::Service::SetTestingApiBinderForTesting(base::BindRepeating(
+      &AudioServiceTestHelper::BindTestingApiReceiver, base::Unretained(this)));
 }
 
 AudioServiceTestHelper::~AudioServiceTestHelper() {
-  if (base::FeatureList::IsEnabled(features::kAudioServiceOutOfProcess))
-    audio::Service::SetTestingApiBinderForTesting(base::NullCallback());
+  audio::Service::SetTestingApiBinderForTesting(base::NullCallback());
 }
 
 void AudioServiceTestHelper::BindTestingApiReceiver(
     mojo::PendingReceiver<audio::mojom::TestingApi> receiver) {
+  CHECK(base::FeatureList::IsEnabled(features::kAudioServiceOutOfProcess))
+      << "Audio Service API binder shouldn't be invoked from this process when "
+         "the AudioServiceOutOfProcess feature is disabled.";
   testing_api_->BindReceiver(std::move(receiver));
 }
 
diff --git a/content/public/test/mock_client_hints_controller_delegate.h b/content/public/test/mock_client_hints_controller_delegate.h
index cb3fa1f4..d696677 100644
--- a/content/public/test/mock_client_hints_controller_delegate.h
+++ b/content/public/test/mock_client_hints_controller_delegate.h
@@ -38,7 +38,8 @@
   bool IsJavaScriptAllowed(const GURL& url,
                            content::RenderFrameHost* parent_rfh) override;
 
-  bool AreThirdPartyCookiesBlocked(const GURL& url) override;
+  bool AreThirdPartyCookiesBlocked(const GURL& url,
+                                   content::RenderFrameHost* rfh) override;
 
   blink::UserAgentMetadata GetUserAgentMetadata() override;
   void PersistClientHints(
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 4ac5126..ad22119f 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -373,11 +373,12 @@
 
 bool ShellContentBrowserClient::ShouldUrlUseApplicationIsolationLevel(
     BrowserContext* browser_context,
-    const GURL& url) {
-  // Enable application isolation level to allow restricted APIs in WPT.
-  // Note that this will not turn on application isolation for all URLs; the
-  // content layer will still run its own checks.
-  return true;
+    const GURL& url,
+    bool origin_matches_flag) {
+  // Enable application isolation level to allow restricted APIs in WPT.  Note
+  // that will only turn on application isolation for URLs which pass the
+  // content layer checks, as specified by `origin_matches_flag`.
+  return origin_matches_flag;
 }
 
 GeneratedCodeCacheSettings
diff --git a/content/shell/browser/shell_content_browser_client.h b/content/shell/browser/shell_content_browser_client.h
index 86be57a..5c0180e4 100644
--- a/content/shell/browser/shell_content_browser_client.h
+++ b/content/shell/browser/shell_content_browser_client.h
@@ -65,7 +65,8 @@
   std::unique_ptr<WebContentsViewDelegate> GetWebContentsViewDelegate(
       WebContents* web_contents) override;
   bool ShouldUrlUseApplicationIsolationLevel(BrowserContext* browser_context,
-                                             const GURL& url) override;
+                                             const GURL& url,
+                                             bool origin_matches_flag) override;
   GeneratedCodeCacheSettings GetGeneratedCodeCacheSettings(
       content::BrowserContext* context) override;
   base::OnceClosure SelectClientCertificate(
diff --git a/content/shell/utility/shell_content_utility_client.cc b/content/shell/utility/shell_content_utility_client.cc
index e01bd7e..997bdfa 100644
--- a/content/shell/utility/shell_content_utility_client.cc
+++ b/content/shell/utility/shell_content_utility_client.cc
@@ -63,7 +63,7 @@
   }
 
   void DoCrashImmediately(DoCrashImmediatelyCallback callback) override {
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   }
 
   void CreateFolder(CreateFolderCallback callback) override {
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 3e54149..7ec71fb 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2186,6 +2186,7 @@
     "../browser/browsing_topics/browsing_topics_site_data_manager_impl_unittest.cc",
     "../browser/browsing_topics/browsing_topics_site_data_storage_unittest.cc",
     "../browser/buckets/bucket_manager_host_unittest.cc",
+    "../browser/buckets/bucket_utils_unittest.cc",
     "../browser/byte_stream_unittest.cc",
     "../browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc",
     "../browser/cache_storage/cache_storage_cache_unittest.cc",
diff --git a/content/test/content_browser_test_test.cc b/content/test/content_browser_test_test.cc
index 64335ec..d3e395a 100644
--- a/content/test/content_browser_test_test.cc
+++ b/content/test/content_browser_test_test.cc
@@ -209,7 +209,7 @@
 }
 // Basic Test to crash
 IN_PROC_BROWSER_TEST_F(MockContentBrowserTest, DISABLED_CrashTest) {
-  IMMEDIATE_CRASH();
+  base::ImmediateCrash();
 }
 
 // This is disabled due to flakiness: https://crbug.com/1086372
diff --git a/content/test/data/accessibility/html/math-expected-android-external.txt b/content/test/data/accessibility/html/math-expected-android-external.txt
index d9d4e0f4..a97ae4e 100644
--- a/content/test/data/accessibility/html/math-expected-android-external.txt
+++ b/content/test/data/accessibility/html/math-expected-android-external.txt
@@ -1,13 +1,11 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
-++++View actions:[AX_FOCUS] bundle:[chromeRole="math", roleDescription="math"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="mathMLMath", roleDescription="math"]
 ++++++View actions:[AX_FOCUS] bundle:[chromeRole="mathMLRow"]
 ++++++++View actions:[AX_FOCUS] bundle:[chromeRole="mathMLSup"]
 ++++++++++View text:"𝐴" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLIdentifier"]
 ++++++++++View text:"2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLNumber"]
-++++++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
 ++++++++View text:"+" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLOperator"]
-++++++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
 ++++++++View actions:[AX_FOCUS] bundle:[chromeRole="mathMLSup"]
 ++++++++++View text:"𝐵" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLIdentifier"]
 ++++++++++View text:"2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLNumber"]
\ No newline at end of file
diff --git a/content/test/mock_client_hints_controller_delegate.cc b/content/test/mock_client_hints_controller_delegate.cc
index bb97b5e..487eeb8 100644
--- a/content/test/mock_client_hints_controller_delegate.cc
+++ b/content/test/mock_client_hints_controller_delegate.cc
@@ -58,7 +58,8 @@
 }
 
 bool MockClientHintsControllerDelegate::AreThirdPartyCookiesBlocked(
-    const GURL& url) {
+    const GURL& url,
+    content::RenderFrameHost* rfh) {
   return false;
 }
 
diff --git a/content/utility/speech/speech_recognition_sandbox_hook_linux.cc b/content/utility/speech/speech_recognition_sandbox_hook_linux.cc
index c8309229..b2f5292 100644
--- a/content/utility/speech/speech_recognition_sandbox_hook_linux.cc
+++ b/content/utility/speech/speech_recognition_sandbox_hook_linux.cc
@@ -38,7 +38,7 @@
         language_packs_dir.AsEndingWithSeparator().value()));
   }
 
-#if BUILDFLAG(ENABLE_SODA)
+#if BUILDFLAG(ENABLE_SODA_INTEGRATION_TESTS)
   auto test_resources_dir = GetSodaTestResourcesDirectory();
   if (!test_resources_dir.empty()) {
     permissions.push_back(BrokerFilePermission::ReadOnlyRecursive(
@@ -53,7 +53,7 @@
 
 bool SpeechRecognitionPreSandboxHook(
     sandbox::policy::SandboxLinux::Options options) {
-#if BUILDFLAG(ENABLE_SODA)
+#if BUILDFLAG(ENABLE_SODA_INTEGRATION_TESTS)
   base::FilePath test_binary_path = GetSodaTestBinaryPath();
   DVLOG(0) << "SODA test binary path: " << test_binary_path.value().c_str();
   DCHECK(base::PathExists(test_binary_path));
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index 001db460..f874b0a1 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -293,7 +293,7 @@
   ScopedExtensionPrefUpdate& operator=(const ScopedExtensionPrefUpdate&) =
       delete;
 
-  ~ScopedExtensionPrefUpdate() override {}
+  ~ScopedExtensionPrefUpdate() override = default;
 
   // ScopedDictionaryPrefUpdate overrides:
   std::unique_ptr<prefs::DictionaryValueUpdate> Get() override {
@@ -422,8 +422,8 @@
   if (!parent.IsParent(child))
     return child.AsUTF8Unsafe();
 
-  base::FilePath::StringType retval = child.value().substr(
-      parent.value().length());
+  base::FilePath::StringType retval =
+      child.value().substr(parent.value().length());
   if (base::FilePath::IsSeparator(retval[0]))
     retval = retval.substr(1);
 #if BUILDFLAG(IS_WIN)
@@ -463,17 +463,17 @@
   // Fix these paths.
   prefs::ScopedDictionaryPrefUpdate update(prefs_, pref_names::kExtensions);
   auto update_dict = update.Get();
-  for (auto i = absolute_keys.begin(); i != absolute_keys.end(); ++i) {
+  for (const std::string& key : absolute_keys) {
     std::unique_ptr<prefs::DictionaryValueUpdate> extension_dict;
-    if (!update_dict->GetDictionaryWithoutPathExpansion(*i, &extension_dict)) {
-      NOTREACHED() << "Control should never reach here for extension " << *i;
+    if (!update_dict->GetDictionaryWithoutPathExpansion(key, &extension_dict)) {
+      NOTREACHED() << "Control should never reach here for extension " << key;
       continue;
     }
     std::string path_string;
     extension_dict->GetString(kPrefPath, &path_string);
     base::FilePath path = base::FilePath::FromUTF8Unsafe(path_string);
     extension_dict->SetString(kPrefPath,
-        MakePathRelative(install_directory_, path));
+                              MakePathRelative(install_directory_, path));
   }
 }
 
@@ -852,7 +852,7 @@
     const T& permissions) {
   auto values = std::make_unique<base::ListValue>();
   for (typename T::const_iterator i = permissions.begin();
-      i != permissions.end(); ++i) {
+       i != permissions.end(); ++i) {
     std::unique_ptr<base::Value> detail(i->ToValue());
     if (detail) {
       base::Value::Dict tmp;
@@ -1296,7 +1296,7 @@
 }
 
 void ExtensionPrefs::SetExtensionRunning(const std::string& extension_id,
-    bool is_running) {
+                                         bool is_running) {
   UpdateExtensionPref(extension_id, kPrefRunning,
                       std::make_unique<base::Value>(is_running));
 }
@@ -1349,7 +1349,8 @@
 }
 
 bool ExtensionPrefs::DoesExtensionHaveState(
-    const std::string& id, Extension::State check_state) const {
+    const std::string& id,
+    Extension::State check_state) const {
   const base::DictionaryValue* extension = GetExtensionPref(id);
   if (!extension)
     return false;
@@ -1603,17 +1604,16 @@
   // should not be in the final extension prefs. All entries here should have
   // a corresponding Remove() call in FinishDelayedInstallInfo().
   if (extension->RequiresSortOrdinal()) {
-    extension_dict->SetString(
-        kPrefSuggestedPageOrdinal,
-        page_ordinal.IsValid() ? page_ordinal.ToInternalValue()
-                               : std::string());
+    extension_dict->SetString(kPrefSuggestedPageOrdinal,
+                              page_ordinal.IsValid()
+                                  ? page_ordinal.ToInternalValue()
+                                  : std::string());
   }
   extension_dict->SetInteger(kDelayedInstallReason,
                              static_cast<int>(delay_reason));
 }
 
-bool ExtensionPrefs::RemoveDelayedInstallInfo(
-    const std::string& extension_id) {
+bool ExtensionPrefs::RemoveDelayedInstallInfo(const std::string& extension_id) {
   if (!GetExtensionPref(extension_id))
     return false;
   ScopedExtensionPrefUpdate update(prefs_, extension_id);
@@ -1621,8 +1621,7 @@
   return result;
 }
 
-bool ExtensionPrefs::FinishDelayedInstallInfo(
-    const std::string& extension_id) {
+bool ExtensionPrefs::FinishDelayedInstallInfo(const std::string& extension_id) {
   CHECK(crx_file::id_util::IdIsValid(extension_id));
   ScopedExtensionPrefUpdate update(prefs_, extension_id);
   auto extension_dict = update.Get();
@@ -1660,8 +1659,7 @@
 
 std::unique_ptr<ExtensionInfo> ExtensionPrefs::GetDelayedInstallInfo(
     const std::string& extension_id) const {
-  const base::DictionaryValue* extension_prefs =
-      GetExtensionPref(extension_id);
+  const base::DictionaryValue* extension_prefs = GetExtensionPref(extension_id);
   if (!extension_prefs)
     return nullptr;
 
@@ -1675,8 +1673,7 @@
 
 ExtensionPrefs::DelayReason ExtensionPrefs::GetDelayedInstallReason(
     const std::string& extension_id) const {
-  const base::DictionaryValue* extension_prefs =
-      GetExtensionPref(extension_id);
+  const base::DictionaryValue* extension_prefs = GetExtensionPref(extension_id);
   if (!extension_prefs)
     return DELAY_REASON_NONE;
 
@@ -1709,8 +1706,7 @@
   return extensions_info;
 }
 
-bool ExtensionPrefs::IsFromWebStore(
-    const std::string& extension_id) const {
+bool ExtensionPrefs::IsFromWebStore(const std::string& extension_id) const {
   const base::DictionaryValue* dictionary = GetExtensionPref(extension_id);
   if (dictionary)
     return dictionary->FindBoolKey(kPrefFromWebStore).value_or(false);
@@ -1936,13 +1932,9 @@
 
 void ExtensionPrefs::GetExtensions(ExtensionIdList* out) const {
   CHECK(out);
-
-  std::unique_ptr<ExtensionsInfo> extensions_info(GetInstalledExtensionsInfo());
-
-  for (size_t i = 0; i < extensions_info->size(); ++i) {
-    ExtensionInfo* info = extensions_info->at(i).get();
-    out->push_back(info->extension_id);
-  }
+  base::ranges::transform(*GetInstalledExtensionsInfo(),
+                          std::back_inserter(*out),
+                          [](const auto& info) { return info->extension_id; });
 }
 
 void ExtensionPrefs::AddObserver(ExtensionPrefsObserver* observer) {
@@ -2003,8 +1995,7 @@
 
 bool ExtensionPrefs::HasIncognitoPrefValue(const std::string& pref_key) const {
   bool has_incognito_pref_value = false;
-  extension_pref_value_map_->GetEffectivePrefValue(pref_key,
-                                                   true,
+  extension_pref_value_map_->GetEffectivePrefValue(pref_key, true,
                                                    &has_incognito_pref_value);
   return has_incognito_pref_value;
 }
@@ -2028,17 +2019,13 @@
   UpdateExtensionPref(extension_id, kPrefGeometryCache, std::move(cache));
 }
 
-const base::DictionaryValue* ExtensionPrefs::GetInstallSignature() const {
-  // TODO (https://crbug.com/1342019) This should call
-  // `PrefService::GetDict`, which will in turn require the return type to
-  // be `base::Value::Dict`.
-  return &base::Value::AsDictionaryValue(prefs_->GetValue(kInstallSignature));
+const base::Value::Dict& ExtensionPrefs::GetInstallSignature() const {
+  return prefs_->GetDict(kInstallSignature);
 }
 
-void ExtensionPrefs::SetInstallSignature(
-    const base::DictionaryValue* signature) {
+void ExtensionPrefs::SetInstallSignature(base::Value::Dict* signature) {
   if (signature) {
-    prefs_->Set(kInstallSignature, *signature);
+    prefs_->Set(kInstallSignature, base::Value(std::move(*signature)));
     DVLOG(1) << "SetInstallSignature - saving";
   } else {
     DVLOG(1) << "SetInstallSignature - clearing";
@@ -2238,10 +2225,8 @@
   MakePathsRelative();
 
   // Ensure that any early observers are watching before prefs are initialized.
-  for (auto iter = early_observers.cbegin(); iter != early_observers.cend();
-       ++iter) {
-    (*iter)->OnExtensionPrefsAvailable(this);
-  }
+  for (auto* observer : early_observers)
+    observer->OnExtensionPrefsAvailable(this);
 
   InitPrefStore();
 
diff --git a/extensions/browser/extension_prefs.h b/extensions/browser/extension_prefs.h
index 312a1c5..792db91 100644
--- a/extensions/browser/extension_prefs.h
+++ b/extensions/browser/extension_prefs.h
@@ -658,14 +658,14 @@
   // Used by AppWindowGeometryCache to persist its cache. These methods
   // should not be called directly.
   const base::DictionaryValue* GetGeometryCache(
-        const std::string& extension_id) const;
+      const std::string& extension_id) const;
   void SetGeometryCache(const std::string& extension_id,
                         std::unique_ptr<base::DictionaryValue> cache);
 
   // Used for verification of installed extension ids. For the Set method, pass
   // null to remove the preference.
-  const base::DictionaryValue* GetInstallSignature() const;
-  void SetInstallSignature(const base::DictionaryValue* signature);
+  const base::Value::Dict& GetInstallSignature() const;
+  void SetInstallSignature(base::Value::Dict* signature);
 
   // The installation parameter associated with the extension.
   std::string GetInstallParam(const std::string& extension_id) const;
diff --git a/gin/shell/gin_main.cc b/gin/shell/gin_main.cc
index 6f05e21..f0779e6 100644
--- a/gin/shell/gin_main.cc
+++ b/gin/shell/gin_main.cc
@@ -72,12 +72,12 @@
   gin::V8Initializer::LoadV8Snapshot();
 #endif
 
-  base::SingleThreadTaskExecutor main_thread_task_executor;
-  base::ThreadPoolInstance::CreateAndStartWithDefaultParams("gin");
-
   // Initialize the base::FeatureList since IsolateHolder can depend on it.
   base::FeatureList::SetInstance(base::WrapUnique(new base::FeatureList));
 
+  base::SingleThreadTaskExecutor main_thread_task_executor;
+  base::ThreadPoolInstance::CreateAndStartWithDefaultParams("gin");
+
   {
     gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
                                    gin::ArrayBufferAllocator::SharedInstance());
diff --git a/google_apis/gaia/gaia_auth_fetcher.cc b/google_apis/gaia/gaia_auth_fetcher.cc
index ad12fd4..b795308 100644
--- a/google_apis/gaia/gaia_auth_fetcher.cc
+++ b/google_apis/gaia/gaia_auth_fetcher.cc
@@ -51,22 +51,20 @@
 
 std::unique_ptr<const GaiaAuthConsumer::ClientOAuthResult>
 ExtractOAuth2TokenPairResponse(const std::string& data) {
-  std::unique_ptr<base::Value> value = base::JSONReader::ReadDeprecated(data);
-  if (!value.get() || value->type() != base::Value::Type::DICTIONARY)
+  absl::optional<base::Value> value = base::JSONReader::Read(data);
+  if (!value || !value->is_dict())
     return nullptr;
+  base::Value::Dict& dict = value->GetDict();
 
-  base::DictionaryValue* dict =
-        static_cast<base::DictionaryValue*>(value.get());
-
-  std::string* refresh_token = dict->FindStringKey("refresh_token");
-  std::string* access_token = dict->FindStringKey("access_token");
-  absl::optional<int> expires_in_secs = dict->FindIntKey("expires_in");
+  std::string* refresh_token = dict.FindString("refresh_token");
+  std::string* access_token = dict.FindString("access_token");
+  absl::optional<int> expires_in_secs = dict.FindInt("expires_in");
   if (!refresh_token || !access_token || !expires_in_secs.has_value())
     return nullptr;
 
   // Extract ID token when obtaining refresh token. Do not fail if absent,
   // but log to keep track.
-  std::string* id_token = dict->FindStringKey("id_token");
+  std::string* id_token = dict.FindString("id_token");
   if (!id_token)
     LOG(ERROR) << "Missing ID token on refresh token fetch response.";
   gaia::TokenServiceFlags service_flags =
@@ -88,13 +86,12 @@
   if (response_code == net::HTTP_INTERNAL_SERVER_ERROR)
     return GaiaAuthConsumer::TokenRevocationStatus::kServerError;
 
-  std::unique_ptr<base::Value> value = base::JSONReader::ReadDeprecated(data);
-  if (!value.get() || value->type() != base::Value::Type::DICTIONARY)
+  absl::optional<base::Value> value = base::JSONReader::Read(data);
+  if (!value || !value->is_dict())
     return GaiaAuthConsumer::TokenRevocationStatus::kUnknownError;
+  base::Value::Dict& dict = value->GetDict();
 
-  base::DictionaryValue* dict =
-      static_cast<base::DictionaryValue*>(value.get());
-  std::string* error = dict->FindStringKey("error");
+  std::string* error = dict.FindString("error");
   if (!error)
     return GaiaAuthConsumer::TokenRevocationStatus::kUnknownError;
 
@@ -106,12 +103,11 @@
   return GaiaAuthConsumer::TokenRevocationStatus::kUnknownError;
 }
 
-std::unique_ptr<base::DictionaryValue> ParseJSONDict(const std::string& data) {
-  std::unique_ptr<base::DictionaryValue> response_dict;
+base::Value::Dict ParseJSONDict(const std::string& data) {
+  base::Value::Dict response_dict;
   absl::optional<base::Value> message_value = base::JSONReader::Read(data);
   if (message_value && message_value->is_dict()) {
-    response_dict = std::make_unique<base::DictionaryValue>();
-    response_dict->MergeDictionary(base::OptionalToPtr(message_value));
+    response_dict.Merge(std::move(message_value->GetDict()));
   }
   return response_dict;
 }
@@ -195,8 +191,7 @@
 const char GaiaAuthFetcher::kOAuth2CodeToTokenPairDeviceIdParam[] =
     "device_id=%s&device_type=chrome";
 // static
-const char GaiaAuthFetcher::kOAuth2RevokeTokenBodyFormat[] =
-    "token=%s";
+const char GaiaAuthFetcher::kOAuth2RevokeTokenBodyFormat[] = "token=%s";
 // static
 const char GaiaAuthFetcher::kMergeSessionFormat[] =
     "?uberauth=%s&"
@@ -239,7 +234,7 @@
               source_)),
       reauth_api_url_(GaiaUrls::GetInstance()->reauth_api_url()) {}
 
-GaiaAuthFetcher::~GaiaAuthFetcher() {}
+GaiaAuthFetcher::~GaiaAuthFetcher() = default;
 
 bool GaiaAuthFetcher::HasPendingFetch() {
   return fetch_pending_;
@@ -359,10 +354,9 @@
   std::string encoded_continue_url =
       base::EscapeUrlEncodedData(continue_url, true);
   std::string encoded_source = base::EscapeUrlEncodedData(source, true);
-  std::string result = base::StringPrintf(kMergeSessionFormat,
-                                          encoded_auth_token.c_str(),
-                                          encoded_continue_url.c_str(),
-                                          encoded_source.c_str());
+  std::string result =
+      base::StringPrintf(kMergeSessionFormat, encoded_auth_token.c_str(),
+                         encoded_continue_url.c_str(), encoded_source.c_str());
   if (!external_cc_result.empty()) {
     base::StringAppendF(
         &result, "&externalCcResult=%s",
@@ -523,7 +517,7 @@
   DCHECK(!fetch_pending_) << "Tried to fetch two things at once!";
 
   VLOG(1) << "Starting StartTokenFetchForUberAuthExchange with access_token="
-           << access_token;
+          << access_token;
   std::string authentication_header =
       base::StringPrintf(kOAuthHeaderFormat, access_token.c_str());
   net::NetworkTrafficAnnotationTag traffic_annotation =
@@ -701,9 +695,9 @@
     const std::string& parent_obfuscated_gaia_id,
     const std::string& parent_credential) {
   // Create the post body.
-  base::DictionaryValue post_body_value;
-  post_body_value.SetString("credentialType", "password");
-  post_body_value.SetString("credential", parent_credential);
+  base::Value::Dict post_body_value;
+  post_body_value.Set("credentialType", "password");
+  post_body_value.Set("credential", parent_credential);
   std::string post_body;
   bool write_success = base::JSONWriter::Write(post_body_value, &post_body);
   DCHECK(write_success);
@@ -822,8 +816,7 @@
   }
   if (error == kServiceUnavailableShortError ||
       error == kServiceUnavailableError) {
-    return GoogleServiceAuthError(
-        GoogleServiceAuthError::SERVICE_UNAVAILABLE);
+    return GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE);
   }
 
   DLOG(WARNING) << "Incomprehensible response from Google Accounts servers.";
@@ -900,12 +893,11 @@
                                              net::Error net_error,
                                              int response_code) {
   if (net_error == net::OK) {
-    std::unique_ptr<base::DictionaryValue> response_dict = ParseJSONDict(data);
+    base::Value::Dict response_dict = ParseJSONDict(data);
 
     if (response_code == net::HTTP_OK) {
-      std::string rapt_token;
-      response_dict->GetString("encodedRapt", &rapt_token);
-      if (rapt_token.empty()) {
+      std::string* rapt_token = response_dict.FindString("encodedRapt");
+      if (!rapt_token) {
         // This should not happen unless there is a bug on the server,
         // since if we get HTTP_OK response, we should get a RAPT token.
         DLOG(ERROR) << "Got HTTP-OK ReauthAPI response with empty RAPT token";
@@ -913,13 +905,14 @@
             GaiaAuthConsumer::ReAuthProofTokenStatus::kUnknownError);
         return;
       }
-      consumer_->OnReAuthProofTokenSuccess(rapt_token);
+      consumer_->OnReAuthProofTokenSuccess(*rapt_token);
     } else {
-      const std::string error_message =
-          response_dict->FindPath({"error", "message"})->GetString();
+      const std::string* error_message =
+          response_dict.FindStringByDottedPath("error.message");
+      CHECK(error_message);
 
       consumer_->OnReAuthProofTokenFailure(
-          ErrorMessageToReAuthProofTokenStatus(error_message));
+          ErrorMessageToReAuthProofTokenStatus(*error_message));
     }
   } else {
     consumer_->OnReAuthProofTokenFailure(
@@ -981,10 +974,9 @@
   OnURLLoadCompleteInternal(net_error, response_code, data);
 }
 
-void GaiaAuthFetcher::OnURLLoadCompleteInternal(
-    net::Error net_error,
-    int response_code,
-    std::string data) {
+void GaiaAuthFetcher::OnURLLoadCompleteInternal(net::Error net_error,
+                                                int response_code,
+                                                std::string data) {
   fetch_pending_ = false;
 
   // Some of the GAIA requests perform redirects, which results in the final URL
@@ -995,11 +987,10 @@
   DispatchFetchedRequest(url, data, net_error, response_code);
 }
 
-void GaiaAuthFetcher::DispatchFetchedRequest(
-    const GURL& url,
-    const std::string& data,
-    net::Error net_error,
-    int response_code) {
+void GaiaAuthFetcher::DispatchFetchedRequest(const GURL& url,
+                                             const std::string& data,
+                                             net::Error net_error,
+                                             int response_code) {
   if (url == oauth2_token_gurl_) {
     OnOAuth2TokenPairFetched(data, net_error, response_code);
   } else if (base::StartsWith(url.spec(), merge_session_gurl_.spec(),
diff --git a/google_apis/google_api_keys.cc b/google_apis/google_api_keys.cc
index ca4572f..dd27a1b 100644
--- a/google_apis/google_api_keys.cc
+++ b/google_apis/google_api_keys.cc
@@ -78,17 +78,16 @@
 #define GOOGLE_API_KEY_REMOTING DUMMY_API_TOKEN
 #endif
 
+// API key for the Speech On-Device API (SODA).
+#if !defined(GOOGLE_API_KEY_SODA)
+#define GOOGLE_API_KEY_SODA DUMMY_API_TOKEN
+#endif
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // API key for the Nearby Sharing Service.
 #if !defined(GOOGLE_API_KEY_SHARING)
 #define GOOGLE_API_KEY_SHARING DUMMY_API_TOKEN
 #endif
-#endif
-
-// API key for the Speech On-Device API (SODA).
-#if !defined(GOOGLE_API_KEY_SODA)
-#define GOOGLE_API_KEY_SODA DUMMY_API_TOKEN
-#endif
 
 // API key for the ReadAloud API.
 #if !defined(GOOGLE_API_KEY_READ_ALOUD)
@@ -99,6 +98,7 @@
 #if !defined(GOOGLE_API_KEY_FRESNEL)
 #define GOOGLE_API_KEY_FRESNEL DUMMY_API_TOKEN
 #endif
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 // These are used as shortcuts for developers and users providing
 // OAuth credentials via preprocessor defines or environment
@@ -144,15 +144,14 @@
         STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_REMOTING), nullptr, std::string(),
         environment.get(), command_line, gaia_config);
 
+    api_key_soda_ = CalculateKeyValue(
+        GOOGLE_API_KEY_SODA, STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_SODA),
+        nullptr, std::string(), environment.get(), command_line, gaia_config);
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     api_key_sharing_ = CalculateKeyValue(
         GOOGLE_API_KEY_SHARING, STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_SHARING),
         nullptr, std::string(), environment.get(), command_line, gaia_config);
-#endif
-
-    api_key_soda_ = CalculateKeyValue(
-        GOOGLE_API_KEY_SODA, STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_SODA),
-        nullptr, std::string(), environment.get(), command_line, gaia_config);
 
     api_key_read_aloud_ = CalculateKeyValue(
         GOOGLE_API_KEY_READ_ALOUD,
@@ -162,6 +161,7 @@
     api_key_fresnel_ = CalculateKeyValue(
         GOOGLE_API_KEY_FRESNEL, STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_FRESNEL),
         nullptr, std::string(), environment.get(), command_line, gaia_config);
+#endif
 
     metrics_key_ = CalculateKeyValue(
         GOOGLE_METRICS_SIGNING_KEY,
@@ -218,12 +218,12 @@
 #endif
   std::string api_key_non_stable() const { return api_key_non_stable_; }
   std::string api_key_remoting() const { return api_key_remoting_; }
+  std::string api_key_soda() const { return api_key_soda_; }
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   std::string api_key_sharing() const { return api_key_sharing_; }
-#endif
-  std::string api_key_soda() const { return api_key_soda_; }
   std::string api_key_read_aloud() const { return api_key_read_aloud_; }
   std::string api_key_fresnel() const { return api_key_fresnel_; }
+#endif
 
   std::string metrics_key() const { return metrics_key_; }
 
@@ -327,12 +327,12 @@
   std::string api_key_;
   std::string api_key_non_stable_;
   std::string api_key_remoting_;
+  std::string api_key_soda_;
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   std::string api_key_sharing_;
-#endif
-  std::string api_key_soda_;
   std::string api_key_read_aloud_;
   std::string api_key_fresnel_;
+#endif
   std::string metrics_key_;
   std::string client_ids_[CLIENT_NUM_ITEMS];
   std::string client_secrets_[CLIENT_NUM_ITEMS];
@@ -357,15 +357,14 @@
   return g_api_key_cache.Get().api_key_remoting();
 }
 
+std::string GetSodaAPIKey() {
+  return g_api_key_cache.Get().api_key_soda();
+}
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 std::string GetSharingAPIKey() {
   return g_api_key_cache.Get().api_key_sharing();
 }
-#endif
-
-std::string GetSodaAPIKey() {
-  return g_api_key_cache.Get().api_key_soda();
-}
 
 std::string GetReadAloudAPIKey() {
   return g_api_key_cache.Get().api_key_read_aloud();
@@ -374,6 +373,7 @@
 std::string GetFresnelAPIKey() {
   return g_api_key_cache.Get().api_key_fresnel();
 }
+#endif
 
 #if BUILDFLAG(SUPPORT_EXTERNAL_GOOGLE_API_KEY)
 void SetAPIKey(const std::string& api_key) {
diff --git a/google_apis/google_api_keys.h b/google_apis/google_api_keys.h
index 77aae51..4275578d 100644
--- a/google_apis/google_api_keys.h
+++ b/google_apis/google_api_keys.h
@@ -79,19 +79,19 @@
 // Retrieves the Chrome Remote Desktop API key.
 std::string GetRemotingAPIKey();
 
+// Retrieves the Speech On-Device API (SODA) API Key.
+std::string GetSodaAPIKey();
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // Retrieves the Sharing API Key.
 std::string GetSharingAPIKey();
-#endif
-
-// Retrieves the Speech On-Device API (SODA) API Key.
-std::string GetSodaAPIKey();
 
 // Retrieves the ReadAloud API Key.
 std::string GetReadAloudAPIKey();
 
 // Retrieves the Fresnel API Key.
 std::string GetFresnelAPIKey();
+#endif
 
 #if BUILDFLAG(SUPPORT_EXTERNAL_GOOGLE_API_KEY)
 // Sets the API key. This should be called as early as possible before this
diff --git a/gpu/command_buffer/common/gpu_memory_buffer_support.cc b/gpu/command_buffer/common/gpu_memory_buffer_support.cc
index be165b55..a298f524 100644
--- a/gpu/command_buffer/common/gpu_memory_buffer_support.cc
+++ b/gpu/command_buffer/common/gpu_memory_buffer_support.cc
@@ -204,7 +204,7 @@
 GPU_EXPORT bool NativeBufferNeedsPlatformSpecificTextureTarget(
     gfx::BufferFormat format,
     gfx::BufferPlane plane) {
-#if defined(USE_OZONE) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
+#if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
     BUILDFLAG(IS_WIN)
   // Always use GL_TEXTURE_2D as the target for RGB textures.
   // https://crbug.com/916728
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index c26ea5e..187c57a 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -3174,7 +3174,7 @@
   gfx::BufferFormat buffer_format = gfx::BufferFormat::RGBA_8888;
   if (format == GL_RGB) {
     buffer_format = gfx::BufferFormat::RGBX_8888;
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
     // BGRX format is preferred for Ozone as it matches the format used by the
     // buffer queue and is as a result guaranteed to work on all devices.
     // TODO(reveman): Define this format in one place instead of having to
diff --git a/gpu/command_buffer/service/shared_image/gl_image_backing.cc b/gpu/command_buffer/service/shared_image/gl_image_backing.cc
index ca3609a7..b7f473e 100644
--- a/gpu/command_buffer/service/shared_image/gl_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/gl_image_backing.cc
@@ -520,12 +520,12 @@
 std::unique_ptr<OverlayImageRepresentation> GLImageBacking::ProduceOverlay(
     SharedImageManager* manager,
     MemoryTypeTracker* tracker) {
-#if defined(USE_OZONE) || BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_WIN)
   return std::make_unique<OverlayGLImageRepresentation>(manager, this, tracker,
                                                         image_);
-#else   // || defined(USE_OZONE) || BUILDFLAG(IS_WIN))
+#else   // || BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_WIN))
   return SharedImageBacking::ProduceOverlay(manager, tracker);
-#endif  // defined(USE_OZONE) || BUILDFLAG(IS_WIN)
+#endif  // BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_WIN)
 }
 
 std::unique_ptr<DawnImageRepresentation> GLImageBacking::ProduceDawn(
diff --git a/gpu/command_buffer/service/shared_image/gl_texture_image_backing.cc b/gpu/command_buffer/service/shared_image/gl_texture_image_backing.cc
index cc86c89..d57c4c8 100644
--- a/gpu/command_buffer/service/shared_image/gl_texture_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/gl_texture_image_backing.cc
@@ -346,7 +346,7 @@
 }
 
 void GLTextureImageBacking::CreateEGLImage() {
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || defined(USE_OZONE)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_OZONE)
   SharedContextState* shared_context_state = factory()->GetSharedContextState();
   ui::ScopedMakeCurrent smc(shared_context_state->context(),
                             shared_context_state->surface());
diff --git a/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory.cc
index 9fbcb87..035c2fd 100644
--- a/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory.cc
+++ b/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory.cc
@@ -7,6 +7,7 @@
 #include <list>
 #include <utility>
 
+#include "build/build_config.h"
 #include "components/viz/common/resources/resource_sizes.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
@@ -164,7 +165,7 @@
   // Linux and ChromeOS support WebGPU/Compat on GL. All other platforms
   // do not support WebGPU on GL.
   if (usage & SHARED_IMAGE_USAGE_WEBGPU) {
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || defined(USE_OZONE)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_OZONE)
     if (use_webgpu_adapter_ != WebGPUAdapterName::kCompat) {
       return false;
     }
diff --git a/gpu/command_buffer/service/shared_image/ozone_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/ozone_image_backing_factory.cc
index 69657437..8089095 100644
--- a/gpu/command_buffer/service/shared_image/ozone_image_backing_factory.cc
+++ b/gpu/command_buffer/service/shared_image/ozone_image_backing_factory.cc
@@ -224,7 +224,7 @@
   // OzoneImageBacking should be used for all scanout buffers.
   constexpr uint32_t kPrimaryPlaneUsageFlags =
       SHARED_IMAGE_USAGE_DISPLAY_READ | SHARED_IMAGE_USAGE_DISPLAY_WRITE |
-      SHARED_IMAGE_USAGE_SCANOUT | SHARED_IMAGE_USAGE_RASTER;
+      SHARED_IMAGE_USAGE_SCANOUT;
   if (usage != kPrimaryPlaneUsageFlags || gmb_type != gfx::EMPTY_BUFFER) {
     return false;
   }
diff --git a/gpu/command_buffer/service/shared_image/shared_image_factory.cc b/gpu/command_buffer/service/shared_image/shared_image_factory.cc
index 6e11209..fbeb85e 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_factory.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_factory.cc
@@ -43,7 +43,7 @@
 #include "gpu/vulkan/vulkan_device_queue.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "gpu/command_buffer/service/shared_image/ozone_image_backing_factory.h"
 #include "ui/ozone/public/gl_ozone.h"
 #include "ui/ozone/public/ozone_platform.h"
@@ -247,7 +247,7 @@
     factories_.push_back(std::move(external_vk_image_factory));
   }
 #endif  // BUILDFLAG(ENABLE_VULKAN)
-#elif defined(USE_OZONE)
+#elif BUILDFLAG(IS_OZONE)
   // For all Ozone platforms - Desktop Linux, ChromeOS, Fuchsia, CastOS.
   if (ui::OzonePlatform::GetInstance()
           ->GetPlatformRuntimeProperties()
@@ -266,7 +266,7 @@
 #endif  // BUILDFLAG(IS_FUCHSIA)
   }
 #endif  // BUILDFLAG(ENABLE_VULKAN)
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
 
 #if BUILDFLAG(IS_MAC)
   auto iosurface_backing_factory =
diff --git a/gpu/command_buffer/service/shared_image/shared_image_representation.cc b/gpu/command_buffer/service/shared_image/shared_image_representation.cc
index 2dc87c1..2f79dcc2 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_representation.cc
@@ -317,7 +317,7 @@
   NOTREACHED();
   return nullptr;
 }
-#elif defined(USE_OZONE)
+#elif BUILDFLAG(IS_OZONE)
 scoped_refptr<gfx::NativePixmap> OverlayImageRepresentation::GetNativePixmap() {
   return backing()->GetNativePixmap();
 }
diff --git a/gpu/command_buffer/service/shared_image/shared_image_representation.h b/gpu/command_buffer/service/shared_image/shared_image_representation.h
index c792828..1ceff00 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_representation.h
+++ b/gpu/command_buffer/service/shared_image/shared_image_representation.h
@@ -502,7 +502,7 @@
     GetAHardwareBufferFenceSync() {
       return representation()->GetAHardwareBufferFenceSync();
     }
-#elif defined(USE_OZONE)
+#elif BUILDFLAG(IS_OZONE)
     scoped_refptr<gfx::NativePixmap> GetNativePixmap() {
       return representation()->GetNativePixmap();
     }
@@ -557,7 +557,7 @@
   virtual AHardwareBuffer* GetAHardwareBuffer();
   virtual std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
   GetAHardwareBufferFenceSync();
-#elif defined(USE_OZONE)
+#elif BUILDFLAG(IS_OZONE)
   scoped_refptr<gfx::NativePixmap> GetNativePixmap();
 #elif BUILDFLAG(IS_WIN)
   virtual scoped_refptr<gl::DCOMPSurfaceProxy> GetDCOMPSurfaceProxy();
diff --git a/gpu/command_buffer/tests/command_buffer_gles2_tests_main.cc b/gpu/command_buffer/tests/command_buffer_gles2_tests_main.cc
index 0ee6ae4..48cbf43 100644
--- a/gpu/command_buffer/tests/command_buffer_gles2_tests_main.cc
+++ b/gpu/command_buffer/tests/command_buffer_gles2_tests_main.cc
@@ -21,7 +21,7 @@
 
 int RunHelper(base::TestSuite* testSuite) {
   base::MessagePumpType pump_type = base::MessagePumpType::IO;
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   pump_type = base::MessagePumpType::UI;
 #endif
   base::SingleThreadTaskExecutor executor(pump_type);
diff --git a/gpu/command_buffer/tests/egl_test.cc b/gpu/command_buffer/tests/egl_test.cc
index f124f02..60a905f9 100644
--- a/gpu/command_buffer/tests/egl_test.cc
+++ b/gpu/command_buffer/tests/egl_test.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
+#include "build/build_config.h"
 #include "gpu/gles2_conform_support/egl/test_support.h"
 
 // This file tests EGL basic interface for command_buffer_gles2, the mode of
@@ -36,7 +37,7 @@
   EGLDisplay display2 = eglGetDisplay(EGL_DEFAULT_DISPLAY);
   EXPECT_EQ(display1, display2);
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   EGLNativeDisplayType invalid_display_type =
       static_cast<EGLNativeDisplayType>(0x1);
 #else
diff --git a/gpu/command_buffer/tests/fuzzer_main.cc b/gpu/command_buffer/tests/fuzzer_main.cc
index 55ab8d96..74d6e55 100644
--- a/gpu/command_buffer/tests/fuzzer_main.cc
+++ b/gpu/command_buffer/tests/fuzzer_main.cc
@@ -50,7 +50,7 @@
 #include "ui/gl/init/gl_factory.h"
 #include "ui/gl/test/gl_surface_test_support.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
@@ -325,7 +325,7 @@
     [[maybe_unused]] auto* command_line =
         base::CommandLine::ForCurrentProcess();
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
     ui::OzonePlatform::InitializeForGPU(ui::OzonePlatform::InitParams());
 #endif
 
diff --git a/gpu/command_buffer/tests/gl_test_setup_helper.cc b/gpu/command_buffer/tests/gl_test_setup_helper.cc
index 0fa50424..4b21799 100644
--- a/gpu/command_buffer/tests/gl_test_setup_helper.cc
+++ b/gpu/command_buffer/tests/gl_test_setup_helper.cc
@@ -10,7 +10,7 @@
 #include "gpu/command_buffer/tests/gl_test_utils.h"
 #include "ui/gl/init/gl_factory.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
@@ -33,7 +33,7 @@
   task_environment_ = std::make_unique<base::test::TaskEnvironment>(
       base::test::TaskEnvironment::MainThreadType::UI);
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   // Make Ozone run in single-process mode.
   ui::OzonePlatform::InitParams params;
   params.single_process = true;
@@ -42,7 +42,7 @@
   // initialized the UI thread.
   ui::OzonePlatform::InitializeForUI(params);
   ui::OzonePlatform::InitializeForGPU(params);
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
 
   display_ = gpu::GLTestHelper::InitializeGLDefault();
   ::gles2::Initialize();
diff --git a/gpu/command_buffer/tests/lpm/gl_lpm_fuzzer.cc b/gpu/command_buffer/tests/lpm/gl_lpm_fuzzer.cc
index 82aaa833..09edf46b 100644
--- a/gpu/command_buffer/tests/lpm/gl_lpm_fuzzer.cc
+++ b/gpu/command_buffer/tests/lpm/gl_lpm_fuzzer.cc
@@ -21,6 +21,7 @@
 #include "base/message_loop/message_pump_type.h"
 #include "base/strings/string_split.h"
 #include "base/task/single_thread_task_executor.h"
+#include "build/build_config.h"
 #include "gpu/command_buffer/client/gles2_lib.h"
 #include "gpu/command_buffer/tests/gl_manager.h"
 #include "gpu/command_buffer/tests/gl_test_utils.h"
@@ -33,7 +34,7 @@
 #include "ui/gl/gl_version_info.h"
 #include "ui/gl/init/gl_factory.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
@@ -53,7 +54,7 @@
                                     gl::kANGLEImplementationNullName);
     base::FeatureList::InitializeInstance(std::string(), std::string());
     base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO);
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
     ui::OzonePlatform::InitializeForGPU(ui::OzonePlatform::InitParams());
 #endif
     gpu::GLTestHelper::InitializeGLDefault();
diff --git a/gpu/config/gpu_info_collector.cc b/gpu/config/gpu_info_collector.cc
index cf9a5d3..040328e 100644
--- a/gpu/config/gpu_info_collector.cc
+++ b/gpu/config/gpu_info_collector.cc
@@ -45,7 +45,7 @@
 #include "base/mac/foundation_util.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"           // nogncheck
 #include "ui/ozone/public/platform_gl_egl_utility.h"  // nogncheck
 #endif
@@ -586,7 +586,7 @@
     }
   }
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   if (const auto* const egl_utility =
           ui::OzonePlatform::GetInstance()->GetPlatformGLEGLUtility()) {
     egl_utility->CollectGpuExtraInfo(prefs.enable_native_gpu_memory_buffers,
diff --git a/gpu/config/gpu_preferences.h b/gpu/config/gpu_preferences.h
index c24a247..6483e339 100644
--- a/gpu/config/gpu_preferences.h
+++ b/gpu/config/gpu_preferences.h
@@ -15,7 +15,7 @@
 #include "media/media_buildflags.h"
 #include "ui/gfx/buffer_types.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "base/message_loop/message_pump_type.h"
 #endif
 
@@ -271,7 +271,7 @@
   // only enabled on Windows platform for the info collection GPU process.
   bool enable_perf_data_collection = false;
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   // Determines message pump type for the GPU thread.
   base::MessagePumpType message_pump_type = base::MessagePumpType::DEFAULT;
 #endif
diff --git a/gpu/config/gpu_preferences_unittest.cc b/gpu/config/gpu_preferences_unittest.cc
index d9e10b7..14310f1 100644
--- a/gpu/config/gpu_preferences_unittest.cc
+++ b/gpu/config/gpu_preferences_unittest.cc
@@ -89,7 +89,7 @@
             right.enable_gpu_blocked_time_metric);
   EXPECT_EQ(left.enable_perf_data_collection,
             right.enable_perf_data_collection);
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   EXPECT_EQ(left.message_pump_type, right.message_pump_type);
 #endif
   EXPECT_EQ(left.enable_native_gpu_memory_buffers,
@@ -180,7 +180,7 @@
                                mojom::DawnBackendValidationLevel::kPartial)
     GPU_PREFERENCES_FIELD(enable_gpu_blocked_time_metric, true)
     GPU_PREFERENCES_FIELD(enable_perf_data_collection, true)
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
     GPU_PREFERENCES_FIELD_ENUM(message_pump_type, base::MessagePumpType::UI,
                                base::MessagePumpType::UI)
 #endif
@@ -273,7 +273,7 @@
   PRINT_INT(enable_dawn_backend_validation);
   PRINT_BOOL(enable_gpu_blocked_time_metric);
   PRINT_BOOL(enable_perf_data_collection);
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   PRINT_INT(message_pump_type);
 #endif
   PRINT_BOOL(enable_native_gpu_memory_buffers);
diff --git a/gpu/gles2_conform_support/egl/thread_state.cc b/gpu/gles2_conform_support/egl/thread_state.cc
index bbbe284a..d9f19f19 100644
--- a/gpu/gles2_conform_support/egl/thread_state.cc
+++ b/gpu/gles2_conform_support/egl/thread_state.cc
@@ -27,7 +27,7 @@
 #include "ui/gl/gl_switches.h"
 #include "ui/gl/init/gl_factory.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
@@ -86,7 +86,7 @@
       // Need to call both Init and InitFromArgv, since Windows does not use
       // argc, argv in CommandLine::Init(argc, argv).
       command_line->InitFromArgv(argv);
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
       ui::OzonePlatform::InitializeForGPU(ui::OzonePlatform::InitParams());
 #endif
       gl::GLDisplay* display =
diff --git a/gpu/ipc/host/gpu_memory_buffer_support.cc b/gpu/ipc/host/gpu_memory_buffer_support.cc
index 1dc8fcf..e263a607 100644
--- a/gpu/ipc/host/gpu_memory_buffer_support.cc
+++ b/gpu/ipc/host/gpu_memory_buffer_support.cc
@@ -16,7 +16,7 @@
     GpuMemoryBufferSupport* support) {
   GpuMemoryBufferConfigurationSet configurations;
 
-#if defined(USE_OZONE) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \
+#if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \
     BUILDFLAG(IS_ANDROID)
   const gfx::BufferFormat kBufferFormats[] = {
       gfx::BufferFormat::R_8,
@@ -57,7 +57,7 @@
         configurations.insert(gfx::BufferUsageAndFormat(usage, format));
     }
   }
-#endif  // defined(USE_OZONE) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) ||
+#endif  // BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) ||
         // BUILDFLAG(IS_ANDROID)
 
   return configurations;
@@ -67,7 +67,7 @@
                                                 gfx::BufferUsage usage) {
   if (!NativeBufferNeedsPlatformSpecificTextureTarget(format))
     return false;
-#if defined(USE_OZONE) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \
+#if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \
     BUILDFLAG(IS_ANDROID)
   GpuMemoryBufferSupport support;
   GpuMemoryBufferConfigurationSet native_configurations =
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index 323ca49..55289de 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -49,7 +49,7 @@
 #include <GLES2/gl2.h>
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/ozone/public/surface_factory_ozone.h"
 #endif
@@ -400,7 +400,7 @@
 
   base::ElapsedTimer elapsed_timer;
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   // Initialize Ozone GPU after the watchdog in case it hangs. The sandbox
   // may also have started at this point.
   ui::OzonePlatform::InitParams params;
@@ -426,7 +426,7 @@
       ui::OzonePlatform::GetInstance()
           ->GetSurfaceFactoryOzone()
           ->GetSupportedFormatsForTexturing();
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
 
   if (!gl_use_swiftshader_) {
     gl_use_swiftshader_ = EnableSwiftShaderIfNeeded(
@@ -761,7 +761,7 @@
                         gpu_info_.sandboxed);
 
   init_successful_ = true;
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   ui::OzonePlatform::GetInstance()->AfterSandboxEntry();
   gpu_feature_info_.supported_buffer_formats_for_allocation_and_texturing =
       std::move(supported_buffer_formats_for_texturing);
@@ -818,7 +818,7 @@
                                   const GpuPreferences& gpu_preferences) {
   gpu_preferences_ = gpu_preferences;
   init_successful_ = true;
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   ui::OzonePlatform::InitParams params;
   params.single_process = true;
 
@@ -961,7 +961,7 @@
     AdjustInfoToSwiftShader();
   }
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   const std::vector<gfx::BufferFormat> supported_buffer_formats_for_texturing =
       ui::OzonePlatform::GetInstance()
           ->GetSurfaceFactoryOzone()
diff --git a/gpu/ipc/service/gpu_memory_buffer_factory_test_template.h b/gpu/ipc/service/gpu_memory_buffer_factory_test_template.h
index 7a30e31..f6cd7097 100644
--- a/gpu/ipc/service/gpu_memory_buffer_factory_test_template.h
+++ b/gpu/ipc/service/gpu_memory_buffer_factory_test_template.h
@@ -17,7 +17,7 @@
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gl/gl_display.h"
 
-#if BUILDFLAG(IS_WIN) || defined(USE_OZONE)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_OZONE)
 #include "base/command_line.h"
 #include "ui/gl/gl_switches.h"
 #include "ui/gl/init/gl_factory.h"
@@ -29,7 +29,7 @@
 template <typename GpuMemoryBufferFactoryType>
 class GpuMemoryBufferFactoryTest : public testing::Test {
  public:
-#if BUILDFLAG(IS_WIN) || defined(USE_OZONE)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_OZONE)
   // Overridden from testing::Test:
   void SetUp() override {
 #if BUILDFLAG(IS_WIN)
@@ -40,7 +40,7 @@
     display_ = gl::GLSurfaceTestSupport::InitializeOneOff();
   }
   void TearDown() override { gl::GLSurfaceTestSupport::ShutdownGL(display_); }
-#endif  // BUILDFLAG(IS_WIN) || defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_OZONE)
 
  protected:
   base::test::TaskEnvironment task_environment_{
diff --git a/gpu/ipc/service/image_transport_surface_linux.cc b/gpu/ipc/service/image_transport_surface_linux.cc
index 7ff285f..2e07507c 100644
--- a/gpu/ipc/service/image_transport_surface_linux.cc
+++ b/gpu/ipc/service/image_transport_surface_linux.cc
@@ -4,6 +4,7 @@
 
 #include "gpu/ipc/service/image_transport_surface.h"
 
+#include "build/build_config.h"
 #include "gpu/ipc/service/pass_through_image_transport_surface.h"
 #include "ui/gl/init/gl_factory.h"
 
@@ -18,7 +19,7 @@
   DCHECK_NE(surface_handle, kNullSurfaceHandle);
   scoped_refptr<gl::GLSurface> surface;
   bool override_vsync_for_multi_window_swap = false;
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   surface = gl::init::CreateSurfacelessViewGLSurface(display, surface_handle);
 #endif
   if (!surface) {
diff --git a/gpu/perftests/run_all_tests.cc b/gpu/perftests/run_all_tests.cc
index 1883fb01..54ca574 100644
--- a/gpu/perftests/run_all_tests.cc
+++ b/gpu/perftests/run_all_tests.cc
@@ -12,14 +12,14 @@
 #include "build/build_config.h"
 #include "ui/gl/init/gl_factory.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
 static int RunHelper(base::TestSuite* test_suite) {
   base::FeatureList::InitializeInstance(std::string(), std::string());
   std::unique_ptr<base::SingleThreadTaskExecutor> executor;
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   executor = std::make_unique<base::SingleThreadTaskExecutor>(
       base::MessagePumpType::UI);
   ui::OzonePlatform::InitParams params;
diff --git a/gpu/vulkan/init/vulkan_factory.cc b/gpu/vulkan/init/vulkan_factory.cc
index 3e37eacd..827d7f8 100644
--- a/gpu/vulkan/init/vulkan_factory.cc
+++ b/gpu/vulkan/init/vulkan_factory.cc
@@ -15,7 +15,7 @@
 #include "gpu/vulkan/win32/vulkan_implementation_win32.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/ozone/public/surface_factory_ozone.h"
 #endif
@@ -29,7 +29,7 @@
 std::unique_ptr<VulkanImplementation> CreateVulkanImplementation(
     bool use_swiftshader,
     bool allow_protected_memory) {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   return ui::OzonePlatform::GetInstance()
       ->GetSurfaceFactoryOzone()
       ->CreateVulkanImplementation(use_swiftshader, allow_protected_memory);
diff --git a/gpu/vulkan/tests/basic_vulkan_test.cc b/gpu/vulkan/tests/basic_vulkan_test.cc
index 56dce50b..1656446 100644
--- a/gpu/vulkan/tests/basic_vulkan_test.cc
+++ b/gpu/vulkan/tests/basic_vulkan_test.cc
@@ -6,12 +6,13 @@
 
 #include "base/command_line.h"
 #include "base/strings/string_piece_forward.h"
+#include "build/build_config.h"
 #include "gpu/vulkan/init/vulkan_factory.h"
 #include "gpu/vulkan/tests/native_window.h"
 #include "gpu/vulkan/vulkan_surface.h"
 #include "ui/gfx/geometry/rect.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
@@ -23,7 +24,7 @@
 
 void BasicVulkanTest::SetUp() {
   bool supports_swapchain = true;
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   supports_swapchain = ui::OzonePlatform::GetInstance()
                            ->GetPlatformProperties()
                            .supports_vulkan_swap_chain;
diff --git a/gpu/vulkan/tests/native_window.cc b/gpu/vulkan/tests/native_window.cc
index 9e6b654..2013a098 100644
--- a/gpu/vulkan/tests/native_window.cc
+++ b/gpu/vulkan/tests/native_window.cc
@@ -5,10 +5,11 @@
 #include "gpu/vulkan/tests/native_window.h"
 
 #include "base/containers/flat_map.h"
+#include "build/build_config.h"
 #include "ui/platform_window/platform_window_delegate.h"
 #include "ui/platform_window/platform_window_init_properties.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
@@ -23,7 +24,7 @@
   void Initialize(const gfx::Rect& bounds) {
     DCHECK(!platform_window_);
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
     ui::PlatformWindowInitProperties props(bounds);
     platform_window_ = ui::OzonePlatform::GetInstance()->CreatePlatformWindow(
         this, std::move(props));
diff --git a/gpu/vulkan/tests/vulkan_tests_main.cc b/gpu/vulkan/tests/vulkan_tests_main.cc
index 317e141..c11946d 100644
--- a/gpu/vulkan/tests/vulkan_tests_main.cc
+++ b/gpu/vulkan/tests/vulkan_tests_main.cc
@@ -7,10 +7,11 @@
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_suite.h"
+#include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/events/platform/platform_event_source.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
@@ -28,7 +29,7 @@
         base::test::TaskEnvironment::MainThreadType::UI);
     platform_event_source_ = ui::PlatformEventSource::CreateDefault();
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
     // Make Ozone run in single-process mode.
     ui::OzonePlatform::InitParams params;
     params.single_process = true;
diff --git a/headless/test/DEPS b/headless/test/DEPS
index a0437553..d6a5726 100644
--- a/headless/test/DEPS
+++ b/headless/test/DEPS
@@ -7,6 +7,7 @@
   "+printing",
   "+third_party/blink/public",
   "+third_party/crashpad/crashpad/client",
+  "+third_party/inspector_protocol/crdtp",
   "+third_party/skia/include",
   "+components/devtools/simple_devtools_protocol_client",
   "+components/policy",
diff --git a/headless/test/headless_browser_browsertest.cc b/headless/test/headless_browser_browsertest.cc
index 38f1eb219..ed83d8a 100644
--- a/headless/test/headless_browser_browsertest.cc
+++ b/headless/test/headless_browser_browsertest.cc
@@ -18,6 +18,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "components/devtools/simple_devtools_protocol_client/simple_devtools_protocol_client.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/permission_controller_delegate.h"
@@ -32,13 +33,7 @@
 #include "headless/lib/browser/headless_browser_impl.h"
 #include "headless/lib/browser/headless_select_file_dialog_factory.h"
 #include "headless/lib/browser/headless_web_contents_impl.h"
-#include "headless/public/devtools/domains/inspector.h"
-#include "headless/public/devtools/domains/network.h"
-#include "headless/public/devtools/domains/page.h"
-#include "headless/public/devtools/domains/tracing.h"
 #include "headless/public/headless_browser.h"
-#include "headless/public/headless_devtools_client.h"
-#include "headless/public/headless_devtools_target.h"
 #include "headless/public/headless_web_contents.h"
 #include "headless/test/headless_browser_test.h"
 #include "headless/test/headless_browser_test_utils.h"
@@ -64,6 +59,8 @@
 #include "third_party/crashpad/crashpad/client/crash_report_database.h"  // nogncheck
 #endif
 
+using simple_devtools_protocol_client::SimpleDevToolsProtocolClient;
+
 using testing::UnorderedElementsAre;
 
 namespace headless {
@@ -270,11 +267,11 @@
   HeadlessWebContents* web_contents =
       browser_context->CreateWebContentsBuilder().Build();
 
-  EXPECT_TRUE(ResultBool(
+  EXPECT_THAT(
       EvaluateScript(web_contents,
                      "(document.createElement('canvas').getContext('webgl')"
                      "    instanceof WebGLRenderingContext)"),
-      "result.value"));
+      DictHasValue("result.result.value", true));
 }
 
 IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, ClipboardCopyPasteText) {
@@ -310,64 +307,21 @@
 #if !BUILDFLAG(IS_MAC)
   // On Mac headless does not override the screen dimensions, so they are
   // left with the actual screen values.
-  EXPECT_EQ(
-      kDefaultOptions.window_size.width(),
-      ResultInt(EvaluateScript(web_contents, "screen.width"), "result.value"));
-  EXPECT_EQ(
-      kDefaultOptions.window_size.height(),
-      ResultInt(EvaluateScript(web_contents, "screen.height"), "result.value"));
+  EXPECT_THAT(
+      EvaluateScript(web_contents, "screen.width"),
+      DictHasValue("result.result.value", kDefaultOptions.window_size.width()));
+  EXPECT_THAT(EvaluateScript(web_contents, "screen.height"),
+              DictHasValue("result.result.value",
+                           kDefaultOptions.window_size.height()));
 #endif  // !BUILDFLAG(IS_MAC)
-  EXPECT_EQ(kDefaultOptions.window_size.width(),
-            ResultInt(EvaluateScript(web_contents, "window.innerWidth"),
-                      "result.value"));
-  EXPECT_EQ(kDefaultOptions.window_size.height(),
-            ResultInt(EvaluateScript(web_contents, "window.innerHeight"),
-                      "result.value"));
+  EXPECT_THAT(
+      EvaluateScript(web_contents, "window.innerWidth"),
+      DictHasValue("result.result.value", kDefaultOptions.window_size.width()));
+  EXPECT_THAT(EvaluateScript(web_contents, "window.innerHeight"),
+              DictHasValue("result.result.value",
+                           kDefaultOptions.window_size.height()));
 }
 
-namespace {
-
-class CookieSetter {
- public:
-  CookieSetter(HeadlessBrowserTest* browser_test,
-               HeadlessWebContents* web_contents,
-               std::unique_ptr<network::SetCookieParams> set_cookie_params)
-      : browser_test_(browser_test),
-        web_contents_(web_contents),
-        devtools_client_(HeadlessDevToolsClient::Create()) {
-    web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
-    devtools_client_->GetNetwork()->GetExperimental()->SetCookie(
-        std::move(set_cookie_params),
-        base::BindOnce(&CookieSetter::OnSetCookieResult,
-                       base::Unretained(this)));
-  }
-
-  CookieSetter(const CookieSetter&) = delete;
-  CookieSetter& operator=(const CookieSetter&) = delete;
-
-  ~CookieSetter() {
-    web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get());
-  }
-
-  void OnSetCookieResult(std::unique_ptr<network::SetCookieResult> result) {
-    result_ = std::move(result);
-    browser_test_->FinishAsynchronousTest();
-  }
-
-  std::unique_ptr<network::SetCookieResult> TakeResult() {
-    return std::move(result_);
-  }
-
- private:
-  raw_ptr<HeadlessBrowserTest, DanglingUntriaged> browser_test_;  // Not owned.
-  raw_ptr<HeadlessWebContents, DanglingUntriaged> web_contents_;  // Not owned.
-  std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
-
-  std::unique_ptr<network::SetCookieResult> result_;
-};
-
-}  // namespace
-
 // TODO(skyostil): This test currently relies on being able to run a shell
 // script.
 #if BUILDFLAG(IS_POSIX)
@@ -433,8 +387,7 @@
 #endif  // BUILDFLAG(IS_POSIX)
 
 class CrashReporterTest : public HeadlessBrowserTest,
-                          public HeadlessWebContents::Observer,
-                          inspector::ExperimentalObserver {
+                          public HeadlessWebContents::Observer {
  public:
   CrashReporterTest() {}
   ~CrashReporterTest() override = default;
@@ -454,21 +407,21 @@
 
   // HeadlessWebContents::Observer implementation:
   void DevToolsTargetReady() override {
-    EXPECT_TRUE(web_contents_->GetDevToolsTarget());
-    devtools_client_ = HeadlessDevToolsClient::Create();
-    web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
-    devtools_client_->GetInspector()->GetExperimental()->AddObserver(this);
+    devtools_client_.AttachToWebContents(
+        HeadlessWebContentsImpl::From(web_contents_)->web_contents());
+
+    devtools_client_.AddEventHandler(
+        "Inspector.targetCrashed",
+        base::BindRepeating(&CrashReporterTest::OnTargetCrashed,
+                            base::Unretained(this)));
   }
 
-  // inspector::ExperimentalObserver implementation:
-  void OnTargetCrashed(const inspector::TargetCrashedParams&) override {
-    FinishAsynchronousTest();
-  }
+  void OnTargetCrashed(const base::Value::Dict&) { FinishAsynchronousTest(); }
 
  protected:
   raw_ptr<HeadlessBrowserContext, DanglingUntriaged> browser_context_ = nullptr;
   raw_ptr<HeadlessWebContents, DanglingUntriaged> web_contents_ = nullptr;
-  std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
+  SimpleDevToolsProtocolClient devtools_client_;
   base::FilePath crash_dumps_dir_;
 };
 
@@ -537,84 +490,67 @@
                 blink::PermissionType::NOTIFICATIONS, url, url));
 }
 
-namespace {
-
-class TraceHelper : public tracing::ExperimentalObserver {
+class BrowserTargetTracingTest : public HeadlessBrowserTest {
  public:
-  TraceHelper(HeadlessBrowserTest* browser_test, HeadlessDevToolsTarget* target)
-      : browser_test_(browser_test),
-        target_(target),
-        client_(HeadlessDevToolsClient::Create()),
-        tracing_data_(std::make_unique<base::ListValue>()) {
-    EXPECT_FALSE(target_->IsAttached());
-    target_->AttachClient(client_.get());
-    EXPECT_TRUE(target_->IsAttached());
+  BrowserTargetTracingTest() = default;
 
-    client_->GetTracing()->GetExperimental()->AddObserver(this);
+ protected:
+  void RunTest() {
+    browser_devtools_client_.AttachToBrowser();
 
-    client_->GetTracing()->GetExperimental()->Start(
-        tracing::StartParams::Builder().Build(),
-        base::BindOnce(&TraceHelper::OnTracingStarted, base::Unretained(this)));
-  }
+    browser_devtools_client_.AddEventHandler(
+        "Tracing.dataCollected",
+        base::BindRepeating(&BrowserTargetTracingTest::OnDataCollected,
+                            base::Unretained(this)));
 
-  TraceHelper(const TraceHelper&) = delete;
-  TraceHelper& operator=(const TraceHelper&) = delete;
+    browser_devtools_client_.AddEventHandler(
+        "Tracing.tracingComplete",
+        base::BindRepeating(&BrowserTargetTracingTest::OnTracingComplete,
+                            base::Unretained(this)));
 
-  ~TraceHelper() override {
-    target_->DetachClient(client_.get());
-    EXPECT_FALSE(target_->IsAttached());
-  }
+    browser_devtools_client_.SendCommand(
+        "Tracing.start",
+        base::BindOnce(&BrowserTargetTracingTest::OnTracingStarted,
+                       base::Unretained(this)));
 
-  std::unique_ptr<base::ListValue> TakeTracingData() {
-    return std::move(tracing_data_);
+    RunAsynchronousTest();
+
+    browser_devtools_client_.DetachClient();
   }
 
  private:
-  void OnTracingStarted(std::unique_ptr<tracing::StartResult>) {
-    // We don't need the callback from End, but the OnTracingComplete event.
-    client_->GetTracing()->GetExperimental()->End(
-        tracing::EndParams::Builder().Build());
+  void OnTracingStarted(base::Value::Dict) {
+    browser_devtools_client_.SendCommand("Tracing.end");
   }
 
-  // tracing::ExperimentalObserver implementation:
-  void OnDataCollected(const tracing::DataCollectedParams& params) override {
-    for (const auto& value : *params.GetValue()) {
-      tracing_data_->Append(value->Clone());
+  void OnDataCollected(const base::Value::Dict& params) {
+    const base::Value::List* value_list =
+        params.FindListByDottedPath("params.value");
+    ASSERT_NE(value_list, nullptr);
+    for (const auto& value : *value_list) {
+      tracing_data_.Append(value.Clone());
     }
   }
 
-  void OnTracingComplete(
-      const tracing::TracingCompleteParams& params) override {
-    browser_test_->FinishAsynchronousTest();
+  void OnTracingComplete(const base::Value::Dict&) {
+    EXPECT_LT(0u, tracing_data_.size());
+
+    FinishAsynchronousTest();
   }
 
-  raw_ptr<HeadlessBrowserTest> browser_test_;  // Not owned.
-  raw_ptr<HeadlessDevToolsTarget> target_;     // Not owned.
-  std::unique_ptr<HeadlessDevToolsClient> client_;
+  SimpleDevToolsProtocolClient browser_devtools_client_;
 
-  std::unique_ptr<base::ListValue> tracing_data_;
+  base::Value::List tracing_data_;
 };
 
-}  // namespace
-
 // Flaky, http://crbug.com/1269261.
 #if BUILDFLAG(IS_WIN)
-#define MAYBE_TraceUsingBrowserDevToolsTarget \
-  DISABLED_TraceUsingBrowserDevToolsTarget
+#define MAYBE_BrowserTargetTracing DISABLED_BrowserTargetTracing
 #else
-#define MAYBE_TraceUsingBrowserDevToolsTarget TraceUsingBrowserDevToolsTarget
+#define MAYBE_BrowserTargetTracing BrowserTargetTracing
 #endif  // BUILDFLAG(IS_WIN)
-IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest,
-                       MAYBE_TraceUsingBrowserDevToolsTarget) {
-  HeadlessDevToolsTarget* target = browser()->GetDevToolsTarget();
-  EXPECT_NE(nullptr, target);
-
-  TraceHelper helper(this, target);
-  RunAsynchronousTest();
-
-  std::unique_ptr<base::ListValue> tracing_data = helper.TakeTracingData();
-  EXPECT_TRUE(tracing_data);
-  EXPECT_LT(0u, tracing_data->GetListDeprecated().size());
+IN_PROC_BROWSER_TEST_F(BrowserTargetTracingTest, MAYBE_BrowserTargetTracing) {
+  RunTest();
 }
 
 IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, WindowPrint) {
@@ -628,8 +564,8 @@
           .SetInitialURL(embedded_test_server()->GetURL("/hello.html"))
           .Build();
   EXPECT_TRUE(WaitForLoad(web_contents));
-  EXPECT_FALSE(ResultHas(EvaluateScript(web_contents, "window.print()"),
-                         "exceptionDetails"));
+  EXPECT_THAT(EvaluateScript(web_contents, "window.print()"),
+              Not(DictHasKey("exceptionDetails")));
 }
 
 class HeadlessBrowserAllowInsecureLocalhostTest : public HeadlessBrowserTest {
diff --git a/headless/test/headless_browser_context_browsertest.cc b/headless/test/headless_browser_context_browsertest.cc
index 1276d06..ad4e031 100644
--- a/headless/test/headless_browser_context_browsertest.cc
+++ b/headless/test/headless_browser_context_browsertest.cc
@@ -17,11 +17,8 @@
 #include "content/public/test/browser_test.h"
 #include "headless/app/headless_shell_switches.h"
 #include "headless/lib/browser/headless_web_contents_impl.h"
-#include "headless/public/devtools/domains/runtime.h"
 #include "headless/public/headless_browser.h"
 #include "headless/public/headless_browser_context.h"
-#include "headless/public/headless_devtools_client.h"
-#include "headless/public/headless_devtools_target.h"
 #include "headless/public/headless_web_contents.h"
 #include "headless/test/headless_browser_test.h"
 #include "headless/test/headless_browser_test_utils.h"
@@ -101,28 +98,22 @@
 
   void OnSecondLoadEventFired(const base::Value::Dict&) {
     // Set cookies on both pages.
-    EXPECT_EQ(
-        kMainPageCookie,
-        ResultString(EvaluateScript(web_contents_,
-                                    base::StringPrintf("document.cookie = '%s'",
-                                                       kMainPageCookie)),
-                     "result.value"));
+    EXPECT_THAT(EvaluateScript(web_contents_,
+                               base::StringPrintf("document.cookie = '%s'",
+                                                  kMainPageCookie)),
+                DictHasValue("result.result.value", kMainPageCookie));
 
-    EXPECT_EQ(
-        kIsolatedPageCookie,
-        ResultString(EvaluateScript(web_contents2_,
-                                    base::StringPrintf("document.cookie = '%s'",
-                                                       kIsolatedPageCookie)),
-                     "result.value"));
+    EXPECT_THAT(EvaluateScript(web_contents2_,
+                               base::StringPrintf("document.cookie = '%s'",
+                                                  kIsolatedPageCookie)),
+                DictHasValue("result.result.value", kIsolatedPageCookie));
 
     // Get cookies from both pages and verify.
-    EXPECT_EQ(kMainPageCookie,
-              ResultString(EvaluateScript(web_contents_, "document.cookie"),
-                           "result.value"));
+    EXPECT_THAT(EvaluateScript(web_contents_, "document.cookie"),
+                DictHasValue("result.result.value", kMainPageCookie));
 
-    EXPECT_EQ(kIsolatedPageCookie,
-              ResultString(EvaluateScript(web_contents2_, "document.cookie"),
-                           "result.value"));
+    EXPECT_THAT(EvaluateScript(web_contents2_, "document.cookie"),
+                DictHasValue("result.result.value", kIsolatedPageCookie));
 
     web_contents2_->RemoveObserver(this);
     web_contents2_->Close();
diff --git a/headless/test/headless_browser_test.cc b/headless/test/headless_browser_test.cc
index cc6c7087..806c046 100644
--- a/headless/test/headless_browser_test.cc
+++ b/headless/test/headless_browser_test.cc
@@ -21,9 +21,6 @@
 #include "headless/lib/browser/headless_browser_main_parts.h"
 #include "headless/lib/browser/headless_web_contents_impl.h"
 #include "headless/lib/headless_content_main_delegate.h"
-#include "headless/public/devtools/domains/emulation.h"
-#include "headless/public/headless_devtools_client.h"
-#include "headless/public/headless_devtools_target.h"
 #include "headless/public/headless_web_contents.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/size.h"
@@ -126,90 +123,4 @@
   run_loop_->Quit();
 }
 
-HeadlessAsyncDevTooledBrowserTest::HeadlessAsyncDevTooledBrowserTest()
-    : browser_context_(nullptr),
-      web_contents_(nullptr),
-      render_process_exited_(false) {}
-
-HeadlessAsyncDevTooledBrowserTest::~HeadlessAsyncDevTooledBrowserTest() =
-    default;
-
-void HeadlessAsyncDevTooledBrowserTest::DevToolsTargetReady() {
-  EXPECT_TRUE(web_contents_->GetDevToolsTarget());
-  web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
-#if BUILDFLAG(IS_MAC)
-  devtools_client_->GetEmulation()->SetDeviceMetricsOverride(
-      emulation::SetDeviceMetricsOverrideParams::Builder()
-          .SetWidth(0)
-          .SetHeight(0)
-          .SetDeviceScaleFactor(1)
-          .SetMobile(false)
-          .Build(),
-      base::BindOnce(
-          [](HeadlessAsyncDevTooledBrowserTest* self) {
-            self->RunDevTooledTest();
-          },
-          base::Unretained(this)));
-#else
-  RunDevTooledTest();
-#endif
-}
-
-void HeadlessAsyncDevTooledBrowserTest::RenderProcessExited(
-    base::TerminationStatus status,
-    int exit_code) {
-  if (status == base::TERMINATION_STATUS_NORMAL_TERMINATION)
-    return;
-
-  FinishAsynchronousTest();
-  render_process_exited_ = true;
-  FAIL() << "Abnormal renderer termination "
-         << "(status=" << status << ", exit_code=" << exit_code << ")";
-}
-
-void HeadlessAsyncDevTooledBrowserTest::RunTest() {
-  devtools_client_ = HeadlessDevToolsClient::Create();
-  browser_devtools_client_ = HeadlessDevToolsClient::Create();
-  interceptor_ = std::make_unique<TestNetworkInterceptor>();
-  HeadlessBrowserContext::Builder builder =
-      browser()->CreateBrowserContextBuilder();
-  CustomizeHeadlessBrowserContext(builder);
-  browser_context_ = builder.Build();
-
-  browser()->SetDefaultBrowserContext(browser_context_);
-  browser()->GetDevToolsTarget()->AttachClient(browser_devtools_client_.get());
-
-  HeadlessWebContents::Builder web_contents_builder =
-      browser_context_->CreateWebContentsBuilder();
-  web_contents_builder.SetEnableBeginFrameControl(GetEnableBeginFrameControl());
-  CustomizeHeadlessWebContents(web_contents_builder);
-  web_contents_ = web_contents_builder.Build();
-
-  web_contents_->AddObserver(this);
-
-  RunAsynchronousTest();
-  interceptor_.reset();
-  if (!render_process_exited_)
-    web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get());
-  web_contents_->RemoveObserver(this);
-  web_contents_->Close();
-  web_contents_ = nullptr;
-  browser()->GetDevToolsTarget()->DetachClient(browser_devtools_client_.get());
-  browser_context_->Close();
-  browser_context_ = nullptr;
-  // Let the tasks that might have beein scheduled during web contents
-  // being closed run (see https://crbug.com/1036627 for details).
-  base::RunLoop().RunUntilIdle();
-}
-
-bool HeadlessAsyncDevTooledBrowserTest::GetEnableBeginFrameControl() {
-  return false;
-}
-
-void HeadlessAsyncDevTooledBrowserTest::CustomizeHeadlessBrowserContext(
-    HeadlessBrowserContext::Builder& builder) {}
-
-void HeadlessAsyncDevTooledBrowserTest::CustomizeHeadlessWebContents(
-    HeadlessWebContents::Builder& builder) {}
-
 }  // namespace headless
diff --git a/headless/test/headless_browser_test.h b/headless/test/headless_browser_test.h
index 3ce8bda..07b8d5ac 100644
--- a/headless/test/headless_browser_test.h
+++ b/headless/test/headless_browser_test.h
@@ -6,21 +6,16 @@
 #define HEADLESS_TEST_HEADLESS_BROWSER_TEST_H_
 
 #include <memory>
-#include <string>
 
-#include "base/memory/raw_ptr.h"
 #include "build/build_config.h"
 #include "content/public/test/browser_test_base.h"
 #include "headless/public/headless_browser.h"
-#include "headless/public/headless_web_contents.h"
-#include "headless/test/test_network_interceptor.h"
 
 namespace base {
 class RunLoop;
 }
 
 namespace headless {
-class HeadlessDevToolsClient;
 
 // Base class for tests which require a full instance of the headless browser.
 class HeadlessBrowserTest : public content::BrowserTestBase {
@@ -64,69 +59,6 @@
   std::unique_ptr<base::RunLoop> run_loop_;
 };
 
-// TODO(eseckler): Make macro more sheriff-friendly.
-#define HEADLESS_ASYNC_DEVTOOLED_TEST_F(TEST_FIXTURE_NAME)               \
-  IN_PROC_BROWSER_TEST_F(TEST_FIXTURE_NAME, RunAsyncTest) { RunTest(); } \
-  class AsyncHeadlessBrowserTestNeedsSemicolon##TEST_FIXTURE_NAME {}
-
-#define HEADLESS_ASYNC_DEVTOOLED_TEST_P(TEST_FIXTURE_NAME)               \
-  IN_PROC_BROWSER_TEST_P(TEST_FIXTURE_NAME, RunAsyncTest) { RunTest(); } \
-  class AsyncHeadlessBrowserTestNeedsSemicolon##TEST_FIXTURE_NAME {}
-
-#define DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(TEST_FIXTURE_NAME)  \
-  IN_PROC_BROWSER_TEST_F(TEST_FIXTURE_NAME, DISABLED_RunAsyncTest) { \
-    RunTest();                                                       \
-  }                                                                  \
-  class AsyncHeadlessBrowserTestNeedsSemicolon##TEST_FIXTURE_NAME {}
-
-#define DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_P(TEST_FIXTURE_NAME)  \
-  IN_PROC_BROWSER_TEST_P(TEST_FIXTURE_NAME, DISABLED_RunAsyncTest) { \
-    RunTest();                                                       \
-  }                                                                  \
-  class AsyncHeadlessBrowserTestNeedsSemicolon##TEST_FIXTURE_NAME {}
-
-// Base class for tests that require access to a DevToolsClient. Subclasses
-// should override the RunDevTooledTest() method, which is called asynchronously
-// when the DevToolsClient is ready.
-class HeadlessAsyncDevTooledBrowserTest : public HeadlessBrowserTest,
-                                          public HeadlessWebContents::Observer {
- public:
-  HeadlessAsyncDevTooledBrowserTest();
-  ~HeadlessAsyncDevTooledBrowserTest() override;
-
-  // HeadlessWebContentsObserver implementation:
-  void DevToolsTargetReady() override;
-  void RenderProcessExited(base::TerminationStatus status,
-                           int exit_code) override;
-
-  // Implemented by tests and used to send request(s) to DevTools. Subclasses
-  // need to ensure that FinishAsynchronousTest() is called after response(s)
-  // are processed (e.g. in a callback).
-  virtual void RunDevTooledTest() = 0;
-
-  // Whether to enable BeginFrameControl when creating |web_contents_|.
-  virtual bool GetEnableBeginFrameControl();
-
-  // Allows the HeadlessBrowserContext used in testing to be customized.
-  virtual void CustomizeHeadlessBrowserContext(
-      HeadlessBrowserContext::Builder& builder);
-
-  // Allows the HeadlessWebContents used in testing to be customized.
-  virtual void CustomizeHeadlessWebContents(
-      HeadlessWebContents::Builder& builder);
-
- protected:
-  void RunTest();
-
-  raw_ptr<HeadlessBrowserContext, DanglingUntriaged>
-      browser_context_;  // Not owned.
-  raw_ptr<HeadlessWebContents, DanglingUntriaged> web_contents_;
-  std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
-  std::unique_ptr<HeadlessDevToolsClient> browser_devtools_client_;
-  bool render_process_exited_;
-  std::unique_ptr<TestNetworkInterceptor> interceptor_;
-};
-
 }  // namespace headless
 
 #endif  // HEADLESS_TEST_HEADLESS_BROWSER_TEST_H_
diff --git a/headless/test/headless_browser_test_utils.cc b/headless/test/headless_browser_test_utils.cc
index 7c0d8d16..743a5c2 100644
--- a/headless/test/headless_browser_test_utils.cc
+++ b/headless/test/headless_browser_test_utils.cc
@@ -5,15 +5,16 @@
 #include "headless/test/headless_browser_test_utils.h"
 
 #include "base/bind.h"
+#include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/run_loop.h"
-#include "base/strings/strcat.h"
 #include "components/devtools/simple_devtools_protocol_client/simple_devtools_protocol_client.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "headless/lib/browser/headless_web_contents_impl.h"
 #include "headless/public/headless_web_contents.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 using simple_devtools_protocol_client::SimpleDevToolsProtocolClient;
@@ -88,94 +89,131 @@
   focus_observer.Wait();
 }
 
-namespace {
+///////////////////////////////////////////////////////////////////////
+// base::Value::Dict helpers.
 
-std::string GetString(const base::Value::Dict& dict,
-                      base::StringPiece root,
-                      base::StringPiece key) {
-  const std::string path = base::StrCat({root, key});
-  const std::string* return_string = dict.FindStringByDottedPath(path);
-  CHECK(return_string) << "Missing value for '" << path << "' in:\n"
-                       << dict.DebugString();
-  return *return_string;
+std::string DictString(const base::Value::Dict& dict, base::StringPiece path) {
+  const std::string* result = dict.FindStringByDottedPath(path);
+  CHECK(result) << "Missing value for '" << path << "' in:\n"
+                << dict.DebugString();
+  return *result;
 }
 
-int GetInt(const base::Value::Dict& dict,
-           base::StringPiece root,
-           base::StringPiece key) {
-  const std::string path = base::StrCat({root, key});
-  absl::optional<int> return_int = dict.FindIntByDottedPath(path);
-  CHECK(return_int) << "Missing value for '" << path << "' in:\n"
-                    << dict.DebugString();
-  return *return_int;
+int DictInt(const base::Value::Dict& dict, base::StringPiece path) {
+  absl::optional<int> result = dict.FindIntByDottedPath(path);
+  CHECK(result) << "Missing value for '" << path << "' in:\n"
+                << dict.DebugString();
+  return *result;
 }
 
-bool GetBool(const base::Value::Dict& dict,
-             base::StringPiece root,
-             base::StringPiece key) {
-  const std::string path = base::StrCat({root, key});
-  absl::optional<bool> return_bool = dict.FindBoolByDottedPath(path);
-  CHECK(return_bool) << "Missing value for '" << path << "' in:\n"
-                     << dict.DebugString();
-  return *return_bool;
+bool DictBool(const base::Value::Dict& dict, base::StringPiece path) {
+  absl::optional<bool> result = dict.FindBoolByDottedPath(path);
+  CHECK(result) << "Missing value for '" << path << "' in:\n"
+                << dict.DebugString();
+  return *result;
 }
 
-bool Has(const base::Value::Dict& dict,
-         base::StringPiece root,
-         base::StringPiece key) {
-  const std::string path = base::StrCat({root, key});
+bool DictHas(const base::Value::Dict& dict, base::StringPiece path) {
   return dict.FindByDottedPath(path) != nullptr;
 }
 
+///////////////////////////////////////////////////////////////////////
+// GMock matchers
+
+namespace {
+// Cannot use Value::DebugString here due to newlines.
+std::string ToJSON(const base::ValueView& value) {
+  std::string json;
+  base::JSONWriter::Write(value, &json);
+  return json;
+}
+
+class DictHasPathValueMatcher
+    : public testing::MatcherInterface<const base::Value::Dict&> {
+ public:
+  DictHasPathValueMatcher(const std::string& path, base::Value expected_value)
+      : path_(path), expected_value_(std::move(expected_value)) {}
+
+  DictHasPathValueMatcher& operator=(const DictHasPathValueMatcher& other) =
+      delete;
+
+  ~DictHasPathValueMatcher() override = default;
+
+  bool MatchAndExplain(const base::Value::Dict& dict,
+                       testing::MatchResultListener* listener) const override {
+    const base::Value* dict_value = dict.FindByDottedPath(path_);
+    if (!dict_value) {
+      *listener << "Dictionary '" << ToJSON(dict) << "' does not have path '"
+                << path_ << "'";
+      return false;
+    }
+    if (*dict_value != expected_value_) {
+      *listener << "Dictionary path value '" << path_ << "' is '"
+                << ToJSON(*dict_value) << "', expected '"
+                << ToJSON(expected_value_) << "'";
+      return false;
+    }
+    return true;
+  }
+
+  void DescribeTo(std::ostream* os) const override {
+    *os << "has path '" << path_ << "' with value '" << ToJSON(expected_value_)
+        << "'";
+  }
+
+  void DescribeNegationTo(std::ostream* os) const override {
+    *os << "does not have path '" << path_ << "' with value '"
+        << ToJSON(expected_value_) << "'";
+  }
+
+ private:
+  const std::string path_;
+  const base::Value expected_value_;
+};
+
+class DictHasKeyMatcher
+    : public testing::MatcherInterface<const base::Value::Dict&> {
+ public:
+  explicit DictHasKeyMatcher(const std::string& key) : key_(key) {}
+
+  DictHasKeyMatcher& operator=(const DictHasKeyMatcher& other) = delete;
+
+  ~DictHasKeyMatcher() override = default;
+
+  bool MatchAndExplain(const base::Value::Dict& dict,
+                       testing::MatchResultListener* listener) const override {
+    const base::Value* dict_value = dict.Find(key_);
+    if (!dict_value) {
+      *listener << "Dictionary '" << ToJSON(dict) << "' does not have key '"
+                << key_ << "'";
+      return false;
+    }
+    return true;
+  }
+
+  void DescribeTo(std::ostream* os) const override {
+    *os << "has key '" << key_ << "'";
+  }
+
+  void DescribeNegationTo(std::ostream* os) const override {
+    *os << "does not have key '" << key_ << "'";
+  }
+
+ private:
+  const std::string key_;
+};
+
 }  // namespace
 
-bool ResultError(const base::Value::Dict& result,
-                 int* code,
-                 std::string* message) {
-  absl::optional<int> error_code = result.FindIntByDottedPath("error.code");
-  if (code && error_code)
-    *code = *error_code;
-
-  const std::string* error_message =
-      result.FindStringByDottedPath("error.message");
-  if (message && error_message)
-    *message = *error_message;
-
-  return error_code || error_message;
+testing::Matcher<const base::Value::Dict&> DictHasPathValue(
+    const std::string& path,
+    base::Value expected_value) {
+  return testing::MakeMatcher(
+      new DictHasPathValueMatcher(path, std::move(expected_value)));
 }
 
-std::string ResultString(const base::Value::Dict& result,
-                         base::StringPiece key) {
-  return GetString(result, "result.", key);
-}
-
-int ResultInt(const base::Value::Dict& result, base::StringPiece key) {
-  return GetInt(result, "result.", key);
-}
-
-bool ResultBool(const base::Value::Dict& result, base::StringPiece key) {
-  return GetBool(result, "result.", key);
-}
-
-bool ResultHas(const base::Value::Dict& result, base::StringPiece key) {
-  return Has(result, "result.", key);
-}
-
-std::string ParamsString(const base::Value::Dict& params,
-                         base::StringPiece key) {
-  return GetString(params, "params.", key);
-}
-
-int ParamsInt(const base::Value::Dict& params, base::StringPiece key) {
-  return GetInt(params, "params.", key);
-}
-
-bool ParamsBool(const base::Value::Dict& params, base::StringPiece key) {
-  return GetBool(params, "params.", key);
-}
-
-bool ParamHas(const base::Value::Dict& result, base::StringPiece key) {
-  return Has(result, "params.", key);
+testing::Matcher<const base::Value::Dict&> DictHasKey(const std::string& key) {
+  return testing::MakeMatcher(new DictHasKeyMatcher(key));
 }
 
 }  // namespace headless
diff --git a/headless/test/headless_browser_test_utils.h b/headless/test/headless_browser_test_utils.h
index 3b55b5f8..f947005 100644
--- a/headless/test/headless_browser_test_utils.h
+++ b/headless/test/headless_browser_test_utils.h
@@ -10,6 +10,7 @@
 #include "base/strings/string_piece.h"
 #include "base/values.h"
 #include "net/base/net_errors.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
 
 namespace simple_devtools_protocol_client {
 class SimpleDevToolsProtocolClient;
@@ -52,24 +53,28 @@
   return param;
 }
 
-// Convenience functions to retrieve values from a |result| Dict.
-bool ResultError(const base::Value::Dict& result,
-                 int* code = nullptr,
-                 std::string* message = nullptr);
-std::string ResultString(const base::Value::Dict& result,
-                         base::StringPiece key);
-int ResultInt(const base::Value::Dict& result, base::StringPiece key);
-bool ResultBool(const base::Value::Dict& result, base::StringPiece key);
+// Convenience functions to retrieve values from a base::Value::Dict and
+// CHECK fail if the specified path is not found.
+std::string DictString(const base::Value::Dict& dict, base::StringPiece path);
+int DictInt(const base::Value::Dict& dict, base::StringPiece path);
+bool DictBool(const base::Value::Dict& dict, base::StringPiece path);
+bool DictHas(const base::Value::Dict& dict, base::StringPiece path);
 
-bool ResultHas(const base::Value::Dict& result, base::StringPiece key);
+// A custom GMock matcher which matches if a base::Value::Dict has
+// a path |path| that is equal to |value|.
+testing::Matcher<const base::Value::Dict&> DictHasPathValue(
+    const std::string& path,
+    base::Value expected_value);
 
-// Convenience functions to retrieve values from a |params| Dict.
-std::string ParamsString(const base::Value::Dict& params,
-                         base::StringPiece key);
-int ParamsInt(const base::Value::Dict& params, base::StringPiece key);
-bool ParamsBool(const base::Value::Dict& params, base::StringPiece key);
+template <typename T>
+testing::Matcher<const base::Value::Dict&> DictHasValue(const std::string& path,
+                                                        T expected_value) {
+  return DictHasPathValue(path, base::Value(expected_value));
+}
 
-bool ParamHas(const base::Value::Dict& params, base::StringPiece key);
+// A custom GMock matcher which matches if a base::Value::Dict has
+// the key |key|.
+testing::Matcher<const base::Value::Dict&> DictHasKey(const std::string& key);
 
 }  // namespace headless
 
diff --git a/headless/test/headless_client_browsertest.cc b/headless/test/headless_client_browsertest.cc
index 42cac4b..8dedddac 100644
--- a/headless/test/headless_client_browsertest.cc
+++ b/headless/test/headless_client_browsertest.cc
@@ -29,7 +29,7 @@
 
   void AttachToTarget(base::Value::Dict result) {
     base::Value::Dict params;
-    params.Set("targetId", ResultString(result, "targetId"));
+    params.Set("targetId", DictString(result, "result.targetId"));
     params.Set("flatten", true);
     browser_devtools_client_.SendCommand(
         "Target.attachToTarget", std::move(params),
@@ -39,7 +39,7 @@
 
   void CreateSession(base::Value::Dict result) {
     session_client_ = browser_devtools_client_.CreateSession(
-        ResultString(result, "sessionId"));
+        DictString(result, "result.sessionId"));
 
     session_client_->SendCommand(
         "Runtime.evaluate", Param("expression", "window.location.href"),
@@ -48,7 +48,7 @@
   }
 
   void FinishTest(base::Value::Dict result) {
-    EXPECT_EQ("about:blank", ResultString(result, "result.value"));
+    EXPECT_THAT(result, DictHasValue("result.result.value", "about:blank"));
     session_client_.reset();
     FinishAsynchronousTest();
   }
diff --git a/headless/test/headless_devtooled_browsertest.cc b/headless/test/headless_devtooled_browsertest.cc
index 7539e04..3b017d670 100644
--- a/headless/test/headless_devtooled_browsertest.cc
+++ b/headless/test/headless_devtooled_browsertest.cc
@@ -10,7 +10,6 @@
 #include "build/build_config.h"
 #include "headless/lib/browser/headless_web_contents_impl.h"
 #include "headless/public/headless_browser_context.h"
-#include "headless/public/headless_devtools_target.h"
 
 namespace headless {
 
diff --git a/headless/test/headless_devtools_client_browsertest.cc b/headless/test/headless_devtools_client_browsertest.cc
index 615720c..0196be78 100644
--- a/headless/test/headless_devtools_client_browsertest.cc
+++ b/headless/test/headless_devtools_client_browsertest.cc
@@ -10,33 +10,25 @@
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/path_service.h"
-#include "base/run_loop.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_restrictions.h"
+#include "base/values.h"
 #include "build/build_config.h"
+#include "components/devtools/simple_devtools_protocol_client/simple_devtools_protocol_client.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/no_renderer_crashes_assertion.h"
 #include "headless/lib/browser/headless_web_contents_impl.h"
-#include "headless/public/devtools/domains/browser.h"
-#include "headless/public/devtools/domains/dom.h"
-#include "headless/public/devtools/domains/dom_snapshot.h"
-#include "headless/public/devtools/domains/emulation.h"
-#include "headless/public/devtools/domains/inspector.h"
-#include "headless/public/devtools/domains/network.h"
-#include "headless/public/devtools/domains/page.h"
-#include "headless/public/devtools/domains/runtime.h"
-#include "headless/public/devtools/domains/target.h"
-#include "headless/public/headless_browser.h"
-#include "headless/public/headless_devtools_client.h"
-#include "headless/public/headless_devtools_target.h"
 #include "headless/test/headless_browser_test.h"
+#include "headless/test/headless_browser_test_utils.h"
+#include "headless/test/headless_devtooled_browsertest.h"
 #include "net/test/spawned_test_server/spawned_test_server.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/chrome_debug_urls.h"
+#include "third_party/inspector_protocol/crdtp/dispatch.h"
 #include "url/gurl.h"
 
 #define EXPECT_SIZE_EQ(expected, actual)               \
@@ -45,6 +37,8 @@
     EXPECT_EQ((expected).height(), (actual).height()); \
   } while (false)
 
+using simple_devtools_protocol_client::SimpleDevToolsProtocolClient;
+
 using testing::ElementsAre;
 using testing::NotNull;
 using testing::UnorderedElementsAre;
@@ -52,135 +46,122 @@
 namespace headless {
 
 class HeadlessDevToolsClientNavigationTest
-    : public HeadlessAsyncDevTooledBrowserTest,
-      page::ExperimentalObserver {
+    : public HeadlessDevTooledBrowserTest {
  public:
   void RunDevTooledTest() override {
-    EXPECT_TRUE(embedded_test_server()->Start());
-    std::unique_ptr<page::NavigateParams> params =
-        page::NavigateParams::Builder()
-            .SetUrl(embedded_test_server()->GetURL("/hello.html").spec())
-            .Build();
-    devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
-    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-    devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
-    run_loop.Run();
-    devtools_client_->GetPage()->Navigate(std::move(params));
+    ASSERT_TRUE(embedded_test_server()->Start());
+
+    devtools_client_.AddEventHandler(
+        "Page.loadEventFired",
+        base::BindRepeating(
+            &HeadlessDevToolsClientNavigationTest::OnLoadEventFired,
+            base::Unretained(this)));
+    SendCommandSync(devtools_client_, "Page.enable");
+
+    devtools_client_.SendCommand(
+        "Page.navigate",
+        Param("url", embedded_test_server()->GetURL("/hello.html").spec()));
   }
 
-  void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
-    devtools_client_->GetPage()->Disable();
-    devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this);
+  void OnLoadEventFired(const base::Value::Dict& params) {
+    devtools_client_.SendCommand("Page.disable");
     FinishAsynchronousTest();
   }
-
-  // Check that events with no parameters still get a parameters object.
-  void OnFrameResized(const page::FrameResizedParams& params) override {}
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientNavigationTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessDevToolsClientNavigationTest);
 
 class HeadlessDevToolsClientWindowManagementTest
-    : public HeadlessAsyncDevTooledBrowserTest {
+    : public HeadlessDevTooledBrowserTest {
  public:
-  void SetWindowBounds(
-      const gfx::Rect& rect,
-      base::OnceCallback<void(std::unique_ptr<browser::SetWindowBoundsResult>)>
-          callback) {
-    std::unique_ptr<browser::Bounds> bounds =
-        browser::Bounds::Builder()
-            .SetLeft(rect.x())
-            .SetTop(rect.y())
-            .SetWidth(rect.width())
-            .SetHeight(rect.height())
-            .SetWindowState(browser::WindowState::NORMAL)
-            .Build();
-    int window_id = HeadlessWebContentsImpl::From(web_contents_)->window_id();
-    std::unique_ptr<browser::SetWindowBoundsParams> params =
-        browser::SetWindowBoundsParams::Builder()
-            .SetWindowId(window_id)
-            .SetBounds(std::move(bounds))
-            .Build();
-    browser_devtools_client_->GetBrowser()->GetExperimental()->SetWindowBounds(
-        std::move(params), std::move(callback));
+  int window_id() {
+    return HeadlessWebContentsImpl::From(web_contents_)->window_id();
   }
 
-  void SetWindowState(
-      const browser::WindowState state,
-      base::OnceCallback<void(std::unique_ptr<browser::SetWindowBoundsResult>)>
-          callback) {
-    std::unique_ptr<browser::Bounds> bounds =
-        browser::Bounds::Builder().SetWindowState(state).Build();
-    int window_id = HeadlessWebContentsImpl::From(web_contents_)->window_id();
-    std::unique_ptr<browser::SetWindowBoundsParams> params =
-        browser::SetWindowBoundsParams::Builder()
-            .SetWindowId(window_id)
-            .SetBounds(std::move(bounds))
-            .Build();
-    browser_devtools_client_->GetBrowser()->GetExperimental()->SetWindowBounds(
-        std::move(params), std::move(callback));
+  void SetWindowBounds(
+      const gfx::Rect& rect,
+      SimpleDevToolsProtocolClient::ResponseCallback callback) {
+    base::Value::Dict params;
+    params.Set("windowId", window_id());
+    params.SetByDottedPath("bounds.left", rect.x());
+    params.SetByDottedPath("bounds.top", rect.y());
+    params.SetByDottedPath("bounds.width", rect.width());
+    params.SetByDottedPath("bounds.height", rect.height());
+    params.SetByDottedPath("bounds.windowState", "normal");
+
+    browser_devtools_client_.SendCommand(
+        "Browser.setWindowBounds", std::move(params), std::move(callback));
+  }
+
+  void SetWindowState(const std::string& window_state,
+                      SimpleDevToolsProtocolClient::ResponseCallback callback) {
+    base::Value::Dict params;
+    params.Set("windowId", window_id());
+    params.SetByDottedPath("bounds.windowState", window_state);
+
+    browser_devtools_client_.SendCommand(
+        "Browser.setWindowBounds", std::move(params), std::move(callback));
   }
 
   void GetWindowBounds(
-      base::OnceCallback<void(std::unique_ptr<browser::GetWindowBoundsResult>)>
-          callback) {
-    int window_id = HeadlessWebContentsImpl::From(web_contents_)->window_id();
-    std::unique_ptr<browser::GetWindowBoundsParams> params =
-        browser::GetWindowBoundsParams::Builder()
-            .SetWindowId(window_id)
-            .Build();
-
-    browser_devtools_client_->GetBrowser()->GetExperimental()->GetWindowBounds(
-        std::move(params), std::move(callback));
+      SimpleDevToolsProtocolClient::ResponseCallback callback) {
+    browser_devtools_client_.SendCommand("Browser.getWindowBounds",
+                                         Param("windowId", window_id()),
+                                         std::move(callback));
   }
 
-  void CheckWindowBounds(
-      const gfx::Rect& bounds,
-      const browser::WindowState state,
-      std::unique_ptr<browser::GetWindowBoundsResult> result) {
-    const browser::Bounds* actual_bounds = result->GetBounds();
-// Mac does not support repositioning, as we don't show any actual window.
+  void CheckWindowBounds(const gfx::Rect& bounds,
+                         const std::string window_state,
+                         base::Value::Dict result) {
+    gfx::Rect actual_bounds(DictInt(result, "result.bounds.left"),
+                            DictInt(result, "result.bounds.top"),
+                            DictInt(result, "result.bounds.width"),
+                            DictInt(result, "result.bounds.height"));
+
+    std::string actual_window_state =
+        DictString(result, "result.bounds.windowState");
+
+    // Mac does not support repositioning, as we don't show any actual window.
 #if !BUILDFLAG(IS_MAC)
-    EXPECT_EQ(bounds.x(), actual_bounds->GetLeft());
-    EXPECT_EQ(bounds.y(), actual_bounds->GetTop());
+    EXPECT_EQ(bounds.x(), actual_bounds.x());
+    EXPECT_EQ(bounds.y(), actual_bounds.y());
 #endif  // !BUILDFLAG(IS_MAC)
-    EXPECT_EQ(bounds.width(), actual_bounds->GetWidth());
-    EXPECT_EQ(bounds.height(), actual_bounds->GetHeight());
-    EXPECT_EQ(state, actual_bounds->GetWindowState());
+    EXPECT_EQ(bounds.width(), actual_bounds.width());
+    EXPECT_EQ(bounds.height(), actual_bounds.height());
+    EXPECT_EQ(window_state, actual_window_state);
   }
 };
 
 class HeadlessDevToolsClientChangeWindowBoundsTest
     : public HeadlessDevToolsClientWindowManagementTest {
+  gfx::Rect new_bounds() { return gfx::Rect(100, 200, 300, 400); }
+
   void RunDevTooledTest() override {
     SetWindowBounds(
-        gfx::Rect(100, 200, 300, 400),
+        new_bounds(),
         base::BindOnce(
             &HeadlessDevToolsClientChangeWindowBoundsTest::OnSetWindowBounds,
             base::Unretained(this)));
   }
 
-  void OnSetWindowBounds(
-      std::unique_ptr<browser::SetWindowBoundsResult> result) {
+  void OnSetWindowBounds(base::Value::Dict result) {
     GetWindowBounds(base::BindOnce(
         &HeadlessDevToolsClientChangeWindowBoundsTest::OnGetWindowBounds,
         base::Unretained(this)));
   }
 
-  void OnGetWindowBounds(
-      std::unique_ptr<browser::GetWindowBoundsResult> result) {
-    CheckWindowBounds(gfx::Rect(100, 200, 300, 400),
-                      browser::WindowState::NORMAL, std::move(result));
+  void OnGetWindowBounds(base::Value::Dict result) {
+    CheckWindowBounds(new_bounds(), "normal", std::move(result));
     FinishAsynchronousTest();
   }
 };
 
 #if BUILDFLAG(IS_MAC) && defined(ADDRESS_SANITIZER)
 // TODO(crbug.com/1086872): Disabled due to flakiness on Mac ASAN.
-DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(
+DISABLED_HEADLESS_DEVTOOLED_TEST_F(
     HeadlessDevToolsClientChangeWindowBoundsTest);
 #else
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientChangeWindowBoundsTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessDevToolsClientChangeWindowBoundsTest);
 #endif
 
 class HeadlessDevToolsClientOuterSizeTest
@@ -192,292 +173,138 @@
                        base::Unretained(this)));
   }
 
-  void OnSetWindowBounds(
-      std::unique_ptr<browser::SetWindowBoundsResult> result) {
-    devtools_client_->GetRuntime()->Evaluate(
-        "window.outerWidth",
-        base::BindOnce(&HeadlessDevToolsClientOuterSizeTest::OnOuterWidthResult,
-                       base::Unretained(this)));
-    devtools_client_->GetRuntime()->Evaluate(
-        "window.outerHeight",
-        base::BindOnce(
-            &HeadlessDevToolsClientOuterSizeTest::OnOuterHeightResult,
-            base::Unretained(this)));
-  }
+  void OnSetWindowBounds(base::Value::Dict) {
+    EXPECT_EQ(800, Evaluate("window.outerWidth"));
+    EXPECT_EQ(600, Evaluate("window.outerHeight"));
 
-  void OnOuterWidthResult(std::unique_ptr<runtime::EvaluateResult> result) {
-    EXPECT_TRUE(result->GetResult()->HasValue());
-    EXPECT_EQ(800, result->GetResult()->GetValue()->GetInt());
-  }
-
-  void OnOuterHeightResult(std::unique_ptr<runtime::EvaluateResult> result) {
-    EXPECT_TRUE(result->GetResult()->HasValue());
-    EXPECT_EQ(600, result->GetResult()->GetValue()->GetInt());
     FinishAsynchronousTest();
   }
+
+  int Evaluate(const std::string& expression) {
+    base::Value::Dict result = SendCommandSync(
+        devtools_client_, "Runtime.evaluate", Param("expression", expression));
+    return DictInt(result, "result.result.value");
+  }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientOuterSizeTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessDevToolsClientOuterSizeTest);
 
 class HeadlessDevToolsClientChangeWindowStateTest
     : public HeadlessDevToolsClientWindowManagementTest {
  public:
   explicit HeadlessDevToolsClientChangeWindowStateTest(
-      browser::WindowState state)
-      : state_(state) {}
+      const std::string& window_state)
+      : window_state_(window_state) {}
 
   void RunDevTooledTest() override {
     SetWindowState(
-        state_,
+        window_state_,
         base::BindOnce(
             &HeadlessDevToolsClientChangeWindowStateTest::OnSetWindowState,
             base::Unretained(this)));
   }
 
-  void OnSetWindowState(
-      std::unique_ptr<browser::SetWindowBoundsResult> result) {
+  void OnSetWindowState(base::Value::Dict) {
     GetWindowBounds(base::BindOnce(
         &HeadlessDevToolsClientChangeWindowStateTest::OnGetWindowState,
         base::Unretained(this)));
   }
 
-  void OnGetWindowState(
-      std::unique_ptr<browser::GetWindowBoundsResult> result) {
+  void OnGetWindowState(base::Value::Dict result) {
     HeadlessBrowser::Options::Builder builder;
     const HeadlessBrowser::Options kDefaultOptions = builder.Build();
-    CheckWindowBounds(gfx::Rect(kDefaultOptions.window_size), state_,
+    CheckWindowBounds(gfx::Rect(kDefaultOptions.window_size), window_state_,
                       std::move(result));
     FinishAsynchronousTest();
   }
 
  protected:
-  browser::WindowState state_;
+  std::string window_state_;
 };
 
 class HeadlessDevToolsClientMinimizeWindowTest
     : public HeadlessDevToolsClientChangeWindowStateTest {
  public:
   HeadlessDevToolsClientMinimizeWindowTest()
-      : HeadlessDevToolsClientChangeWindowStateTest(
-            browser::WindowState::MINIMIZED) {}
+      : HeadlessDevToolsClientChangeWindowStateTest("minimized") {}
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientMinimizeWindowTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessDevToolsClientMinimizeWindowTest);
 
 class HeadlessDevToolsClientMaximizeWindowTest
     : public HeadlessDevToolsClientChangeWindowStateTest {
  public:
   HeadlessDevToolsClientMaximizeWindowTest()
-      : HeadlessDevToolsClientChangeWindowStateTest(
-            browser::WindowState::MAXIMIZED) {}
+      : HeadlessDevToolsClientChangeWindowStateTest("maximized") {}
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientMaximizeWindowTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessDevToolsClientMaximizeWindowTest);
 
 class HeadlessDevToolsClientFullscreenWindowTest
     : public HeadlessDevToolsClientChangeWindowStateTest {
  public:
   HeadlessDevToolsClientFullscreenWindowTest()
-      : HeadlessDevToolsClientChangeWindowStateTest(
-            browser::WindowState::FULLSCREEN) {}
+      : HeadlessDevToolsClientChangeWindowStateTest("fullscreen") {}
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientFullscreenWindowTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessDevToolsClientFullscreenWindowTest);
 
-class HeadlessDevToolsClientEvalTest
-    : public HeadlessAsyncDevTooledBrowserTest {
+class HeadlessDevToolsClientEvalTest : public HeadlessDevTooledBrowserTest {
  public:
   void RunDevTooledTest() override {
-    std::unique_ptr<runtime::EvaluateParams> params =
-        runtime::EvaluateParams::Builder().SetExpression("1 + 2").Build();
-    devtools_client_->GetRuntime()->Evaluate(
-        std::move(params),
-        base::BindOnce(&HeadlessDevToolsClientEvalTest::OnFirstResult,
-                       base::Unretained(this)));
-    // Test the convenience overload which only takes the required command
-    // parameters.
-    devtools_client_->GetRuntime()->Evaluate(
-        "24 * 7",
-        base::BindOnce(&HeadlessDevToolsClientEvalTest::OnSecondResult,
-                       base::Unretained(this)));
-  }
+    base::Value::Dict result = SendCommandSync(
+        devtools_client_, "Runtime.evaluate", Param("expression", "1 + 2"));
 
-  void OnFirstResult(std::unique_ptr<runtime::EvaluateResult> result) {
-    EXPECT_TRUE(result->GetResult()->HasValue());
-    EXPECT_EQ(3, result->GetResult()->GetValue()->GetInt());
-  }
+    EXPECT_THAT(result, DictHasValue("result.result.value", 3));
 
-  void OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result) {
-    EXPECT_TRUE(result->GetResult()->HasValue());
-    EXPECT_EQ(168, result->GetResult()->GetValue()->GetInt());
     FinishAsynchronousTest();
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientEvalTest);
-
-class HeadlessDevToolsClientCallbackTest
-    : public HeadlessAsyncDevTooledBrowserTest {
- public:
-  HeadlessDevToolsClientCallbackTest() : first_result_received_(false) {}
-
-  void RunDevTooledTest() override {
-    // Null callback without parameters.
-    devtools_client_->GetPage()->Enable();
-    // Null callback with parameters.
-    devtools_client_->GetRuntime()->Evaluate("true");
-    // Non-null callback without parameters.
-    devtools_client_->GetPage()->Disable(
-        base::BindOnce(&HeadlessDevToolsClientCallbackTest::OnFirstResult,
-                       base::Unretained(this)));
-    // Non-null callback with parameters.
-    devtools_client_->GetRuntime()->Evaluate(
-        "true",
-        base::BindOnce(&HeadlessDevToolsClientCallbackTest::OnSecondResult,
-                       base::Unretained(this)));
-  }
-
-  void OnFirstResult() {
-    EXPECT_FALSE(first_result_received_);
-    first_result_received_ = true;
-  }
-
-  void OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result) {
-    EXPECT_TRUE(first_result_received_);
-    FinishAsynchronousTest();
-  }
-
- private:
-  bool first_result_received_;
-};
-
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientCallbackTest);
-
-class HeadlessDevToolsClientObserverTest
-    : public HeadlessAsyncDevTooledBrowserTest,
-      network::Observer {
- public:
-  void RunDevTooledTest() override {
-    EXPECT_TRUE(embedded_test_server()->Start());
-    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-    devtools_client_->GetNetwork()->AddObserver(this);
-    devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
-    run_loop.Run();
-
-    devtools_client_->GetPage()->Navigate(
-        embedded_test_server()->GetURL("/hello.html").spec());
-  }
-
-  void OnRequestWillBeSent(
-      const network::RequestWillBeSentParams& params) override {
-    EXPECT_EQ("GET", params.GetRequest()->GetMethod());
-    EXPECT_EQ(embedded_test_server()->GetURL("/hello.html").spec(),
-              params.GetRequest()->GetUrl());
-  }
-
-  void OnResponseReceived(
-      const network::ResponseReceivedParams& params) override {
-    EXPECT_EQ(200, params.GetResponse()->GetStatus());
-    EXPECT_EQ("OK", params.GetResponse()->GetStatusText());
-    const std::string* content_type_value =
-        params.GetResponse()->GetHeaders().FindString("Content-Type");
-    ASSERT_THAT(content_type_value, NotNull());
-    EXPECT_EQ("text/html", *content_type_value);
-
-    devtools_client_->GetNetwork()->Disable();
-    devtools_client_->GetNetwork()->RemoveObserver(this);
-    FinishAsynchronousTest();
-  }
-};
-
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientObserverTest);
-
-class HeadlessDevToolsClientExperimentalTest
-    : public HeadlessAsyncDevTooledBrowserTest,
-      page::ExperimentalObserver {
- public:
-  void RunDevTooledTest() override {
-    EXPECT_TRUE(embedded_test_server()->Start());
-    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-    devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
-    devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
-    run_loop.Run();
-    // Check that experimental commands require parameter objects.
-    devtools_client_->GetRuntime()
-        ->GetExperimental()
-        ->SetCustomObjectFormatterEnabled(
-            runtime::SetCustomObjectFormatterEnabledParams::Builder()
-                .SetEnabled(false)
-                .Build());
-
-    // Check that a previously experimental command which takes no parameters
-    // still works by giving it a parameter object.
-    devtools_client_->GetRuntime()->GetExperimental()->RunIfWaitingForDebugger(
-        runtime::RunIfWaitingForDebuggerParams::Builder().Build());
-
-    devtools_client_->GetPage()->Navigate(
-        embedded_test_server()->GetURL("/hello.html").spec());
-  }
-
-  void OnFrameStoppedLoading(
-      const page::FrameStoppedLoadingParams& params) override {
-    devtools_client_->GetPage()->Disable();
-    devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this);
-
-    // Check that a non-experimental command which has no return value can be
-    // called with a void() callback.
-    devtools_client_->GetPage()->Reload(
-        page::ReloadParams::Builder().Build(),
-        base::BindOnce(&HeadlessDevToolsClientExperimentalTest::OnReloadStarted,
-                       base::Unretained(this)));
-  }
-
-  void OnReloadStarted() { FinishAsynchronousTest(); }
-};
-
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientExperimentalTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessDevToolsClientEvalTest);
 
 class HeadlessDevToolsNavigationControlTest
-    : public HeadlessAsyncDevTooledBrowserTest,
-      network::ExperimentalObserver,
-      page::ExperimentalObserver {
+    : public HeadlessDevTooledBrowserTest {
  public:
   void RunDevTooledTest() override {
-    EXPECT_TRUE(embedded_test_server()->Start());
-    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-    devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
-    devtools_client_->GetNetwork()->GetExperimental()->AddObserver(this);
-    devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
-    run_loop.Run();
-    devtools_client_->GetNetwork()->Enable();
+    ASSERT_TRUE(embedded_test_server()->Start());
 
-    std::unique_ptr<headless::network::RequestPattern> match_all =
-        headless::network::RequestPattern::Builder().SetUrlPattern("*").Build();
-    std::vector<std::unique_ptr<headless::network::RequestPattern>> patterns;
-    patterns.push_back(std::move(match_all));
-    devtools_client_->GetNetwork()->GetExperimental()->SetRequestInterception(
-        network::SetRequestInterceptionParams::Builder()
-            .SetPatterns(std::move(patterns))
-            .Build());
-    devtools_client_->GetPage()->Navigate(
-        embedded_test_server()->GetURL("/hello.html").spec());
+    devtools_client_.AddEventHandler(
+        "Network.requestIntercepted",
+        base::BindRepeating(
+            &HeadlessDevToolsNavigationControlTest::OnRequestIntercepted,
+            base::Unretained(this)));
+
+    devtools_client_.AddEventHandler(
+        "Page.frameStoppedLoading",
+        base::BindRepeating(
+            &HeadlessDevToolsNavigationControlTest::OnFrameStoppedLoading,
+            base::Unretained(this)));
+
+    SendCommandSync(devtools_client_, "Page.enable");
+    SendCommandSync(devtools_client_, "Network.enable");
+
+    base::Value::List patterns;
+    patterns.Append(Param("urlPattern", "*"));
+    devtools_client_.SendCommand("Network.setRequestInterception",
+                                 Param("patterns", std::move(patterns)));
+
+    devtools_client_.SendCommand(
+        "Page.navigate",
+        Param("url", embedded_test_server()->GetURL("/hello.html").spec()));
   }
 
-  void OnRequestIntercepted(
-      const network::RequestInterceptedParams& params) override {
-    if (params.GetIsNavigationRequest())
+  void OnRequestIntercepted(const base::Value::Dict& params) {
+    if (DictBool(params, "params.isNavigationRequest"))
       navigation_requested_ = true;
+
     // Allow the navigation to proceed.
-    devtools_client_->GetNetwork()
-        ->GetExperimental()
-        ->ContinueInterceptedRequest(
-            network::ContinueInterceptedRequestParams::Builder()
-                .SetInterceptionId(params.GetInterceptionId())
-                .Build());
+    devtools_client_.SendCommand(
+        "Network.continueInterceptedRequest",
+        Param("interceptionId", DictString(params, "params.interceptionId")));
   }
 
-  void OnFrameStoppedLoading(
-      const page::FrameStoppedLoadingParams& params) override {
+  void OnFrameStoppedLoading(const base::Value::Dict& params) {
     EXPECT_TRUE(navigation_requested_);
     FinishAsynchronousTest();
   }
@@ -486,22 +313,23 @@
   bool navigation_requested_ = false;
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsNavigationControlTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessDevToolsNavigationControlTest);
 
-class HeadlessCrashObserverTest : public HeadlessAsyncDevTooledBrowserTest,
-                                  inspector::ExperimentalObserver {
+class HeadlessCrashObserverTest : public HeadlessDevTooledBrowserTest {
  public:
   void RunDevTooledTest() override {
-    devtools_client_->GetInspector()->GetExperimental()->AddObserver(this);
-    devtools_client_->GetInspector()->GetExperimental()->Enable(
-        inspector::EnableParams::Builder().Build());
-    devtools_client_->GetPage()->Navigate(blink::kChromeUICrashURL);
+    devtools_client_.AddEventHandler(
+        "Inspector.targetCrashed",
+        base::BindRepeating(&HeadlessCrashObserverTest::OnTargetCrashed,
+                            base::Unretained(this)));
+
+    SendCommandSync(devtools_client_, "Inspector.enable");
+
+    devtools_client_.SendCommand("Page.navigate",
+                                 Param("url", blink::kChromeUICrashURL));
   }
 
-  void OnTargetCrashed(const inspector::TargetCrashedParams& params) override {
-    FinishAsynchronousTest();
-    render_process_exited_ = true;
-  }
+  void OnTargetCrashed(const base::Value::Dict&) { FinishAsynchronousTest(); }
 
   // Make sure we don't fail because the renderer crashed!
   void RenderProcessExited(base::TerminationStatus status,
@@ -525,55 +353,54 @@
 
 // TODO(1272554): HeadlessCrashObserverTest.RunAsyncTest is flaky on Win debug.
 #if BUILDFLAG(IS_WIN) && !defined(NDEBUG)
-DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessCrashObserverTest);
+DISABLED_HEADLESS_DEVTOOLED_TEST_F(HeadlessCrashObserverTest);
 #else
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessCrashObserverTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessCrashObserverTest);
 #endif
 
-class HeadlessDevToolsClientAttachTest
-    : public HeadlessAsyncDevTooledBrowserTest {
+class HeadlessDevToolsClientAttachTest : public HeadlessDevTooledBrowserTest {
  public:
   void RunDevTooledTest() override {
-    other_devtools_client_ = HeadlessDevToolsClient::Create();
     HeadlessDevToolsTarget* devtools_target =
         web_contents_->GetDevToolsTarget();
-
     EXPECT_TRUE(devtools_target->IsAttached());
+
     // Detach the existing client, attach the other client.
-    devtools_target->DetachClient(devtools_client_.get());
+    devtools_client_.DetachClient();
     EXPECT_FALSE(devtools_target->IsAttached());
-    devtools_target->AttachClient(other_devtools_client_.get());
+
+    other_devtools_client_.AttachToWebContents(
+        HeadlessWebContentsImpl::From(web_contents_)->web_contents());
     EXPECT_TRUE(devtools_target->IsAttached());
 
     // Now, let's make sure this devtools client works.
-    other_devtools_client_->GetRuntime()->Evaluate(
-        "24 * 7",
+    other_devtools_client_.SendCommand(
+        "Runtime.evaluate", Param("expression", "24 * 7"),
         base::BindOnce(&HeadlessDevToolsClientAttachTest::OnFirstResult,
                        base::Unretained(this)));
   }
 
-  void OnFirstResult(std::unique_ptr<runtime::EvaluateResult> result) {
-    EXPECT_TRUE(result->GetResult()->HasValue());
-    EXPECT_EQ(24 * 7, result->GetResult()->GetValue()->GetInt());
+  void OnFirstResult(base::Value::Dict result) {
+    EXPECT_THAT(result, DictHasValue("result.result.value", 24 * 7));
 
     HeadlessDevToolsTarget* devtools_target =
         web_contents_->GetDevToolsTarget();
-
     EXPECT_TRUE(devtools_target->IsAttached());
-    devtools_target->DetachClient(other_devtools_client_.get());
+    other_devtools_client_.DetachClient();
     EXPECT_FALSE(devtools_target->IsAttached());
-    devtools_target->AttachClient(devtools_client_.get());
+    devtools_client_.AttachToWebContents(
+        HeadlessWebContentsImpl::From(web_contents_)->web_contents());
     EXPECT_TRUE(devtools_target->IsAttached());
 
-    devtools_client_->GetRuntime()->Evaluate(
-        "27 * 4",
+    // Verify that the original client still works.
+    devtools_client_.SendCommand(
+        "Runtime.evaluate", Param("expression", "27 * 4"),
         base::BindOnce(&HeadlessDevToolsClientAttachTest::OnSecondResult,
                        base::Unretained(this)));
   }
 
-  void OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result) {
-    EXPECT_TRUE(result->GetResult()->HasValue());
-    EXPECT_EQ(27 * 4, result->GetResult()->GetValue()->GetInt());
+  void OnSecondResult(base::Value::Dict result) {
+    EXPECT_THAT(result, DictHasValue("result.result.value", 27 * 4));
 
     // If everything worked, this call will not crash, since it
     // detaches devtools_client_.
@@ -581,69 +408,54 @@
   }
 
  protected:
-  std::unique_ptr<HeadlessDevToolsClient> other_devtools_client_;
+  SimpleDevToolsProtocolClient other_devtools_client_;
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientAttachTest);
-
-class HeadlessDevToolsMethodCallErrorTest
-    : public HeadlessAsyncDevTooledBrowserTest,
-      public page::Observer {
- public:
-  void RunDevTooledTest() override {
-    EXPECT_TRUE(embedded_test_server()->Start());
-    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-    devtools_client_->GetPage()->AddObserver(this);
-    devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
-    run_loop.Run();
-    devtools_client_->GetPage()->Navigate(
-        embedded_test_server()->GetURL("/hello.html").spec());
-  }
-
-  void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
-    devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this);
-    devtools_client_->GetDOM()->GetDocument(
-        base::BindOnce(&HeadlessDevToolsMethodCallErrorTest::OnGetDocument,
-                       base::Unretained(this)));
-  }
-
-  void OnGetDocument(std::unique_ptr<dom::GetDocumentResult> result) {
-    devtools_client_->GetDOM()->QuerySelector(
-        dom::QuerySelectorParams::Builder()
-            .SetNodeId(result->GetRoot()->GetNodeId())
-            .SetSelector("<o_O>")
-            .Build(),
-        base::BindOnce(&HeadlessDevToolsMethodCallErrorTest::OnQuerySelector,
-                       base::Unretained(this)));
-  }
-
-  void OnQuerySelector(std::unique_ptr<dom::QuerySelectorResult> result) {
-    EXPECT_EQ(nullptr, result);
-    FinishAsynchronousTest();
-  }
-};
-
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsMethodCallErrorTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessDevToolsClientAttachTest);
 
 class HeadlessDevToolsNetworkBlockedUrlTest
-    : public HeadlessAsyncDevTooledBrowserTest,
-      public page::Observer,
-      public network::Observer {
+    : public HeadlessDevTooledBrowserTest {
  public:
   void RunDevTooledTest() override {
-    EXPECT_TRUE(embedded_test_server()->Start());
-    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-    devtools_client_->GetPage()->AddObserver(this);
-    devtools_client_->GetPage()->Enable();
-    devtools_client_->GetNetwork()->AddObserver(this);
-    devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
-    run_loop.Run();
-    std::vector<std::string> blockedUrls;
-    blockedUrls.push_back("dom_tree_test.css");
-    devtools_client_->GetNetwork()->GetExperimental()->SetBlockedURLs(
-        network::SetBlockedURLsParams::Builder().SetUrls(blockedUrls).Build());
-    devtools_client_->GetPage()->Navigate(
-        embedded_test_server()->GetURL("/dom_tree_test.html").spec());
+    ASSERT_TRUE(embedded_test_server()->Start());
+
+    devtools_client_.AddEventHandler(
+        "Network.requestWillBeSent",
+        base::BindRepeating(
+            &HeadlessDevToolsNetworkBlockedUrlTest::OnRequestWillBeSent,
+            base::Unretained(this)));
+
+    devtools_client_.AddEventHandler(
+        "Network.responseReceived",
+        base::BindRepeating(
+            &HeadlessDevToolsNetworkBlockedUrlTest::OnResponseReceived,
+            base::Unretained(this)));
+
+    devtools_client_.AddEventHandler(
+        "Network.loadingFailed",
+        base::BindRepeating(
+            &HeadlessDevToolsNetworkBlockedUrlTest::OnLoadingFailed,
+            base::Unretained(this)));
+
+    SendCommandSync(devtools_client_, "Network.enable");
+
+    devtools_client_.AddEventHandler(
+        "Page.loadEventFired",
+        base::BindRepeating(
+            &HeadlessDevToolsNetworkBlockedUrlTest::OnLoadEventFired,
+            base::Unretained(this)));
+
+    SendCommandSync(devtools_client_, "Page.enable");
+
+    base::Value::List urls;
+    urls.Append("dom_tree_test.css");
+    devtools_client_.SendCommand("Network.setBlockedURLs",
+                                 Param("urls", std::move(urls)));
+
+    devtools_client_.SendCommand(
+        "Page.navigate",
+        Param("url",
+              embedded_test_server()->GetURL("/dom_tree_test.html").spec()));
   }
 
   std::string GetUrlPath(const std::string& url) const {
@@ -651,24 +463,24 @@
     return gurl.path();
   }
 
-  void OnRequestWillBeSent(
-      const network::RequestWillBeSentParams& params) override {
-    std::string path = GetUrlPath(params.GetRequest()->GetUrl());
+  void OnRequestWillBeSent(const base::Value::Dict& params) {
+    std::string path = GetUrlPath(DictString(params, "params.request.url"));
     requests_to_be_sent_.push_back(path);
-    request_id_to_path_[params.GetRequestId()] = path;
+    request_id_to_path_[DictString(params, "params.requestId")] = path;
   }
 
-  void OnResponseReceived(
-      const network::ResponseReceivedParams& params) override {
-    responses_received_.push_back(GetUrlPath(params.GetResponse()->GetUrl()));
+  void OnResponseReceived(const base::Value::Dict& params) {
+    responses_received_.push_back(
+        GetUrlPath(DictString(params, "params.response.url")));
   }
 
-  void OnLoadingFailed(const network::LoadingFailedParams& failed) override {
-    failures_.push_back(request_id_to_path_[failed.GetRequestId()]);
-    EXPECT_EQ(network::BlockedReason::INSPECTOR, failed.GetBlockedReason());
+  void OnLoadingFailed(const base::Value::Dict& params) {
+    failures_.push_back(
+        request_id_to_path_[DictString(params, "params.requestId")]);
+    EXPECT_EQ("inspector", DictString(params, "params.blockedReason"));
   }
 
-  void OnLoadEventFired(const page::LoadEventFiredParams&) override {
+  void OnLoadEventFired(const base::Value::Dict&) {
     EXPECT_THAT(
         requests_to_be_sent_,
         testing::UnorderedElementsAre("/dom_tree_test.html",
@@ -676,6 +488,7 @@
     EXPECT_THAT(responses_received_,
                 ElementsAre("/dom_tree_test.html", "/iframe.html"));
     EXPECT_THAT(failures_, ElementsAre("/dom_tree_test.css"));
+
     FinishAsynchronousTest();
   }
 
@@ -685,75 +498,48 @@
   std::vector<std::string> failures_;
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsNetworkBlockedUrlTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessDevToolsNetworkBlockedUrlTest);
 
 class DevToolsNetworkOfflineEmulationTest
-    : public HeadlessAsyncDevTooledBrowserTest,
-      public page::Observer,
-      public network::Observer {
+    : public HeadlessDevTooledBrowserTest {
   void RunDevTooledTest() override {
-    EXPECT_TRUE(embedded_test_server()->Start());
-    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-    devtools_client_->GetPage()->AddObserver(this);
-    devtools_client_->GetPage()->Enable();
-    devtools_client_->GetNetwork()->AddObserver(this);
-    devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
-    run_loop.Run();
-    std::unique_ptr<network::EmulateNetworkConditionsParams> params =
-        network::EmulateNetworkConditionsParams::Builder()
-            .SetOffline(true)
-            .SetLatency(0)
-            .SetDownloadThroughput(0)
-            .SetUploadThroughput(0)
-            .Build();
-    devtools_client_->GetNetwork()->EmulateNetworkConditions(std::move(params));
-    devtools_client_->GetPage()->Navigate(
-        embedded_test_server()->GetURL("/hello.html").spec());
+    ASSERT_TRUE(embedded_test_server()->Start());
+
+    devtools_client_.AddEventHandler(
+        "Network.loadingFailed",
+        base::BindRepeating(
+            &DevToolsNetworkOfflineEmulationTest::OnLoadingFailed,
+            base::Unretained(this)));
+
+    SendCommandSync(devtools_client_, "Network.enable");
+
+    base::Value::Dict params;
+    params.Set("offline", true);
+    params.Set("latency", 0);
+    params.Set("downloadThroughput", 0);
+    params.Set("uploadThroughput", 0);
+    devtools_client_.SendCommand("Network.emulateNetworkConditions",
+                                 std::move(params));
+
+    devtools_client_.SendCommand(
+        "Page.navigate",
+        Param("url", embedded_test_server()->GetURL("/hello.html").spec()));
   }
 
-  void OnLoadingFailed(const network::LoadingFailedParams& failed) override {
-    EXPECT_EQ("net::ERR_INTERNET_DISCONNECTED", failed.GetErrorText());
+  void OnLoadingFailed(const base::Value::Dict& params) {
+    EXPECT_THAT(params, DictHasValue("params.errorText",
+                                     "net::ERR_INTERNET_DISCONNECTED"));
+    EXPECT_EQ("net::ERR_INTERNET_DISCONNECTED",
+              DictString(params, "params.errorText"));
+
     FinishAsynchronousTest();
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevToolsNetworkOfflineEmulationTest);
-
-class RawDevtoolsProtocolTest
-    : public HeadlessAsyncDevTooledBrowserTest,
-      public HeadlessDevToolsClient::RawProtocolListener {
- public:
-  void RunDevTooledTest() override {
-    devtools_client_->SetRawProtocolListener(this);
-
-    base::Value::Dict message_dict;
-    message_dict.Set("id", devtools_client_->GetNextRawDevToolsMessageId());
-    message_dict.Set("method", "Runtime.evaluate");
-    base::Value::Dict params_dict;
-    params_dict.Set("expression", "1+1");
-    message_dict.Set("params", std::move(params_dict));
-    base::Value message(std::move(message_dict));
-    std::string json_message;
-    base::JSONWriter::Write(message, &json_message);
-    devtools_client_->SendRawDevToolsMessage(json_message);
-  }
-
-  bool OnProtocolMessage(base::span<const uint8_t> json_message,
-                         const base::DictionaryValue& parsed_message) override {
-    EXPECT_EQ(
-        "{\"id\":1,\"result\":{\"result\":{\"type\":\"number\","
-        "\"value\":2,\"description\":\"2\"}}}",
-        std::string(json_message.begin(), json_message.end()));
-
-    FinishAsynchronousTest();
-    return true;
-  }
-};
-
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(RawDevtoolsProtocolTest);
+HEADLESS_DEVTOOLED_TEST_F(DevToolsNetworkOfflineEmulationTest);
 
 class DevToolsAttachAndDetachNotifications
-    : public HeadlessAsyncDevTooledBrowserTest {
+    : public HeadlessDevTooledBrowserTest {
  public:
   void DevToolsClientAttached() override { dev_tools_client_attached_ = true; }
 
@@ -773,48 +559,90 @@
   bool dev_tools_client_detached_ = false;
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevToolsAttachAndDetachNotifications);
+HEADLESS_DEVTOOLED_TEST_F(DevToolsAttachAndDetachNotifications);
 
-class DomTreeExtractionBrowserTest : public HeadlessAsyncDevTooledBrowserTest,
-                                     public page::Observer {
+class DomTreeExtractionBrowserTest : public HeadlessDevTooledBrowserTest {
  public:
   void RunDevTooledTest() override {
-    EXPECT_TRUE(embedded_test_server()->Start());
-    devtools_client_->GetPage()->AddObserver(this);
-    devtools_client_->GetPage()->Enable();
-    devtools_client_->GetPage()->Navigate(
-        embedded_test_server()->GetURL("/dom_tree_test.html").spec());
+    ASSERT_TRUE(embedded_test_server()->Start());
+
+    devtools_client_.AddEventHandler(
+        "Page.loadEventFired",
+        base::BindRepeating(&DomTreeExtractionBrowserTest::OnLoadEventFired,
+                            base::Unretained(this)));
+
+    SendCommandSync(devtools_client_, "Page.enable");
+
+    devtools_client_.SendCommand(
+        "Page.navigate",
+        Param("url",
+              embedded_test_server()->GetURL("/dom_tree_test.html").spec()));
   }
 
-  void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
-    devtools_client_->GetPage()->Disable();
-    devtools_client_->GetPage()->RemoveObserver(this);
+  void OnLoadEventFired(const base::Value::Dict&) {
+    SendCommandSync(devtools_client_, "Page.disable");
 
-    std::vector<std::string> css_whitelist = {
-        "color",       "display",      "font-style", "font-family",
-        "margin-left", "margin-right", "margin-top", "margin-bottom"};
-    devtools_client_->GetDOMSnapshot()->GetExperimental()->GetSnapshot(
-        dom_snapshot::GetSnapshotParams::Builder()
-            .SetComputedStyleWhitelist(std::move(css_whitelist))
-            .Build(),
-        base::BindOnce(&DomTreeExtractionBrowserTest::OnGetSnapshotResult,
+    base::Value::List css_whitelist;
+    css_whitelist.Append("color");
+    css_whitelist.Append("display");
+    css_whitelist.Append("font-style");
+    css_whitelist.Append("font-family");
+    css_whitelist.Append("margin-left");
+    css_whitelist.Append("margin-right");
+    css_whitelist.Append("margin-top");
+    css_whitelist.Append("margin-bottom");
+    devtools_client_.SendCommand(
+        "DOMSnapshot.getSnapshot",
+        Param("computedStyleWhitelist", css_whitelist),
+        base::BindOnce(&DomTreeExtractionBrowserTest::OnGetSnapshotDone,
                        base::Unretained(this)));
   }
 
-  void OnGetSnapshotResult(
-      std::unique_ptr<dom_snapshot::GetSnapshotResult> result) {
+  void OnGetSnapshotDone(base::Value::Dict result) {
+    std::vector<base::Value::Dict> dom_nodes;
+    GetDomNodes(result, &dom_nodes);
+
+    std::vector<base::Value::Dict> computed_styles;
+    GetComputedStyles(result, &computed_styles);
+
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    base::FilePath source_root_dir;
+    base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir);
+
+    CompareToGolden(
+        dom_nodes,
+        source_root_dir.Append(FILE_PATH_LITERAL(
+            "headless/test/dom_tree_extraction_expected_nodes.txt")));
+
+    CompareToGolden(
+        computed_styles,
+        source_root_dir.Append(FILE_PATH_LITERAL(
+            "headless/test/dom_tree_extraction_expected_styles.txt")));
+
+    FinishAsynchronousTest();
+  }
+
+  void GetDomNodes(const base::Value::Dict& result,
+                   std::vector<base::Value::Dict>* dom_nodes) {
     GURL::Replacements replace_port;
     replace_port.SetPortStr("");
 
-    std::vector<base::Value> dom_nodes(result->GetDomNodes()->size());
+    const base::Value::List* dom_nodes_list =
+        result.FindListByDottedPath("result.domNodes");
+    ASSERT_NE(dom_nodes_list, nullptr);
+    ASSERT_GT(dom_nodes_list->size(), 0ul);
+
+    const base::Value::List* layout_tree_nodes_list =
+        result.FindListByDottedPath("result.layoutTreeNodes");
+    ASSERT_NE(layout_tree_nodes_list, nullptr);
+    ASSERT_GT(layout_tree_nodes_list->size(), 0ul);
 
     // For convenience, flatten the dom tree into an array of dicts.
-    for (size_t i = 0; i < result->GetDomNodes()->size(); i++) {
-      dom_snapshot::DOMNode* node = (*result->GetDomNodes())[i].get();
-
-      dom_nodes[i] = node->Serialize();
-      ASSERT_TRUE(dom_nodes[i].is_dict());
-      base::Value::Dict& node_dict = dom_nodes[i].GetDict();
+    dom_nodes->reserve(dom_nodes_list->size());
+    for (const auto& dom_node : *dom_nodes_list) {
+      ASSERT_TRUE(dom_node.is_dict());
+      dom_nodes->push_back(dom_node.GetDict().Clone());
+      base::Value::Dict& node_dict = dom_nodes->back();
 
       // Node IDs are assigned in a non deterministic way.
       if (node_dict.Find("backendNodeId"))
@@ -837,197 +665,216 @@
                                          .spec());
       }
 
-      // Merge LayoutTreeNode data into the dictionary.
-      if (base::Value* layout_node_index_value =
-              node_dict.Find("layoutNodeIndex")) {
-        int layout_node_index = layout_node_index_value->GetInt();
-        ASSERT_LE(0, layout_node_index);
-        ASSERT_GT(result->GetLayoutTreeNodes()->size(),
-                  static_cast<size_t>(layout_node_index));
-        const std::unique_ptr<dom_snapshot::LayoutTreeNode>& layout_node =
-            (*result->GetLayoutTreeNodes())[layout_node_index];
+      // Golden file expects scrollOffsetXY to have fractional part.
+      // TODO(kvitekp): Consider updating golden files.
+      if (absl::optional<double> x = node_dict.FindDouble("scrollOffsetX"))
+        node_dict.Set("scrollOffsetX", *x);
 
-        node_dict.Set("boundingBox",
-                      layout_node->GetBoundingBox()->Serialize());
+      if (absl::optional<double> y = node_dict.FindDouble("scrollOffsetY"))
+        node_dict.Set("scrollOffsetY", *y);
 
-        if (layout_node->HasLayoutText())
-          node_dict.Set("layoutText", layout_node->GetLayoutText());
+      // Merge LayoutTreeNode data into the dom_node dictionary.
+      if (absl::optional<int> layout_node_index =
+              node_dict.FindInt("layoutNodeIndex")) {
+        ASSERT_LE(0, *layout_node_index);
+        ASSERT_GT(layout_tree_nodes_list->size(),
+                  static_cast<size_t>(*layout_node_index));
 
-        if (layout_node->HasStyleIndex())
-          node_dict.Set("styleIndex", layout_node->GetStyleIndex());
+        const base::Value::Dict& layout_tree_node =
+            (*layout_tree_nodes_list)[*layout_node_index].GetDict();
 
-        if (layout_node->HasInlineTextNodes()) {
-          base::Value::List inline_text_nodes;
-          for (const std::unique_ptr<dom_snapshot::InlineTextBox>&
-                   inline_text_box : *layout_node->GetInlineTextNodes()) {
-            inline_text_nodes.Append(inline_text_box->Serialize());
+        if (const base::Value::Dict* bounding_box =
+                layout_tree_node.FindDict("boundingBox")) {
+          node_dict.Set("boundingBox", bounding_box->Clone());
+          FixBoundingBox(node_dict);
+        }
+
+        if (const std::string* layout_text =
+                layout_tree_node.FindString("layoutText")) {
+          node_dict.Set("layoutText", *layout_text);
+        }
+
+        if (absl::optional<int> style_index =
+                layout_tree_node.FindInt("styleIndex")) {
+          node_dict.Set("styleIndex", *style_index);
+        }
+
+        if (const base::Value::List* inline_text_nodes =
+                layout_tree_node.FindList("inlineTextNodes")) {
+          base::Value::List list = inline_text_nodes->Clone();
+          for (auto& list_entry : list) {
+            ASSERT_TRUE(list_entry.is_dict());
+            FixBoundingBox(list_entry.GetDict());
           }
-          node_dict.Set("inlineTextNodes", std::move(inline_text_nodes));
+          node_dict.Set("inlineTextNodes", std::move(list));
         }
       }
     }
+  }
 
-    std::vector<std::unique_ptr<base::DictionaryValue>> computed_styles(
-        result->GetComputedStyles()->size());
+  void FixBoundingBox(base::Value::Dict& dict) {
+    if (base::Value::Dict* bounding_box = dict.FindDict("boundingBox")) {
+      // The golden file expects double values in boundingBox.
+      // TODO(kvitekp): Consider updating the golden and just
+      // cloning the |boundingBox| dictionary here.
+      bounding_box->Set("x", *bounding_box->FindDouble("x"));
+      bounding_box->Set("y", *bounding_box->FindDouble("y"));
+      bounding_box->Set("width", *bounding_box->FindDouble("width"));
+      bounding_box->Set("height", *bounding_box->FindDouble("height"));
+    }
+  }
 
-    for (size_t i = 0; i < result->GetComputedStyles()->size(); i++) {
-      std::unique_ptr<base::DictionaryValue> style(new base::DictionaryValue());
-      for (const auto& style_property :
-           *(*result->GetComputedStyles())[i]->GetProperties()) {
-        style->SetString(style_property->GetName(), style_property->GetValue());
+  void GetComputedStyles(const base::Value::Dict& result,
+                         std::vector<base::Value::Dict>* computed_styles) {
+    const base::Value::List* computed_styles_list =
+        result.FindListByDottedPath("result.computedStyles");
+    ASSERT_NE(computed_styles_list, nullptr);
+    ASSERT_GT(computed_styles_list->size(), 0ul);
+
+    computed_styles->reserve(computed_styles_list->size());
+    for (const auto& computed_style : *computed_styles_list) {
+      ASSERT_TRUE(computed_style.is_dict());
+      const base::Value::List* properties_list =
+          computed_style.GetDict().FindList("properties");
+      ASSERT_NE(properties_list, nullptr);
+
+      base::Value::Dict computed_style_dict;
+      for (const auto& property : *properties_list) {
+        ASSERT_TRUE(property.is_dict());
+        const base::Value::Dict& property_dict = property.GetDict();
+        computed_style_dict.Set(*property_dict.FindString("name"),
+                                *property_dict.FindString("value"));
       }
-      computed_styles[i] = std::move(style);
+      computed_styles->push_back(std::move(computed_style_dict));
     }
+  }
 
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    base::FilePath source_root_dir;
-    base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir);
-    base::FilePath expected_dom_nodes_path =
-        source_root_dir.Append(FILE_PATH_LITERAL(
-            "headless/test/dom_tree_extraction_expected_nodes.txt"));
-    std::string expected_dom_nodes;
-    ASSERT_TRUE(
-        base::ReadFileToString(expected_dom_nodes_path, &expected_dom_nodes));
+  void CompareToGolden(const std::vector<base::Value::Dict>& entries,
+                       base::FilePath expected_filepath) {
+    std::string expected_entries;
+    ASSERT_TRUE(base::ReadFileToString(expected_filepath, &expected_entries));
 
-    std::string dom_nodes_result;
-    for (const base::Value& entry : dom_nodes) {
-      std::string result_json;
+    std::string actual_entries;
+    for (const base::Value::Dict& entry : entries) {
+      std::string entry_json;
       base::JSONWriter::WriteWithOptions(
-          entry, base::JSONWriter::OPTIONS_PRETTY_PRINT, &result_json);
-
-      dom_nodes_result += result_json;
+          entry, base::JSONWriter::OPTIONS_PRETTY_PRINT, &entry_json);
+      actual_entries += entry_json;
     }
 
 #if BUILDFLAG(IS_WIN)
-    ASSERT_TRUE(base::RemoveChars(dom_nodes_result, "\r", &dom_nodes_result));
+    ASSERT_TRUE(base::RemoveChars(actual_entries, "\r", &actual_entries));
 #endif
 
-    EXPECT_EQ(expected_dom_nodes, dom_nodes_result);
-
-    base::FilePath expected_styles_path =
-        source_root_dir.Append(FILE_PATH_LITERAL(
-            "headless/test/dom_tree_extraction_expected_styles.txt"));
-    std::string expected_computed_styles;
-    ASSERT_TRUE(base::ReadFileToString(expected_styles_path,
-                                       &expected_computed_styles));
-
-    std::string computed_styles_result;
-    for (size_t i = 0; i < computed_styles.size(); i++) {
-      std::string result_json;
-      base::JSONWriter::WriteWithOptions(*computed_styles[i],
-                                         base::JSONWriter::OPTIONS_PRETTY_PRINT,
-                                         &result_json);
-
-      computed_styles_result += result_json;
-    }
-
-#if BUILDFLAG(IS_WIN)
-    ASSERT_TRUE(base::RemoveChars(computed_styles_result, "\r",
-                                  &computed_styles_result));
-#endif
-
-    EXPECT_EQ(expected_computed_styles, computed_styles_result);
-    FinishAsynchronousTest();
+    EXPECT_EQ(expected_entries, actual_entries);
   }
 };
 
 // TODO(crbug.com/1090930): Fix this test on Fuchsia and re-enable.
 // NOTE: These macros expand to: DomTreeExtractionBrowserTest.RunAsyncTest
 #if BUILDFLAG(IS_FUCHSIA)
-DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(DomTreeExtractionBrowserTest);
+DISABLED_HEADLESS_DEVTOOLED_TEST_F(DomTreeExtractionBrowserTest);
 #else
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(DomTreeExtractionBrowserTest);
+HEADLESS_DEVTOOLED_TEST_F(DomTreeExtractionBrowserTest);
 #endif
 
-// This feature uses network observation and works exactly and only for
-// network::ErrorReason::BLOCKED_BY_CLIENT modifications that are initiated
-// via network::ExperimentalObserver.
 class BlockedByClient_NetworkObserver_Test
-    : public HeadlessAsyncDevTooledBrowserTest,
-      public network::ExperimentalObserver,
-      public page::Observer {
+    : public HeadlessDevTooledBrowserTest {
  public:
   void RunDevTooledTest() override {
     ASSERT_TRUE(embedded_test_server()->Start());
 
+    devtools_client_.AddEventHandler(
+        "Network.requestIntercepted",
+        base::BindRepeating(
+            &BlockedByClient_NetworkObserver_Test::OnRequestIntercepted,
+            base::Unretained(this)));
+
+    devtools_client_.AddEventHandler(
+        "Network.requestWillBeSent",
+        base::BindRepeating(
+            &BlockedByClient_NetworkObserver_Test::OnRequestWillBeSent,
+            base::Unretained(this)));
+
+    devtools_client_.AddEventHandler(
+        "Network.loadingFailed",
+        base::BindRepeating(
+            &BlockedByClient_NetworkObserver_Test::OnLoadingFailed,
+            base::Unretained(this)));
+
+    SendCommandSync(devtools_client_, "Network.enable");
+
     // Intercept all network requests.
-    devtools_client_->GetNetwork()->GetExperimental()->AddObserver(this);
-    devtools_client_->GetNetwork()->Enable();
-    std::vector<std::unique_ptr<network::RequestPattern>> patterns;
-    patterns.emplace_back(
-        network::RequestPattern::Builder().SetUrlPattern("*").Build());
-    devtools_client_->GetNetwork()->GetExperimental()->SetRequestInterception(
-        network::SetRequestInterceptionParams::Builder()
-            .SetPatterns(std::move(patterns))
-            .Build());
+    base::Value::List patterns;
+    patterns.Append(Param("urlPattern", "*"));
+    devtools_client_.SendCommand("Network.setRequestInterception",
+                                 Param("patterns", std::move(patterns)));
 
-    // For observing OnLoadEventFired.
-    devtools_client_->GetPage()->AddObserver(this);
-    devtools_client_->GetPage()->Enable();
+    devtools_client_.AddEventHandler(
+        "Page.loadEventFired",
+        base::BindRepeating(
+            &BlockedByClient_NetworkObserver_Test::OnLoadEventFired,
+            base::Unretained(this)));
 
-    devtools_client_->GetPage()->Navigate(
-        embedded_test_server()->GetURL("/resource_cancel_test.html").spec());
+    SendCommandSync(devtools_client_, "Page.enable");
+
+    devtools_client_.SendCommand(
+        "Page.navigate", Param("url", embedded_test_server()
+                                          ->GetURL("/resource_cancel_test.html")
+                                          .spec()));
   }
 
-  // Overrides network::ExperimentalObserver.
-  void OnRequestIntercepted(
-      const network::RequestInterceptedParams& params) override {
-    urls_seen_.push_back(GURL(params.GetRequest()->GetUrl()).ExtractFileName());
+  void OnRequestIntercepted(const base::Value::Dict& params) {
+    const std::string url = DictString(params, "params.request.url");
 
-    auto continue_intercept_params =
-        network::ContinueInterceptedRequestParams::Builder()
-            .SetInterceptionId(params.GetInterceptionId())
-            .Build();
+    urls_seen_.push_back(GURL(url).ExtractFileName());
+
+    base::Value::Dict continue_intercept_params;
+    continue_intercept_params.Set("interceptionId",
+                                  DictString(params, "params.interceptionId"));
+
+    // TODO(kvitekp): comment does not match the following code, review!!!
 
     // We *abort* fetching Ahem.ttf, and *fail* for test.jpg
     // to verify that both ways result in a failed loading event,
     // which we'll observe in OnLoadingFailed below.
     // Also, we abort iframe2.html because it turns out frame interception
     // uses a very different codepath than other resources.
-    if (EndsWith(params.GetRequest()->GetUrl(), "/test.jpg",
-                 base::CompareCase::SENSITIVE)) {
-      continue_intercept_params->SetErrorReason(
-          network::ErrorReason::BLOCKED_BY_CLIENT);
-    } else if (EndsWith(params.GetRequest()->GetUrl(), "/Ahem.ttf",
-                        base::CompareCase::SENSITIVE)) {
-      continue_intercept_params->SetErrorReason(
-          network::ErrorReason::BLOCKED_BY_CLIENT);
-    } else if (EndsWith(params.GetRequest()->GetUrl(), "/iframe2.html",
-                        base::CompareCase::SENSITIVE)) {
-      continue_intercept_params->SetErrorReason(
-          network::ErrorReason::BLOCKED_BY_CLIENT);
+    if (EndsWith(url, "/test.jpg", base::CompareCase::SENSITIVE)) {
+      continue_intercept_params.Set("errorReason", "BlockedByClient");
+    } else if (EndsWith(url, "/Ahem.ttf", base::CompareCase::SENSITIVE)) {
+      continue_intercept_params.Set("errorReason", "BlockedByClient");
+    } else if (EndsWith(url, "/iframe2.html", base::CompareCase::SENSITIVE)) {
+      continue_intercept_params.Set("errorReason", "BlockedByClient");
     }
 
-    devtools_client_->GetNetwork()
-        ->GetExperimental()
-        ->ContinueInterceptedRequest(std::move(continue_intercept_params));
+    devtools_client_.SendCommand("Network.continueInterceptedRequest",
+                                 std::move(continue_intercept_params));
   }
 
-  // Overrides network::ExperimentalObserver.
-  void OnRequestWillBeSent(
-      const network::RequestWillBeSentParams& params) override {
+  void OnRequestWillBeSent(const base::Value::Dict& params) {
     // Here, we just record the URLs (filenames) for each request ID, since
-    // we won't have access to them in ::OnLoadingFailed below.
-    urls_by_id_[params.GetRequestId()] =
-        GURL(params.GetRequest()->GetUrl()).ExtractFileName();
+    // we won't have access to them in OnLoadingFailed below.
+    urls_by_id_[DictString(params, "params.requestId")] =
+        GURL(DictString(params, "params.request.url")).ExtractFileName();
   }
 
-  // Overrides network::ExperimentalObserver.
-  void OnLoadingFailed(const network::LoadingFailedParams& params) override {
+  void OnLoadingFailed(const base::Value::Dict& params) {
     // Record the failed loading events so we can verify below that we
     // received the events.
-    urls_that_failed_to_load_.push_back(urls_by_id_[params.GetRequestId()]);
-    EXPECT_EQ(network::BlockedReason::INSPECTOR, params.GetBlockedReason());
+    urls_that_failed_to_load_.push_back(
+        urls_by_id_[DictString(params, "params.requestId")]);
+    EXPECT_EQ("inspector", DictString(params, "params.blockedReason"));
   }
 
-  // Overrides page::ExperimentalObserver.
-  void OnLoadEventFired(const page::LoadEventFiredParams&) override {
+  void OnLoadEventFired(const base::Value::Dict&) {
     EXPECT_THAT(urls_that_failed_to_load_,
                 UnorderedElementsAre("test.jpg", "Ahem.ttf", "iframe2.html"));
     EXPECT_THAT(urls_seen_, UnorderedElementsAre("resource_cancel_test.html",
                                                  "dom_tree_test.css",
                                                  "test.jpg", "iframe.html",
                                                  "iframe2.html", "Ahem.ttf"));
+
     FinishAsynchronousTest();
   }
 
@@ -1037,12 +884,10 @@
   std::map<std::string, std::string> urls_by_id_;
 };
 
-DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(BlockedByClient_NetworkObserver_Test);
+HEADLESS_DEVTOOLED_TEST_F(BlockedByClient_NetworkObserver_Test);
 
 class DevtoolsInterceptionWithAuthProxyTest
-    : public HeadlessAsyncDevTooledBrowserTest,
-      public network::ExperimentalObserver,
-      public page::Observer {
+    : public HeadlessDevTooledBrowserTest {
  public:
   DevtoolsInterceptionWithAuthProxyTest()
       : proxy_server_(net::SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
@@ -1051,66 +896,69 @@
 
   void SetUp() override {
     ASSERT_TRUE(proxy_server_.Start());
-    HeadlessAsyncDevTooledBrowserTest::SetUp();
+    HeadlessDevTooledBrowserTest::SetUp();
   }
 
   void RunDevTooledTest() override {
-    EXPECT_TRUE(embedded_test_server()->Start());
-    devtools_client_->GetNetwork()->GetExperimental()->AddObserver(this);
-    devtools_client_->GetNetwork()->Enable();
-    std::unique_ptr<headless::network::RequestPattern> match_all =
-        headless::network::RequestPattern::Builder().SetUrlPattern("*").Build();
-    std::vector<std::unique_ptr<headless::network::RequestPattern>> patterns;
-    patterns.push_back(std::move(match_all));
-    devtools_client_->GetNetwork()->GetExperimental()->SetRequestInterception(
-        network::SetRequestInterceptionParams::Builder()
-            .SetPatterns(std::move(patterns))
-            .Build());
+    ASSERT_TRUE(embedded_test_server()->Start());
 
-    devtools_client_->GetPage()->AddObserver(this);
+    devtools_client_.AddEventHandler(
+        "Network.requestIntercepted",
+        base::BindRepeating(
+            &DevtoolsInterceptionWithAuthProxyTest::OnRequestIntercepted,
+            base::Unretained(this)));
 
-    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-    devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
-    run_loop.Run();
+    SendCommandSync(devtools_client_, "Network.enable");
 
-    devtools_client_->GetPage()->Navigate(
-        embedded_test_server()->GetURL("/dom_tree_test.html").spec());
+    // Intercept all network requests.
+    base::Value::List patterns;
+    patterns.Append(Param("urlPattern", "*"));
+    devtools_client_.SendCommand("Network.setRequestInterception",
+                                 Param("patterns", std::move(patterns)));
+
+    devtools_client_.AddEventHandler(
+        "Page.loadEventFired",
+        base::BindRepeating(
+            &DevtoolsInterceptionWithAuthProxyTest::OnLoadEventFired,
+            base::Unretained(this)));
+
+    SendCommandSync(devtools_client_, "Page.enable");
+
+    devtools_client_.SendCommand(
+        "Page.navigate",
+        Param("url",
+              embedded_test_server()->GetURL("/dom_tree_test.html").spec()));
   }
 
-  void OnRequestIntercepted(
-      const network::RequestInterceptedParams& params) override {
-    if (params.HasAuthChallenge()) {
+  void OnRequestIntercepted(const base::Value::Dict& params) {
+    base::Value::Dict continue_intercept_params;
+    continue_intercept_params.Set("interceptionId",
+                                  DictString(params, "params.interceptionId"));
+
+    if (DictHas(params, "params.authChallenge")) {
       auth_challenge_seen_ = true;
-      devtools_client_->GetNetwork()
-          ->GetExperimental()
-          ->ContinueInterceptedRequest(
-              network::ContinueInterceptedRequestParams::Builder()
-                  .SetInterceptionId(params.GetInterceptionId())
-                  .SetAuthChallengeResponse(
-                      network::AuthChallengeResponse::Builder()
-                          .SetResponse(network::AuthChallengeResponseResponse::
-                                           PROVIDE_CREDENTIALS)
-                          .SetUsername("foo")  // These are tested by the proxy.
-                          .SetPassword("bar")
-                          .Build())
-                  .Build());
+
+      base::Value::Dict auth_challenge_response;
+      auth_challenge_response.Set("response", "ProvideCredentials");
+      auth_challenge_response.Set("username", "foo");
+      auth_challenge_response.Set("password", "bar");
+      continue_intercept_params.Set("authChallengeResponse",
+                                    std::move(auth_challenge_response));
     } else {
-      devtools_client_->GetNetwork()
-          ->GetExperimental()
-          ->ContinueInterceptedRequest(
-              network::ContinueInterceptedRequestParams::Builder()
-                  .SetInterceptionId(params.GetInterceptionId())
-                  .Build());
-      GURL url(params.GetRequest()->GetUrl());
+      GURL url(DictString(params, "params.request.url"));
       files_loaded_.insert(url.path());
     }
+
+    devtools_client_.SendCommand("Network.continueInterceptedRequest",
+                                 std::move(continue_intercept_params));
   }
 
-  void OnLoadEventFired(const page::LoadEventFiredParams&) override {
+  void OnLoadEventFired(const base::Value::Dict&) {
     EXPECT_TRUE(auth_challenge_seen_);
     EXPECT_THAT(files_loaded_,
                 ElementsAre("/Ahem.ttf", "/dom_tree_test.css",
                             "/dom_tree_test.html", "/iframe.html"));
+
     FinishAsynchronousTest();
   }
 
@@ -1135,23 +983,24 @@
 // TODO(crbug.com/1090933): Reenable on Fuchsia when fixed.
 // NOTE: This macro expands to:
 //   DevtoolsInterceptionWithAuthProxyTest.RunAsyncTest
-DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevtoolsInterceptionWithAuthProxyTest);
+DISABLED_HEADLESS_DEVTOOLED_TEST_F(DevtoolsInterceptionWithAuthProxyTest);
 #else
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevtoolsInterceptionWithAuthProxyTest);
+HEADLESS_DEVTOOLED_TEST_F(DevtoolsInterceptionWithAuthProxyTest);
 #endif
 
-class NavigatorLanguages : public HeadlessAsyncDevTooledBrowserTest {
+class NavigatorLanguages : public HeadlessDevTooledBrowserTest {
  public:
   void RunDevTooledTest() override {
-    devtools_client_->GetRuntime()->Evaluate(
-        "JSON.stringify(navigator.languages)",
-        base::BindOnce(&NavigatorLanguages::OnResult, base::Unretained(this)));
+    devtools_client_.SendCommand(
+        "Runtime.evaluate",
+        Param("expression", "JSON.stringify(navigator.languages)"),
+        base::BindOnce(&NavigatorLanguages::OnEvaluateResult,
+                       base::Unretained(this)));
   }
 
-  void OnResult(std::unique_ptr<runtime::EvaluateResult> result) {
-    EXPECT_TRUE(result->GetResult()->HasValue());
-    EXPECT_EQ("[\"en-UK\",\"DE\",\"FR\"]",
-              result->GetResult()->GetValue()->GetString());
+  void OnEvaluateResult(base::Value::Dict result) {
+    EXPECT_THAT(result, DictHasValue("result.result.value",
+                                     "[\"en-UK\",\"DE\",\"FR\"]"));
     FinishAsynchronousTest();
   }
 
@@ -1161,6 +1010,6 @@
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(NavigatorLanguages);
+HEADLESS_DEVTOOLED_TEST_F(NavigatorLanguages);
 
 }  // namespace headless
diff --git a/headless/test/headless_origin_trials_browsertest.cc b/headless/test/headless_origin_trials_browsertest.cc
index 353f2ef..f41daec 100644
--- a/headless/test/headless_origin_trials_browsertest.cc
+++ b/headless/test/headless_origin_trials_browsertest.cc
@@ -75,10 +75,10 @@
   // enable the WebComponents V0 origin trial.
   // TODO(crbug.com/1050190): Implement a permanent, sample trial so this test
   // doesn't rely on WebComponents V0, which will eventually go away.
-  EXPECT_FALSE(ResultBool(
+  EXPECT_THAT(
       EvaluateScript(web_contents,
                      "'createShadowRoot' in document.createElement('div')"),
-      "result.value"));
+      DictHasValue("result.result.value", false));
 }
 
 IN_PROC_BROWSER_TEST_F(HeadlessOriginTrialsBrowserTest,
diff --git a/headless/test/headless_printtopdf_browsertest.cc b/headless/test/headless_printtopdf_browsertest.cc
index 5cd36cb..bc0da70 100644
--- a/headless/test/headless_printtopdf_browsertest.cc
+++ b/headless/test/headless_printtopdf_browsertest.cc
@@ -6,19 +6,20 @@
 #include <string>
 #include <vector>
 
+#include "base/base64.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/json/json_writer.h"
 #include "base/numerics/safe_conversions.h"
-#include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/test/values_test_util.h"
+#include "base/values.h"
 #include "content/public/test/browser_test.h"
 #include "headless/app/headless_shell_switches.h"
-#include "headless/lib/browser/headless_web_contents_impl.h"
-#include "headless/public/devtools/domains/io.h"
-#include "headless/public/devtools/domains/page.h"
-#include "headless/public/devtools/domains/runtime.h"
 #include "headless/test/headless_browser_test.h"
+#include "headless/test/headless_browser_test_utils.h"
+#include "headless/test/headless_devtooled_browsertest.h"
 #include "pdf/pdf.h"
 #include "printing/buildflags/buildflags.h"
 #include "printing/pdf_render_settings.h"
@@ -26,6 +27,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/inspector_protocol/crdtp/dispatch.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/geometry/size_f.h"
@@ -85,7 +87,7 @@
 
 }  // namespace
 
-class HeadlessPDFPagesBrowserTest : public HeadlessAsyncDevTooledBrowserTest {
+class HeadlessPDFPagesBrowserTest : public HeadlessDevTooledBrowserTest {
  public:
   const double kPaperWidth = 10;
   const double kPaperHeight = 15;
@@ -95,38 +97,44 @@
   const int kDpi = 300;
 
   void RunDevTooledTest() override {
-    std::string height_expression = "document.body.style.height = '" +
-                                    base::NumberToString(kDocHeight) + "in'";
-    std::unique_ptr<runtime::EvaluateParams> params =
-        runtime::EvaluateParams::Builder()
-            .SetExpression("document.body.style.background = '#123456';" +
-                           height_expression)
-            .Build();
-    devtools_client_->GetRuntime()->Evaluate(
-        std::move(params),
+    std::string script =
+        "document.body.style.background = '#123456';"
+        "document.body.style.height = '" +
+        base::NumberToString(kDocHeight) + "in'";
+
+    devtools_client_.SendCommand(
+        "Runtime.evaluate", Param("expression", script),
         base::BindOnce(&HeadlessPDFPagesBrowserTest::OnPageSetupCompleted,
                        base::Unretained(this)));
   }
 
-  void OnPageSetupCompleted(std::unique_ptr<runtime::EvaluateResult> result) {
-    devtools_client_->GetPage()->GetExperimental()->PrintToPDF(
-        page::PrintToPDFParams::Builder()
-            .SetPrintBackground(true)
-            .SetPaperHeight(kPaperHeight)
-            .SetPaperWidth(kPaperWidth)
-            .SetMarginTop(0)
-            .SetMarginBottom(0)
-            .SetMarginLeft(0)
-            .SetMarginRight(0)
-            .Build(),
+  void OnPageSetupCompleted(base::Value::Dict) {
+    base::Value::Dict params;
+    params.Set("printBackground", true);
+    params.Set("paperHeight", kPaperHeight);
+    params.Set("paperWidth", kPaperWidth);
+    params.Set("marginTop", 0);
+    params.Set("marginBottom", 0);
+    params.Set("marginLeft", 0);
+    params.Set("marginRight", 0);
+
+    devtools_client_.SendCommand(
+        "Page.printToPDF", std::move(params),
         base::BindOnce(&HeadlessPDFPagesBrowserTest::OnPDFCreated,
                        base::Unretained(this)));
   }
 
-  void OnPDFCreated(std::unique_ptr<page::PrintToPDFResult> result) {
-    protocol::Binary pdf_data = result->GetData();
+  void OnPDFCreated(base::Value::Dict result) {
+    std::string pdf_data_base64 = DictString(result, "result.data");
+    ASSERT_FALSE(pdf_data_base64.empty());
+
+    std::string pdf_data;
+    ASSERT_TRUE(base::Base64Decode(pdf_data_base64, &pdf_data));
     EXPECT_GT(pdf_data.size(), 0U);
-    auto pdf_span = base::make_span(pdf_data.data(), pdf_data.size());
+
+    auto pdf_span = base::make_span(
+        reinterpret_cast<const uint8_t*>(pdf_data.data()), pdf_data.size());
+
     int num_pages;
     EXPECT_TRUE(chrome_pdf::GetPDFDocInfo(pdf_span, &num_pages, nullptr));
     EXPECT_EQ(std::ceil(kDocHeight / kPaperHeight), num_pages);
@@ -160,73 +168,82 @@
       EXPECT_EQ(0x34, page_bitmap_data[1]);  // G
       EXPECT_EQ(0x12, page_bitmap_data[2]);  // R
     }
+
     FinishAsynchronousTest();
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessPDFPagesBrowserTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessPDFPagesBrowserTest);
 
-class HeadlessPDFStreamBrowserTest : public HeadlessAsyncDevTooledBrowserTest {
+class HeadlessPDFStreamBrowserTest : public HeadlessDevTooledBrowserTest {
  public:
   const double kPaperWidth = 10;
   const double kPaperHeight = 15;
   const double kDocHeight = 50;
 
   void RunDevTooledTest() override {
-    std::string height_expression = "document.body.style.height = '" +
-                                    base::NumberToString(kDocHeight) + "in'";
-    std::unique_ptr<runtime::EvaluateParams> params =
-        runtime::EvaluateParams::Builder()
-            .SetExpression(height_expression)
-            .Build();
-    devtools_client_->GetRuntime()->Evaluate(
-        std::move(params),
+    std::string script = "document.body.style.height = '" +
+                         base::NumberToString(kDocHeight) + "in'";
+
+    devtools_client_.SendCommand(
+        "Runtime.evaluate", Param("expression", script),
         base::BindOnce(&HeadlessPDFStreamBrowserTest::OnPageSetupCompleted,
                        base::Unretained(this)));
   }
 
-  void OnPageSetupCompleted(std::unique_ptr<runtime::EvaluateResult> result) {
-    devtools_client_->GetPage()->GetExperimental()->PrintToPDF(
-        page::PrintToPDFParams::Builder()
-            .SetTransferMode(page::PrintToPDFTransferMode::RETURN_AS_STREAM)
-            .SetPaperHeight(kPaperHeight)
-            .SetPaperWidth(kPaperWidth)
-            .SetMarginTop(0)
-            .SetMarginBottom(0)
-            .SetMarginLeft(0)
-            .SetMarginRight(0)
-            .Build(),
+  void OnPageSetupCompleted(base::Value::Dict) {
+    base::Value::Dict params;
+    params.Set("transferMode", "ReturnAsStream");
+    params.Set("printBackground", true);
+    params.Set("paperHeight", kPaperHeight);
+    params.Set("paperWidth", kPaperWidth);
+    params.Set("marginTop", 0);
+    params.Set("marginBottom", 0);
+    params.Set("marginLeft", 0);
+    params.Set("marginRight", 0);
+
+    devtools_client_.SendCommand(
+        "Page.printToPDF", std::move(params),
         base::BindOnce(&HeadlessPDFStreamBrowserTest::OnPDFCreated,
                        base::Unretained(this)));
   }
 
-  void OnPDFCreated(std::unique_ptr<page::PrintToPDFResult> result) {
-    EXPECT_EQ(result->GetData().size(), 0U);
-    stream_ = result->GetStream();
-    devtools_client_->GetIO()->Read(
-        stream_, base::BindOnce(&HeadlessPDFStreamBrowserTest::OnReadChunk,
-                                base::Unretained(this)));
+  void OnPDFCreated(base::Value::Dict result) {
+    EXPECT_THAT(result, DictHasValue("result.data", std::string()));
+
+    stream_ = DictString(result, "result.stream");
+
+    devtools_client_.SendCommand(
+        "IO.read", Param("handle", std::string(stream_)),
+        base::BindOnce(&HeadlessPDFStreamBrowserTest::OnReadChunk,
+                       base::Unretained(this)));
   }
 
-  void OnReadChunk(std::unique_ptr<io::ReadResult> result) {
-    base64_data_ = base64_data_ + result->GetData();
-    if (result->GetEof()) {
+  void OnReadChunk(base::Value::Dict result) {
+    EXPECT_THAT(result, DictHasValue("result.base64Encoded", true));
+
+    const std::string base64_pdf_data_chunk = DictString(result, "result.data");
+    base64_pdf_data_.append(base64_pdf_data_chunk);
+
+    if (DictBool(result, "result.eof")) {
       OnPDFLoaded();
     } else {
-      devtools_client_->GetIO()->Read(
-          stream_, base::BindOnce(&HeadlessPDFStreamBrowserTest::OnReadChunk,
-                                  base::Unretained(this)));
+      devtools_client_.SendCommand(
+          "IO.read", Param("handle", std::string(stream_)),
+          base::BindOnce(&HeadlessPDFStreamBrowserTest::OnReadChunk,
+                         base::Unretained(this)));
     }
   }
 
   void OnPDFLoaded() {
-    EXPECT_GT(base64_data_.size(), 0U);
-    bool success;
-    protocol::Binary pdf_data =
-        protocol::Binary::fromBase64(base64_data_, &success);
-    EXPECT_TRUE(success);
+    EXPECT_GT(base64_pdf_data_.size(), 0U);
+
+    std::string pdf_data;
+    ASSERT_TRUE(base::Base64Decode(base64_pdf_data_, &pdf_data));
     EXPECT_GT(pdf_data.size(), 0U);
-    auto pdf_span = base::make_span(pdf_data.data(), pdf_data.size());
+
+    auto pdf_span = base::make_span(
+        reinterpret_cast<const uint8_t*>(pdf_data.data()), pdf_data.size());
 
     int num_pages;
     EXPECT_TRUE(chrome_pdf::GetPDFDocInfo(pdf_span, &num_pages, nullptr));
@@ -241,64 +258,80 @@
 
  private:
   std::string stream_;
-  std::string base64_data_;
+  std::string base64_pdf_data_;
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessPDFStreamBrowserTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessPDFStreamBrowserTest);
 
-class HeadlessPDFBrowserTestBase : public HeadlessAsyncDevTooledBrowserTest,
-                                   public page::Observer {
+class HeadlessPDFBrowserTestBase : public HeadlessDevTooledBrowserTest {
  public:
   void RunDevTooledTest() override {
     ASSERT_TRUE(embedded_test_server()->Start());
 
-    devtools_client_->GetPage()->AddObserver(this);
+    devtools_client_.AddEventHandler(
+        "Page.loadEventFired",
+        base::BindRepeating(&HeadlessPDFBrowserTestBase::OnLoadEventFired,
+                            base::Unretained(this)));
+    SendCommandSync(devtools_client_, "Page.enable");
 
-    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-    devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
-    run_loop.Run();
-
-    devtools_client_->GetPage()->Navigate(
-        embedded_test_server()->GetURL(GetUrl()).spec());
+    devtools_client_.SendCommand(
+        "Page.navigate",
+        Param("url", embedded_test_server()->GetURL(GetUrl()).spec()));
   }
 
-  void OnLoadEventFired(const page::LoadEventFiredParams&) override {
-    devtools_client_->GetPage()->GetExperimental()->PrintToPDF(
-        GetPrintToPDFParams(),
+  void OnLoadEventFired(const base::Value::Dict&) {
+    devtools_client_.SendCommand(
+        "Page.printToPDF", GetPrintToPDFParams(),
         base::BindOnce(&HeadlessPDFBrowserTestBase::OnPDFCreated,
                        base::Unretained(this)));
   }
 
-  void OnPDFCreated(std::unique_ptr<page::PrintToPDFResult> result) {
-    if (result) {
-      protocol::Binary pdf_data = result->GetData();
+  void OnPDFCreated(base::Value::Dict result) {
+    absl::optional<int> error_code = result.FindIntByDottedPath("error.code");
+    const std::string* error_message =
+        result.FindStringByDottedPath("error.message");
+    ASSERT_EQ(error_code.has_value(), !!error_message);
+    if (error_code || error_message) {
+      OnPDFFailure(*error_code, *error_message);
+    } else {
+      std::string pdf_data_base64 = DictString(result, "result.data");
+      ASSERT_FALSE(pdf_data_base64.empty());
+
+      std::string pdf_data;
+      ASSERT_TRUE(base::Base64Decode(pdf_data_base64, &pdf_data));
       ASSERT_GT(pdf_data.size(), 0U);
-      auto pdf_span = base::make_span(pdf_data.data(), pdf_data.size());
+
+      auto pdf_span = base::make_span(
+          reinterpret_cast<const uint8_t*>(pdf_data.data()), pdf_data.size());
       int num_pages;
       ASSERT_TRUE(chrome_pdf::GetPDFDocInfo(pdf_span, &num_pages, nullptr));
       OnPDFReady(pdf_span, num_pages);
-    } else {
-      OnPDFFailure();
     }
 
     FinishAsynchronousTest();
   }
 
   virtual const char* GetUrl() = 0;
-  virtual std::unique_ptr<page::PrintToPDFParams> GetPrintToPDFParams() {
-    return page::PrintToPDFParams::Builder()
-        .SetPrintBackground(true)
-        .SetPaperHeight(41)
-        .SetPaperWidth(41)
-        .SetMarginTop(0)
-        .SetMarginBottom(0)
-        .SetMarginLeft(0)
-        .SetMarginRight(0)
-        .Build();
+
+  virtual base::Value::Dict GetPrintToPDFParams() {
+    base::Value::Dict params;
+    params.Set("printBackground", true);
+    params.Set("paperHeight", 41);
+    params.Set("paperWidth", 41);
+    params.Set("marginTop", 0);
+    params.Set("marginBottom", 0);
+    params.Set("marginLeft", 0);
+    params.Set("marginRight", 0);
+
+    return params;
   }
+
   virtual void OnPDFReady(base::span<const uint8_t> pdf_span,
                           int num_pages) = 0;
-  virtual void OnPDFFailure() { EXPECT_TRUE(false); }
+
+  virtual void OnPDFFailure(int code, const std::string& message) {
+    ADD_FAILURE() << "code=" << code << " message: " << message;
+  }
 };
 
 class HeadlessPDFPageSizeRoundingBrowserTest
@@ -311,64 +344,73 @@
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessPDFPageSizeRoundingBrowserTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessPDFPageSizeRoundingBrowserTest);
 
 class HeadlessPDFPageRangesBrowserTest
     : public HeadlessPDFBrowserTestBase,
-      public testing::WithParamInterface<std::tuple<const char*, int>> {
+      public testing::WithParamInterface<
+          std::tuple<const char*, int, const char*>> {
  public:
   const char* GetUrl() override { return "/lorem_ipsum.html"; }
 
-  std::unique_ptr<page::PrintToPDFParams> GetPrintToPDFParams() override {
-    return page::PrintToPDFParams::Builder()
-        .SetPaperHeight(8.5)
-        .SetPaperWidth(11)
-        .SetMarginTop(0.5)
-        .SetMarginBottom(0.5)
-        .SetMarginLeft(0.5)
-        .SetMarginRight(0.5)
-        .SetPageRanges(page_ranges())
-        .Build();
+  base::Value::Dict GetPrintToPDFParams() override {
+    base::Value::Dict params;
+    params.Set("pageRanges", page_ranges());
+    params.Set("paperHeight", 8.5);
+    params.Set("paperWidth", 11);
+    params.Set("marginTop", 0.5);
+    params.Set("marginBottom", 0.5);
+    params.Set("marginLeft", 0.5);
+    params.Set("marginRight", 0.5);
+
+    return params;
   }
 
   void OnPDFReady(base::span<const uint8_t> pdf_span, int num_pages) override {
     EXPECT_THAT(num_pages, testing::Eq(expected_page_count()));
   }
 
-  void OnPDFFailure() override {
+  void OnPDFFailure(int code, const std::string& message) override {
     EXPECT_THAT(-1, testing::Eq(expected_page_count()));
+    EXPECT_THAT(
+        code, testing::Eq(static_cast<int>(crdtp::DispatchCode::SERVER_ERROR)));
+    EXPECT_THAT(message, testing::Eq(expected_error_message()));
   }
 
   std::string page_ranges() { return std::get<0>(GetParam()); }
   int expected_page_count() { return std::get<1>(GetParam()); }
+  std::string expected_error_message() { return std::get<2>(GetParam()); }
 };
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         HeadlessPDFPageRangesBrowserTest,
-                         testing::Values(std::make_tuple("1-9", 4),
-                                         std::make_tuple("1-3", 3),
-                                         std::make_tuple("2-4", 3),
-                                         std::make_tuple("4-9", 1),
-                                         std::make_tuple("5-9", -1),
-                                         std::make_tuple("9-5", -1),
-                                         std::make_tuple("abc", -1)));
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    HeadlessPDFPageRangesBrowserTest,
+    testing::Values(
+        std::make_tuple("1-9", 4, ""),
+        std::make_tuple("1-3", 3, ""),
+        std::make_tuple("2-4", 3, ""),
+        std::make_tuple("4-9", 1, ""),
+        std::make_tuple("5-9", -1, "Page range exceeds page count"),
+        std::make_tuple("9-5", -1, "Page range is invalid (start > end)"),
+        std::make_tuple("abc", -1, "Page range syntax error")));
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_P(HeadlessPDFPageRangesBrowserTest);
+HEADLESS_DEVTOOLED_TEST_P(HeadlessPDFPageRangesBrowserTest);
 
 class HeadlessPDFOOPIFBrowserTest : public HeadlessPDFBrowserTestBase {
  public:
   const char* GetUrl() override { return "/oopif.html"; }
 
-  std::unique_ptr<page::PrintToPDFParams> GetPrintToPDFParams() override {
-    return page::PrintToPDFParams::Builder()
-        .SetPrintBackground(true)
-        .SetPaperHeight(10)
-        .SetPaperWidth(15)
-        .SetMarginTop(0)
-        .SetMarginBottom(0)
-        .SetMarginLeft(0)
-        .SetMarginRight(0)
-        .Build();
+  base::Value::Dict GetPrintToPDFParams() override {
+    base::Value::Dict params;
+    params.Set("printBackground", true);
+    params.Set("paperHeight", 10);
+    params.Set("paperWidth", 15);
+    params.Set("marginTop", 0);
+    params.Set("marginBottom", 0);
+    params.Set("marginLeft", 0);
+    params.Set("marginRight", 0);
+
+    return params;
   }
 
   void OnPDFReady(base::span<const uint8_t> pdf_span, int num_pages) override {
@@ -384,7 +426,7 @@
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessPDFOOPIFBrowserTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessPDFOOPIFBrowserTest);
 
 #if BUILDFLAG(ENABLE_TAGGED_PDF)
 
@@ -578,7 +620,7 @@
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_P(HeadlessTaggedPDFBrowserTest);
+HEADLESS_DEVTOOLED_TEST_P(HeadlessTaggedPDFBrowserTest);
 
 INSTANTIATE_TEST_SUITE_P(All,
                          HeadlessTaggedPDFBrowserTest,
@@ -603,12 +645,11 @@
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_P(HeadlessTaggedPDFDisabledBrowserTest);
+HEADLESS_DEVTOOLED_TEST_P(HeadlessTaggedPDFDisabledBrowserTest);
 
 INSTANTIATE_TEST_SUITE_P(All,
                          HeadlessTaggedPDFDisabledBrowserTest,
                          ::testing::ValuesIn(kTaggedPDFTestData));
-
 #endif  // BUILDFLAG(ENABLE_TAGGED_PDF)
 
 }  // namespace headless
diff --git a/headless/test/headless_protocol_browsertest.cc b/headless/test/headless_protocol_browsertest.cc
index 3a7719e..d542df8 100644
--- a/headless/test/headless_protocol_browsertest.cc
+++ b/headless/test/headless_protocol_browsertest.cc
@@ -96,7 +96,7 @@
 
 void HeadlessProtocolBrowserTest::OnLoadEventFired(
     const base::Value::Dict& params) {
-  DCHECK_EQ(*params.FindString("method"), "Page.loadEventFired");
+  ASSERT_THAT(params, DictHasValue("method", "Page.loadEventFired"));
 
   base::ScopedAllowBlockingForTesting allow_blocking;
   base::FilePath src_dir;
@@ -146,9 +146,7 @@
     LOG(ERROR) << "Test result: " << json_params;
   }
 
-  const std::string* output =
-      params.FindStringByDottedPath("result.result.value");
-  ProcessTestResult(output ? *output : std::string());
+  ProcessTestResult(DictString(params, "result.result.value"));
 
   FinishTest();
 }
@@ -182,7 +180,7 @@
 
 void HeadlessProtocolBrowserTest::OnConsoleAPICalled(
     const base::Value::Dict& params) {
-  DCHECK_EQ(*params.FindString("method"), "Runtime.consoleAPICalled");
+  ASSERT_THAT(params, DictHasValue("method", "Runtime.consoleAPICalled"));
 
   const base::Value::List* args = params.FindListByDottedPath("params.args");
   if (!args || args->empty())
diff --git a/headless/test/headless_web_contents_browsertest.cc b/headless/test/headless_web_contents_browsertest.cc
index 8ba779e..0ddfa9b 100644
--- a/headless/test/headless_web_contents_browsertest.cc
+++ b/headless/test/headless_web_contents_browsertest.cc
@@ -6,36 +6,29 @@
 #include <string>
 #include <vector>
 
+#include "base/base64.h"
 #include "base/bind.h"
 #include "base/check_op.h"
 #include "base/command_line.h"
 #include "base/json/json_writer.h"
 #include "base/memory/raw_ptr.h"
-#include "base/run_loop.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "cc/base/switches.h"
 #include "cc/test/pixel_test_utils.h"
+#include "components/devtools/simple_devtools_protocol_client/simple_devtools_protocol_client.h"
 #include "components/viz/common/switches.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
 #include "headless/app/headless_shell_switches.h"
 #include "headless/lib/browser/headless_web_contents_impl.h"
-#include "headless/public/devtools/domains/browser.h"
-#include "headless/public/devtools/domains/dom_snapshot.h"
-#include "headless/public/devtools/domains/emulation.h"
-#include "headless/public/devtools/domains/headless_experimental.h"
-#include "headless/public/devtools/domains/io.h"
-#include "headless/public/devtools/domains/page.h"
-#include "headless/public/devtools/domains/runtime.h"
-#include "headless/public/devtools/domains/security.h"
-#include "headless/public/devtools/domains/target.h"
 #include "headless/public/headless_browser.h"
-#include "headless/public/headless_devtools_client.h"
 #include "headless/public/headless_web_contents.h"
 #include "headless/test/headless_browser_test.h"
 #include "headless/test/headless_browser_test_utils.h"
+#include "headless/test/headless_devtooled_browsertest.h"
+#include "headless/test/test_network_interceptor.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -53,7 +46,10 @@
 using testing::UnorderedElementsAre;
 using testing::UnorderedElementsAreArray;
 
+using simple_devtools_protocol_client::SimpleDevToolsProtocolClient;
+
 namespace headless {
+
 class HeadlessWebContentsTest : public HeadlessBrowserTest {};
 
 IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, Navigation) {
@@ -128,8 +124,8 @@
           .Build();
   WaitForLoadAndGainFocus(web_contents);
 
-  EXPECT_TRUE(ResultBool(EvaluateScript(web_contents, "document.hasFocus()"),
-                         "result.value"));
+  EXPECT_THAT(EvaluateScript(web_contents, "document.hasFocus()"),
+              DictHasValue("result.result.value", true));
 
   HeadlessWebContents* web_contents2 =
       browser_context->CreateWebContentsBuilder()
@@ -138,10 +134,10 @@
   WaitForLoadAndGainFocus(web_contents2);
 
   // Focus of different WebContents is independent.
-  EXPECT_TRUE(ResultBool(EvaluateScript(web_contents, "document.hasFocus()"),
-                         "result.value"));
-  EXPECT_TRUE(ResultBool(EvaluateScript(web_contents2, "document.hasFocus()"),
-                         "result.value"));
+  EXPECT_THAT(EvaluateScript(web_contents, "document.hasFocus()"),
+              DictHasValue("result.result.value", true));
+  EXPECT_THAT(EvaluateScript(web_contents2, "document.hasFocus()"),
+              DictHasValue("result.result.value", true));
 }
 
 IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, HandleSSLError) {
@@ -161,14 +157,16 @@
 }
 
 namespace {
-bool DecodePNG(const protocol::Binary& png_data, SkBitmap* bitmap) {
-  return gfx::PNGCodec::Decode(png_data.data(), png_data.size(), bitmap);
+bool DecodePNG(const std::string& png_data, SkBitmap* bitmap) {
+  return gfx::PNGCodec::Decode(
+      reinterpret_cast<const unsigned char*>(png_data.data()), png_data.size(),
+      bitmap);
 }
 }  // namespace
 
 // Parameter specifies whether --disable-gpu should be used.
 class HeadlessWebContentsScreenshotTest
-    : public HeadlessAsyncDevTooledBrowserTest,
+    : public HeadlessDevTooledBrowserTest,
       public ::testing::WithParamInterface<bool> {
  public:
   void SetUp() override {
@@ -177,32 +175,33 @@
       UseSoftwareCompositing();
       SetUpWithoutGPU();
     } else {
-      HeadlessAsyncDevTooledBrowserTest::SetUp();
+      HeadlessDevTooledBrowserTest::SetUp();
     }
   }
 
   void RunDevTooledTest() override {
-    std::unique_ptr<runtime::EvaluateParams> params =
-        runtime::EvaluateParams::Builder()
-            .SetExpression("document.body.style.background = '#0000ff'")
-            .Build();
-    devtools_client_->GetRuntime()->Evaluate(
-        std::move(params),
+    devtools_client_.SendCommand(
+        "Runtime.evaluate",
+        Param("expression", "document.body.style.background = '#0000ff'"),
         base::BindOnce(&HeadlessWebContentsScreenshotTest::OnPageSetupCompleted,
                        base::Unretained(this)));
   }
 
-  void OnPageSetupCompleted(std::unique_ptr<runtime::EvaluateResult> result) {
-    devtools_client_->GetPage()->GetExperimental()->CaptureScreenshot(
-        page::CaptureScreenshotParams::Builder().Build(),
+  void OnPageSetupCompleted(base::Value::Dict) {
+    devtools_client_.SendCommand(
+        "Page.captureScreenshot",
         base::BindOnce(&HeadlessWebContentsScreenshotTest::OnScreenshotCaptured,
                        base::Unretained(this)));
   }
 
-  void OnScreenshotCaptured(
-      std::unique_ptr<page::CaptureScreenshotResult> result) {
-    protocol::Binary png_data = result->GetData();
+  void OnScreenshotCaptured(base::Value::Dict result) {
+    std::string png_data_base64 = DictString(result, "result.data");
+    ASSERT_FALSE(png_data_base64.empty());
+
+    std::string png_data;
+    ASSERT_TRUE(base::Base64Decode(png_data_base64, &png_data));
     EXPECT_GT(png_data.size(), 0U);
+
     SkBitmap result_bitmap;
     EXPECT_TRUE(DecodePNG(png_data, &result_bitmap));
 
@@ -211,11 +210,12 @@
     SkColor actual_color = result_bitmap.getColor(400, 300);
     SkColor expected_color = SkColorSetRGB(0x00, 0x00, 0xff);
     EXPECT_EQ(expected_color, actual_color);
+
     FinishAsynchronousTest();
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_P(HeadlessWebContentsScreenshotTest);
+HEADLESS_DEVTOOLED_TEST_P(HeadlessWebContentsScreenshotTest);
 
 // Instantiate test case for both software and gpu compositing modes.
 INSTANTIATE_TEST_SUITE_P(HeadlessWebContentsScreenshotTests,
@@ -227,36 +227,33 @@
     : public HeadlessWebContentsScreenshotTest {
  public:
   void RunDevTooledTest() override {
-    browser_devtools_client_->GetBrowser()->GetExperimental()->SetWindowBounds(
-        browser::SetWindowBoundsParams::Builder()
-            .SetWindowId(
-                HeadlessWebContentsImpl::From(web_contents_)->window_id())
-            .SetBounds(browser::Bounds::Builder()
-                           .SetLeft(600)
-                           .SetTop(100)
-                           .SetWidth(800)
-                           .SetHeight(600)
-                           .Build())
-            .Build(),
+    base::Value::Dict params;
+    params.Set("windowId",
+               HeadlessWebContentsImpl::From(web_contents_)->window_id());
+    params.SetByDottedPath("bounds.left", 600);
+    params.SetByDottedPath("bounds.top", 100);
+    params.SetByDottedPath("bounds.width", 800);
+    params.SetByDottedPath("bounds.height", 600);
+
+    browser_devtools_client_.SendCommand(
+        "Browser.setWindowBounds", std::move(params),
         base::BindOnce(
             &HeadlessWebContentsScreenshotWindowPositionTest::OnWindowBoundsSet,
             base::Unretained(this)));
   }
 
-  void OnWindowBoundsSet(
-      std::unique_ptr<browser::SetWindowBoundsResult> result) {
-    EXPECT_TRUE(result);
+  void OnWindowBoundsSet(base::Value::Dict result) {
+    EXPECT_NE(result.FindDict("result"), nullptr);
     HeadlessWebContentsScreenshotTest::RunDevTooledTest();
   }
 };
 
 #if BUILDFLAG(IS_MAC) && defined(ADDRESS_SANITIZER)
 // TODO(crbug.com/1086872): Disabled due to flakiness on Mac ASAN.
-DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_P(
+DISABLED_HEADLESS_DEVTOOLED_TEST_P(
     HeadlessWebContentsScreenshotWindowPositionTest);
 #else
-HEADLESS_ASYNC_DEVTOOLED_TEST_P(
-    HeadlessWebContentsScreenshotWindowPositionTest);
+HEADLESS_DEVTOOLED_TEST_P(HeadlessWebContentsScreenshotWindowPositionTest);
 #endif
 
 // Instantiate test case for both software and gpu compositing modes.
@@ -264,52 +261,42 @@
                          HeadlessWebContentsScreenshotWindowPositionTest,
                          ::testing::Bool());
 
-class HeadlessWebContentsSecurityTest
-    : public HeadlessAsyncDevTooledBrowserTest,
-      public security::ExperimentalObserver {
- public:
-  void RunDevTooledTest() override {
-    devtools_client_->GetSecurity()->GetExperimental()->AddObserver(this);
-    devtools_client_->GetSecurity()->GetExperimental()->Enable(
-        security::EnableParams::Builder().Build());
-  }
-
-  void OnSecurityStateChanged(
-      const security::SecurityStateChangedParams& params) override {
-    EXPECT_EQ(security::SecurityState::NEUTRAL, params.GetSecurityState());
-
-    devtools_client_->GetSecurity()->GetExperimental()->Disable(
-        security::DisableParams::Builder().Build());
-    devtools_client_->GetSecurity()->GetExperimental()->RemoveObserver(this);
-    FinishAsynchronousTest();
-  }
-};
-
 // Regression test for https://crbug.com/733569.
 class HeadlessWebContentsRequestStorageQuotaTest
-    : public HeadlessAsyncDevTooledBrowserTest,
-      public runtime::Observer {
+    : public HeadlessDevTooledBrowserTest {
  public:
   void RunDevTooledTest() override {
     EXPECT_TRUE(embedded_test_server()->Start());
 
-    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-    devtools_client_->GetRuntime()->AddObserver(this);
-    devtools_client_->GetRuntime()->Enable(run_loop.QuitClosure());
-    run_loop.Run();
+    devtools_client_.AddEventHandler(
+        "Runtime.consoleAPICalled",
+        base::BindRepeating(
+            &HeadlessWebContentsRequestStorageQuotaTest::OnConsoleAPICalled,
+            base::Unretained(this)));
+    SendCommandSync(devtools_client_, "Runtime.enable");
 
     // Should not crash and call console.log() if quota request succeeds.
-    devtools_client_->GetPage()->Navigate(
-        embedded_test_server()->GetURL("/request_storage_quota.html").spec());
+    devtools_client_.SendCommand(
+        "Page.navigate",
+        Param("url", embedded_test_server()
+                         ->GetURL("/request_storage_quota.html")
+                         .spec()));
   }
 
-  void OnConsoleAPICalled(
-      const runtime::ConsoleAPICalledParams& params) override {
+  void OnConsoleAPICalled(const base::Value::Dict& params) {
+    const base::Value::List* args = params.FindListByDottedPath("params.args");
+    ASSERT_NE(args, nullptr);
+    ASSERT_GT(args->size(), 0ul);
+
+    const base::Value* value = args->front().GetDict().Find("value");
+    ASSERT_NE(value, nullptr);
+    EXPECT_EQ(value->GetString(), "success");
+
     FinishAsynchronousTest();
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessWebContentsRequestStorageQuotaTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessWebContentsRequestStorageQuotaTest);
 
 IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, BrowserTabChangeContent) {
   EXPECT_TRUE(embedded_test_server()->Start());
@@ -324,8 +311,8 @@
   std::string script = "window.location = '" +
                        embedded_test_server()->GetURL("/hello.html").spec() +
                        "';";
-  EXPECT_FALSE(
-      ResultHas(EvaluateScript(web_contents, script), "exceptionDetails"));
+  EXPECT_THAT(EvaluateScript(web_contents, script),
+              Not(DictHasKey("exceptionDetails")));
 
   // This will time out if the previous script did not work.
   EXPECT_TRUE(WaitForLoad(web_contents));
@@ -349,8 +336,8 @@
   std::string script =
       "var event = new MouseEvent('click', {'button': 1});"
       "document.getElementsByTagName('a')[0].dispatchEvent(event);";
-  EXPECT_FALSE(
-      ResultHas(EvaluateScript(web_contents, script), "exceptionDetails"));
+  EXPECT_THAT(EvaluateScript(web_contents, script),
+              Not(DictHasKey("exceptionDetails")));
   // Check that we have a new tab.
   EXPECT_EQ(2u, browser_context->GetAllWebContents().size());
 }
@@ -358,10 +345,9 @@
 // BeginFrameControl is not supported on MacOS.
 #if !BUILDFLAG(IS_MAC)
 
-class HeadlessWebContentsBeginFrameControlTest
-    : public HeadlessBrowserTest,
-      public headless_experimental::ExperimentalObserver,
-      public page::Observer {
+// TODO(kvitekp): Check to see if this could be trimmed down by using
+// Pre/PostRunAsynchronousTest().
+class HeadlessWebContentsBeginFrameControlTest : public HeadlessBrowserTest {
  public:
   HeadlessWebContentsBeginFrameControlTest() {}
 
@@ -373,35 +359,7 @@
  protected:
   virtual std::string GetTestHtmlFile() = 0;
   virtual void OnNeedsBeginFrame() {}
-  virtual void OnFrameFinished(
-      std::unique_ptr<headless_experimental::BeginFrameResult> result) {}
-
-  void RunTest() {
-    browser_devtools_client_ = HeadlessDevToolsClient::Create();
-    devtools_client_ = HeadlessDevToolsClient::Create();
-    browser_context_ = browser()->CreateBrowserContextBuilder().Build();
-    browser()->SetDefaultBrowserContext(browser_context_);
-    browser()->GetDevToolsTarget()->AttachClient(
-        browser_devtools_client_.get());
-
-    EXPECT_TRUE(embedded_test_server()->Start());
-
-    browser_devtools_client_->GetTarget()->GetExperimental()->CreateTarget(
-        target::CreateTargetParams::Builder()
-            .SetUrl("about:blank")
-            .SetWidth(200)
-            .SetHeight(200)
-            .SetEnableBeginFrameControl(true)
-            .Build(),
-        base::BindOnce(
-            &HeadlessWebContentsBeginFrameControlTest::OnCreateTargetResult,
-            base::Unretained(this)));
-
-    RunAsynchronousTest();
-
-    browser()->GetDevToolsTarget()->DetachClient(
-        browser_devtools_client_.get());
-  }
+  virtual void OnFrameFinished(base::Value::Dict result) {}
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     HeadlessBrowserTest::SetUpCommandLine(command_line);
@@ -413,44 +371,75 @@
     command_line->AppendSwitch(blink::switches::kDisableThreadedScrolling);
   }
 
-  void OnCreateTargetResult(
-      std::unique_ptr<target::CreateTargetResult> result) {
-    web_contents_ = HeadlessWebContentsImpl::From(
-        browser()->GetWebContentsForDevToolsAgentHostId(result->GetTargetId()));
+  void RunTest() {
+    browser_context_ = browser()->CreateBrowserContextBuilder().Build();
+    browser()->SetDefaultBrowserContext(browser_context_);
+    browser_devtools_client_.AttachToBrowser();
 
-    web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
-    devtools_client_->GetHeadlessExperimental()->GetExperimental()->AddObserver(
-        this);
-    devtools_client_->GetHeadlessExperimental()->GetExperimental()->Enable(
-        headless_experimental::EnableParams::Builder().Build());
+    EXPECT_TRUE(embedded_test_server()->Start());
 
-    devtools_client_->GetPage()->GetExperimental()->StopLoading(
-        page::StopLoadingParams::Builder().Build(),
+    base::Value::Dict params;
+    params.Set("url", "about:blank");
+    params.Set("width", 200);
+    params.Set("height", 200);
+    params.Set("enableBeginFrameControl", true);
+    browser_devtools_client_.SendCommand(
+        "Target.createTarget", std::move(params),
         base::BindOnce(
-            &HeadlessWebContentsBeginFrameControlTest::LoadingStopped,
+            &HeadlessWebContentsBeginFrameControlTest::OnTargetCreated,
+            base::Unretained(this)));
+
+    RunAsynchronousTest();
+
+    browser_devtools_client_.DetachClient();
+  }
+
+  void OnTargetCreated(base::Value::Dict result) {
+    const std::string targetId = DictString(result, "result.targetId");
+    ASSERT_FALSE(targetId.empty());
+
+    web_contents_ = HeadlessWebContentsImpl::From(
+        browser()->GetWebContentsForDevToolsAgentHostId(targetId));
+
+    devtools_client_.AttachToWebContents(web_contents_->web_contents());
+
+    devtools_client_.AddEventHandler(
+        "HeadlessExperimental.needsBeginFramesChanged",
+        on_needs_begin_frames_changed_handler_);
+    devtools_client_.SendCommand("HeadlessExperimental.enable");
+
+    devtools_client_.SendCommand(
+        "Page.stopLoading",
+        base::BindOnce(
+            &HeadlessWebContentsBeginFrameControlTest::OnLoadingStopped,
             base::Unretained(this)));
   }
 
-  void LoadingStopped(std::unique_ptr<page::StopLoadingResult>) {
-    devtools_client_->GetPage()->AddObserver(this);
-    devtools_client_->GetPage()->Enable(base::BindOnce(
-        &HeadlessWebContentsBeginFrameControlTest::PageDomainEnabled,
-        base::Unretained(this)));
+  void OnLoadingStopped(base::Value::Dict) {
+    devtools_client_.AddEventHandler("Page.loadEventFired",
+                                     on_load_event_fired_handler_);
+
+    devtools_client_.SendCommand(
+        "Page.enable",
+        base::BindOnce(
+            &HeadlessWebContentsBeginFrameControlTest::OnPageDomainEnabled,
+            base::Unretained(this)));
   }
 
-  void PageDomainEnabled() {
-    devtools_client_->GetPage()->Navigate(
-        page::NavigateParams::Builder()
-            .SetUrl(embedded_test_server()->GetURL(GetTestHtmlFile()).spec())
-            .Build());
+  void OnPageDomainEnabled(base::Value::Dict) {
+    devtools_client_.SendCommand(
+        "Page.navigate",
+        Param("url", embedded_test_server()->GetURL(GetTestHtmlFile()).spec()));
   }
 
-  // page::Observer implementation:
-  void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
+  void OnLoadEventFired(const base::Value::Dict& params) {
     TRACE_EVENT0("headless",
                  "HeadlessWebContentsBeginFrameControlTest::OnLoadEventFired");
-    devtools_client_->GetPage()->Disable();
-    devtools_client_->GetPage()->RemoveObserver(this);
+
+    devtools_client_.SendCommand("Page.disable");
+    devtools_client_.RemoveEventHandler("Page.loadEventFired",
+                                        on_load_event_fired_handler_);
+
     page_ready_ = true;
     if (needs_begin_frames_) {
       DCHECK(!frame_in_flight_);
@@ -458,15 +447,14 @@
     }
   }
 
-  // headless_experimental::ExperimentalObserver implementation:
-  void OnNeedsBeginFramesChanged(
-      const headless_experimental::NeedsBeginFramesChangedParams& params)
-      override {
+  void OnNeedsBeginFramesChanged(const base::Value::Dict& params) {
     TRACE_EVENT1(
         "headless",
         "HeadlessWebContentsBeginFrameControlTest::OnNeedsBeginFramesChanged",
-        "needs_begin_frames", params.GetNeedsBeginFrames());
-    needs_begin_frames_ = params.GetNeedsBeginFrames();
+        "needs_begin_frames",
+        *params.FindBoolByDottedPath("params.needsBeginFrames"));
+    needs_begin_frames_ =
+        *params.FindBoolByDottedPath("params.needsBeginFrames");
     // With full-pipeline mode and surface sync, the needs_begin_frame signal
     // should become and then always stay true.
     EXPECT_TRUE(needs_begin_frames_);
@@ -483,24 +471,21 @@
     frame_in_flight_ = true;
     num_begin_frames_++;
 
-    auto builder = headless_experimental::BeginFrameParams::Builder();
-    if (screenshot) {
-      builder.SetScreenshot(
-          headless_experimental::ScreenshotParams::Builder().Build());
-    }
+    base::Value::Dict params;
+    if (screenshot)
+      params.Set("screenshot", base::Value::Dict());
 
-    devtools_client_->GetHeadlessExperimental()->GetExperimental()->BeginFrame(
-        builder.Build(),
+    devtools_client_.SendCommand(
+        "HeadlessExperimental.beginFrame", std::move(params),
         base::BindOnce(&HeadlessWebContentsBeginFrameControlTest::FrameFinished,
                        base::Unretained(this)));
   }
 
-  void FrameFinished(
-      std::unique_ptr<headless_experimental::BeginFrameResult> result) {
-    TRACE_EVENT2("headless",
-                 "HeadlessWebContentsBeginFrameControlTest::FrameFinished",
-                 "has_damage", result->GetHasDamage(), "has_screenshot_data",
-                 result->HasScreenshotData());
+  void FrameFinished(base::Value::Dict result) {
+    TRACE_EVENT2(
+        "headless", "HeadlessWebContentsBeginFrameControlTest::FrameFinished",
+        "has_damage", DictBool(result, "result.hasDamage"),
+        "has_screenshot_data", DictString(result, "result.screenshotData"));
 
     // Post OnFrameFinished call so that any pending OnNeedsBeginFramesChanged
     // call will be executed first.
@@ -511,16 +496,15 @@
             base::Unretained(this), std::move(result)));
   }
 
-  void NotifyOnFrameFinished(
-      std::unique_ptr<headless_experimental::BeginFrameResult> result) {
+  void NotifyOnFrameFinished(base::Value::Dict result) {
     frame_in_flight_ = false;
     OnFrameFinished(std::move(result));
   }
 
   void PostFinishAsynchronousTest() {
-    devtools_client_->GetHeadlessExperimental()
-        ->GetExperimental()
-        ->RemoveObserver(this);
+    devtools_client_.RemoveEventHandler(
+        "HeadlessExperimental.needsBeginFramesChanged",
+        on_needs_begin_frames_changed_handler_);
 
     browser()->BrowserMainThread()->PostTask(
         FROM_HERE,
@@ -538,8 +522,19 @@
   bool needs_begin_frames_ = false;
   bool frame_in_flight_ = false;
   int num_begin_frames_ = 0;
-  std::unique_ptr<HeadlessDevToolsClient> browser_devtools_client_;
-  std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
+
+  SimpleDevToolsProtocolClient browser_devtools_client_;
+  SimpleDevToolsProtocolClient devtools_client_;
+
+  SimpleDevToolsProtocolClient::EventCallback on_load_event_fired_handler_ =
+      base::BindRepeating(
+          &HeadlessWebContentsBeginFrameControlTest::OnLoadEventFired,
+          base::Unretained(this));
+
+  SimpleDevToolsProtocolClient::EventCallback
+      on_needs_begin_frames_changed_handler_ = base::BindRepeating(
+          &HeadlessWebContentsBeginFrameControlTest::OnNeedsBeginFramesChanged,
+          base::Unretained(this));
 };
 
 class HeadlessWebContentsBeginFrameControlBasicTest
@@ -555,14 +550,18 @@
 
   void OnNeedsBeginFrame() override { BeginFrame(true); }
 
-  void OnFrameFinished(std::unique_ptr<headless_experimental::BeginFrameResult>
-                           result) override {
+  void OnFrameFinished(base::Value::Dict result) override {
     if (num_begin_frames_ == 1) {
       // First BeginFrame should have caused damage and have a screenshot.
-      EXPECT_TRUE(result->GetHasDamage());
-      ASSERT_TRUE(result->HasScreenshotData());
-      protocol::Binary png_data = result->GetScreenshotData();
-      EXPECT_LT(0u, png_data.size());
+      EXPECT_TRUE(DictBool(result, "result.hasDamage"));
+
+      std::string png_data_base64 = DictString(result, "result.screenshotData");
+      ASSERT_FALSE(png_data_base64.empty());
+
+      std::string png_data;
+      ASSERT_TRUE(base::Base64Decode(png_data_base64, &png_data));
+      EXPECT_GT(png_data.size(), 0U);
+
       SkBitmap result_bitmap;
       EXPECT_TRUE(DecodePNG(png_data, &result_bitmap));
       EXPECT_EQ(200, result_bitmap.width());
@@ -574,7 +573,7 @@
       DCHECK_EQ(2, num_begin_frames_);
       // Can't guarantee that the second BeginFrame didn't have damage, but it
       // should not have a screenshot.
-      EXPECT_FALSE(result->HasScreenshotData());
+      EXPECT_FALSE(result.FindStringByDottedPath("result.screenshotData"));
     }
 
     if (num_begin_frames_ < 2) {
@@ -588,7 +587,7 @@
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessWebContentsBeginFrameControlBasicTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessWebContentsBeginFrameControlBasicTest);
 
 class HeadlessWebContentsBeginFrameControlViewportTest
     : public HeadlessWebContentsBeginFrameControlTest {
@@ -607,36 +606,31 @@
   }
 
   void SetUpViewport() {
-    devtools_client_->GetEmulation()
-        ->GetExperimental()
-        ->SetDeviceMetricsOverride(
-            emulation::SetDeviceMetricsOverrideParams::Builder()
-                .SetWidth(0)
-                .SetHeight(0)
-                .SetDeviceScaleFactor(0)
-                .SetMobile(false)
-                .SetViewport(page::Viewport::Builder()
-                                 .SetX(200)
-                                 .SetY(200)
-                                 .SetWidth(100)
-                                 .SetHeight(100)
-                                 .SetScale(3)
-                                 .Build())
-                .Build(),
-            base::BindOnce(&HeadlessWebContentsBeginFrameControlViewportTest::
-                               SetDeviceMetricsOverrideDone,
-                           base::Unretained(this)));
+    base::Value::Dict params;
+    params.Set("width", 0);
+    params.Set("height", 0);
+    params.Set("deviceScaleFactor", 0);
+    params.Set("mobile", false);
+    params.SetByDottedPath("viewport.x", 200);
+    params.SetByDottedPath("viewport.y", 200);
+    params.SetByDottedPath("viewport.width", 100);
+    params.SetByDottedPath("viewport.height", 100);
+    params.SetByDottedPath("viewport.scale", 3);
+
+    devtools_client_.SendCommand(
+        "Emulation.setDeviceMetricsOverride", std::move(params),
+        base::BindOnce(&HeadlessWebContentsBeginFrameControlViewportTest::
+                           OnSetDeviceMetricsOverrideDone,
+                       base::Unretained(this)));
   }
 
-  void SetDeviceMetricsOverrideDone(
-      std::unique_ptr<emulation::SetDeviceMetricsOverrideResult> result) {
-    EXPECT_TRUE(result);
+  void OnSetDeviceMetricsOverrideDone(base::Value::Dict result) {
+    EXPECT_THAT(result, DictHasKey("result"));
     // Take a screenshot in the second BeginFrame.
     BeginFrame(true);
   }
 
-  void OnFrameFinished(std::unique_ptr<headless_experimental::BeginFrameResult>
-                           result) override {
+  void OnFrameFinished(base::Value::Dict result) override {
     if (num_begin_frames_ == 1) {
       SetUpViewport();
       return;
@@ -645,25 +639,28 @@
     DCHECK_EQ(2, num_begin_frames_);
     // Second BeginFrame should have a screenshot of the configured viewport and
     // of the correct size.
-    EXPECT_TRUE(result->GetHasDamage());
-    EXPECT_TRUE(result->HasScreenshotData());
-    if (result->HasScreenshotData()) {
-      protocol::Binary png_data = result->GetScreenshotData();
-      EXPECT_LT(0u, png_data.size());
-      SkBitmap result_bitmap;
-      EXPECT_TRUE(DecodePNG(png_data, &result_bitmap));
+    EXPECT_TRUE(*result.FindBoolByDottedPath("result.hasDamage"));
 
-      // Expext a 300x300 bitmap that is all blue.
-      SkBitmap expected_bitmap;
-      SkImageInfo info;
-      expected_bitmap.allocPixels(
-          SkImageInfo::MakeN32(300, 300, kOpaque_SkAlphaType), /*row_bytes=*/0);
-      expected_bitmap.eraseColor(SkColorSetRGB(0x00, 0x00, 0xff));
+    std::string png_data_base64 = DictString(result, "result.screenshotData");
+    ASSERT_FALSE(png_data_base64.empty());
 
-      EXPECT_TRUE(
-          cc::MatchesBitmap(result_bitmap, expected_bitmap,
-                            cc::ExactPixelComparator(/*discard_alpha=*/false)));
-    }
+    std::string png_data;
+    ASSERT_TRUE(base::Base64Decode(png_data_base64, &png_data));
+    ASSERT_GT(png_data.size(), 0ul);
+
+    SkBitmap result_bitmap;
+    EXPECT_TRUE(DecodePNG(png_data, &result_bitmap));
+
+    // Expect a 300x300 bitmap that is all blue.
+    SkBitmap expected_bitmap;
+    SkImageInfo info;
+    expected_bitmap.allocPixels(
+        SkImageInfo::MakeN32(300, 300, kOpaque_SkAlphaType), /*rowBytes=*/0);
+    expected_bitmap.eraseColor(SkColorSetRGB(0x00, 0x00, 0xff));
+
+    EXPECT_TRUE(
+        cc::MatchesBitmap(result_bitmap, expected_bitmap,
+                          cc::ExactPixelComparator(/*discard_alpha=*/false)));
 
     // Post completion to avoid deleting the WebContents on the same callstack
     // as frame finished callback.
@@ -671,40 +668,41 @@
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(
-    HeadlessWebContentsBeginFrameControlViewportTest);
+HEADLESS_DEVTOOLED_TEST_F(HeadlessWebContentsBeginFrameControlViewportTest);
 
 #endif  // !BUILDFLAG(IS_MAC)
 
-class CookiesEnabled : public HeadlessAsyncDevTooledBrowserTest,
-                       page::Observer {
+class CookiesEnabled : public HeadlessDevTooledBrowserTest {
  public:
   void RunDevTooledTest() override {
-    devtools_client_->GetPage()->AddObserver(this);
-    devtools_client_->GetPage()->Enable();
-
     EXPECT_TRUE(embedded_test_server()->Start());
-    devtools_client_->GetPage()->Navigate(
-        embedded_test_server()->GetURL("/cookie.html").spec());
+
+    devtools_client_.AddEventHandler(
+        "Page.loadEventFired",
+        base::BindRepeating(&CookiesEnabled::OnLoadEventFired,
+                            base::Unretained(this)));
+    devtools_client_.SendCommand("Page.enable");
+
+    devtools_client_.SendCommand(
+        "Page.navigate",
+        Param("url", embedded_test_server()->GetURL("/cookie.html").spec()));
   }
 
-  // page::Observer implementation:
-  void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
-    devtools_client_->GetRuntime()->Evaluate(
-        "window.test_result",
-        base::BindOnce(&CookiesEnabled::OnResult, base::Unretained(this)));
+  void OnLoadEventFired(const base::Value::Dict& params) {
+    devtools_client_.SendCommand(
+        "Runtime.evaluate", Param("expression", "window.test_result"),
+        base::BindOnce(&CookiesEnabled::OnEvaluateResult,
+                       base::Unretained(this)));
   }
 
-  void OnResult(std::unique_ptr<runtime::EvaluateResult> result) {
-    EXPECT_TRUE(result->GetResult()->HasValue());
-    const base::Value* value = result->GetResult()->GetValue();
-    EXPECT_TRUE(value->is_string());
-    EXPECT_EQ("0", value->GetString());
+  void OnEvaluateResult(base::Value::Dict result) {
+    EXPECT_EQ(DictString(result, "result.result.value"), "0");
+
     FinishAsynchronousTest();
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(CookiesEnabled);
+HEADLESS_DEVTOOLED_TEST_F(CookiesEnabled);
 
 namespace {
 const char* kPageWhichOpensAWindow = R"(
@@ -729,22 +727,36 @@
 )";
 }  // namespace
 
-class WebContentsOpenTest : public runtime::Observer,
-                            public HeadlessAsyncDevTooledBrowserTest {
+class WebContentsOpenTest : public HeadlessDevTooledBrowserTest {
  public:
+  void PreRunAsynchronousTest() override {
+    interceptor_ = std::make_unique<TestNetworkInterceptor>();
+  }
+
+  void PostRunAsynchronousTest() override { interceptor_.reset(); }
+
   void RunDevTooledTest() override {
+    DCHECK(interceptor_);
+
     interceptor_->InsertResponse("http://foo.com/index.html",
                                  {kPageWhichOpensAWindow, "text/html"});
     interceptor_->InsertResponse("http://foo.com/page2.html",
                                  {kPage2, "text/html"});
 
-    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-    devtools_client_->GetRuntime()->AddObserver(this);
-    devtools_client_->GetRuntime()->Enable(run_loop.QuitClosure());
-    run_loop.Run();
+    devtools_client_.AddEventHandler(
+        "Runtime.consoleAPICalled",
+        base::BindRepeating(&WebContentsOpenTest::OnConsoleAPICalled,
+                            base::Unretained(this)));
+    SendCommandSync(devtools_client_, "Runtime.enable");
 
-    devtools_client_->GetPage()->Navigate("http://foo.com/index.html");
+    devtools_client_.SendCommand("Page.navigate",
+                                 Param("url", "http://foo.com/index.html"));
   }
+
+  virtual void OnConsoleAPICalled(const base::Value::Dict& params) {}
+
+ protected:
+  std::unique_ptr<TestNetworkInterceptor> interceptor_;
 };
 
 class DontBlockWebContentsOpenTest : public WebContentsOpenTest {
@@ -754,8 +766,7 @@
     builder.SetBlockNewWebContents(false);
   }
 
-  void OnConsoleAPICalled(
-      const runtime::ConsoleAPICalledParams& params) override {
+  void OnConsoleAPICalled(const base::Value::Dict& params) override {
     EXPECT_THAT(
         interceptor_->urls_requested(),
         ElementsAre("http://foo.com/index.html", "http://foo.com/page2.html"));
@@ -763,7 +774,7 @@
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(DontBlockWebContentsOpenTest);
+HEADLESS_DEVTOOLED_TEST_F(DontBlockWebContentsOpenTest);
 
 class BlockWebContentsOpenTest : public WebContentsOpenTest {
  public:
@@ -772,14 +783,13 @@
     builder.SetBlockNewWebContents(true);
   }
 
-  void OnConsoleAPICalled(
-      const runtime::ConsoleAPICalledParams& params) override {
+  void OnConsoleAPICalled(const base::Value::Dict& params) override {
     EXPECT_THAT(interceptor_->urls_requested(),
                 ElementsAre("http://foo.com/index.html"));
     FinishAsynchronousTest();
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(BlockWebContentsOpenTest);
+HEADLESS_DEVTOOLED_TEST_F(BlockWebContentsOpenTest);
 
 }  // namespace headless
diff --git a/infra/config/generated/builders/ci/GPU FYI Win Builder/properties.json b/infra/config/generated/builders/ci/GPU FYI Win Builder/properties.json
index a6a7b71f..86053e9 100644
--- a/infra/config/generated/builders/ci/GPU FYI Win Builder/properties.json
+++ b/infra/config/generated/builders/ci/GPU FYI Win Builder/properties.json
@@ -1,4 +1,82 @@
 {
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "GPU FYI Win Builder",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 32
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              }
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Win10 FYI x86 Release (NVIDIA)",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 32
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "GPU FYI Win Builder",
+                "project": "chromium"
+              },
+              "run_tests_serially": true
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "GPU FYI Win Builder",
+          "project": "chromium"
+        }
+      ],
+      "builder_ids_in_scope_for_testing": [
+        {
+          "bucket": "ci",
+          "builder": "Win10 FYI x86 Release (NVIDIA)",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "gpu-fyi-try-win10-nvidia-rel-32",
+          "group": "tryserver.chromium.win"
+        }
+      ]
+    }
+  },
   "$build/reclient": {
     "instance": "rbe-chromium-trusted",
     "jobs": 80,
diff --git "a/infra/config/generated/builders/ci/Win10 FYI x86 Release \050NVIDIA\051/properties.json" "b/infra/config/generated/builders/ci/Win10 FYI x86 Release \050NVIDIA\051/properties.json"
index ee8f716..48355a7 100644
--- "a/infra/config/generated/builders/ci/Win10 FYI x86 Release \050NVIDIA\051/properties.json"
+++ "b/infra/config/generated/builders/ci/Win10 FYI x86 Release \050NVIDIA\051/properties.json"
@@ -1,4 +1,75 @@
 {
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "GPU FYI Win Builder",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 32
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              }
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Win10 FYI x86 Release (NVIDIA)",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 32
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "GPU FYI Win Builder",
+                "project": "chromium"
+              },
+              "run_tests_serially": true
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "Win10 FYI x86 Release (NVIDIA)",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "gpu-fyi-try-win10-nvidia-rel-32",
+          "group": "tryserver.chromium.win"
+        }
+      ]
+    }
+  },
   "$recipe_engine/resultdb/test_presentation": {
     "column_keys": [],
     "grouping_keys": [
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-rel-32/properties.json b/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-rel-32/properties.json
index 07097fb..2f8eb3c8 100644
--- a/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-rel-32/properties.json
+++ b/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-rel-32/properties.json
@@ -1,4 +1,76 @@
 {
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "GPU FYI Win Builder",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 32
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              }
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Win10 FYI x86 Release (NVIDIA)",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 32
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "GPU FYI Win Builder",
+                "project": "chromium"
+              },
+              "run_tests_serially": true
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "GPU FYI Win Builder",
+          "project": "chromium"
+        }
+      ],
+      "builder_ids_in_scope_for_testing": [
+        {
+          "bucket": "ci",
+          "builder": "Win10 FYI x86 Release (NVIDIA)",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
   "$build/goma": {
     "enable_ats": false,
     "rpc_extra_params": "?prod",
diff --git a/infra/config/generated/builders/try/linux-rel-ml/properties.json b/infra/config/generated/builders/try/linux-rel-ml/properties.json
index 7176931..9eb08bb4 100644
--- a/infra/config/generated/builders/try/linux-rel-ml/properties.json
+++ b/infra/config/generated/builders/try/linux-rel-ml/properties.json
@@ -148,11 +148,6 @@
       }
     }
   },
-  "$build/goma": {
-    "enable_ats": true,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-untrusted",
     "metrics_project": "chromium-reclient-metrics"
diff --git a/infra/config/generated/builders/try/linux-webkit-msan-rel/properties.json b/infra/config/generated/builders/try/linux-webkit-msan-rel/properties.json
index 50cdf3d..bd4970a 100644
--- a/infra/config/generated/builders/try/linux-webkit-msan-rel/properties.json
+++ b/infra/config/generated/builders/try/linux-webkit-msan-rel/properties.json
@@ -38,11 +38,6 @@
       ]
     }
   },
-  "$build/goma": {
-    "enable_ats": true,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-untrusted",
     "metrics_project": "chromium-reclient-metrics"
diff --git a/infra/config/generated/builders/try/linux-wpt-fyi-rel/properties.json b/infra/config/generated/builders/try/linux-wpt-fyi-rel/properties.json
index 4c00daf..1011d0f 100644
--- a/infra/config/generated/builders/try/linux-wpt-fyi-rel/properties.json
+++ b/infra/config/generated/builders/try/linux-wpt-fyi-rel/properties.json
@@ -1,9 +1,4 @@
 {
-  "$build/goma": {
-    "enable_ats": true,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-untrusted",
     "metrics_project": "chromium-reclient-metrics"
diff --git a/infra/config/generated/builders/try/linux-wpt-identity-fyi-rel/properties.json b/infra/config/generated/builders/try/linux-wpt-identity-fyi-rel/properties.json
index 4c00daf..1011d0f 100644
--- a/infra/config/generated/builders/try/linux-wpt-identity-fyi-rel/properties.json
+++ b/infra/config/generated/builders/try/linux-wpt-identity-fyi-rel/properties.json
@@ -1,9 +1,4 @@
 {
-  "$build/goma": {
-    "enable_ats": true,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-untrusted",
     "metrics_project": "chromium-reclient-metrics"
diff --git a/infra/config/generated/builders/try/linux-wpt-input-fyi-rel/properties.json b/infra/config/generated/builders/try/linux-wpt-input-fyi-rel/properties.json
index 4c00daf..1011d0f 100644
--- a/infra/config/generated/builders/try/linux-wpt-input-fyi-rel/properties.json
+++ b/infra/config/generated/builders/try/linux-wpt-input-fyi-rel/properties.json
@@ -1,9 +1,4 @@
 {
-  "$build/goma": {
-    "enable_ats": true,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-untrusted",
     "metrics_project": "chromium-reclient-metrics"
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
index 908765a..5fcce30 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -950,6 +950,21 @@
 
 ci.thin_tester(
     name = "Win10 FYI x86 Release (NVIDIA)",
+    builder_spec = builder_config.builder_spec(
+        execution_mode = builder_config.execution_mode.TEST,
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = [
+                "mb",
+            ],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 32,
+        ),
+        run_tests_serially = True,
+    ),
     console_view_entry = consoles.console_view_entry(
         category = "Windows|10|x86|Nvidia",
         short_name = "rel",
@@ -959,6 +974,19 @@
 
 gpu_fyi_windows_builder(
     name = "GPU FYI Win Builder",
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = [
+                "mb",
+            ],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 32,
+        ),
+    ),
     console_view_entry = consoles.console_view_entry(
         category = "Windows|Builder|Release",
         short_name = "x86",
diff --git a/infra/config/subprojects/chromium/gpu.try.star b/infra/config/subprojects/chromium/gpu.try.star
index 55a593c..8d642b1 100644
--- a/infra/config/subprojects/chromium/gpu.try.star
+++ b/infra/config/subprojects/chromium/gpu.try.star
@@ -398,6 +398,10 @@
 gpu_win_builder(
     name = "gpu-fyi-try-win10-nvidia-rel-32",
     pool = "luci.chromium.gpu.win10.nvidia.try",
+    mirrors = [
+        "ci/GPU FYI Win Builder",
+        "ci/Win10 FYI x86 Release (NVIDIA)",
+    ],
 )
 
 gpu_win_builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
index d827453..5638d40 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
@@ -282,6 +282,7 @@
     mirrors = [
         "ci/WebKit Linux MSAN",
     ],
+    goma_backend = None,
 )
 
 try_.builder(
@@ -293,14 +294,17 @@
 
 try_.builder(
     name = "linux-wpt-fyi-rel",
+    goma_backend = None,
 )
 
 try_.builder(
     name = "linux-wpt-identity-fyi-rel",
+    goma_backend = None,
 )
 
 try_.builder(
     name = "linux-wpt-input-fyi-rel",
+    goma_backend = None,
 )
 
 try_.builder(
@@ -728,4 +732,5 @@
     cores = 16,
     builderless = False,
     experiments = {"chromium_rts.experimental_model": 100},
+    goma_backend = None,
 )
diff --git a/ios/chrome/browser/sync/sync_service_factory_unittest.cc b/ios/chrome/browser/sync/sync_service_factory_unittest.cc
index 9fad64c..db30293 100644
--- a/ios/chrome/browser/sync/sync_service_factory_unittest.cc
+++ b/ios/chrome/browser/sync/sync_service_factory_unittest.cc
@@ -53,7 +53,7 @@
  protected:
   // Returns the collection of default datatypes.
   syncer::ModelTypeSet DefaultDatatypes() {
-    static_assert(43 == syncer::GetNumModelTypes(),
+    static_assert(44 == syncer::GetNumModelTypes(),
                   "When adding a new type, you probably want to add it here as "
                   "well (assuming it is already enabled).");
 
diff --git a/ios/chrome/browser/variations/ios_chrome_variations_seed_fetcher.mm b/ios/chrome/browser/variations/ios_chrome_variations_seed_fetcher.mm
index d46a3693..6472aec 100644
--- a/ios/chrome/browser/variations/ios_chrome_variations_seed_fetcher.mm
+++ b/ios/chrome/browser/variations/ios_chrome_variations_seed_fetcher.mm
@@ -50,7 +50,7 @@
 }
 
 // Whether the current binary should fetch Finch seed for experiment purpose.
-@property(nonatomic, readonly) BOOL fetchingEnabled;
+@property(nonatomic, assign) BOOL fetchingEnabled;
 
 // The URL of the variations server, including query parameters that identifies
 // the request initiator.
@@ -133,8 +133,8 @@
 
     if (base::StartsWith(arg, url_switch)) {
       _variationsDomain = arg.substr(url_switch.size());
-      if (!_fetchingEnabled && !_variationsDomain.empty()) {
-        _fetchingEnabled = YES;
+      if (!self.fetchingEnabled && !_variationsDomain.empty()) {
+        self.fetchingEnabled = YES;
       }
     } else if (base::StartsWith(arg, channel_switch)) {
       _forcedChannel = arg.substr(channel_switch.size());
diff --git a/ios/chrome/browser/variations/ios_chrome_variations_seed_fetcher_unittest.mm b/ios/chrome/browser/variations/ios_chrome_variations_seed_fetcher_unittest.mm
index 24d5c54..f3c76ff 100644
--- a/ios/chrome/browser/variations/ios_chrome_variations_seed_fetcher_unittest.mm
+++ b/ios/chrome/browser/variations/ios_chrome_variations_seed_fetcher_unittest.mm
@@ -56,6 +56,9 @@
 // test cases. This avoids writing to global variable for the downloaded seed.
 @interface TestVariationsSeedFetcher : IOSChromeVariationsSeedFetcher
 
+// Exposure of parent class property.
+@property(nonatomic, assign) BOOL fetchingEnabled;
+
 // Initializer with designated arguments that substitutes for command line args.
 - (instancetype)initWithCommandLineArgsForTesting:
     (NSArray<NSString*>*)arguments;
@@ -68,6 +71,9 @@
     (NSArray<NSString*>*)arguments {
   self = [super init];
   if (self) {
+    self.fetchingEnabled = NO;
+    // This overrides `self.fetchingEnabled` if variations server URL is set in
+    // the argument.
     [self applySwitchesFromArguments:arguments];
   }
   return self;
@@ -152,7 +158,7 @@
       [[TestVariationsSeedFetcher alloc] initWithCommandLineArgsForTesting:@[]];
   fetcher.delegate = delegate;
   [fetcher startSeedFetch];
-  base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.1));
+  base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.05));
   EXPECT_OCMOCK_VERIFY(delegate);
 }
 
@@ -183,7 +189,7 @@
   TestVariationsSeedFetcher* fetcher = [[TestVariationsSeedFetcher alloc]
       initWithCommandLineArgsForTesting:@[ argument ]];
   [fetcher startSeedFetch];
-  base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.1));
+  base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.05));
   EXPECT_OCMOCK_VERIFY(mockURLSession);
 }
 
@@ -207,12 +213,12 @@
   fetcher.delegate = delegate;
   fetcher.startTimeOfOngoingSeedRequest = [NSDate now];
   [fetcher onSeedRequestCompletedWithData:nil response:responseOk error:error];
-  base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.1));
+  base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.05));
   EXPECT_EQ([IOSChromeVariationsSeedStore popSeed], nil);
   EXPECT_EQ(fetcher.startTimeOfOngoingSeedRequest, nil);
   fetcher.startTimeOfOngoingSeedRequest = [NSDate now];
   [fetcher onSeedRequestCompletedWithData:nil response:responseError error:nil];
-  base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.1));
+  base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.05));
   EXPECT_EQ([IOSChromeVariationsSeedStore popSeed], nil);
   EXPECT_EQ(fetcher.startTimeOfOngoingSeedRequest, nil);
   EXPECT_OCMOCK_VERIFY(delegate);
@@ -245,7 +251,7 @@
   [fetcherWithSeed onSeedRequestCompletedWithData:nil
                                          response:response
                                             error:nil];
-  base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.1));
+  base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.05));
   EXPECT_EQ([fetcherWithSeed startTimeOfOngoingSeedRequest], nil);
   EXPECT_EQ([IOSChromeVariationsSeedStore popSeed], expectedSeed);
   EXPECT_OCMOCK_VERIFY(delegate);
@@ -272,7 +278,7 @@
   [fetcherWithSeed onSeedRequestCompletedWithData:nil
                                          response:response
                                             error:nil];
-  base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.1));
+  base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.05));
   EXPECT_EQ([fetcherWithSeed startTimeOfOngoingSeedRequest], nil);
   EXPECT_EQ([IOSChromeVariationsSeedStore popSeed], nil);
   EXPECT_OCMOCK_VERIFY(delegate);
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index 6530e255..eb8859f5 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-dde22e33c62acb6a0fd0284a2b765c78dae7736d
\ No newline at end of file
+c52ed4976e8ec08ff04e34d92e15d437714af78c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index f260641..e52f0bd6 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-0877c54347328e4c05eb00e86602531bc266a5e3
\ No newline at end of file
+b0f272180d465f5b3373e6d51cb610d282bd7b43
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 91e0f1be..1989611 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-5c5347e6ce81fa4f9326336c18c420275a842b3a
\ No newline at end of file
+c02346aa7dc4becf07ca8f18bfebf75ec9675db0
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 4a15b21..a2eea63a 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-e940bddddacda1bf7110f8d1b0eaed01929caf0b
\ No newline at end of file
+525d82b0fe7565213918ed5e9dcfab5329ce8782
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index b36163b..1c126b1 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-afb88a6af107dfc62f193a232f177ebc33096900
\ No newline at end of file
+e50913370bb273c69701dab279d7d3e7e5057fde
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 940b73e2..120fe300 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-397e3b367a155ae6e771b074e112a03ac5c4d7d7
\ No newline at end of file
+1ca677bf69f0e991a8bbe1c44df01df33db2305c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index 8a92ea90..089c9d61 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-43942db2cb7cc955b0faf7545ea13627717a66e8
\ No newline at end of file
+9cb1602e055e0d828d7edfded9f0f0a237a5a49e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index edd8523..1a0f541 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-72b120d9f22f69ea4526d219eb4c6f66769fc8fe
\ No newline at end of file
+51c6fe4fd3caee58cbc006c6991e6994ecc02532
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index 246b5f7..bd405b3 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-f2b7c85eb65c78c5c33a0b13608d2fbde46de314
\ No newline at end of file
+17d3962c23897542fd27f59dc94bac7cf44702c8
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index 68640de..b4ae195 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-23f518454c61517022b3c66b7917ea88a1878673
\ No newline at end of file
+bbb3f248be7d13a3f09d4f8104ff49cfcf931ce6
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index dc645f81..ae0412e4 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-208849510c567832422b8edbc82a3882fcff0fe2
\ No newline at end of file
+7dc79c4d685295f798be86961e6b93bbf94fe9ac
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index b15d512..b95b252 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-951e6f58d4e1c5e5e96f8031840a0169a0200020
\ No newline at end of file
+9cb0b719698d5505f373b1faae45ae67762ca0d2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 9daaa9a..38cfb5c 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-eb060be022144de8f94074472acaa007664094fc
\ No newline at end of file
+9906581541c21c3b2752abad21c0c210a4963bf3
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index ca438d8..14e53e6 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-4af7d54ce4def746404f95c0f0d36d62bdc25427
\ No newline at end of file
+2fefdd321fa6644a82a51c8a4b05d6a0053f9401
\ No newline at end of file
diff --git a/media/gpu/chromeos/image_processor_factory.cc b/media/gpu/chromeos/image_processor_factory.cc
index ede557d..cc1379fb 100644
--- a/media/gpu/chromeos/image_processor_factory.cc
+++ b/media/gpu/chromeos/image_processor_factory.cc
@@ -167,7 +167,6 @@
     const std::vector<PixelLayoutCandidate>& input_candidates,
     const gfx::Rect& input_visible_rect,
     const gfx::Size& output_size,
-    size_t num_buffers,
     scoped_refptr<base::SequencedTaskRunner> client_task_runner,
     ImageProcessorFactory::PickFormatCB out_format_picker,
     ImageProcessor::ErrorCB error_cb) {
@@ -202,7 +201,6 @@
     const std::vector<PixelLayoutCandidate>& input_candidates,
     const gfx::Rect& input_visible_rect,
     const gfx::Size& output_size,
-    size_t num_buffers,
     scoped_refptr<base::SequencedTaskRunner> client_task_runner,
     ImageProcessorFactory::PickFormatCB out_format_picker,
     ImageProcessor::ErrorCB error_cb) {
@@ -283,15 +281,15 @@
 #elif BUILDFLAG(USE_V4L2_CODEC)
   if (base::FeatureList::IsEnabled(media::kPreferGLImageProcessor)) {
     auto processor = CreateGLImageProcessorWithInputCandidates(
-        input_candidates, input_visible_rect, output_size, num_buffers,
-        client_task_runner, out_format_picker, error_cb);
+        input_candidates, input_visible_rect, output_size, client_task_runner,
+        out_format_picker, error_cb);
     if (processor)
       return processor;
   }
   if (base::FeatureList::IsEnabled(media::kPreferLibYuvImageProcessor)) {
     auto processor = CreateLibYUVImageProcessorWithInputCandidates(
-        input_candidates, input_visible_rect, output_size, num_buffers,
-        client_task_runner, out_format_picker, error_cb);
+        input_candidates, input_visible_rect, output_size, client_task_runner,
+        out_format_picker, error_cb);
     if (processor)
       return processor;
   }
diff --git a/mojo/core/embedder/BUILD.gn b/mojo/core/embedder/BUILD.gn
index 32327cd..3628a563 100644
--- a/mojo/core/embedder/BUILD.gn
+++ b/mojo/core/embedder/BUILD.gn
@@ -18,11 +18,7 @@
 
   defines = [ "IS_MOJO_CORE_EMBEDDER_IMPL" ]
 
-  public_deps = [
-    "//base",
-    "//mojo/public/cpp/platform",
-    "//third_party/ipcz/src:ipcz_chromium",
-  ]
+  public_deps = [ "//base" ]
 
   deps = [
     ":features",
diff --git a/mojo/core/embedder/embedder.cc b/mojo/core/embedder/embedder.cc
index 234f788f..e128732 100644
--- a/mojo/core/embedder/embedder.cc
+++ b/mojo/core/embedder/embedder.cc
@@ -21,7 +21,6 @@
 #include "mojo/core/entrypoints.h"
 #include "mojo/core/ipcz_api.h"
 #include "mojo/core/ipcz_driver/base_shared_memory_service.h"
-#include "mojo/core/ipcz_driver/driver.h"
 #include "mojo/core/ipcz_driver/transport.h"
 #include "mojo/core/node_controller.h"
 #include "mojo/public/c/system/thunks.h"
@@ -142,32 +141,7 @@
 
 void InstallMojoIpczBaseSharedMemoryHooks() {
   DCHECK(IsMojoIpczEnabled());
-  ipcz_driver::BaseSharedMemoryService::InstallHooks();
-}
-
-const IpczAPI& GetIpczAPIForMojo() {
-  return GetIpczAPI();
-}
-
-const IpczDriver& GetIpczDriverForMojo() {
-  return ipcz_driver::kDriver;
-}
-
-IpczDriverHandle CreateIpczTransportFromEndpoint(
-    mojo::PlatformChannelEndpoint endpoint,
-    const TransportEndpointTypes& endpoint_types,
-    base::Process remote_process) {
-  auto transport = ipcz_driver::Transport::Create(
-      {
-          .source = endpoint_types.local_is_broker
-                        ? ipcz_driver::Transport::kBroker
-                        : ipcz_driver::Transport::kNonBroker,
-          .destination = endpoint_types.remote_is_broker
-                             ? ipcz_driver::Transport::kBroker
-                             : ipcz_driver::Transport::kNonBroker,
-      },
-      std::move(endpoint), std::move(remote_process));
-  return ipcz_driver::ObjectBase::ReleaseAsHandle(std::move(transport));
+  mojo::core::ipcz_driver::BaseSharedMemoryService::InstallHooks();
 }
 
 }  // namespace core
diff --git a/mojo/core/embedder/embedder.h b/mojo/core/embedder/embedder.h
index 9d5d588..c748281 100644
--- a/mojo/core/embedder/embedder.h
+++ b/mojo/core/embedder/embedder.h
@@ -9,13 +9,10 @@
 
 #include "base/component_export.h"
 #include "base/memory/ref_counted.h"
-#include "base/process/process.h"
 #include "base/process/process_handle.h"
 #include "base/task/single_thread_task_runner.h"
 #include "build/build_config.h"
 #include "mojo/core/embedder/configuration.h"
-#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
-#include "third_party/ipcz/include/ipcz/ipcz.h"
 
 namespace mojo {
 namespace core {
@@ -62,25 +59,6 @@
 COMPONENT_EXPORT(MOJO_CORE_EMBEDDER)
 void InstallMojoIpczBaseSharedMemoryHooks();
 
-// These functions expose the IpczAPI and IpczDriver structures used internally
-// by the Mojo Core implementation when MojoIpcz is enabled.
-COMPONENT_EXPORT(MOJO_CORE_EMBEDDER) const IpczAPI& GetIpczAPIForMojo();
-COMPONENT_EXPORT(MOJO_CORE_EMBEDDER) const IpczDriver& GetIpczDriverForMojo();
-
-// Creates a new ipcz driver transport from `endpoint`. The caller must specify
-// whether each end of the transport will be attached to a broker node. If the
-// local endpoint is a broker, `process` identifies the remote process if
-// possible.
-struct TransportEndpointTypes {
-  bool local_is_broker;
-  bool remote_is_broker;
-};
-COMPONENT_EXPORT(MOJO_CORE_EMBEDDER)
-IpczDriverHandle CreateIpczTransportFromEndpoint(
-    mojo::PlatformChannelEndpoint endpoint,
-    const TransportEndpointTypes& endpoint_types,
-    base::Process remote_process = base::Process());
-
 }  // namespace core
 }  // namespace mojo
 
diff --git a/mojo/public/cpp/bindings/lib/string_serialization.h b/mojo/public/cpp/bindings/lib/string_serialization.h
index a5d8434..c9efd3b 100644
--- a/mojo/public/cpp/bindings/lib/string_serialization.h
+++ b/mojo/public/cpp/bindings/lib/string_serialization.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 #include <string.h>
 
+#include "base/strings/string_util.h"
 #include "mojo/public/cpp/bindings/lib/array_internal.h"
 #include "mojo/public/cpp/bindings/lib/message_fragment.h"
 #include "mojo/public/cpp/bindings/lib/serialization_forward.h"
@@ -41,7 +42,8 @@
     if (!input)
       return CallSetToNullIfExists<Traits>(output);
     bool ok = Traits::Read(StringDataView(input, message), output);
-    if (ok && !Traits::IsValidUTF8(*output)) {
+    if (ok && !base::IsStringUTF8(
+                  base::StringPiece(input->storage(), input->size()))) {
       RecordInvalidStringDeserialization();
     }
     return ok;
diff --git a/mojo/public/cpp/bindings/lib/string_traits_wtf.cc b/mojo/public/cpp/bindings/lib/string_traits_wtf.cc
index 14498696..a975350 100644
--- a/mojo/public/cpp/bindings/lib/string_traits_wtf.cc
+++ b/mojo/public/cpp/bindings/lib/string_traits_wtf.cc
@@ -32,15 +32,4 @@
   return true;
 }
 
-// static
-bool StringTraits<WTF::String>::IsValidUTF8(const WTF::String& value) {
-  if (value.IsNull())
-    return true;
-  if (!value.Is8Bit())
-    return false;
-  base::span<const LChar> data = value.Span8();
-  return base::IsStringUTF8(base::StringPiece(
-      reinterpret_cast<const char*>(data.data()), data.size()));
-}
-
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/string_traits_stl.h b/mojo/public/cpp/bindings/string_traits_stl.h
index 67185a7a..6842ed4 100644
--- a/mojo/public/cpp/bindings/string_traits_stl.h
+++ b/mojo/public/cpp/bindings/string_traits_stl.h
@@ -7,7 +7,6 @@
 
 #include <string>
 
-#include "base/strings/string_util.h"
 #include "mojo/public/cpp/bindings/string_traits.h"
 
 namespace mojo {
@@ -20,10 +19,6 @@
     output->assign(input.storage(), input.size());
     return true;
   }
-
-  static bool IsValidUTF8(const std::string& value) {
-    return base::IsStringUTF8(value);
-  }
 };
 
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/string_traits_string_piece.h b/mojo/public/cpp/bindings/string_traits_string_piece.h
index fdec14a9..b4e52df 100644
--- a/mojo/public/cpp/bindings/string_traits_string_piece.h
+++ b/mojo/public/cpp/bindings/string_traits_string_piece.h
@@ -6,7 +6,6 @@
 #define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STRING_PIECE_H_
 
 #include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
 #include "mojo/public/cpp/bindings/string_traits.h"
 
 namespace mojo {
@@ -33,10 +32,6 @@
     *output = base::StringPiece(input.storage(), input.size());
     return true;
   }
-
-  static bool IsValidUTF8(const base::StringPiece& value) {
-    return base::IsStringUTF8(value);
-  }
 };
 
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/string_traits_wtf.h b/mojo/public/cpp/bindings/string_traits_wtf.h
index e368942..2097c3e 100644
--- a/mojo/public/cpp/bindings/string_traits_wtf.h
+++ b/mojo/public/cpp/bindings/string_traits_wtf.h
@@ -21,8 +21,6 @@
   static WTF::StringUTF8Adaptor GetUTF8(const WTF::String& input);
 
   static bool Read(StringDataView input, WTF::String* output);
-
-  static bool IsValidUTF8(const WTF::String& value);
 };
 
 }  // namespace mojo
diff --git a/mojo/public/tools/mojom/version_compatibility_unittest.py b/mojo/public/tools/mojom/version_compatibility_unittest.py
index 22498e4..7b71ce65 100644
--- a/mojo/public/tools/mojom/version_compatibility_unittest.py
+++ b/mojo/public/tools/mojom/version_compatibility_unittest.py
@@ -60,40 +60,48 @@
     """Adding a value to an existing version is not allowed, even if the old
     enum was marked [Extensible]. Note that it is irrelevant whether or not the
     new enum is marked [Extensible]."""
-    self.assertNotBackwardCompatible('[Extensible] enum E { kFoo, kBar };',
-                                     'enum E { kFoo, kBar, kBaz };')
     self.assertNotBackwardCompatible(
-        '[Extensible] enum E { kFoo, kBar };',
-        '[Extensible] enum E { kFoo, kBar, kBaz };')
+        '[Extensible] enum E { [Default] kFoo, kBar };',
+        'enum E { kFoo, kBar, kBaz };')
     self.assertNotBackwardCompatible(
-        '[Extensible] enum E { kFoo, [MinVersion=1] kBar };',
+        '[Extensible] enum E { [Default] kFoo, kBar };',
+        '[Extensible] enum E { [Default] kFoo, kBar, kBaz };')
+    self.assertNotBackwardCompatible(
+        '[Extensible] enum E { [Default] kFoo, [MinVersion=1] kBar };',
         'enum E { kFoo, [MinVersion=1] kBar, [MinVersion=1] kBaz };')
 
   def testEnumValueRemoval(self):
     """Removal of an enum value is never valid even for [Extensible] enums."""
     self.assertNotBackwardCompatible('enum E { kFoo, kBar };',
                                      'enum E { kFoo };')
-    self.assertNotBackwardCompatible('[Extensible] enum E { kFoo, kBar };',
-                                     '[Extensible] enum E { kFoo };')
     self.assertNotBackwardCompatible(
-        '[Extensible] enum E { kA, [MinVersion=1] kB };',
-        '[Extensible] enum E { kA, };')
+        '[Extensible] enum E { [Default] kFoo, kBar };',
+        '[Extensible] enum E { [Default] kFoo };')
     self.assertNotBackwardCompatible(
-        '[Extensible] enum E { kA, [MinVersion=1] kB, [MinVersion=1] kZ };',
-        '[Extensible] enum E { kA, [MinVersion=1] kB };')
+        '[Extensible] enum E { [Default] kA, [MinVersion=1] kB };',
+        '[Extensible] enum E { [Default] kA, };')
+    self.assertNotBackwardCompatible(
+        """[Extensible] enum E {
+          [Default] kA,
+          [MinVersion=1] kB,
+          [MinVersion=1] kZ };""",
+        '[Extensible] enum E { [Default] kA, [MinVersion=1] kB };')
 
   def testNewExtensibleEnumValueWithMinVersion(self):
     """Adding a new and properly [MinVersion]'d value to an [Extensible] enum
     is a backward-compatible change. Note that it is irrelevant whether or not
     the new enum is marked [Extensible]."""
-    self.assertBackwardCompatible('[Extensible] enum E { kA, kB };',
+    self.assertBackwardCompatible('[Extensible] enum E { [Default] kA, kB };',
                                   'enum E { kA, kB, [MinVersion=1] kC };')
     self.assertBackwardCompatible(
-        '[Extensible] enum E { kA, kB };',
-        '[Extensible] enum E { kA, kB, [MinVersion=1] kC };')
+        '[Extensible] enum E { [Default] kA, kB };',
+        '[Extensible] enum E { [Default] kA, kB, [MinVersion=1] kC };')
     self.assertBackwardCompatible(
-        '[Extensible] enum E { kA, [MinVersion=1] kB };',
-        '[Extensible] enum E { kA, [MinVersion=1] kB, [MinVersion=2] kC };')
+        '[Extensible] enum E { [Default] kA, [MinVersion=1] kB };',
+        """[Extensible] enum E {
+          [Default] kA,
+          [MinVersion=1] kB,
+          [MinVersion=2] kC };""")
 
   def testRenameEnumValue(self):
     """Renaming an enum value does not affect backward-compatibility. Only
@@ -161,14 +169,17 @@
         'struct S {}; struct T { S s; };',
         'struct S { [MinVersion=1] int32 x; }; struct T { S s; };')
     self.assertBackwardCompatible(
-        '[Extensible] enum E { kA }; struct S { E e; };',
-        '[Extensible] enum E { kA, [MinVersion=1] kB }; struct S { E e; };')
+        '[Extensible] enum E { [Default] kA }; struct S { E e; };',
+        """[Extensible] enum E {
+          [Default] kA,
+          [MinVersion=1] kB };
+          struct S { E e; };""")
     self.assertNotBackwardCompatible(
         'struct S {}; struct T { S s; };',
         'struct S { int32 x; }; struct T { S s; };')
     self.assertNotBackwardCompatible(
-        '[Extensible] enum E { kA }; struct S { E e; };',
-        '[Extensible] enum E { kA, kB }; struct S { E e; };')
+        '[Extensible] enum E { [Default] kA }; struct S { E e; };',
+        '[Extensible] enum E { [Default] kA, kB }; struct S { E e; };')
 
   def testNewStructFieldWithInvalidMinVersion(self):
     """Adding a new field using an existing MinVersion breaks backward-
@@ -305,14 +316,17 @@
         'struct S {}; union U { S s; };',
         'struct S { [MinVersion=1] int32 x; }; union U { S s; };')
     self.assertBackwardCompatible(
-        '[Extensible] enum E { kA }; union U { E e; };',
-        '[Extensible] enum E { kA, [MinVersion=1] kB }; union U { E e; };')
+        '[Extensible] enum E { [Default] kA }; union U { E e; };',
+        """[Extensible] enum E {
+          [Default] kA,
+          [MinVersion=1] kB };
+          union U { E e; };""")
     self.assertNotBackwardCompatible(
         'struct S {}; union U { S s; };',
         'struct S { int32 x; }; union U { S s; };')
     self.assertNotBackwardCompatible(
-        '[Extensible] enum E { kA }; union U { E e; };',
-        '[Extensible] enum E { kA, kB }; union U { E e; };')
+        '[Extensible] enum E { [Default] kA }; union U { E e; };',
+        '[Extensible] enum E { [Default] kA, kB }; union U { E e; };')
 
   def testNewUnionFieldWithInvalidMinVersion(self):
     """Adding a new field using an existing MinVersion breaks backward-
diff --git a/net/dns/dns_test_util.cc b/net/dns/dns_test_util.cc
index 8dc38880..4616de4b 100644
--- a/net/dns/dns_test_util.cc
+++ b/net/dns/dns_test_util.cc
@@ -819,7 +819,7 @@
   // Session not expected to be used for anything that will actually require
   // random numbers.
   auto null_random_callback =
-      base::BindRepeating([](int, int) -> int { IMMEDIATE_CRASH(); });
+      base::BindRepeating([](int, int) -> int { base::ImmediateCrash(); });
 
   return base::MakeRefCounted<DnsSession>(
       effective_config_.value(), null_random_callback, nullptr /* net_log */);
diff --git a/net/dns/https_record_rdata.cc b/net/dns/https_record_rdata.cc
index 58693d7ad..beb9d7a6 100644
--- a/net/dns/https_record_rdata.cc
+++ b/net/dns/https_record_rdata.cc
@@ -450,7 +450,7 @@
   return base::Contains(kSupportedKeys, key);
 #else
   // Only intended for DCHECKs.
-  IMMEDIATE_CRASH();
+  base::ImmediateCrash();
 #endif  // DCHECK_IS_ON()
 }
 
diff --git a/net/dns/mock_host_resolver.cc b/net/dns/mock_host_resolver.cc
index 21c312be..196ef2e7 100644
--- a/net/dns/mock_host_resolver.cc
+++ b/net/dns/mock_host_resolver.cc
@@ -1436,34 +1436,36 @@
     return ERR_IO_PENDING;
   }
 
-  const AddressList* GetAddressResults() const override { IMMEDIATE_CRASH(); }
+  const AddressList* GetAddressResults() const override {
+    base::ImmediateCrash();
+  }
 
   const std::vector<HostResolverEndpointResult>* GetEndpointResults()
       const override {
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   }
 
   const absl::optional<std::vector<std::string>>& GetTextResults()
       const override {
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   }
 
   const absl::optional<std::vector<HostPortPair>>& GetHostnameResults()
       const override {
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   }
 
   const std::set<std::string>* GetDnsAliasResults() const override {
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   }
 
   net::ResolveErrorInfo GetResolveErrorInfo() const override {
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   }
 
   const absl::optional<HostCache::EntryStaleness>& GetStaleInfo()
       const override {
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   }
 
   void ChangeRequestPriority(RequestPriority priority) override {}
diff --git a/net/dns/resolve_context_unittest.cc b/net/dns/resolve_context_unittest.cc
index 6bc0e28..22c5d9c 100644
--- a/net/dns/resolve_context_unittest.cc
+++ b/net/dns/resolve_context_unittest.cc
@@ -48,7 +48,7 @@
 
   scoped_refptr<DnsSession> CreateDnsSession(const DnsConfig& config) {
     auto null_random_callback =
-        base::BindRepeating([](int, int) -> int { IMMEDIATE_CRASH(); });
+        base::BindRepeating([](int, int) -> int { base::ImmediateCrash(); });
     return base::MakeRefCounted<DnsSession>(config, null_random_callback,
                                             nullptr /* netlog */);
   }
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 66375f2..576423a 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -124,9 +124,9 @@
   ]
   deps = [
     "//base",
+    "//components/named_mojo_ipc_server",
     "//mojo/public/cpp/platform",
     "//remoting/host/base",
-    "//remoting/host/mojo_ipc",
   ]
 }
 
@@ -350,8 +350,8 @@
     "it2me_desktop_environment.h",
     "keyboard_layout_monitor.cc",
     "keyboard_layout_monitor.h",
-    "mojo_caller_security_checker.h",
     "mojo_caller_security_checker.cc",
+    "mojo_caller_security_checker.h",
     "mouse_cursor_monitor_proxy.cc",
     "mouse_cursor_monitor_proxy.h",
     "mouse_shape_pump.cc",
@@ -409,6 +409,7 @@
     "//base:i18n",
     "//build:branding_buildflags",
     "//build:chromeos_buildflags",
+    "//components/named_mojo_ipc_server",
     "//components/policy/core/common",
     "//components/webrtc:thread_wrapper",
     "//crypto",
@@ -419,7 +420,6 @@
     "//remoting/base:authorization",
     "//remoting/host/file_transfer:common",
     "//remoting/host/input_monitor",
-    "//remoting/host/mojo_ipc",
     "//remoting/host/mojom",
     "//remoting/host/remote_open_url:common",
     "//remoting/host/security_key",
@@ -800,6 +800,8 @@
     ":test_support",
     "//build:branding_buildflags",
     "//build:chromeos_buildflags",
+    "//components/named_mojo_ipc_server",
+    "//components/named_mojo_ipc_server:test_support",
     "//net:test_support",
     "//remoting/codec:encoder",
     "//remoting/host/base",
@@ -807,9 +809,6 @@
     "//remoting/host/input_monitor:input_monitor",
     "//remoting/host/it2me:common",
     "//remoting/host/it2me:constants",
-    "//remoting/host/mojo_ipc",
-    "//remoting/host/mojo_ipc:test_support",
-    "//remoting/host/mojo_ipc:unit_tests",
     "//remoting/host/mojom",
     "//remoting/host/native_messaging",
     "//remoting/host/remote_open_url:test_support",
diff --git a/remoting/host/DEPS b/remoting/host/DEPS
index 8818446..5d4d463b 100644
--- a/remoting/host/DEPS
+++ b/remoting/host/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+ash",
   "+cc/output",
+  "+components/named_mojo_ipc_server",
   "+components/policy/core/common",
   "+components/viz/common",
   "+components/webrtc",
@@ -48,4 +49,4 @@
   "it2me_desktop_environment*": [
     "+components/user_manager"
   ],
-}
\ No newline at end of file
+}
diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc
index 625dd3d..326830e 100644
--- a/remoting/host/chromoting_host.cc
+++ b/remoting/host/chromoting_host.cc
@@ -16,6 +16,7 @@
 #include "base/ranges/algorithm.h"
 #include "base/task/single_thread_task_runner.h"
 #include "build/build_config.h"
+#include "components/named_mojo_ipc_server/named_mojo_ipc_server.h"
 #include "components/webrtc/thread_wrapper.h"
 #include "remoting/base/constants.h"
 #include "remoting/base/logging.h"
@@ -24,7 +25,6 @@
 #include "remoting/host/input_injector.h"
 #include "remoting/host/ipc_constants.h"
 #include "remoting/host/mojo_caller_security_checker.h"
-#include "remoting/host/mojo_ipc/mojo_ipc_server.h"
 #include "remoting/protocol/client_stub.h"
 #include "remoting/protocol/host_stub.h"
 #include "remoting/protocol/ice_connection_to_client.h"
@@ -44,29 +44,29 @@
 namespace {
 
 const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
-  // Number of initial errors (in sequence) to ignore before applying
-  // exponential back-off rules.
-  5,
+    // Number of initial errors (in sequence) to ignore before applying
+    // exponential back-off rules.
+    5,
 
-  // Initial delay for exponential back-off in ms.
-  2000,
+    // Initial delay for exponential back-off in ms.
+    2000,
 
-  // Factor by which the waiting time will be multiplied.
-  2,
+    // Factor by which the waiting time will be multiplied.
+    2,
 
-  // Fuzzing percentage. ex: 10% will spread requests randomly
-  // between 90%-100% of the calculated time.
-  0,
+    // Fuzzing percentage. ex: 10% will spread requests randomly
+    // between 90%-100% of the calculated time.
+    0,
 
-  // Maximum amount of time we are willing to delay our request in ms.
-  -1,
+    // Maximum amount of time we are willing to delay our request in ms.
+    -1,
 
-  // Time to keep an entry from being discarded even when it
-  // has no significant state, -1 to never discard.
-  -1,
+    // Time to keep an entry from being discarded even when it
+    // has no significant state, -1 to never discard.
+    -1,
 
-  // Don't use initial delay unless the last request was an error.
-  false,
+    // Don't use initial delay unless the last request was an error.
+    false,
 };
 
 }  // namespace
@@ -126,7 +126,8 @@
   DCHECK(!ipc_server_);
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
-  ipc_server_ = std::make_unique<MojoIpcServer<mojom::ChromotingHostServices>>(
+  ipc_server_ = std::make_unique<
+      named_mojo_ipc_server::NamedMojoIpcServer<mojom::ChromotingHostServices>>(
       GetChromotingHostServicesServerName(), this,
       base::BindRepeating(&IsTrustedMojoEndpoint));
   ipc_server_->StartServer();
@@ -159,7 +160,8 @@
   // authenticates. This allows the backoff to protect from parallel
   // connection attempts as well as sequential ones.
   if (login_backoff_.ShouldRejectRequest()) {
-    LOG(WARNING) << "Disconnecting client " << client->client_jid() << " due to"
+    LOG(WARNING) << "Disconnecting client " << client->client_jid()
+                 << " due to"
                     " an overload of failed login attempts.";
     client->DisconnectSession(protocol::HOST_OVERLOAD);
     return;
@@ -265,8 +267,8 @@
 }
 
 void ChromotingHost::OnIncomingSession(
-      protocol::Session* session,
-      protocol::SessionManager::IncomingSessionResponse* response) {
+    protocol::Session* session,
+    protocol::SessionManager::IncomingSessionResponse* response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(started_);
 
diff --git a/remoting/host/chromoting_host.h b/remoting/host/chromoting_host.h
index 8986dd5..dd5651a 100644
--- a/remoting/host/chromoting_host.h
+++ b/remoting/host/chromoting_host.h
@@ -27,20 +27,23 @@
 #include "remoting/protocol/connection_to_client.h"
 #include "remoting/protocol/pairing_registry.h"
 #include "remoting/protocol/session_manager.h"
+#include "remoting/protocol/transport_context.h"
 
 namespace base {
 class SingleThreadTaskRunner;
 }  // namespace base
 
+namespace named_mojo_ipc_server {
+class IpcServer;
+}
+
 namespace remoting {
 
 namespace protocol {
 class InputStub;
-class TransportContext;
 }  // namespace protocol
 
 class DesktopEnvironmentFactory;
-class IpcServer;
 
 // A class to implement the functionality of a host process.
 //
@@ -195,7 +198,7 @@
 
   // IPC server that runs the CRD host service API. Non-null if the server name
   // is set and the host is started.
-  std::unique_ptr<IpcServer> ipc_server_;
+  std::unique_ptr<named_mojo_ipc_server::IpcServer> ipc_server_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/remoting/host/chromoting_host_services_client_unittest.cc b/remoting/host/chromoting_host_services_client_unittest.cc
index de48d32..9d3381e 100644
--- a/remoting/host/chromoting_host_services_client_unittest.cc
+++ b/remoting/host/chromoting_host_services_client_unittest.cc
@@ -16,9 +16,10 @@
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
+#include "components/named_mojo_ipc_server/named_mojo_ipc_server.h"
+#include "components/named_mojo_ipc_server/named_mojo_ipc_test_util.h"
+#include "remoting/host/chromoting_host.h"
 #include "remoting/host/mojo_caller_security_checker.h"
-#include "remoting/host/mojo_ipc/mojo_ipc_server.h"
-#include "remoting/host/mojo_ipc/mojo_ipc_test_util.h"
 #include "remoting/host/mojom/chromoting_host_services.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -41,7 +42,9 @@
   void SetRemoteDisconnectCallback(base::OnceClosure callback);
 
   std::unique_ptr<ChromotingHostServicesClient> client_;
-  std::unique_ptr<MojoIpcServer<mojom::ChromotingHostServices>> ipc_server_;
+  std::unique_ptr<
+      named_mojo_ipc_server::NamedMojoIpcServer<mojom::ChromotingHostServices>>
+      ipc_server_;
   std::vector<mojo::PendingReceiver<mojom::ChromotingSessionServices>>
       receivers_;
 
@@ -60,14 +63,15 @@
 };
 
 ChromotingHostServicesClientTest::ChromotingHostServicesClientTest() {
-  auto test_server_name = test::GenerateRandomServerName();
+  auto test_server_name =
+      named_mojo_ipc_server::test::GenerateRandomServerName();
   auto environment = base::Environment::Create();
   environment_ = environment.get();
   client_ = base::WrapUnique(new ChromotingHostServicesClient(
       std::move(environment), test_server_name));
-  ipc_server_ = std::make_unique<MojoIpcServer<mojom::ChromotingHostServices>>(
-      test_server_name, this,
-      base::BindRepeating(&IsTrustedMojoEndpoint));
+  ipc_server_ = std::make_unique<
+      named_mojo_ipc_server::NamedMojoIpcServer<mojom::ChromotingHostServices>>(
+      test_server_name, this, base::BindRepeating(&IsTrustedMojoEndpoint));
   ipc_server_->set_on_invitation_sent_callback_for_testing(
       base::BindRepeating(&ChromotingHostServicesClientTest::OnInvitationSent,
                           base::Unretained(this)));
diff --git a/remoting/host/chromoting_host_unittest.cc b/remoting/host/chromoting_host_unittest.cc
index 76ea6f1e7..9b4b5698 100644
--- a/remoting/host/chromoting_host_unittest.cc
+++ b/remoting/host/chromoting_host_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "components/named_mojo_ipc_server/fake_ipc_server.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/network_change_notifier.h"
 #include "remoting/base/auto_thread_task_runner.h"
@@ -24,7 +25,6 @@
 #include "remoting/host/fake_desktop_environment.h"
 #include "remoting/host/fake_mouse_cursor_monitor.h"
 #include "remoting/host/host_mock_objects.h"
-#include "remoting/host/mojo_ipc/fake_ipc_server.h"
 #include "remoting/host/mojom/chromoting_host_services.mojom.h"
 #include "remoting/proto/video.pb.h"
 #include "remoting/protocol/errors.h"
@@ -104,10 +104,8 @@
     session_unowned_jid1_ = "user3@doman/rest-of-jid";
     session_unowned_jid2_ = "user4@doman/rest-of-jid";
 
-    EXPECT_CALL(*session1_, jid())
-        .WillRepeatedly(ReturnRef(session_jid1_));
-    EXPECT_CALL(*session2_, jid())
-        .WillRepeatedly(ReturnRef(session_jid2_));
+    EXPECT_CALL(*session1_, jid()).WillRepeatedly(ReturnRef(session_jid1_));
+    EXPECT_CALL(*session2_, jid()).WillRepeatedly(ReturnRef(session_jid2_));
     EXPECT_CALL(*session_unowned1_, jid())
         .WillRepeatedly(ReturnRef(session_unowned_jid1_));
     EXPECT_CALL(*session_unowned2_, jid())
@@ -141,7 +139,8 @@
   }
 
   // Helper method to pretend a client is connected to ChromotingHost.
-  void SimulateClientConnection(int connection_index, bool authenticate,
+  void SimulateClientConnection(int connection_index,
+                                bool authenticate,
                                 bool reject) {
     std::unique_ptr<protocol::ConnectionToClient> connection = std::move(
         (connection_index == 0) ? owned_connection1_ : owned_connection2_);
@@ -220,8 +219,8 @@
   }
 
   void StartFakeIpcServer() {
-    host_->ipc_server_ =
-        std::make_unique<FakeIpcServer>(&ipc_server_test_state_);
+    host_->ipc_server_ = std::make_unique<named_mojo_ipc_server::FakeIpcServer>(
+        &ipc_server_test_state_);
     host_->ipc_server_->StartServer();
   }
 
@@ -286,7 +285,7 @@
   std::string session_unowned_jid2_;
   protocol::Session::EventHandler* session_unowned1_event_handler_;
   protocol::Session::EventHandler* session_unowned2_event_handler_;
-  FakeIpcServer::TestState ipc_server_test_state_;
+  named_mojo_ipc_server::FakeIpcServer::TestState ipc_server_test_state_;
 
   // Returns the cached client pointers client1_ or client2_.
   ClientSession*& get_client(int connection_index) {
@@ -366,8 +365,9 @@
   host_->OnIncomingSession(session_unowned1_.release(), &response);
   EXPECT_EQ(protocol::SessionManager::ACCEPT, response);
 
-  EXPECT_CALL(*session, Close(_)).WillOnce(InvokeWithoutArgs(
-    this, &ChromotingHostTest::NotifyConnectionClosed1));
+  EXPECT_CALL(*session, Close(_))
+      .WillOnce(InvokeWithoutArgs(
+          this, &ChromotingHostTest::NotifyConnectionClosed1));
   ShutdownHost();
 }
 
@@ -381,18 +381,16 @@
   for (size_t i = 0; i < kNumFailuresIgnored + 1; ++i) {
     // Set expectations and responses for the new session.
     auto session = std::make_unique<MockSession>();
-    EXPECT_CALL(*session, jid())
-        .WillRepeatedly(ReturnRef(session_jid1_));
+    EXPECT_CALL(*session, jid()).WillRepeatedly(ReturnRef(session_jid1_));
     EXPECT_CALL(*session, config())
         .WillRepeatedly(ReturnRef(*session_config1_));
     EXPECT_CALL(*session, SetEventHandler(_))
         .Times(AnyNumber())
         .WillRepeatedly(SaveArg<0>(&session_event_handlers[i]));
-    EXPECT_CALL(*session, Close(_)).WillOnce(
-        InvokeWithoutArgs(
-            [&session_event_handlers, i]() {
-              session_event_handlers[i]->OnSessionStateChange(Session::CLOSED);
-            }));
+    EXPECT_CALL(*session, Close(_))
+        .WillOnce(InvokeWithoutArgs([&session_event_handlers, i]() {
+          session_event_handlers[i]->OnSessionStateChange(Session::CLOSED);
+        }));
     // Simulate the incoming connection.
     host_->OnIncomingSession(session.release(), &response);
     EXPECT_EQ(protocol::SessionManager::ACCEPT, response);
@@ -422,18 +420,16 @@
   for (size_t i = 0; i < kNumFailuresIgnored + 1; ++i) {
     // Set expectations and responses for the new session.
     auto session = std::make_unique<MockSession>();
-    EXPECT_CALL(*session, jid())
-        .WillRepeatedly(ReturnRef(session_jid1_));
+    EXPECT_CALL(*session, jid()).WillRepeatedly(ReturnRef(session_jid1_));
     EXPECT_CALL(*session, config())
         .WillRepeatedly(ReturnRef(*session_config1_));
     EXPECT_CALL(*session, SetEventHandler(_))
         .Times(AnyNumber())
         .WillRepeatedly(SaveArg<0>(&session_event_handlers[i]));
-    EXPECT_CALL(*session, Close(_)).WillOnce(
-        InvokeWithoutArgs(
-            [&session_event_handlers, i]() {
-              session_event_handlers[i]->OnSessionStateChange(Session::CLOSED);
-            }));
+    EXPECT_CALL(*session, Close(_))
+        .WillOnce(InvokeWithoutArgs([&session_event_handlers, i]() {
+          session_event_handlers[i]->OnSessionStateChange(Session::CLOSED);
+        }));
     // Simulate the incoming connection.
     host_->OnIncomingSession(session.release(), &response);
     EXPECT_EQ(protocol::SessionManager::ACCEPT, response);
@@ -452,18 +448,15 @@
   // successful authentication it should not be rejected.
   auto session = std::make_unique<MockSession>();
   protocol::Session::EventHandler* session_event_handler;
-  EXPECT_CALL(*session, jid())
-      .WillRepeatedly(ReturnRef(session_jid1_));
-  EXPECT_CALL(*session, config())
-      .WillRepeatedly(ReturnRef(*session_config1_));
+  EXPECT_CALL(*session, jid()).WillRepeatedly(ReturnRef(session_jid1_));
+  EXPECT_CALL(*session, config()).WillRepeatedly(ReturnRef(*session_config1_));
   EXPECT_CALL(*session, SetEventHandler(_))
       .Times(AnyNumber())
       .WillRepeatedly(SaveArg<0>(&session_event_handler));
-  EXPECT_CALL(*session, Close(_)).WillOnce(
-      InvokeWithoutArgs(
-          [&session_event_handler]() {
-            session_event_handler->OnSessionStateChange(Session::CLOSED);
-          }));
+  EXPECT_CALL(*session, Close(_))
+      .WillOnce(InvokeWithoutArgs([&session_event_handler]() {
+        session_event_handler->OnSessionStateChange(Session::CLOSED);
+      }));
   host_->OnIncomingSession(session.release(), &response);
   EXPECT_EQ(protocol::SessionManager::ACCEPT, response);
 
@@ -475,7 +468,6 @@
 TEST_F(ChromotingHostTest, DISABLED_OnSessionRouteChange) {
   StartHost();
 
-
   ExpectClientConnected(0);
   SimulateClientConnection(0, true, false);
 
diff --git a/remoting/host/ipc_constants.cc b/remoting/host/ipc_constants.cc
index f163a77..1aedf9c 100644
--- a/remoting/host/ipc_constants.cc
+++ b/remoting/host/ipc_constants.cc
@@ -9,9 +9,9 @@
 #include "base/path_service.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
+#include "components/named_mojo_ipc_server/named_mojo_ipc_util.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "remoting/host/base/username.h"
-#include "remoting/host/mojo_ipc/mojo_ipc_util.h"
 
 namespace remoting {
 
@@ -64,20 +64,21 @@
 const mojo::NamedPlatformChannel::ServerName&
 GetChromotingHostServicesServerName() {
   static const base::NoDestructor<mojo::NamedPlatformChannel::ServerName>
-      server_name(WorkingDirectoryIndependentServerNameFromUTF8(
+      server_name(
+          named_mojo_ipc_server::WorkingDirectoryIndependentServerNameFromUTF8(
 #if BUILDFLAG(IS_LINUX)
-          // Linux host creates the socket file in /tmp, and it won't be deleted
-          // until reboot, so we put username in the path in case the user
-          // switches the host owner.
-          base::StringPrintf(kChromotingHostServicesIpcNamePattern,
-                             GetUsername().c_str())
+              // Linux host creates the socket file in /tmp, and it won't be
+              // deleted until reboot, so we put username in the path in case
+              // the user switches the host owner.
+              base::StringPrintf(kChromotingHostServicesIpcNamePattern,
+                                 GetUsername().c_str())
 #else
-          // None of the core Windows processes runs as the host owner so we
-          // can't just put username in the path. This is fine since the named
-          // pipe is accessible by all authenticated users.
-          kChromotingHostServicesIpcName
+              // None of the core Windows processes runs as the host owner so we
+              // can't just put username in the path. This is fine since the
+              // named pipe is accessible by all authenticated users.
+              kChromotingHostServicesIpcName
 #endif
-              ));
+                  ));
   return *server_name;
 }
 
diff --git a/remoting/host/mojo_ipc/BUILD.gn b/remoting/host/mojo_ipc/BUILD.gn
deleted file mode 100644
index c37e0450..0000000
--- a/remoting/host/mojo_ipc/BUILD.gn
+++ /dev/null
@@ -1,72 +0,0 @@
-# Copyright 2021 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-static_library("mojo_ipc") {
-  sources = [
-    "ipc_server.h",
-    "mojo_ipc_server.cc",
-    "mojo_ipc_server.h",
-    "mojo_ipc_util.cc",
-    "mojo_ipc_util.h",
-    "mojo_server_endpoint_connector.h",
-  ]
-  deps = [
-    "//base",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/platform",
-    "//mojo/public/cpp/system",
-  ]
-  if (is_linux) {
-    sources += [
-      "mojo_server_endpoint_connector_linux.cc",
-      "mojo_server_endpoint_connector_linux.h",
-    ]
-  } else if (is_win) {
-    sources += [
-      "mojo_server_endpoint_connector_win.cc",
-      "mojo_server_endpoint_connector_win.h",
-    ]
-  } else {
-    sources += [ "mojo_server_endpoint_connector_unsupported.cc" ]
-  }
-}
-
-static_library("test_support") {
-  testonly = true
-
-  sources = [
-    "fake_ipc_server.cc",
-    "fake_ipc_server.h",
-    "mojo_ipc_test_util.cc",
-    "mojo_ipc_test_util.h",
-  ]
-  deps = [
-    ":mojo_ipc",
-    "//base",
-    "//mojo/public/cpp/platform",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-
-  sources = []
-
-  # Disable MojoIpcServerTest on unsupported platforms (i.e. Mac).
-  if (is_linux || is_win) {
-    sources += [ "mojo_ipc_server_unittest.cc" ]
-  }
-  deps = [
-    ":mojo_ipc",
-    ":test_support",
-    "//base",
-    "//base/test:test_support",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/platform",
-    "//mojo/public/cpp/system",
-    "//remoting/host/mojom",
-    "//testing/gmock",
-    "//testing/gtest",
-  ]
-}
diff --git a/remoting/host/mojo_ipc/mojo_ipc_test_util.h b/remoting/host/mojo_ipc/mojo_ipc_test_util.h
deleted file mode 100644
index bce59ecb..0000000
--- a/remoting/host/mojo_ipc/mojo_ipc_test_util.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef REMOTING_HOST_MOJO_IPC_MOJO_IPC_TEST_UTIL_H_
-#define REMOTING_HOST_MOJO_IPC_MOJO_IPC_TEST_UTIL_H_
-
-#include "mojo/public/cpp/platform/named_platform_channel.h"
-
-namespace remoting {
-namespace test {
-
-// Generates a random server name for unittests.
-mojo::NamedPlatformChannel::ServerName GenerateRandomServerName();
-
-}  // namespace test
-}  // namespace remoting
-
-#endif  // REMOTING_HOST_MOJO_IPC_MOJO_IPC_TEST_UTIL_H_
diff --git a/remoting/host/mojo_ipc/mojo_server_endpoint_connector_linux.h b/remoting/host/mojo_ipc/mojo_server_endpoint_connector_linux.h
deleted file mode 100644
index ce0b917e..0000000
--- a/remoting/host/mojo_ipc/mojo_server_endpoint_connector_linux.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef REMOTING_HOST_MOJO_IPC_MOJO_SERVER_ENDPOINT_CONNECTOR_LINUX_H_
-#define REMOTING_HOST_MOJO_IPC_MOJO_SERVER_ENDPOINT_CONNECTOR_LINUX_H_
-
-#include "base/files/file_descriptor_watcher_posix.h"
-#include "base/memory/raw_ptr.h"
-#include "base/sequence_checker.h"
-#include "base/thread_annotations.h"
-#include "remoting/host/mojo_ipc/mojo_server_endpoint_connector.h"
-
-namespace remoting {
-
-// Linux implementation for MojoServerEndpointConnector.
-class MojoServerEndpointConnectorLinux final
-    : public MojoServerEndpointConnector {
- public:
-  explicit MojoServerEndpointConnectorLinux(Delegate* delegate);
-  MojoServerEndpointConnectorLinux(const MojoServerEndpointConnectorLinux&) =
-      delete;
-  MojoServerEndpointConnectorLinux& operator=(
-      const MojoServerEndpointConnectorLinux&) = delete;
-  ~MojoServerEndpointConnectorLinux() override;
-
-  // MojoServerEndpointConnector implementation.
-  void Connect(mojo::PlatformChannelServerEndpoint server_endpoint) override;
-
- private:
-  void OnFileCanReadWithoutBlocking();
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  raw_ptr<Delegate> delegate_ GUARDED_BY_CONTEXT(sequence_checker_);
-
-  // These are only valid/non-null when there is a pending connection.
-  // Note that `pending_server_endpoint_` must outlive
-  // `read_watcher_controller_`; otherwise a bad file descriptor error will
-  // occur at destruction.
-  mojo::PlatformChannelServerEndpoint pending_server_endpoint_
-      GUARDED_BY_CONTEXT(sequence_checker_);
-  std::unique_ptr<base::FileDescriptorWatcher::Controller>
-      read_watcher_controller_ GUARDED_BY_CONTEXT(sequence_checker_);
-
-  base::WeakPtrFactory<MojoServerEndpointConnectorLinux> weak_factory_{this};
-};
-
-}  // namespace remoting
-
-#endif  // REMOTING_HOST_MOJO_IPC_MOJO_SERVER_ENDPOINT_CONNECTOR_LINUX_H_
diff --git a/remoting/host/mojo_ipc/mojo_server_endpoint_connector_unsupported.cc b/remoting/host/mojo_ipc/mojo_server_endpoint_connector_unsupported.cc
deleted file mode 100644
index 014a11a..0000000
--- a/remoting/host/mojo_ipc/mojo_server_endpoint_connector_unsupported.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "remoting/host/mojo_ipc/mojo_server_endpoint_connector.h"
-
-namespace remoting {
-
-// static
-// Dummy implementation that returns nullptr for unsupported platforms, i.e.
-// Mac.
-// TODO(yuweih): Implement MojoServerEndpointConnector for Mac.
-std::unique_ptr<MojoServerEndpointConnector>
-MojoServerEndpointConnector::Create(Delegate* delegate) {
-  return nullptr;
-}
-
-}  // namespace remoting
diff --git a/remoting/host/mojom/BUILD.gn b/remoting/host/mojom/BUILD.gn
index c98f02a5..692c404 100644
--- a/remoting/host/mojom/BUILD.gn
+++ b/remoting/host/mojom/BUILD.gn
@@ -13,7 +13,6 @@
     "remote_support.mojom",
     "remote_url_opener.mojom",
     "remoting_host.mojom",
-    "testing.mojom",
     "webauthn_proxy.mojom",
     "webrtc_types.mojom",
     "wrapped_primitives.mojom",
diff --git a/services/device/geolocation/android/junit/src/org/chromium/device/geolocation/LocationProviderTest.java b/services/device/geolocation/android/junit/src/org/chromium/device/geolocation/LocationProviderTest.java
index b60d694..fcccd37 100644
--- a/services/device/geolocation/android/junit/src/org/chromium/device/geolocation/LocationProviderTest.java
+++ b/services/device/geolocation/android/junit/src/org/chromium/device/geolocation/LocationProviderTest.java
@@ -10,7 +10,6 @@
 
 import android.content.Context;
 import android.location.LocationManager;
-import android.os.Build;
 
 import com.google.android.gms.location.FusedLocationProviderClient;
 
@@ -37,7 +36,7 @@
  * Test suite for Java Geolocation.
  */
 @RunWith(ParameterizedRobolectricTestRunner.class)
-@Config(sdk = Build.VERSION_CODES.M, manifest = Config.NONE)
+@Config(manifest = Config.NONE)
 public class LocationProviderTest {
     public static enum LocationProviderType { MOCK, ANDROID, GMS_CORE }
 
diff --git a/services/proxy_resolver/mock_proxy_host_resolver.cc b/services/proxy_resolver/mock_proxy_host_resolver.cc
index eeb6da27..d4bf523 100644
--- a/services/proxy_resolver/mock_proxy_host_resolver.cc
+++ b/services/proxy_resolver/mock_proxy_host_resolver.cc
@@ -119,7 +119,7 @@
   }
 
   const std::vector<net::IPAddress>& GetResults() const override {
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   }
 
  private:
diff --git a/services/test/echo/echo_service.cc b/services/test/echo/echo_service.cc
index 4db6108..2baaa1fe 100644
--- a/services/test/echo/echo_service.cc
+++ b/services/test/echo/echo_service.cc
@@ -39,7 +39,7 @@
 }
 
 void EchoService::Crash() {
-  IMMEDIATE_CRASH();
+  base::ImmediateCrash();
 }
 
 #if BUILDFLAG(IS_WIN)
diff --git a/skia/ext/skcolorspace_trfn.h b/skia/ext/skcolorspace_trfn.h
index 99581ca..96872b7 100644
--- a/skia/ext/skcolorspace_trfn.h
+++ b/skia/ext/skcolorspace_trfn.h
@@ -85,6 +85,18 @@
 // on macOS.
 static constexpr skcms_TransferFunction kRec709Apple = {1.961f, 1.};
 
+// If the sRGB transfer function is f(x), then this transfer function is
+// f(x * 1023 / 510). This function gives 510 values to SDR content, and can
+// reach a maximum brightnes of 4.99x SDR brightness.
+static constexpr skcms_TransferFunction kSRGBExtended1023Over510 = {
+    SkNamedTransferFnExt::kSRGB.g,
+    SkNamedTransferFnExt::kSRGB.a * 1023 / 510,
+    SkNamedTransferFnExt::kSRGB.b,
+    SkNamedTransferFnExt::kSRGB.c * 1023 / 510,
+    SkNamedTransferFnExt::kSRGB.d * 1023 / 510,
+    SkNamedTransferFnExt::kSRGB.e,
+    SkNamedTransferFnExt::kSRGB.f};
+
 }  // namespace SkNamedTransferFnExt
 
 #endif  // SKIA_EXT_SKCOLORSPACE_TRFN_H_
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index e996d711..45b3acc 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -32826,58 +32826,6 @@
       {
         "args": [
           "--gs-results-bucket=chromium-result-details",
-          "--recover-devices",
-          "--git-revision=${got_revision}"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "chrome_public_unit_test_apk"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "PQ3A.190801.002",
-              "device_os_flavor": "google",
-              "device_os_type": "userdebug",
-              "device_type": "walleye",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "chrome_public_unit_test_apk",
-        "test_id_prefix": "ninja://chrome/android:chrome_public_unit_test_apk/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
         "isolate_profile_data": true,
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 6b90889..99a6997 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -15515,58 +15515,6 @@
       {
         "args": [
           "--gs-results-bucket=chromium-result-details",
-          "--recover-devices",
-          "--git-revision=${got_revision}"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "chrome_public_unit_test_apk"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "PQ3A.190801.002",
-              "device_os_flavor": "google",
-              "device_os_type": "userdebug",
-              "device_type": "walleye",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "chrome_public_unit_test_apk",
-        "test_id_prefix": "ninja://chrome/android:chrome_public_unit_test_apk/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
         "isolate_profile_data": true,
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 969be8b..1c754bd 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -13221,56 +13221,6 @@
       {
         "args": [
           "--gs-results-bucket=chromium-result-details",
-          "--recover-devices",
-          "--git-revision=${got_revision}"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "chrome_public_unit_test_apk"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "chrome_public_unit_test_apk",
-        "test_id_prefix": "ninja://chrome/android:chrome_public_unit_test_apk/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
         "merge": {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 35d504e5..c225dea1 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1444,6 +1444,11 @@
     }
   },
   'chrome_public_unit_test_apk': {
+    'remove_from': [
+      'android-asan',  # https://crbug.com/964562
+      'android-code-coverage-native', # Does not generate profraw data.
+      'android-pie-arm64-coverage-experimental-rel', # Does not generate profraw data.
+    ],
     'modifications': {
       'android-12-x64-rel': {
         'args': [
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index b15e5a6..c5a6a76 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -186,6 +186,21 @@
             ]
         }
     ],
+    "AndroidDiscardOccludedBitmaps": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "DiscardOccludedBitmaps"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidDynamicPerformanceFramework": [
         {
             "platforms": [
@@ -321,6 +336,26 @@
             ]
         }
     ],
+    "AndroidOmniboxUxRevampPhase2": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_Active_Omnibox_Small_Margin_20221025",
+                    "params": {
+                        "enable_modernize_visual_update_on_tablet": "false",
+                        "modernize_visual_update_active_color_on_omnibox": "true",
+                        "modernize_visual_update_small_bottom_margin": "true"
+                    },
+                    "enable_features": [
+                        "OmniboxModernizeVisualUpdate"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidPermissionsCache": [
         {
             "platforms": [
@@ -459,6 +494,25 @@
             ]
         }
     ],
+    "AndroidTabSelectionEditorV2": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "enable_longpress_entry": "true",
+                        "enable_share": "true"
+                    },
+                    "enable_features": [
+                        "TabSelectionEditorV2"
+                    ]
+                }
+            ]
+        }
+    ],
     "AppCloseRefreshClankStudy": [
         {
             "platforms": [
@@ -6819,6 +6873,25 @@
             ]
         }
     ],
+    "MerchantWidePromotion": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "enabled_merchant_wide_promotion_20221102",
+                    "enable_features": [
+                        "MerchantWidePromotion"
+                    ]
+                }
+            ]
+        }
+    ],
     "MessagePumpEpoll": [
         {
             "platforms": [
@@ -7424,16 +7497,20 @@
                 {
                     "name": "AndroidExperiments",
                     "params": {
+                        "MaxNumRepeatableQueries": "4",
                         "OmniboxDynamicMaxAutocompleteIncreasedLimit": "15",
                         "OmniboxLocalZeroSuggestAgeThreshold": "90",
                         "OmniboxMaxURLMatches": "8",
                         "PrefixSuggestIgnoreDuplicateVisits": "true",
+                        "PrivilegeRepeatableQueries": "true",
+                        "ScaleRepeatableQueriesScore": "true",
                         "UIMaxAutocompleteMatches": "10",
                         "ZeroSuggestIgnoreDuplicateVisits": "false"
                     },
                     "enable_features": [
                         "AndroidAuxiliarySearch",
                         "LocalHistorySuggestRevamp",
+                        "NewTabPageTilesTitleWrapAround",
                         "OmniboxDynamicMaxAutocomplete",
                         "OmniboxFocusTriggersSRPZeroSuggest",
                         "OmniboxHeaderPaddingUpdate",
@@ -7442,10 +7519,12 @@
                         "OmniboxMostVisitedTilesDynamicSpacing",
                         "OmniboxMostVisitedTilesFadingOnTablet",
                         "OmniboxMostVisitedTilesOnSrp",
+                        "OmniboxMostVisitedTilesTitleWrapAround",
                         "OmniboxRemoveSuggestionHeaderCapitalization",
                         "OmniboxRemoveSuggestionHeaderChevron",
                         "OmniboxRetainSuggestionsWithHeaders",
-                        "OmniboxUIExperimentMaxAutocompleteMatches"
+                        "OmniboxUIExperimentMaxAutocompleteMatches",
+                        "OrganicRepeatableQueries"
                     ]
                 }
             ]
diff --git a/third_party/bidimapper/README.chromium b/third_party/bidimapper/README.chromium
index 54ada91cd..b7132c25 100644
--- a/third_party/bidimapper/README.chromium
+++ b/third_party/bidimapper/README.chromium
@@ -1,10 +1,10 @@
 Name: Implementation of WebDriver BiDi standard
 Short Name: chromium-bidi
-URL: https://github.com/GoogleChromeLabs/chromium-bidi/archive/1593bab554ddf9e9fdb8d40cb4f2cf4c019e901a.zip
+URL: https://github.com/GoogleChromeLabs/chromium-bidi/archive/866d5e6551d52a6f4d01f39e7b2c042c1c23e1fc.zip
 Version: 0
-Date: 2022-10-07
-Revision: 1593bab554ddf9e9fdb8d40cb4f2cf4c019e901a
-SHA-512: 0289ea7aa3a8f9c0b9208ddc6263e74654c55c42663d6627c5b4efa1a9d55088a3a3b3647d6cfd4aef23f7ea551e14c7dc187bbfd2fa7469d0b3276f18a5ce96
+Date: 2022-11-02
+Revision: 866d5e6551d52a6f4d01f39e7b2c042c1c23e1fc
+SHA-512: 5fd22d53f8ec2452598d4679d8f77fa4f55fdcc5430dd02f585e9fb6fd6c6f5797de378ddd480f107cd7815d85fb8cfa8687f0d823b5183e329819dfbd995aa4
 License: Apache 2.0
 License File: LICENSE
 Security Critical: no
diff --git a/third_party/bidimapper/mapper.js b/third_party/bidimapper/mapper.js
index f7cc529..56ef637 100644
--- a/third_party/bidimapper/mapper.js
+++ b/third_party/bidimapper/mapper.js
@@ -1,4 +1,4 @@
-!function(){"use strict";function e(e){return(...t)=>{if(globalThis.document?.documentElement){console.log(e,...t);const n=function(e){const t=e+"_log",n=document.getElementById(t);if(n)return n;const s=document.createElement("div");return s.id=t,s.innerHTML=`<h3>${e}:</h3>`,document.body.appendChild(s),s}(e),s=document.createElement("pre");s.textContent=t.join(", "),n.appendChild(s)}}}class t{constructor(e,t,n){this.error=e,this.message=t,this.stacktrace=n}error;message;stacktrace;toErrorResponse(e){return{id:e,error:this.error,message:this.message,stacktrace:this.stacktrace}}}class n extends t{constructor(e,t){super("unknown error",e,t)}}class s extends t{constructor(e,t){super("unknown command",e,t)}}class a extends t{constructor(e,t){super("invalid argument",e,t)}}class r extends t{constructor(e){super("no such frame",e)}}class i{method;params;constructor(e,t){this.method=e,this.params=t}}var o;!function(e){e.assertEqual=e=>e,e.assertIs=function(e){},e.assertNever=function(e){throw new Error},e.arrayToEnum=e=>{const t={};for(const n of e)t[n]=n;return t},e.getValidEnumValues=t=>{const n=e.objectKeys(t).filter((e=>"number"!=typeof t[t[e]])),s={};for(const e of n)s[e]=t[e];return e.objectValues(s)},e.objectValues=t=>e.objectKeys(t).map((function(e){return t[e]})),e.objectKeys="function"==typeof Object.keys?e=>Object.keys(e):e=>{const t=[];for(const n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.push(n);return t},e.find=(e,t)=>{for(const n of e)if(t(n))return n},e.isInteger="function"==typeof Number.isInteger?e=>Number.isInteger(e):e=>"number"==typeof e&&isFinite(e)&&Math.floor(e)===e,e.joinValues=function(e,t=" | "){return e.map((e=>"string"==typeof e?`'${e}'`:e)).join(t)},e.jsonStringifyReplacer=(e,t)=>"bigint"==typeof t?t.toString():t}(o||(o={}));const c=o.arrayToEnum(["string","nan","number","integer","float","boolean","date","bigint","symbol","function","undefined","null","array","object","unknown","promise","void","never","map","set"]),d=e=>{switch(typeof e){case"undefined":return c.undefined;case"string":return c.string;case"number":return isNaN(e)?c.nan:c.number;case"boolean":return c.boolean;case"function":return c.function;case"bigint":return c.bigint;case"object":return Array.isArray(e)?c.array:null===e?c.null:e.then&&"function"==typeof e.then&&e.catch&&"function"==typeof e.catch?c.promise:"undefined"!=typeof Map&&e instanceof Map?c.map:"undefined"!=typeof Set&&e instanceof Set?c.set:"undefined"!=typeof Date&&e instanceof Date?c.date:c.object;default:return c.unknown}},l=o.arrayToEnum(["invalid_type","invalid_literal","custom","invalid_union","invalid_union_discriminator","invalid_enum_value","unrecognized_keys","invalid_arguments","invalid_return_type","invalid_date","invalid_string","too_small","too_big","invalid_intersection_types","not_multiple_of"]);class u extends Error{constructor(e){super(),this.issues=[],this.addIssue=e=>{this.issues=[...this.issues,e]},this.addIssues=(e=[])=>{this.issues=[...this.issues,...e]};const t=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,t):this.__proto__=t,this.name="ZodError",this.issues=e}get errors(){return this.issues}format(e){const t=e||function(e){return e.message},n={_errors:[]},s=e=>{for(const a of e.issues)if("invalid_union"===a.code)a.unionErrors.map(s);else if("invalid_return_type"===a.code)s(a.returnTypeError);else if("invalid_arguments"===a.code)s(a.argumentsError);else if(0===a.path.length)n._errors.push(t(a));else{let e=n,s=0;for(;s<a.path.length;){const n=a.path[s];s===a.path.length-1?(e[n]=e[n]||{_errors:[]},e[n]._errors.push(t(a))):e[n]=e[n]||{_errors:[]},e=e[n],s++}}};return s(this),n}toString(){return this.message}get message(){return JSON.stringify(this.issues,o.jsonStringifyReplacer,2)}get isEmpty(){return 0===this.issues.length}flatten(e=(e=>e.message)){const t={},n=[];for(const s of this.issues)s.path.length>0?(t[s.path[0]]=t[s.path[0]]||[],t[s.path[0]].push(e(s))):n.push(e(s));return{formErrors:n,fieldErrors:t}}get formErrors(){return this.flatten()}}u.create=e=>new u(e);const p=(e,t)=>{let n;switch(e.code){case l.invalid_type:n=e.received===c.undefined?"Required":`Expected ${e.expected}, received ${e.received}`;break;case l.invalid_literal:n=`Invalid literal value, expected ${JSON.stringify(e.expected,o.jsonStringifyReplacer)}`;break;case l.unrecognized_keys:n=`Unrecognized key(s) in object: ${o.joinValues(e.keys,", ")}`;break;case l.invalid_union:n="Invalid input";break;case l.invalid_union_discriminator:n=`Invalid discriminator value. Expected ${o.joinValues(e.options)}`;break;case l.invalid_enum_value:n=`Invalid enum value. Expected ${o.joinValues(e.options)}, received '${e.received}'`;break;case l.invalid_arguments:n="Invalid function arguments";break;case l.invalid_return_type:n="Invalid function return type";break;case l.invalid_date:n="Invalid date";break;case l.invalid_string:"object"==typeof e.validation?"startsWith"in e.validation?n=`Invalid input: must start with "${e.validation.startsWith}"`:"endsWith"in e.validation?n=`Invalid input: must end with "${e.validation.endsWith}"`:o.assertNever(e.validation):n="regex"!==e.validation?`Invalid ${e.validation}`:"Invalid";break;case l.too_small:n="array"===e.type?`Array must contain ${e.inclusive?"at least":"more than"} ${e.minimum} element(s)`:"string"===e.type?`String must contain ${e.inclusive?"at least":"over"} ${e.minimum} character(s)`:"number"===e.type?`Number must be greater than ${e.inclusive?"or equal to ":""}${e.minimum}`:"date"===e.type?`Date must be greater than ${e.inclusive?"or equal to ":""}${new Date(e.minimum)}`:"Invalid input";break;case l.too_big:n="array"===e.type?`Array must contain ${e.inclusive?"at most":"less than"} ${e.maximum} element(s)`:"string"===e.type?`String must contain ${e.inclusive?"at most":"under"} ${e.maximum} character(s)`:"number"===e.type?`Number must be less than ${e.inclusive?"or equal to ":""}${e.maximum}`:"date"===e.type?`Date must be smaller than ${e.inclusive?"or equal to ":""}${new Date(e.maximum)}`:"Invalid input";break;case l.custom:n="Invalid input";break;case l.invalid_intersection_types:n="Intersection results could not be merged";break;case l.not_multiple_of:n=`Number must be a multiple of ${e.multipleOf}`;break;default:n=t.defaultError,o.assertNever(e)}return{message:n}};let h=p;function m(){return h}const g=e=>{const{data:t,path:n,errorMaps:s,issueData:a}=e,r=[...n,...a.path||[]],i={...a,path:r};let o="";const c=s.filter((e=>!!e)).slice().reverse();for(const e of c)o=e(i,{data:t,defaultError:o}).message;return{...a,path:r,message:a.message||o}};function f(e,t){const n=g({issueData:t,data:e.data,path:e.path,errorMaps:[e.common.contextualErrorMap,e.schemaErrorMap,m(),p].filter((e=>!!e))});e.common.issues.push(n)}class v{constructor(){this.value="valid"}dirty(){"valid"===this.value&&(this.value="dirty")}abort(){"aborted"!==this.value&&(this.value="aborted")}static mergeArray(e,t){const n=[];for(const s of t){if("aborted"===s.status)return y;"dirty"===s.status&&e.dirty(),n.push(s.value)}return{status:e.value,value:n}}static async mergeObjectAsync(e,t){const n=[];for(const e of t)n.push({key:await e.key,value:await e.value});return v.mergeObjectSync(e,n)}static mergeObjectSync(e,t){const n={};for(const s of t){const{key:t,value:a}=s;if("aborted"===t.status)return y;if("aborted"===a.status)return y;"dirty"===t.status&&e.dirty(),"dirty"===a.status&&e.dirty(),(void 0!==a.value||s.alwaysSet)&&(n[t.value]=a.value)}return{status:e.value,value:n}}}const y=Object.freeze({status:"aborted"}),b=e=>({status:"valid",value:e}),w=e=>"aborted"===e.status,_=e=>"dirty"===e.status,x=e=>"valid"===e.status,C=e=>void 0!==typeof Promise&&e instanceof Promise;var I;!function(e){e.errToObj=e=>"string"==typeof e?{message:e}:e||{},e.toString=e=>"string"==typeof e?e:null==e?void 0:e.message}(I||(I={}));class S{constructor(e,t,n,s){this.parent=e,this.data=t,this._path=n,this._key=s}get path(){return this._path.concat(this._key)}}const T=(e,t)=>{if(x(t))return{success:!0,data:t.value};if(!e.common.issues.length)throw new Error("Validation failed but no issues detected.");return{success:!1,error:new u(e.common.issues)}};function k(e){if(!e)return{};const{errorMap:t,invalid_type_error:n,required_error:s,description:a}=e;if(t&&(n||s))throw new Error('Can\'t use "invalid_type_error" or "required_error" in conjunction with custom error map.');if(t)return{errorMap:t,description:a};return{errorMap:(e,t)=>"invalid_type"!==e.code?{message:t.defaultError}:void 0===t.data?{message:null!=s?s:t.defaultError}:{message:null!=n?n:t.defaultError},description:a}}class E{constructor(e){this.spa=this.safeParseAsync,this.superRefine=this._refinement,this._def=e,this.parse=this.parse.bind(this),this.safeParse=this.safeParse.bind(this),this.parseAsync=this.parseAsync.bind(this),this.safeParseAsync=this.safeParseAsync.bind(this),this.spa=this.spa.bind(this),this.refine=this.refine.bind(this),this.refinement=this.refinement.bind(this),this.superRefine=this.superRefine.bind(this),this.optional=this.optional.bind(this),this.nullable=this.nullable.bind(this),this.nullish=this.nullish.bind(this),this.array=this.array.bind(this),this.promise=this.promise.bind(this),this.or=this.or.bind(this),this.and=this.and.bind(this),this.transform=this.transform.bind(this),this.default=this.default.bind(this),this.describe=this.describe.bind(this),this.isNullable=this.isNullable.bind(this),this.isOptional=this.isOptional.bind(this)}get description(){return this._def.description}_getType(e){return d(e.data)}_getOrReturnCtx(e,t){return t||{common:e.parent.common,data:e.data,parsedType:d(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}_processInputParams(e){return{status:new v,ctx:{common:e.parent.common,data:e.data,parsedType:d(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}}_parseSync(e){const t=this._parse(e);if(C(t))throw new Error("Synchronous parse encountered promise.");return t}_parseAsync(e){const t=this._parse(e);return Promise.resolve(t)}parse(e,t){const n=this.safeParse(e,t);if(n.success)return n.data;throw n.error}safeParse(e,t){var n;const s={common:{issues:[],async:null!==(n=null==t?void 0:t.async)&&void 0!==n&&n,contextualErrorMap:null==t?void 0:t.errorMap},path:(null==t?void 0:t.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:d(e)},a=this._parseSync({data:e,path:s.path,parent:s});return T(s,a)}async parseAsync(e,t){const n=await this.safeParseAsync(e,t);if(n.success)return n.data;throw n.error}async safeParseAsync(e,t){const n={common:{issues:[],contextualErrorMap:null==t?void 0:t.errorMap,async:!0},path:(null==t?void 0:t.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:d(e)},s=this._parse({data:e,path:[],parent:n}),a=await(C(s)?s:Promise.resolve(s));return T(n,a)}refine(e,t){const n=e=>"string"==typeof t||void 0===t?{message:t}:"function"==typeof t?t(e):t;return this._refinement(((t,s)=>{const a=e(t),r=()=>s.addIssue({code:l.custom,...n(t)});return"undefined"!=typeof Promise&&a instanceof Promise?a.then((e=>!!e||(r(),!1))):!!a||(r(),!1)}))}refinement(e,t){return this._refinement(((n,s)=>!!e(n)||(s.addIssue("function"==typeof t?t(n,s):t),!1)))}_refinement(e){return new le({schema:this,typeName:be.ZodEffects,effect:{type:"refinement",refinement:e}})}optional(){return ue.create(this)}nullable(){return pe.create(this)}nullish(){return this.optional().nullable()}array(){return $.create(this)}promise(){return de.create(this)}or(e){return q.create([this,e])}and(e){return Q.create(this,e)}transform(e){return new le({schema:this,typeName:be.ZodEffects,effect:{type:"transform",transform:e}})}default(e){return new he({innerType:this,defaultValue:"function"==typeof e?e:()=>e,typeName:be.ZodDefault})}brand(){return new fe({typeName:be.ZodBranded,type:this,...k(void 0)})}describe(e){return new(0,this.constructor)({...this._def,description:e})}isOptional(){return this.safeParse(void 0).success}isNullable(){return this.safeParse(null).success}}const O=/^c[^\s-]{8,}$/i,P=/^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i,D=/^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;class N extends E{constructor(){super(...arguments),this._regex=(e,t,n)=>this.refinement((t=>e.test(t)),{validation:t,code:l.invalid_string,...I.errToObj(n)}),this.nonempty=e=>this.min(1,I.errToObj(e)),this.trim=()=>new N({...this._def,checks:[...this._def.checks,{kind:"trim"}]})}_parse(e){if(this._getType(e)!==c.string){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.string,received:t.parsedType}),y}const t=new v;let n;for(const s of this._def.checks)if("min"===s.kind)e.data.length<s.value&&(n=this._getOrReturnCtx(e,n),f(n,{code:l.too_small,minimum:s.value,type:"string",inclusive:!0,message:s.message}),t.dirty());else if("max"===s.kind)e.data.length>s.value&&(n=this._getOrReturnCtx(e,n),f(n,{code:l.too_big,maximum:s.value,type:"string",inclusive:!0,message:s.message}),t.dirty());else if("email"===s.kind)D.test(e.data)||(n=this._getOrReturnCtx(e,n),f(n,{validation:"email",code:l.invalid_string,message:s.message}),t.dirty());else if("uuid"===s.kind)P.test(e.data)||(n=this._getOrReturnCtx(e,n),f(n,{validation:"uuid",code:l.invalid_string,message:s.message}),t.dirty());else if("cuid"===s.kind)O.test(e.data)||(n=this._getOrReturnCtx(e,n),f(n,{validation:"cuid",code:l.invalid_string,message:s.message}),t.dirty());else if("url"===s.kind)try{new URL(e.data)}catch(a){n=this._getOrReturnCtx(e,n),f(n,{validation:"url",code:l.invalid_string,message:s.message}),t.dirty()}else if("regex"===s.kind){s.regex.lastIndex=0;s.regex.test(e.data)||(n=this._getOrReturnCtx(e,n),f(n,{validation:"regex",code:l.invalid_string,message:s.message}),t.dirty())}else"trim"===s.kind?e.data=e.data.trim():"startsWith"===s.kind?e.data.startsWith(s.value)||(n=this._getOrReturnCtx(e,n),f(n,{code:l.invalid_string,validation:{startsWith:s.value},message:s.message}),t.dirty()):"endsWith"===s.kind?e.data.endsWith(s.value)||(n=this._getOrReturnCtx(e,n),f(n,{code:l.invalid_string,validation:{endsWith:s.value},message:s.message}),t.dirty()):o.assertNever(s);return{status:t.value,value:e.data}}_addCheck(e){return new N({...this._def,checks:[...this._def.checks,e]})}email(e){return this._addCheck({kind:"email",...I.errToObj(e)})}url(e){return this._addCheck({kind:"url",...I.errToObj(e)})}uuid(e){return this._addCheck({kind:"uuid",...I.errToObj(e)})}cuid(e){return this._addCheck({kind:"cuid",...I.errToObj(e)})}regex(e,t){return this._addCheck({kind:"regex",regex:e,...I.errToObj(t)})}startsWith(e,t){return this._addCheck({kind:"startsWith",value:e,...I.errToObj(t)})}endsWith(e,t){return this._addCheck({kind:"endsWith",value:e,...I.errToObj(t)})}min(e,t){return this._addCheck({kind:"min",value:e,...I.errToObj(t)})}max(e,t){return this._addCheck({kind:"max",value:e,...I.errToObj(t)})}length(e,t){return this.min(e,t).max(e,t)}get isEmail(){return!!this._def.checks.find((e=>"email"===e.kind))}get isURL(){return!!this._def.checks.find((e=>"url"===e.kind))}get isUUID(){return!!this._def.checks.find((e=>"uuid"===e.kind))}get isCUID(){return!!this._def.checks.find((e=>"cuid"===e.kind))}get minLength(){let e=null;for(const t of this._def.checks)"min"===t.kind&&(null===e||t.value>e)&&(e=t.value);return e}get maxLength(){let e=null;for(const t of this._def.checks)"max"===t.kind&&(null===e||t.value<e)&&(e=t.value);return e}}function M(e,t){const n=(e.toString().split(".")[1]||"").length,s=(t.toString().split(".")[1]||"").length,a=n>s?n:s;return parseInt(e.toFixed(a).replace(".",""))%parseInt(t.toFixed(a).replace(".",""))/Math.pow(10,a)}N.create=e=>new N({checks:[],typeName:be.ZodString,...k(e)});class j extends E{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte,this.step=this.multipleOf}_parse(e){if(this._getType(e)!==c.number){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.number,received:t.parsedType}),y}let t;const n=new v;for(const s of this._def.checks)if("int"===s.kind)o.isInteger(e.data)||(t=this._getOrReturnCtx(e,t),f(t,{code:l.invalid_type,expected:"integer",received:"float",message:s.message}),n.dirty());else if("min"===s.kind){(s.inclusive?e.data<s.value:e.data<=s.value)&&(t=this._getOrReturnCtx(e,t),f(t,{code:l.too_small,minimum:s.value,type:"number",inclusive:s.inclusive,message:s.message}),n.dirty())}else if("max"===s.kind){(s.inclusive?e.data>s.value:e.data>=s.value)&&(t=this._getOrReturnCtx(e,t),f(t,{code:l.too_big,maximum:s.value,type:"number",inclusive:s.inclusive,message:s.message}),n.dirty())}else"multipleOf"===s.kind?0!==M(e.data,s.value)&&(t=this._getOrReturnCtx(e,t),f(t,{code:l.not_multiple_of,multipleOf:s.value,message:s.message}),n.dirty()):o.assertNever(s);return{status:n.value,value:e.data}}gte(e,t){return this.setLimit("min",e,!0,I.toString(t))}gt(e,t){return this.setLimit("min",e,!1,I.toString(t))}lte(e,t){return this.setLimit("max",e,!0,I.toString(t))}lt(e,t){return this.setLimit("max",e,!1,I.toString(t))}setLimit(e,t,n,s){return new j({...this._def,checks:[...this._def.checks,{kind:e,value:t,inclusive:n,message:I.toString(s)}]})}_addCheck(e){return new j({...this._def,checks:[...this._def.checks,e]})}int(e){return this._addCheck({kind:"int",message:I.toString(e)})}positive(e){return this._addCheck({kind:"min",value:0,inclusive:!1,message:I.toString(e)})}negative(e){return this._addCheck({kind:"max",value:0,inclusive:!1,message:I.toString(e)})}nonpositive(e){return this._addCheck({kind:"max",value:0,inclusive:!0,message:I.toString(e)})}nonnegative(e){return this._addCheck({kind:"min",value:0,inclusive:!0,message:I.toString(e)})}multipleOf(e,t){return this._addCheck({kind:"multipleOf",value:e,message:I.toString(t)})}get minValue(){let e=null;for(const t of this._def.checks)"min"===t.kind&&(null===e||t.value>e)&&(e=t.value);return e}get maxValue(){let e=null;for(const t of this._def.checks)"max"===t.kind&&(null===e||t.value<e)&&(e=t.value);return e}get isInt(){return!!this._def.checks.find((e=>"int"===e.kind))}}j.create=e=>new j({checks:[],typeName:be.ZodNumber,...k(e)});class R extends E{_parse(e){if(this._getType(e)!==c.bigint){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.bigint,received:t.parsedType}),y}return b(e.data)}}R.create=e=>new R({typeName:be.ZodBigInt,...k(e)});class A extends E{_parse(e){if(this._getType(e)!==c.boolean){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.boolean,received:t.parsedType}),y}return b(e.data)}}A.create=e=>new A({typeName:be.ZodBoolean,...k(e)});class L extends E{_parse(e){if(this._getType(e)!==c.date){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.date,received:t.parsedType}),y}if(isNaN(e.data.getTime())){return f(this._getOrReturnCtx(e),{code:l.invalid_date}),y}const t=new v;let n;for(const s of this._def.checks)"min"===s.kind?e.data.getTime()<s.value&&(n=this._getOrReturnCtx(e,n),f(n,{code:l.too_small,message:s.message,inclusive:!0,minimum:s.value,type:"date"}),t.dirty()):"max"===s.kind?e.data.getTime()>s.value&&(n=this._getOrReturnCtx(e,n),f(n,{code:l.too_big,message:s.message,inclusive:!0,maximum:s.value,type:"date"}),t.dirty()):o.assertNever(s);return{status:t.value,value:new Date(e.data.getTime())}}_addCheck(e){return new L({...this._def,checks:[...this._def.checks,e]})}min(e,t){return this._addCheck({kind:"min",value:e.getTime(),message:I.toString(t)})}max(e,t){return this._addCheck({kind:"max",value:e.getTime(),message:I.toString(t)})}get minDate(){let e=null;for(const t of this._def.checks)"min"===t.kind&&(null===e||t.value>e)&&(e=t.value);return null!=e?new Date(e):null}get maxDate(){let e=null;for(const t of this._def.checks)"max"===t.kind&&(null===e||t.value<e)&&(e=t.value);return null!=e?new Date(e):null}}L.create=e=>new L({checks:[],typeName:be.ZodDate,...k(e)});class Z extends E{_parse(e){if(this._getType(e)!==c.undefined){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.undefined,received:t.parsedType}),y}return b(e.data)}}Z.create=e=>new Z({typeName:be.ZodUndefined,...k(e)});class B extends E{_parse(e){if(this._getType(e)!==c.null){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.null,received:t.parsedType}),y}return b(e.data)}}B.create=e=>new B({typeName:be.ZodNull,...k(e)});class F extends E{constructor(){super(...arguments),this._any=!0}_parse(e){return b(e.data)}}F.create=e=>new F({typeName:be.ZodAny,...k(e)});class z extends E{constructor(){super(...arguments),this._unknown=!0}_parse(e){return b(e.data)}}z.create=e=>new z({typeName:be.ZodUnknown,...k(e)});class V extends E{_parse(e){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.never,received:t.parsedType}),y}}V.create=e=>new V({typeName:be.ZodNever,...k(e)});class U extends E{_parse(e){if(this._getType(e)!==c.undefined){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.void,received:t.parsedType}),y}return b(e.data)}}U.create=e=>new U({typeName:be.ZodVoid,...k(e)});class $ extends E{_parse(e){const{ctx:t,status:n}=this._processInputParams(e),s=this._def;if(t.parsedType!==c.array)return f(t,{code:l.invalid_type,expected:c.array,received:t.parsedType}),y;if(null!==s.minLength&&t.data.length<s.minLength.value&&(f(t,{code:l.too_small,minimum:s.minLength.value,type:"array",inclusive:!0,message:s.minLength.message}),n.dirty()),null!==s.maxLength&&t.data.length>s.maxLength.value&&(f(t,{code:l.too_big,maximum:s.maxLength.value,type:"array",inclusive:!0,message:s.maxLength.message}),n.dirty()),t.common.async)return Promise.all(t.data.map(((e,n)=>s.type._parseAsync(new S(t,e,t.path,n))))).then((e=>v.mergeArray(n,e)));const a=t.data.map(((e,n)=>s.type._parseSync(new S(t,e,t.path,n))));return v.mergeArray(n,a)}get element(){return this._def.type}min(e,t){return new $({...this._def,minLength:{value:e,message:I.toString(t)}})}max(e,t){return new $({...this._def,maxLength:{value:e,message:I.toString(t)}})}length(e,t){return this.min(e,t).max(e,t)}nonempty(e){return this.min(1,e)}}var W;$.create=(e,t)=>new $({type:e,minLength:null,maxLength:null,typeName:be.ZodArray,...k(t)}),function(e){e.mergeShapes=(e,t)=>({...e,...t})}(W||(W={}));const K=e=>t=>new J({...e,shape:()=>({...e.shape(),...t})});function H(e){if(e instanceof J){const t={};for(const n in e.shape){const s=e.shape[n];t[n]=ue.create(H(s))}return new J({...e._def,shape:()=>t})}return e instanceof $?$.create(H(e.element)):e instanceof ue?ue.create(H(e.unwrap())):e instanceof pe?pe.create(H(e.unwrap())):e instanceof Y?Y.create(e.items.map((e=>H(e)))):e}class J extends E{constructor(){super(...arguments),this._cached=null,this.nonstrict=this.passthrough,this.augment=K(this._def),this.extend=K(this._def)}_getCached(){if(null!==this._cached)return this._cached;const e=this._def.shape(),t=o.objectKeys(e);return this._cached={shape:e,keys:t}}_parse(e){if(this._getType(e)!==c.object){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.object,received:t.parsedType}),y}const{status:t,ctx:n}=this._processInputParams(e),{shape:s,keys:a}=this._getCached(),r=[];if(!(this._def.catchall instanceof V&&"strip"===this._def.unknownKeys))for(const e in n.data)a.includes(e)||r.push(e);const i=[];for(const e of a){const t=s[e],a=n.data[e];i.push({key:{status:"valid",value:e},value:t._parse(new S(n,a,n.path,e)),alwaysSet:e in n.data})}if(this._def.catchall instanceof V){const e=this._def.unknownKeys;if("passthrough"===e)for(const e of r)i.push({key:{status:"valid",value:e},value:{status:"valid",value:n.data[e]}});else if("strict"===e)r.length>0&&(f(n,{code:l.unrecognized_keys,keys:r}),t.dirty());else if("strip"!==e)throw new Error("Internal ZodObject error: invalid unknownKeys value.")}else{const e=this._def.catchall;for(const t of r){const s=n.data[t];i.push({key:{status:"valid",value:t},value:e._parse(new S(n,s,n.path,t)),alwaysSet:t in n.data})}}return n.common.async?Promise.resolve().then((async()=>{const e=[];for(const t of i){const n=await t.key;e.push({key:n,value:await t.value,alwaysSet:t.alwaysSet})}return e})).then((e=>v.mergeObjectSync(t,e))):v.mergeObjectSync(t,i)}get shape(){return this._def.shape()}strict(e){return I.errToObj,new J({...this._def,unknownKeys:"strict",...void 0!==e?{errorMap:(t,n)=>{var s,a,r,i;const o=null!==(r=null===(a=(s=this._def).errorMap)||void 0===a?void 0:a.call(s,t,n).message)&&void 0!==r?r:n.defaultError;return"unrecognized_keys"===t.code?{message:null!==(i=I.errToObj(e).message)&&void 0!==i?i:o}:{message:o}}}:{}})}strip(){return new J({...this._def,unknownKeys:"strip"})}passthrough(){return new J({...this._def,unknownKeys:"passthrough"})}setKey(e,t){return this.augment({[e]:t})}merge(e){return new J({unknownKeys:e._def.unknownKeys,catchall:e._def.catchall,shape:()=>W.mergeShapes(this._def.shape(),e._def.shape()),typeName:be.ZodObject})}catchall(e){return new J({...this._def,catchall:e})}pick(e){const t={};return o.objectKeys(e).map((e=>{this.shape[e]&&(t[e]=this.shape[e])})),new J({...this._def,shape:()=>t})}omit(e){const t={};return o.objectKeys(this.shape).map((n=>{-1===o.objectKeys(e).indexOf(n)&&(t[n]=this.shape[n])})),new J({...this._def,shape:()=>t})}deepPartial(){return H(this)}partial(e){const t={};if(e)return o.objectKeys(this.shape).map((n=>{-1===o.objectKeys(e).indexOf(n)?t[n]=this.shape[n]:t[n]=this.shape[n].optional()})),new J({...this._def,shape:()=>t});for(const e in this.shape){const n=this.shape[e];t[e]=n.optional()}return new J({...this._def,shape:()=>t})}required(){const e={};for(const t in this.shape){let n=this.shape[t];for(;n instanceof ue;)n=n._def.innerType;e[t]=n}return new J({...this._def,shape:()=>e})}keyof(){return ie(o.objectKeys(this.shape))}}J.create=(e,t)=>new J({shape:()=>e,unknownKeys:"strip",catchall:V.create(),typeName:be.ZodObject,...k(t)}),J.strictCreate=(e,t)=>new J({shape:()=>e,unknownKeys:"strict",catchall:V.create(),typeName:be.ZodObject,...k(t)}),J.lazycreate=(e,t)=>new J({shape:e,unknownKeys:"strip",catchall:V.create(),typeName:be.ZodObject,...k(t)});class q extends E{_parse(e){const{ctx:t}=this._processInputParams(e),n=this._def.options;if(t.common.async)return Promise.all(n.map((async e=>{const n={...t,common:{...t.common,issues:[]},parent:null};return{result:await e._parseAsync({data:t.data,path:t.path,parent:n}),ctx:n}}))).then((function(e){for(const t of e)if("valid"===t.result.status)return t.result;for(const n of e)if("dirty"===n.result.status)return t.common.issues.push(...n.ctx.common.issues),n.result;const n=e.map((e=>new u(e.ctx.common.issues)));return f(t,{code:l.invalid_union,unionErrors:n}),y}));{let e;const s=[];for(const a of n){const n={...t,common:{...t.common,issues:[]},parent:null},r=a._parseSync({data:t.data,path:t.path,parent:n});if("valid"===r.status)return r;"dirty"!==r.status||e||(e={result:r,ctx:n}),n.common.issues.length&&s.push(n.common.issues)}if(e)return t.common.issues.push(...e.ctx.common.issues),e.result;const a=s.map((e=>new u(e)));return f(t,{code:l.invalid_union,unionErrors:a}),y}}get options(){return this._def.options}}q.create=(e,t)=>new q({options:e,typeName:be.ZodUnion,...k(t)});class G extends E{_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==c.object)return f(t,{code:l.invalid_type,expected:c.object,received:t.parsedType}),y;const n=this.discriminator,s=t.data[n],a=this.options.get(s);return a?t.common.async?a._parseAsync({data:t.data,path:t.path,parent:t}):a._parseSync({data:t.data,path:t.path,parent:t}):(f(t,{code:l.invalid_union_discriminator,options:this.validDiscriminatorValues,path:[n]}),y)}get discriminator(){return this._def.discriminator}get validDiscriminatorValues(){return Array.from(this.options.keys())}get options(){return this._def.options}static create(e,t,n){const s=new Map;try{t.forEach((t=>{const n=t.shape[e].value;s.set(n,t)}))}catch(e){throw new Error("The discriminator value could not be extracted from all the provided schemas")}if(s.size!==t.length)throw new Error("Some of the discriminator values are not unique");return new G({typeName:be.ZodDiscriminatedUnion,discriminator:e,options:s,...k(n)})}}function X(e,t){const n=d(e),s=d(t);if(e===t)return{valid:!0,data:e};if(n===c.object&&s===c.object){const n=o.objectKeys(t),s=o.objectKeys(e).filter((e=>-1!==n.indexOf(e))),a={...e,...t};for(const n of s){const s=X(e[n],t[n]);if(!s.valid)return{valid:!1};a[n]=s.data}return{valid:!0,data:a}}if(n===c.array&&s===c.array){if(e.length!==t.length)return{valid:!1};const n=[];for(let s=0;s<e.length;s++){const a=X(e[s],t[s]);if(!a.valid)return{valid:!1};n.push(a.data)}return{valid:!0,data:n}}return n===c.date&&s===c.date&&+e==+t?{valid:!0,data:e}:{valid:!1}}class Q extends E{_parse(e){const{status:t,ctx:n}=this._processInputParams(e),s=(e,s)=>{if(w(e)||w(s))return y;const a=X(e.value,s.value);return a.valid?((_(e)||_(s))&&t.dirty(),{status:t.value,value:a.data}):(f(n,{code:l.invalid_intersection_types}),y)};return n.common.async?Promise.all([this._def.left._parseAsync({data:n.data,path:n.path,parent:n}),this._def.right._parseAsync({data:n.data,path:n.path,parent:n})]).then((([e,t])=>s(e,t))):s(this._def.left._parseSync({data:n.data,path:n.path,parent:n}),this._def.right._parseSync({data:n.data,path:n.path,parent:n}))}}Q.create=(e,t,n)=>new Q({left:e,right:t,typeName:be.ZodIntersection,...k(n)});class Y extends E{_parse(e){const{status:t,ctx:n}=this._processInputParams(e);if(n.parsedType!==c.array)return f(n,{code:l.invalid_type,expected:c.array,received:n.parsedType}),y;if(n.data.length<this._def.items.length)return f(n,{code:l.too_small,minimum:this._def.items.length,inclusive:!0,type:"array"}),y;!this._def.rest&&n.data.length>this._def.items.length&&(f(n,{code:l.too_big,maximum:this._def.items.length,inclusive:!0,type:"array"}),t.dirty());const s=n.data.map(((e,t)=>{const s=this._def.items[t]||this._def.rest;return s?s._parse(new S(n,e,n.path,t)):null})).filter((e=>!!e));return n.common.async?Promise.all(s).then((e=>v.mergeArray(t,e))):v.mergeArray(t,s)}get items(){return this._def.items}rest(e){return new Y({...this._def,rest:e})}}Y.create=(e,t)=>{if(!Array.isArray(e))throw new Error("You must pass an array of schemas to z.tuple([ ... ])");return new Y({items:e,typeName:be.ZodTuple,rest:null,...k(t)})};class ee extends E{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(e){const{status:t,ctx:n}=this._processInputParams(e);if(n.parsedType!==c.object)return f(n,{code:l.invalid_type,expected:c.object,received:n.parsedType}),y;const s=[],a=this._def.keyType,r=this._def.valueType;for(const e in n.data)s.push({key:a._parse(new S(n,e,n.path,e)),value:r._parse(new S(n,n.data[e],n.path,e))});return n.common.async?v.mergeObjectAsync(t,s):v.mergeObjectSync(t,s)}get element(){return this._def.valueType}static create(e,t,n){return new ee(t instanceof E?{keyType:e,valueType:t,typeName:be.ZodRecord,...k(n)}:{keyType:N.create(),valueType:e,typeName:be.ZodRecord,...k(t)})}}class te extends E{_parse(e){const{status:t,ctx:n}=this._processInputParams(e);if(n.parsedType!==c.map)return f(n,{code:l.invalid_type,expected:c.map,received:n.parsedType}),y;const s=this._def.keyType,a=this._def.valueType,r=[...n.data.entries()].map((([e,t],r)=>({key:s._parse(new S(n,e,n.path,[r,"key"])),value:a._parse(new S(n,t,n.path,[r,"value"]))})));if(n.common.async){const e=new Map;return Promise.resolve().then((async()=>{for(const n of r){const s=await n.key,a=await n.value;if("aborted"===s.status||"aborted"===a.status)return y;"dirty"!==s.status&&"dirty"!==a.status||t.dirty(),e.set(s.value,a.value)}return{status:t.value,value:e}}))}{const e=new Map;for(const n of r){const s=n.key,a=n.value;if("aborted"===s.status||"aborted"===a.status)return y;"dirty"!==s.status&&"dirty"!==a.status||t.dirty(),e.set(s.value,a.value)}return{status:t.value,value:e}}}}te.create=(e,t,n)=>new te({valueType:t,keyType:e,typeName:be.ZodMap,...k(n)});class ne extends E{_parse(e){const{status:t,ctx:n}=this._processInputParams(e);if(n.parsedType!==c.set)return f(n,{code:l.invalid_type,expected:c.set,received:n.parsedType}),y;const s=this._def;null!==s.minSize&&n.data.size<s.minSize.value&&(f(n,{code:l.too_small,minimum:s.minSize.value,type:"set",inclusive:!0,message:s.minSize.message}),t.dirty()),null!==s.maxSize&&n.data.size>s.maxSize.value&&(f(n,{code:l.too_big,maximum:s.maxSize.value,type:"set",inclusive:!0,message:s.maxSize.message}),t.dirty());const a=this._def.valueType;function r(e){const n=new Set;for(const s of e){if("aborted"===s.status)return y;"dirty"===s.status&&t.dirty(),n.add(s.value)}return{status:t.value,value:n}}const i=[...n.data.values()].map(((e,t)=>a._parse(new S(n,e,n.path,t))));return n.common.async?Promise.all(i).then((e=>r(e))):r(i)}min(e,t){return new ne({...this._def,minSize:{value:e,message:I.toString(t)}})}max(e,t){return new ne({...this._def,maxSize:{value:e,message:I.toString(t)}})}size(e,t){return this.min(e,t).max(e,t)}nonempty(e){return this.min(1,e)}}ne.create=(e,t)=>new ne({valueType:e,minSize:null,maxSize:null,typeName:be.ZodSet,...k(t)});class se extends E{constructor(){super(...arguments),this.validate=this.implement}_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==c.function)return f(t,{code:l.invalid_type,expected:c.function,received:t.parsedType}),y;function n(e,n){return g({data:e,path:t.path,errorMaps:[t.common.contextualErrorMap,t.schemaErrorMap,m(),p].filter((e=>!!e)),issueData:{code:l.invalid_arguments,argumentsError:n}})}function s(e,n){return g({data:e,path:t.path,errorMaps:[t.common.contextualErrorMap,t.schemaErrorMap,m(),p].filter((e=>!!e)),issueData:{code:l.invalid_return_type,returnTypeError:n}})}const a={errorMap:t.common.contextualErrorMap},r=t.data;return this._def.returns instanceof de?b((async(...e)=>{const t=new u([]),i=await this._def.args.parseAsync(e,a).catch((s=>{throw t.addIssue(n(e,s)),t})),o=await r(...i);return await this._def.returns._def.type.parseAsync(o,a).catch((e=>{throw t.addIssue(s(o,e)),t}))})):b(((...e)=>{const t=this._def.args.safeParse(e,a);if(!t.success)throw new u([n(e,t.error)]);const i=r(...t.data),o=this._def.returns.safeParse(i,a);if(!o.success)throw new u([s(i,o.error)]);return o.data}))}parameters(){return this._def.args}returnType(){return this._def.returns}args(...e){return new se({...this._def,args:Y.create(e).rest(z.create())})}returns(e){return new se({...this._def,returns:e})}implement(e){return this.parse(e)}strictImplement(e){return this.parse(e)}static create(e,t,n){return new se({args:e||Y.create([]).rest(z.create()),returns:t||z.create(),typeName:be.ZodFunction,...k(n)})}}class ae extends E{get schema(){return this._def.getter()}_parse(e){const{ctx:t}=this._processInputParams(e);return this._def.getter()._parse({data:t.data,path:t.path,parent:t})}}ae.create=(e,t)=>new ae({getter:e,typeName:be.ZodLazy,...k(t)});class re extends E{_parse(e){if(e.data!==this._def.value){return f(this._getOrReturnCtx(e),{code:l.invalid_literal,expected:this._def.value}),y}return{status:"valid",value:e.data}}get value(){return this._def.value}}function ie(e,t){return new oe({values:e,typeName:be.ZodEnum,...k(t)})}re.create=(e,t)=>new re({value:e,typeName:be.ZodLiteral,...k(t)});class oe extends E{_parse(e){if("string"!=typeof e.data){const t=this._getOrReturnCtx(e),n=this._def.values;return f(t,{expected:o.joinValues(n),received:t.parsedType,code:l.invalid_type}),y}if(-1===this._def.values.indexOf(e.data)){const t=this._getOrReturnCtx(e),n=this._def.values;return f(t,{received:t.data,code:l.invalid_enum_value,options:n}),y}return b(e.data)}get options(){return this._def.values}get enum(){const e={};for(const t of this._def.values)e[t]=t;return e}get Values(){const e={};for(const t of this._def.values)e[t]=t;return e}get Enum(){const e={};for(const t of this._def.values)e[t]=t;return e}}oe.create=ie;class ce extends E{_parse(e){const t=o.getValidEnumValues(this._def.values),n=this._getOrReturnCtx(e);if(n.parsedType!==c.string&&n.parsedType!==c.number){const e=o.objectValues(t);return f(n,{expected:o.joinValues(e),received:n.parsedType,code:l.invalid_type}),y}if(-1===t.indexOf(e.data)){const e=o.objectValues(t);return f(n,{received:n.data,code:l.invalid_enum_value,options:e}),y}return b(e.data)}get enum(){return this._def.values}}ce.create=(e,t)=>new ce({values:e,typeName:be.ZodNativeEnum,...k(t)});class de extends E{_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==c.promise&&!1===t.common.async)return f(t,{code:l.invalid_type,expected:c.promise,received:t.parsedType}),y;const n=t.parsedType===c.promise?t.data:Promise.resolve(t.data);return b(n.then((e=>this._def.type.parseAsync(e,{path:t.path,errorMap:t.common.contextualErrorMap}))))}}de.create=(e,t)=>new de({type:e,typeName:be.ZodPromise,...k(t)});class le extends E{innerType(){return this._def.schema}_parse(e){const{status:t,ctx:n}=this._processInputParams(e),s=this._def.effect||null;if("preprocess"===s.type){const e=s.transform(n.data);return n.common.async?Promise.resolve(e).then((e=>this._def.schema._parseAsync({data:e,path:n.path,parent:n}))):this._def.schema._parseSync({data:e,path:n.path,parent:n})}const a={addIssue:e=>{f(n,e),e.fatal?t.abort():t.dirty()},get path(){return n.path}};if(a.addIssue=a.addIssue.bind(a),"refinement"===s.type){const e=e=>{const t=s.refinement(e,a);if(n.common.async)return Promise.resolve(t);if(t instanceof Promise)throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");return e};if(!1===n.common.async){const s=this._def.schema._parseSync({data:n.data,path:n.path,parent:n});return"aborted"===s.status?y:("dirty"===s.status&&t.dirty(),e(s.value),{status:t.value,value:s.value})}return this._def.schema._parseAsync({data:n.data,path:n.path,parent:n}).then((n=>"aborted"===n.status?y:("dirty"===n.status&&t.dirty(),e(n.value).then((()=>({status:t.value,value:n.value}))))))}if("transform"===s.type){if(!1===n.common.async){const e=this._def.schema._parseSync({data:n.data,path:n.path,parent:n});if(!x(e))return e;const r=s.transform(e.value,a);if(r instanceof Promise)throw new Error("Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.");return{status:t.value,value:r}}return this._def.schema._parseAsync({data:n.data,path:n.path,parent:n}).then((e=>x(e)?Promise.resolve(s.transform(e.value,a)).then((e=>({status:t.value,value:e}))):e))}o.assertNever(s)}}le.create=(e,t,n)=>new le({schema:e,typeName:be.ZodEffects,effect:t,...k(n)}),le.createWithPreprocess=(e,t,n)=>new le({schema:t,effect:{type:"preprocess",transform:e},typeName:be.ZodEffects,...k(n)});class ue extends E{_parse(e){return this._getType(e)===c.undefined?b(void 0):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}}ue.create=(e,t)=>new ue({innerType:e,typeName:be.ZodOptional,...k(t)});class pe extends E{_parse(e){return this._getType(e)===c.null?b(null):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}}pe.create=(e,t)=>new pe({innerType:e,typeName:be.ZodNullable,...k(t)});class he extends E{_parse(e){const{ctx:t}=this._processInputParams(e);let n=t.data;return t.parsedType===c.undefined&&(n=this._def.defaultValue()),this._def.innerType._parse({data:n,path:t.path,parent:t})}removeDefault(){return this._def.innerType}}he.create=(e,t)=>new ue({innerType:e,typeName:be.ZodOptional,...k(t)});class me extends E{_parse(e){if(this._getType(e)!==c.nan){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.nan,received:t.parsedType}),y}return{status:"valid",value:e.data}}}me.create=e=>new me({typeName:be.ZodNaN,...k(e)});const ge=Symbol("zod_brand");class fe extends E{_parse(e){const{ctx:t}=this._processInputParams(e),n=t.data;return this._def.type._parse({data:n,path:t.path,parent:t})}unwrap(){return this._def.type}}const ve=(e,t={},n)=>e?F.create().superRefine(((s,a)=>{if(!e(s)){const e="function"==typeof t?t(s):t,r="string"==typeof e?{message:e}:e;a.addIssue({code:"custom",...r,fatal:n})}})):F.create(),ye={object:J.lazycreate};var be;!function(e){e.ZodString="ZodString",e.ZodNumber="ZodNumber",e.ZodNaN="ZodNaN",e.ZodBigInt="ZodBigInt",e.ZodBoolean="ZodBoolean",e.ZodDate="ZodDate",e.ZodUndefined="ZodUndefined",e.ZodNull="ZodNull",e.ZodAny="ZodAny",e.ZodUnknown="ZodUnknown",e.ZodNever="ZodNever",e.ZodVoid="ZodVoid",e.ZodArray="ZodArray",e.ZodObject="ZodObject",e.ZodUnion="ZodUnion",e.ZodDiscriminatedUnion="ZodDiscriminatedUnion",e.ZodIntersection="ZodIntersection",e.ZodTuple="ZodTuple",e.ZodRecord="ZodRecord",e.ZodMap="ZodMap",e.ZodSet="ZodSet",e.ZodFunction="ZodFunction",e.ZodLazy="ZodLazy",e.ZodLiteral="ZodLiteral",e.ZodEnum="ZodEnum",e.ZodEffects="ZodEffects",e.ZodNativeEnum="ZodNativeEnum",e.ZodOptional="ZodOptional",e.ZodNullable="ZodNullable",e.ZodDefault="ZodDefault",e.ZodPromise="ZodPromise",e.ZodBranded="ZodBranded"}(be||(be={}));const we=N.create,_e=j.create,xe=me.create,Ce=R.create,Ie=A.create,Se=L.create,Te=Z.create,ke=B.create,Ee=F.create,Oe=z.create,Pe=V.create,De=U.create,Ne=$.create,Me=J.create,je=J.strictCreate,Re=q.create,Ae=G.create,Le=Q.create,Ze=Y.create,Be=ee.create,Fe=te.create,ze=ne.create,Ve=se.create,Ue=ae.create,$e=re.create,We=oe.create,Ke=ce.create,He=de.create,Je=le.create,qe=ue.create,Ge=pe.create,Xe=le.createWithPreprocess,Qe=y;var Ye=Object.freeze({__proto__:null,getParsedType:d,ZodParsedType:c,defaultErrorMap:p,setErrorMap:function(e){h=e},getErrorMap:m,makeIssue:g,EMPTY_PATH:[],addIssueToContext:f,ParseStatus:v,INVALID:y,DIRTY:e=>({status:"dirty",value:e}),OK:b,isAborted:w,isDirty:_,isValid:x,isAsync:C,ZodType:E,ZodString:N,ZodNumber:j,ZodBigInt:R,ZodBoolean:A,ZodDate:L,ZodUndefined:Z,ZodNull:B,ZodAny:F,ZodUnknown:z,ZodNever:V,ZodVoid:U,ZodArray:$,get objectUtil(){return W},ZodObject:J,ZodUnion:q,ZodDiscriminatedUnion:G,ZodIntersection:Q,ZodTuple:Y,ZodRecord:ee,ZodMap:te,ZodSet:ne,ZodFunction:se,ZodLazy:ae,ZodLiteral:re,ZodEnum:oe,ZodNativeEnum:ce,ZodPromise:de,ZodEffects:le,ZodTransformer:le,ZodOptional:ue,ZodNullable:pe,ZodDefault:he,ZodNaN:me,BRAND:ge,ZodBranded:fe,custom:ve,Schema:E,ZodSchema:E,late:ye,get ZodFirstPartyTypeKind(){return be},any:Ee,array:Ne,bigint:Ce,boolean:Ie,date:Se,discriminatedUnion:Ae,effect:Je,enum:We,function:Ve,instanceof:(e,t={message:`Input not instance of ${e.name}`})=>ve((t=>t instanceof e),t,!0),intersection:Le,lazy:Ue,literal:$e,map:Fe,nan:xe,nativeEnum:Ke,never:Pe,null:ke,nullable:Ge,number:_e,object:Me,oboolean:()=>Ie().optional(),onumber:()=>_e().optional(),optional:qe,ostring:()=>we().optional(),preprocess:Xe,promise:He,record:Be,set:ze,strictObject:je,string:we,transformer:Je,tuple:Ze,undefined:Te,union:Re,unknown:Oe,void:De,NEVER:Qe,ZodIssueCode:l,quotelessJson:e=>JSON.stringify(e,null,2).replace(/"([^"]+)":/g,"$1:"),ZodError:u});const et=e("command parser");function tt(e,t){const n=t.safeParse(e);if(n.success)return n.data;et(`Command ${JSON.stringify(e)} parse failed: ${JSON.stringify(n)}.`);const s=n.error.errors.map((e=>`${e.message} in ${e.path.map((e=>JSON.stringify(e))).join("/")}.`)).join(" ");throw new a(s)}var nt,st,at,rt,it,ot;!function(e){e.RemoteReferenceSchema=Ye.object({handle:Ye.string().min(1)});const t=Ye.object({type:Ye.literal("undefined")}),n=Ye.object({type:Ye.literal("null")}),s=Ye.object({type:Ye.literal("string"),value:Ye.string()}),a=Ye.enum(["NaN","-0","Infinity","+Infinity","-Infinity"]),r=Ye.object({type:Ye.literal("number"),value:Ye.union([a,Ye.number()])}),i=Ye.object({type:Ye.literal("boolean"),value:Ye.boolean()}),o=Ye.object({type:Ye.literal("bigint"),value:Ye.string()}),c=Ye.union([t,n,s,r,i,o]);e.LocalValueSchema=Ye.lazy((()=>Ye.union([c,u,p,m,g,f,v])));const d=Ye.union([e.RemoteReferenceSchema,e.LocalValueSchema]),l=Ye.array(d),u=Ye.lazy((()=>Ye.object({type:Ye.literal("array"),value:l}))),p=Ye.object({type:Ye.literal("date"),value:Ye.string().min(1)}),h=Ye.lazy((()=>Ye.tuple([Ye.union([Ye.string(),d]),d]))),m=Ye.object({type:Ye.literal("map"),value:Ye.array(h)}),g=Ye.object({type:Ye.literal("object"),value:Ye.array(h)}),f=Ye.lazy((()=>Ye.object({type:Ye.literal("regexp"),value:Ye.object({pattern:Ye.string(),flags:Ye.string().optional()})}))),v=Ye.lazy((()=>Ye.object({type:Ye.literal("set"),value:l})));e.BrowsingContextSchema=Ye.string()}(nt||(nt={})),function(e){const t=Ye.enum(["window","dedicated-worker","shared-worker","service-worker","worker","paint-worklet","audio-worklet","worklet"]),n=Ye.object({context:nt.BrowsingContextSchema.optional(),type:t.optional()});e.parseGetRealmsParams=function(e){return tt(e,n)};const s=Ye.object({context:nt.BrowsingContextSchema,sandbox:Ye.string().optional()}),a=Ye.object({realm:Ye.string().min(1)}),r=Ye.union([a,s]),i=Ye.enum(["root","none"]),o=Ye.object({expression:Ye.string(),awaitPromise:Ye.boolean(),target:r,resultOwnership:i.optional()});e.parseEvaluateParams=function(e){return tt(e,o)};const c=Ye.object({target:r,handles:Ye.array(Ye.string())});e.parseDisownParams=function(e){return tt(e,c)};const d=Ye.union([nt.RemoteReferenceSchema,nt.LocalValueSchema]),l=Ye.object({functionDeclaration:Ye.string(),target:r,arguments:Ye.array(d).optional(),this:d.optional(),awaitPromise:Ye.boolean(),resultOwnership:i.optional()});e.parseCallFunctionParams=function(e){return tt(e,l)}}(st||(st={})),function(e){const t=Ye.object({maxDepth:Ye.number().int().nonnegative().max(9007199254740991).optional(),root:nt.BrowsingContextSchema.optional()});e.parseGetTreeParams=function(e){return tt(e,t)};const n=Ye.enum(["none","interactive","complete"]),s=Ye.object({context:nt.BrowsingContextSchema,url:Ye.string().url(),wait:n.optional()});e.parseNavigateParams=function(e){return tt(e,s)};const a=Ye.object({type:Ye.enum(["tab","window"])});e.parseCreateParams=function(e){return tt(e,a)};const r=Ye.object({context:nt.BrowsingContextSchema});e.parseCloseParams=function(e){return tt(e,r)};class o extends i{static method="browsingContext.load";constructor(e){super(o.method,e)}}e.LoadEvent=o;class c extends i{static method="browsingContext.domContentLoaded";constructor(e){super(c.method,e)}}e.DomContentLoadedEvent=c;class d extends i{static method="browsingContext.contextCreated";constructor(e){super(d.method,e)}}e.ContextCreatedEvent=d;class l extends i{static method="browsingContext.contextDestroyed";constructor(e){super(l.method,e)}}e.ContextDestroyedEvent=l,function(e){const t=Ye.object({context:nt.BrowsingContextSchema,selector:Ye.string()});e.parseFindElementParams=function(e){return tt(e,t)}}(e.PROTO||(e.PROTO={})),e.EventNames=[o.method,c.method,d.method,l.method]}(at||(at={})),function(e){class t extends i{static method="log.entryAdded";constructor(e){super(t.method,e)}}e.LogEntryAddedEvent=t,e.EventNames=[t.method]}(rt||(rt={})),function(e){const t=Ye.object({cdpMethod:Ye.string(),cdpParams:Ye.object({}).passthrough(),cdpSession:Ye.string().optional()});e.parseSendCommandParams=function(e){return tt(e,t)};const n=Ye.object({context:nt.BrowsingContextSchema});e.parseGetSessionParams=function(e){return tt(e,n)};class s extends i{static method="cdp.eventReceived";constructor(e){super(s.method,e)}}e.EventReceivedEvent=s,e.EventNames=[s.method]}(it||(it={})),function(e){const t=Ye.enum([...at.EventNames,...rt.EventNames,...it.EventNames]),n=Ye.object({events:Ye.array(t),contexts:Ye.array(nt.BrowsingContextSchema).optional()});e.parseSubscribeParams=function(e){return tt(e,n)}}(ot||(ot={}));class ct{#e=()=>{};#t=()=>{};#n;#s=!1;get isFinished(){return this.#s}constructor(){this.#n=new Promise(((e,t)=>{this.#e=e,this.#t=t}))}then(e,t){return this.#n.then(e,t)}catch(e){return this.#n.catch(e)}resolve(e){this.#s=!0,this.#e(e)}reject(e){this.#s=!0,this.#t(e)}finally(e){return this.#n.finally(e)}[Symbol.toStringTag]="Promise"}const dt=["%s","%d","%i","%f","%o","%O","%c"];function lt(e){return dt.some((t=>e.includes(t)))}function ut(e){if(!["array","bigint","date","number","object","string"].includes(e.type))return pt(e);if("bigint"===e.type)return e.value.toString()+"n";if("number"===e.type)return e.value.toString();if(["date","string"].includes(e.type))return JSON.stringify(e.value);if("object"===e.type)return"{"+e.value.map((e=>`${JSON.stringify(e[0])}:${ut(e[1])}`)).join(",")+"}";if("array"===e.type)return"["+e.value.map((e=>ut(e))).join(",")+"]";throw Error("Invalid value type: "+e.toString())}function pt(e){if(!e.hasOwnProperty("value"))return e.type;switch(e.type){case"string":case"number":case"boolean":case"bigint":return e.value;case"regexp":return`/${e.value.pattern}/${e.value.flags}`;case"date":return new Date(e.value).toString();case"object":return`Object(${e.value.length})`;case"array":return`Array(${e.value.length})`;case"map":return`Map(${e.value.length})`;case"set":return`Set(${e.value.length})`;case"node":return"node";default:return e.type}}function ht(e,t){return 0==e.length?"":"string"===e[0].type&&lt(e[0].value.toString())&&t?function(e){let t="";const n=e[0].value.toString(),s=e.slice(1,void 0),a=n.split(new RegExp(dt.map((e=>"("+e+")")).join("|"),"g"));for(const n of a)if(void 0!==n&&""!=n)if(lt(n)){const a=s.shift();if(void 0===a)throw new Error('Less value is provided: "'+ht(e,!1)+'"');"%s"===n?t+=pt(a):"%d"===n||"%i"===n?["bigint","number","string"].includes(a.type)?t+=parseInt(a.value.toString(),10):t+="NaN":"%f"===n?["bigint","number","string"].includes(a.type)?t+=parseFloat(a.value.toString()):t+="NaN":t+=ut(a)}else t+=n;if(s.length>0)throw new Error('More value is provided: "'+ht(e,!1)+'"');return t}(e):e.map((e=>pt(e))).join(" ")}class mt{static#a=0;static#r=1;static#i=new Map;static async serializeCdpObject(e,t,n){const s=await n.cdpClient.Runtime.callFunctionOn({functionDeclaration:String((e=>e)),awaitPromise:!1,arguments:[e],generateWebDriverValue:!0,executionContextId:n.executionContextId});return await this.#o(s,n,t)}static async stringifyObject(e,t){return(await t.cdpClient.Runtime.callFunctionOn({functionDeclaration:String((function(e){return String(e)})),awaitPromise:!1,arguments:[e],returnByValue:!0,executionContextId:t.executionContextId})).result.value}static async callFunction(e,t,n,s,r,i){const o=`(...args)=>{ return _callFunction((\n${t}\n), args);\n      function _callFunction(f, args) {\n        const deserializedThis = args.shift();\n        const deserializedArgs = args;\n        return f.apply(deserializedThis, deserializedArgs);\n      }}`,c=[await this.#c(n,e)];let d;c.push(...await Promise.all(s.map((async t=>await this.#c(t,e)))));try{d=await e.cdpClient.Runtime.callFunctionOn({functionDeclaration:o,awaitPromise:r,arguments:c,generateWebDriverValue:!0,executionContextId:e.executionContextId})}catch(e){if(-32e3===e.code&&["Could not find object with given id","Argument should belong to the same JavaScript world as target object"].includes(e.message))throw new a("Handle was not found.");throw e}return d.exceptionDetails?{exceptionDetails:await this.#d(d.exceptionDetails,this.#r,i,e),realm:e.realmId}:{result:await mt.#o(d,e,i),realm:e.realmId}}static realmDestroyed(e){return Array.from(this.#i.entries()).filter((([t,n])=>n===e.realmId)).map((([e,t])=>this.#i.delete(e)))}static async disown(e,t){if(mt.#i.get(t)===e.realmId){try{await e.cdpClient.Runtime.releaseObject({objectId:t})}catch(e){if(-32e3!==e.code||"Invalid remote object id"!==e.message)throw e}this.#i.delete(t)}}static async#d(e,t,n,s){const a=e.stackTrace?.callFrames.map((e=>({url:e.url,functionName:e.functionName,lineNumber:e.lineNumber-t,columnNumber:e.columnNumber}))),r=await this.serializeCdpObject(e.exception,n,s),i=await this.stringifyObject(e.exception,s);return{exception:r,columnNumber:e.columnNumber,lineNumber:e.lineNumber-t,stackTrace:{callFrames:a||[]},text:i||e.text}}static async#o(e,t,n){const s=e.result.webDriverValue;if(!e.result.objectId)return s;const a=e.result.objectId,r=s;return"root"===n?(r.handle=a,this.#i.set(a,t.realmId)):await t.cdpClient.Runtime.releaseObject({objectId:a}),r}static async scriptEvaluate(e,t,n,s){let a=await e.cdpClient.Runtime.evaluate({contextId:e.executionContextId,expression:t,awaitPromise:n,generateWebDriverValue:!0});return a.exceptionDetails?{exceptionDetails:await this.#d(a.exceptionDetails,this.#a,s,e),realm:e.realmId}:{result:await mt.#o(a,e,s),realm:e.realmId}}static async#c(e,t){if("handle"in e)return{objectId:e.handle};switch(e.type){case"undefined":return{unserializableValue:"undefined"};case"null":return{unserializableValue:"null"};case"string":return{value:e.value};case"number":return"NaN"===e.value?{unserializableValue:"NaN"}:"-0"===e.value?{unserializableValue:"-0"}:"+Infinity"===e.value?{unserializableValue:"+Infinity"}:"Infinity"===e.value?{unserializableValue:"Infinity"}:"-Infinity"===e.value?{unserializableValue:"-Infinity"}:{value:e.value};case"boolean":return{value:!!e.value};case"bigint":return{unserializableValue:`BigInt(${JSON.stringify(e.value)})`};case"date":return{unserializableValue:`new Date(Date.parse(${JSON.stringify(e.value)}))`};case"regexp":return{unserializableValue:`new RegExp(${JSON.stringify(e.value.pattern)}, ${JSON.stringify(e.value.flags)})`};case"map":{const n=await this.#l(e.value,t);return{objectId:(await t.cdpClient.Runtime.callFunctionOn({functionDeclaration:String((function(...e){const t=new Map;for(let n=0;n<e.length;n+=2)t.set(e[n],e[n+1]);return t})),awaitPromise:!1,arguments:n,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}case"object":{const n=await this.#l(e.value,t);return{objectId:(await t.cdpClient.Runtime.callFunctionOn({functionDeclaration:String((function(...e){const t={};for(let n=0;n<e.length;n+=2){t[e[n]]=e[n+1]}return t})),awaitPromise:!1,arguments:n,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}case"array":{const n=await mt.#u(e.value,t);return{objectId:(await t.cdpClient.Runtime.callFunctionOn({functionDeclaration:String((function(...e){return e})),awaitPromise:!1,arguments:n,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}case"set":{const n=await this.#u(e.value,t);return{objectId:(await t.cdpClient.Runtime.callFunctionOn({functionDeclaration:String((function(...e){return new Set(e)})),awaitPromise:!1,arguments:n,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}default:throw new Error(`Value ${JSON.stringify(e)} is not deserializable.`)}}static async#l(e,t){const n=[];for(let s of e){const e=s[0],a=s[1];let r,i;r="string"==typeof e?{value:e}:await this.#c(e,t),i=await this.#c(a,t),n.push(r),n.push(i)}return n}static async#u(e,t){const n=[];for(let s of e)n.push(await this.#c(s,t));return n}}class gt{static#p=new Map;static getTopLevelContexts(){return Array.from(gt.#p.values()).filter((e=>null===e.parentId))}static removeContext(e){gt.#p.delete(e)}static addContext(e){gt.#p.set(e.contextId,e),null!==e.parentId&&gt.getKnownContext(e.parentId).addChild(e)}static hasKnownContext(e){return gt.#p.has(e)}static findContext(e){return gt.#p.get(e)}static getKnownContext(e){const t=gt.findContext(e);if(void 0===t)throw new r(`Context ${e} not found`);return t}}var ft;!function(e){e.window="window"}(ft||(ft={}));class vt{static#h=new Map;static create(e,t,n,s,a,r,i,o){const c=new vt(e,t,n,s,a,r,i,o);return vt.#h.set(c.realmId,c),c}static findRealms(e={}){return Array.from(vt.#h.values()).filter((t=>(void 0===e.realmId||e.realmId===t.realmId)&&((void 0===e.browsingContextId||e.browsingContextId===t.browsingContextId)&&((void 0===e.executionContextId||e.executionContextId===t.executionContextId)&&((void 0===e.type||e.type===t.type)&&((void 0===e.sandbox||e.sandbox===t.#m)&&(void 0===e.cdpSessionId||e.cdpSessionId===t.#g)))))))}static findRealm(e){const t=vt.findRealms(e);if(1===t.length)return t[0]}static getRealm(e){const t=vt.findRealm(e);if(void 0===t)throw new r(`Realm ${JSON.stringify(e)} not found`);return t}static clearBrowsingContext(e){vt.findRealms({browsingContextId:e}).map((e=>e.delete()))}delete(){vt.#h.delete(this.realmId),mt.realmDestroyed(this)}#f;#v;#y;#b;#w;#m;#g;#_;constructor(e,t,n,s,a,r,i,o){this.#f=e,this.#v=t,this.#y=n,this.#m=r,this.#b=s,this.#w=a,this.#g=i,this.#_=o}toBiDi(){return{realm:this.realmId,origin:this.origin,type:this.type,context:this.browsingContextId,...void 0!==this.#m?{sandbox:this.#m}:{}}}get realmId(){return this.#f}get browsingContextId(){return this.#v}get executionContextId(){return this.#y}get origin(){return this.#b}get type(){return this.#w}get cdpClient(){return this.#_}async callFunction(e,t,n,s,a){const r=gt.getKnownContext(this.browsingContextId);return await r.awaitUnblocked(),{result:await mt.callFunction(this,e,t,n,s,a)}}async scriptEvaluate(e,t,n){const s=gt.getKnownContext(this.browsingContextId);return await s.awaitUnblocked(),{result:await mt.scriptEvaluate(this,e,t,n)}}async disown(e){await mt.disown(this,e)}async serializeCdpObject(e,t){return await mt.serializeCdpObject(e,t,this)}async stringifyObject(e,t){return mt.stringifyObject(e,this)}}class yt{#x;#_;#g;#C;constructor(e,t,n,s){this.#g=n,this.#_=t,this.#x=e,this.#C=s}static create(e,t,n,s){const a=new yt(e,t,n,s);return a.#I(),a}#I(){this.#S()}#S(){this.#T()}#T(){this.#_.Runtime.on("consoleAPICalled",(async e=>{const t=vt.findRealm({cdpSessionId:this.#g,executionContextId:e.executionContextId}),n=void 0===t?e.args:await Promise.all(e.args.map((async e=>t.serializeCdpObject(e,"none"))));await this.#C.sendEvent(new rt.LogEntryAddedEvent({level:yt.#k(e.type),source:{realm:t?.realmId??"UNKNOWN",context:t?.browsingContextId??"UNKNOWN"},text:ht(n,!0),timestamp:Math.round(e.timestamp),stackTrace:yt.#E(e.stackTrace),type:"console",method:"warning"===e.type?"warn":e.type,args:n}),t?.browsingContextId??"UNKNOWN")})),this.#_.Runtime.on("exceptionThrown",(async e=>{const t=vt.findRealm({cdpSessionId:this.#g,executionContextId:e.exceptionDetails.executionContextId}),n=await(async()=>e.exceptionDetails.exception?void 0===t?JSON.stringify(e.exceptionDetails.exception):await t.stringifyObject(e.exceptionDetails.exception,t):e.exceptionDetails.text)();await this.#C.sendEvent(new rt.LogEntryAddedEvent({level:"error",source:{realm:t?.realmId??"UNKNOWN",context:t?.browsingContextId??"UNKNOWN"},text:n,timestamp:Math.round(e.timestamp),stackTrace:yt.#E(e.exceptionDetails.stackTrace),type:"javascript"}),t?.browsingContextId??"UNKNOWN")}))}static#k(e){return["assert","error"].includes(e)?"error":["debug","trace"].includes(e)?"debug":["warn","warning"].includes(e)?"warning":"info"}static#E(e){const t=e?.callFrames.map((e=>({columnNumber:e.columnNumber,functionName:e.functionName,lineNumber:e.lineNumber,url:e.url})));return t?{callFrames:t}:void 0}}var bt=at.LoadEvent;class wt{#O={documentInitialized:new ct,targetUnblocked:new ct,Page:{navigatedWithinDocument:new ct,lifecycleEvent:{DOMContentLoaded:new ct,load:new ct}}};#x;#P;#D="about:blank";#N=null;#g;#_;#M;#C;#j=new Map;#R;get#A(){if(void 0===this.#R)throw new Error(`No default realm for browsing context ${this.#x}`);return this.#R}constructor(e,t,n,s,a,r){this.#x=e,this.#P=t,this.#_=n,this.#C=r,this.#g=a,this.#M=s,this.#L(),gt.addContext(this)}static async createFrameContext(e,t,n,s,a,r){const i=new wt(e,t,n,s,a,r);i.#O.targetUnblocked.resolve(),await r.sendEvent(new at.ContextCreatedEvent(i.serializeToBidiValue()),i.contextId)}static async createTargetContext(e,t,n,s,a,r){const i=new wt(e,t,n,s,a,r);i.#Z(),await r.sendEvent(new at.ContextCreatedEvent(i.serializeToBidiValue()),i.contextId)}convertFrameToTargetContext(e,t){this.#B(e,t),this.#Z()}async delete(){if(await this.#F(),null!==this.parentId){gt.getKnownContext(this.parentId).#j.delete(this.contextId)}await this.#C.sendEvent(new at.ContextDestroyedEvent(this.serializeToBidiValue()),this.contextId),gt.removeContext(this.contextId)}async#F(){await Promise.all(this.children.map((e=>e.delete())))}#B(e,t){this.#O.targetUnblocked.isFinished||this.#O.targetUnblocked.reject("OOPiF"),this.#O.targetUnblocked=new ct,this.#_=e,this.#g=t,this.#L()}async#Z(){yt.create(this.#x,this.#_,this.#g,this.#C),await this.#_.Runtime.enable(),await this.#_.Page.enable(),await this.#_.Page.setLifecycleEventsEnabled({enabled:!0}),await this.#_.Target.setAutoAttach({autoAttach:!0,waitForDebuggerOnStart:!0,flatten:!0}),await this.#_.Runtime.runIfWaitingForDebugger(),this.#O.targetUnblocked.resolve()}get contextId(){return this.#x}get parentId(){return this.#P}get cdpSessionId(){return this.#g}get children(){return Array.from(this.#j.values())}get url(){return this.#D}addChild(e){this.#j.set(e.contextId,e)}async awaitLoaded(){await this.#O.Page.lifecycleEvent.load}async awaitUnblocked(){await this.#O.targetUnblocked}serializeToBidiValue(e=0,t=!0){return{context:this.#x,url:this.url,children:e>0?this.children.map((t=>t.serializeToBidiValue(e-1,!1))):null,...t?{parent:this.#P}:{}}}#L(){this.#_.Target.on("targetInfoChanged",(e=>{this.contextId===e.targetInfo.targetId&&(this.#D=e.targetInfo.url)})),this.#_.Page.on("frameNavigated",(async e=>{this.contextId===e.frame.id&&(this.#D=e.frame.url+(e.frame.urlFragment??""),await this.#F(),vt.clearBrowsingContext(this.contextId))})),this.#_.Page.on("navigatedWithinDocument",(e=>{this.contextId===e.frameId&&(this.#D=e.url,this.#O.Page.navigatedWithinDocument.resolve(e))})),this.#_.Page.on("lifecycleEvent",(async e=>{if(this.contextId===e.frameId)if("init"===e.name&&(this.#z(e.loaderId),this.#O.documentInitialized.resolve()),"commit"!==e.name){if(e.loaderId===this.#N)switch(e.name){case"DOMContentLoaded":this.#O.Page.lifecycleEvent.DOMContentLoaded.resolve(e),await this.#C.sendEvent(new at.DomContentLoadedEvent({context:this.contextId,navigation:this.#N,url:this.#D}),this.contextId);break;case"load":this.#O.Page.lifecycleEvent.load.resolve(e),await this.#C.sendEvent(new bt({context:this.contextId,navigation:this.#N,url:this.#D}),this.contextId)}}else this.#N=e.loaderId})),this.#_.Runtime.on("executionContextCreated",(e=>{if(e.context.auxData.frameId!==this.contextId)return;if(!["default","isolated"].includes(e.context.auxData.type))return;const t=vt.create(e.context.uniqueId,this.contextId,e.context.id,this.#V(e),ft.window,"isolated"===e.context.auxData.type?e.context.name:void 0,this.#g,this.#_);e.context.auxData.isDefault&&(this.#R=t)})),this.#_.Runtime.on("executionContextDestroyed",(e=>{vt.findRealms({cdpSessionId:this.#g,executionContextId:e.executionContextId}).map((e=>e.delete()))}))}#V(e){return"isolated"===e.context.auxData.type?this.#A.origin:["://",""].includes(e.context.origin)?"null":e.context.origin}#z(e){this.#N!==e&&(this.#O.documentInitialized.isFinished||this.#O.documentInitialized.reject("Document changed"),this.#O.documentInitialized=new ct,this.#O.Page.navigatedWithinDocument.isFinished||this.#O.Page.navigatedWithinDocument.reject("Document changed"),this.#O.Page.navigatedWithinDocument=new ct,this.#O.Page.lifecycleEvent.DOMContentLoaded.isFinished||this.#O.Page.lifecycleEvent.DOMContentLoaded.reject("Document changed"),this.#O.Page.lifecycleEvent.DOMContentLoaded=new ct,this.#O.Page.lifecycleEvent.load.isFinished||this.#O.Page.lifecycleEvent.load.reject("Document changed"),this.#O.Page.lifecycleEvent.load=new ct,this.#N=e)}async navigate(e,t){await this.#O.targetUnblocked;const s=await this.#_.Page.navigate({url:e,frameId:this.contextId});if(s.errorText)throw new n(s.errorText);switch(void 0!==s.loaderId&&s.loaderId!==this.#N&&this.#z(s.loaderId),t){case"none":break;case"interactive":void 0===s.loaderId?await this.#O.Page.navigatedWithinDocument:await this.#O.Page.lifecycleEvent.DOMContentLoaded;break;case"complete":void 0===s.loaderId?await this.#O.Page.navigatedWithinDocument:await this.#O.Page.lifecycleEvent.load;break;default:throw new Error(`Not implemented wait '${t}'`)}return{result:{navigation:s.loaderId||null,url:e}}}async findElement(e){await this.#O.targetUnblocked;const t=String((e=>document.querySelector(e))),n=[{type:"string",value:e}];return await this.#A.callFunction(t,{type:"undefined"},n,!0,"root")}async getOrCreateSandbox(e){if(void 0===e||""===e)return this.#A;let t=vt.findRealms({browsingContextId:this.contextId,sandbox:e});if(0==t.length&&(await this.#_.Page.createIsolatedWorld({frameId:this.contextId,worldName:e}),t=vt.findRealms({browsingContextId:this.contextId,sandbox:e})),1!==t.length)throw Error(`Sandbox ${e} wasn't created.`);return t[0]}}const _t=e("context");class xt{sessions=new Set;#U;#$;#M;#C;constructor(e,t,n,s){this.#U=e,this.#$=t,this.#M=n,this.#C=s,this.#W(this.#U.browserClient())}#W(e){this.#K(e)}#K(e){e.Target.on("attachedToTarget",(async t=>{await this.#H(t,e)})),e.Target.on("detachedFromTarget",(async e=>{await xt.#J(e)}))}#q(e){if(this.sessions.has(e))return;this.sessions.add(e);const t=this.#U.getCdpClient(e);this.#K(t),t.on("event",(async(t,n)=>{await this.#C.sendEvent({method:"cdp.eventReceived",params:{cdpMethod:t,cdpParams:n,cdpSession:e}},null)})),t.Page.on("frameAttached",(async n=>{await wt.createFrameContext(n.frameId,n.parentFrameId,t,this.#M,e,this.#C)}))}async#H(e,t){_t("AttachedToTarget event received: "+JSON.stringify(e));const{sessionId:n,targetInfo:s}=e;let a=this.#U.getCdpClient(n);if(!this.#G(s))return await a.Runtime.runIfWaitingForDebugger(),void await t.Target.detachFromTarget(e);this.#q(n),gt.hasKnownContext(s.targetId)?gt.getKnownContext(s.targetId).convertFrameToTargetContext(a,n):await wt.createTargetContext(s.targetId,null,a,this.#M,n,this.#C)}static async#J(e){const t=e.targetId;await(gt.findContext(t)?.delete())}async process_browsingContext_getTree(e){return{result:{contexts:(void 0===e.root?gt.getTopLevelContexts():[gt.getKnownContext(e.root)]).map((t=>t.serializeToBidiValue(e.maxDepth??Number.MAX_VALUE)))}}}async process_browsingContext_create(e){const t=this.#U.browserClient(),n=(await t.Target.createTarget({url:"about:blank",newWindow:"window"===e.type})).targetId,s=gt.getKnownContext(n);return await s.awaitLoaded(),{result:s.serializeToBidiValue(1)}}async process_browsingContext_navigate(e){const t=gt.getKnownContext(e.context);return await t.navigate(e.url,void 0!==e.wait?e.wait:"none")}static async#X(e){if("realm"in e)return vt.getRealm({realmId:e.realm});const t=gt.getKnownContext(e.context);return await t.getOrCreateSandbox(e.sandbox)}async process_script_evaluate(e){const t=await xt.#X(e.target);return await t.scriptEvaluate(e.expression,e.awaitPromise,e.resultOwnership??"none")}process_script_getRealms(e){void 0!==e.context&&gt.getKnownContext(e.context);return{result:{realms:vt.findRealms({browsingContextId:e.context,type:e.type}).map((e=>e.toBiDi()))}}}async process_script_callFunction(e){const t=await xt.#X(e.target);return await t.callFunction(e.functionDeclaration,e.this||{type:"undefined"},e.arguments||[],e.awaitPromise,e.resultOwnership??"none")}async process_script_disown(e){const t=await xt.#X(e.target);return await Promise.all(e.handles.map((async e=>await t.disown(e)))),{result:{}}}async process_PROTO_browsingContext_findElement(e){const t=gt.getKnownContext(e.context);return await t.findElement(e.selector)}async process_browsingContext_close(e){const t=this.#U.browserClient();if(null!==gt.getKnownContext(e.context).parentId)throw new a("Not a top-level browsing context cannot be closed.");const n=new Promise((async n=>{const s=a=>{a.targetId===e.context&&(t.Target.removeListener("detachedFromTarget",s),n())};t.Target.on("detachedFromTarget",s)}));return await this.#U.browserClient().Target.closeTarget({targetId:e.context}),await n,{result:{}}}#G(e){return e.targetId!==this.#$&&["page","iframe"].includes(e.type)}async process_cdp_sendCommand(e){return{result:await this.#U.sendCommand(e.cdpMethod,e.cdpParams,e.cdpSession??null),cdpSession:e.cdpSession}}async process_cdp_getSession(e){const t=e.context,n=gt.getKnownContext(t).cdpSessionId;return void 0===n?{result:{cdpSession:null}}:{result:{cdpSession:n}}}}class Ct{#Q;#M;#C;static run(e,t,n,s){new Ct(e,t,n,s).#Y()}constructor(e,t,n,s){this.#C=n,this.#M=t,this.#Q=new xt(e,s,t,n)}#Y(){this.#M.on("message",(e=>this.#ee(e)))}async#te(){return{result:{ready:!1,message:"already connected"}}}async#ne(e,t){return await this.#C.subscribe(e.events,e.contexts??[null],t),{result:{}}}async#se(e,t){return await this.#C.unsubscribe(e.events,e.contexts??[null],t),{result:{}}}async#ae(e){switch(e.method){case"session.status":return await this.#te();case"session.subscribe":return await this.#ne(ot.parseSubscribeParams(e.params),e.channel??null);case"session.unsubscribe":return await this.#se(ot.parseSubscribeParams(e.params),e.channel??null);case"browsingContext.create":return await this.#Q.process_browsingContext_create(at.parseCreateParams(e.params));case"browsingContext.close":return await this.#Q.process_browsingContext_close(at.parseCloseParams(e.params));case"browsingContext.getTree":return await this.#Q.process_browsingContext_getTree(at.parseGetTreeParams(e.params));case"browsingContext.navigate":return await this.#Q.process_browsingContext_navigate(at.parseNavigateParams(e.params));case"script.getRealms":return this.#Q.process_script_getRealms(st.parseGetRealmsParams(e.params));case"script.callFunction":return await this.#Q.process_script_callFunction(st.parseCallFunctionParams(e.params));case"script.evaluate":return await this.#Q.process_script_evaluate(st.parseEvaluateParams(e.params));case"script.disown":return await this.#Q.process_script_disown(st.parseDisownParams(e.params));case"PROTO.browsingContext.findElement":return await this.#Q.process_PROTO_browsingContext_findElement(at.PROTO.parseFindElementParams(e.params));case"cdp.sendCommand":return await this.#Q.process_cdp_sendCommand(it.parseSendCommandParams(e.params));case"cdp.getSession":return await this.#Q.process_cdp_getSession(it.parseGetSessionParams(e.params));default:throw new s(`Unknown command '${e.method}'.`)}}#ee=async e=>{try{const t=await this.#ae(e),n={id:e.id,...t};await this.#M.sendMessage(n,e.channel??null)}catch(s){if(s instanceof t){const t=s;await this.#M.sendMessage(t.toErrorResponse(e.id),e.channel??null)}else{const t=s;console.error(t),await this.#M.sendMessage(new n(t.message).toErrorResponse(e.id),e.channel??null)}}}}function It(){}function St(){St.init.call(this)}function Tt(e){return void 0===e._maxListeners?St.defaultMaxListeners:e._maxListeners}function kt(e,t,n){if(t)e.call(n);else for(var s=e.length,a=Rt(e,s),r=0;r<s;++r)a[r].call(n)}function Et(e,t,n,s){if(t)e.call(n,s);else for(var a=e.length,r=Rt(e,a),i=0;i<a;++i)r[i].call(n,s)}function Ot(e,t,n,s,a){if(t)e.call(n,s,a);else for(var r=e.length,i=Rt(e,r),o=0;o<r;++o)i[o].call(n,s,a)}function Pt(e,t,n,s,a,r){if(t)e.call(n,s,a,r);else for(var i=e.length,o=Rt(e,i),c=0;c<i;++c)o[c].call(n,s,a,r)}function Dt(e,t,n,s){if(t)e.apply(n,s);else for(var a=e.length,r=Rt(e,a),i=0;i<a;++i)r[i].apply(n,s)}function Nt(e,t,n,s){var a,r,i,o;if("function"!=typeof n)throw new TypeError('"listener" argument must be a function');if((r=e._events)?(r.newListener&&(e.emit("newListener",t,n.listener?n.listener:n),r=e._events),i=r[t]):(r=e._events=new It,e._eventsCount=0),i){if("function"==typeof i?i=r[t]=s?[n,i]:[i,n]:s?i.unshift(n):i.push(n),!i.warned&&(a=Tt(e))&&a>0&&i.length>a){i.warned=!0;var c=new Error("Possible EventEmitter memory leak detected. "+i.length+" "+t+" listeners added. Use emitter.setMaxListeners() to increase limit");c.name="MaxListenersExceededWarning",c.emitter=e,c.type=t,c.count=i.length,o=c,"function"==typeof console.warn?console.warn(o):console.log(o)}}else i=r[t]=n,++e._eventsCount;return e}function Mt(e,t,n){var s=!1;function a(){e.removeListener(t,a),s||(s=!0,n.apply(e,arguments))}return a.listener=n,a}function jt(e){var t=this._events;if(t){var n=t[e];if("function"==typeof n)return 1;if(n)return n.length}return 0}function Rt(e,t){for(var n=new Array(t);t--;)n[t]=e[t];return n}It.prototype=Object.create(null),St.EventEmitter=St,St.usingDomains=!1,St.prototype.domain=void 0,St.prototype._events=void 0,St.prototype._maxListeners=void 0,St.defaultMaxListeners=10,St.init=function(){this.domain=null,St.usingDomains&&undefined.active,this._events&&this._events!==Object.getPrototypeOf(this)._events||(this._events=new It,this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},St.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||isNaN(e))throw new TypeError('"n" argument must be a positive number');return this._maxListeners=e,this},St.prototype.getMaxListeners=function(){return Tt(this)},St.prototype.emit=function(e){var t,n,s,a,r,i,o,c="error"===e;if(i=this._events)c=c&&null==i.error;else if(!c)return!1;if(o=this.domain,c){if(t=arguments[1],!o){if(t instanceof Error)throw t;var d=new Error('Uncaught, unspecified "error" event. ('+t+")");throw d.context=t,d}return t||(t=new Error('Uncaught, unspecified "error" event')),t.domainEmitter=this,t.domain=o,t.domainThrown=!1,o.emit("error",t),!1}if(!(n=i[e]))return!1;var l="function"==typeof n;switch(s=arguments.length){case 1:kt(n,l,this);break;case 2:Et(n,l,this,arguments[1]);break;case 3:Ot(n,l,this,arguments[1],arguments[2]);break;case 4:Pt(n,l,this,arguments[1],arguments[2],arguments[3]);break;default:for(a=new Array(s-1),r=1;r<s;r++)a[r-1]=arguments[r];Dt(n,l,this,a)}return!0},St.prototype.addListener=function(e,t){return Nt(this,e,t,!1)},St.prototype.on=St.prototype.addListener,St.prototype.prependListener=function(e,t){return Nt(this,e,t,!0)},St.prototype.once=function(e,t){if("function"!=typeof t)throw new TypeError('"listener" argument must be a function');return this.on(e,Mt(this,e,t)),this},St.prototype.prependOnceListener=function(e,t){if("function"!=typeof t)throw new TypeError('"listener" argument must be a function');return this.prependListener(e,Mt(this,e,t)),this},St.prototype.removeListener=function(e,t){var n,s,a,r,i;if("function"!=typeof t)throw new TypeError('"listener" argument must be a function');if(!(s=this._events))return this;if(!(n=s[e]))return this;if(n===t||n.listener&&n.listener===t)0==--this._eventsCount?this._events=new It:(delete s[e],s.removeListener&&this.emit("removeListener",e,n.listener||t));else if("function"!=typeof n){for(a=-1,r=n.length;r-- >0;)if(n[r]===t||n[r].listener&&n[r].listener===t){i=n[r].listener,a=r;break}if(a<0)return this;if(1===n.length){if(n[0]=void 0,0==--this._eventsCount)return this._events=new It,this;delete s[e]}else!function(e,t){for(var n=t,s=n+1,a=e.length;s<a;n+=1,s+=1)e[n]=e[s];e.pop()}(n,a);s.removeListener&&this.emit("removeListener",e,i||t)}return this},St.prototype.removeAllListeners=function(e){var t,n;if(!(n=this._events))return this;if(!n.removeListener)return 0===arguments.length?(this._events=new It,this._eventsCount=0):n[e]&&(0==--this._eventsCount?this._events=new It:delete n[e]),this;if(0===arguments.length){for(var s,a=Object.keys(n),r=0;r<a.length;++r)"removeListener"!==(s=a[r])&&this.removeAllListeners(s);return this.removeAllListeners("removeListener"),this._events=new It,this._eventsCount=0,this}if("function"==typeof(t=n[e]))this.removeListener(e,t);else if(t)do{this.removeListener(e,t[t.length-1])}while(t[0]);return this},St.prototype.listeners=function(e){var t,n=this._events;return n&&(t=n[e])?"function"==typeof t?[t.listener||t]:function(e){for(var t=new Array(e.length),n=0;n<t.length;++n)t[n]=e[n].listener||e[n];return t}(t):[]},St.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):jt.call(e,t)},St.prototype.listenerCount=jt,St.prototype.eventNames=function(){return this._eventsCount>0?Reflect.ownKeys(this._events):[]};const At=[{domain:"Accessibility",commands:["disable","enable","getPartialAXTree","getFullAXTree","getRootAXNode","getAXNodeAndAncestors","getChildAXNodes","queryAXTree"]},{domain:"Animation",commands:["disable","enable","getCurrentTime","getPlaybackRate","releaseAnimations","resolveAnimation","seekAnimations","setPaused","setPlaybackRate","setTiming"]},{domain:"Audits",commands:["getEncodedResponse","disable","enable","checkContrast"]},{domain:"BackgroundService",commands:["startObserving","stopObserving","setRecording","clearEvents"]},{domain:"Browser",commands:["setPermission","grantPermissions","resetPermissions","setDownloadBehavior","cancelDownload","close","crash","crashGpuProcess","getVersion","getBrowserCommandLine","getHistograms","getHistogram","getWindowBounds","getWindowForTarget","setWindowBounds","setDockTile","executeBrowserCommand"]},{domain:"CSS",commands:["addRule","collectClassNames","createStyleSheet","disable","enable","forcePseudoState","getBackgroundColors","getComputedStyleForNode","getInlineStylesForNode","getMatchedStylesForNode","getMediaQueries","getPlatformFontsForNode","getStyleSheetText","getLayersForNode","trackComputedStyleUpdates","takeComputedStyleUpdates","setEffectivePropertyValueForNode","setKeyframeKey","setMediaText","setContainerQueryText","setSupportsText","setRuleSelector","setStyleSheetText","setStyleTexts","startRuleUsageTracking","stopRuleUsageTracking","takeCoverageDelta","setLocalFontsEnabled"]},{domain:"CacheStorage",commands:["deleteCache","deleteEntry","requestCacheNames","requestCachedResponse","requestEntries"]},{domain:"Cast",commands:["enable","disable","setSinkToUse","startDesktopMirroring","startTabMirroring","stopCasting"]},{domain:"DOM",commands:["collectClassNamesFromSubtree","copyTo","describeNode","scrollIntoViewIfNeeded","disable","discardSearchResults","enable","focus","getAttributes","getBoxModel","getContentQuads","getDocument","getFlattenedDocument","getNodesForSubtreeByStyle","getNodeForLocation","getOuterHTML","getRelayoutBoundary","getSearchResults","hideHighlight","highlightNode","highlightRect","markUndoableState","moveTo","performSearch","pushNodeByPathToFrontend","pushNodesByBackendIdsToFrontend","querySelector","querySelectorAll","redo","removeAttribute","removeNode","requestChildNodes","requestNode","resolveNode","setAttributeValue","setAttributesAsText","setFileInputFiles","setNodeStackTracesEnabled","getNodeStackTraces","getFileInfo","setInspectedNode","setNodeName","setNodeValue","setOuterHTML","undo","getFrameOwner","getContainerForNode","getQueryingDescendantsForContainer"]},{domain:"DOMDebugger",commands:["getEventListeners","removeDOMBreakpoint","removeEventListenerBreakpoint","removeInstrumentationBreakpoint","removeXHRBreakpoint","setBreakOnCSPViolation","setDOMBreakpoint","setEventListenerBreakpoint","setInstrumentationBreakpoint","setXHRBreakpoint"]},{domain:"EventBreakpoints",commands:["setInstrumentationBreakpoint","removeInstrumentationBreakpoint"]},{domain:"DOMSnapshot",commands:["disable","enable","getSnapshot","captureSnapshot"]},{domain:"DOMStorage",commands:["clear","disable","enable","getDOMStorageItems","removeDOMStorageItem","setDOMStorageItem"]},{domain:"Database",commands:["disable","enable","executeSQL","getDatabaseTableNames"]},{domain:"DeviceOrientation",commands:["clearDeviceOrientationOverride","setDeviceOrientationOverride"]},{domain:"Emulation",commands:["canEmulate","clearDeviceMetricsOverride","clearGeolocationOverride","resetPageScaleFactor","setFocusEmulationEnabled","setAutoDarkModeOverride","setCPUThrottlingRate","setDefaultBackgroundColorOverride","setDeviceMetricsOverride","setScrollbarsHidden","setDocumentCookieDisabled","setEmitTouchEventsForMouse","setEmulatedMedia","setEmulatedVisionDeficiency","setGeolocationOverride","setIdleOverride","clearIdleOverride","setNavigatorOverrides","setPageScaleFactor","setScriptExecutionDisabled","setTouchEmulationEnabled","setVirtualTimePolicy","setLocaleOverride","setTimezoneOverride","setVisibleSize","setDisabledImageTypes","setHardwareConcurrencyOverride","setUserAgentOverride","setAutomationOverride"]},{domain:"HeadlessExperimental",commands:["beginFrame","disable","enable"]},{domain:"IO",commands:["close","read","resolveBlob"]},{domain:"IndexedDB",commands:["clearObjectStore","deleteDatabase","deleteObjectStoreEntries","disable","enable","requestData","getMetadata","requestDatabase","requestDatabaseNames"]},{domain:"Input",commands:["dispatchDragEvent","dispatchKeyEvent","insertText","imeSetComposition","dispatchMouseEvent","dispatchTouchEvent","emulateTouchFromMouseEvent","setIgnoreInputEvents","setInterceptDrags","synthesizePinchGesture","synthesizeScrollGesture","synthesizeTapGesture"]},{domain:"Inspector",commands:["disable","enable"]},{domain:"LayerTree",commands:["compositingReasons","disable","enable","loadSnapshot","makeSnapshot","profileSnapshot","releaseSnapshot","replaySnapshot","snapshotCommandLog"]},{domain:"Log",commands:["clear","disable","enable","startViolationsReport","stopViolationsReport"]},{domain:"Memory",commands:["getDOMCounters","prepareForLeakDetection","forciblyPurgeJavaScriptMemory","setPressureNotificationsSuppressed","simulatePressureNotification","startSampling","stopSampling","getAllTimeSamplingProfile","getBrowserSamplingProfile","getSamplingProfile"]},{domain:"Network",commands:["setAcceptedEncodings","clearAcceptedEncodingsOverride","canClearBrowserCache","canClearBrowserCookies","canEmulateNetworkConditions","clearBrowserCache","clearBrowserCookies","continueInterceptedRequest","deleteCookies","disable","emulateNetworkConditions","enable","getAllCookies","getCertificate","getCookies","getResponseBody","getRequestPostData","getResponseBodyForInterception","takeResponseBodyForInterceptionAsStream","replayXHR","searchInResponseBody","setBlockedURLs","setBypassServiceWorker","setCacheDisabled","setCookie","setCookies","setExtraHTTPHeaders","setAttachDebugStack","setRequestInterception","setUserAgentOverride","getSecurityIsolationStatus","enableReportingApi","loadNetworkResource"]},{domain:"Overlay",commands:["disable","enable","getHighlightObjectForTest","getGridHighlightObjectsForTest","getSourceOrderHighlightObjectForTest","hideHighlight","highlightFrame","highlightNode","highlightQuad","highlightRect","highlightSourceOrder","setInspectMode","setShowAdHighlights","setPausedInDebuggerMessage","setShowDebugBorders","setShowFPSCounter","setShowGridOverlays","setShowFlexOverlays","setShowScrollSnapOverlays","setShowContainerQueryOverlays","setShowPaintRects","setShowLayoutShiftRegions","setShowScrollBottleneckRects","setShowHitTestBorders","setShowWebVitals","setShowViewportSizeOnResize","setShowHinge","setShowIsolatedElements"]},{domain:"Page",commands:["addScriptToEvaluateOnLoad","addScriptToEvaluateOnNewDocument","bringToFront","captureScreenshot","captureSnapshot","clearDeviceMetricsOverride","clearDeviceOrientationOverride","clearGeolocationOverride","createIsolatedWorld","deleteCookie","disable","enable","getAppManifest","getInstallabilityErrors","getManifestIcons","getAppId","getCookies","getFrameTree","getLayoutMetrics","getNavigationHistory","resetNavigationHistory","getResourceContent","getResourceTree","handleJavaScriptDialog","navigate","navigateToHistoryEntry","printToPDF","reload","removeScriptToEvaluateOnLoad","removeScriptToEvaluateOnNewDocument","screencastFrameAck","searchInResource","setAdBlockingEnabled","setBypassCSP","getPermissionsPolicyState","getOriginTrials","setDeviceMetricsOverride","setDeviceOrientationOverride","setFontFamilies","setFontSizes","setDocumentContent","setDownloadBehavior","setGeolocationOverride","setLifecycleEventsEnabled","setTouchEmulationEnabled","startScreencast","stopLoading","crash","close","setWebLifecycleState","stopScreencast","produceCompilationCache","addCompilationCache","clearCompilationCache","setSPCTransactionMode","generateTestReport","waitForDebugger","setInterceptFileChooserDialog"]},{domain:"Performance",commands:["disable","enable","setTimeDomain","getMetrics"]},{domain:"PerformanceTimeline",commands:["enable"]},{domain:"Security",commands:["disable","enable","setIgnoreCertificateErrors","handleCertificateError","setOverrideCertificateErrors"]},{domain:"ServiceWorker",commands:["deliverPushMessage","disable","dispatchSyncEvent","dispatchPeriodicSyncEvent","enable","inspectWorker","setForceUpdateOnPageLoad","skipWaiting","startWorker","stopAllWorkers","stopWorker","unregister","updateRegistration"]},{domain:"Storage",commands:["getStorageKeyForFrame","clearDataForOrigin","getCookies","setCookies","clearCookies","getUsageAndQuota","overrideQuotaForOrigin","trackCacheStorageForOrigin","trackIndexedDBForOrigin","untrackCacheStorageForOrigin","untrackIndexedDBForOrigin","getTrustTokens","clearTrustTokens","getInterestGroupDetails","setInterestGroupTracking"]},{domain:"SystemInfo",commands:["getInfo","getProcessInfo"]},{domain:"Target",commands:["activateTarget","attachToTarget","attachToBrowserTarget","closeTarget","exposeDevToolsProtocol","createBrowserContext","getBrowserContexts","createTarget","detachFromTarget","disposeBrowserContext","getTargetInfo","getTargets","sendMessageToTarget","setAutoAttach","autoAttachRelated","setDiscoverTargets","setRemoteLocations"]},{domain:"Tethering",commands:["bind","unbind"]},{domain:"Tracing",commands:["end","getCategories","recordClockSyncMarker","requestMemoryDump","start"]},{domain:"Fetch",commands:["disable","enable","failRequest","fulfillRequest","continueRequest","continueWithAuth","continueResponse","getResponseBody","takeResponseBodyAsStream"]},{domain:"WebAudio",commands:["enable","disable","getRealtimeData"]},{domain:"WebAuthn",commands:["enable","disable","addVirtualAuthenticator","removeVirtualAuthenticator","addCredential","getCredential","getCredentials","removeCredential","clearCredentials","setUserVerified","setAutomaticPresenceSimulation"]},{domain:"Media",commands:["enable","disable"]},{domain:"Console",commands:["clearMessages","disable","enable"]},{domain:"Debugger",commands:["continueToLocation","disable","enable","evaluateOnCallFrame","getPossibleBreakpoints","getScriptSource","getWasmBytecode","getStackTrace","pause","pauseOnAsyncCall","removeBreakpoint","restartFrame","resume","searchInContent","setAsyncCallStackDepth","setBlackboxPatterns","setBlackboxedRanges","setBreakpoint","setInstrumentationBreakpoint","setBreakpointByUrl","setBreakpointOnFunctionCall","setBreakpointsActive","setPauseOnExceptions","setReturnValue","setScriptSource","setSkipAllPauses","setVariableValue","stepInto","stepOut","stepOver"]},{domain:"HeapProfiler",commands:["addInspectedHeapObject","collectGarbage","disable","enable","getHeapObjectId","getObjectByHeapObjectId","getSamplingProfile","startSampling","startTrackingHeapObjects","stopSampling","stopTrackingHeapObjects","takeHeapSnapshot"]},{domain:"Profiler",commands:["disable","enable","getBestEffortCoverage","setSamplingInterval","start","startPreciseCoverage","startTypeProfile","stop","stopPreciseCoverage","stopTypeProfile","takePreciseCoverage","takeTypeProfile"]},{domain:"Runtime",commands:["awaitPromise","callFunctionOn","compileScript","disable","discardConsoleEntries","enable","evaluate","getIsolateId","getHeapUsage","getProperties","globalLexicalScopeNames","queryObjects","releaseObject","releaseObjectGroup","runIfWaitingForDebugger","runScript","setAsyncCallStackDepth","setCustomObjectFormatterEnabled","setMaxCallStackSizeToCapture","terminateExecution","addBinding","removeBinding","getExceptionDetails"]},{domain:"Schema",commands:["getDomains"]}],Lt=new Map;class Zt extends St{_client;constructor(e){super(),this._client=e}}for(let e of At){class t extends Zt{constructor(e){super(e)}}for(let n of e.commands)Object.defineProperty(t.prototype,n,{value:async function(t){return await this._client.sendCommand(`${e.domain}.${n}`,t)}});Lt.set(e.domain,t)}class Bt extends St{_cdpConnection;_sessionId;_domains;constructor(e,t){super(),this._cdpConnection=e,this._sessionId=t,this._domains=new Map;for(const[e,t]of Lt.entries())this._domains.set(e,new t(this)),Object.defineProperty(this,e,{get(){return this._domains.get(e)}})}sendCommand(e,t){return this._cdpConnection.sendCommand(e,t,this._sessionId)}_onCdpEvent(e,t){this.emit("event",e,t);const[n,s]=e.split("."),a=this._domains.get(n);a&&a.emit(s,t)}}function Ft(e,t){return new Bt(e,t)}const zt=e("cdp");class Vt{_transport;_browserCdpClient;_sessionCdpClients=new Map;_commandCallbacks=new Map;_nextId;constructor(e){this._transport=e,this._nextId=0,this._transport.setOnMessage(this._onMessage),this._browserCdpClient=Ft(this,null)}close(){this._transport.close();for(const[e,{reject:t}]of this._commandCallbacks)t(new Error("Disconnected"));this._commandCallbacks.clear(),this._sessionCdpClients.clear()}browserClient(){return this._browserCdpClient}getCdpClient(e){const t=this._sessionCdpClients.get(e);if(!t)throw new Error("Unknown CDP session ID");return t}sendCommand(e,t,n){return new Promise(((s,a)=>{const r=this._nextId++;this._commandCallbacks.set(r,{resolve:s,reject:a});let i={id:r,method:e,params:t};n&&(i.sessionId=n);const o=JSON.stringify(i);this._transport.sendMessage(o),zt("sent > "+o)}))}_onMessage=async e=>{zt("received < "+e);const t=JSON.parse(e);if("Target.attachedToTarget"===t.method){const{sessionId:e}=t.params;this._sessionCdpClients.set(e,Ft(this,e))}else if("Target.detachedFromTarget"===t.method){const{sessionId:e}=t.params;this._sessionCdpClients.get(e)&&this._sessionCdpClients.delete(e)}if(void 0!==t.id){const e=this._commandCallbacks.get(t.id);e&&(t.result?e.resolve(t.result):t.error&&e.reject(t.error))}else if(t.method){const e=t.sessionId?this._sessionCdpClients.get(t.sessionId):this._browserCdpClient;e&&e._onCdpEvent(t.method,t.params||{})}}}const Ut=e("bidi");class $t extends St{_transport;constructor(e){super(),this._transport=e,this._transport.setOnMessage(this.#ee)}async sendMessage(e,t){null!==t&&(e.channel=t);const n=JSON.stringify(e);Ut("sent > "+n),this._transport.sendMessage(n)}close(){this._transport.close()}#ee=async e=>{let t;Ut("received < "+e);try{t=this.#re(e)}catch(t){return void this.#ie(e,"invalid argument",t.message,null)}this.emit("message",t)};#ie(e,t,n,s){const a=this.#oe(e,t,n);this.sendMessage(a,s)}#ce(e){return null===e?"null":Array.isArray(e)?"array":typeof e}#oe(e,t,n){let s;try{const t=JSON.parse(e);"object"===this.#ce(t)&&"id"in t&&(s=t.id)}catch{}return{id:s,error:t,message:n}}#re(e){let t;try{t=JSON.parse(e)}catch{throw new Error("Cannot parse data as JSON")}const n=this.#ce(t);if("object"!==n)throw new Error(`Expected JSON object but got ${n}`);const{id:s,method:a,params:r,channel:i}=t,o=this.#ce(s);if("number"!==o||!Number.isInteger(s)||s<0)throw new Error(`Expected unsigned integer but got ${o}`);const c=this.#ce(a);if("string"!==c)throw new Error(`Expected string method but got ${c}`);const d=this.#ce(r);if("object"!==d)throw new Error(`Expected object params but got ${d}`);if(void 0!==i){const e=this.#ce(i);if("string"!==e)throw new Error(`Expected string channel but got ${e}`)}return{id:s,method:a,params:r,channel:i}}}class Wt{#de=0;#le=new Map;getChannelsSubscribedToEvent(e,t){return Array.from(this.#le.keys()).map((n=>({priority:this.#ue(e,t,n),channel:n}))).filter((({priority:e,channel:t})=>null!==e)).sort(((e,t)=>e.priority-t.priority)).map((({priority:e,channel:t})=>t))}#ue(e,t,n){const s=this.#le.get(n);if(void 0===s)return null;let a=[s.get(null)?.get(e),s.get(t)?.get(e)].filter((e=>void 0!==e));return 0===a.length?null:Math.min(...a)}subscribe(e,t,n){this.#le.has(n)||this.#le.set(n,new Map);const s=this.#le.get(n);s.has(t)||s.set(t,new Map);const a=s.get(t);a.has(e)||a.set(e,this.#de++)}unsubscribe(e,t,n){if(!this.#le.has(n))return;const s=this.#le.get(n);if(!s.has(t))return;const a=s.get(t);a.delete(e),0===a.size&&s.delete(e),0===s.size&&this.#le.delete(n)}}class Kt{#pe;#M;constructor(e){this.#M=e,this.#pe=new Wt}async sendEvent(e,t){const n=this.#pe.getChannelsSubscribedToEvent(e.method,t);for(const t of n)await this.#M.sendMessage(e,t)}async subscribe(e,t,n){for(let s of e)for(let e of t)this.#pe.subscribe(s,e,n)}async unsubscribe(e,t,n){for(let s of e)for(let e of t)this.#pe.unsubscribe(s,e,n)}}
+!function(){"use strict";function e(e){return(...t)=>{if(globalThis.document?.documentElement){console.log(e,...t);const n=function(e){const t=e+"_log",n=document.getElementById(t);if(n)return n;const s=document.createElement("div");return s.id=t,s.innerHTML=`<h3>${e}:</h3>`,document.body.appendChild(s),s}(e),s=document.createElement("pre");s.textContent=t.join(", "),n.appendChild(s)}}}class t{constructor(e,t,n){this.error=e,this.message=t,this.stacktrace=n}error;message;stacktrace;toErrorResponse(e){return{id:e,error:this.error,message:this.message,stacktrace:this.stacktrace}}}class n extends t{constructor(e,t){super("unknown error",e,t)}}class s extends t{constructor(e,t){super("unknown command",e,t)}}class r extends t{constructor(e,t){super("invalid argument",e,t)}}class a extends t{constructor(e){super("no such frame",e)}}class i{method;params;constructor(e,t){this.method=e,this.params=t}}var o;!function(e){e.assertEqual=e=>e,e.assertIs=function(e){},e.assertNever=function(e){throw new Error},e.arrayToEnum=e=>{const t={};for(const n of e)t[n]=n;return t},e.getValidEnumValues=t=>{const n=e.objectKeys(t).filter((e=>"number"!=typeof t[t[e]])),s={};for(const e of n)s[e]=t[e];return e.objectValues(s)},e.objectValues=t=>e.objectKeys(t).map((function(e){return t[e]})),e.objectKeys="function"==typeof Object.keys?e=>Object.keys(e):e=>{const t=[];for(const n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.push(n);return t},e.find=(e,t)=>{for(const n of e)if(t(n))return n},e.isInteger="function"==typeof Number.isInteger?e=>Number.isInteger(e):e=>"number"==typeof e&&isFinite(e)&&Math.floor(e)===e,e.joinValues=function(e,t=" | "){return e.map((e=>"string"==typeof e?`'${e}'`:e)).join(t)},e.jsonStringifyReplacer=(e,t)=>"bigint"==typeof t?t.toString():t}(o||(o={}));const c=o.arrayToEnum(["string","nan","number","integer","float","boolean","date","bigint","symbol","function","undefined","null","array","object","unknown","promise","void","never","map","set"]),d=e=>{switch(typeof e){case"undefined":return c.undefined;case"string":return c.string;case"number":return isNaN(e)?c.nan:c.number;case"boolean":return c.boolean;case"function":return c.function;case"bigint":return c.bigint;case"object":return Array.isArray(e)?c.array:null===e?c.null:e.then&&"function"==typeof e.then&&e.catch&&"function"==typeof e.catch?c.promise:"undefined"!=typeof Map&&e instanceof Map?c.map:"undefined"!=typeof Set&&e instanceof Set?c.set:"undefined"!=typeof Date&&e instanceof Date?c.date:c.object;default:return c.unknown}},l=o.arrayToEnum(["invalid_type","invalid_literal","custom","invalid_union","invalid_union_discriminator","invalid_enum_value","unrecognized_keys","invalid_arguments","invalid_return_type","invalid_date","invalid_string","too_small","too_big","invalid_intersection_types","not_multiple_of"]);class u extends Error{constructor(e){super(),this.issues=[],this.addIssue=e=>{this.issues=[...this.issues,e]},this.addIssues=(e=[])=>{this.issues=[...this.issues,...e]};const t=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,t):this.__proto__=t,this.name="ZodError",this.issues=e}get errors(){return this.issues}format(e){const t=e||function(e){return e.message},n={_errors:[]},s=e=>{for(const r of e.issues)if("invalid_union"===r.code)r.unionErrors.map(s);else if("invalid_return_type"===r.code)s(r.returnTypeError);else if("invalid_arguments"===r.code)s(r.argumentsError);else if(0===r.path.length)n._errors.push(t(r));else{let e=n,s=0;for(;s<r.path.length;){const n=r.path[s];s===r.path.length-1?(e[n]=e[n]||{_errors:[]},e[n]._errors.push(t(r))):e[n]=e[n]||{_errors:[]},e=e[n],s++}}};return s(this),n}toString(){return this.message}get message(){return JSON.stringify(this.issues,o.jsonStringifyReplacer,2)}get isEmpty(){return 0===this.issues.length}flatten(e=(e=>e.message)){const t={},n=[];for(const s of this.issues)s.path.length>0?(t[s.path[0]]=t[s.path[0]]||[],t[s.path[0]].push(e(s))):n.push(e(s));return{formErrors:n,fieldErrors:t}}get formErrors(){return this.flatten()}}u.create=e=>new u(e);const h=(e,t)=>{let n;switch(e.code){case l.invalid_type:n=e.received===c.undefined?"Required":`Expected ${e.expected}, received ${e.received}`;break;case l.invalid_literal:n=`Invalid literal value, expected ${JSON.stringify(e.expected,o.jsonStringifyReplacer)}`;break;case l.unrecognized_keys:n=`Unrecognized key(s) in object: ${o.joinValues(e.keys,", ")}`;break;case l.invalid_union:n="Invalid input";break;case l.invalid_union_discriminator:n=`Invalid discriminator value. Expected ${o.joinValues(e.options)}`;break;case l.invalid_enum_value:n=`Invalid enum value. Expected ${o.joinValues(e.options)}, received '${e.received}'`;break;case l.invalid_arguments:n="Invalid function arguments";break;case l.invalid_return_type:n="Invalid function return type";break;case l.invalid_date:n="Invalid date";break;case l.invalid_string:"object"==typeof e.validation?"startsWith"in e.validation?n=`Invalid input: must start with "${e.validation.startsWith}"`:"endsWith"in e.validation?n=`Invalid input: must end with "${e.validation.endsWith}"`:o.assertNever(e.validation):n="regex"!==e.validation?`Invalid ${e.validation}`:"Invalid";break;case l.too_small:n="array"===e.type?`Array must contain ${e.inclusive?"at least":"more than"} ${e.minimum} element(s)`:"string"===e.type?`String must contain ${e.inclusive?"at least":"over"} ${e.minimum} character(s)`:"number"===e.type?`Number must be greater than ${e.inclusive?"or equal to ":""}${e.minimum}`:"date"===e.type?`Date must be greater than ${e.inclusive?"or equal to ":""}${new Date(e.minimum)}`:"Invalid input";break;case l.too_big:n="array"===e.type?`Array must contain ${e.inclusive?"at most":"less than"} ${e.maximum} element(s)`:"string"===e.type?`String must contain ${e.inclusive?"at most":"under"} ${e.maximum} character(s)`:"number"===e.type?`Number must be less than ${e.inclusive?"or equal to ":""}${e.maximum}`:"date"===e.type?`Date must be smaller than ${e.inclusive?"or equal to ":""}${new Date(e.maximum)}`:"Invalid input";break;case l.custom:n="Invalid input";break;case l.invalid_intersection_types:n="Intersection results could not be merged";break;case l.not_multiple_of:n=`Number must be a multiple of ${e.multipleOf}`;break;default:n=t.defaultError,o.assertNever(e)}return{message:n}};let p=h;function m(){return p}const g=e=>{const{data:t,path:n,errorMaps:s,issueData:r}=e,a=[...n,...r.path||[]],i={...r,path:a};let o="";const c=s.filter((e=>!!e)).slice().reverse();for(const e of c)o=e(i,{data:t,defaultError:o}).message;return{...r,path:a,message:r.message||o}};function f(e,t){const n=g({issueData:t,data:e.data,path:e.path,errorMaps:[e.common.contextualErrorMap,e.schemaErrorMap,m(),h].filter((e=>!!e))});e.common.issues.push(n)}class v{constructor(){this.value="valid"}dirty(){"valid"===this.value&&(this.value="dirty")}abort(){"aborted"!==this.value&&(this.value="aborted")}static mergeArray(e,t){const n=[];for(const s of t){if("aborted"===s.status)return y;"dirty"===s.status&&e.dirty(),n.push(s.value)}return{status:e.value,value:n}}static async mergeObjectAsync(e,t){const n=[];for(const e of t)n.push({key:await e.key,value:await e.value});return v.mergeObjectSync(e,n)}static mergeObjectSync(e,t){const n={};for(const s of t){const{key:t,value:r}=s;if("aborted"===t.status)return y;if("aborted"===r.status)return y;"dirty"===t.status&&e.dirty(),"dirty"===r.status&&e.dirty(),(void 0!==r.value||s.alwaysSet)&&(n[t.value]=r.value)}return{status:e.value,value:n}}}const y=Object.freeze({status:"aborted"}),w=e=>({status:"valid",value:e}),b=e=>"aborted"===e.status,x=e=>"dirty"===e.status,_=e=>"valid"===e.status,C=e=>void 0!==typeof Promise&&e instanceof Promise;var I;!function(e){e.errToObj=e=>"string"==typeof e?{message:e}:e||{},e.toString=e=>"string"==typeof e?e:null==e?void 0:e.message}(I||(I={}));class S{constructor(e,t,n,s){this.parent=e,this.data=t,this._path=n,this._key=s}get path(){return this._path.concat(this._key)}}const T=(e,t)=>{if(_(t))return{success:!0,data:t.value};if(!e.common.issues.length)throw new Error("Validation failed but no issues detected.");return{success:!1,error:new u(e.common.issues)}};function k(e){if(!e)return{};const{errorMap:t,invalid_type_error:n,required_error:s,description:r}=e;if(t&&(n||s))throw new Error('Can\'t use "invalid_type_error" or "required_error" in conjunction with custom error map.');if(t)return{errorMap:t,description:r};return{errorMap:(e,t)=>"invalid_type"!==e.code?{message:t.defaultError}:void 0===t.data?{message:null!=s?s:t.defaultError}:{message:null!=n?n:t.defaultError},description:r}}class E{constructor(e){this.spa=this.safeParseAsync,this.superRefine=this._refinement,this._def=e,this.parse=this.parse.bind(this),this.safeParse=this.safeParse.bind(this),this.parseAsync=this.parseAsync.bind(this),this.safeParseAsync=this.safeParseAsync.bind(this),this.spa=this.spa.bind(this),this.refine=this.refine.bind(this),this.refinement=this.refinement.bind(this),this.superRefine=this.superRefine.bind(this),this.optional=this.optional.bind(this),this.nullable=this.nullable.bind(this),this.nullish=this.nullish.bind(this),this.array=this.array.bind(this),this.promise=this.promise.bind(this),this.or=this.or.bind(this),this.and=this.and.bind(this),this.transform=this.transform.bind(this),this.default=this.default.bind(this),this.describe=this.describe.bind(this),this.isNullable=this.isNullable.bind(this),this.isOptional=this.isOptional.bind(this)}get description(){return this._def.description}_getType(e){return d(e.data)}_getOrReturnCtx(e,t){return t||{common:e.parent.common,data:e.data,parsedType:d(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}_processInputParams(e){return{status:new v,ctx:{common:e.parent.common,data:e.data,parsedType:d(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}}_parseSync(e){const t=this._parse(e);if(C(t))throw new Error("Synchronous parse encountered promise.");return t}_parseAsync(e){const t=this._parse(e);return Promise.resolve(t)}parse(e,t){const n=this.safeParse(e,t);if(n.success)return n.data;throw n.error}safeParse(e,t){var n;const s={common:{issues:[],async:null!==(n=null==t?void 0:t.async)&&void 0!==n&&n,contextualErrorMap:null==t?void 0:t.errorMap},path:(null==t?void 0:t.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:d(e)},r=this._parseSync({data:e,path:s.path,parent:s});return T(s,r)}async parseAsync(e,t){const n=await this.safeParseAsync(e,t);if(n.success)return n.data;throw n.error}async safeParseAsync(e,t){const n={common:{issues:[],contextualErrorMap:null==t?void 0:t.errorMap,async:!0},path:(null==t?void 0:t.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:d(e)},s=this._parse({data:e,path:[],parent:n}),r=await(C(s)?s:Promise.resolve(s));return T(n,r)}refine(e,t){const n=e=>"string"==typeof t||void 0===t?{message:t}:"function"==typeof t?t(e):t;return this._refinement(((t,s)=>{const r=e(t),a=()=>s.addIssue({code:l.custom,...n(t)});return"undefined"!=typeof Promise&&r instanceof Promise?r.then((e=>!!e||(a(),!1))):!!r||(a(),!1)}))}refinement(e,t){return this._refinement(((n,s)=>!!e(n)||(s.addIssue("function"==typeof t?t(n,s):t),!1)))}_refinement(e){return new le({schema:this,typeName:we.ZodEffects,effect:{type:"refinement",refinement:e}})}optional(){return ue.create(this)}nullable(){return he.create(this)}nullish(){return this.optional().nullable()}array(){return $.create(this)}promise(){return de.create(this)}or(e){return q.create([this,e])}and(e){return Q.create(this,e)}transform(e){return new le({schema:this,typeName:we.ZodEffects,effect:{type:"transform",transform:e}})}default(e){return new pe({innerType:this,defaultValue:"function"==typeof e?e:()=>e,typeName:we.ZodDefault})}brand(){return new fe({typeName:we.ZodBranded,type:this,...k(void 0)})}describe(e){return new(0,this.constructor)({...this._def,description:e})}isOptional(){return this.safeParse(void 0).success}isNullable(){return this.safeParse(null).success}}const O=/^c[^\s-]{8,}$/i,P=/^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i,D=/^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;class N extends E{constructor(){super(...arguments),this._regex=(e,t,n)=>this.refinement((t=>e.test(t)),{validation:t,code:l.invalid_string,...I.errToObj(n)}),this.nonempty=e=>this.min(1,I.errToObj(e)),this.trim=()=>new N({...this._def,checks:[...this._def.checks,{kind:"trim"}]})}_parse(e){if(this._getType(e)!==c.string){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.string,received:t.parsedType}),y}const t=new v;let n;for(const s of this._def.checks)if("min"===s.kind)e.data.length<s.value&&(n=this._getOrReturnCtx(e,n),f(n,{code:l.too_small,minimum:s.value,type:"string",inclusive:!0,message:s.message}),t.dirty());else if("max"===s.kind)e.data.length>s.value&&(n=this._getOrReturnCtx(e,n),f(n,{code:l.too_big,maximum:s.value,type:"string",inclusive:!0,message:s.message}),t.dirty());else if("email"===s.kind)D.test(e.data)||(n=this._getOrReturnCtx(e,n),f(n,{validation:"email",code:l.invalid_string,message:s.message}),t.dirty());else if("uuid"===s.kind)P.test(e.data)||(n=this._getOrReturnCtx(e,n),f(n,{validation:"uuid",code:l.invalid_string,message:s.message}),t.dirty());else if("cuid"===s.kind)O.test(e.data)||(n=this._getOrReturnCtx(e,n),f(n,{validation:"cuid",code:l.invalid_string,message:s.message}),t.dirty());else if("url"===s.kind)try{new URL(e.data)}catch(r){n=this._getOrReturnCtx(e,n),f(n,{validation:"url",code:l.invalid_string,message:s.message}),t.dirty()}else if("regex"===s.kind){s.regex.lastIndex=0;s.regex.test(e.data)||(n=this._getOrReturnCtx(e,n),f(n,{validation:"regex",code:l.invalid_string,message:s.message}),t.dirty())}else"trim"===s.kind?e.data=e.data.trim():"startsWith"===s.kind?e.data.startsWith(s.value)||(n=this._getOrReturnCtx(e,n),f(n,{code:l.invalid_string,validation:{startsWith:s.value},message:s.message}),t.dirty()):"endsWith"===s.kind?e.data.endsWith(s.value)||(n=this._getOrReturnCtx(e,n),f(n,{code:l.invalid_string,validation:{endsWith:s.value},message:s.message}),t.dirty()):o.assertNever(s);return{status:t.value,value:e.data}}_addCheck(e){return new N({...this._def,checks:[...this._def.checks,e]})}email(e){return this._addCheck({kind:"email",...I.errToObj(e)})}url(e){return this._addCheck({kind:"url",...I.errToObj(e)})}uuid(e){return this._addCheck({kind:"uuid",...I.errToObj(e)})}cuid(e){return this._addCheck({kind:"cuid",...I.errToObj(e)})}regex(e,t){return this._addCheck({kind:"regex",regex:e,...I.errToObj(t)})}startsWith(e,t){return this._addCheck({kind:"startsWith",value:e,...I.errToObj(t)})}endsWith(e,t){return this._addCheck({kind:"endsWith",value:e,...I.errToObj(t)})}min(e,t){return this._addCheck({kind:"min",value:e,...I.errToObj(t)})}max(e,t){return this._addCheck({kind:"max",value:e,...I.errToObj(t)})}length(e,t){return this.min(e,t).max(e,t)}get isEmail(){return!!this._def.checks.find((e=>"email"===e.kind))}get isURL(){return!!this._def.checks.find((e=>"url"===e.kind))}get isUUID(){return!!this._def.checks.find((e=>"uuid"===e.kind))}get isCUID(){return!!this._def.checks.find((e=>"cuid"===e.kind))}get minLength(){let e=null;for(const t of this._def.checks)"min"===t.kind&&(null===e||t.value>e)&&(e=t.value);return e}get maxLength(){let e=null;for(const t of this._def.checks)"max"===t.kind&&(null===e||t.value<e)&&(e=t.value);return e}}function M(e,t){const n=(e.toString().split(".")[1]||"").length,s=(t.toString().split(".")[1]||"").length,r=n>s?n:s;return parseInt(e.toFixed(r).replace(".",""))%parseInt(t.toFixed(r).replace(".",""))/Math.pow(10,r)}N.create=e=>new N({checks:[],typeName:we.ZodString,...k(e)});class j extends E{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte,this.step=this.multipleOf}_parse(e){if(this._getType(e)!==c.number){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.number,received:t.parsedType}),y}let t;const n=new v;for(const s of this._def.checks)if("int"===s.kind)o.isInteger(e.data)||(t=this._getOrReturnCtx(e,t),f(t,{code:l.invalid_type,expected:"integer",received:"float",message:s.message}),n.dirty());else if("min"===s.kind){(s.inclusive?e.data<s.value:e.data<=s.value)&&(t=this._getOrReturnCtx(e,t),f(t,{code:l.too_small,minimum:s.value,type:"number",inclusive:s.inclusive,message:s.message}),n.dirty())}else if("max"===s.kind){(s.inclusive?e.data>s.value:e.data>=s.value)&&(t=this._getOrReturnCtx(e,t),f(t,{code:l.too_big,maximum:s.value,type:"number",inclusive:s.inclusive,message:s.message}),n.dirty())}else"multipleOf"===s.kind?0!==M(e.data,s.value)&&(t=this._getOrReturnCtx(e,t),f(t,{code:l.not_multiple_of,multipleOf:s.value,message:s.message}),n.dirty()):o.assertNever(s);return{status:n.value,value:e.data}}gte(e,t){return this.setLimit("min",e,!0,I.toString(t))}gt(e,t){return this.setLimit("min",e,!1,I.toString(t))}lte(e,t){return this.setLimit("max",e,!0,I.toString(t))}lt(e,t){return this.setLimit("max",e,!1,I.toString(t))}setLimit(e,t,n,s){return new j({...this._def,checks:[...this._def.checks,{kind:e,value:t,inclusive:n,message:I.toString(s)}]})}_addCheck(e){return new j({...this._def,checks:[...this._def.checks,e]})}int(e){return this._addCheck({kind:"int",message:I.toString(e)})}positive(e){return this._addCheck({kind:"min",value:0,inclusive:!1,message:I.toString(e)})}negative(e){return this._addCheck({kind:"max",value:0,inclusive:!1,message:I.toString(e)})}nonpositive(e){return this._addCheck({kind:"max",value:0,inclusive:!0,message:I.toString(e)})}nonnegative(e){return this._addCheck({kind:"min",value:0,inclusive:!0,message:I.toString(e)})}multipleOf(e,t){return this._addCheck({kind:"multipleOf",value:e,message:I.toString(t)})}get minValue(){let e=null;for(const t of this._def.checks)"min"===t.kind&&(null===e||t.value>e)&&(e=t.value);return e}get maxValue(){let e=null;for(const t of this._def.checks)"max"===t.kind&&(null===e||t.value<e)&&(e=t.value);return e}get isInt(){return!!this._def.checks.find((e=>"int"===e.kind))}}j.create=e=>new j({checks:[],typeName:we.ZodNumber,...k(e)});class R extends E{_parse(e){if(this._getType(e)!==c.bigint){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.bigint,received:t.parsedType}),y}return w(e.data)}}R.create=e=>new R({typeName:we.ZodBigInt,...k(e)});class A extends E{_parse(e){if(this._getType(e)!==c.boolean){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.boolean,received:t.parsedType}),y}return w(e.data)}}A.create=e=>new A({typeName:we.ZodBoolean,...k(e)});class L extends E{_parse(e){if(this._getType(e)!==c.date){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.date,received:t.parsedType}),y}if(isNaN(e.data.getTime())){return f(this._getOrReturnCtx(e),{code:l.invalid_date}),y}const t=new v;let n;for(const s of this._def.checks)"min"===s.kind?e.data.getTime()<s.value&&(n=this._getOrReturnCtx(e,n),f(n,{code:l.too_small,message:s.message,inclusive:!0,minimum:s.value,type:"date"}),t.dirty()):"max"===s.kind?e.data.getTime()>s.value&&(n=this._getOrReturnCtx(e,n),f(n,{code:l.too_big,message:s.message,inclusive:!0,maximum:s.value,type:"date"}),t.dirty()):o.assertNever(s);return{status:t.value,value:new Date(e.data.getTime())}}_addCheck(e){return new L({...this._def,checks:[...this._def.checks,e]})}min(e,t){return this._addCheck({kind:"min",value:e.getTime(),message:I.toString(t)})}max(e,t){return this._addCheck({kind:"max",value:e.getTime(),message:I.toString(t)})}get minDate(){let e=null;for(const t of this._def.checks)"min"===t.kind&&(null===e||t.value>e)&&(e=t.value);return null!=e?new Date(e):null}get maxDate(){let e=null;for(const t of this._def.checks)"max"===t.kind&&(null===e||t.value<e)&&(e=t.value);return null!=e?new Date(e):null}}L.create=e=>new L({checks:[],typeName:we.ZodDate,...k(e)});class B extends E{_parse(e){if(this._getType(e)!==c.undefined){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.undefined,received:t.parsedType}),y}return w(e.data)}}B.create=e=>new B({typeName:we.ZodUndefined,...k(e)});class Z extends E{_parse(e){if(this._getType(e)!==c.null){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.null,received:t.parsedType}),y}return w(e.data)}}Z.create=e=>new Z({typeName:we.ZodNull,...k(e)});class F extends E{constructor(){super(...arguments),this._any=!0}_parse(e){return w(e.data)}}F.create=e=>new F({typeName:we.ZodAny,...k(e)});class z extends E{constructor(){super(...arguments),this._unknown=!0}_parse(e){return w(e.data)}}z.create=e=>new z({typeName:we.ZodUnknown,...k(e)});class V extends E{_parse(e){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.never,received:t.parsedType}),y}}V.create=e=>new V({typeName:we.ZodNever,...k(e)});class U extends E{_parse(e){if(this._getType(e)!==c.undefined){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.void,received:t.parsedType}),y}return w(e.data)}}U.create=e=>new U({typeName:we.ZodVoid,...k(e)});class $ extends E{_parse(e){const{ctx:t,status:n}=this._processInputParams(e),s=this._def;if(t.parsedType!==c.array)return f(t,{code:l.invalid_type,expected:c.array,received:t.parsedType}),y;if(null!==s.minLength&&t.data.length<s.minLength.value&&(f(t,{code:l.too_small,minimum:s.minLength.value,type:"array",inclusive:!0,message:s.minLength.message}),n.dirty()),null!==s.maxLength&&t.data.length>s.maxLength.value&&(f(t,{code:l.too_big,maximum:s.maxLength.value,type:"array",inclusive:!0,message:s.maxLength.message}),n.dirty()),t.common.async)return Promise.all(t.data.map(((e,n)=>s.type._parseAsync(new S(t,e,t.path,n))))).then((e=>v.mergeArray(n,e)));const r=t.data.map(((e,n)=>s.type._parseSync(new S(t,e,t.path,n))));return v.mergeArray(n,r)}get element(){return this._def.type}min(e,t){return new $({...this._def,minLength:{value:e,message:I.toString(t)}})}max(e,t){return new $({...this._def,maxLength:{value:e,message:I.toString(t)}})}length(e,t){return this.min(e,t).max(e,t)}nonempty(e){return this.min(1,e)}}var K;$.create=(e,t)=>new $({type:e,minLength:null,maxLength:null,typeName:we.ZodArray,...k(t)}),function(e){e.mergeShapes=(e,t)=>({...e,...t})}(K||(K={}));const W=e=>t=>new J({...e,shape:()=>({...e.shape(),...t})});function H(e){if(e instanceof J){const t={};for(const n in e.shape){const s=e.shape[n];t[n]=ue.create(H(s))}return new J({...e._def,shape:()=>t})}return e instanceof $?$.create(H(e.element)):e instanceof ue?ue.create(H(e.unwrap())):e instanceof he?he.create(H(e.unwrap())):e instanceof Y?Y.create(e.items.map((e=>H(e)))):e}class J extends E{constructor(){super(...arguments),this._cached=null,this.nonstrict=this.passthrough,this.augment=W(this._def),this.extend=W(this._def)}_getCached(){if(null!==this._cached)return this._cached;const e=this._def.shape(),t=o.objectKeys(e);return this._cached={shape:e,keys:t}}_parse(e){if(this._getType(e)!==c.object){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.object,received:t.parsedType}),y}const{status:t,ctx:n}=this._processInputParams(e),{shape:s,keys:r}=this._getCached(),a=[];if(!(this._def.catchall instanceof V&&"strip"===this._def.unknownKeys))for(const e in n.data)r.includes(e)||a.push(e);const i=[];for(const e of r){const t=s[e],r=n.data[e];i.push({key:{status:"valid",value:e},value:t._parse(new S(n,r,n.path,e)),alwaysSet:e in n.data})}if(this._def.catchall instanceof V){const e=this._def.unknownKeys;if("passthrough"===e)for(const e of a)i.push({key:{status:"valid",value:e},value:{status:"valid",value:n.data[e]}});else if("strict"===e)a.length>0&&(f(n,{code:l.unrecognized_keys,keys:a}),t.dirty());else if("strip"!==e)throw new Error("Internal ZodObject error: invalid unknownKeys value.")}else{const e=this._def.catchall;for(const t of a){const s=n.data[t];i.push({key:{status:"valid",value:t},value:e._parse(new S(n,s,n.path,t)),alwaysSet:t in n.data})}}return n.common.async?Promise.resolve().then((async()=>{const e=[];for(const t of i){const n=await t.key;e.push({key:n,value:await t.value,alwaysSet:t.alwaysSet})}return e})).then((e=>v.mergeObjectSync(t,e))):v.mergeObjectSync(t,i)}get shape(){return this._def.shape()}strict(e){return I.errToObj,new J({...this._def,unknownKeys:"strict",...void 0!==e?{errorMap:(t,n)=>{var s,r,a,i;const o=null!==(a=null===(r=(s=this._def).errorMap)||void 0===r?void 0:r.call(s,t,n).message)&&void 0!==a?a:n.defaultError;return"unrecognized_keys"===t.code?{message:null!==(i=I.errToObj(e).message)&&void 0!==i?i:o}:{message:o}}}:{}})}strip(){return new J({...this._def,unknownKeys:"strip"})}passthrough(){return new J({...this._def,unknownKeys:"passthrough"})}setKey(e,t){return this.augment({[e]:t})}merge(e){return new J({unknownKeys:e._def.unknownKeys,catchall:e._def.catchall,shape:()=>K.mergeShapes(this._def.shape(),e._def.shape()),typeName:we.ZodObject})}catchall(e){return new J({...this._def,catchall:e})}pick(e){const t={};return o.objectKeys(e).map((e=>{this.shape[e]&&(t[e]=this.shape[e])})),new J({...this._def,shape:()=>t})}omit(e){const t={};return o.objectKeys(this.shape).map((n=>{-1===o.objectKeys(e).indexOf(n)&&(t[n]=this.shape[n])})),new J({...this._def,shape:()=>t})}deepPartial(){return H(this)}partial(e){const t={};if(e)return o.objectKeys(this.shape).map((n=>{-1===o.objectKeys(e).indexOf(n)?t[n]=this.shape[n]:t[n]=this.shape[n].optional()})),new J({...this._def,shape:()=>t});for(const e in this.shape){const n=this.shape[e];t[e]=n.optional()}return new J({...this._def,shape:()=>t})}required(){const e={};for(const t in this.shape){let n=this.shape[t];for(;n instanceof ue;)n=n._def.innerType;e[t]=n}return new J({...this._def,shape:()=>e})}keyof(){return ie(o.objectKeys(this.shape))}}J.create=(e,t)=>new J({shape:()=>e,unknownKeys:"strip",catchall:V.create(),typeName:we.ZodObject,...k(t)}),J.strictCreate=(e,t)=>new J({shape:()=>e,unknownKeys:"strict",catchall:V.create(),typeName:we.ZodObject,...k(t)}),J.lazycreate=(e,t)=>new J({shape:e,unknownKeys:"strip",catchall:V.create(),typeName:we.ZodObject,...k(t)});class q extends E{_parse(e){const{ctx:t}=this._processInputParams(e),n=this._def.options;if(t.common.async)return Promise.all(n.map((async e=>{const n={...t,common:{...t.common,issues:[]},parent:null};return{result:await e._parseAsync({data:t.data,path:t.path,parent:n}),ctx:n}}))).then((function(e){for(const t of e)if("valid"===t.result.status)return t.result;for(const n of e)if("dirty"===n.result.status)return t.common.issues.push(...n.ctx.common.issues),n.result;const n=e.map((e=>new u(e.ctx.common.issues)));return f(t,{code:l.invalid_union,unionErrors:n}),y}));{let e;const s=[];for(const r of n){const n={...t,common:{...t.common,issues:[]},parent:null},a=r._parseSync({data:t.data,path:t.path,parent:n});if("valid"===a.status)return a;"dirty"!==a.status||e||(e={result:a,ctx:n}),n.common.issues.length&&s.push(n.common.issues)}if(e)return t.common.issues.push(...e.ctx.common.issues),e.result;const r=s.map((e=>new u(e)));return f(t,{code:l.invalid_union,unionErrors:r}),y}}get options(){return this._def.options}}q.create=(e,t)=>new q({options:e,typeName:we.ZodUnion,...k(t)});class G extends E{_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==c.object)return f(t,{code:l.invalid_type,expected:c.object,received:t.parsedType}),y;const n=this.discriminator,s=t.data[n],r=this.options.get(s);return r?t.common.async?r._parseAsync({data:t.data,path:t.path,parent:t}):r._parseSync({data:t.data,path:t.path,parent:t}):(f(t,{code:l.invalid_union_discriminator,options:this.validDiscriminatorValues,path:[n]}),y)}get discriminator(){return this._def.discriminator}get validDiscriminatorValues(){return Array.from(this.options.keys())}get options(){return this._def.options}static create(e,t,n){const s=new Map;try{t.forEach((t=>{const n=t.shape[e].value;s.set(n,t)}))}catch(e){throw new Error("The discriminator value could not be extracted from all the provided schemas")}if(s.size!==t.length)throw new Error("Some of the discriminator values are not unique");return new G({typeName:we.ZodDiscriminatedUnion,discriminator:e,options:s,...k(n)})}}function X(e,t){const n=d(e),s=d(t);if(e===t)return{valid:!0,data:e};if(n===c.object&&s===c.object){const n=o.objectKeys(t),s=o.objectKeys(e).filter((e=>-1!==n.indexOf(e))),r={...e,...t};for(const n of s){const s=X(e[n],t[n]);if(!s.valid)return{valid:!1};r[n]=s.data}return{valid:!0,data:r}}if(n===c.array&&s===c.array){if(e.length!==t.length)return{valid:!1};const n=[];for(let s=0;s<e.length;s++){const r=X(e[s],t[s]);if(!r.valid)return{valid:!1};n.push(r.data)}return{valid:!0,data:n}}return n===c.date&&s===c.date&&+e==+t?{valid:!0,data:e}:{valid:!1}}class Q extends E{_parse(e){const{status:t,ctx:n}=this._processInputParams(e),s=(e,s)=>{if(b(e)||b(s))return y;const r=X(e.value,s.value);return r.valid?((x(e)||x(s))&&t.dirty(),{status:t.value,value:r.data}):(f(n,{code:l.invalid_intersection_types}),y)};return n.common.async?Promise.all([this._def.left._parseAsync({data:n.data,path:n.path,parent:n}),this._def.right._parseAsync({data:n.data,path:n.path,parent:n})]).then((([e,t])=>s(e,t))):s(this._def.left._parseSync({data:n.data,path:n.path,parent:n}),this._def.right._parseSync({data:n.data,path:n.path,parent:n}))}}Q.create=(e,t,n)=>new Q({left:e,right:t,typeName:we.ZodIntersection,...k(n)});class Y extends E{_parse(e){const{status:t,ctx:n}=this._processInputParams(e);if(n.parsedType!==c.array)return f(n,{code:l.invalid_type,expected:c.array,received:n.parsedType}),y;if(n.data.length<this._def.items.length)return f(n,{code:l.too_small,minimum:this._def.items.length,inclusive:!0,type:"array"}),y;!this._def.rest&&n.data.length>this._def.items.length&&(f(n,{code:l.too_big,maximum:this._def.items.length,inclusive:!0,type:"array"}),t.dirty());const s=n.data.map(((e,t)=>{const s=this._def.items[t]||this._def.rest;return s?s._parse(new S(n,e,n.path,t)):null})).filter((e=>!!e));return n.common.async?Promise.all(s).then((e=>v.mergeArray(t,e))):v.mergeArray(t,s)}get items(){return this._def.items}rest(e){return new Y({...this._def,rest:e})}}Y.create=(e,t)=>{if(!Array.isArray(e))throw new Error("You must pass an array of schemas to z.tuple([ ... ])");return new Y({items:e,typeName:we.ZodTuple,rest:null,...k(t)})};class ee extends E{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(e){const{status:t,ctx:n}=this._processInputParams(e);if(n.parsedType!==c.object)return f(n,{code:l.invalid_type,expected:c.object,received:n.parsedType}),y;const s=[],r=this._def.keyType,a=this._def.valueType;for(const e in n.data)s.push({key:r._parse(new S(n,e,n.path,e)),value:a._parse(new S(n,n.data[e],n.path,e))});return n.common.async?v.mergeObjectAsync(t,s):v.mergeObjectSync(t,s)}get element(){return this._def.valueType}static create(e,t,n){return new ee(t instanceof E?{keyType:e,valueType:t,typeName:we.ZodRecord,...k(n)}:{keyType:N.create(),valueType:e,typeName:we.ZodRecord,...k(t)})}}class te extends E{_parse(e){const{status:t,ctx:n}=this._processInputParams(e);if(n.parsedType!==c.map)return f(n,{code:l.invalid_type,expected:c.map,received:n.parsedType}),y;const s=this._def.keyType,r=this._def.valueType,a=[...n.data.entries()].map((([e,t],a)=>({key:s._parse(new S(n,e,n.path,[a,"key"])),value:r._parse(new S(n,t,n.path,[a,"value"]))})));if(n.common.async){const e=new Map;return Promise.resolve().then((async()=>{for(const n of a){const s=await n.key,r=await n.value;if("aborted"===s.status||"aborted"===r.status)return y;"dirty"!==s.status&&"dirty"!==r.status||t.dirty(),e.set(s.value,r.value)}return{status:t.value,value:e}}))}{const e=new Map;for(const n of a){const s=n.key,r=n.value;if("aborted"===s.status||"aborted"===r.status)return y;"dirty"!==s.status&&"dirty"!==r.status||t.dirty(),e.set(s.value,r.value)}return{status:t.value,value:e}}}}te.create=(e,t,n)=>new te({valueType:t,keyType:e,typeName:we.ZodMap,...k(n)});class ne extends E{_parse(e){const{status:t,ctx:n}=this._processInputParams(e);if(n.parsedType!==c.set)return f(n,{code:l.invalid_type,expected:c.set,received:n.parsedType}),y;const s=this._def;null!==s.minSize&&n.data.size<s.minSize.value&&(f(n,{code:l.too_small,minimum:s.minSize.value,type:"set",inclusive:!0,message:s.minSize.message}),t.dirty()),null!==s.maxSize&&n.data.size>s.maxSize.value&&(f(n,{code:l.too_big,maximum:s.maxSize.value,type:"set",inclusive:!0,message:s.maxSize.message}),t.dirty());const r=this._def.valueType;function a(e){const n=new Set;for(const s of e){if("aborted"===s.status)return y;"dirty"===s.status&&t.dirty(),n.add(s.value)}return{status:t.value,value:n}}const i=[...n.data.values()].map(((e,t)=>r._parse(new S(n,e,n.path,t))));return n.common.async?Promise.all(i).then((e=>a(e))):a(i)}min(e,t){return new ne({...this._def,minSize:{value:e,message:I.toString(t)}})}max(e,t){return new ne({...this._def,maxSize:{value:e,message:I.toString(t)}})}size(e,t){return this.min(e,t).max(e,t)}nonempty(e){return this.min(1,e)}}ne.create=(e,t)=>new ne({valueType:e,minSize:null,maxSize:null,typeName:we.ZodSet,...k(t)});class se extends E{constructor(){super(...arguments),this.validate=this.implement}_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==c.function)return f(t,{code:l.invalid_type,expected:c.function,received:t.parsedType}),y;function n(e,n){return g({data:e,path:t.path,errorMaps:[t.common.contextualErrorMap,t.schemaErrorMap,m(),h].filter((e=>!!e)),issueData:{code:l.invalid_arguments,argumentsError:n}})}function s(e,n){return g({data:e,path:t.path,errorMaps:[t.common.contextualErrorMap,t.schemaErrorMap,m(),h].filter((e=>!!e)),issueData:{code:l.invalid_return_type,returnTypeError:n}})}const r={errorMap:t.common.contextualErrorMap},a=t.data;return this._def.returns instanceof de?w((async(...e)=>{const t=new u([]),i=await this._def.args.parseAsync(e,r).catch((s=>{throw t.addIssue(n(e,s)),t})),o=await a(...i);return await this._def.returns._def.type.parseAsync(o,r).catch((e=>{throw t.addIssue(s(o,e)),t}))})):w(((...e)=>{const t=this._def.args.safeParse(e,r);if(!t.success)throw new u([n(e,t.error)]);const i=a(...t.data),o=this._def.returns.safeParse(i,r);if(!o.success)throw new u([s(i,o.error)]);return o.data}))}parameters(){return this._def.args}returnType(){return this._def.returns}args(...e){return new se({...this._def,args:Y.create(e).rest(z.create())})}returns(e){return new se({...this._def,returns:e})}implement(e){return this.parse(e)}strictImplement(e){return this.parse(e)}static create(e,t,n){return new se({args:e||Y.create([]).rest(z.create()),returns:t||z.create(),typeName:we.ZodFunction,...k(n)})}}class re extends E{get schema(){return this._def.getter()}_parse(e){const{ctx:t}=this._processInputParams(e);return this._def.getter()._parse({data:t.data,path:t.path,parent:t})}}re.create=(e,t)=>new re({getter:e,typeName:we.ZodLazy,...k(t)});class ae extends E{_parse(e){if(e.data!==this._def.value){return f(this._getOrReturnCtx(e),{code:l.invalid_literal,expected:this._def.value}),y}return{status:"valid",value:e.data}}get value(){return this._def.value}}function ie(e,t){return new oe({values:e,typeName:we.ZodEnum,...k(t)})}ae.create=(e,t)=>new ae({value:e,typeName:we.ZodLiteral,...k(t)});class oe extends E{_parse(e){if("string"!=typeof e.data){const t=this._getOrReturnCtx(e),n=this._def.values;return f(t,{expected:o.joinValues(n),received:t.parsedType,code:l.invalid_type}),y}if(-1===this._def.values.indexOf(e.data)){const t=this._getOrReturnCtx(e),n=this._def.values;return f(t,{received:t.data,code:l.invalid_enum_value,options:n}),y}return w(e.data)}get options(){return this._def.values}get enum(){const e={};for(const t of this._def.values)e[t]=t;return e}get Values(){const e={};for(const t of this._def.values)e[t]=t;return e}get Enum(){const e={};for(const t of this._def.values)e[t]=t;return e}}oe.create=ie;class ce extends E{_parse(e){const t=o.getValidEnumValues(this._def.values),n=this._getOrReturnCtx(e);if(n.parsedType!==c.string&&n.parsedType!==c.number){const e=o.objectValues(t);return f(n,{expected:o.joinValues(e),received:n.parsedType,code:l.invalid_type}),y}if(-1===t.indexOf(e.data)){const e=o.objectValues(t);return f(n,{received:n.data,code:l.invalid_enum_value,options:e}),y}return w(e.data)}get enum(){return this._def.values}}ce.create=(e,t)=>new ce({values:e,typeName:we.ZodNativeEnum,...k(t)});class de extends E{_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==c.promise&&!1===t.common.async)return f(t,{code:l.invalid_type,expected:c.promise,received:t.parsedType}),y;const n=t.parsedType===c.promise?t.data:Promise.resolve(t.data);return w(n.then((e=>this._def.type.parseAsync(e,{path:t.path,errorMap:t.common.contextualErrorMap}))))}}de.create=(e,t)=>new de({type:e,typeName:we.ZodPromise,...k(t)});class le extends E{innerType(){return this._def.schema}_parse(e){const{status:t,ctx:n}=this._processInputParams(e),s=this._def.effect||null;if("preprocess"===s.type){const e=s.transform(n.data);return n.common.async?Promise.resolve(e).then((e=>this._def.schema._parseAsync({data:e,path:n.path,parent:n}))):this._def.schema._parseSync({data:e,path:n.path,parent:n})}const r={addIssue:e=>{f(n,e),e.fatal?t.abort():t.dirty()},get path(){return n.path}};if(r.addIssue=r.addIssue.bind(r),"refinement"===s.type){const e=e=>{const t=s.refinement(e,r);if(n.common.async)return Promise.resolve(t);if(t instanceof Promise)throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");return e};if(!1===n.common.async){const s=this._def.schema._parseSync({data:n.data,path:n.path,parent:n});return"aborted"===s.status?y:("dirty"===s.status&&t.dirty(),e(s.value),{status:t.value,value:s.value})}return this._def.schema._parseAsync({data:n.data,path:n.path,parent:n}).then((n=>"aborted"===n.status?y:("dirty"===n.status&&t.dirty(),e(n.value).then((()=>({status:t.value,value:n.value}))))))}if("transform"===s.type){if(!1===n.common.async){const e=this._def.schema._parseSync({data:n.data,path:n.path,parent:n});if(!_(e))return e;const a=s.transform(e.value,r);if(a instanceof Promise)throw new Error("Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.");return{status:t.value,value:a}}return this._def.schema._parseAsync({data:n.data,path:n.path,parent:n}).then((e=>_(e)?Promise.resolve(s.transform(e.value,r)).then((e=>({status:t.value,value:e}))):e))}o.assertNever(s)}}le.create=(e,t,n)=>new le({schema:e,typeName:we.ZodEffects,effect:t,...k(n)}),le.createWithPreprocess=(e,t,n)=>new le({schema:t,effect:{type:"preprocess",transform:e},typeName:we.ZodEffects,...k(n)});class ue extends E{_parse(e){return this._getType(e)===c.undefined?w(void 0):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}}ue.create=(e,t)=>new ue({innerType:e,typeName:we.ZodOptional,...k(t)});class he extends E{_parse(e){return this._getType(e)===c.null?w(null):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}}he.create=(e,t)=>new he({innerType:e,typeName:we.ZodNullable,...k(t)});class pe extends E{_parse(e){const{ctx:t}=this._processInputParams(e);let n=t.data;return t.parsedType===c.undefined&&(n=this._def.defaultValue()),this._def.innerType._parse({data:n,path:t.path,parent:t})}removeDefault(){return this._def.innerType}}pe.create=(e,t)=>new ue({innerType:e,typeName:we.ZodOptional,...k(t)});class me extends E{_parse(e){if(this._getType(e)!==c.nan){const t=this._getOrReturnCtx(e);return f(t,{code:l.invalid_type,expected:c.nan,received:t.parsedType}),y}return{status:"valid",value:e.data}}}me.create=e=>new me({typeName:we.ZodNaN,...k(e)});const ge=Symbol("zod_brand");class fe extends E{_parse(e){const{ctx:t}=this._processInputParams(e),n=t.data;return this._def.type._parse({data:n,path:t.path,parent:t})}unwrap(){return this._def.type}}const ve=(e,t={},n)=>e?F.create().superRefine(((s,r)=>{if(!e(s)){const e="function"==typeof t?t(s):t,a="string"==typeof e?{message:e}:e;r.addIssue({code:"custom",...a,fatal:n})}})):F.create(),ye={object:J.lazycreate};var we;!function(e){e.ZodString="ZodString",e.ZodNumber="ZodNumber",e.ZodNaN="ZodNaN",e.ZodBigInt="ZodBigInt",e.ZodBoolean="ZodBoolean",e.ZodDate="ZodDate",e.ZodUndefined="ZodUndefined",e.ZodNull="ZodNull",e.ZodAny="ZodAny",e.ZodUnknown="ZodUnknown",e.ZodNever="ZodNever",e.ZodVoid="ZodVoid",e.ZodArray="ZodArray",e.ZodObject="ZodObject",e.ZodUnion="ZodUnion",e.ZodDiscriminatedUnion="ZodDiscriminatedUnion",e.ZodIntersection="ZodIntersection",e.ZodTuple="ZodTuple",e.ZodRecord="ZodRecord",e.ZodMap="ZodMap",e.ZodSet="ZodSet",e.ZodFunction="ZodFunction",e.ZodLazy="ZodLazy",e.ZodLiteral="ZodLiteral",e.ZodEnum="ZodEnum",e.ZodEffects="ZodEffects",e.ZodNativeEnum="ZodNativeEnum",e.ZodOptional="ZodOptional",e.ZodNullable="ZodNullable",e.ZodDefault="ZodDefault",e.ZodPromise="ZodPromise",e.ZodBranded="ZodBranded"}(we||(we={}));const be=N.create,xe=j.create,_e=me.create,Ce=R.create,Ie=A.create,Se=L.create,Te=B.create,ke=Z.create,Ee=F.create,Oe=z.create,Pe=V.create,De=U.create,Ne=$.create,Me=J.create,je=J.strictCreate,Re=q.create,Ae=G.create,Le=Q.create,Be=Y.create,Ze=ee.create,Fe=te.create,ze=ne.create,Ve=se.create,Ue=re.create,$e=ae.create,Ke=oe.create,We=ce.create,He=de.create,Je=le.create,qe=ue.create,Ge=he.create,Xe=le.createWithPreprocess,Qe=y;var Ye=Object.freeze({__proto__:null,getParsedType:d,ZodParsedType:c,defaultErrorMap:h,setErrorMap:function(e){p=e},getErrorMap:m,makeIssue:g,EMPTY_PATH:[],addIssueToContext:f,ParseStatus:v,INVALID:y,DIRTY:e=>({status:"dirty",value:e}),OK:w,isAborted:b,isDirty:x,isValid:_,isAsync:C,ZodType:E,ZodString:N,ZodNumber:j,ZodBigInt:R,ZodBoolean:A,ZodDate:L,ZodUndefined:B,ZodNull:Z,ZodAny:F,ZodUnknown:z,ZodNever:V,ZodVoid:U,ZodArray:$,get objectUtil(){return K},ZodObject:J,ZodUnion:q,ZodDiscriminatedUnion:G,ZodIntersection:Q,ZodTuple:Y,ZodRecord:ee,ZodMap:te,ZodSet:ne,ZodFunction:se,ZodLazy:re,ZodLiteral:ae,ZodEnum:oe,ZodNativeEnum:ce,ZodPromise:de,ZodEffects:le,ZodTransformer:le,ZodOptional:ue,ZodNullable:he,ZodDefault:pe,ZodNaN:me,BRAND:ge,ZodBranded:fe,custom:ve,Schema:E,ZodSchema:E,late:ye,get ZodFirstPartyTypeKind(){return we},any:Ee,array:Ne,bigint:Ce,boolean:Ie,date:Se,discriminatedUnion:Ae,effect:Je,enum:Ke,function:Ve,instanceof:(e,t={message:`Input not instance of ${e.name}`})=>ve((t=>t instanceof e),t,!0),intersection:Le,lazy:Ue,literal:$e,map:Fe,nan:_e,nativeEnum:We,never:Pe,null:ke,nullable:Ge,number:xe,object:Me,oboolean:()=>Ie().optional(),onumber:()=>xe().optional(),optional:qe,ostring:()=>be().optional(),preprocess:Xe,promise:He,record:Ze,set:ze,strictObject:je,string:be,transformer:Je,tuple:Be,undefined:Te,union:Re,unknown:Oe,void:De,NEVER:Qe,ZodIssueCode:l,quotelessJson:e=>JSON.stringify(e,null,2).replace(/"([^"]+)":/g,"$1:"),ZodError:u});const et=e("command parser");function tt(e,t){const n=t.safeParse(e);if(n.success)return n.data;et(`Command ${JSON.stringify(e)} parse failed: ${JSON.stringify(n)}.`);const s=n.error.errors.map((e=>`${e.message} in ${e.path.map((e=>JSON.stringify(e))).join("/")}.`)).join(" ");throw new r(s)}var nt,st,rt,at,it,ot;!function(e){e.RemoteReferenceSchema=Ye.object({handle:Ye.string().min(1)});const t=Ye.object({type:Ye.literal("undefined")}),n=Ye.object({type:Ye.literal("null")}),s=Ye.object({type:Ye.literal("string"),value:Ye.string()}),r=Ye.enum(["NaN","-0","Infinity","+Infinity","-Infinity"]),a=Ye.object({type:Ye.literal("number"),value:Ye.union([r,Ye.number()])}),i=Ye.object({type:Ye.literal("boolean"),value:Ye.boolean()}),o=Ye.object({type:Ye.literal("bigint"),value:Ye.string()}),c=Ye.union([t,n,s,a,i,o]);e.LocalValueSchema=Ye.lazy((()=>Ye.union([c,u,h,m,g,f,v])));const d=Ye.union([e.RemoteReferenceSchema,e.LocalValueSchema]),l=Ye.array(d),u=Ye.lazy((()=>Ye.object({type:Ye.literal("array"),value:l}))),h=Ye.object({type:Ye.literal("date"),value:Ye.string().min(1)}),p=Ye.lazy((()=>Ye.tuple([Ye.union([Ye.string(),d]),d]))),m=Ye.object({type:Ye.literal("map"),value:Ye.array(p)}),g=Ye.object({type:Ye.literal("object"),value:Ye.array(p)}),f=Ye.lazy((()=>Ye.object({type:Ye.literal("regexp"),value:Ye.object({pattern:Ye.string(),flags:Ye.string().optional()})}))),v=Ye.lazy((()=>Ye.object({type:Ye.literal("set"),value:l})));e.BrowsingContextSchema=Ye.string()}(nt||(nt={})),function(e){const t=Ye.enum(["window","dedicated-worker","shared-worker","service-worker","worker","paint-worklet","audio-worklet","worklet"]),n=Ye.object({context:nt.BrowsingContextSchema.optional(),type:t.optional()});e.parseGetRealmsParams=function(e){return tt(e,n)};const s=Ye.object({context:nt.BrowsingContextSchema,sandbox:Ye.string().optional()}),r=Ye.object({realm:Ye.string().min(1)}),a=Ye.union([r,s]),i=Ye.enum(["root","none"]),o=Ye.object({expression:Ye.string(),awaitPromise:Ye.boolean(),target:a,resultOwnership:i.optional()});e.parseEvaluateParams=function(e){return tt(e,o)};const c=Ye.object({target:a,handles:Ye.array(Ye.string())});e.parseDisownParams=function(e){return tt(e,c)};const d=Ye.union([nt.RemoteReferenceSchema,nt.LocalValueSchema]),l=Ye.object({functionDeclaration:Ye.string(),target:a,arguments:Ye.array(d).optional(),this:d.optional(),awaitPromise:Ye.boolean(),resultOwnership:i.optional()});e.parseCallFunctionParams=function(e){return tt(e,l)}}(st||(st={})),function(e){const t=Ye.object({maxDepth:Ye.number().int().nonnegative().max(9007199254740991).optional(),root:nt.BrowsingContextSchema.optional()});e.parseGetTreeParams=function(e){return tt(e,t)};const n=Ye.enum(["none","interactive","complete"]),s=Ye.object({context:nt.BrowsingContextSchema,url:Ye.string().url(),wait:n.optional()});e.parseNavigateParams=function(e){return tt(e,s)};const r=Ye.object({type:Ye.enum(["tab","window"]),referenceContext:nt.BrowsingContextSchema.optional()});e.parseCreateParams=function(e){return tt(e,r)};const a=Ye.object({context:nt.BrowsingContextSchema});e.parseCloseParams=function(e){return tt(e,a)};class o extends i{static method="browsingContext.load";constructor(e){super(o.method,e)}}e.LoadEvent=o;class c extends i{static method="browsingContext.domContentLoaded";constructor(e){super(c.method,e)}}e.DomContentLoadedEvent=c;class d extends i{static method="browsingContext.contextCreated";constructor(e){super(d.method,e)}}e.ContextCreatedEvent=d;class l extends i{static method="browsingContext.contextDestroyed";constructor(e){super(l.method,e)}}e.ContextDestroyedEvent=l,function(e){const t=Ye.object({context:nt.BrowsingContextSchema,selector:Ye.string()});e.parseFindElementParams=function(e){return tt(e,t)}}(e.PROTO||(e.PROTO={})),e.EventNames=[o.method,c.method,d.method,l.method]}(rt||(rt={})),function(e){class t extends i{static method="log.entryAdded";constructor(e){super(t.method,e)}}e.LogEntryAddedEvent=t,e.EventNames=[t.method]}(at||(at={})),function(e){const t=Ye.object({cdpMethod:Ye.string(),cdpParams:Ye.object({}).passthrough(),cdpSession:Ye.string().optional()});e.parseSendCommandParams=function(e){return tt(e,t)};const n=Ye.object({context:nt.BrowsingContextSchema});e.parseGetSessionParams=function(e){return tt(e,n)};class s extends i{static method="cdp.eventReceived";constructor(e){super(s.method,e)}}e.EventReceivedEvent=s,e.EventNames=[s.method]}(it||(it={})),function(e){const t=Ye.enum([...rt.EventNames,...at.EventNames,...it.EventNames]),n=Ye.object({events:Ye.array(t),contexts:Ye.array(nt.BrowsingContextSchema).optional()});e.parseSubscribeParams=function(e){return tt(e,n)}}(ot||(ot={}));class ct{#e=()=>{};#t=()=>{};#n;#s=!1;get isFinished(){return this.#s}constructor(){this.#n=new Promise(((e,t)=>{this.#e=e,this.#t=t}))}then(e,t){return this.#n.then(e,t)}catch(e){return this.#n.catch(e)}resolve(e){this.#s=!0,this.#e(e)}reject(e){this.#s=!0,this.#t(e)}finally(e){return this.#n.finally(e)}[Symbol.toStringTag]="Promise"}const dt=["%s","%d","%i","%f","%o","%O","%c"];function lt(e){return dt.some((t=>e.includes(t)))}function ut(e){if(!["array","bigint","date","number","object","string"].includes(e.type))return ht(e);if("bigint"===e.type)return e.value.toString()+"n";if("number"===e.type)return e.value.toString();if(["date","string"].includes(e.type))return JSON.stringify(e.value);if("object"===e.type)return"{"+e.value.map((e=>`${JSON.stringify(e[0])}:${ut(e[1])}`)).join(",")+"}";if("array"===e.type)return"["+e.value.map((e=>ut(e))).join(",")+"]";throw Error("Invalid value type: "+e.toString())}function ht(e){if(!e.hasOwnProperty("value"))return e.type;switch(e.type){case"string":case"number":case"boolean":case"bigint":return e.value;case"regexp":return`/${e.value.pattern}/${e.value.flags}`;case"date":return new Date(e.value).toString();case"object":return`Object(${e.value.length})`;case"array":return`Array(${e.value.length})`;case"map":return`Map(${e.value.length})`;case"set":return`Set(${e.value.length})`;case"node":return"node";default:return e.type}}function pt(e,t){return 0==e.length?"":"string"===e[0].type&&lt(e[0].value.toString())&&t?function(e){let t="";const n=e[0].value.toString(),s=e.slice(1,void 0),r=n.split(new RegExp(dt.map((e=>"("+e+")")).join("|"),"g"));for(const n of r)if(void 0!==n&&""!=n)if(lt(n)){const r=s.shift();if(void 0===r)throw new Error('Less value is provided: "'+pt(e,!1)+'"');"%s"===n?t+=ht(r):"%d"===n||"%i"===n?["bigint","number","string"].includes(r.type)?t+=parseInt(r.value.toString(),10):t+="NaN":"%f"===n?["bigint","number","string"].includes(r.type)?t+=parseFloat(r.value.toString()):t+="NaN":t+=ut(r)}else t+=n;if(s.length>0)throw new Error('More value is provided: "'+pt(e,!1)+'"');return t}(e):e.map((e=>ht(e))).join(" ")}class mt{static#r=0;static#a=1;static#i=new Map;static async serializeCdpObject(e,t,n){const s=await n.cdpClient.Runtime.callFunctionOn({functionDeclaration:String((e=>e)),awaitPromise:!1,arguments:[e],generateWebDriverValue:!0,executionContextId:n.executionContextId});return await this.#o(s,n,t)}static async stringifyObject(e,t){return(await t.cdpClient.Runtime.callFunctionOn({functionDeclaration:String((function(e){return String(e)})),awaitPromise:!1,arguments:[e],returnByValue:!0,executionContextId:t.executionContextId})).result.value}static async callFunction(e,t,n,s,a,i){const o=`(...args)=>{ return _callFunction((\n${t}\n), args);\n      function _callFunction(f, args) {\n        const deserializedThis = args.shift();\n        const deserializedArgs = args;\n        return f.apply(deserializedThis, deserializedArgs);\n      }}`,c=[await this.#c(n,e)];let d;c.push(...await Promise.all(s.map((async t=>await this.#c(t,e)))));try{d=await e.cdpClient.Runtime.callFunctionOn({functionDeclaration:o,awaitPromise:a,arguments:c,generateWebDriverValue:!0,executionContextId:e.executionContextId})}catch(e){if(-32e3===e.code&&["Could not find object with given id","Argument should belong to the same JavaScript world as target object"].includes(e.message))throw new r("Handle was not found.");throw e}return d.exceptionDetails?{exceptionDetails:await this.#d(d.exceptionDetails,this.#a,i,e),realm:e.realmId}:{result:await mt.#o(d,e,i),realm:e.realmId}}static realmDestroyed(e){return Array.from(this.#i.entries()).filter((([t,n])=>n===e.realmId)).map((([e,t])=>this.#i.delete(e)))}static async disown(e,t){if(mt.#i.get(t)===e.realmId){try{await e.cdpClient.Runtime.releaseObject({objectId:t})}catch(e){if(-32e3!==e.code||"Invalid remote object id"!==e.message)throw e}this.#i.delete(t)}}static async#d(e,t,n,s){const r=e.stackTrace?.callFrames.map((e=>({url:e.url,functionName:e.functionName,lineNumber:e.lineNumber-t,columnNumber:e.columnNumber}))),a=await this.serializeCdpObject(e.exception,n,s),i=await this.stringifyObject(e.exception,s);return{exception:a,columnNumber:e.columnNumber,lineNumber:e.lineNumber-t,stackTrace:{callFrames:r||[]},text:i||e.text}}static async#o(e,t,n){const s=e.result.webDriverValue;if(!e.result.objectId)return s;const r=e.result.objectId,a=s;return"root"===n?(a.handle=r,this.#i.set(r,t.realmId)):await t.cdpClient.Runtime.releaseObject({objectId:r}),a}static async scriptEvaluate(e,t,n,s){let r=await e.cdpClient.Runtime.evaluate({contextId:e.executionContextId,expression:t,awaitPromise:n,generateWebDriverValue:!0});return r.exceptionDetails?{exceptionDetails:await this.#d(r.exceptionDetails,this.#r,s,e),realm:e.realmId}:{result:await mt.#o(r,e,s),realm:e.realmId}}static async#c(e,t){if("handle"in e)return{objectId:e.handle};switch(e.type){case"undefined":return{unserializableValue:"undefined"};case"null":return{unserializableValue:"null"};case"string":return{value:e.value};case"number":return"NaN"===e.value?{unserializableValue:"NaN"}:"-0"===e.value?{unserializableValue:"-0"}:"+Infinity"===e.value?{unserializableValue:"+Infinity"}:"Infinity"===e.value?{unserializableValue:"Infinity"}:"-Infinity"===e.value?{unserializableValue:"-Infinity"}:{value:e.value};case"boolean":return{value:!!e.value};case"bigint":return{unserializableValue:`BigInt(${JSON.stringify(e.value)})`};case"date":return{unserializableValue:`new Date(Date.parse(${JSON.stringify(e.value)}))`};case"regexp":return{unserializableValue:`new RegExp(${JSON.stringify(e.value.pattern)}, ${JSON.stringify(e.value.flags)})`};case"map":{const n=await this.#l(e.value,t);return{objectId:(await t.cdpClient.Runtime.callFunctionOn({functionDeclaration:String((function(...e){const t=new Map;for(let n=0;n<e.length;n+=2)t.set(e[n],e[n+1]);return t})),awaitPromise:!1,arguments:n,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}case"object":{const n=await this.#l(e.value,t);return{objectId:(await t.cdpClient.Runtime.callFunctionOn({functionDeclaration:String((function(...e){const t={};for(let n=0;n<e.length;n+=2){t[e[n]]=e[n+1]}return t})),awaitPromise:!1,arguments:n,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}case"array":{const n=await mt.#u(e.value,t);return{objectId:(await t.cdpClient.Runtime.callFunctionOn({functionDeclaration:String((function(...e){return e})),awaitPromise:!1,arguments:n,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}case"set":{const n=await this.#u(e.value,t);return{objectId:(await t.cdpClient.Runtime.callFunctionOn({functionDeclaration:String((function(...e){return new Set(e)})),awaitPromise:!1,arguments:n,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}default:throw new Error(`Value ${JSON.stringify(e)} is not deserializable.`)}}static async#l(e,t){const n=[];for(let s of e){const e=s[0],r=s[1];let a,i;a="string"==typeof e?{value:e}:await this.#c(e,t),i=await this.#c(r,t),n.push(a),n.push(i)}return n}static async#u(e,t){const n=[];for(let s of e)n.push(await this.#c(s,t));return n}}class gt{static#h=new Map;static getTopLevelContexts(){return Array.from(gt.#h.values()).filter((e=>null===e.parentId))}static removeContext(e){gt.#h.delete(e)}static addContext(e){gt.#h.set(e.contextId,e),null!==e.parentId&&gt.getKnownContext(e.parentId).addChild(e)}static hasKnownContext(e){return gt.#h.has(e)}static findContext(e){return gt.#h.get(e)}static getKnownContext(e){const t=gt.findContext(e);if(void 0===t)throw new a(`Context ${e} not found`);return t}}var ft;!function(e){e.window="window"}(ft||(ft={}));class vt{static#p=new Map;static create(e,t,n,s,r,a,i,o){const c=new vt(e,t,n,s,r,a,i,o);return vt.#p.set(c.realmId,c),c}static findRealms(e={}){return Array.from(vt.#p.values()).filter((t=>(void 0===e.realmId||e.realmId===t.realmId)&&((void 0===e.browsingContextId||e.browsingContextId===t.browsingContextId)&&((void 0===e.executionContextId||e.executionContextId===t.executionContextId)&&((void 0===e.type||e.type===t.type)&&((void 0===e.sandbox||e.sandbox===t.#m)&&(void 0===e.cdpSessionId||e.cdpSessionId===t.#g)))))))}static findRealm(e){const t=vt.findRealms(e);if(1===t.length)return t[0]}static getRealm(e){const t=vt.findRealm(e);if(void 0===t)throw new a(`Realm ${JSON.stringify(e)} not found`);return t}static clearBrowsingContext(e){vt.findRealms({browsingContextId:e}).map((e=>e.delete()))}delete(){vt.#p.delete(this.realmId),mt.realmDestroyed(this)}#f;#v;#y;#w;#b;#m;#g;#x;constructor(e,t,n,s,r,a,i,o){this.#f=e,this.#v=t,this.#y=n,this.#m=a,this.#w=s,this.#b=r,this.#g=i,this.#x=o}toBiDi(){return{realm:this.realmId,origin:this.origin,type:this.type,context:this.browsingContextId,...void 0!==this.#m?{sandbox:this.#m}:{}}}get realmId(){return this.#f}get browsingContextId(){return this.#v}get executionContextId(){return this.#y}get origin(){return this.#w}get type(){return this.#b}get cdpClient(){return this.#x}async callFunction(e,t,n,s,r){const a=gt.getKnownContext(this.browsingContextId);return await a.awaitUnblocked(),{result:await mt.callFunction(this,e,t,n,s,r)}}async scriptEvaluate(e,t,n){const s=gt.getKnownContext(this.browsingContextId);return await s.awaitUnblocked(),{result:await mt.scriptEvaluate(this,e,t,n)}}async disown(e){await mt.disown(this,e)}async serializeCdpObject(e,t){return await mt.serializeCdpObject(e,t,this)}async stringifyObject(e,t){return mt.stringifyObject(e,this)}}class yt{#_;#x;#g;#C;constructor(e,t,n,s){this.#g=n,this.#x=t,this.#_=e,this.#C=s}static create(e,t,n,s){const r=new yt(e,t,n,s);return r.#I(),r}#I(){this.#S()}#S(){this.#T()}#T(){this.#x.Runtime.on("consoleAPICalled",(async e=>{const t=vt.findRealm({cdpSessionId:this.#g,executionContextId:e.executionContextId}),n=void 0===t?e.args:await Promise.all(e.args.map((async e=>t.serializeCdpObject(e,"none"))));await this.#C.sendEvent(new at.LogEntryAddedEvent({level:yt.#k(e.type),source:{realm:t?.realmId??"UNKNOWN",context:t?.browsingContextId??"UNKNOWN"},text:pt(n,!0),timestamp:Math.round(e.timestamp),stackTrace:yt.#E(e.stackTrace),type:"console",method:"warning"===e.type?"warn":e.type,args:n}),t?.browsingContextId??"UNKNOWN")})),this.#x.Runtime.on("exceptionThrown",(async e=>{const t=vt.findRealm({cdpSessionId:this.#g,executionContextId:e.exceptionDetails.executionContextId}),n=await(async()=>e.exceptionDetails.exception?void 0===t?JSON.stringify(e.exceptionDetails.exception):await t.stringifyObject(e.exceptionDetails.exception,t):e.exceptionDetails.text)();await this.#C.sendEvent(new at.LogEntryAddedEvent({level:"error",source:{realm:t?.realmId??"UNKNOWN",context:t?.browsingContextId??"UNKNOWN"},text:n,timestamp:Math.round(e.timestamp),stackTrace:yt.#E(e.exceptionDetails.stackTrace),type:"javascript"}),t?.browsingContextId??"UNKNOWN")}))}static#k(e){return["assert","error"].includes(e)?"error":["debug","trace"].includes(e)?"debug":["warn","warning"].includes(e)?"warn":"info"}static#E(e){const t=e?.callFrames.map((e=>({columnNumber:e.columnNumber,functionName:e.functionName,lineNumber:e.lineNumber,url:e.url})));return t?{callFrames:t}:void 0}}var wt=rt.LoadEvent;class bt{#O={documentInitialized:new ct,targetUnblocked:new ct,Page:{navigatedWithinDocument:new ct,lifecycleEvent:{DOMContentLoaded:new ct,load:new ct}}};#_;#P;#D;#N;#C;#M=new Map;#j="about:blank";#R=null;#g;#x;#A;get#L(){if(void 0===this.#A)throw new Error(`No default realm for browsing context ${this.#_}`);return this.#A}constructor(e,t,n,s,r,a,i){this.#_=e,this.#P=t,this.#x=n,this.#D=a,this.#C=i,this.#g=r,this.#N=s,this.#B(),gt.addContext(this)}static async createFrameContext(e,t,n,s,r,a){const i=new bt(e,t,n,s,r,null,a);i.#O.targetUnblocked.resolve(),await a.sendEvent(new rt.ContextCreatedEvent(i.serializeToBidiValue()),i.contextId)}static async createTargetContext(e,t,n,s,r,a,i){const o=new bt(e,t,n,s,r,a,i);o.#Z(),await i.sendEvent(new rt.ContextCreatedEvent(o.serializeToBidiValue()),o.contextId)}get cdpBrowserContextId(){return this.#D}convertFrameToTargetContext(e,t){this.#F(e,t),this.#Z()}async delete(){if(await this.#z(),null!==this.parentId){gt.getKnownContext(this.parentId).#M.delete(this.contextId)}await this.#C.sendEvent(new rt.ContextDestroyedEvent(this.serializeToBidiValue()),this.contextId),gt.removeContext(this.contextId)}async#z(){await Promise.all(this.children.map((e=>e.delete())))}#F(e,t){this.#O.targetUnblocked.isFinished||this.#O.targetUnblocked.reject("OOPiF"),this.#O.targetUnblocked=new ct,this.#x=e,this.#g=t,this.#B()}async#Z(){yt.create(this.#_,this.#x,this.#g,this.#C),await this.#x.Runtime.enable(),await this.#x.Page.enable(),await this.#x.Page.setLifecycleEventsEnabled({enabled:!0}),await this.#x.Target.setAutoAttach({autoAttach:!0,waitForDebuggerOnStart:!0,flatten:!0}),await this.#x.Runtime.runIfWaitingForDebugger(),this.#O.targetUnblocked.resolve()}get contextId(){return this.#_}get parentId(){return this.#P}get cdpSessionId(){return this.#g}get children(){return Array.from(this.#M.values())}get url(){return this.#j}addChild(e){this.#M.set(e.contextId,e)}async awaitLoaded(){await this.#O.Page.lifecycleEvent.load}async awaitUnblocked(){await this.#O.targetUnblocked}serializeToBidiValue(e=0,t=!0){return{context:this.#_,url:this.url,children:e>0?this.children.map((t=>t.serializeToBidiValue(e-1,!1))):null,...t?{parent:this.#P}:{}}}#B(){this.#x.Target.on("targetInfoChanged",(e=>{this.contextId===e.targetInfo.targetId&&(this.#j=e.targetInfo.url)})),this.#x.Page.on("frameNavigated",(async e=>{this.contextId===e.frame.id&&(this.#j=e.frame.url+(e.frame.urlFragment??""),await this.#z(),vt.clearBrowsingContext(this.contextId))})),this.#x.Page.on("navigatedWithinDocument",(e=>{this.contextId===e.frameId&&(this.#j=e.url,this.#O.Page.navigatedWithinDocument.resolve(e))})),this.#x.Page.on("lifecycleEvent",(async e=>{if(this.contextId===e.frameId)if("init"===e.name&&(this.#V(e.loaderId),this.#O.documentInitialized.resolve()),"commit"!==e.name){if(e.loaderId===this.#R)switch(e.name){case"DOMContentLoaded":this.#O.Page.lifecycleEvent.DOMContentLoaded.resolve(e),await this.#C.sendEvent(new rt.DomContentLoadedEvent({context:this.contextId,navigation:this.#R,url:this.#j}),this.contextId);break;case"load":this.#O.Page.lifecycleEvent.load.resolve(e),await this.#C.sendEvent(new wt({context:this.contextId,navigation:this.#R,url:this.#j}),this.contextId)}}else this.#R=e.loaderId})),this.#x.Runtime.on("executionContextCreated",(e=>{if(e.context.auxData.frameId!==this.contextId)return;if(!["default","isolated"].includes(e.context.auxData.type))return;const t=vt.create(e.context.uniqueId,this.contextId,e.context.id,this.#U(e),ft.window,"isolated"===e.context.auxData.type?e.context.name:void 0,this.#g,this.#x);e.context.auxData.isDefault&&(this.#A=t)})),this.#x.Runtime.on("executionContextDestroyed",(e=>{vt.findRealms({cdpSessionId:this.#g,executionContextId:e.executionContextId}).map((e=>e.delete()))}))}#U(e){return"isolated"===e.context.auxData.type?this.#L.origin:["://",""].includes(e.context.origin)?"null":e.context.origin}#V(e){this.#R!==e&&(this.#O.documentInitialized.isFinished||this.#O.documentInitialized.reject("Document changed"),this.#O.documentInitialized=new ct,this.#O.Page.navigatedWithinDocument.isFinished||this.#O.Page.navigatedWithinDocument.reject("Document changed"),this.#O.Page.navigatedWithinDocument=new ct,this.#O.Page.lifecycleEvent.DOMContentLoaded.isFinished||this.#O.Page.lifecycleEvent.DOMContentLoaded.reject("Document changed"),this.#O.Page.lifecycleEvent.DOMContentLoaded=new ct,this.#O.Page.lifecycleEvent.load.isFinished||this.#O.Page.lifecycleEvent.load.reject("Document changed"),this.#O.Page.lifecycleEvent.load=new ct,this.#R=e)}async navigate(e,t){await this.#O.targetUnblocked;const s=await this.#x.Page.navigate({url:e,frameId:this.contextId});if(s.errorText)throw new n(s.errorText);switch(void 0!==s.loaderId&&s.loaderId!==this.#R&&this.#V(s.loaderId),t){case"none":break;case"interactive":void 0===s.loaderId?await this.#O.Page.navigatedWithinDocument:await this.#O.Page.lifecycleEvent.DOMContentLoaded;break;case"complete":void 0===s.loaderId?await this.#O.Page.navigatedWithinDocument:await this.#O.Page.lifecycleEvent.load;break;default:throw new Error(`Not implemented wait '${t}'`)}return{result:{navigation:s.loaderId||null,url:e}}}async findElement(e){await this.#O.targetUnblocked;const t=String((e=>document.querySelector(e))),n=[{type:"string",value:e}];return await this.#L.callFunction(t,{type:"undefined"},n,!0,"root")}async getOrCreateSandbox(e){if(void 0===e||""===e)return this.#L;let t=vt.findRealms({browsingContextId:this.contextId,sandbox:e});if(0==t.length&&(await this.#x.Page.createIsolatedWorld({frameId:this.contextId,worldName:e}),t=vt.findRealms({browsingContextId:this.contextId,sandbox:e})),1!==t.length)throw Error(`Sandbox ${e} wasn't created.`);return t[0]}}const xt=e("context");class _t{sessions=new Set;#$;#K;#N;#C;constructor(e,t,n,s){this.#$=e,this.#K=t,this.#N=n,this.#C=s,this.#W(this.#$.browserClient())}#W(e){this.#H(e)}#H(e){e.Target.on("attachedToTarget",(async t=>{await this.#J(t,e)})),e.Target.on("detachedFromTarget",(async e=>{await _t.#q(e)}))}#G(e){if(this.sessions.has(e))return;this.sessions.add(e);const t=this.#$.getCdpClient(e);this.#H(t),t.on("event",(async(t,n)=>{await this.#C.sendEvent({method:"cdp.eventReceived",params:{cdpMethod:t,cdpParams:n,cdpSession:e}},null)})),t.Page.on("frameAttached",(async n=>{await bt.createFrameContext(n.frameId,n.parentFrameId,t,this.#N,e,this.#C)}))}async#J(e,t){xt("AttachedToTarget event received: "+JSON.stringify(e));const{sessionId:n,targetInfo:s}=e;let r=this.#$.getCdpClient(n);if(!this.#X(s))return await r.Runtime.runIfWaitingForDebugger(),void await t.Target.detachFromTarget(e);this.#G(n),gt.hasKnownContext(s.targetId)?gt.getKnownContext(s.targetId).convertFrameToTargetContext(r,n):await bt.createTargetContext(s.targetId,null,r,this.#N,n,e.targetInfo.browserContextId??null,this.#C)}static async#q(e){const t=e.targetId;await(gt.findContext(t)?.delete())}async process_browsingContext_getTree(e){return{result:{contexts:(void 0===e.root?gt.getTopLevelContexts():[gt.getKnownContext(e.root)]).map((t=>t.serializeToBidiValue(e.maxDepth??Number.MAX_VALUE)))}}}async process_browsingContext_create(e){const t=this.#$.browserClient();let n;if(void 0!==e.referenceContext&&(n=gt.getKnownContext(e.referenceContext),null!==n.parentId))throw new r("referenceContext should be a top-level context");const s=(await t.Target.createTarget({url:"about:blank",newWindow:"window"===e.type,...n?.cdpBrowserContextId?{browserContextId:n.cdpBrowserContextId}:{}})).targetId,a=gt.getKnownContext(s);return await a.awaitLoaded(),{result:a.serializeToBidiValue(1)}}async process_browsingContext_navigate(e){const t=gt.getKnownContext(e.context);return await t.navigate(e.url,void 0!==e.wait?e.wait:"none")}static async#Q(e){if("realm"in e)return vt.getRealm({realmId:e.realm});const t=gt.getKnownContext(e.context);return await t.getOrCreateSandbox(e.sandbox)}async process_script_evaluate(e){const t=await _t.#Q(e.target);return await t.scriptEvaluate(e.expression,e.awaitPromise,e.resultOwnership??"none")}process_script_getRealms(e){void 0!==e.context&&gt.getKnownContext(e.context);return{result:{realms:vt.findRealms({browsingContextId:e.context,type:e.type}).map((e=>e.toBiDi()))}}}async process_script_callFunction(e){const t=await _t.#Q(e.target);return await t.callFunction(e.functionDeclaration,e.this||{type:"undefined"},e.arguments||[],e.awaitPromise,e.resultOwnership??"none")}async process_script_disown(e){const t=await _t.#Q(e.target);return await Promise.all(e.handles.map((async e=>await t.disown(e)))),{result:{}}}async process_PROTO_browsingContext_findElement(e){const t=gt.getKnownContext(e.context);return await t.findElement(e.selector)}async process_browsingContext_close(e){const t=this.#$.browserClient();if(null!==gt.getKnownContext(e.context).parentId)throw new r("Not a top-level browsing context cannot be closed.");const n=new Promise((async n=>{const s=r=>{r.targetId===e.context&&(t.Target.removeListener("detachedFromTarget",s),n())};t.Target.on("detachedFromTarget",s)}));return await this.#$.browserClient().Target.closeTarget({targetId:e.context}),await n,{result:{}}}#X(e){return e.targetId!==this.#K&&["page","iframe"].includes(e.type)}async process_cdp_sendCommand(e){return{result:await this.#$.sendCommand(e.cdpMethod,e.cdpParams,e.cdpSession??null),cdpSession:e.cdpSession}}async process_cdp_getSession(e){const t=e.context,n=gt.getKnownContext(t).cdpSessionId;return void 0===n?{result:{cdpSession:null}}:{result:{cdpSession:n}}}}class Ct{#Y;#N;#C;static run(e,t,n,s){new Ct(e,t,n,s).#ee()}constructor(e,t,n,s){this.#C=n,this.#N=t,this.#Y=new _t(e,s,t,n)}#ee(){this.#N.on("message",(e=>this.#te(e)))}async#ne(){return{result:{ready:!1,message:"already connected"}}}async#se(e,t){return await this.#C.subscribe(e.events,e.contexts??[null],t),{result:{}}}async#re(e,t){return await this.#C.unsubscribe(e.events,e.contexts??[null],t),{result:{}}}async#ae(e){switch(e.method){case"session.status":return await this.#ne();case"session.subscribe":return await this.#se(ot.parseSubscribeParams(e.params),e.channel??null);case"session.unsubscribe":return await this.#re(ot.parseSubscribeParams(e.params),e.channel??null);case"browsingContext.create":return await this.#Y.process_browsingContext_create(rt.parseCreateParams(e.params));case"browsingContext.close":return await this.#Y.process_browsingContext_close(rt.parseCloseParams(e.params));case"browsingContext.getTree":return await this.#Y.process_browsingContext_getTree(rt.parseGetTreeParams(e.params));case"browsingContext.navigate":return await this.#Y.process_browsingContext_navigate(rt.parseNavigateParams(e.params));case"script.getRealms":return this.#Y.process_script_getRealms(st.parseGetRealmsParams(e.params));case"script.callFunction":return await this.#Y.process_script_callFunction(st.parseCallFunctionParams(e.params));case"script.evaluate":return await this.#Y.process_script_evaluate(st.parseEvaluateParams(e.params));case"script.disown":return await this.#Y.process_script_disown(st.parseDisownParams(e.params));case"PROTO.browsingContext.findElement":return await this.#Y.process_PROTO_browsingContext_findElement(rt.PROTO.parseFindElementParams(e.params));case"cdp.sendCommand":return await this.#Y.process_cdp_sendCommand(it.parseSendCommandParams(e.params));case"cdp.getSession":return await this.#Y.process_cdp_getSession(it.parseGetSessionParams(e.params));default:throw new s(`Unknown command '${e.method}'.`)}}#te=async e=>{try{const t=await this.#ae(e),n={id:e.id,...t};await this.#N.sendMessage(n,e.channel??null)}catch(s){if(s instanceof t){const t=s;await this.#N.sendMessage(t.toErrorResponse(e.id),e.channel??null)}else{const t=s;console.error(t),await this.#N.sendMessage(new n(t.message).toErrorResponse(e.id),e.channel??null)}}}}function It(){}function St(){St.init.call(this)}function Tt(e){return void 0===e._maxListeners?St.defaultMaxListeners:e._maxListeners}function kt(e,t,n){if(t)e.call(n);else for(var s=e.length,r=Rt(e,s),a=0;a<s;++a)r[a].call(n)}function Et(e,t,n,s){if(t)e.call(n,s);else for(var r=e.length,a=Rt(e,r),i=0;i<r;++i)a[i].call(n,s)}function Ot(e,t,n,s,r){if(t)e.call(n,s,r);else for(var a=e.length,i=Rt(e,a),o=0;o<a;++o)i[o].call(n,s,r)}function Pt(e,t,n,s,r,a){if(t)e.call(n,s,r,a);else for(var i=e.length,o=Rt(e,i),c=0;c<i;++c)o[c].call(n,s,r,a)}function Dt(e,t,n,s){if(t)e.apply(n,s);else for(var r=e.length,a=Rt(e,r),i=0;i<r;++i)a[i].apply(n,s)}function Nt(e,t,n,s){var r,a,i,o;if("function"!=typeof n)throw new TypeError('"listener" argument must be a function');if((a=e._events)?(a.newListener&&(e.emit("newListener",t,n.listener?n.listener:n),a=e._events),i=a[t]):(a=e._events=new It,e._eventsCount=0),i){if("function"==typeof i?i=a[t]=s?[n,i]:[i,n]:s?i.unshift(n):i.push(n),!i.warned&&(r=Tt(e))&&r>0&&i.length>r){i.warned=!0;var c=new Error("Possible EventEmitter memory leak detected. "+i.length+" "+t+" listeners added. Use emitter.setMaxListeners() to increase limit");c.name="MaxListenersExceededWarning",c.emitter=e,c.type=t,c.count=i.length,o=c,"function"==typeof console.warn?console.warn(o):console.log(o)}}else i=a[t]=n,++e._eventsCount;return e}function Mt(e,t,n){var s=!1;function r(){e.removeListener(t,r),s||(s=!0,n.apply(e,arguments))}return r.listener=n,r}function jt(e){var t=this._events;if(t){var n=t[e];if("function"==typeof n)return 1;if(n)return n.length}return 0}function Rt(e,t){for(var n=new Array(t);t--;)n[t]=e[t];return n}It.prototype=Object.create(null),St.EventEmitter=St,St.usingDomains=!1,St.prototype.domain=void 0,St.prototype._events=void 0,St.prototype._maxListeners=void 0,St.defaultMaxListeners=10,St.init=function(){this.domain=null,St.usingDomains&&undefined.active,this._events&&this._events!==Object.getPrototypeOf(this)._events||(this._events=new It,this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},St.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||isNaN(e))throw new TypeError('"n" argument must be a positive number');return this._maxListeners=e,this},St.prototype.getMaxListeners=function(){return Tt(this)},St.prototype.emit=function(e){var t,n,s,r,a,i,o,c="error"===e;if(i=this._events)c=c&&null==i.error;else if(!c)return!1;if(o=this.domain,c){if(t=arguments[1],!o){if(t instanceof Error)throw t;var d=new Error('Uncaught, unspecified "error" event. ('+t+")");throw d.context=t,d}return t||(t=new Error('Uncaught, unspecified "error" event')),t.domainEmitter=this,t.domain=o,t.domainThrown=!1,o.emit("error",t),!1}if(!(n=i[e]))return!1;var l="function"==typeof n;switch(s=arguments.length){case 1:kt(n,l,this);break;case 2:Et(n,l,this,arguments[1]);break;case 3:Ot(n,l,this,arguments[1],arguments[2]);break;case 4:Pt(n,l,this,arguments[1],arguments[2],arguments[3]);break;default:for(r=new Array(s-1),a=1;a<s;a++)r[a-1]=arguments[a];Dt(n,l,this,r)}return!0},St.prototype.addListener=function(e,t){return Nt(this,e,t,!1)},St.prototype.on=St.prototype.addListener,St.prototype.prependListener=function(e,t){return Nt(this,e,t,!0)},St.prototype.once=function(e,t){if("function"!=typeof t)throw new TypeError('"listener" argument must be a function');return this.on(e,Mt(this,e,t)),this},St.prototype.prependOnceListener=function(e,t){if("function"!=typeof t)throw new TypeError('"listener" argument must be a function');return this.prependListener(e,Mt(this,e,t)),this},St.prototype.removeListener=function(e,t){var n,s,r,a,i;if("function"!=typeof t)throw new TypeError('"listener" argument must be a function');if(!(s=this._events))return this;if(!(n=s[e]))return this;if(n===t||n.listener&&n.listener===t)0==--this._eventsCount?this._events=new It:(delete s[e],s.removeListener&&this.emit("removeListener",e,n.listener||t));else if("function"!=typeof n){for(r=-1,a=n.length;a-- >0;)if(n[a]===t||n[a].listener&&n[a].listener===t){i=n[a].listener,r=a;break}if(r<0)return this;if(1===n.length){if(n[0]=void 0,0==--this._eventsCount)return this._events=new It,this;delete s[e]}else!function(e,t){for(var n=t,s=n+1,r=e.length;s<r;n+=1,s+=1)e[n]=e[s];e.pop()}(n,r);s.removeListener&&this.emit("removeListener",e,i||t)}return this},St.prototype.removeAllListeners=function(e){var t,n;if(!(n=this._events))return this;if(!n.removeListener)return 0===arguments.length?(this._events=new It,this._eventsCount=0):n[e]&&(0==--this._eventsCount?this._events=new It:delete n[e]),this;if(0===arguments.length){for(var s,r=Object.keys(n),a=0;a<r.length;++a)"removeListener"!==(s=r[a])&&this.removeAllListeners(s);return this.removeAllListeners("removeListener"),this._events=new It,this._eventsCount=0,this}if("function"==typeof(t=n[e]))this.removeListener(e,t);else if(t)do{this.removeListener(e,t[t.length-1])}while(t[0]);return this},St.prototype.listeners=function(e){var t,n=this._events;return n&&(t=n[e])?"function"==typeof t?[t.listener||t]:function(e){for(var t=new Array(e.length),n=0;n<t.length;++n)t[n]=e[n].listener||e[n];return t}(t):[]},St.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):jt.call(e,t)},St.prototype.listenerCount=jt,St.prototype.eventNames=function(){return this._eventsCount>0?Reflect.ownKeys(this._events):[]};const At=[{domain:"Accessibility",commands:["disable","enable","getPartialAXTree","getFullAXTree","getRootAXNode","getAXNodeAndAncestors","getChildAXNodes","queryAXTree"]},{domain:"Animation",commands:["disable","enable","getCurrentTime","getPlaybackRate","releaseAnimations","resolveAnimation","seekAnimations","setPaused","setPlaybackRate","setTiming"]},{domain:"Audits",commands:["getEncodedResponse","disable","enable","checkContrast"]},{domain:"BackgroundService",commands:["startObserving","stopObserving","setRecording","clearEvents"]},{domain:"Browser",commands:["setPermission","grantPermissions","resetPermissions","setDownloadBehavior","cancelDownload","close","crash","crashGpuProcess","getVersion","getBrowserCommandLine","getHistograms","getHistogram","getWindowBounds","getWindowForTarget","setWindowBounds","setDockTile","executeBrowserCommand"]},{domain:"CSS",commands:["addRule","collectClassNames","createStyleSheet","disable","enable","forcePseudoState","getBackgroundColors","getComputedStyleForNode","getInlineStylesForNode","getMatchedStylesForNode","getMediaQueries","getPlatformFontsForNode","getStyleSheetText","getLayersForNode","trackComputedStyleUpdates","takeComputedStyleUpdates","setEffectivePropertyValueForNode","setKeyframeKey","setMediaText","setContainerQueryText","setSupportsText","setRuleSelector","setStyleSheetText","setStyleTexts","startRuleUsageTracking","stopRuleUsageTracking","takeCoverageDelta","setLocalFontsEnabled"]},{domain:"CacheStorage",commands:["deleteCache","deleteEntry","requestCacheNames","requestCachedResponse","requestEntries"]},{domain:"Cast",commands:["enable","disable","setSinkToUse","startDesktopMirroring","startTabMirroring","stopCasting"]},{domain:"DOM",commands:["collectClassNamesFromSubtree","copyTo","describeNode","scrollIntoViewIfNeeded","disable","discardSearchResults","enable","focus","getAttributes","getBoxModel","getContentQuads","getDocument","getFlattenedDocument","getNodesForSubtreeByStyle","getNodeForLocation","getOuterHTML","getRelayoutBoundary","getSearchResults","hideHighlight","highlightNode","highlightRect","markUndoableState","moveTo","performSearch","pushNodeByPathToFrontend","pushNodesByBackendIdsToFrontend","querySelector","querySelectorAll","redo","removeAttribute","removeNode","requestChildNodes","requestNode","resolveNode","setAttributeValue","setAttributesAsText","setFileInputFiles","setNodeStackTracesEnabled","getNodeStackTraces","getFileInfo","setInspectedNode","setNodeName","setNodeValue","setOuterHTML","undo","getFrameOwner","getContainerForNode","getQueryingDescendantsForContainer"]},{domain:"DOMDebugger",commands:["getEventListeners","removeDOMBreakpoint","removeEventListenerBreakpoint","removeInstrumentationBreakpoint","removeXHRBreakpoint","setBreakOnCSPViolation","setDOMBreakpoint","setEventListenerBreakpoint","setInstrumentationBreakpoint","setXHRBreakpoint"]},{domain:"EventBreakpoints",commands:["setInstrumentationBreakpoint","removeInstrumentationBreakpoint"]},{domain:"DOMSnapshot",commands:["disable","enable","getSnapshot","captureSnapshot"]},{domain:"DOMStorage",commands:["clear","disable","enable","getDOMStorageItems","removeDOMStorageItem","setDOMStorageItem"]},{domain:"Database",commands:["disable","enable","executeSQL","getDatabaseTableNames"]},{domain:"DeviceOrientation",commands:["clearDeviceOrientationOverride","setDeviceOrientationOverride"]},{domain:"Emulation",commands:["canEmulate","clearDeviceMetricsOverride","clearGeolocationOverride","resetPageScaleFactor","setFocusEmulationEnabled","setAutoDarkModeOverride","setCPUThrottlingRate","setDefaultBackgroundColorOverride","setDeviceMetricsOverride","setScrollbarsHidden","setDocumentCookieDisabled","setEmitTouchEventsForMouse","setEmulatedMedia","setEmulatedVisionDeficiency","setGeolocationOverride","setIdleOverride","clearIdleOverride","setNavigatorOverrides","setPageScaleFactor","setScriptExecutionDisabled","setTouchEmulationEnabled","setVirtualTimePolicy","setLocaleOverride","setTimezoneOverride","setVisibleSize","setDisabledImageTypes","setHardwareConcurrencyOverride","setUserAgentOverride","setAutomationOverride"]},{domain:"HeadlessExperimental",commands:["beginFrame","disable","enable"]},{domain:"IO",commands:["close","read","resolveBlob"]},{domain:"IndexedDB",commands:["clearObjectStore","deleteDatabase","deleteObjectStoreEntries","disable","enable","requestData","getMetadata","requestDatabase","requestDatabaseNames"]},{domain:"Input",commands:["dispatchDragEvent","dispatchKeyEvent","insertText","imeSetComposition","dispatchMouseEvent","dispatchTouchEvent","emulateTouchFromMouseEvent","setIgnoreInputEvents","setInterceptDrags","synthesizePinchGesture","synthesizeScrollGesture","synthesizeTapGesture"]},{domain:"Inspector",commands:["disable","enable"]},{domain:"LayerTree",commands:["compositingReasons","disable","enable","loadSnapshot","makeSnapshot","profileSnapshot","releaseSnapshot","replaySnapshot","snapshotCommandLog"]},{domain:"Log",commands:["clear","disable","enable","startViolationsReport","stopViolationsReport"]},{domain:"Memory",commands:["getDOMCounters","prepareForLeakDetection","forciblyPurgeJavaScriptMemory","setPressureNotificationsSuppressed","simulatePressureNotification","startSampling","stopSampling","getAllTimeSamplingProfile","getBrowserSamplingProfile","getSamplingProfile"]},{domain:"Network",commands:["setAcceptedEncodings","clearAcceptedEncodingsOverride","canClearBrowserCache","canClearBrowserCookies","canEmulateNetworkConditions","clearBrowserCache","clearBrowserCookies","continueInterceptedRequest","deleteCookies","disable","emulateNetworkConditions","enable","getAllCookies","getCertificate","getCookies","getResponseBody","getRequestPostData","getResponseBodyForInterception","takeResponseBodyForInterceptionAsStream","replayXHR","searchInResponseBody","setBlockedURLs","setBypassServiceWorker","setCacheDisabled","setCookie","setCookies","setExtraHTTPHeaders","setAttachDebugStack","setRequestInterception","setUserAgentOverride","getSecurityIsolationStatus","enableReportingApi","loadNetworkResource"]},{domain:"Overlay",commands:["disable","enable","getHighlightObjectForTest","getGridHighlightObjectsForTest","getSourceOrderHighlightObjectForTest","hideHighlight","highlightFrame","highlightNode","highlightQuad","highlightRect","highlightSourceOrder","setInspectMode","setShowAdHighlights","setPausedInDebuggerMessage","setShowDebugBorders","setShowFPSCounter","setShowGridOverlays","setShowFlexOverlays","setShowScrollSnapOverlays","setShowContainerQueryOverlays","setShowPaintRects","setShowLayoutShiftRegions","setShowScrollBottleneckRects","setShowHitTestBorders","setShowWebVitals","setShowViewportSizeOnResize","setShowHinge","setShowIsolatedElements"]},{domain:"Page",commands:["addScriptToEvaluateOnLoad","addScriptToEvaluateOnNewDocument","bringToFront","captureScreenshot","captureSnapshot","clearDeviceMetricsOverride","clearDeviceOrientationOverride","clearGeolocationOverride","createIsolatedWorld","deleteCookie","disable","enable","getAppManifest","getInstallabilityErrors","getManifestIcons","getAppId","getCookies","getFrameTree","getLayoutMetrics","getNavigationHistory","resetNavigationHistory","getResourceContent","getResourceTree","handleJavaScriptDialog","navigate","navigateToHistoryEntry","printToPDF","reload","removeScriptToEvaluateOnLoad","removeScriptToEvaluateOnNewDocument","screencastFrameAck","searchInResource","setAdBlockingEnabled","setBypassCSP","getPermissionsPolicyState","getOriginTrials","setDeviceMetricsOverride","setDeviceOrientationOverride","setFontFamilies","setFontSizes","setDocumentContent","setDownloadBehavior","setGeolocationOverride","setLifecycleEventsEnabled","setTouchEmulationEnabled","startScreencast","stopLoading","crash","close","setWebLifecycleState","stopScreencast","produceCompilationCache","addCompilationCache","clearCompilationCache","setSPCTransactionMode","generateTestReport","waitForDebugger","setInterceptFileChooserDialog"]},{domain:"Performance",commands:["disable","enable","setTimeDomain","getMetrics"]},{domain:"PerformanceTimeline",commands:["enable"]},{domain:"Security",commands:["disable","enable","setIgnoreCertificateErrors","handleCertificateError","setOverrideCertificateErrors"]},{domain:"ServiceWorker",commands:["deliverPushMessage","disable","dispatchSyncEvent","dispatchPeriodicSyncEvent","enable","inspectWorker","setForceUpdateOnPageLoad","skipWaiting","startWorker","stopAllWorkers","stopWorker","unregister","updateRegistration"]},{domain:"Storage",commands:["getStorageKeyForFrame","clearDataForOrigin","getCookies","setCookies","clearCookies","getUsageAndQuota","overrideQuotaForOrigin","trackCacheStorageForOrigin","trackIndexedDBForOrigin","untrackCacheStorageForOrigin","untrackIndexedDBForOrigin","getTrustTokens","clearTrustTokens","getInterestGroupDetails","setInterestGroupTracking"]},{domain:"SystemInfo",commands:["getInfo","getProcessInfo"]},{domain:"Target",commands:["activateTarget","attachToTarget","attachToBrowserTarget","closeTarget","exposeDevToolsProtocol","createBrowserContext","getBrowserContexts","createTarget","detachFromTarget","disposeBrowserContext","getTargetInfo","getTargets","sendMessageToTarget","setAutoAttach","autoAttachRelated","setDiscoverTargets","setRemoteLocations"]},{domain:"Tethering",commands:["bind","unbind"]},{domain:"Tracing",commands:["end","getCategories","recordClockSyncMarker","requestMemoryDump","start"]},{domain:"Fetch",commands:["disable","enable","failRequest","fulfillRequest","continueRequest","continueWithAuth","continueResponse","getResponseBody","takeResponseBodyAsStream"]},{domain:"WebAudio",commands:["enable","disable","getRealtimeData"]},{domain:"WebAuthn",commands:["enable","disable","addVirtualAuthenticator","removeVirtualAuthenticator","addCredential","getCredential","getCredentials","removeCredential","clearCredentials","setUserVerified","setAutomaticPresenceSimulation"]},{domain:"Media",commands:["enable","disable"]},{domain:"Console",commands:["clearMessages","disable","enable"]},{domain:"Debugger",commands:["continueToLocation","disable","enable","evaluateOnCallFrame","getPossibleBreakpoints","getScriptSource","getWasmBytecode","getStackTrace","pause","pauseOnAsyncCall","removeBreakpoint","restartFrame","resume","searchInContent","setAsyncCallStackDepth","setBlackboxPatterns","setBlackboxedRanges","setBreakpoint","setInstrumentationBreakpoint","setBreakpointByUrl","setBreakpointOnFunctionCall","setBreakpointsActive","setPauseOnExceptions","setReturnValue","setScriptSource","setSkipAllPauses","setVariableValue","stepInto","stepOut","stepOver"]},{domain:"HeapProfiler",commands:["addInspectedHeapObject","collectGarbage","disable","enable","getHeapObjectId","getObjectByHeapObjectId","getSamplingProfile","startSampling","startTrackingHeapObjects","stopSampling","stopTrackingHeapObjects","takeHeapSnapshot"]},{domain:"Profiler",commands:["disable","enable","getBestEffortCoverage","setSamplingInterval","start","startPreciseCoverage","startTypeProfile","stop","stopPreciseCoverage","stopTypeProfile","takePreciseCoverage","takeTypeProfile"]},{domain:"Runtime",commands:["awaitPromise","callFunctionOn","compileScript","disable","discardConsoleEntries","enable","evaluate","getIsolateId","getHeapUsage","getProperties","globalLexicalScopeNames","queryObjects","releaseObject","releaseObjectGroup","runIfWaitingForDebugger","runScript","setAsyncCallStackDepth","setCustomObjectFormatterEnabled","setMaxCallStackSizeToCapture","terminateExecution","addBinding","removeBinding","getExceptionDetails"]},{domain:"Schema",commands:["getDomains"]}],Lt=new Map;class Bt extends St{_client;constructor(e){super(),this._client=e}}for(let e of At){class t extends Bt{constructor(e){super(e)}}for(let n of e.commands)Object.defineProperty(t.prototype,n,{value:async function(t){return await this._client.sendCommand(`${e.domain}.${n}`,t)}});Lt.set(e.domain,t)}class Zt extends St{_cdpConnection;_sessionId;_domains;constructor(e,t){super(),this._cdpConnection=e,this._sessionId=t,this._domains=new Map;for(const[e,t]of Lt.entries())this._domains.set(e,new t(this)),Object.defineProperty(this,e,{get(){return this._domains.get(e)}})}sendCommand(e,t){return this._cdpConnection.sendCommand(e,t,this._sessionId)}_onCdpEvent(e,t){this.emit("event",e,t);const[n,s]=e.split("."),r=this._domains.get(n);r&&r.emit(s,t)}}function Ft(e,t){return new Zt(e,t)}const zt=e("cdp");class Vt{_transport;_browserCdpClient;_sessionCdpClients=new Map;_commandCallbacks=new Map;_nextId;constructor(e){this._transport=e,this._nextId=0,this._transport.setOnMessage(this._onMessage),this._browserCdpClient=Ft(this,null)}close(){this._transport.close();for(const[e,{reject:t}]of this._commandCallbacks)t(new Error("Disconnected"));this._commandCallbacks.clear(),this._sessionCdpClients.clear()}browserClient(){return this._browserCdpClient}getCdpClient(e){const t=this._sessionCdpClients.get(e);if(!t)throw new Error("Unknown CDP session ID");return t}sendCommand(e,t,n){return new Promise(((s,r)=>{const a=this._nextId++;this._commandCallbacks.set(a,{resolve:s,reject:r});let i={id:a,method:e,params:t};n&&(i.sessionId=n);const o=JSON.stringify(i);this._transport.sendMessage(o),zt("sent > "+o)}))}_onMessage=async e=>{zt("received < "+e);const t=JSON.parse(e);if("Target.attachedToTarget"===t.method){const{sessionId:e}=t.params;this._sessionCdpClients.set(e,Ft(this,e))}else if("Target.detachedFromTarget"===t.method){const{sessionId:e}=t.params;this._sessionCdpClients.get(e)&&this._sessionCdpClients.delete(e)}if(void 0!==t.id){const e=this._commandCallbacks.get(t.id);e&&(t.result?e.resolve(t.result):t.error&&e.reject(t.error))}else if(t.method){const e=t.sessionId?this._sessionCdpClients.get(t.sessionId):this._browserCdpClient;e&&e._onCdpEvent(t.method,t.params||{})}}}const Ut=e("bidi");class $t extends St{_transport;constructor(e){super(),this._transport=e,this._transport.setOnMessage(this.#te)}async sendMessage(e,t){null!==t&&(e.channel=t);const n=JSON.stringify(e);Ut("sent > "+n),this._transport.sendMessage(n)}close(){this._transport.close()}#te=async e=>{let t;Ut("received < "+e);try{t=this.#ie(e)}catch(t){return void this.#oe(e,"invalid argument",t.message,null)}this.emit("message",t)};#oe(e,t,n,s){const r=this.#ce(e,t,n);this.sendMessage(r,s)}#de(e){return null===e?"null":Array.isArray(e)?"array":typeof e}#ce(e,t,n){let s;try{const t=JSON.parse(e);"object"===this.#de(t)&&"id"in t&&(s=t.id)}catch{}return{id:s,error:t,message:n}}#ie(e){let t;try{t=JSON.parse(e)}catch{throw new Error("Cannot parse data as JSON")}const n=this.#de(t);if("object"!==n)throw new Error(`Expected JSON object but got ${n}`);const{id:s,method:r,params:a}=t,i=this.#de(s);if("number"!==i||!Number.isInteger(s)||s<0)throw new Error(`Expected unsigned integer but got ${i}`);const o=this.#de(r);if("string"!==o)throw new Error(`Expected string method but got ${o}`);const c=this.#de(a);if("object"!==c)throw new Error(`Expected object params but got ${c}`);let d=t.channel;if(void 0!==d){const e=this.#de(d);if("string"!==e)throw new Error(`Expected string channel but got ${e}`);""===d&&(d=void 0)}return{id:s,method:r,params:a,channel:d}}}class Kt{#le=0;#ue=new Map;getChannelsSubscribedToEvent(e,t){return Array.from(this.#ue.keys()).map((n=>({priority:this.#he(e,t,n),channel:n}))).filter((({priority:e,channel:t})=>null!==e)).sort(((e,t)=>e.priority-t.priority)).map((({priority:e,channel:t})=>t))}#he(e,t,n){const s=this.#ue.get(n);if(void 0===s)return null;let r=[s.get(null)?.get(e),s.get(t)?.get(e)].filter((e=>void 0!==e));return 0===r.length?null:Math.min(...r)}subscribe(e,t,n){this.#ue.has(n)||this.#ue.set(n,new Map);const s=this.#ue.get(n);s.has(t)||s.set(t,new Map);const r=s.get(t);r.has(e)||r.set(e,this.#le++)}unsubscribe(e,t,n){if(!this.#ue.has(n))return;const s=this.#ue.get(n);if(!s.has(t))return;const r=s.get(t);r.delete(e),0===r.size&&s.delete(e),0===s.size&&this.#ue.delete(n)}}class Wt{static#pe=0;#me;constructor(){this.#me=++Wt.#pe}get id(){return this.#me}}class Ht{#ge;#fe=[];#ve;constructor(e,t=(e=>{})){this.#ge=e,this.#ve=t}get(){return this.#fe}add(e){for(this.#fe.push(e);this.#fe.length>this.#ge;){const e=this.#fe.shift();void 0!==e&&this.#ve(e)}}}class Jt extends Wt{#_;#ye;constructor(e,t){super(),this.#_=t,this.#ye=e}get contextId(){return this.#_}get event(){return this.#ye}}class qt{static#we=new Map([["log.entryAdded",100]]);#be=new Map;#xe=new Map;#_e=new Map;#Ce;#N;constructor(e){this.#N=e,this.#Ce=new Kt}static#Ie(e,t,n){return JSON.stringify({eventName:e,browsingContext:t,channel:n})}async sendEvent(e,t){const n=new Jt(e,t),s=this.#Ce.getChannelsSubscribedToEvent(e.method,t);this.#Se(n);for(const t of s)await this.#N.sendMessage(e,t),this.#Te(n,t)}async subscribe(e,t,n){for(let s of e)for(let e of t)if(null===e||gt.hasKnownContext(e)){this.#Ce.subscribe(s,e,n);for(let t of this.#ke(s,e,n))await this.#N.sendMessage(t.event,n),this.#Te(t,n)}}async unsubscribe(e,t,n){for(let s of e)for(let e of t)this.#Ce.unsubscribe(s,e,n)}#Se(e){const t=e.event.method;if(!qt.#we.has(t))return;const n=qt.#Ie(t,e.contextId);this.#xe.has(n)||this.#xe.set(n,new Ht(qt.#we.get(t))),this.#xe.get(n).add(e),this.#be.has(t)||this.#be.set(t,new Set),this.#be.get(t).add(e.contextId)}#Te(e,t){const n=e.event.method;if(!qt.#we.has(n))return;const s=qt.#Ie(n,e.contextId,t);this.#_e.set(s,Math.max(this.#_e.get(s)??0,e.id))}#ke(e,t,n){const s=qt.#Ie(e,t),r=qt.#Ie(e,t,n),a=this.#_e.get(r)??-1/0,i=this.#xe.get(s)?.get().filter((e=>e.id>a))??[];return null===t&&Array.from(this.#be.get(e)?.keys()??[]).filter((e=>null!==e)).map((t=>this.#ke(e,t,n))).forEach((e=>i.push(...e))),i.sort(((e,t)=>e.id-t.id))}}
 /**
      * Copyright 2021 Google LLC.
      * Copyright (c) Microsoft Corporation.
@@ -16,5 +16,5 @@
      * limitations under the License.
      *
      * @license
-     */const Ht=e("system"),Jt=async function(){return await new Promise((e=>{window.setSelfTargetId=function(t){Ht("current target ID: "+t),e(t)}}))}();(async()=>{window.document.documentElement.innerHTML="<h1>Bidi mapper runs here!</h1><h2>Don't close.</h2>",window.document.title="BiDi Mapper";const e=function(){class e{_onMessage=null;constructor(){window.cdp.onmessage=e=>{this._onMessage&&this._onMessage.call(null,e)}}setOnMessage(e){this._onMessage=e}async sendMessage(e){window.cdp.send(e)}close(){this._onMessage=null,window.cdp.onmessage=null}}return new Vt(new e)}(),t=e.browserClient(),n=function(){class e{_onMessage=null;constructor(){window.onBidiMessage=e=>{this._onMessage&&this._onMessage.call(null,e)}}setOnMessage(e){this._onMessage=e}async sendMessage(e){window.sendBidiResponse(e)}close(){this._onMessage=null,window.onBidiMessage=null}}return new $t(new e)}(),s=new Kt(n),a=await Jt;Ct.run(e,n,s,a),await async function(e){await e.Target.setDiscoverTargets({discover:!0}),await e.Target.setAutoAttach({autoAttach:!0,waitForDebuggerOnStart:!0,flatten:!0}),await Promise.all(gt.getTopLevelContexts().map((e=>e.awaitLoaded())))}(t),Ht("launched"),n.sendMessage({launched:!0},null)})()}();
+     */const Gt=e("system"),Xt=async function(){return await new Promise((e=>{window.setSelfTargetId=function(t){Gt("current target ID: "+t),e(t)}}))}();(async()=>{window.document.documentElement.innerHTML="<h1>Bidi mapper runs here!</h1><h2>Don't close.</h2>",window.document.title="BiDi Mapper";const e=function(){class e{_onMessage=null;constructor(){window.cdp.onmessage=e=>{this._onMessage&&this._onMessage.call(null,e)}}setOnMessage(e){this._onMessage=e}async sendMessage(e){window.cdp.send(e)}close(){this._onMessage=null,window.cdp.onmessage=null}}return new Vt(new e)}(),t=e.browserClient(),n=function(){class e{_onMessage=null;constructor(){window.onBidiMessage=e=>{this._onMessage&&this._onMessage.call(null,e)}}setOnMessage(e){this._onMessage=e}async sendMessage(e){window.sendBidiResponse(e)}close(){this._onMessage=null,window.onBidiMessage=null}}return new $t(new e)}(),s=new qt(n),r=await Xt;Ct.run(e,n,s,r),await async function(e){await e.Target.setDiscoverTargets({discover:!0}),await e.Target.setAutoAttach({autoAttach:!0,waitForDebuggerOnStart:!0,flatten:!0}),await Promise.all(gt.getTopLevelContexts().map((e=>e.awaitLoaded())))}(t),Gt("launched"),n.sendMessage({launched:!0},null)})()}();
 //# sourceMappingURL=mapper.js.map
diff --git a/third_party/blink/public/common/tokens/tokens_mojom_traits.h b/third_party/blink/public/common/tokens/tokens_mojom_traits.h
index ba14fdf..d8ec43b 100644
--- a/third_party/blink/public/common/tokens/tokens_mojom_traits.h
+++ b/third_party/blink/public/common/tokens/tokens_mojom_traits.h
@@ -56,7 +56,7 @@
       case blink::FrameToken::IndexOf<blink::RemoteFrameToken>():
         return DataView::Tag::kRemoteFrameToken;
     }
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   }
 
   static const blink::LocalFrameToken& local_frame_token(
@@ -112,7 +112,7 @@
       case blink::WorkerToken::IndexOf<blink::SharedWorkerToken>():
         return DataView::Tag::kSharedWorkerToken;
     }
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   }
 
   static const blink::DedicatedWorkerToken& dedicated_worker_token(
@@ -181,7 +181,7 @@
       case blink::WorkletToken::IndexOf<blink::PaintWorkletToken>():
         return DataView::Tag::kPaintWorkletToken;
     }
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   }
 
   static const blink::AnimationWorkletToken& animation_worklet_token(
@@ -256,7 +256,7 @@
       case blink::ExecutionContextToken::IndexOf<blink::PaintWorkletToken>():
         return DataView::Tag::kPaintWorkletToken;
     }
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   }
 
   static const blink::LocalFrameToken& local_frame_token(
@@ -321,7 +321,7 @@
           blink::DedicatedWorkerToken>():
         return DataView::Tag::kDedicatedWorkerToken;
     }
-    IMMEDIATE_CRASH();
+    base::ImmediateCrash();
   }
 
   static const blink::DocumentToken& document_token(
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index f27fa76..7a789dd 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -3388,6 +3388,10 @@
     DetachLayoutTree();
   }
   GetDocument().GetStyleEngine().RemovedFromFlatTree(*this);
+
+  // Ensure removal from accessibility cache even if it doesn't have layout.
+  if (GetDocument().HasAXObjectCache())
+    GetDocument().ExistingAXObjectCache()->Remove(this);
 }
 
 void Node::RegisterScrollTimeline(ScrollTimeline* timeline) {
diff --git a/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc b/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
index bc63f4b4..3dd96696 100644
--- a/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
+++ b/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
@@ -93,7 +93,9 @@
     return scripted_idle_scheduler_->TaskRunner();
   }
 
-  PageScheduler* GetPageScheduler() const override { return page_scheduler_; }
+  PageScheduler* GetPageScheduler() const override {
+    return page_scheduler_.get();
+  }
   AgentGroupScheduler* GetAgentGroupScheduler() override {
     return &page_scheduler_->GetAgentGroupScheduler();
   }
@@ -172,7 +174,7 @@
 
  private:
   MockScriptedIdleTaskControllerScheduler* scripted_idle_scheduler_;
-  Persistent<PageScheduler> page_scheduler_;
+  std::unique_ptr<PageScheduler> page_scheduler_;
   base::WeakPtrFactory<FrameScheduler> weak_ptr_factory_{this};
 };
 
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 252a6e0..d4e86eb0 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -416,7 +416,7 @@
       return nullptr;
     SetNeedsUnbufferedInputEvents(true);
     frame_dispatcher_ = std::make_unique<CanvasResourceDispatcher>(
-        nullptr,
+        nullptr, GetDocument().GetTaskRunner(TaskType::kInternalDefault),
         GetPage()
             ->GetPageScheduler()
             ->GetAgentGroupScheduler()
diff --git a/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_inner_config.h b/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_inner_config.h
index fa27aa4..25fe777 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_inner_config.h
+++ b/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_inner_config.h
@@ -62,14 +62,6 @@
   // Attribute's union type based on its value type.
   template <typename T>
   struct AttributeUnion;
-  template <>
-  struct AttributeUnion<String> {
-    using Type = V8UnionOpaquePropertyOrUSVString;
-  };
-  template <>
-  struct AttributeUnion<uint32_t> {
-    using Type = V8UnionOpaquePropertyOrUnsignedLong;
-  };
 
   // Get attribute's visibility.
   template <Attribute attr>
@@ -119,6 +111,15 @@
   }
 };
 
+template <>
+struct FencedFrameInnerConfig::AttributeUnion<String> {
+  using Type = V8UnionOpaquePropertyOrUSVString;
+};
+template <>
+struct FencedFrameInnerConfig::AttributeUnion<uint32_t> {
+  using Type = V8UnionOpaquePropertyOrUnsignedLong;
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FENCED_FRAME_FENCED_FRAME_INNER_CONFIG_H_
diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
index 612b4c37..aaa1de6 100644
--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
+++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
@@ -385,6 +385,7 @@
   if (!frame_dispatcher_) {
     scoped_refptr<base::SingleThreadTaskRunner>
         agent_group_scheduler_compositor_task_runner;
+    scoped_refptr<base::SingleThreadTaskRunner> dispatcher_task_runner;
     if (auto* top_execution_context = GetTopExecutionContext()) {
       agent_group_scheduler_compositor_task_runner =
           top_execution_context->GetAgentGroupSchedulerCompositorTaskRunner();
@@ -393,14 +394,18 @@
       // SharedWorkers, but for windows and other workers it should be non-null.
       DCHECK(top_execution_context->IsSharedWorkerGlobalScope() ||
              agent_group_scheduler_compositor_task_runner);
+
+      dispatcher_task_runner =
+          top_execution_context->GetTaskRunner(TaskType::kInternalDefault);
     }
 
     // The frame dispatcher connects the current thread of OffscreenCanvas
     // (either main or worker) to the browser process and remains unchanged
     // throughout the lifetime of this OffscreenCanvas.
     frame_dispatcher_ = std::make_unique<CanvasResourceDispatcher>(
-        this, std::move(agent_group_scheduler_compositor_task_runner),
-        client_id_, sink_id_, placeholder_canvas_id_, size_);
+        this, std::move(dispatcher_task_runner),
+        std::move(agent_group_scheduler_compositor_task_runner), client_id_,
+        sink_id_, placeholder_canvas_id_, size_);
 
     if (HasPlaceholderCanvas())
       frame_dispatcher_->SetPlaceholderCanvasDispatcher(placeholder_canvas_id_);
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index d9e1deee..e2a8e2d 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -955,7 +955,6 @@
   visitor->Trace(next_related_page_);
   visitor->Trace(prev_related_page_);
   visitor->Trace(agent_group_scheduler_);
-  visitor->Trace(page_scheduler_);
   Supplementable<Page>::Trace(visitor);
 }
 
@@ -1014,7 +1013,7 @@
       });
   page_visibility_observer_set_.Clear();
 
-  page_scheduler_->Shutdown();
+  page_scheduler_ = nullptr;
 }
 
 void Page::RegisterPluginsChangedObserver(PluginsChangedObserver* observer) {
@@ -1036,7 +1035,7 @@
 
 PageScheduler* Page::GetPageScheduler() const {
   DCHECK(page_scheduler_);
-  return page_scheduler_;
+  return page_scheduler_.get();
 }
 
 bool Page::IsOrdinary() const {
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h
index 0e73a60..b1306177 100644
--- a/third_party/blink/renderer/core/page/page.h
+++ b/third_party/blink/renderer/core/page/page.h
@@ -512,7 +512,7 @@
   // pages or not.
   FrameScheduler::SchedulingAffectingFeatureHandle has_related_pages_;
 
-  Member<PageScheduler> page_scheduler_;
+  std::unique_ptr<PageScheduler> page_scheduler_;
 
   // Overrides for various media features, set from DevTools.
   std::unique_ptr<MediaFeatureOverrides> media_feature_overrides_;
diff --git a/third_party/blink/renderer/core/scroll/scroll_test.cc b/third_party/blink/renderer/core/scroll/scroll_test.cc
index 0be9bb8..f155eef8 100644
--- a/third_party/blink/renderer/core/scroll/scroll_test.cc
+++ b/third_party/blink/renderer/core/scroll/scroll_test.cc
@@ -32,9 +32,10 @@
 namespace blink {
 
 namespace {
-const double kScrollAnimationDuration =
-    (::features::IsImpulseScrollAnimationEnabled() ? 1.5 : 0.5);
+double ScrollAnimationDuration() {
+  return ::features::IsImpulseScrollAnimationEnabled() ? 1.5 : 0.5;
 }
+}  // namespace
 
 class FractionalScrollSimTest : public SimTest, public PaintTestConfigurations {
  public:
@@ -242,7 +243,7 @@
   // The callback is executed when the animation finishes at
   // ScrollAnimator::TickAnimation.
   Compositor().BeginFrame();
-  Compositor().BeginFrame(kScrollAnimationDuration);
+  Compositor().BeginFrame(ScrollAnimationDuration());
   ASSERT_TRUE(finished);
 }
 
@@ -286,7 +287,7 @@
   // The callback is executed when the animation finishes at
   // ScrollAnimator::TickAnimation.
   Compositor().BeginFrame();
-  Compositor().BeginFrame(kScrollAnimationDuration);
+  Compositor().BeginFrame(ScrollAnimationDuration());
   ASSERT_TRUE(finished);
 }
 
@@ -330,7 +331,7 @@
   // The callback is executed when the animation finishes at
   // ScrollAnimator::TickAnimation.
   Compositor().BeginFrame();
-  Compositor().BeginFrame(kScrollAnimationDuration);
+  Compositor().BeginFrame(ScrollAnimationDuration());
   ASSERT_TRUE(finished);
 }
 
@@ -380,7 +381,7 @@
 
   // The callback is executed when the animation finishes at
   // ScrollAnimator::TickAnimation.
-  Compositor().BeginFrame(kScrollAnimationDuration);
+  Compositor().BeginFrame(ScrollAnimationDuration());
   ASSERT_TRUE(finished);
 }
 
diff --git a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
index 3f2fd241..2daed8f 100644
--- a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
+++ b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
@@ -157,16 +157,18 @@
   if (!host || !execution_context)
     return;
 
-  network::mojom::ReferrerPolicy referrer_policy =
+  network::mojom::ReferrerPolicy document_referrer_policy =
       execution_context->GetReferrerPolicy();
   String outgoing_referrer = execution_context->OutgoingReferrer();
 
   Vector<mojom::blink::SpeculationCandidatePtr> candidates;
-  auto push_candidates = [&candidates, &referrer_policy, &outgoing_referrer,
-                          &execution_context](
+  auto push_candidates = [&candidates, &document_referrer_policy,
+                          &outgoing_referrer, &execution_context](
                              mojom::blink::SpeculationAction action,
                              const HeapVector<Member<SpeculationRule>>& rules) {
     for (SpeculationRule* rule : rules) {
+      network::mojom::ReferrerPolicy referrer_policy =
+          rule->referrer_policy().value_or(document_referrer_policy);
       for (const KURL& url : rule->urls()) {
         Referrer referrer = SecurityPolicy::GenerateReferrer(
             referrer_policy, url, outgoing_referrer);
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rule.cc b/third_party/blink/renderer/core/speculation_rules/speculation_rule.cc
index 20aed5ab..d87be47 100644
--- a/third_party/blink/renderer/core/speculation_rules/speculation_rule.cc
+++ b/third_party/blink/renderer/core/speculation_rules/speculation_rule.cc
@@ -13,11 +13,13 @@
     Vector<KURL> urls,
     DocumentRulePredicate* predicate,
     RequiresAnonymousClientIPWhenCrossOrigin requires_anonymous_client_ip,
-    absl::optional<mojom::blink::SpeculationTargetHint> target_hint)
+    absl::optional<mojom::blink::SpeculationTargetHint> target_hint,
+    absl::optional<network::mojom::ReferrerPolicy> referrer_policy)
     : urls_(std::move(urls)),
       predicate_(predicate),
       requires_anonymous_client_ip_(requires_anonymous_client_ip),
-      target_browsing_context_name_hint_(target_hint) {}
+      target_browsing_context_name_hint_(target_hint),
+      referrer_policy_(referrer_policy) {}
 
 SpeculationRule::~SpeculationRule() = default;
 
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rule.h b/third_party/blink/renderer/core/speculation_rules/speculation_rule.h
index ff1fe1a..702e77f2 100644
--- a/third_party/blink/renderer/core/speculation_rules/speculation_rule.h
+++ b/third_party/blink/renderer/core/speculation_rules/speculation_rule.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SPECULATION_RULES_SPECULATION_RULE_H_
 
 #include "base/types/strong_alias.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -31,7 +32,8 @@
       Vector<KURL>,
       DocumentRulePredicate*,
       RequiresAnonymousClientIPWhenCrossOrigin,
-      absl::optional<mojom::blink::SpeculationTargetHint> target_hint);
+      absl::optional<mojom::blink::SpeculationTargetHint> target_hint,
+      absl::optional<network::mojom::ReferrerPolicy>);
   ~SpeculationRule();
 
   const Vector<KURL>& urls() const { return urls_; }
@@ -43,6 +45,9 @@
   target_browsing_context_name_hint() const {
     return target_browsing_context_name_hint_;
   }
+  absl::optional<network::mojom::ReferrerPolicy> referrer_policy() const {
+    return referrer_policy_;
+  }
 
   void Trace(Visitor*) const;
 
@@ -52,6 +57,7 @@
   const RequiresAnonymousClientIPWhenCrossOrigin requires_anonymous_client_ip_;
   const absl::optional<mojom::blink::SpeculationTargetHint>
       target_browsing_context_name_hint_;
+  const absl::optional<network::mojom::ReferrerPolicy> referrer_policy_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc
index dffd7a0..6550b1531 100644
--- a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc
+++ b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/speculation_rules/speculation_rule_set.h"
 
 #include "base/containers/contains.h"
+#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
 #include "third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/speculation_rules/document_rule_predicate.h"
@@ -13,6 +14,7 @@
 #include "third_party/blink/renderer/platform/json/json_values.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_policy.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_view.h"
 
 namespace blink {
@@ -57,7 +59,11 @@
   const char* const kKnownKeys[] = {"source", "urls", "requires", "target_hint",
                                     "where"};
   for (wtf_size_t i = 0; i < input->size(); ++i) {
-    if (!base::Contains(kKnownKeys, input->at(i).first))
+    const String& input_key = input->at(i).first;
+    const bool conditionally_known_key =
+        RuntimeEnabledFeatures::SpeculationRulesReferrerPolicyKeyEnabled() &&
+        (input_key == "referrer_policy");
+    if (!base::Contains(kKnownKeys, input_key) && !conditionally_known_key)
       return nullptr;
   }
 
@@ -174,9 +180,30 @@
     }
   }
 
+  absl::optional<network::mojom::ReferrerPolicy> referrer_policy;
+  JSONValue* referrer_policy_value = input->Get("referrer_policy");
+  if (referrer_policy_value) {
+    // Feature gated due to known keys check above.
+    DCHECK(RuntimeEnabledFeatures::SpeculationRulesReferrerPolicyKeyEnabled());
+
+    String referrer_policy_str;
+    if (!referrer_policy_value->AsString(&referrer_policy_str))
+      return nullptr;
+
+    network::mojom::ReferrerPolicy referrer_policy_out =
+        network::mojom::ReferrerPolicy::kDefault;
+    if (!SecurityPolicy::ReferrerPolicyFromString(
+            referrer_policy_str, kDoNotSupportReferrerPolicyLegacyKeywords,
+            &referrer_policy_out)) {
+      return nullptr;
+    }
+    DCHECK_NE(referrer_policy_out, network::mojom::ReferrerPolicy::kDefault);
+    referrer_policy = referrer_policy_out;
+  }
+
   return MakeGarbageCollected<SpeculationRule>(
       std::move(urls), document_rule_predicate, requires_anonymous_client_ip,
-      target_hint);
+      target_hint, referrer_policy);
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc
index 73501f3..031c830 100644
--- a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc
+++ b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc
@@ -44,6 +44,7 @@
 using ::testing::AllOf;
 using ::testing::ElementsAre;
 using ::testing::Not;
+using ::testing::PrintToString;
 
 // Convenience matcher for list rules that sub-matches on their URLs.
 class ListRuleMatcher {
@@ -93,6 +94,18 @@
   return arg->requires_anonymous_client_ip_when_cross_origin();
 }
 
+MATCHER(SetsReferrerPolicy,
+        std::string(negation ? "doesn't set" : "sets") + " a referrer policy") {
+  return arg->referrer_policy().has_value();
+}
+
+MATCHER_P(ReferrerPolicyIs,
+          policy,
+          std::string(negation ? "doesn't have" : "has") + " " +
+              PrintToString(policy) + " as the referrer policy") {
+  return arg->referrer_policy() == policy;
+}
+
 class SpeculationRuleSetTest : public ::testing::Test {
  public:
   SpeculationRuleSetTest()
@@ -266,6 +279,9 @@
 }
 
 TEST_F(SpeculationRuleSetTest, DropUnrecognizedRules) {
+  ScopedSpeculationRulesReferrerPolicyKeyForTest enable_referrer_policy_key{
+      true};
+
   auto* rule_set = SpeculationRuleSet::Parse(
       R"({"prefetch": [)"
 
@@ -284,6 +300,16 @@
       // A rule with an unrecognized requirement.
       R"({"source": "list", "urls": ["/"], "requires": ["more-vespene-gas"]},)"
 
+      // A rule with a referrer_policy of incorrect type.
+      R"({"source": "list", "urls": ["/"], "referrer_policy": 42},)"
+
+      // A rule with an unrecognized referrer_policy.
+      R"({"source": "list", "urls": ["/"],
+          "referrer_policy": "no-referrrrrrrer"},)"
+
+      // A rule with a legacy value for referrer_policy.
+      R"({"source": "list", "urls": ["/"], "referrer_policy": "never"},)"
+
       // Invalid URLs within a list rule should be discarded.
       // This includes totally invalid ones and ones with unacceptable schemes.
       R"({"source": "list",
@@ -398,6 +424,32 @@
             mojom::blink::SpeculationTargetHint::kBlank);
 }
 
+TEST_F(SpeculationRuleSetTest, ReferrerPolicy) {
+  ScopedSpeculationRulesReferrerPolicyKeyForTest enable_referrer_policy_key{
+      true};
+
+  auto* rule_set = SpeculationRuleSet::Parse(
+      R"({
+        "prefetch": [{
+          "source": "list",
+          "urls": ["https://example.com/index2.html"],
+          "referrer_policy": "strict-origin"
+        }, {
+          "source": "list",
+          "urls": ["https://example.com/index3.html"]
+        }]
+      })",
+      KURL("https://example.com/"), execution_context());
+  ASSERT_TRUE(rule_set);
+  EXPECT_THAT(
+      rule_set->prefetch_rules(),
+      ElementsAre(AllOf(MatchesListOfURLs("https://example.com/index2.html"),
+                        ReferrerPolicyIs(
+                            network::mojom::ReferrerPolicy::kStrictOrigin)),
+                  AllOf(MatchesListOfURLs("https://example.com/index3.html"),
+                        Not(SetsReferrerPolicy()))));
+}
+
 TEST_F(SpeculationRuleSetTest, PropagatesToDocument) {
   // A <script> with a case-insensitive type match should be propagated to the
   // document.
diff --git a/third_party/blink/renderer/core/testing/sim/sim_test.cc b/third_party/blink/renderer/core/testing/sim/sim_test.cc
index 589dc6f1..1c319eb 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_test.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_test.cc
@@ -70,8 +70,8 @@
   //
   // Use RunUntilIdle() instead of blink::test::RunPendingTask(), because
   // blink::test::RunPendingTask() posts directly to
-  // Thread::Current()->GetDeprecatedTaskRunner(), which makes it incompatible
-  // with a TestingPlatformSupportWithMockScheduler.
+  // scheduler::GetSingleThreadTaskRunnerForTesting(), which makes it
+  // incompatible with a TestingPlatformSupportWithMockScheduler.
   base::RunLoop().RunUntilIdle();
 
   // Shut down this stuff before settings change to keep the world
diff --git a/third_party/blink/renderer/modules/breakout_box/media_stream_video_track_underlying_source_test.cc b/third_party/blink/renderer/modules/breakout_box/media_stream_video_track_underlying_source_test.cc
index 608c8de..513c32f 100644
--- a/third_party/blink/renderer/modules/breakout_box/media_stream_video_track_underlying_source_test.cc
+++ b/third_party/blink/renderer/modules/breakout_box/media_stream_video_track_underlying_source_test.cc
@@ -202,7 +202,7 @@
   for (wtf_size_t i = 1; i <= buffer_size; ++i) {
     VideoFrame* video_frame =
         ReadObjectFromStream<VideoFrame>(v8_scope, reader);
-    EXPECT_EQ(base::Microseconds(*video_frame->timestamp()), base::Seconds(i));
+    EXPECT_EQ(base::Microseconds(video_frame->timestamp()), base::Seconds(i));
   }
 
   // Pulling causes a pending pull since there are no frames available for
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.cc b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
index 1432403..d587144 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
@@ -483,7 +483,6 @@
   DCHECK(frame);
   handle_ = base::MakeRefCounted<VideoFrameHandle>(
       frame, std::move(sk_image), context, std::move(monitoring_source_id));
-
   external_allocated_memory_ =
       media::VideoFrame::AllocationSize(frame->format(), frame->coded_size());
   context->GetIsolate()->AdjustAmountOfExternalAllocatedMemory(
@@ -974,19 +973,14 @@
   return local_frame->natural_size().width();
 }
 
-absl::optional<int64_t> VideoFrame::timestamp() const {
-  auto local_frame = handle_->frame();
-  if (!local_frame || local_frame->timestamp() == media::kNoTimestamp)
-    return absl::nullopt;
-  return local_frame->timestamp().InMicroseconds();
+int64_t VideoFrame::timestamp() const {
+  return handle_->timestamp().InMicroseconds();
 }
 
 absl::optional<uint64_t> VideoFrame::duration() const {
-  auto local_frame = handle_->frame();
-  // TODO(sandersd): Can a duration be kNoTimestamp?
-  if (!local_frame || !local_frame->metadata().frame_duration.has_value())
-    return absl::nullopt;
-  return local_frame->metadata().frame_duration->InMicroseconds();
+  if (auto duration = handle_->duration())
+    return duration->InMicroseconds();
+  return absl::nullopt;
 }
 
 VideoColorSpace* VideoFrame::colorSpace() {
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.h b/third_party/blink/renderer/modules/webcodecs/video_frame.h
index 4b71dbb..73666f47 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.h
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.h
@@ -77,7 +77,7 @@
 
   absl::optional<V8VideoPixelFormat> format() const;
 
-  absl::optional<int64_t> timestamp() const;
+  int64_t timestamp() const;
   absl::optional<uint64_t> duration() const;
 
   uint32_t codedWidth() const;
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.idl b/third_party/blink/renderer/modules/webcodecs/video_frame.idl
index 728a2fa..a740d487 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.idl
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.idl
@@ -20,7 +20,7 @@
   readonly attribute VideoPixelFormat? format;
 
   // Presentation timestamp, in microseconds.
-  readonly attribute long long? timestamp;
+  readonly attribute long long timestamp;
   // Presentation duration, in microseconds.
   readonly attribute unsigned long long? duration;
 
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame_handle.cc b/third_party/blink/renderer/modules/webcodecs/video_frame_handle.cc
index 7934b53..696498fc 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame_handle.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame_handle.cc
@@ -17,7 +17,9 @@
                                    ExecutionContext* context,
                                    std::string monitoring_source_id)
     : frame_(std::move(frame)),
-      monitoring_source_id_(std::move(monitoring_source_id)) {
+      monitoring_source_id_(std::move(monitoring_source_id)),
+      timestamp_(frame_->timestamp()),
+      duration_(frame_->metadata().frame_duration) {
   DCHECK(frame_);
   DCHECK(context);
 
@@ -45,7 +47,9 @@
     : sk_image_(std::move(sk_image)),
       frame_(std::move(frame)),
       close_auditor_(std::move(close_auditor)),
-      monitoring_source_id_(std::move(monitoring_source_id)) {
+      monitoring_source_id_(std::move(monitoring_source_id)),
+      timestamp_(frame_->timestamp()),
+      duration_(frame_->metadata().frame_duration) {
   DCHECK(frame_);
   DCHECK(close_auditor_);
   MaybeMonitorOpenFrame();
@@ -56,7 +60,9 @@
                                    std::string monitoring_source_id)
     : sk_image_(std::move(sk_image)),
       frame_(std::move(frame)),
-      monitoring_source_id_(std::move(monitoring_source_id)) {
+      monitoring_source_id_(std::move(monitoring_source_id)),
+      timestamp_(frame_->timestamp()),
+      duration_(frame_->metadata().frame_duration) {
   DCHECK(frame_);
   MaybeMonitorOpenFrame();
 }
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame_handle.h b/third_party/blink/renderer/modules/webcodecs/video_frame_handle.h
index 6bf3c434..a350f1a 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame_handle.h
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame_handle.h
@@ -95,6 +95,9 @@
       WebGPUExternalTextureExpireCallback
           webgpu_external_texture_expire_callback);
 
+  base::TimeDelta timestamp() const { return timestamp_; }
+  absl::optional<base::TimeDelta> duration() const { return duration_; }
+
  private:
   friend class WTF::ThreadSafeRefCounted<VideoFrameHandle>;
   ~VideoFrameHandle();
@@ -119,6 +122,12 @@
   Vector<WebGPUExternalTextureExpireCallback>
       webgpu_external_texture_expire_callbacks_;
   std::string monitoring_source_id_;
+
+  // Timestamp and duration must remain valid after close(). If the timestamp or
+  // duration of the underlying media::VideoFrame are modified after
+  // blink::VideoFrame construction, those updates won't appear here.
+  const base::TimeDelta timestamp_;
+  const absl::optional<base::TimeDelta> duration_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame_test.cc b/third_party/blink/renderer/modules/webcodecs/video_frame_test.cc
index 9ed66d5..5d600f19 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame_test.cc
@@ -92,14 +92,14 @@
   VideoFrame* blink_frame =
       CreateBlinkVideoFrame(media_frame, scope.GetExecutionContext());
 
-  EXPECT_EQ(1000u, blink_frame->timestamp().value());
+  EXPECT_EQ(1000u, blink_frame->timestamp());
   EXPECT_EQ(112u, blink_frame->codedWidth());
   EXPECT_EQ(208u, blink_frame->codedHeight());
   EXPECT_EQ(media_frame, blink_frame->frame());
 
   blink_frame->close();
 
-  EXPECT_FALSE(blink_frame->timestamp().has_value());
+  EXPECT_EQ(1000u, blink_frame->timestamp());
   EXPECT_EQ(0u, blink_frame->codedWidth());
   EXPECT_EQ(0u, blink_frame->codedHeight());
   EXPECT_EQ(nullptr, blink_frame->frame());
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
index cc243918..7f8b656 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
@@ -69,6 +69,7 @@
 
 CanvasResourceDispatcher::CanvasResourceDispatcher(
     CanvasResourceDispatcherClient* client,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     scoped_refptr<base::SingleThreadTaskRunner>
         agent_group_scheduler_compositor_task_runner,
     uint32_t client_id,
@@ -84,6 +85,7 @@
       animation_power_mode_voter_(
           power_scheduler::PowerModeArbiter::GetInstance()->NewVoter(
               "PowerModeVoter.Animation.Canvas")),
+      task_runner_(std::move(task_runner)),
       agent_group_scheduler_compositor_task_runner_(
           std::move(agent_group_scheduler_compositor_task_runner)) {
   // Frameless canvas pass an invalid |frame_sink_id_|; don't create mojo
@@ -487,21 +489,17 @@
   if (!agent_group_scheduler_compositor_task_runner_)
     return;
 
-  scoped_refptr<base::SingleThreadTaskRunner> dispatcher_task_runner =
-      Thread::Current()->GetDeprecatedTaskRunner();
-
-  // If the offscreencanvas is in the same tread as the canvas, we will update
+  // If the offscreencanvas is in the same thread as the canvas, we will update
   // the canvas resource dispatcher directly. So Offscreen Canvas can behave in
   // a more synchronous way when it's on the main thread.
   if (IsMainThread()) {
-    UpdatePlaceholderDispatcher(GetWeakPtr(), dispatcher_task_runner,
+    UpdatePlaceholderDispatcher(GetWeakPtr(), task_runner_,
                                 placeholder_canvas_id);
   } else {
     PostCrossThreadTask(
         *agent_group_scheduler_compositor_task_runner_, FROM_HERE,
         CrossThreadBindOnce(UpdatePlaceholderDispatcher, GetWeakPtr(),
-                            std::move(dispatcher_task_runner),
-                            placeholder_canvas_id));
+                            task_runner_, placeholder_canvas_id));
   }
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
index ee182b1..3d55d2d 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
@@ -45,13 +45,18 @@
     kInvalidPlaceholderCanvasId = -1,
   };
 
-  CanvasResourceDispatcher(CanvasResourceDispatcherClient*,
-                           scoped_refptr<base::SingleThreadTaskRunner>
-                               agent_group_scheduler_compositor_task_runner,
-                           uint32_t client_id,
-                           uint32_t sink_id,
-                           int placeholder_canvas_id,
-                           const gfx::Size&);
+  // `task_runner` is the task runner this object is associated with and
+  // executes on. `agent_group_scheduler_compositor_task_runner` is the
+  // compositor task runner for the associated canvas element.
+  CanvasResourceDispatcher(
+      CanvasResourceDispatcherClient*,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner>
+          agent_group_scheduler_compositor_task_runner,
+      uint32_t client_id,
+      uint32_t sink_id,
+      int placeholder_canvas_id,
+      const gfx::Size&);
 
   ~CanvasResourceDispatcher() override;
   void SetNeedsBeginFrame(bool);
@@ -157,6 +162,7 @@
 
   std::unique_ptr<power_scheduler::PowerModeVoter> animation_power_mode_voter_;
 
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner>
       agent_group_scheduler_compositor_task_runner_;
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc
index d245b39..6357ffb 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc
@@ -12,6 +12,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h"
+#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
 #include "third_party/blink/renderer/platform/graphics/test/mock_compositor_frame_sink.h"
@@ -46,12 +47,15 @@
 class MockCanvasResourceDispatcher : public CanvasResourceDispatcher {
  public:
   MockCanvasResourceDispatcher()
-      : CanvasResourceDispatcher(nullptr /* client */,
-                                 base::ThreadTaskRunnerHandle::Get(),
-                                 kClientId,
-                                 kSinkId,
-                                 0 /* placeholder_canvas_id* */,
-                                 {kWidth, kHeight} /* canvas_size */) {}
+      : CanvasResourceDispatcher(
+            /*client=*/nullptr,
+            /*task_runner=*/scheduler::GetSingleThreadTaskRunnerForTesting(),
+            /*agent_group_scheduler_compositor_task_runner=*/
+            scheduler::GetSingleThreadTaskRunnerForTesting(),
+            kClientId,
+            kSinkId,
+            /*placeholder_canvas_id=*/0,
+            /*canvas_size=*/{kWidth, kHeight}) {}
 
   MOCK_METHOD2(PostImageToPlaceholder,
                void(scoped_refptr<CanvasResource>&&,
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
index 1ad1d61..dab242f 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
@@ -11,6 +11,7 @@
 #include "components/viz/test/test_gpu_memory_buffer_manager.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
@@ -302,7 +303,8 @@
 
   MockCanvasResourceDispatcherClient client;
   CanvasResourceDispatcher resource_dispatcher(
-      &client, base::ThreadTaskRunnerHandle::Get(), 1 /* client_id */,
+      &client, scheduler::GetSingleThreadTaskRunnerForTesting(),
+      scheduler::GetSingleThreadTaskRunnerForTesting(), 1 /* client_id */,
       1 /* sink_id */, 1 /* placeholder_canvas_id */, kSize);
 
   auto provider = CanvasResourceProvider::CreateSharedBitmapProvider(
diff --git a/third_party/blink/renderer/platform/graphics/color.cc b/third_party/blink/renderer/platform/graphics/color.cc
index 6004ba3..3852e7b3 100644
--- a/third_party/blink/renderer/platform/graphics/color.cc
+++ b/third_party/blink/renderer/platform/graphics/color.cc
@@ -366,6 +366,34 @@
 }
 
 // static
+Color::ColorSpace Color::ColorInterpolationSpaceToColorSpace(
+    Color::ColorInterpolationSpace color_interpolation_space) {
+  switch (color_interpolation_space) {
+    case (ColorInterpolationSpace::kXYZD65):
+      return ColorSpace::kXYZD65;
+    case (ColorInterpolationSpace::kXYZD50):
+      return ColorSpace::kXYZD50;
+    case (ColorInterpolationSpace::kSRGBLinear):
+      return ColorSpace::kSRGBLinear;
+    case (ColorInterpolationSpace::kLab):
+      return ColorSpace::kLab;
+    case (ColorInterpolationSpace::kOklab):
+      return ColorSpace::kOklab;
+    case (ColorInterpolationSpace::kLch):
+      return ColorSpace::kLch;
+    case (ColorInterpolationSpace::kOklch):
+      return ColorSpace::kOklch;
+    case (ColorInterpolationSpace::kHSL):
+      return ColorSpace::kHSL;
+    case (ColorInterpolationSpace::kHWB):
+      return ColorSpace::kHWB;
+    case (ColorInterpolationSpace::kSRGB):
+    case (ColorInterpolationSpace::kNone):
+      return ColorSpace::kSRGB;
+  }
+}
+
+// static
 Color Color::InterpolateColors(
     Color::ColorInterpolationSpace interpolation_space,
     absl::optional<HueInterpolationMethod> hue_method,
@@ -440,8 +468,41 @@
                                     alpha2.value(), color2.alpha_is_none_)
           : blink::Blend(alpha2.value(), alpha1.value(), percentage);
 
-  Color result =
-      FromColorFunction(ColorSpace::kSRGB, param0, param1, param2, alpha);
+  Color result;
+  ColorSpace result_color_space =
+      ColorInterpolationSpaceToColorSpace(interpolation_space);
+  // TODO: Write a FromColorSpace function that accounts for all these options.
+  if (ValidColorSpaceForFromColorFunction(result_color_space)) {
+    result =
+        FromColorFunction(result_color_space, param0, param1, param2, alpha);
+  } else {
+    switch (result_color_space) {
+      case ColorSpace::kLab:
+        result = FromLab(param0, param1, param2, alpha);
+        break;
+      case ColorSpace::kOklab:
+        result = FromOklab(param0, param1, param2, alpha);
+        break;
+      case ColorSpace::kLch:
+        result = FromLch(param0, param1, param2, alpha);
+        break;
+      case ColorSpace::kOklch:
+        result = FromOklch(param0, param1, param2, alpha);
+        break;
+      case ColorSpace::kHSL:
+        // TODO: Accept optionals
+        result = FromHSLA(param0.value(), param1.value(), param2.value(),
+                          alpha.value());
+        break;
+      case ColorSpace::kHWB:
+        // TODO: Accept optionals
+        result = FromHWBA(param0.value(), param1.value(), param2.value(),
+                          alpha.value());
+        break;
+      default:
+        NOTREACHED();
+    }
+  }
 
   result.UnpremultiplyColor();
 
diff --git a/third_party/blink/renderer/platform/graphics/color.h b/third_party/blink/renderer/platform/graphics/color.h
index 4bee612..d3e2565 100644
--- a/third_party/blink/renderer/platform/graphics/color.h
+++ b/third_party/blink/renderer/platform/graphics/color.h
@@ -322,6 +322,10 @@
       Color::HueInterpolationMethod hue_interpolation_method =
           Color::HueInterpolationMethod::kShorter);
 
+  ColorSpace GetColorSpace() const { return color_space_; }
+  static ColorSpace ColorInterpolationSpaceToColorSpace(
+      Color::ColorInterpolationSpace color_interpolation_space);
+
   FRIEND_TEST_ALL_PREFIXES(BlinkColor, ColorMixNone);
   FRIEND_TEST_ALL_PREFIXES(BlinkColor, ColorInterpolation);
   FRIEND_TEST_ALL_PREFIXES(BlinkColor, HueInterpolation);
diff --git a/third_party/blink/renderer/platform/graphics/color_test.cc b/third_party/blink/renderer/platform/graphics/color_test.cc
index 48edc4f3..042a5e3 100644
--- a/third_party/blink/renderer/platform/graphics/color_test.cc
+++ b/third_party/blink/renderer/platform/graphics/color_test.cc
@@ -89,6 +89,9 @@
         color_mix_test.mix_space, color_mix_test.hue_method,
         color_mix_test.color_left, color_mix_test.color_right,
         color_mix_test.percentage_left, color_mix_test.alpha_multiplier);
+    EXPECT_EQ(
+        result.GetColorSpace(),
+        Color::ColorInterpolationSpaceToColorSpace(color_mix_test.mix_space));
     SkColor4f resultSkColor = result.toSkColor4f();
     SkColor4f expectedSkColor = color_mix_test.color_expected.toSkColor4f();
     EXPECT_NEAR(resultSkColor.fR, expectedSkColor.fR, 0.001f)
@@ -178,6 +181,7 @@
   };
 
   // Tests extracted from the CSS Color 4 spec.
+  // https://csswg.sesse.net/css-color-4/#interpolation-alpha
   ColorsTest colors_test[] = {
       {Color::FromColorFunction(Color::ColorSpace::kSRGB, 0.24f, 0.12f, 0.98f,
                                 0.4f),
@@ -198,7 +202,9 @@
                                 0.72f, 0.6f),
        Color::ColorInterpolationSpace::kLch,
        Color::HueInterpolationMethod::kShorter, 0.5f,
-       Color::FromLch(58.873f, 81.126f, 63.64f, 0.5f)}};
+       // There is an issue with the spec where the hue is un-premultiplied even
+       // though it shouldn't be.
+       Color::FromLch(58.873f, 81.126f, 31.82f, 0.5f)}};
 
   for (auto& color_test : colors_test) {
     Color result = Color::InterpolateColors(
diff --git a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
index f80357f9..444739eb 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
@@ -220,8 +220,14 @@
     // Always convert to N32 format.  This is a constraint of the software
     // compositor.
     constexpr SkColorType dst_color_type = kN32_SkColorType;
-    viz::ResourceFormat resource_format =
-        viz::SkColorTypeToResourceFormat(dst_color_type);
+    // TODO(vasilyt): this used to be
+    // viz::SkColorTypeToResourceFormat(dst_color_type), but on some
+    // platforms (including Mac), kN32_SkColorType is BGRA8888 which
+    // is disallowed as a bitmap format. Deeper refactorings are
+    // needed to fix this properly; in the meantime, force the use of
+    // viz::RGBA_8888 as the resource format. This addresses assertion
+    // failures when serializing these bitmaps to the GPU process.
+    viz::ResourceFormat resource_format = viz::RGBA_8888;
     RegisteredBitmap registered =
         CreateOrRecycleBitmap(size, resource_format, bitmap_registrar);
 
diff --git a/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder_test.cc b/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder_test.cc
index 313e9348..ae4887e 100644
--- a/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder_test.cc
+++ b/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder_test.cc
@@ -6,6 +6,7 @@
 
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
@@ -25,13 +26,15 @@
 
 class MockCanvasResourceDispatcher : public CanvasResourceDispatcher {
  public:
-  MockCanvasResourceDispatcher(unsigned placeholder_id)
-      : CanvasResourceDispatcher(/*client=*/nullptr,
-                                 base::ThreadTaskRunnerHandle::Get(),
-                                 kClientId,
-                                 kSinkId,
-                                 placeholder_id,
-                                 /*canvas_size=*/{kWidth, kHeight}) {}
+  explicit MockCanvasResourceDispatcher(unsigned placeholder_id)
+      : CanvasResourceDispatcher(
+            /*client=*/nullptr,
+            scheduler::GetSingleThreadTaskRunnerForTesting(),
+            scheduler::GetSingleThreadTaskRunnerForTesting(),
+            kClientId,
+            kSinkId,
+            placeholder_id,
+            /*canvas_size=*/{kWidth, kHeight}) {}
 
   MOCK_METHOD2(ReclaimResource,
                void(viz::ResourceId, scoped_refptr<CanvasResource>&&));
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index e8e3e6b..dabf0278 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -714,7 +714,7 @@
     },
     {
       name: "CSSFontFamilyMath",
-      status: "experimental",
+      status: "stable",
       implied_by: ["MathMLCore"],
     },
     {
@@ -780,22 +780,22 @@
     },
     {
       name: "CSSMathDepth",
-      status: "experimental",
+      status: "stable",
       implied_by: ["MathMLCore"],
     },
     {
       name: "CSSMathShift",
-      status: "experimental",
+      status: "stable",
       implied_by: ["MathMLCore"],
     },
     {
       name: "CSSMathStyle",
-      status: "experimental",
+      status: "stable",
       implied_by: ["MathMLCore"],
     },
     {
       name: "CSSMathVariant",
-      status: "experimental",
+      status: "stable",
       implied_by: ["MathMLCore"],
     },
     {
@@ -1664,7 +1664,7 @@
     },
     {
       name: "MathMLCore",
-      status:"experimental",
+      status: "stable",
       depends_on: ["LayoutNG"],
       base_feature: "MathMLCore",
     },
@@ -2557,6 +2557,11 @@
     {
       "name": "SpeculationRulesPrefetchWithSubresources",
     },
+    // Adds a "referrer_policy" key to speculation rules. See https://crbug.com/1355146
+    {
+      name: "SpeculationRulesReferrerPolicyKey",
+      base_feature: "SpeculationRulesReferrerPolicyKey",
+    },
     {
       name: "SrcsetMaxDensity",
     },
diff --git a/third_party/blink/renderer/platform/scheduler/DEPS b/third_party/blink/renderer/platform/scheduler/DEPS
index 99b6695..4cdd605 100644
--- a/third_party/blink/renderer/platform/scheduler/DEPS
+++ b/third_party/blink/renderer/platform/scheduler/DEPS
@@ -59,7 +59,6 @@
   "+third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h",
   "+third_party/blink/renderer/platform/heap/collection_support/heap_vector.h",
   "+third_party/blink/renderer/platform/heap/garbage_collected.h",
-  "+third_party/blink/renderer/platform/heap/member.h",
   "+third_party/blink/renderer/platform/heap/persistent.h",
   "+third_party/blink/renderer/platform/heap/prefinalizer.h",
   "+third_party/blink/renderer/platform/instrumentation",
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 eabe11c..367f69a 100644
--- a/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc
@@ -73,7 +73,9 @@
     return base::ThreadTaskRunnerHandle::Get();
   }
 
-  PageScheduler* GetPageScheduler() const override { return page_scheduler_; }
+  PageScheduler* GetPageScheduler() const override {
+    return page_scheduler_.get();
+  }
   AgentGroupScheduler* GetAgentGroupScheduler() override {
     return &page_scheduler_->GetAgentGroupScheduler();
   }
@@ -151,7 +153,7 @@
   }
 
  private:
-  Persistent<PageScheduler> page_scheduler_;
+  std::unique_ptr<PageScheduler> page_scheduler_;
   base::WeakPtrFactory<FrameScheduler> weak_ptr_factory_{this};
 };
 
@@ -192,14 +194,9 @@
   scoped_refptr<WidgetScheduler> CreateWidgetScheduler() override {
     return base::MakeRefCounted<DummyWidgetScheduler>();
   }
-  void Trace(Visitor* visitor) const override {
-    PageScheduler::Trace(visitor);
-    visitor->Trace(agent_group_scheduler_);
-  }
-  void Shutdown() override {}
 
  private:
-  Member<AgentGroupScheduler> agent_group_scheduler_;
+  Persistent<AgentGroupScheduler> agent_group_scheduler_;
 };
 
 // TODO(altimin,yutak): Merge with SimpleThread in platform.cc.
@@ -320,7 +317,8 @@
   DummyAgentGroupScheduler(const DummyAgentGroupScheduler&) = delete;
   DummyAgentGroupScheduler& operator=(const DummyAgentGroupScheduler&) = delete;
 
-  PageScheduler* CreatePageScheduler(PageScheduler::Delegate*) override {
+  std::unique_ptr<PageScheduler> CreatePageScheduler(
+      PageScheduler::Delegate*) override {
     return CreateDummyPageScheduler();
   }
   scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override {
@@ -356,8 +354,8 @@
   return std::make_unique<DummyFrameScheduler>();
 }
 
-PageScheduler* CreateDummyPageScheduler() {
-  return MakeGarbageCollected<DummyPageScheduler>();
+std::unique_ptr<PageScheduler> CreateDummyPageScheduler() {
+  return std::make_unique<DummyPageScheduler>();
 }
 
 AgentGroupScheduler* CreateDummyAgentGroupScheduler() {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/agent_group_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/agent_group_scheduler_impl.cc
index ccf5dc7c..fcf3aa6 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/agent_group_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/agent_group_scheduler_impl.cc
@@ -58,11 +58,10 @@
   compositor_task_queue_->DetachFromMainThreadScheduler();
 }
 
-PageScheduler* AgentGroupSchedulerImpl::CreatePageScheduler(
+std::unique_ptr<PageScheduler> AgentGroupSchedulerImpl::CreatePageScheduler(
     PageScheduler::Delegate* delegate) {
-  auto* page_scheduler =
-      MakeGarbageCollected<PageSchedulerImpl>(delegate, *this);
-  main_thread_scheduler_.AddPageScheduler(page_scheduler);
+  auto page_scheduler = std::make_unique<PageSchedulerImpl>(delegate, *this);
+  main_thread_scheduler_.AddPageScheduler(page_scheduler.get());
   return page_scheduler;
 }
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/agent_group_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/agent_group_scheduler_impl.h
index ea13735..c57693f 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/agent_group_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/agent_group_scheduler_impl.h
@@ -41,7 +41,8 @@
   AgentGroupSchedulerImpl& operator=(const AgentGroupSchedulerImpl&) = delete;
   ~AgentGroupSchedulerImpl() override = default;
 
-  PageScheduler* CreatePageScheduler(PageScheduler::Delegate*) override;
+  std::unique_ptr<PageScheduler> CreatePageScheduler(
+      PageScheduler::Delegate*) override;
   scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override;
   scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override;
   scoped_refptr<MainThreadTaskQueue> CompositorTaskQueue();
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
index f003c99..fca7372 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
@@ -317,7 +317,7 @@
   std::unique_ptr<FrameTaskQueueController> frame_task_queue_controller_;
 
   MainThreadSchedulerImpl* const main_thread_scheduler_;  // NOT OWNED
-  WeakPersistent<PageSchedulerImpl> parent_page_scheduler_;
+  PageSchedulerImpl* parent_page_scheduler_;              // NOT OWNED
   FrameScheduler::Delegate* delegate_;                    // NOT OWNED
   SchedulingLifecycleState throttling_state_;
   TraceableState<bool, TracingCategory::kInfo> frame_visible_;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
index ed9132f..2a84282 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -67,12 +67,15 @@
 
 // This is a wrapper around MainThreadSchedulerImpl::CreatePageScheduler, that
 // returns the PageScheduler as a PageSchedulerImpl.
-PageSchedulerImpl* CreatePageScheduler(
+std::unique_ptr<PageSchedulerImpl> CreatePageScheduler(
     PageScheduler::Delegate* page_scheduler_delegate,
     MainThreadSchedulerImpl* scheduler,
     AgentGroupScheduler& agent_group_scheduler) {
-  return static_cast<PageSchedulerImpl*>(
-      agent_group_scheduler.CreatePageScheduler(page_scheduler_delegate));
+  std::unique_ptr<PageScheduler> page_scheduler =
+      agent_group_scheduler.CreatePageScheduler(page_scheduler_delegate);
+  std::unique_ptr<PageSchedulerImpl> page_scheduler_impl(
+      static_cast<PageSchedulerImpl*>(page_scheduler.release()));
+  return page_scheduler_impl;
 }
 
 // This is a wrapper around PageSchedulerImpl::CreateFrameScheduler, that
@@ -227,10 +230,10 @@
         CreatePageScheduler(nullptr, scheduler_.get(), *agent_group_scheduler_);
     frame_scheduler_delegate_ = std::make_unique<
         testing::StrictMock<FrameSchedulerDelegateForTesting>>();
-    frame_scheduler_ =
-        CreateFrameScheduler(page_scheduler_, frame_scheduler_delegate_.get(),
-                             /*is_in_embedded_frame_tree=*/false,
-                             FrameScheduler::FrameType::kSubframe);
+    frame_scheduler_ = CreateFrameScheduler(
+        page_scheduler_.get(), frame_scheduler_delegate_.get(),
+        /*is_in_embedded_frame_tree=*/false,
+        FrameScheduler::FrameType::kSubframe);
   }
 
   void ResetFrameScheduler(bool is_in_embedded_frame_tree,
@@ -238,7 +241,7 @@
     auto new_delegate_ = std::make_unique<
         testing::StrictMock<FrameSchedulerDelegateForTesting>>();
     frame_scheduler_ =
-        CreateFrameScheduler(page_scheduler_, new_delegate_.get(),
+        CreateFrameScheduler(page_scheduler_.get(), new_delegate_.get(),
                              is_in_embedded_frame_tree, frame_type);
     frame_scheduler_delegate_ = std::move(new_delegate_);
   }
@@ -258,9 +261,7 @@
   void TearDown() override {
     throttleable_task_queue_.reset();
     frame_scheduler_.reset();
-    if (page_scheduler_)
-      page_scheduler_->Shutdown();
-    page_scheduler_ = nullptr;
+    page_scheduler_.reset();
     agent_group_scheduler_ = nullptr;
     scheduler_->Shutdown();
     scheduler_.reset();
@@ -496,7 +497,7 @@
   base::test::TaskEnvironment task_environment_;
   std::unique_ptr<MainThreadSchedulerImpl> scheduler_;
   Persistent<AgentGroupScheduler> agent_group_scheduler_;
-  Persistent<PageSchedulerImpl> page_scheduler_;
+  std::unique_ptr<PageSchedulerImpl> page_scheduler_;
   std::unique_ptr<FrameSchedulerImpl> frame_scheduler_;
   std::unique_ptr<testing::StrictMock<FrameSchedulerDelegateForTesting>>
       frame_scheduler_delegate_;
@@ -1586,7 +1587,7 @@
             TaskQueue::QueuePriority::kLowPriority);
 
   frame_scheduler_ =
-      CreateFrameScheduler(page_scheduler_, nullptr,
+      CreateFrameScheduler(page_scheduler_.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kMainFrame);
 
@@ -1629,7 +1630,7 @@
             TaskQueue::QueuePriority::kNormalPriority);
 
   frame_scheduler_ =
-      CreateFrameScheduler(page_scheduler_, nullptr,
+      CreateFrameScheduler(page_scheduler_.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kMainFrame);
 
@@ -1671,7 +1672,7 @@
             TaskQueue::QueuePriority::kNormalPriority);
 
   frame_scheduler_ =
-      CreateFrameScheduler(page_scheduler_, nullptr,
+      CreateFrameScheduler(page_scheduler_.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kMainFrame);
 
@@ -1949,8 +1950,7 @@
 TEST_F(FrameSchedulerImplTest, ComputePriorityForDetachedFrame) {
   auto task_queue = GetTaskQueue(TaskType::kJavascriptTimerDelayedLowNesting);
   // Just check that it does not crash.
-  page_scheduler_->Shutdown();
-  page_scheduler_ = nullptr;
+  page_scheduler_.reset();
   frame_scheduler_->ComputePriority(task_queue.get());
 }
 
@@ -2389,11 +2389,11 @@
   MockMainThreadScheduler mock_main_thread_scheduler{task_environment_};
   AgentGroupScheduler* agent_group_scheduler =
       mock_main_thread_scheduler.CreateAgentGroupScheduler();
-  PageSchedulerImpl* page_scheduler = CreatePageScheduler(
+  std::unique_ptr<PageSchedulerImpl> page_scheduler = CreatePageScheduler(
       nullptr, &mock_main_thread_scheduler, *agent_group_scheduler);
 
   std::unique_ptr<FrameSchedulerImpl> main_frame_scheduler =
-      CreateFrameScheduler(page_scheduler, nullptr,
+      CreateFrameScheduler(page_scheduler.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kMainFrame);
 
@@ -2403,7 +2403,6 @@
   main_frame_scheduler->OnFirstContentfulPaintInMainFrame();
 
   main_frame_scheduler = nullptr;
-  page_scheduler->Shutdown();
   page_scheduler = nullptr;
   agent_group_scheduler = nullptr;
   mock_main_thread_scheduler.Shutdown();
@@ -2413,13 +2412,13 @@
   MockMainThreadScheduler mock_main_thread_scheduler{task_environment_};
   AgentGroupScheduler* agent_group_scheduler =
       mock_main_thread_scheduler.CreateAgentGroupScheduler();
-  PageSchedulerImpl* page_scheduler = CreatePageScheduler(
+  std::unique_ptr<PageSchedulerImpl> page_scheduler = CreatePageScheduler(
       nullptr, &mock_main_thread_scheduler, *agent_group_scheduler);
 
   // Test for direct subframes.
   {
     std::unique_ptr<FrameSchedulerImpl> subframe_scheduler =
-        CreateFrameScheduler(page_scheduler, nullptr,
+        CreateFrameScheduler(page_scheduler.get(), nullptr,
                              /*is_in_embedded_frame_tree=*/false,
                              FrameScheduler::FrameType::kSubframe);
 
@@ -2431,7 +2430,7 @@
   // Now test for embedded main frames.
   {
     std::unique_ptr<FrameSchedulerImpl> subframe_scheduler =
-        CreateFrameScheduler(page_scheduler, nullptr,
+        CreateFrameScheduler(page_scheduler.get(), nullptr,
                              /*is_in_embedded_frame_tree=*/true,
                              FrameScheduler::FrameType::kMainFrame);
 
@@ -2440,7 +2439,6 @@
     subframe_scheduler->OnFirstMeaningfulPaint();
   }
 
-  page_scheduler->Shutdown();
   page_scheduler = nullptr;
   agent_group_scheduler = nullptr;
   mock_main_thread_scheduler.Shutdown();
@@ -2799,7 +2797,8 @@
   // Create a FrameScheduler that is same-origin with the main frame, and an
   // associated throttled TaskRunner.
   std::unique_ptr<FrameSchedulerImpl> other_frame_scheduler =
-      CreateFrameScheduler(page_scheduler_, frame_scheduler_delegate_.get(),
+      CreateFrameScheduler(page_scheduler_.get(),
+                           frame_scheduler_delegate_.get(),
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kSubframe);
   ASSERT_FALSE(other_frame_scheduler->IsCrossOriginToNearestMainFrame());
@@ -2853,10 +2852,10 @@
       GetTaskRunner();
   // |other_task_runner| is throttled. It belongs to a different frame on the
   // same page.
-  const auto other_frame_scheduler =
-      CreateFrameScheduler(page_scheduler_, frame_scheduler_delegate_.get(),
-                           /*is_in_embedded_frame_tree=*/false,
-                           FrameScheduler::FrameType::kSubframe);
+  const auto other_frame_scheduler = CreateFrameScheduler(
+      page_scheduler_.get(), frame_scheduler_delegate_.get(),
+      /*is_in_embedded_frame_tree=*/false,
+      FrameScheduler::FrameType::kSubframe);
   const scoped_refptr<base::SingleThreadTaskRunner> other_task_runner =
       GetTaskRunner(other_frame_scheduler.get());
 
@@ -2981,7 +2980,8 @@
   // Create a new FrameScheduler that remains cross-origin with the main frame
   // throughout the test.
   std::unique_ptr<FrameSchedulerImpl> cross_origin_frame_scheduler =
-      CreateFrameScheduler(page_scheduler_, frame_scheduler_delegate_.get(),
+      CreateFrameScheduler(page_scheduler_.get(),
+                           frame_scheduler_delegate_.get(),
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kSubframe);
   cross_origin_frame_scheduler->SetCrossOriginToNearestMainFrame(true);
@@ -3255,7 +3255,8 @@
 TEST_F(FrameSchedulerImplThrottleForegroundTimersEnabledTest,
        VisibleCrossOriginFrameThrottling) {
   std::unique_ptr<FrameSchedulerImpl> cross_origin_frame_scheduler =
-      CreateFrameScheduler(page_scheduler_, frame_scheduler_delegate_.get(),
+      CreateFrameScheduler(page_scheduler_.get(),
+                           frame_scheduler_delegate_.get(),
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kSubframe);
   page_scheduler_->SetPageVisible(true);
@@ -3289,7 +3290,8 @@
 TEST_F(FrameSchedulerImplThrottleForegroundTimersEnabledTest,
        HiddenCrossOriginFrameThrottling) {
   std::unique_ptr<FrameSchedulerImpl> cross_origin_frame_scheduler =
-      CreateFrameScheduler(page_scheduler_, frame_scheduler_delegate_.get(),
+      CreateFrameScheduler(page_scheduler_.get(),
+                           frame_scheduler_delegate_.get(),
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kSubframe);
   page_scheduler_->SetPageVisible(true);
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc
index d76272c..1ab058a 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc
@@ -62,8 +62,7 @@
   void TearDown() override {
     frame_task_queue_controller_.reset();
     frame_scheduler_.reset();
-    page_scheduler_->Shutdown();
-    page_scheduler_ = nullptr;
+    page_scheduler_.reset();
     agent_group_scheduler_ = nullptr;
     scheduler_->Shutdown();
     scheduler_.reset();
@@ -115,7 +114,7 @@
   base::test::TaskEnvironment task_environment_;
   std::unique_ptr<MainThreadSchedulerImpl> scheduler_;
   Persistent<AgentGroupScheduler> agent_group_scheduler_;
-  Persistent<PageScheduler> page_scheduler_;
+  std::unique_ptr<PageScheduler> page_scheduler_;
   std::unique_ptr<FrameScheduler> frame_scheduler_;
   std::unique_ptr<FrameTaskQueueController> frame_task_queue_controller_;
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc
index 6d14829c..391d4ad 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc
@@ -126,7 +126,7 @@
         break;
       case FrameStatus::kMainFrameVisibleService:
         builder.SetFrameType(FrameScheduler::FrameType::kMainFrame)
-            .SetPageScheduler(playing_view_)
+            .SetPageScheduler(playing_view_.get())
             .SetIsFrameVisible(true);
         break;
       case FrameStatus::kMainFrameHidden:
@@ -135,7 +135,7 @@
         break;
       case FrameStatus::kMainFrameHiddenService:
         builder.SetFrameType(FrameScheduler::FrameType::kMainFrame)
-            .SetPageScheduler(playing_view_);
+            .SetPageScheduler(playing_view_.get());
         break;
       case FrameStatus::kMainFrameBackground:
         builder.SetFrameType(FrameScheduler::FrameType::kMainFrame);
@@ -146,7 +146,7 @@
         break;
       case FrameStatus::kMainFrameBackgroundExemptOther:
         builder.SetFrameType(FrameScheduler::FrameType::kMainFrame)
-            .SetPageScheduler(throtting_exempt_view_);
+            .SetPageScheduler(throtting_exempt_view_.get());
         break;
       case FrameStatus::kSameOriginVisible:
         builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
@@ -155,7 +155,7 @@
         break;
       case FrameStatus::kSameOriginVisibleService:
         builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
-            .SetPageScheduler(playing_view_)
+            .SetPageScheduler(playing_view_.get())
             .SetIsFrameVisible(true);
         break;
       case FrameStatus::kSameOriginHidden:
@@ -164,7 +164,7 @@
         break;
       case FrameStatus::kSameOriginHiddenService:
         builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
-            .SetPageScheduler(playing_view_);
+            .SetPageScheduler(playing_view_.get());
         break;
       case FrameStatus::kSameOriginBackground:
         builder.SetFrameType(FrameScheduler::FrameType::kSubframe);
@@ -175,7 +175,7 @@
         break;
       case FrameStatus::kSameOriginBackgroundExemptOther:
         builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
-            .SetPageScheduler(throtting_exempt_view_);
+            .SetPageScheduler(throtting_exempt_view_.get());
         break;
       case FrameStatus::kCrossOriginVisible:
         builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
@@ -186,7 +186,7 @@
       case FrameStatus::kCrossOriginVisibleService:
         builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
             .SetIsCrossOriginToNearestMainFrame(true)
-            .SetPageScheduler(playing_view_)
+            .SetPageScheduler(playing_view_.get())
             .SetIsFrameVisible(true);
         break;
       case FrameStatus::kCrossOriginHidden:
@@ -197,7 +197,7 @@
       case FrameStatus::kCrossOriginHiddenService:
         builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
             .SetIsCrossOriginToNearestMainFrame(true)
-            .SetPageScheduler(playing_view_);
+            .SetPageScheduler(playing_view_.get());
         break;
       case FrameStatus::kCrossOriginBackground:
         builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
@@ -211,7 +211,7 @@
       case FrameStatus::kCrossOriginBackgroundExemptOther:
         builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
             .SetIsCrossOriginToNearestMainFrame(true)
-            .SetPageScheduler(throtting_exempt_view_);
+            .SetPageScheduler(throtting_exempt_view_.get());
         break;
       case FrameStatus::kCount:
         NOTREACHED();
@@ -225,9 +225,9 @@
   std::unique_ptr<MainThreadSchedulerImpl> scheduler_;
   MainThreadMetricsHelper* metrics_helper_;  // NOT OWNED
   std::unique_ptr<base::HistogramTester> histogram_tester_;
-  Persistent<FakePageScheduler> playing_view_ =
+  std::unique_ptr<FakePageScheduler> playing_view_ =
       FakePageScheduler::Builder().SetIsAudioPlaying(true).Build();
-  Persistent<FakePageScheduler> throtting_exempt_view_ =
+  std::unique_ptr<FakePageScheduler> throtting_exempt_view_ =
       FakePageScheduler::Builder().SetIsThrottlingExempt(true).Build();
 };
 
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 d8d6b86..d1cb1a2dc 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
@@ -603,9 +603,7 @@
 
 bool MainThreadSchedulerImpl::
     IsAnyOrdinaryMainFrameWaitingForFirstMeaningfulPaint() const {
-  if (!main_thread_only().page_schedulers)
-    return false;
-  for (const PageSchedulerImpl* ps : *main_thread_only().page_schedulers) {
+  for (const PageSchedulerImpl* ps : main_thread_only().page_schedulers) {
     if (ps->IsOrdinary() && ps->IsWaitingForMainFrameMeaningfulPaint())
       return true;
   }
@@ -614,9 +612,7 @@
 
 bool MainThreadSchedulerImpl::
     IsAnyOrdinaryMainFrameWaitingForFirstContentfulPaint() const {
-  if (!main_thread_only().page_schedulers)
-    return false;
-  for (const PageSchedulerImpl* ps : *main_thread_only().page_schedulers) {
+  for (const PageSchedulerImpl* ps : main_thread_only().page_schedulers) {
     if (ps->IsOrdinary() && ps->IsWaitingForMainFrameContentfulPaint())
       return true;
   }
@@ -734,9 +730,7 @@
 }
 
 bool MainThreadSchedulerImpl::IsIpcTrackingEnabledForAllPages() {
-  if (!main_thread_only().page_schedulers)
-    return true;
-  for (PageSchedulerImpl* scheduler : *main_thread_only().page_schedulers) {
+  for (auto* scheduler : main_thread_only().page_schedulers) {
     if (!(scheduler->IsInBackForwardCache() &&
           scheduler->has_ipc_detection_enabled())) {
       return false;
@@ -1062,11 +1056,8 @@
 
 void MainThreadSchedulerImpl::OnAudioStateChanged() {
   bool is_audio_playing = false;
-  if (main_thread_only().page_schedulers) {
-    for (PageSchedulerImpl* page_scheduler :
-         *main_thread_only().page_schedulers) {
-      is_audio_playing = is_audio_playing || page_scheduler->IsAudioPlaying();
-    }
+  for (PageSchedulerImpl* page_scheduler : main_thread_only().page_schedulers) {
+    is_audio_playing = is_audio_playing || page_scheduler->IsAudioPlaying();
   }
 
   if (is_audio_playing == main_thread_only().is_audio_playing)
@@ -1796,11 +1787,8 @@
 
   ForceUpdatePolicy();
 
-  if (main_thread_only().page_schedulers) {
-    for (PageSchedulerImpl* page_scheduler :
-         *main_thread_only().page_schedulers) {
-      page_scheduler->OnVirtualTimeEnabled();
-    }
+  for (auto* page_scheduler : main_thread_only().page_schedulers) {
+    page_scheduler->OnVirtualTimeEnabled();
   }
 }
 
@@ -1906,11 +1894,8 @@
   dict.Add("default_gesture_prevented", any_thread().default_gesture_prevented);
   dict.Add("is_audio_playing", main_thread_only().is_audio_playing);
   dict.Add("page_schedulers", [&](perfetto::TracedValue context) {
-    if (!main_thread_only().page_schedulers)
-      return;
     auto array = std::move(context).WriteArray();
-    for (PageSchedulerImpl* page_scheduler :
-         *main_thread_only().page_schedulers) {
+    for (const auto* page_scheduler : main_thread_only().page_schedulers) {
       page_scheduler->WriteIntoTrace(array.AppendItem(), optional_now);
     }
   });
@@ -1998,11 +1983,8 @@
       "MainThreadSchedulerImpl::DispatchRequestBeginMainFrameNotExpected",
       "has_tasks", has_tasks);
   bool success = false;
-  if (main_thread_only().page_schedulers) {
-    for (PageSchedulerImpl* page_scheduler :
-         *main_thread_only().page_schedulers) {
-      success |= page_scheduler->RequestBeginMainFrameNotExpected(has_tasks);
-    }
+  for (PageSchedulerImpl* page_scheduler : main_thread_only().page_schedulers) {
+    success |= page_scheduler->RequestBeginMainFrameNotExpected(has_tasks);
   }
   main_thread_only().compositor_will_send_main_frame_not_expected =
       success && has_tasks;
@@ -2299,12 +2281,7 @@
 
 void MainThreadSchedulerImpl::AddPageScheduler(
     PageSchedulerImpl* page_scheduler) {
-  if (!main_thread_only().page_schedulers) {
-    main_thread_only().page_schedulers =
-        MakeGarbageCollected<HeapHashSet<WeakMember<PageSchedulerImpl>>>();
-  }
-
-  main_thread_only().page_schedulers->insert(page_scheduler);
+  main_thread_only().page_schedulers.insert(page_scheduler);
   DetachOnIPCTaskPostedWhileInBackForwardCacheHandler();
   if (page_scheduler->IsOrdinary()) {
     memory_purge_manager_.OnPageCreated(
@@ -2321,10 +2298,9 @@
 
 void MainThreadSchedulerImpl::RemovePageScheduler(
     PageSchedulerImpl* page_scheduler) {
-  DCHECK(main_thread_only().page_schedulers);
-  DCHECK(main_thread_only().page_schedulers->find(page_scheduler) !=
-         main_thread_only().page_schedulers->end());
-  main_thread_only().page_schedulers->erase(page_scheduler);
+  DCHECK(main_thread_only().page_schedulers.find(page_scheduler) !=
+         main_thread_only().page_schedulers.end());
+  main_thread_only().page_schedulers.erase(page_scheduler);
   if (page_scheduler->IsOrdinary()) {
     memory_purge_manager_.OnPageDestroyed(
         page_scheduler->GetPageLifecycleState());
@@ -2359,9 +2335,7 @@
 
 void MainThreadSchedulerImpl::BroadcastIntervention(const String& message) {
   helper_.CheckOnValidThread();
-  if (!main_thread_only().page_schedulers)
-    return;
-  for (PageSchedulerImpl* page_scheduler : *main_thread_only().page_schedulers)
+  for (auto* page_scheduler : main_thread_only().page_schedulers)
     page_scheduler->ReportIntervention(message);
 }
 
@@ -2454,16 +2428,13 @@
     return;
   }
 
-  if (main_thread_only().page_schedulers) {
-    for (PageSchedulerImpl* page_scheduler :
-         *main_thread_only().page_schedulers) {
-      auto status = RecordTaskUkmImpl(
-          queue, task, task_timing,
-          page_scheduler->SelectFrameForUkmAttribution(), false);
-      UMA_HISTOGRAM_ENUMERATION(
-          "Scheduler.Experimental.Renderer.UkmRecordingStatus", status,
-          UkmRecordingStatus::kCount);
-    }
+  for (PageSchedulerImpl* page_scheduler : main_thread_only().page_schedulers) {
+    auto status = RecordTaskUkmImpl(
+        queue, task, task_timing,
+        page_scheduler->SelectFrameForUkmAttribution(), false);
+    UMA_HISTOGRAM_ENUMERATION(
+        "Scheduler.Experimental.Renderer.UkmRecordingStatus", status,
+        UkmRecordingStatus::kCount);
   }
 }
 
@@ -2488,9 +2459,7 @@
       frame_scheduler->GetUkmSourceId());
 
   builder.SetVersion(kUkmMetricVersion);
-  builder.SetPageSchedulers(main_thread_only().page_schedulers
-                                ? main_thread_only().page_schedulers->size()
-                                : 0u);
+  builder.SetPageSchedulers(main_thread_only().page_schedulers.size());
 
   builder.SetRendererBackgrounded(
       main_thread_only().renderer_backgrounded.get());
@@ -2579,11 +2548,8 @@
 void MainThreadSchedulerImpl::OnTraceLogEnabled() {
   CreateTraceEventObjectSnapshot();
   tracing_controller_.OnTraceLogEnabled();
-  if (main_thread_only().page_schedulers) {
-    for (PageSchedulerImpl* page_scheduler :
-         *main_thread_only().page_schedulers) {
-      page_scheduler->OnTraceLogEnabled();
-    }
+  for (PageSchedulerImpl* page_scheduler : main_thread_only().page_schedulers) {
+    page_scheduler->OnTraceLogEnabled();
   }
 }
 
@@ -2779,12 +2745,9 @@
 }
 
 bool MainThreadSchedulerImpl::AllPagesFrozen() const {
-  if (!main_thread_only().page_schedulers ||
-      main_thread_only().page_schedulers->empty()) {
+  if (main_thread_only().page_schedulers.empty())
     return false;
-  }
-  for (const PageSchedulerImpl* scheduler :
-       *main_thread_only().page_schedulers) {
+  for (const auto* scheduler : main_thread_only().page_schedulers) {
     if (!scheduler->IsFrozen())
       return false;
   }
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 538794a3..eb0c52a1 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
@@ -816,7 +816,7 @@
     TraceableState<bool, TracingCategory::kDebug> has_navigated;
     TraceableState<bool, TracingCategory::kDebug> pause_timers_for_webview;
     base::TimeTicks background_status_changed_at;
-    Persistent<HeapHashSet<WeakMember<PageSchedulerImpl>>> page_schedulers;
+    HashSet<PageSchedulerImpl*> page_schedulers;  // Not owned.
     base::ObserverList<RAILModeObserver>::Unchecked
         rail_mode_observers;  // Not owned.
     MainThreadMetricsHelper metrics_helper;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
index 82a248e..d235150 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
@@ -72,12 +72,15 @@
 
 // This is a wrapper around MainThreadSchedulerImpl::CreatePageScheduler, that
 // returns the PageScheduler as a PageSchedulerImpl.
-PageSchedulerImpl* CreatePageScheduler(
+std::unique_ptr<PageSchedulerImpl> CreatePageScheduler(
     PageScheduler::Delegate* page_scheduler_delegate,
     ThreadSchedulerBase* scheduler,
     AgentGroupScheduler& agent_group_scheduler) {
-  return static_cast<PageSchedulerImpl*>(
-      agent_group_scheduler.CreatePageScheduler(page_scheduler_delegate));
+  std::unique_ptr<PageScheduler> page_scheduler =
+      agent_group_scheduler.CreatePageScheduler(page_scheduler_delegate);
+  std::unique_ptr<PageSchedulerImpl> page_scheduler_impl(
+      static_cast<PageSchedulerImpl*>(page_scheduler.release()));
+  return page_scheduler_impl;
 }
 
 // This is a wrapper around PageSchedulerImpl::CreateFrameScheduler, that
@@ -463,10 +466,10 @@
       compositor_task_runner_ = agent_group_scheduler_->CompositorTaskQueue()
                                     ->GetTaskRunnerWithDefaultTaskType();
     }
-    page_scheduler_ = MakeGarbageCollected<NiceMock<MockPageSchedulerImpl>>(
+    page_scheduler_ = std::make_unique<NiceMock<MockPageSchedulerImpl>>(
         scheduler_.get(), *agent_group_scheduler_);
     main_frame_scheduler_ =
-        CreateFrameScheduler(page_scheduler_, nullptr,
+        CreateFrameScheduler(page_scheduler_.get(), nullptr,
                              /*is_in_embedded_frame_tree=*/false,
                              FrameScheduler::FrameType::kMainFrame);
 
@@ -530,9 +533,7 @@
   void TearDown() override {
     widget_scheduler_.reset();
     main_frame_scheduler_.reset();
-    if (page_scheduler_)
-      page_scheduler_->Shutdown();
-    page_scheduler_ = nullptr;
+    page_scheduler_.reset();
     agent_group_scheduler_ = nullptr;
     scheduler_->Shutdown();
     base::RunLoop().RunUntilIdle();
@@ -1009,7 +1010,7 @@
 
   std::unique_ptr<MainThreadSchedulerImplForTest> scheduler_;
   Persistent<AgentGroupSchedulerImpl> agent_group_scheduler_;
-  Persistent<MockPageSchedulerImpl> page_scheduler_;
+  std::unique_ptr<MockPageSchedulerImpl> page_scheduler_;
   std::unique_ptr<FrameSchedulerImpl> main_frame_scheduler_;
   scoped_refptr<WidgetScheduler> widget_scheduler_;
 
@@ -2567,8 +2568,7 @@
 
 TEST_F(MainThreadSchedulerImplTest, ShutdownPreventsPostingOfNewTasks) {
   main_frame_scheduler_.reset();
-  page_scheduler_->Shutdown();
-  page_scheduler_ = nullptr;
+  page_scheduler_.reset();
   scheduler_->Shutdown();
   Vector<String> run_order;
   PostTestTasks(&run_order, "D1 C1");
@@ -3182,18 +3182,18 @@
   // (by posting tasks, creating child schedulers, etc) and converts it into a
   // traced value. This test checks that no internal checks fire during this.
 
-  PageSchedulerImpl* page_scheduler1 =
+  std::unique_ptr<PageSchedulerImpl> page_scheduler1 =
       CreatePageScheduler(nullptr, scheduler_.get(), *agent_group_scheduler_);
-  scheduler_->AddPageScheduler(page_scheduler1);
+  scheduler_->AddPageScheduler(page_scheduler1.get());
 
   std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
-      CreateFrameScheduler(page_scheduler1, nullptr,
+      CreateFrameScheduler(page_scheduler1.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kSubframe);
 
-  PageSchedulerImpl* page_scheduler2 =
+  std::unique_ptr<PageSchedulerImpl> page_scheduler2 =
       CreatePageScheduler(nullptr, scheduler_.get(), *agent_group_scheduler_);
-  scheduler_->AddPageScheduler(page_scheduler2);
+  scheduler_->AddPageScheduler(page_scheduler2.get());
 
   std::unique_ptr<CPUTimeBudgetPool> time_budget_pool =
       scheduler_->CreateCPUTimeBudgetPoolForTesting("test");
@@ -3223,7 +3223,7 @@
 
   // Store documents inside the back-forward cache. IPCs are only tracked IFF
   // all pages are in the back-forward cache.
-  PageSchedulerImpl* page_scheduler = page_scheduler_;
+  PageSchedulerImpl* page_scheduler = page_scheduler_.get();
   page_scheduler->SetPageBackForwardCached(true);
   base::RunLoop().RunUntilIdle();
   {
@@ -3234,9 +3234,9 @@
 
   // Adding a new page scheduler results in IPCs not being logged, as this
   // page scheduler is not in the cache.
-  PageSchedulerImpl* page_scheduler1 =
+  std::unique_ptr<PageSchedulerImpl> page_scheduler1 =
       CreatePageScheduler(nullptr, scheduler_.get(), *agent_group_scheduler_);
-  scheduler_->AddPageScheduler(page_scheduler1);
+  scheduler_->AddPageScheduler(page_scheduler1.get());
   base::RunLoop().RunUntilIdle();
   {
     base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash(2);
@@ -3246,8 +3246,7 @@
 
   // Removing an un-cached page scheduler results in IPCs being logged, as all
   // page schedulers are now in the cache.
-  page_scheduler1->Shutdown();
-  page_scheduler1 = nullptr;
+  page_scheduler1.reset();
   base::RunLoop().RunUntilIdle();
   {
     base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash(3);
@@ -3295,7 +3294,7 @@
       .WillRepeatedly(testing::Return(true));
   base::RunLoop().RunUntilIdle();
 
-  Mock::VerifyAndClearExpectations(page_scheduler_);
+  Mock::VerifyAndClearExpectations(page_scheduler_.get());
 
   scheduler_->OnPendingTasksChanged(false);
   EXPECT_CALL(*page_scheduler_, RequestBeginMainFrameNotExpected(false))
@@ -3303,7 +3302,7 @@
       .WillRepeatedly(testing::Return(true));
   base::RunLoop().RunUntilIdle();
 
-  Mock::VerifyAndClearExpectations(page_scheduler_);
+  Mock::VerifyAndClearExpectations(page_scheduler_.get());
 }
 
 TEST_F(MainThreadSchedulerImplTest,
@@ -3316,7 +3315,7 @@
       .WillRepeatedly(testing::Return(true));
   base::RunLoop().RunUntilIdle();
 
-  Mock::VerifyAndClearExpectations(page_scheduler_);
+  Mock::VerifyAndClearExpectations(page_scheduler_.get());
 }
 
 #if BUILDFLAG(IS_ANDROID)
@@ -3338,12 +3337,13 @@
 
 TEST_F(MainThreadSchedulerImplTest, FreezesCompositorQueueWhenAllPagesFrozen) {
   main_frame_scheduler_.reset();
-  page_scheduler_->Shutdown();
-  page_scheduler_ = nullptr;
+  page_scheduler_.reset();
 
-  PageScheduler* sched_1 = agent_group_scheduler_->CreatePageScheduler(nullptr);
+  std::unique_ptr<PageScheduler> sched_1 =
+      agent_group_scheduler_->CreatePageScheduler(nullptr);
   sched_1->SetPageVisible(false);
-  PageScheduler* sched_2 = agent_group_scheduler_->CreatePageScheduler(nullptr);
+  std::unique_ptr<PageScheduler> sched_2 =
+      agent_group_scheduler_->CreatePageScheduler(nullptr);
   sched_2->SetPageVisible(false);
 
   Vector<String> run_order;
@@ -3361,23 +3361,21 @@
   EXPECT_THAT(run_order, testing::ElementsAre("D2"));
 
   run_order.clear();
-  PageScheduler* sched_3 = agent_group_scheduler_->CreatePageScheduler(nullptr);
+  std::unique_ptr<PageScheduler> sched_3 =
+      agent_group_scheduler_->CreatePageScheduler(nullptr);
   sched_3->SetPageVisible(false);
   base::RunLoop().RunUntilIdle();
   EXPECT_THAT(run_order, testing::ElementsAre("C2"));
 
   run_order.clear();
   PostTestTasks(&run_order, "D3 C3");
-  sched_3->Shutdown();
-  sched_3 = nullptr;
+  sched_3.reset();
   base::RunLoop().RunUntilIdle();
   EXPECT_THAT(run_order, testing::ElementsAre("D3"));
 
   run_order.clear();
-  sched_1->Shutdown();
-  sched_1 = nullptr;
-  sched_2->Shutdown();
-  sched_2 = nullptr;
+  sched_1.reset();
+  sched_2.reset();
   base::RunLoop().RunUntilIdle();
   EXPECT_THAT(run_order, testing::ElementsAre("C3"));
 }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
index 57c12df..dcd459ec 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -192,16 +192,12 @@
       &PageSchedulerImpl::DoFreezePage, base::Unretained(this)));
 }
 
-PageSchedulerImpl::~PageSchedulerImpl() = default;
-
-void PageSchedulerImpl::Shutdown() {
-  weak_factory_.InvalidateWeakPtrs();
+PageSchedulerImpl::~PageSchedulerImpl() {
   // TODO(alexclarke): Find out why we can't rely on the web view outliving the
   // frame.
   for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
     frame_scheduler->DetachFromPageScheduler();
   }
-  frame_schedulers_.clear();
   main_thread_scheduler_->RemovePageScheduler(this);
 }
 
@@ -357,11 +353,6 @@
   is_main_frame_local_ = is_local;
 }
 
-void PageSchedulerImpl::Trace(Visitor* visitor) const {
-  PageScheduler::Trace(visitor);
-  visitor->Trace(agent_group_scheduler_);
-}
-
 void PageSchedulerImpl::RegisterFrameSchedulerImpl(
     FrameSchedulerImpl* frame_scheduler) {
   base::LazyNow lazy_now(main_thread_scheduler_->GetTickClock());
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
index c9686b8..d6d3fa67 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
@@ -59,7 +59,6 @@
   ~PageSchedulerImpl() override;
 
   // PageScheduler implementation:
-  void Shutdown() override;
   void OnTitleOrFaviconUpdated() override;
   void SetPageVisible(bool page_visible) override;
   void SetPageFrozen(bool) override;
@@ -72,8 +71,6 @@
   bool IsInBackForwardCache() const override {
     return is_stored_in_back_forward_cache_;
   }
-  void Trace(Visitor* visitor) const override;
-
   bool has_ipc_detection_enabled() { return has_ipc_detection_enabled_; }
 
   std::unique_ptr<FrameScheduler> CreateFrameScheduler(
@@ -269,7 +266,7 @@
   TraceableVariableController tracing_controller_;
   HashSet<FrameSchedulerImpl*> frame_schedulers_;
   MainThreadSchedulerImpl* main_thread_scheduler_;
-  Member<AgentGroupSchedulerImpl> agent_group_scheduler_;
+  Persistent<AgentGroupSchedulerImpl> agent_group_scheduler_;
 
   PageVisibilityState page_visibility_;
   base::TimeTicks page_visibility_changed_time_;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
index 8ab8927d..dc2aab0 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
@@ -47,12 +47,15 @@
 
 // This is a wrapper around MainThreadSchedulerImpl::CreatePageScheduler, that
 // returns the PageScheduler as a PageSchedulerImpl.
-PageSchedulerImpl* CreatePageScheduler(
+std::unique_ptr<PageSchedulerImpl> CreatePageScheduler(
     PageScheduler::Delegate* page_scheduler_delegate,
     MainThreadSchedulerImpl* scheduler,
     AgentGroupScheduler& agent_group_scheduler) {
-  return static_cast<PageSchedulerImpl*>(
-      agent_group_scheduler.CreatePageScheduler(page_scheduler_delegate));
+  std::unique_ptr<PageScheduler> page_scheduler =
+      agent_group_scheduler.CreatePageScheduler(page_scheduler_delegate);
+  std::unique_ptr<PageSchedulerImpl> page_scheduler_impl(
+      static_cast<PageSchedulerImpl*>(page_scheduler.release()));
+  return page_scheduler_impl;
 }
 
 // This is a wrapper around PageSchedulerImpl::CreateFrameScheduler, that
@@ -112,16 +115,14 @@
         CreatePageScheduler(page_scheduler_delegate_.get(), scheduler_.get(),
                             *agent_group_scheduler_);
     frame_scheduler_ =
-        CreateFrameScheduler(page_scheduler_, nullptr,
+        CreateFrameScheduler(page_scheduler_.get(), nullptr,
                              /*is_in_embedded_frame_tree=*/false,
                              FrameScheduler::FrameType::kSubframe);
   }
 
   void TearDown() override {
     frame_scheduler_.reset();
-    if (page_scheduler_)
-      page_scheduler_->Shutdown();
-    page_scheduler_ = nullptr;
+    page_scheduler_.reset();
     agent_group_scheduler_ = nullptr;
     scheduler_->Shutdown();
     scheduler_.reset();
@@ -256,7 +257,7 @@
   scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_;
   std::unique_ptr<MainThreadSchedulerImpl> scheduler_;
   Persistent<AgentGroupScheduler> agent_group_scheduler_;
-  Persistent<PageSchedulerImpl> page_scheduler_;
+  std::unique_ptr<PageSchedulerImpl> page_scheduler_;
   std::unique_ptr<FrameSchedulerImpl> frame_scheduler_;
   std::unique_ptr<MockPageSchedulerDelegate> page_scheduler_delegate_;
 
@@ -284,8 +285,7 @@
       page_scheduler_->CreateFrameScheduler(
           nullptr, /*is_in_embedded_frame_tree=*/false,
           FrameScheduler::FrameType::kSubframe));
-  page_scheduler_->Shutdown();
-  page_scheduler_ = nullptr;
+  page_scheduler_.reset();
 }
 
 namespace {
@@ -368,10 +368,10 @@
 }
 
 TEST_F(PageSchedulerImplTest, RepeatingTimers_OneBackgroundOneForeground) {
-  PageSchedulerImpl* page_scheduler2 =
+  std::unique_ptr<PageSchedulerImpl> page_scheduler2 =
       CreatePageScheduler(nullptr, scheduler_.get(), *agent_group_scheduler_);
   std::unique_ptr<FrameSchedulerImpl> frame_scheduler2 =
-      CreateFrameScheduler(page_scheduler2, nullptr,
+      CreateFrameScheduler(page_scheduler2.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kSubframe);
 
@@ -405,10 +405,10 @@
   // 1st Page is loaded.
   EXPECT_FALSE(page_scheduler_->IsLoading());
 
-  PageSchedulerImpl* page_scheduler2 =
+  std::unique_ptr<PageSchedulerImpl> page_scheduler2 =
       CreatePageScheduler(nullptr, scheduler_.get(), *agent_group_scheduler_);
   std::unique_ptr<FrameSchedulerImpl> frame_scheduler2 =
-      CreateFrameScheduler(page_scheduler2, nullptr,
+      CreateFrameScheduler(page_scheduler2.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kMainFrame);
 
@@ -685,7 +685,7 @@
   vtc->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
 
   std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
-      CreateFrameScheduler(page_scheduler_, nullptr,
+      CreateFrameScheduler(page_scheduler_.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kSubframe);
 
@@ -717,7 +717,7 @@
 TEST_F(PageSchedulerImplTest, DeleteFrameSchedulers_InTask) {
   for (int i = 0; i < 10; i++) {
     FrameSchedulerImpl* frame_scheduler =
-        CreateFrameScheduler(page_scheduler_, nullptr,
+        CreateFrameScheduler(page_scheduler_.get(), nullptr,
                              /*is_in_embedded_frame_tree=*/false,
                              FrameScheduler::FrameType::kSubframe)
             .release();
@@ -729,13 +729,9 @@
   test_task_runner_->FastForwardUntilNoTasksRemain();
 }
 
-TEST_F(PageSchedulerImplTest, ShutdownPageScheduler_InTask) {
+TEST_F(PageSchedulerImplTest, DeletePageScheduler_InTask) {
   ThrottleableTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask(
-      FROM_HERE,
-      WTF::BindOnce(
-          [](PageSchedulerImpl* page_scheduler) { page_scheduler->Shutdown(); },
-          page_scheduler_));
-  page_scheduler_ = nullptr;
+      FROM_HERE, MakeDeletionTask(page_scheduler_.release()));
   test_task_runner_->FastForwardUntilNoTasksRemain();
 }
 
@@ -743,7 +739,7 @@
   page_scheduler_->SetPageVisible(false);
 
   FrameSchedulerImpl* frame_scheduler =
-      CreateFrameScheduler(page_scheduler_, nullptr,
+      CreateFrameScheduler(page_scheduler_.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kSubframe)
           .release();
@@ -798,7 +794,7 @@
   vtc->SetVirtualTimePolicy(VirtualTimePolicy::kDeterministicLoading);
 
   std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
-      CreateFrameScheduler(page_scheduler_, nullptr,
+      CreateFrameScheduler(page_scheduler_.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kSubframe);
 
@@ -865,7 +861,7 @@
   base::TimeTicks time_second_task;
 
   std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
-      CreateFrameScheduler(page_scheduler_, nullptr,
+      CreateFrameScheduler(page_scheduler_.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kSubframe);
 
@@ -900,7 +896,7 @@
   vtc->SetVirtualTimePolicy(VirtualTimePolicy::kDeterministicLoading);
 
   std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
-      CreateFrameScheduler(page_scheduler_, nullptr,
+      CreateFrameScheduler(page_scheduler_.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kSubframe);
 
@@ -949,7 +945,7 @@
   Vector<int> run_order;
 
   std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
-      CreateFrameScheduler(page_scheduler_, nullptr,
+      CreateFrameScheduler(page_scheduler_.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kSubframe);
   VirtualTimeController* vtc = page_scheduler_->GetVirtualTimeController();
@@ -1169,7 +1165,7 @@
   base::TimeTicks start_time = test_task_runner_->NowTicks();
 
   Vector<base::TimeTicks> run_times;
-  frame_scheduler_ = CreateFrameScheduler(page_scheduler_, nullptr,
+  frame_scheduler_ = CreateFrameScheduler(page_scheduler_.get(), nullptr,
                                           /*is_in_embedded_frame_tree=*/false,
                                           FrameScheduler::FrameType::kSubframe);
   page_scheduler_->SetPageVisible(true);
@@ -1222,18 +1218,18 @@
 
 TEST_F(PageSchedulerImplTest, OpenWebSocketExemptsFromBudgetThrottling) {
   InitializeTrialParams();
-  PageSchedulerImpl* page_scheduler =
+  std::unique_ptr<PageSchedulerImpl> page_scheduler =
       CreatePageScheduler(nullptr, scheduler_.get(), *agent_group_scheduler_);
   base::TimeTicks start_time = test_task_runner_->NowTicks();
 
   Vector<base::TimeTicks> run_times;
 
   std::unique_ptr<FrameSchedulerImpl> frame_scheduler1 =
-      CreateFrameScheduler(page_scheduler, nullptr,
+      CreateFrameScheduler(page_scheduler.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kSubframe);
   std::unique_ptr<FrameSchedulerImpl> frame_scheduler2 =
-      CreateFrameScheduler(page_scheduler, nullptr,
+      CreateFrameScheduler(page_scheduler.get(), nullptr,
                            /*is_in_embedded_frame_tree=*/false,
                            FrameScheduler::FrameType::kSubframe);
 
@@ -1365,8 +1361,7 @@
   EXPECT_TRUE(page_scheduler_->IsAudioPlaying());
   page_scheduler_->AudioStateChanged(false);
 
-  page_scheduler_->Shutdown();
-  page_scheduler_ = nullptr;
+  page_scheduler_.reset();
 
   test_task_runner_->FastForwardUntilNoTasksRemain();
 }
diff --git a/third_party/blink/renderer/platform/scheduler/public/agent_group_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/agent_group_scheduler.h
index ddf8708e..f086286 100644
--- a/third_party/blink/renderer/platform/scheduler/public/agent_group_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/agent_group_scheduler.h
@@ -32,7 +32,8 @@
 
   // Creates a new PageScheduler for a given Page. Must be called from the
   // associated WebThread.
-  virtual PageScheduler* CreatePageScheduler(PageScheduler::Delegate*) = 0;
+  virtual std::unique_ptr<PageScheduler> CreatePageScheduler(
+      PageScheduler::Delegate*) = 0;
 
   virtual void BindInterfaceBroker(
       mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
diff --git a/third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h b/third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h
index e3ade41..8cbd533 100644
--- a/third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h
+++ b/third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h
@@ -32,7 +32,7 @@
 // - Tests
 
 PLATFORM_EXPORT std::unique_ptr<FrameScheduler> CreateDummyFrameScheduler();
-PLATFORM_EXPORT PageScheduler* CreateDummyPageScheduler();
+PLATFORM_EXPORT std::unique_ptr<PageScheduler> CreateDummyPageScheduler();
 PLATFORM_EXPORT AgentGroupScheduler* CreateDummyAgentGroupScheduler();
 PLATFORM_EXPORT std::unique_ptr<WebThreadScheduler>
 CreateDummyWebMainThreadScheduler();
diff --git a/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h
index 08ab9f5..1a80b8d 100644
--- a/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h
@@ -7,7 +7,6 @@
 
 #include <memory>
 #include "third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h"
-#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/page_lifecycle_state.h"
@@ -24,7 +23,7 @@
 class WidgetScheduler;
 }  // namespace scheduler
 
-class PLATFORM_EXPORT PageScheduler : public GarbageCollected<PageScheduler> {
+class PLATFORM_EXPORT PageScheduler {
  public:
   class PLATFORM_EXPORT Delegate {
    public:
@@ -89,10 +88,6 @@
 
   // Creates a WebWidgetScheduler implementation.
   virtual scoped_refptr<scheduler::WidgetScheduler> CreateWidgetScheduler() = 0;
-
-  virtual void Shutdown() = 0;
-
-  virtual void Trace(Visitor*) const {}
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/test/fake_agent_group_scheduler_scheduler.h b/third_party/blink/renderer/platform/scheduler/test/fake_agent_group_scheduler_scheduler.h
index 4c642af5..1d923bc6 100644
--- a/third_party/blink/renderer/platform/scheduler/test/fake_agent_group_scheduler_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/test/fake_agent_group_scheduler_scheduler.h
@@ -26,7 +26,8 @@
     return base::ThreadTaskRunnerHandle::Get();
   }
 
-  PageScheduler* CreatePageScheduler(PageScheduler::Delegate*) override {
+  std::unique_ptr<PageScheduler> CreatePageScheduler(
+      PageScheduler::Delegate*) override {
     return nullptr;
   }
 
diff --git a/third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h b/third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h
index ba225e5..f67bd28 100644
--- a/third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h
@@ -125,7 +125,7 @@
     }
 
    private:
-    Persistent<PageScheduler> page_scheduler_;
+    PageScheduler* page_scheduler_ = nullptr;
     bool is_page_visible_ = false;
     bool is_frame_visible_ = false;
     FrameScheduler::FrameType frame_type_ =
@@ -190,7 +190,7 @@
   }
 
  private:
-  Persistent<PageScheduler> page_scheduler_;
+  PageScheduler* page_scheduler_;  // NOT OWNED
 
   bool is_page_visible_;
   bool is_frame_visible_;
diff --git a/third_party/blink/renderer/platform/scheduler/test/fake_page_scheduler.h b/third_party/blink/renderer/platform/scheduler/test/fake_page_scheduler.h
index 613caa9..c6ffaea 100644
--- a/third_party/blink/renderer/platform/scheduler/test/fake_page_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/test/fake_page_scheduler.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_TEST_FAKE_PAGE_SCHEDULER_H_
 
 #include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
-#include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h"
 #include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
 
@@ -38,9 +37,9 @@
       return *this;
     }
 
-    FakePageScheduler* Build() {
-      return MakeGarbageCollected<FakePageScheduler>(is_audio_playing_,
-                                                     is_throttling_exempt_);
+    std::unique_ptr<FakePageScheduler> Build() {
+      return std::make_unique<FakePageScheduler>(is_audio_playing_,
+                                                 is_throttling_exempt_);
     }
 
    private:
@@ -85,17 +84,10 @@
     return nullptr;
   }
 
-  void Shutdown() override {}
-
-  void Trace(Visitor* visitor) const override {
-    PageScheduler::Trace(visitor);
-    visitor->Trace(agent_group_scheduler_);
-  }
-
  private:
   bool is_audio_playing_;
   bool is_throttling_exempt_;
-  Member<AgentGroupScheduler> agent_group_scheduler_;
+  Persistent<AgentGroupScheduler> agent_group_scheduler_;
 };
 
 }  // namespace scheduler
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy_unittest.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy_unittest.cc
index 1052c0d..1a6dd38 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy_unittest.cc
@@ -138,7 +138,7 @@
 
   ~WorkerSchedulerProxyTest() override {
     frame_scheduler_.reset();
-    page_scheduler_->Shutdown();
+    page_scheduler_.reset();
     main_thread_scheduler_->Shutdown();
   }
 
@@ -146,7 +146,7 @@
   base::test::TaskEnvironment task_environment_;
   std::unique_ptr<MainThreadSchedulerImpl> main_thread_scheduler_;
   Persistent<AgentGroupScheduler> agent_group_scheduler_;
-  Persistent<PageScheduler> page_scheduler_;
+  std::unique_ptr<PageScheduler> page_scheduler_;
   std::unique_ptr<FrameScheduler> frame_scheduler_;
 };
 
diff --git a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view_unittest.cc b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view_unittest.cc
index e154694..2f2b6d025 100644
--- a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view_unittest.cc
+++ b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view_unittest.cc
@@ -258,7 +258,7 @@
  protected:
   base::test::TaskEnvironment task_environment_;
   cc::TestTaskGraphRunner test_task_graph_runner_;
-  Persistent<PageScheduler> dummy_page_scheduler_;
+  std::unique_ptr<PageScheduler> dummy_page_scheduler_;
   FakeLayerTreeViewDelegate layer_tree_view_delegate_;
   LayerTreeViewWithFrameSinkTracking layer_tree_view_;
 };
@@ -323,7 +323,7 @@
   base::test::TaskEnvironment task_environment;
 
   cc::TestTaskGraphRunner test_task_graph_runner;
-  auto* page_scheduler = scheduler::CreateDummyPageScheduler();
+  auto page_scheduler = scheduler::CreateDummyPageScheduler();
   // Synchronously callback with null FrameSink.
   StubLayerTreeViewDelegate layer_tree_view_delegate;
   VisibilityTestLayerTreeView layer_tree_view(&layer_tree_view_delegate,
@@ -369,7 +369,8 @@
   base::test::TaskEnvironment task_environment;
 
   cc::TestTaskGraphRunner test_task_graph_runner;
-  PageScheduler* dummy_page_scheduler = scheduler::CreateDummyPageScheduler();
+  std::unique_ptr<PageScheduler> dummy_page_scheduler =
+      scheduler::CreateDummyPageScheduler();
   StubLayerTreeViewDelegate layer_tree_view_delegate;
   LayerTreeView layer_tree_view(&layer_tree_view_delegate,
                                 dummy_page_scheduler->CreateWidgetScheduler());
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 248b6de9..f586aa5 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -273,6 +273,9 @@
 crbug.com/1218540 virtual/shared-storage-fenced-frame-mparch/* [ Pass ]
 crbug.com/1218540 virtual/shared-storage-fenced-frame-shadow-dom/* [ Pass ]
 
+# These tests require flags enabled under the virtual/prerender/ suite.
+crbug.com/1355146 external/wpt/speculation-rules/prerender/referrer-policy-from-rules.html* [ Skip ]
+crbug.com/1355146 virtual/prerender/external/wpt/speculation-rules/prerender/referrer-policy-from-rules.html* [ Pass ]
 # This test is originally added with external/wpt/speculation-rules/prerender/* skipped and broken.
 # TODO(https://crbug.com/1356449): Fix the test.
 crbug.com/1356449 external/wpt/speculation-rules/prerender/credentialed-prerender-opt-in.html [ Skip ]
@@ -3374,14 +3377,14 @@
 crbug.com/626703 [ Win ] virtual/partitioned-cookies/http/tests/inspector-protocol/network/disabled-cache-navigation.js [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
-crbug.com/626703 [ Linux ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Failure Crash ]
-crbug.com/626703 [ Mac11 ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Failure Crash ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Failure Crash ]
-crbug.com/626703 [ Mac12 ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Failure Crash ]
-crbug.com/626703 [ Mac12-arm64 ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Failure Crash ]
-crbug.com/626703 [ Win11 ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Failure Crash ]
+crbug.com/626703 [ Linux ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Crash Failure ]
+crbug.com/626703 [ Mac11 ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Crash Failure ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Crash Failure ]
+crbug.com/626703 [ Mac12 ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Crash Failure ]
+crbug.com/626703 [ Mac12-arm64 ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Crash Failure ]
+crbug.com/626703 [ Win11 ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Crash Failure ]
 crbug.com/626703 [ Mac10.15 ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Crash ]
-crbug.com/626703 [ Win10.20h2 ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Timeout Crash ]
+crbug.com/626703 [ Win10.20h2 ] external/wpt/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html [ Crash Timeout ]
 crbug.com/626703 [ Mac10.15 ] virtual/pending-beacon/external/wpt/pending-beacon/pending_post_beacon-cors.tentative.https.window.html [ Timeout ]
 crbug.com/626703 [ Mac11 ] virtual/pending-beacon/external/wpt/pending-beacon/pending_beacon-sendonhidden.tentative.https.window.html [ Timeout ]
 crbug.com/626703 [ Mac11 ] virtual/pending-beacon/external/wpt/pending-beacon/pending_post_beacon-cors.tentative.https.window.html [ Timeout ]
@@ -7255,6 +7258,8 @@
 crbug.com/1380069 [ Linux ] virtual/pending-beacon/external/wpt/pending-beacon/pending_beacon-sendonhidden.tentative.https.window.html [ Timeout ]
 crbug.com/1380069 [ Mac12 ] virtual/pending-beacon/external/wpt/pending-beacon/pending_post_beacon-cors.tentative.https.window.html [ Timeout ]
 crbug.com/1380078 [ Mac11 ] external/wpt/resource-timing/nested-context-navigations-embed.html [ Failure ]
+crbug.com/1380575 [ Linux ] virtual/scalefactor200/fast/forms/file/file-appearance-no-default-width.html [ Failure ]
+crbug.com/1376986 [ Mac11 ] virtual/compute-pressure/external/wpt/compute-pressure/compute_pressure_duplicate_updates.tentative.https.window.html [ Timeout ]
 
 # Sheriff 2022-10-07
 crbug.com/1372556 [ Linux ] external/wpt/css/css-text/text-transform/text-transform-capitalize-* [ Failure Pass ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index a53fe00..e686b5b4 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -902,7 +902,10 @@
     "bases": [ "external/wpt/speculation-rules/prerender",
                "wpt_internal/prerender",
                "http/tests/inspector-protocol/prerender"],
-    "args": [ "--enable-features=SameSiteCrossOriginForSpeculationRulesPrerender" ]
+    "args": [
+      "--enable-blink-features=SpeculationRulesReferrerPolicyKey",
+      "--enable-features=SameSiteCrossOriginForSpeculationRulesPrerender"
+    ]
   },
   {
     "prefix": "no-different-origin-dialogs",
@@ -1281,7 +1284,7 @@
       "http/tests/inspector-protocol/prefetch"
     ],
     "args": [
-      "--enable-blink-features=SpeculationRulesPrefetchProxy,SpeculationRulesFetchFromHeader",
+      "--enable-blink-features=SpeculationRulesPrefetchProxy,SpeculationRulesFetchFromHeader,SpeculationRulesReferrerPolicyKey",
       "--enable-features=SpeculationRulesPrefetchProxy,PrefetchUseContentRefactor",
       "--bypass-prefetch-proxy-for-host=not-web-platform.test",
       "--isolated-prerender-allow-all-domains"
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 a9eb8780..ea3131b 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
@@ -619768,7 +619768,7 @@
      },
      "back": {
       "back.py": [
-       "86ad2278ab3970c892760edc2c764b4e55cea91f",
+       "21e8498ccd57da9b101055f39fedab02ef2f2fcb",
        [
         null,
         {}
@@ -619883,7 +619883,7 @@
        ]
       ],
       "click.py": [
-       "8abcfe789d4e9b721f824a37b3f6873783a6c420",
+       "bd4e2c6801a737a000f8bb8de032a8480121c17e",
        [
         null,
         {}
@@ -619985,7 +619985,7 @@
        ]
       ],
       "send_keys.py": [
-       "3cb1c7469d8a81855716d3109ee039e80b5ef84f",
+       "5f8d3f01d6e3ea18484c31209f63bcf8b3c3e44c",
        [
         null,
         {}
@@ -620010,7 +620010,7 @@
        ]
       ],
       "execute_async.py": [
-       "89533057dd3ae3599486722e5209c60cae2f5c5c",
+       "697df72bb9c60b5fc7d51f0eafc0eb2155517f7d",
        [
         null,
         {}
@@ -620063,7 +620063,7 @@
        ]
       ],
       "execute.py": [
-       "a12080ca2459758459583e3fbeb3ce32435262a9",
+       "d1f1f137edf6cfd5f9ed376f72291b0ff8b80709",
        [
         null,
         {}
@@ -620175,7 +620175,7 @@
      },
      "forward": {
       "forward.py": [
-       "97a935c8cd83b33c75eef9337673c1704d038450",
+       "61c66e201b9e9898cec78c86c37fbf894b48c457",
        [
         null,
         {}
@@ -620220,7 +620220,7 @@
      },
      "get_active_element": {
       "get.py": [
-       "145440082f713706417a0c4aa0eabd6433912e10",
+       "2b79ebd58485156573d5369372bfe6a8a1587a9f",
        [
         null,
         {}
@@ -620247,7 +620247,7 @@
      },
      "get_computed_label": {
       "get.py": [
-       "12aa82eb4e460daf2248bca2a0072e7cdde43574",
+       "901b9809ca801c10358ed1b3c937b3729b7eb937",
        [
         null,
         {}
@@ -620256,7 +620256,7 @@
      },
      "get_computed_role": {
       "get.py": [
-       "d242f7383223b38d5059f683951ee626a9352a72",
+       "84e7ab249f8c4f9b29e24b7213bad77b2ee841fe",
        [
         null,
         {}
@@ -620297,7 +620297,7 @@
      },
      "get_element_attribute": {
       "get.py": [
-       "5119949fedb3e1663f7a4610b11ef1a71d562278",
+       "1d003073be7acfe2ecc5bf1309809749c246cb6e",
        [
         null,
         {}
@@ -620315,7 +620315,7 @@
      },
      "get_element_css_value": {
       "get.py": [
-       "5ab0b3b58dc7b332beeeb989cb2f65c0f9db8e53",
+       "3d4a31018b5792d3dda35262042febaee616751c",
        [
         null,
         {}
@@ -620333,7 +620333,7 @@
      },
      "get_element_property": {
       "get.py": [
-       "46755ffc9b138a00f525a68e89062bc04ed8e28e",
+       "9223f1df684292634f4bef1273cefa587be970a9",
        [
         null,
         {}
@@ -620351,7 +620351,7 @@
      },
      "get_element_rect": {
       "get.py": [
-       "a12b8d4e4f6b13c938b6d145d7667e9965e77802",
+       "97dc7a9f930a9491e19f34d85e140f3df601ca2b",
        [
         null,
         {}
@@ -620369,7 +620369,7 @@
      },
      "get_element_shadow_root": {
       "get.py": [
-       "3cf3c6521daed9e5053202af3d2144c8b1bb7fde",
+       "70168bb87827a5d3094b9660d0785037db80bb8d",
        [
         null,
         {}
@@ -620387,7 +620387,7 @@
      },
      "get_element_tag_name": {
       "get.py": [
-       "b163a58b71a7be3a264021e0c978d76986e1b706",
+       "1e4d5d994e6dcec361dbfc6c509683684a67eb1f",
        [
         null,
         {}
@@ -620405,7 +620405,7 @@
      },
      "get_element_text": {
       "get.py": [
-       "d2a1f180eeea436441940cd2dee0172bd1a5d831",
+       "6f03da9300e4db4f7ea2ff6975d75dcfaaf8e304",
        [
         null,
         {}
@@ -620556,7 +620556,7 @@
      },
      "is_element_enabled": {
       "enabled.py": [
-       "5e8548ba827060ed68e21366293c574ae4e8ecbd",
+       "fa472a69b7b7e618469a652987561dcb0bb25cba",
        [
         null,
         {}
@@ -620574,7 +620574,7 @@
      },
      "is_element_selected": {
       "selected.py": [
-       "4dbc7974d22501f394e0d5682d5772507a70d1ba",
+       "79a49277bb0745a2c8588a9f3c67275f50af120e",
        [
         null,
         {}
@@ -620657,7 +620657,7 @@
        ]
       ],
       "navigate.py": [
-       "74ac33c49656e35950312d003abddb6eaf7e4db2",
+       "a367fc105e126aba603a2c426f2bfe823ccad950",
        [
         null,
         {}
@@ -620949,7 +620949,7 @@
      },
      "refresh": {
       "refresh.py": [
-       "33595d0ff71ad6b493e83dd0a998f38e114007bf",
+       "b3647130c77ec6c0c84514ff9f599b9a872303e7",
        [
         null,
         {}
@@ -621053,7 +621053,7 @@
        ]
       ],
       "switch_webelement.py": [
-       "465efb76e01d42819f502cf7c5d3e2a764e31507",
+       "5b571862054f425425bc4a5d76ab39cf02d00a11",
        [
         null,
         {}
@@ -621094,7 +621094,7 @@
        ]
       ],
       "screenshot.py": [
-       "79ffa15b7461ac6e2ae7087090fafc0ae91cd4fe",
+       "4a248b158bb48da6ca6e347af88c3a33d9dcfdfc",
        [
         null,
         {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-computed-color-mix-function-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-computed-color-mix-function-expected.txt
index 07700e6e..a6c12359 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-computed-color-mix-function-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-computed-color-mix-function-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 474 tests; 25 PASS, 449 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 474 tests; 225 PASS, 249 FAIL, 0 TIMEOUT, 0 NOTRUN.
 FAIL Property color value 'color-mix(in hsl, hsl(120deg 10% 20%), hsl(30deg 30% 40%))' assert_equals: expected "rgb(84, 92, 61)" but got "rgb(46, 56, 46)"
 FAIL Property color value 'color-mix(in hsl, hsl(120deg 10% 20%) 25%, hsl(30deg 30% 40%))' assert_equals: expected "rgb(112, 106, 67)" but got "rgb(46, 56, 46)"
 FAIL Property color value 'color-mix(in hsl, 25% hsl(120deg 10% 20%), hsl(30deg 30% 40%))' assert_equals: expected "rgb(112, 106, 67)" but got "rgb(46, 56, 46)"
@@ -144,186 +144,186 @@
 FAIL Property color value 'color-mix(in hwb, oklab(0 0.365 -0.16) 100%, rgb(0, 0, 0) 0%)' assert_equals: expected "rgb(0, 0, 0)" but got "oklab(0 0.365 -0.16)"
 FAIL Property color value 'color-mix(in hwb, oklch(100 0.399 336.3) 100%, rgb(0, 0, 0) 0%)' assert_equals: expected "rgb(255, 255, 255)" but got "oklch(100 0.399 336.3)"
 FAIL Property color value 'color-mix(in hwb, oklch(0 0.399 336.3) 100%, rgb(0, 0, 0) 0%)' assert_equals: expected "rgb(0, 0, 0)" but got "oklch(0 0.399 336.3)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg), lch(50 60 70deg))' assert_equals: expected "lch(30 40 50)" but got "color(srgb 30 40 50)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg) 25%, lch(50 60 70deg))' assert_equals: expected "lch(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in lch, 25% lch(10 20 30deg), lch(50 60 70deg))' assert_equals: expected "lch(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg), 25% lch(50 60 70deg))' assert_equals: expected "lch(20 30 40)" but got "color(srgb 20 30 40)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg), lch(50 60 70deg) 25%)' assert_equals: expected "lch(20 30 40)" but got "color(srgb 20 30 40)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg) 25%, lch(50 60 70deg) 75%)' assert_equals: expected "lch(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg) 30%, lch(50 60 70deg) 90%)' assert_equals: expected "lch(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg) 12.5%, lch(50 60 70deg) 37.5%)' assert_equals: expected "lch(40 50 60 / 0.5)" but got "color(srgb 40 50 60 / 0.5)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg) 0%, lch(50 60 70deg))' assert_equals: expected "lch(50 60 70)" but got "color(srgb 50 60 70)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / .4), lch(50 60 70deg / .8))' assert_equals: expected "lch(36.666664 46.666664 50 / 0.6)" but got "color(srgb 36.6667 46.6667 83.3333 / 0.6)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / .4) 25%, lch(50 60 70deg / .8))' assert_equals: expected "lch(44.285713 54.285717 60 / 0.7)" but got "color(srgb 44.2857 54.2857 85.7143 / 0.7)"
-FAIL Property color value 'color-mix(in lch, 25% lch(10 20 30deg / .4), lch(50 60 70deg / .8))' assert_equals: expected "lch(44.285713 54.285717 60 / 0.7)" but got "color(srgb 44.2857 54.2857 85.7143 / 0.7)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / .4), 25% lch(50 60 70deg / .8))' assert_equals: expected "lch(26 36 40 / 0.5)" but got "color(srgb 26 36 80 / 0.5)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / .4), lch(50 60 70deg / .8) 25%)' assert_equals: expected "lch(26 36 40 / 0.5)" but got "color(srgb 26 36 80 / 0.5)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / .4) 25%, lch(50 60 70deg / .8) 75%)' assert_equals: expected "lch(44.285713 54.285717 60 / 0.7)" but got "color(srgb 44.2857 54.2857 85.7143 / 0.7)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / .4) 30%, lch(50 60 70deg / .8) 90%)' assert_equals: expected "lch(44.285713 54.285717 60 / 0.7)" but got "color(srgb 44.2857 54.2857 85.7143 / 0.7)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / .4) 12.5%, lch(50 60 70deg / .8) 37.5%)' assert_equals: expected "lch(44.285713 54.285717 60 / 0.35)" but got "color(srgb 44.2857 54.2857 85.7143 / 0.35)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / .4) 0%, lch(50 60 70deg / .8))' assert_equals: expected "lch(50 60 70 / 0.8)" but got "color(srgb 50 60 87.5 / 0.8)"
-FAIL Property color value 'color-mix(in lch, lch(100 0 40deg), lch(100 0 60deg))' assert_equals: expected "lch(100 0 50)" but got "color(srgb 100 0 50)"
-FAIL Property color value 'color-mix(in lch, lch(100 0 60deg), lch(100 0 40deg))' assert_equals: expected "lch(100 0 50)" but got "color(srgb 100 0 50)"
-FAIL Property color value 'color-mix(in lch, lch(100 0 50deg), lch(100 0 330deg))' assert_equals: expected "lch(100 0 10)" but got "color(srgb 100 0 10)"
-FAIL Property color value 'color-mix(in lch, lch(100 0 330deg), lch(100 0 50deg))' assert_equals: expected "lch(100 0 10)" but got "color(srgb 100 0 10)"
-FAIL Property color value 'color-mix(in lch, lch(100 0 20deg), lch(100 0 320deg))' assert_equals: expected "lch(100 0 350)" but got "color(srgb 100 0 350)"
-FAIL Property color value 'color-mix(in lch, lch(100 0 320deg), lch(100 0 20deg))' assert_equals: expected "lch(100 0 350)" but got "color(srgb 100 0 350)"
-FAIL Property color value 'color-mix(in lch shorter hue, lch(100 0 40deg), lch(100 0 60deg))' assert_equals: expected "lch(100 0 50)" but got "color(srgb 100 0 50)"
-FAIL Property color value 'color-mix(in lch shorter hue, lch(100 0 60deg), lch(100 0 40deg))' assert_equals: expected "lch(100 0 50)" but got "color(srgb 100 0 50)"
-FAIL Property color value 'color-mix(in lch shorter hue, lch(100 0 50deg), lch(100 0 330deg))' assert_equals: expected "lch(100 0 10)" but got "color(srgb 100 0 10)"
-FAIL Property color value 'color-mix(in lch shorter hue, lch(100 0 330deg), lch(100 0 50deg))' assert_equals: expected "lch(100 0 10)" but got "color(srgb 100 0 10)"
-FAIL Property color value 'color-mix(in lch shorter hue, lch(100 0 20deg), lch(100 0 320deg))' assert_equals: expected "lch(100 0 350)" but got "color(srgb 100 0 350)"
-FAIL Property color value 'color-mix(in lch shorter hue, lch(100 0 320deg), lch(100 0 20deg))' assert_equals: expected "lch(100 0 350)" but got "color(srgb 100 0 350)"
-FAIL Property color value 'color-mix(in lch longer hue, lch(100 0 40deg), lch(100 0 60deg))' assert_equals: expected "lch(100 0 230)" but got "color(srgb 100 0 230)"
-FAIL Property color value 'color-mix(in lch longer hue, lch(100 0 60deg), lch(100 0 40deg))' assert_equals: expected "lch(100 0 230)" but got "color(srgb 100 0 230)"
-FAIL Property color value 'color-mix(in lch longer hue, lch(100 0 50deg), lch(100 0 330deg))' assert_equals: expected "lch(100 0 190)" but got "color(srgb 100 0 190)"
-FAIL Property color value 'color-mix(in lch longer hue, lch(100 0 330deg), lch(100 0 50deg))' assert_equals: expected "lch(100 0 190)" but got "color(srgb 100 0 190)"
-FAIL Property color value 'color-mix(in lch longer hue, lch(100 0 20deg), lch(100 0 320deg))' assert_equals: expected "lch(100 0 170)" but got "color(srgb 100 0 170)"
-FAIL Property color value 'color-mix(in lch longer hue, lch(100 0 320deg), lch(100 0 20deg))' assert_equals: expected "lch(100 0 170)" but got "color(srgb 100 0 170)"
-FAIL Property color value 'color-mix(in lch increasing hue, lch(100 0 40deg), lch(100 0 60deg))' assert_equals: expected "lch(100 0 50)" but got "color(srgb 100 0 230)"
-FAIL Property color value 'color-mix(in lch increasing hue, lch(100 0 60deg), lch(100 0 40deg))' assert_equals: expected "lch(100 0 230)" but got "color(srgb 100 0 50)"
-FAIL Property color value 'color-mix(in lch increasing hue, lch(100 0 50deg), lch(100 0 330deg))' assert_equals: expected "lch(100 0 190)" but got "color(srgb 100 0 10)"
-FAIL Property color value 'color-mix(in lch increasing hue, lch(100 0 330deg), lch(100 0 50deg))' assert_equals: expected "lch(100 0 10)" but got "color(srgb 100 0 190)"
-FAIL Property color value 'color-mix(in lch increasing hue, lch(100 0 20deg), lch(100 0 320deg))' assert_equals: expected "lch(100 0 170)" but got "color(srgb 100 0 350)"
-FAIL Property color value 'color-mix(in lch increasing hue, lch(100 0 320deg), lch(100 0 20deg))' assert_equals: expected "lch(100 0 350)" but got "color(srgb 100 0 170)"
-FAIL Property color value 'color-mix(in lch decreasing hue, lch(100 0 40deg), lch(100 0 60deg))' assert_equals: expected "lch(100 0 230)" but got "color(srgb 100 0 50)"
-FAIL Property color value 'color-mix(in lch decreasing hue, lch(100 0 60deg), lch(100 0 40deg))' assert_equals: expected "lch(100 0 50)" but got "color(srgb 100 0 230)"
-FAIL Property color value 'color-mix(in lch decreasing hue, lch(100 0 50deg), lch(100 0 330deg))' assert_equals: expected "lch(100 0 10)" but got "color(srgb 100 0 190)"
-FAIL Property color value 'color-mix(in lch decreasing hue, lch(100 0 330deg), lch(100 0 50deg))' assert_equals: expected "lch(100 0 190)" but got "color(srgb 100 0 10)"
-FAIL Property color value 'color-mix(in lch decreasing hue, lch(100 0 20deg), lch(100 0 320deg))' assert_equals: expected "lch(100 0 350)" but got "color(srgb 100 0 170)"
-FAIL Property color value 'color-mix(in lch decreasing hue, lch(100 0 320deg), lch(100 0 20deg))' assert_equals: expected "lch(100 0 170)" but got "color(srgb 100 0 350)"
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg), lch(50 60 70deg))'
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg) 25%, lch(50 60 70deg))'
+PASS Property color value 'color-mix(in lch, 25% lch(10 20 30deg), lch(50 60 70deg))'
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg), 25% lch(50 60 70deg))'
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg), lch(50 60 70deg) 25%)'
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg) 25%, lch(50 60 70deg) 75%)'
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg) 30%, lch(50 60 70deg) 90%)'
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg) 12.5%, lch(50 60 70deg) 37.5%)'
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg) 0%, lch(50 60 70deg))'
+FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / .4), lch(50 60 70deg / .8))' assert_equals: expected "lch(36.666664 46.666664 50 / 0.6)" but got "lch(36.6667 46.6667 50 / 0.6)"
+FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / .4) 25%, lch(50 60 70deg / .8))' assert_equals: expected "lch(44.285713 54.285717 60 / 0.7)" but got "lch(44.2857 54.2857 60 / 0.7)"
+FAIL Property color value 'color-mix(in lch, 25% lch(10 20 30deg / .4), lch(50 60 70deg / .8))' assert_equals: expected "lch(44.285713 54.285717 60 / 0.7)" but got "lch(44.2857 54.2857 60 / 0.7)"
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg / .4), 25% lch(50 60 70deg / .8))'
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg / .4), lch(50 60 70deg / .8) 25%)'
+FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / .4) 25%, lch(50 60 70deg / .8) 75%)' assert_equals: expected "lch(44.285713 54.285717 60 / 0.7)" but got "lch(44.2857 54.2857 60 / 0.7)"
+FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / .4) 30%, lch(50 60 70deg / .8) 90%)' assert_equals: expected "lch(44.285713 54.285717 60 / 0.7)" but got "lch(44.2857 54.2857 60 / 0.7)"
+FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / .4) 12.5%, lch(50 60 70deg / .8) 37.5%)' assert_equals: expected "lch(44.285713 54.285717 60 / 0.35)" but got "lch(44.2857 54.2857 60 / 0.35)"
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg / .4) 0%, lch(50 60 70deg / .8))'
+PASS Property color value 'color-mix(in lch, lch(100 0 40deg), lch(100 0 60deg))'
+PASS Property color value 'color-mix(in lch, lch(100 0 60deg), lch(100 0 40deg))'
+PASS Property color value 'color-mix(in lch, lch(100 0 50deg), lch(100 0 330deg))'
+PASS Property color value 'color-mix(in lch, lch(100 0 330deg), lch(100 0 50deg))'
+PASS Property color value 'color-mix(in lch, lch(100 0 20deg), lch(100 0 320deg))'
+PASS Property color value 'color-mix(in lch, lch(100 0 320deg), lch(100 0 20deg))'
+PASS Property color value 'color-mix(in lch shorter hue, lch(100 0 40deg), lch(100 0 60deg))'
+PASS Property color value 'color-mix(in lch shorter hue, lch(100 0 60deg), lch(100 0 40deg))'
+PASS Property color value 'color-mix(in lch shorter hue, lch(100 0 50deg), lch(100 0 330deg))'
+PASS Property color value 'color-mix(in lch shorter hue, lch(100 0 330deg), lch(100 0 50deg))'
+PASS Property color value 'color-mix(in lch shorter hue, lch(100 0 20deg), lch(100 0 320deg))'
+PASS Property color value 'color-mix(in lch shorter hue, lch(100 0 320deg), lch(100 0 20deg))'
+PASS Property color value 'color-mix(in lch longer hue, lch(100 0 40deg), lch(100 0 60deg))'
+PASS Property color value 'color-mix(in lch longer hue, lch(100 0 60deg), lch(100 0 40deg))'
+PASS Property color value 'color-mix(in lch longer hue, lch(100 0 50deg), lch(100 0 330deg))'
+PASS Property color value 'color-mix(in lch longer hue, lch(100 0 330deg), lch(100 0 50deg))'
+PASS Property color value 'color-mix(in lch longer hue, lch(100 0 20deg), lch(100 0 320deg))'
+PASS Property color value 'color-mix(in lch longer hue, lch(100 0 320deg), lch(100 0 20deg))'
+FAIL Property color value 'color-mix(in lch increasing hue, lch(100 0 40deg), lch(100 0 60deg))' assert_equals: expected "lch(100 0 50)" but got "lch(100 0 230)"
+FAIL Property color value 'color-mix(in lch increasing hue, lch(100 0 60deg), lch(100 0 40deg))' assert_equals: expected "lch(100 0 230)" but got "lch(100 0 50)"
+FAIL Property color value 'color-mix(in lch increasing hue, lch(100 0 50deg), lch(100 0 330deg))' assert_equals: expected "lch(100 0 190)" but got "lch(100 0 10)"
+FAIL Property color value 'color-mix(in lch increasing hue, lch(100 0 330deg), lch(100 0 50deg))' assert_equals: expected "lch(100 0 10)" but got "lch(100 0 190)"
+FAIL Property color value 'color-mix(in lch increasing hue, lch(100 0 20deg), lch(100 0 320deg))' assert_equals: expected "lch(100 0 170)" but got "lch(100 0 350)"
+FAIL Property color value 'color-mix(in lch increasing hue, lch(100 0 320deg), lch(100 0 20deg))' assert_equals: expected "lch(100 0 350)" but got "lch(100 0 170)"
+FAIL Property color value 'color-mix(in lch decreasing hue, lch(100 0 40deg), lch(100 0 60deg))' assert_equals: expected "lch(100 0 230)" but got "lch(100 0 50)"
+FAIL Property color value 'color-mix(in lch decreasing hue, lch(100 0 60deg), lch(100 0 40deg))' assert_equals: expected "lch(100 0 50)" but got "lch(100 0 230)"
+FAIL Property color value 'color-mix(in lch decreasing hue, lch(100 0 50deg), lch(100 0 330deg))' assert_equals: expected "lch(100 0 10)" but got "lch(100 0 190)"
+FAIL Property color value 'color-mix(in lch decreasing hue, lch(100 0 330deg), lch(100 0 50deg))' assert_equals: expected "lch(100 0 190)" but got "lch(100 0 10)"
+FAIL Property color value 'color-mix(in lch decreasing hue, lch(100 0 20deg), lch(100 0 320deg))' assert_equals: expected "lch(100 0 350)" but got "lch(100 0 170)"
+FAIL Property color value 'color-mix(in lch decreasing hue, lch(100 0 320deg), lch(100 0 20deg))' assert_equals: expected "lch(100 0 170)" but got "lch(100 0 350)"
 FAIL Property color value 'color-mix(in lch specified hue, lch(100 0 40deg), lch(100 0 60deg))' assert_true: 'color-mix(in lch specified hue, lch(100 0 40deg), lch(100 0 60deg))' is a supported value for color. expected true got false
 FAIL Property color value 'color-mix(in lch specified hue, lch(100 0 60deg), lch(100 0 40deg))' assert_true: 'color-mix(in lch specified hue, lch(100 0 60deg), lch(100 0 40deg))' is a supported value for color. expected true got false
 FAIL Property color value 'color-mix(in lch specified hue, lch(100 0 50deg), lch(100 0 330deg))' assert_true: 'color-mix(in lch specified hue, lch(100 0 50deg), lch(100 0 330deg))' is a supported value for color. expected true got false
 FAIL Property color value 'color-mix(in lch specified hue, lch(100 0 330deg), lch(100 0 50deg))' assert_true: 'color-mix(in lch specified hue, lch(100 0 330deg), lch(100 0 50deg))' is a supported value for color. expected true got false
 FAIL Property color value 'color-mix(in lch specified hue, lch(100 0 20deg), lch(100 0 320deg))' assert_true: 'color-mix(in lch specified hue, lch(100 0 20deg), lch(100 0 320deg))' is a supported value for color. expected true got false
 FAIL Property color value 'color-mix(in lch specified hue, lch(100 0 320deg), lch(100 0 20deg))' assert_true: 'color-mix(in lch specified hue, lch(100 0 320deg), lch(100 0 20deg))' is a supported value for color. expected true got false
-FAIL Property color value 'color-mix(in lch, lch(none none none), lch(none none none))' assert_equals: expected "lch(none none none)" but got "color(srgb none none none)"
-FAIL Property color value 'color-mix(in lch, lch(none none none), lch(50 60 70deg))' assert_equals: expected "lch(50 60 70)" but got "color(srgb 50 60 70)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg), lch(none none none))' assert_equals: expected "lch(10 20 30)" but got "color(srgb 10 20 30)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 none), lch(50 60 70deg))' assert_equals: expected "lch(30 40 70)" but got "color(srgb 30 40 70)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg), lch(50 60 none))' assert_equals: expected "lch(30 40 30)" but got "color(srgb 30 40 30)"
-FAIL Property color value 'color-mix(in lch, lch(none 20 30deg), lch(50 none 70deg))' assert_equals: expected "lch(50 20 50)" but got "color(srgb 50 20 50)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / none), lch(50 60 70deg))' assert_equals: expected "lch(30 40 50)" but got "color(srgb 30 40 50)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / none), lch(50 60 70deg / 0.5))' assert_equals: expected "lch(30 40 50 / 0.5)" but got "color(srgb 23.3333 33.3333 66.6667 / 0.75)"
-FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / none), lch(50 60 70deg / none))' assert_equals: expected "lch(30 40 50 / none)" but got "color(srgb 30 40 50 / none)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg), oklch(50 60 70deg))' assert_equals: expected "oklch(30 40 50)" but got "color(srgb 30 40 50)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg) 25%, oklch(50 60 70deg))' assert_equals: expected "oklch(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in oklch, 25% oklch(10 20 30deg), oklch(50 60 70deg))' assert_equals: expected "oklch(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg), 25% oklch(50 60 70deg))' assert_equals: expected "oklch(20 30 40)" but got "color(srgb 20 30 40)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg), oklch(50 60 70deg) 25%)' assert_equals: expected "oklch(20 30 40)" but got "color(srgb 20 30 40)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg) 25%, oklch(50 60 70deg) 75%)' assert_equals: expected "oklch(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg) 30%, oklch(50 60 70deg) 90%)' assert_equals: expected "oklch(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg) 12.5%, oklch(50 60 70deg) 37.5%)' assert_equals: expected "oklch(40 50 60 / 0.5)" but got "color(srgb 40 50 60 / 0.5)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg) 0%, oklch(50 60 70deg))' assert_equals: expected "oklch(50 60 70)" but got "color(srgb 50 60 70)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4), oklch(50 60 70deg / .8))' assert_equals: expected "oklch(36.666664 46.666664 50 / 0.6)" but got "color(srgb 36.6667 46.6667 83.3333 / 0.6)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4) 25%, oklch(50 60 70deg / .8))' assert_equals: expected "oklch(44.285713 54.285717 60 / 0.7)" but got "color(srgb 44.2857 54.2857 85.7143 / 0.7)"
-FAIL Property color value 'color-mix(in oklch, 25% oklch(10 20 30deg / .4), oklch(50 60 70deg / .8))' assert_equals: expected "oklch(44.285713 54.285717 60 / 0.7)" but got "color(srgb 44.2857 54.2857 85.7143 / 0.7)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4), 25% oklch(50 60 70deg / .8))' assert_equals: expected "oklch(26 36 40 / 0.5)" but got "color(srgb 26 36 80 / 0.5)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4), oklch(50 60 70deg / .8) 25%)' assert_equals: expected "oklch(26 36 40 / 0.5)" but got "color(srgb 26 36 80 / 0.5)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4) 25%, oklch(50 60 70deg / .8) 75%)' assert_equals: expected "oklch(44.285713 54.285717 60 / 0.7)" but got "color(srgb 44.2857 54.2857 85.7143 / 0.7)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4) 30%, oklch(50 60 70deg / .8) 90%)' assert_equals: expected "oklch(44.285713 54.285717 60 / 0.7)" but got "color(srgb 44.2857 54.2857 85.7143 / 0.7)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4) 12.5%, oklch(50 60 70deg / .8) 37.5%)' assert_equals: expected "oklch(44.285713 54.285717 60 / 0.35)" but got "color(srgb 44.2857 54.2857 85.7143 / 0.35)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4) 0%, oklch(50 60 70deg / .8))' assert_equals: expected "oklch(50 60 70 / 0.8)" but got "color(srgb 50 60 87.5 / 0.8)"
-FAIL Property color value 'color-mix(in oklch, oklch(100 0 40deg), oklch(100 0 60deg))' assert_equals: expected "oklch(100 0 50)" but got "color(srgb 100 0 50)"
-FAIL Property color value 'color-mix(in oklch, oklch(100 0 60deg), oklch(100 0 40deg))' assert_equals: expected "oklch(100 0 50)" but got "color(srgb 100 0 50)"
-FAIL Property color value 'color-mix(in oklch, oklch(100 0 50deg), oklch(100 0 330deg))' assert_equals: expected "oklch(100 0 10)" but got "color(srgb 100 0 10)"
-FAIL Property color value 'color-mix(in oklch, oklch(100 0 330deg), oklch(100 0 50deg))' assert_equals: expected "oklch(100 0 10)" but got "color(srgb 100 0 10)"
-FAIL Property color value 'color-mix(in oklch, oklch(100 0 20deg), oklch(100 0 320deg))' assert_equals: expected "oklch(100 0 350)" but got "color(srgb 100 0 350)"
-FAIL Property color value 'color-mix(in oklch, oklch(100 0 320deg), oklch(100 0 20deg))' assert_equals: expected "oklch(100 0 350)" but got "color(srgb 100 0 350)"
-FAIL Property color value 'color-mix(in oklch shorter hue, oklch(100 0 40deg), oklch(100 0 60deg))' assert_equals: expected "oklch(100 0 50)" but got "color(srgb 100 0 50)"
-FAIL Property color value 'color-mix(in oklch shorter hue, oklch(100 0 60deg), oklch(100 0 40deg))' assert_equals: expected "oklch(100 0 50)" but got "color(srgb 100 0 50)"
-FAIL Property color value 'color-mix(in oklch shorter hue, oklch(100 0 50deg), oklch(100 0 330deg))' assert_equals: expected "oklch(100 0 10)" but got "color(srgb 100 0 10)"
-FAIL Property color value 'color-mix(in oklch shorter hue, oklch(100 0 330deg), oklch(100 0 50deg))' assert_equals: expected "oklch(100 0 10)" but got "color(srgb 100 0 10)"
-FAIL Property color value 'color-mix(in oklch shorter hue, oklch(100 0 20deg), oklch(100 0 320deg))' assert_equals: expected "oklch(100 0 350)" but got "color(srgb 100 0 350)"
-FAIL Property color value 'color-mix(in oklch shorter hue, oklch(100 0 320deg), oklch(100 0 20deg))' assert_equals: expected "oklch(100 0 350)" but got "color(srgb 100 0 350)"
-FAIL Property color value 'color-mix(in oklch longer hue, oklch(100 0 40deg), oklch(100 0 60deg))' assert_equals: expected "oklch(100 0 230)" but got "color(srgb 100 0 230)"
-FAIL Property color value 'color-mix(in oklch longer hue, oklch(100 0 60deg), oklch(100 0 40deg))' assert_equals: expected "oklch(100 0 230)" but got "color(srgb 100 0 230)"
-FAIL Property color value 'color-mix(in oklch longer hue, oklch(100 0 50deg), oklch(100 0 330deg))' assert_equals: expected "oklch(100 0 190)" but got "color(srgb 100 0 190)"
-FAIL Property color value 'color-mix(in oklch longer hue, oklch(100 0 330deg), oklch(100 0 50deg))' assert_equals: expected "oklch(100 0 190)" but got "color(srgb 100 0 190)"
-FAIL Property color value 'color-mix(in oklch longer hue, oklch(100 0 20deg), oklch(100 0 320deg))' assert_equals: expected "oklch(100 0 170)" but got "color(srgb 100 0 170)"
-FAIL Property color value 'color-mix(in oklch longer hue, oklch(100 0 320deg), oklch(100 0 20deg))' assert_equals: expected "oklch(100 0 170)" but got "color(srgb 100 0 170)"
-FAIL Property color value 'color-mix(in oklch increasing hue, oklch(100 0 40deg), oklch(100 0 60deg))' assert_equals: expected "oklch(100 0 50)" but got "color(srgb 100 0 230)"
-FAIL Property color value 'color-mix(in oklch increasing hue, oklch(100 0 60deg), oklch(100 0 40deg))' assert_equals: expected "oklch(100 0 230)" but got "color(srgb 100 0 50)"
-FAIL Property color value 'color-mix(in oklch increasing hue, oklch(100 0 50deg), oklch(100 0 330deg))' assert_equals: expected "oklch(100 0 190)" but got "color(srgb 100 0 10)"
-FAIL Property color value 'color-mix(in oklch increasing hue, oklch(100 0 330deg), oklch(100 0 50deg))' assert_equals: expected "oklch(100 0 10)" but got "color(srgb 100 0 190)"
-FAIL Property color value 'color-mix(in oklch increasing hue, oklch(100 0 20deg), oklch(100 0 320deg))' assert_equals: expected "oklch(100 0 170)" but got "color(srgb 100 0 350)"
-FAIL Property color value 'color-mix(in oklch increasing hue, oklch(100 0 320deg), oklch(100 0 20deg))' assert_equals: expected "oklch(100 0 350)" but got "color(srgb 100 0 170)"
-FAIL Property color value 'color-mix(in oklch decreasing hue, oklch(100 0 40deg), oklch(100 0 60deg))' assert_equals: expected "oklch(100 0 230)" but got "color(srgb 100 0 50)"
-FAIL Property color value 'color-mix(in oklch decreasing hue, oklch(100 0 60deg), oklch(100 0 40deg))' assert_equals: expected "oklch(100 0 50)" but got "color(srgb 100 0 230)"
-FAIL Property color value 'color-mix(in oklch decreasing hue, oklch(100 0 50deg), oklch(100 0 330deg))' assert_equals: expected "oklch(100 0 10)" but got "color(srgb 100 0 190)"
-FAIL Property color value 'color-mix(in oklch decreasing hue, oklch(100 0 330deg), oklch(100 0 50deg))' assert_equals: expected "oklch(100 0 190)" but got "color(srgb 100 0 10)"
-FAIL Property color value 'color-mix(in oklch decreasing hue, oklch(100 0 20deg), oklch(100 0 320deg))' assert_equals: expected "oklch(100 0 350)" but got "color(srgb 100 0 170)"
-FAIL Property color value 'color-mix(in oklch decreasing hue, oklch(100 0 320deg), oklch(100 0 20deg))' assert_equals: expected "oklch(100 0 170)" but got "color(srgb 100 0 350)"
+PASS Property color value 'color-mix(in lch, lch(none none none), lch(none none none))'
+PASS Property color value 'color-mix(in lch, lch(none none none), lch(50 60 70deg))'
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg), lch(none none none))'
+PASS Property color value 'color-mix(in lch, lch(10 20 none), lch(50 60 70deg))'
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg), lch(50 60 none))'
+PASS Property color value 'color-mix(in lch, lch(none 20 30deg), lch(50 none 70deg))'
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg / none), lch(50 60 70deg))'
+FAIL Property color value 'color-mix(in lch, lch(10 20 30deg / none), lch(50 60 70deg / 0.5))' assert_equals: expected "lch(30 40 50 / 0.5)" but got "lch(23.3333 33.3333 50 / 0.75)"
+PASS Property color value 'color-mix(in lch, lch(10 20 30deg / none), lch(50 60 70deg / none))'
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg), oklch(50 60 70deg))'
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg) 25%, oklch(50 60 70deg))'
+PASS Property color value 'color-mix(in oklch, 25% oklch(10 20 30deg), oklch(50 60 70deg))'
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg), 25% oklch(50 60 70deg))'
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg), oklch(50 60 70deg) 25%)'
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg) 25%, oklch(50 60 70deg) 75%)'
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg) 30%, oklch(50 60 70deg) 90%)'
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg) 12.5%, oklch(50 60 70deg) 37.5%)'
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg) 0%, oklch(50 60 70deg))'
+FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4), oklch(50 60 70deg / .8))' assert_equals: expected "oklch(36.666664 46.666664 50 / 0.6)" but got "oklch(36.6667 46.6667 50 / 0.6)"
+FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4) 25%, oklch(50 60 70deg / .8))' assert_equals: expected "oklch(44.285713 54.285717 60 / 0.7)" but got "oklch(44.2857 54.2857 60 / 0.7)"
+FAIL Property color value 'color-mix(in oklch, 25% oklch(10 20 30deg / .4), oklch(50 60 70deg / .8))' assert_equals: expected "oklch(44.285713 54.285717 60 / 0.7)" but got "oklch(44.2857 54.2857 60 / 0.7)"
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4), 25% oklch(50 60 70deg / .8))'
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4), oklch(50 60 70deg / .8) 25%)'
+FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4) 25%, oklch(50 60 70deg / .8) 75%)' assert_equals: expected "oklch(44.285713 54.285717 60 / 0.7)" but got "oklch(44.2857 54.2857 60 / 0.7)"
+FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4) 30%, oklch(50 60 70deg / .8) 90%)' assert_equals: expected "oklch(44.285713 54.285717 60 / 0.7)" but got "oklch(44.2857 54.2857 60 / 0.7)"
+FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4) 12.5%, oklch(50 60 70deg / .8) 37.5%)' assert_equals: expected "oklch(44.285713 54.285717 60 / 0.35)" but got "oklch(44.2857 54.2857 60 / 0.35)"
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg / .4) 0%, oklch(50 60 70deg / .8))'
+PASS Property color value 'color-mix(in oklch, oklch(100 0 40deg), oklch(100 0 60deg))'
+PASS Property color value 'color-mix(in oklch, oklch(100 0 60deg), oklch(100 0 40deg))'
+PASS Property color value 'color-mix(in oklch, oklch(100 0 50deg), oklch(100 0 330deg))'
+PASS Property color value 'color-mix(in oklch, oklch(100 0 330deg), oklch(100 0 50deg))'
+PASS Property color value 'color-mix(in oklch, oklch(100 0 20deg), oklch(100 0 320deg))'
+PASS Property color value 'color-mix(in oklch, oklch(100 0 320deg), oklch(100 0 20deg))'
+PASS Property color value 'color-mix(in oklch shorter hue, oklch(100 0 40deg), oklch(100 0 60deg))'
+PASS Property color value 'color-mix(in oklch shorter hue, oklch(100 0 60deg), oklch(100 0 40deg))'
+PASS Property color value 'color-mix(in oklch shorter hue, oklch(100 0 50deg), oklch(100 0 330deg))'
+PASS Property color value 'color-mix(in oklch shorter hue, oklch(100 0 330deg), oklch(100 0 50deg))'
+PASS Property color value 'color-mix(in oklch shorter hue, oklch(100 0 20deg), oklch(100 0 320deg))'
+PASS Property color value 'color-mix(in oklch shorter hue, oklch(100 0 320deg), oklch(100 0 20deg))'
+PASS Property color value 'color-mix(in oklch longer hue, oklch(100 0 40deg), oklch(100 0 60deg))'
+PASS Property color value 'color-mix(in oklch longer hue, oklch(100 0 60deg), oklch(100 0 40deg))'
+PASS Property color value 'color-mix(in oklch longer hue, oklch(100 0 50deg), oklch(100 0 330deg))'
+PASS Property color value 'color-mix(in oklch longer hue, oklch(100 0 330deg), oklch(100 0 50deg))'
+PASS Property color value 'color-mix(in oklch longer hue, oklch(100 0 20deg), oklch(100 0 320deg))'
+PASS Property color value 'color-mix(in oklch longer hue, oklch(100 0 320deg), oklch(100 0 20deg))'
+FAIL Property color value 'color-mix(in oklch increasing hue, oklch(100 0 40deg), oklch(100 0 60deg))' assert_equals: expected "oklch(100 0 50)" but got "oklch(100 0 230)"
+FAIL Property color value 'color-mix(in oklch increasing hue, oklch(100 0 60deg), oklch(100 0 40deg))' assert_equals: expected "oklch(100 0 230)" but got "oklch(100 0 50)"
+FAIL Property color value 'color-mix(in oklch increasing hue, oklch(100 0 50deg), oklch(100 0 330deg))' assert_equals: expected "oklch(100 0 190)" but got "oklch(100 0 10)"
+FAIL Property color value 'color-mix(in oklch increasing hue, oklch(100 0 330deg), oklch(100 0 50deg))' assert_equals: expected "oklch(100 0 10)" but got "oklch(100 0 190)"
+FAIL Property color value 'color-mix(in oklch increasing hue, oklch(100 0 20deg), oklch(100 0 320deg))' assert_equals: expected "oklch(100 0 170)" but got "oklch(100 0 350)"
+FAIL Property color value 'color-mix(in oklch increasing hue, oklch(100 0 320deg), oklch(100 0 20deg))' assert_equals: expected "oklch(100 0 350)" but got "oklch(100 0 170)"
+FAIL Property color value 'color-mix(in oklch decreasing hue, oklch(100 0 40deg), oklch(100 0 60deg))' assert_equals: expected "oklch(100 0 230)" but got "oklch(100 0 50)"
+FAIL Property color value 'color-mix(in oklch decreasing hue, oklch(100 0 60deg), oklch(100 0 40deg))' assert_equals: expected "oklch(100 0 50)" but got "oklch(100 0 230)"
+FAIL Property color value 'color-mix(in oklch decreasing hue, oklch(100 0 50deg), oklch(100 0 330deg))' assert_equals: expected "oklch(100 0 10)" but got "oklch(100 0 190)"
+FAIL Property color value 'color-mix(in oklch decreasing hue, oklch(100 0 330deg), oklch(100 0 50deg))' assert_equals: expected "oklch(100 0 190)" but got "oklch(100 0 10)"
+FAIL Property color value 'color-mix(in oklch decreasing hue, oklch(100 0 20deg), oklch(100 0 320deg))' assert_equals: expected "oklch(100 0 350)" but got "oklch(100 0 170)"
+FAIL Property color value 'color-mix(in oklch decreasing hue, oklch(100 0 320deg), oklch(100 0 20deg))' assert_equals: expected "oklch(100 0 170)" but got "oklch(100 0 350)"
 FAIL Property color value 'color-mix(in oklch specified hue, oklch(100 0 40deg), oklch(100 0 60deg))' assert_true: 'color-mix(in oklch specified hue, oklch(100 0 40deg), oklch(100 0 60deg))' is a supported value for color. expected true got false
 FAIL Property color value 'color-mix(in oklch specified hue, oklch(100 0 60deg), oklch(100 0 40deg))' assert_true: 'color-mix(in oklch specified hue, oklch(100 0 60deg), oklch(100 0 40deg))' is a supported value for color. expected true got false
 FAIL Property color value 'color-mix(in oklch specified hue, oklch(100 0 50deg), oklch(100 0 330deg))' assert_true: 'color-mix(in oklch specified hue, oklch(100 0 50deg), oklch(100 0 330deg))' is a supported value for color. expected true got false
 FAIL Property color value 'color-mix(in oklch specified hue, oklch(100 0 330deg), oklch(100 0 50deg))' assert_true: 'color-mix(in oklch specified hue, oklch(100 0 330deg), oklch(100 0 50deg))' is a supported value for color. expected true got false
 FAIL Property color value 'color-mix(in oklch specified hue, oklch(100 0 20deg), oklch(100 0 320deg))' assert_true: 'color-mix(in oklch specified hue, oklch(100 0 20deg), oklch(100 0 320deg))' is a supported value for color. expected true got false
 FAIL Property color value 'color-mix(in oklch specified hue, oklch(100 0 320deg), oklch(100 0 20deg))' assert_true: 'color-mix(in oklch specified hue, oklch(100 0 320deg), oklch(100 0 20deg))' is a supported value for color. expected true got false
-FAIL Property color value 'color-mix(in oklch, oklch(none none none), oklch(none none none))' assert_equals: expected "oklch(none none none)" but got "color(srgb none none none)"
-FAIL Property color value 'color-mix(in oklch, oklch(none none none), oklch(50 60 70deg))' assert_equals: expected "oklch(50 60 70)" but got "color(srgb 50 60 70)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg), oklch(none none none))' assert_equals: expected "oklch(10 20 30)" but got "color(srgb 10 20 30)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 none), oklch(50 60 70deg))' assert_equals: expected "oklch(30 40 70)" but got "color(srgb 30 40 70)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg), oklch(50 60 none))' assert_equals: expected "oklch(30 40 30)" but got "color(srgb 30 40 30)"
-FAIL Property color value 'color-mix(in oklch, oklch(none 20 30deg), oklch(50 none 70deg))' assert_equals: expected "oklch(50 20 50)" but got "color(srgb 50 20 50)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / none), oklch(50 60 70deg))' assert_equals: expected "oklch(30 40 50)" but got "color(srgb 30 40 50)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / none), oklch(50 60 70deg / 0.5))' assert_equals: expected "oklch(30 40 50 / 0.5)" but got "color(srgb 23.3333 33.3333 66.6667 / 0.75)"
-FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / none), oklch(50 60 70deg / none))' assert_equals: expected "oklch(30 40 50 / none)" but got "color(srgb 30 40 50 / none)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30), lab(50 60 70))' assert_equals: expected "lab(30 40 50)" but got "color(srgb 30 40 50)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30) 25%, lab(50 60 70))' assert_equals: expected "lab(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in lab, 25% lab(10 20 30), lab(50 60 70))' assert_equals: expected "lab(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30), 25% lab(50 60 70))' assert_equals: expected "lab(20 30 40)" but got "color(srgb 20 30 40)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30), lab(50 60 70) 25%)' assert_equals: expected "lab(20 30 40)" but got "color(srgb 20 30 40)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30) 25%, lab(50 60 70) 75%)' assert_equals: expected "lab(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30) 30%, lab(50 60 70) 90%)' assert_equals: expected "lab(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30) 12.5%, lab(50 60 70) 37.5%)' assert_equals: expected "lab(40 50 60 / 0.5)" but got "color(srgb 40 50 60 / 0.5)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30) 0%, lab(50 60 70))' assert_equals: expected "lab(50 60 70)" but got "color(srgb 50 60 70)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30 / .4), lab(50 60 70 / .8))' assert_equals: expected "lab(36.666664 46.666664 56.666664 / 0.6)" but got "color(srgb 36.6667 46.6667 56.6667 / 0.6)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30 / .4) 25%, lab(50 60 70 / .8))' assert_equals: expected "lab(44.285713 54.285717 64.28571 / 0.7)" but got "color(srgb 44.2857 54.2857 64.2857 / 0.7)"
-FAIL Property color value 'color-mix(in lab, 25% lab(10 20 30 / .4), lab(50 60 70 / .8))' assert_equals: expected "lab(44.285713 54.285717 64.28571 / 0.7)" but got "color(srgb 44.2857 54.2857 64.2857 / 0.7)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30 / .4), 25% lab(50 60 70 / .8))' assert_equals: expected "lab(26 36 46 / 0.5)" but got "color(srgb 26 36 46 / 0.5)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30 / .4), lab(50 60 70 / .8) 25%)' assert_equals: expected "lab(26 36 46 / 0.5)" but got "color(srgb 26 36 46 / 0.5)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30 / .4) 25%, lab(50 60 70 / .8) 75%)' assert_equals: expected "lab(44.285713 54.285717 64.28571 / 0.7)" but got "color(srgb 44.2857 54.2857 64.2857 / 0.7)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30 / .4) 30%, lab(50 60 70 / .8) 90%)' assert_equals: expected "lab(44.285713 54.285717 64.28571 / 0.7)" but got "color(srgb 44.2857 54.2857 64.2857 / 0.7)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30 / .4) 12.5%, lab(50 60 70 / .8) 37.5%)' assert_equals: expected "lab(44.285713 54.285717 64.28571 / 0.35)" but got "color(srgb 44.2857 54.2857 64.2857 / 0.35)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30 / .4) 0%, lab(50 60 70 / .8))' assert_equals: expected "lab(50 60 70 / 0.8)" but got "color(srgb 50 60 70 / 0.8)"
-FAIL Property color value 'color-mix(in lab, lab(none none none), lab(none none none))' assert_equals: expected "lab(none none none)" but got "color(srgb none none none)"
-FAIL Property color value 'color-mix(in lab, lab(none none none), lab(50 60 70))' assert_equals: expected "lab(50 60 70)" but got "color(srgb 50 60 70)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30), lab(none none none))' assert_equals: expected "lab(10 20 30)" but got "color(srgb 10 20 30)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 none), lab(50 60 70))' assert_equals: expected "lab(30 40 70)" but got "color(srgb 30 40 70)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30), lab(50 60 none))' assert_equals: expected "lab(30 40 30)" but got "color(srgb 30 40 30)"
-FAIL Property color value 'color-mix(in lab, lab(none 20 30), lab(50 none 70))' assert_equals: expected "lab(50 20 50)" but got "color(srgb 50 20 50)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30 / none), lab(50 60 70))' assert_equals: expected "lab(30 40 50)" but got "color(srgb 30 40 50)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30 / none), lab(50 60 70 / 0.5))' assert_equals: expected "lab(30 40 50 / 0.5)" but got "color(srgb 23.3333 33.3333 43.3333 / 0.75)"
-FAIL Property color value 'color-mix(in lab, lab(10 20 30 / none), lab(50 60 70 / none))' assert_equals: expected "lab(30 40 50 / none)" but got "color(srgb 30 40 50 / none)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30), oklab(50 60 70))' assert_equals: expected "oklab(30 40 50)" but got "color(srgb 30 40 50)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30) 25%, oklab(50 60 70))' assert_equals: expected "oklab(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in oklab, 25% oklab(10 20 30), oklab(50 60 70))' assert_equals: expected "oklab(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30), 25% oklab(50 60 70))' assert_equals: expected "oklab(20 30 40)" but got "color(srgb 20 30 40)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30), oklab(50 60 70) 25%)' assert_equals: expected "oklab(20 30 40)" but got "color(srgb 20 30 40)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30) 25%, oklab(50 60 70) 75%)' assert_equals: expected "oklab(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30) 30%, oklab(50 60 70) 90%)' assert_equals: expected "oklab(40 50 60)" but got "color(srgb 40 50 60)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30) 12.5%, oklab(50 60 70) 37.5%)' assert_equals: expected "oklab(40 50 60 / 0.5)" but got "color(srgb 40 50 60 / 0.5)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30) 0%, oklab(50 60 70))' assert_equals: expected "oklab(50 60 70)" but got "color(srgb 50 60 70)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / .4), oklab(50 60 70 / .8))' assert_equals: expected "oklab(36.666664 46.666664 56.666664 / 0.6)" but got "color(srgb 36.6667 46.6667 56.6667 / 0.6)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / .4) 25%, oklab(50 60 70 / .8))' assert_equals: expected "oklab(44.285713 54.285717 64.28571 / 0.7)" but got "color(srgb 44.2857 54.2857 64.2857 / 0.7)"
-FAIL Property color value 'color-mix(in oklab, 25% oklab(10 20 30 / .4), oklab(50 60 70 / .8))' assert_equals: expected "oklab(44.285713 54.285717 64.28571 / 0.7)" but got "color(srgb 44.2857 54.2857 64.2857 / 0.7)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / .4), 25% oklab(50 60 70 / .8))' assert_equals: expected "oklab(26 36 46 / 0.5)" but got "color(srgb 26 36 46 / 0.5)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / .4), oklab(50 60 70 / .8) 25%)' assert_equals: expected "oklab(26 36 46 / 0.5)" but got "color(srgb 26 36 46 / 0.5)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / .4) 25%, oklab(50 60 70 / .8) 75%)' assert_equals: expected "oklab(44.285713 54.285717 64.28571 / 0.7)" but got "color(srgb 44.2857 54.2857 64.2857 / 0.7)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / .4) 30%, oklab(50 60 70 / .8) 90%)' assert_equals: expected "oklab(44.285713 54.285717 64.28571 / 0.7)" but got "color(srgb 44.2857 54.2857 64.2857 / 0.7)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / .4) 12.5%, oklab(50 60 70 / .8) 37.5%)' assert_equals: expected "oklab(44.285713 54.285717 64.28571 / 0.35)" but got "color(srgb 44.2857 54.2857 64.2857 / 0.35)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / .4) 0%, oklab(50 60 70 / .8))' assert_equals: expected "oklab(50 60 70 / 0.8)" but got "color(srgb 50 60 70 / 0.8)"
-FAIL Property color value 'color-mix(in oklab, oklab(none none none), oklab(none none none))' assert_equals: expected "oklab(none none none)" but got "color(srgb none none none)"
-FAIL Property color value 'color-mix(in oklab, oklab(none none none), oklab(50 60 70))' assert_equals: expected "oklab(50 60 70)" but got "color(srgb 50 60 70)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30), oklab(none none none))' assert_equals: expected "oklab(10 20 30)" but got "color(srgb 10 20 30)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 none), oklab(50 60 70))' assert_equals: expected "oklab(30 40 70)" but got "color(srgb 30 40 70)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30), oklab(50 60 none))' assert_equals: expected "oklab(30 40 30)" but got "color(srgb 30 40 30)"
-FAIL Property color value 'color-mix(in oklab, oklab(none 20 30), oklab(50 none 70))' assert_equals: expected "oklab(50 20 50)" but got "color(srgb 50 20 50)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / none), oklab(50 60 70))' assert_equals: expected "oklab(30 40 50)" but got "color(srgb 30 40 50)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / none), oklab(50 60 70 / 0.5))' assert_equals: expected "oklab(30 40 50 / 0.5)" but got "color(srgb 23.3333 33.3333 43.3333 / 0.75)"
-FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / none), oklab(50 60 70 / none))' assert_equals: expected "oklab(30 40 50 / none)" but got "color(srgb 30 40 50 / none)"
+PASS Property color value 'color-mix(in oklch, oklch(none none none), oklch(none none none))'
+PASS Property color value 'color-mix(in oklch, oklch(none none none), oklch(50 60 70deg))'
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg), oklch(none none none))'
+PASS Property color value 'color-mix(in oklch, oklch(10 20 none), oklch(50 60 70deg))'
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg), oklch(50 60 none))'
+PASS Property color value 'color-mix(in oklch, oklch(none 20 30deg), oklch(50 none 70deg))'
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg / none), oklch(50 60 70deg))'
+FAIL Property color value 'color-mix(in oklch, oklch(10 20 30deg / none), oklch(50 60 70deg / 0.5))' assert_equals: expected "oklch(30 40 50 / 0.5)" but got "oklch(23.3333 33.3333 50 / 0.75)"
+PASS Property color value 'color-mix(in oklch, oklch(10 20 30deg / none), oklch(50 60 70deg / none))'
+PASS Property color value 'color-mix(in lab, lab(10 20 30), lab(50 60 70))'
+PASS Property color value 'color-mix(in lab, lab(10 20 30) 25%, lab(50 60 70))'
+PASS Property color value 'color-mix(in lab, 25% lab(10 20 30), lab(50 60 70))'
+PASS Property color value 'color-mix(in lab, lab(10 20 30), 25% lab(50 60 70))'
+PASS Property color value 'color-mix(in lab, lab(10 20 30), lab(50 60 70) 25%)'
+PASS Property color value 'color-mix(in lab, lab(10 20 30) 25%, lab(50 60 70) 75%)'
+PASS Property color value 'color-mix(in lab, lab(10 20 30) 30%, lab(50 60 70) 90%)'
+PASS Property color value 'color-mix(in lab, lab(10 20 30) 12.5%, lab(50 60 70) 37.5%)'
+PASS Property color value 'color-mix(in lab, lab(10 20 30) 0%, lab(50 60 70))'
+FAIL Property color value 'color-mix(in lab, lab(10 20 30 / .4), lab(50 60 70 / .8))' assert_equals: expected "lab(36.666664 46.666664 56.666664 / 0.6)" but got "lab(36.6667 46.6667 56.6667 / 0.6)"
+FAIL Property color value 'color-mix(in lab, lab(10 20 30 / .4) 25%, lab(50 60 70 / .8))' assert_equals: expected "lab(44.285713 54.285717 64.28571 / 0.7)" but got "lab(44.2857 54.2857 64.2857 / 0.7)"
+FAIL Property color value 'color-mix(in lab, 25% lab(10 20 30 / .4), lab(50 60 70 / .8))' assert_equals: expected "lab(44.285713 54.285717 64.28571 / 0.7)" but got "lab(44.2857 54.2857 64.2857 / 0.7)"
+PASS Property color value 'color-mix(in lab, lab(10 20 30 / .4), 25% lab(50 60 70 / .8))'
+PASS Property color value 'color-mix(in lab, lab(10 20 30 / .4), lab(50 60 70 / .8) 25%)'
+FAIL Property color value 'color-mix(in lab, lab(10 20 30 / .4) 25%, lab(50 60 70 / .8) 75%)' assert_equals: expected "lab(44.285713 54.285717 64.28571 / 0.7)" but got "lab(44.2857 54.2857 64.2857 / 0.7)"
+FAIL Property color value 'color-mix(in lab, lab(10 20 30 / .4) 30%, lab(50 60 70 / .8) 90%)' assert_equals: expected "lab(44.285713 54.285717 64.28571 / 0.7)" but got "lab(44.2857 54.2857 64.2857 / 0.7)"
+FAIL Property color value 'color-mix(in lab, lab(10 20 30 / .4) 12.5%, lab(50 60 70 / .8) 37.5%)' assert_equals: expected "lab(44.285713 54.285717 64.28571 / 0.35)" but got "lab(44.2857 54.2857 64.2857 / 0.35)"
+PASS Property color value 'color-mix(in lab, lab(10 20 30 / .4) 0%, lab(50 60 70 / .8))'
+PASS Property color value 'color-mix(in lab, lab(none none none), lab(none none none))'
+PASS Property color value 'color-mix(in lab, lab(none none none), lab(50 60 70))'
+PASS Property color value 'color-mix(in lab, lab(10 20 30), lab(none none none))'
+PASS Property color value 'color-mix(in lab, lab(10 20 none), lab(50 60 70))'
+PASS Property color value 'color-mix(in lab, lab(10 20 30), lab(50 60 none))'
+PASS Property color value 'color-mix(in lab, lab(none 20 30), lab(50 none 70))'
+PASS Property color value 'color-mix(in lab, lab(10 20 30 / none), lab(50 60 70))'
+FAIL Property color value 'color-mix(in lab, lab(10 20 30 / none), lab(50 60 70 / 0.5))' assert_equals: expected "lab(30 40 50 / 0.5)" but got "lab(23.3333 33.3333 43.3333 / 0.75)"
+PASS Property color value 'color-mix(in lab, lab(10 20 30 / none), lab(50 60 70 / none))'
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30), oklab(50 60 70))'
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30) 25%, oklab(50 60 70))'
+PASS Property color value 'color-mix(in oklab, 25% oklab(10 20 30), oklab(50 60 70))'
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30), 25% oklab(50 60 70))'
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30), oklab(50 60 70) 25%)'
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30) 25%, oklab(50 60 70) 75%)'
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30) 30%, oklab(50 60 70) 90%)'
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30) 12.5%, oklab(50 60 70) 37.5%)'
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30) 0%, oklab(50 60 70))'
+FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / .4), oklab(50 60 70 / .8))' assert_equals: expected "oklab(36.666664 46.666664 56.666664 / 0.6)" but got "oklab(36.6667 46.6667 56.6667 / 0.6)"
+FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / .4) 25%, oklab(50 60 70 / .8))' assert_equals: expected "oklab(44.285713 54.285717 64.28571 / 0.7)" but got "oklab(44.2857 54.2857 64.2857 / 0.7)"
+FAIL Property color value 'color-mix(in oklab, 25% oklab(10 20 30 / .4), oklab(50 60 70 / .8))' assert_equals: expected "oklab(44.285713 54.285717 64.28571 / 0.7)" but got "oklab(44.2857 54.2857 64.2857 / 0.7)"
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30 / .4), 25% oklab(50 60 70 / .8))'
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30 / .4), oklab(50 60 70 / .8) 25%)'
+FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / .4) 25%, oklab(50 60 70 / .8) 75%)' assert_equals: expected "oklab(44.285713 54.285717 64.28571 / 0.7)" but got "oklab(44.2857 54.2857 64.2857 / 0.7)"
+FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / .4) 30%, oklab(50 60 70 / .8) 90%)' assert_equals: expected "oklab(44.285713 54.285717 64.28571 / 0.7)" but got "oklab(44.2857 54.2857 64.2857 / 0.7)"
+FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / .4) 12.5%, oklab(50 60 70 / .8) 37.5%)' assert_equals: expected "oklab(44.285713 54.285717 64.28571 / 0.35)" but got "oklab(44.2857 54.2857 64.2857 / 0.35)"
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30 / .4) 0%, oklab(50 60 70 / .8))'
+PASS Property color value 'color-mix(in oklab, oklab(none none none), oklab(none none none))'
+PASS Property color value 'color-mix(in oklab, oklab(none none none), oklab(50 60 70))'
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30), oklab(none none none))'
+PASS Property color value 'color-mix(in oklab, oklab(10 20 none), oklab(50 60 70))'
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30), oklab(50 60 none))'
+PASS Property color value 'color-mix(in oklab, oklab(none 20 30), oklab(50 none 70))'
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30 / none), oklab(50 60 70))'
+FAIL Property color value 'color-mix(in oklab, oklab(10 20 30 / none), oklab(50 60 70 / 0.5))' assert_equals: expected "oklab(30 40 50 / 0.5)" but got "oklab(23.3333 33.3333 43.3333 / 0.75)"
+PASS Property color value 'color-mix(in oklab, oklab(10 20 30 / none), oklab(50 60 70 / none))'
 PASS Property color value 'color-mix(in srgb, color(srgb .1 .2 .3), color(srgb .5 .6 .7))'
 PASS Property color value 'color-mix(in srgb, color(srgb .1 .2 .3) 25%, color(srgb .5 .6 .7))'
 PASS Property color value 'color-mix(in srgb, 25% color(srgb .1 .2 .3), color(srgb .5 .6 .7))'
@@ -354,125 +354,125 @@
 PASS Property color value 'color-mix(in srgb, color(srgb .1 .2 .3 / none), color(srgb .5 .6 .7))'
 FAIL Property color value 'color-mix(in srgb, color(srgb .1 .2 .3 / none), color(srgb .5 .6 .7 / 0.5))' assert_equals: expected "color(srgb 0.3 0.4 0.5 / 0.5)" but got "color(srgb 0.233333 0.333333 0.433333 / 0.75)"
 PASS Property color value 'color-mix(in srgb, color(srgb .1 .2 .3 / none), color(srgb .5 .6 .7 / none))'
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), color(srgb-linear .5 .6 .7))' assert_equals: expected "color(srgb-linear 0.3 0.4 0.5)" but got "color(srgb 0.3 0.4 0.5)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 25%, color(srgb-linear .5 .6 .7))' assert_equals: expected "color(srgb-linear 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in srgb-linear, 25% color(srgb-linear .1 .2 .3), color(srgb-linear .5 .6 .7))' assert_equals: expected "color(srgb-linear 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), color(srgb-linear .5 .6 .7) 25%)' assert_equals: expected "color(srgb-linear 0.2 0.3 0.4)" but got "color(srgb 0.2 0.3 0.4)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), 25% color(srgb-linear .5 .6 .7))' assert_equals: expected "color(srgb-linear 0.2 0.3 0.4)" but got "color(srgb 0.2 0.3 0.4)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 25%, color(srgb-linear .5 .6 .7) 75%)' assert_equals: expected "color(srgb-linear 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 30%, color(srgb-linear .5 .6 .7) 90%)' assert_equals: expected "color(srgb-linear 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 12.5%, color(srgb-linear .5 .6 .7) 37.5%)' assert_equals: expected "color(srgb-linear 0.4 0.5 0.6 / 0.5)" but got "color(srgb 0.4 0.5 0.6 / 0.5)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 0%, color(srgb-linear .5 .6 .7))' assert_equals: expected "color(srgb-linear 0.5 0.6 0.7)" but got "color(srgb 0.5 0.6 0.7)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .5), color(srgb-linear .5 .6 .7 / .8))' assert_equals: expected "color(srgb-linear 0.3461539 0.4461539 0.5461539 / 0.65)" but got "color(srgb 0.346154 0.446154 0.546154 / 0.65)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 25%, color(srgb-linear .5 .6 .7 / .8))' assert_equals: expected "color(srgb-linear 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in srgb-linear, 25% color(srgb-linear .1 .2 .3 / .4), color(srgb-linear .5 .6 .7 / .8))' assert_equals: expected "color(srgb-linear 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4), color(srgb-linear .5 .6 .7 / .8) 25%)' assert_equals: expected "color(srgb-linear 0.26000002 0.36 0.46 / 0.5)" but got "color(srgb 0.26 0.36 0.46 / 0.5)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4), 25% color(srgb-linear .5 .6 .7 / .8))' assert_equals: expected "color(srgb-linear 0.26000002 0.36 0.46 / 0.5)" but got "color(srgb 0.26 0.36 0.46 / 0.5)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 25%, color(srgb-linear .5 .6 .7 / .8) 75%)' assert_equals: expected "color(srgb-linear 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 30%, color(srgb-linear .5 .6 .7 / .8) 90%)' assert_equals: expected "color(srgb-linear 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 12.5%, color(srgb-linear .5 .6 .7 / .8) 37.5%)' assert_equals: expected "color(srgb-linear 0.44285715 0.54285717 0.64285713 / 0.35)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.35)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 0%, color(srgb-linear .5 .6 .7 / .8))' assert_equals: expected "color(srgb-linear 0.5 0.6 0.7 / 0.8)" but got "color(srgb 0.5 0.6 0.7 / 0.8)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear 2 3 4 / 5), color(srgb-linear 4 6 8 / 10))' assert_equals: expected "color(srgb-linear 3 4.5 6)" but got "color(srgb 3 4.5 6)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear -2 -3 -4), color(srgb-linear -4 -6 -8))' assert_equals: expected "color(srgb-linear -3 -4.5 -6)" but got "color(srgb -3 -4.5 -6)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear -2 -3 -4 / -5), color(srgb-linear -4 -6 -8 / -10))' assert_equals: expected "color(srgb-linear 0 0 0 / 0)" but got "color(srgb 0 0 0 / 0)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear none none none), color(srgb-linear none none none))' assert_equals: expected "color(srgb-linear none none none)" but got "color(srgb none none none)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear none none none), color(srgb-linear .5 .6 .7))' assert_equals: expected "color(srgb-linear 0.5 0.6 0.7)" but got "color(srgb 0.5 0.6 0.7)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), color(srgb-linear none none none))' assert_equals: expected "color(srgb-linear 0.1 0.2 0.3)" but got "color(srgb 0.1 0.2 0.3)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 none), color(srgb-linear .5 .6 .7))' assert_equals: expected "color(srgb-linear 0.3 0.4 0.7)" but got "color(srgb 0.3 0.4 0.7)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), color(srgb-linear .5 .6 none))' assert_equals: expected "color(srgb-linear 0.3 0.4 0.3)" but got "color(srgb 0.3 0.4 0.3)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear none .2 .3), color(srgb-linear .5 none .7))' assert_equals: expected "color(srgb-linear 0.5 0.2 0.5)" but got "color(srgb 0.5 0.2 0.5)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / none), color(srgb-linear .5 .6 .7))' assert_equals: expected "color(srgb-linear 0.3 0.4 0.5)" but got "color(srgb 0.3 0.4 0.5)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / none), color(srgb-linear .5 .6 .7 / 0.5))' assert_equals: expected "color(srgb-linear 0.3 0.4 0.5 / 0.5)" but got "color(srgb 0.233333 0.333333 0.433333 / 0.75)"
-FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / none), color(srgb-linear .5 .6 .7 / none))' assert_equals: expected "color(srgb-linear 0.3 0.4 0.5 / none)" but got "color(srgb 0.3 0.4 0.5 / none)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3), color(xyz .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.3 0.4 0.5)" but got "color(srgb 0.3 0.4 0.5)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3) 25%, color(xyz .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in xyz, 25% color(xyz .1 .2 .3), color(xyz .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3), color(xyz .5 .6 .7) 25%)' assert_equals: expected "color(xyz-d65 0.2 0.3 0.4)" but got "color(srgb 0.2 0.3 0.4)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3), 25% color(xyz .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.2 0.3 0.4)" but got "color(srgb 0.2 0.3 0.4)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3) 25%, color(xyz .5 .6 .7) 75%)' assert_equals: expected "color(xyz-d65 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3) 30%, color(xyz .5 .6 .7) 90%)' assert_equals: expected "color(xyz-d65 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3) 12.5%, color(xyz .5 .6 .7) 37.5%)' assert_equals: expected "color(xyz-d65 0.4 0.5 0.6 / 0.5)" but got "color(srgb 0.4 0.5 0.6 / 0.5)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3) 0%, color(xyz .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.5 0.6 0.7)" but got "color(srgb 0.5 0.6 0.7)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .5), color(xyz .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.3461539 0.4461539 0.5461539 / 0.65)" but got "color(srgb 0.346154 0.446154 0.546154 / 0.65)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .4) 25%, color(xyz .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in xyz, 25% color(xyz .1 .2 .3 / .4), color(xyz .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .4), color(xyz .5 .6 .7 / .8) 25%)' assert_equals: expected "color(xyz-d65 0.26000002 0.36 0.46 / 0.5)" but got "color(srgb 0.26 0.36 0.46 / 0.5)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .4), 25% color(xyz .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.26000002 0.36 0.46 / 0.5)" but got "color(srgb 0.26 0.36 0.46 / 0.5)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .4) 25%, color(xyz .5 .6 .7 / .8) 75%)' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .4) 30%, color(xyz .5 .6 .7 / .8) 90%)' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .4) 12.5%, color(xyz .5 .6 .7 / .8) 37.5%)' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.35)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.35)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .4) 0%, color(xyz .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.5 0.6 0.7 / 0.8)" but got "color(srgb 0.5 0.6 0.7 / 0.8)"
-FAIL Property color value 'color-mix(in xyz, color(xyz 2 3 4 / 5), color(xyz 4 6 8 / 10))' assert_equals: expected "color(xyz-d65 3 4.5 6)" but got "color(srgb 3 4.5 6)"
-FAIL Property color value 'color-mix(in xyz, color(xyz -2 -3 -4), color(xyz -4 -6 -8))' assert_equals: expected "color(xyz-d65 -3 -4.5 -6)" but got "color(srgb -3 -4.5 -6)"
-FAIL Property color value 'color-mix(in xyz, color(xyz -2 -3 -4 / -5), color(xyz -4 -6 -8 / -10))' assert_equals: expected "color(xyz-d65 0 0 0 / 0)" but got "color(srgb 0 0 0 / 0)"
-FAIL Property color value 'color-mix(in xyz, color(xyz none none none), color(xyz none none none))' assert_equals: expected "color(xyz-d65 none none none)" but got "color(srgb none none none)"
-FAIL Property color value 'color-mix(in xyz, color(xyz none none none), color(xyz .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.5 0.6 0.7)" but got "color(srgb 0.5 0.6 0.7)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3), color(xyz none none none))' assert_equals: expected "color(xyz-d65 0.1 0.2 0.3)" but got "color(srgb 0.1 0.2 0.3)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 none), color(xyz .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.3 0.4 0.7)" but got "color(srgb 0.3 0.4 0.7)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3), color(xyz .5 .6 none))' assert_equals: expected "color(xyz-d65 0.3 0.4 0.3)" but got "color(srgb 0.3 0.4 0.3)"
-FAIL Property color value 'color-mix(in xyz, color(xyz none .2 .3), color(xyz .5 none .7))' assert_equals: expected "color(xyz-d65 0.5 0.2 0.5)" but got "color(srgb 0.5 0.2 0.5)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / none), color(xyz .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.3 0.4 0.5)" but got "color(srgb 0.3 0.4 0.5)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / none), color(xyz .5 .6 .7 / 0.5))' assert_equals: expected "color(xyz-d65 0.3 0.4 0.5 / 0.5)" but got "color(srgb 0.233333 0.333333 0.433333 / 0.75)"
-FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / none), color(xyz .5 .6 .7 / none))' assert_equals: expected "color(xyz-d65 0.3 0.4 0.5 / none)" but got "color(srgb 0.3 0.4 0.5 / none)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), color(xyz-d50 .5 .6 .7))' assert_equals: expected "color(xyz-d50 0.3 0.4 0.5)" but got "color(srgb 0.3 0.4 0.5)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 25%, color(xyz-d50 .5 .6 .7))' assert_equals: expected "color(xyz-d50 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in xyz-d50, 25% color(xyz-d50 .1 .2 .3), color(xyz-d50 .5 .6 .7))' assert_equals: expected "color(xyz-d50 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), color(xyz-d50 .5 .6 .7) 25%)' assert_equals: expected "color(xyz-d50 0.2 0.3 0.4)" but got "color(srgb 0.2 0.3 0.4)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), 25% color(xyz-d50 .5 .6 .7))' assert_equals: expected "color(xyz-d50 0.2 0.3 0.4)" but got "color(srgb 0.2 0.3 0.4)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 25%, color(xyz-d50 .5 .6 .7) 75%)' assert_equals: expected "color(xyz-d50 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 30%, color(xyz-d50 .5 .6 .7) 90%)' assert_equals: expected "color(xyz-d50 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 12.5%, color(xyz-d50 .5 .6 .7) 37.5%)' assert_equals: expected "color(xyz-d50 0.4 0.5 0.6 / 0.5)" but got "color(srgb 0.4 0.5 0.6 / 0.5)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 0%, color(xyz-d50 .5 .6 .7))' assert_equals: expected "color(xyz-d50 0.5 0.6 0.7)" but got "color(srgb 0.5 0.6 0.7)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .5), color(xyz-d50 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d50 0.3461539 0.4461539 0.5461539 / 0.65)" but got "color(srgb 0.346154 0.446154 0.546154 / 0.65)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 25%, color(xyz-d50 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d50 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in xyz-d50, 25% color(xyz-d50 .1 .2 .3 / .4), color(xyz-d50 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d50 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4), color(xyz-d50 .5 .6 .7 / .8) 25%)' assert_equals: expected "color(xyz-d50 0.26000002 0.36 0.46 / 0.5)" but got "color(srgb 0.26 0.36 0.46 / 0.5)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4), 25% color(xyz-d50 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d50 0.26000002 0.36 0.46 / 0.5)" but got "color(srgb 0.26 0.36 0.46 / 0.5)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 25%, color(xyz-d50 .5 .6 .7 / .8) 75%)' assert_equals: expected "color(xyz-d50 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 30%, color(xyz-d50 .5 .6 .7 / .8) 90%)' assert_equals: expected "color(xyz-d50 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 12.5%, color(xyz-d50 .5 .6 .7 / .8) 37.5%)' assert_equals: expected "color(xyz-d50 0.44285715 0.54285717 0.64285713 / 0.35)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.35)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 0%, color(xyz-d50 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d50 0.5 0.6 0.7 / 0.8)" but got "color(srgb 0.5 0.6 0.7 / 0.8)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 2 3 4 / 5), color(xyz-d50 4 6 8 / 10))' assert_equals: expected "color(xyz-d50 3 4.5 6)" but got "color(srgb 3 4.5 6)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 -2 -3 -4), color(xyz-d50 -4 -6 -8))' assert_equals: expected "color(xyz-d50 -3 -4.5 -6)" but got "color(srgb -3 -4.5 -6)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 -2 -3 -4 / -5), color(xyz-d50 -4 -6 -8 / -10))' assert_equals: expected "color(xyz-d50 0 0 0 / 0)" but got "color(srgb 0 0 0 / 0)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 none none none), color(xyz-d50 none none none))' assert_equals: expected "color(xyz-d50 none none none)" but got "color(srgb none none none)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 none none none), color(xyz-d50 .5 .6 .7))' assert_equals: expected "color(xyz-d50 0.5 0.6 0.7)" but got "color(srgb 0.5 0.6 0.7)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), color(xyz-d50 none none none))' assert_equals: expected "color(xyz-d50 0.1 0.2 0.3)" but got "color(srgb 0.1 0.2 0.3)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 none), color(xyz-d50 .5 .6 .7))' assert_equals: expected "color(xyz-d50 0.3 0.4 0.7)" but got "color(srgb 0.3 0.4 0.7)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), color(xyz-d50 .5 .6 none))' assert_equals: expected "color(xyz-d50 0.3 0.4 0.3)" but got "color(srgb 0.3 0.4 0.3)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 none .2 .3), color(xyz-d50 .5 none .7))' assert_equals: expected "color(xyz-d50 0.5 0.2 0.5)" but got "color(srgb 0.5 0.2 0.5)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / none), color(xyz-d50 .5 .6 .7))' assert_equals: expected "color(xyz-d50 0.3 0.4 0.5)" but got "color(srgb 0.3 0.4 0.5)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / none), color(xyz-d50 .5 .6 .7 / 0.5))' assert_equals: expected "color(xyz-d50 0.3 0.4 0.5 / 0.5)" but got "color(srgb 0.233333 0.333333 0.433333 / 0.75)"
-FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / none), color(xyz-d50 .5 .6 .7 / none))' assert_equals: expected "color(xyz-d50 0.3 0.4 0.5 / none)" but got "color(srgb 0.3 0.4 0.5 / none)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), color(xyz-d65 .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.3 0.4 0.5)" but got "color(srgb 0.3 0.4 0.5)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 25%, color(xyz-d65 .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in xyz-d65, 25% color(xyz-d65 .1 .2 .3), color(xyz-d65 .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), color(xyz-d65 .5 .6 .7) 25%)' assert_equals: expected "color(xyz-d65 0.2 0.3 0.4)" but got "color(srgb 0.2 0.3 0.4)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), 25% color(xyz-d65 .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.2 0.3 0.4)" but got "color(srgb 0.2 0.3 0.4)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 25%, color(xyz-d65 .5 .6 .7) 75%)' assert_equals: expected "color(xyz-d65 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 30%, color(xyz-d65 .5 .6 .7) 90%)' assert_equals: expected "color(xyz-d65 0.4 0.5 0.6)" but got "color(srgb 0.4 0.5 0.6)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 12.5%, color(xyz-d65 .5 .6 .7) 37.5%)' assert_equals: expected "color(xyz-d65 0.4 0.5 0.6 / 0.5)" but got "color(srgb 0.4 0.5 0.6 / 0.5)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 0%, color(xyz-d65 .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.5 0.6 0.7)" but got "color(srgb 0.5 0.6 0.7)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .5), color(xyz-d65 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.3461539 0.4461539 0.5461539 / 0.65)" but got "color(srgb 0.346154 0.446154 0.546154 / 0.65)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 25%, color(xyz-d65 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in xyz-d65, 25% color(xyz-d65 .1 .2 .3 / .4), color(xyz-d65 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4), color(xyz-d65 .5 .6 .7 / .8) 25%)' assert_equals: expected "color(xyz-d65 0.26000002 0.36 0.46 / 0.5)" but got "color(srgb 0.26 0.36 0.46 / 0.5)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4), 25% color(xyz-d65 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.26000002 0.36 0.46 / 0.5)" but got "color(srgb 0.26 0.36 0.46 / 0.5)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 25%, color(xyz-d65 .5 .6 .7 / .8) 75%)' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 30%, color(xyz-d65 .5 .6 .7 / .8) 90%)' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.7)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 12.5%, color(xyz-d65 .5 .6 .7 / .8) 37.5%)' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.35)" but got "color(srgb 0.442857 0.542857 0.642857 / 0.35)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 0%, color(xyz-d65 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.5 0.6 0.7 / 0.8)" but got "color(srgb 0.5 0.6 0.7 / 0.8)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 2 3 4 / 5), color(xyz-d65 4 6 8 / 10))' assert_equals: expected "color(xyz-d65 3 4.5 6)" but got "color(srgb 3 4.5 6)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 -2 -3 -4), color(xyz-d65 -4 -6 -8))' assert_equals: expected "color(xyz-d65 -3 -4.5 -6)" but got "color(srgb -3 -4.5 -6)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 -2 -3 -4 / -5), color(xyz-d65 -4 -6 -8 / -10))' assert_equals: expected "color(xyz-d65 0 0 0 / 0)" but got "color(srgb 0 0 0 / 0)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 none none none), color(xyz-d65 none none none))' assert_equals: expected "color(xyz-d65 none none none)" but got "color(srgb none none none)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 none none none), color(xyz-d65 .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.5 0.6 0.7)" but got "color(srgb 0.5 0.6 0.7)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), color(xyz-d65 none none none))' assert_equals: expected "color(xyz-d65 0.1 0.2 0.3)" but got "color(srgb 0.1 0.2 0.3)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 none), color(xyz-d65 .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.3 0.4 0.7)" but got "color(srgb 0.3 0.4 0.7)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), color(xyz-d65 .5 .6 none))' assert_equals: expected "color(xyz-d65 0.3 0.4 0.3)" but got "color(srgb 0.3 0.4 0.3)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 none .2 .3), color(xyz-d65 .5 none .7))' assert_equals: expected "color(xyz-d65 0.5 0.2 0.5)" but got "color(srgb 0.5 0.2 0.5)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / none), color(xyz-d65 .5 .6 .7))' assert_equals: expected "color(xyz-d65 0.3 0.4 0.5)" but got "color(srgb 0.3 0.4 0.5)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / none), color(xyz-d65 .5 .6 .7 / 0.5))' assert_equals: expected "color(xyz-d65 0.3 0.4 0.5 / 0.5)" but got "color(srgb 0.233333 0.333333 0.433333 / 0.75)"
-FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / none), color(xyz-d65 .5 .6 .7 / none))' assert_equals: expected "color(xyz-d65 0.3 0.4 0.5 / none)" but got "color(srgb 0.3 0.4 0.5 / none)"
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), color(srgb-linear .5 .6 .7))'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 25%, color(srgb-linear .5 .6 .7))'
+PASS Property color value 'color-mix(in srgb-linear, 25% color(srgb-linear .1 .2 .3), color(srgb-linear .5 .6 .7))'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), color(srgb-linear .5 .6 .7) 25%)'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), 25% color(srgb-linear .5 .6 .7))'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 25%, color(srgb-linear .5 .6 .7) 75%)'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 30%, color(srgb-linear .5 .6 .7) 90%)'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 12.5%, color(srgb-linear .5 .6 .7) 37.5%)'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 0%, color(srgb-linear .5 .6 .7))'
+FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .5), color(srgb-linear .5 .6 .7 / .8))' assert_equals: expected "color(srgb-linear 0.3461539 0.4461539 0.5461539 / 0.65)" but got "color(srgb-linear 0.346154 0.446154 0.546154 / 0.65)"
+FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 25%, color(srgb-linear .5 .6 .7 / .8))' assert_equals: expected "color(srgb-linear 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb-linear 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in srgb-linear, 25% color(srgb-linear .1 .2 .3 / .4), color(srgb-linear .5 .6 .7 / .8))' assert_equals: expected "color(srgb-linear 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb-linear 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4), color(srgb-linear .5 .6 .7 / .8) 25%)' assert_equals: expected "color(srgb-linear 0.26000002 0.36 0.46 / 0.5)" but got "color(srgb-linear 0.26 0.36 0.46 / 0.5)"
+FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4), 25% color(srgb-linear .5 .6 .7 / .8))' assert_equals: expected "color(srgb-linear 0.26000002 0.36 0.46 / 0.5)" but got "color(srgb-linear 0.26 0.36 0.46 / 0.5)"
+FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 25%, color(srgb-linear .5 .6 .7 / .8) 75%)' assert_equals: expected "color(srgb-linear 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb-linear 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 30%, color(srgb-linear .5 .6 .7 / .8) 90%)' assert_equals: expected "color(srgb-linear 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(srgb-linear 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 12.5%, color(srgb-linear .5 .6 .7 / .8) 37.5%)' assert_equals: expected "color(srgb-linear 0.44285715 0.54285717 0.64285713 / 0.35)" but got "color(srgb-linear 0.442857 0.542857 0.642857 / 0.35)"
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 0%, color(srgb-linear .5 .6 .7 / .8))'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear 2 3 4 / 5), color(srgb-linear 4 6 8 / 10))'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear -2 -3 -4), color(srgb-linear -4 -6 -8))'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear -2 -3 -4 / -5), color(srgb-linear -4 -6 -8 / -10))'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear none none none), color(srgb-linear none none none))'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear none none none), color(srgb-linear .5 .6 .7))'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), color(srgb-linear none none none))'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 none), color(srgb-linear .5 .6 .7))'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), color(srgb-linear .5 .6 none))'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear none .2 .3), color(srgb-linear .5 none .7))'
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / none), color(srgb-linear .5 .6 .7))'
+FAIL Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / none), color(srgb-linear .5 .6 .7 / 0.5))' assert_equals: expected "color(srgb-linear 0.3 0.4 0.5 / 0.5)" but got "color(srgb-linear 0.233333 0.333333 0.433333 / 0.75)"
+PASS Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / none), color(srgb-linear .5 .6 .7 / none))'
+PASS Property color value 'color-mix(in xyz, color(xyz .1 .2 .3), color(xyz .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz, color(xyz .1 .2 .3) 25%, color(xyz .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz, 25% color(xyz .1 .2 .3), color(xyz .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz, color(xyz .1 .2 .3), color(xyz .5 .6 .7) 25%)'
+PASS Property color value 'color-mix(in xyz, color(xyz .1 .2 .3), 25% color(xyz .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz, color(xyz .1 .2 .3) 25%, color(xyz .5 .6 .7) 75%)'
+PASS Property color value 'color-mix(in xyz, color(xyz .1 .2 .3) 30%, color(xyz .5 .6 .7) 90%)'
+PASS Property color value 'color-mix(in xyz, color(xyz .1 .2 .3) 12.5%, color(xyz .5 .6 .7) 37.5%)'
+PASS Property color value 'color-mix(in xyz, color(xyz .1 .2 .3) 0%, color(xyz .5 .6 .7))'
+FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .5), color(xyz .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.3461539 0.4461539 0.5461539 / 0.65)" but got "color(xyz-d65 0.346154 0.446154 0.546154 / 0.65)"
+FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .4) 25%, color(xyz .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(xyz-d65 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in xyz, 25% color(xyz .1 .2 .3 / .4), color(xyz .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(xyz-d65 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .4), color(xyz .5 .6 .7 / .8) 25%)' assert_equals: expected "color(xyz-d65 0.26000002 0.36 0.46 / 0.5)" but got "color(xyz-d65 0.26 0.36 0.46 / 0.5)"
+FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .4), 25% color(xyz .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.26000002 0.36 0.46 / 0.5)" but got "color(xyz-d65 0.26 0.36 0.46 / 0.5)"
+FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .4) 25%, color(xyz .5 .6 .7 / .8) 75%)' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(xyz-d65 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .4) 30%, color(xyz .5 .6 .7 / .8) 90%)' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(xyz-d65 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .4) 12.5%, color(xyz .5 .6 .7 / .8) 37.5%)' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.35)" but got "color(xyz-d65 0.442857 0.542857 0.642857 / 0.35)"
+PASS Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / .4) 0%, color(xyz .5 .6 .7 / .8))'
+PASS Property color value 'color-mix(in xyz, color(xyz 2 3 4 / 5), color(xyz 4 6 8 / 10))'
+PASS Property color value 'color-mix(in xyz, color(xyz -2 -3 -4), color(xyz -4 -6 -8))'
+PASS Property color value 'color-mix(in xyz, color(xyz -2 -3 -4 / -5), color(xyz -4 -6 -8 / -10))'
+PASS Property color value 'color-mix(in xyz, color(xyz none none none), color(xyz none none none))'
+PASS Property color value 'color-mix(in xyz, color(xyz none none none), color(xyz .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz, color(xyz .1 .2 .3), color(xyz none none none))'
+PASS Property color value 'color-mix(in xyz, color(xyz .1 .2 none), color(xyz .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz, color(xyz .1 .2 .3), color(xyz .5 .6 none))'
+PASS Property color value 'color-mix(in xyz, color(xyz none .2 .3), color(xyz .5 none .7))'
+PASS Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / none), color(xyz .5 .6 .7))'
+FAIL Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / none), color(xyz .5 .6 .7 / 0.5))' assert_equals: expected "color(xyz-d65 0.3 0.4 0.5 / 0.5)" but got "color(xyz-d65 0.233333 0.333333 0.433333 / 0.75)"
+PASS Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / none), color(xyz .5 .6 .7 / none))'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), color(xyz-d50 .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 25%, color(xyz-d50 .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz-d50, 25% color(xyz-d50 .1 .2 .3), color(xyz-d50 .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), color(xyz-d50 .5 .6 .7) 25%)'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), 25% color(xyz-d50 .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 25%, color(xyz-d50 .5 .6 .7) 75%)'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 30%, color(xyz-d50 .5 .6 .7) 90%)'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 12.5%, color(xyz-d50 .5 .6 .7) 37.5%)'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 0%, color(xyz-d50 .5 .6 .7))'
+FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .5), color(xyz-d50 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d50 0.3461539 0.4461539 0.5461539 / 0.65)" but got "color(xyz-d50 0.346154 0.446154 0.546154 / 0.65)"
+FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 25%, color(xyz-d50 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d50 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(xyz-d50 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in xyz-d50, 25% color(xyz-d50 .1 .2 .3 / .4), color(xyz-d50 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d50 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(xyz-d50 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4), color(xyz-d50 .5 .6 .7 / .8) 25%)' assert_equals: expected "color(xyz-d50 0.26000002 0.36 0.46 / 0.5)" but got "color(xyz-d50 0.26 0.36 0.46 / 0.5)"
+FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4), 25% color(xyz-d50 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d50 0.26000002 0.36 0.46 / 0.5)" but got "color(xyz-d50 0.26 0.36 0.46 / 0.5)"
+FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 25%, color(xyz-d50 .5 .6 .7 / .8) 75%)' assert_equals: expected "color(xyz-d50 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(xyz-d50 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 30%, color(xyz-d50 .5 .6 .7 / .8) 90%)' assert_equals: expected "color(xyz-d50 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(xyz-d50 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 12.5%, color(xyz-d50 .5 .6 .7 / .8) 37.5%)' assert_equals: expected "color(xyz-d50 0.44285715 0.54285717 0.64285713 / 0.35)" but got "color(xyz-d50 0.442857 0.542857 0.642857 / 0.35)"
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 0%, color(xyz-d50 .5 .6 .7 / .8))'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 2 3 4 / 5), color(xyz-d50 4 6 8 / 10))'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 -2 -3 -4), color(xyz-d50 -4 -6 -8))'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 -2 -3 -4 / -5), color(xyz-d50 -4 -6 -8 / -10))'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 none none none), color(xyz-d50 none none none))'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 none none none), color(xyz-d50 .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), color(xyz-d50 none none none))'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 none), color(xyz-d50 .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), color(xyz-d50 .5 .6 none))'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 none .2 .3), color(xyz-d50 .5 none .7))'
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / none), color(xyz-d50 .5 .6 .7))'
+FAIL Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / none), color(xyz-d50 .5 .6 .7 / 0.5))' assert_equals: expected "color(xyz-d50 0.3 0.4 0.5 / 0.5)" but got "color(xyz-d50 0.233333 0.333333 0.433333 / 0.75)"
+PASS Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / none), color(xyz-d50 .5 .6 .7 / none))'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), color(xyz-d65 .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 25%, color(xyz-d65 .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz-d65, 25% color(xyz-d65 .1 .2 .3), color(xyz-d65 .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), color(xyz-d65 .5 .6 .7) 25%)'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), 25% color(xyz-d65 .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 25%, color(xyz-d65 .5 .6 .7) 75%)'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 30%, color(xyz-d65 .5 .6 .7) 90%)'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 12.5%, color(xyz-d65 .5 .6 .7) 37.5%)'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 0%, color(xyz-d65 .5 .6 .7))'
+FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .5), color(xyz-d65 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.3461539 0.4461539 0.5461539 / 0.65)" but got "color(xyz-d65 0.346154 0.446154 0.546154 / 0.65)"
+FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 25%, color(xyz-d65 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(xyz-d65 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in xyz-d65, 25% color(xyz-d65 .1 .2 .3 / .4), color(xyz-d65 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(xyz-d65 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4), color(xyz-d65 .5 .6 .7 / .8) 25%)' assert_equals: expected "color(xyz-d65 0.26000002 0.36 0.46 / 0.5)" but got "color(xyz-d65 0.26 0.36 0.46 / 0.5)"
+FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4), 25% color(xyz-d65 .5 .6 .7 / .8))' assert_equals: expected "color(xyz-d65 0.26000002 0.36 0.46 / 0.5)" but got "color(xyz-d65 0.26 0.36 0.46 / 0.5)"
+FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 25%, color(xyz-d65 .5 .6 .7 / .8) 75%)' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(xyz-d65 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 30%, color(xyz-d65 .5 .6 .7 / .8) 90%)' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.7)" but got "color(xyz-d65 0.442857 0.542857 0.642857 / 0.7)"
+FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 12.5%, color(xyz-d65 .5 .6 .7 / .8) 37.5%)' assert_equals: expected "color(xyz-d65 0.44285715 0.54285717 0.64285713 / 0.35)" but got "color(xyz-d65 0.442857 0.542857 0.642857 / 0.35)"
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 0%, color(xyz-d65 .5 .6 .7 / .8))'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 2 3 4 / 5), color(xyz-d65 4 6 8 / 10))'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 -2 -3 -4), color(xyz-d65 -4 -6 -8))'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 -2 -3 -4 / -5), color(xyz-d65 -4 -6 -8 / -10))'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 none none none), color(xyz-d65 none none none))'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 none none none), color(xyz-d65 .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), color(xyz-d65 none none none))'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 none), color(xyz-d65 .5 .6 .7))'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), color(xyz-d65 .5 .6 none))'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 none .2 .3), color(xyz-d65 .5 none .7))'
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / none), color(xyz-d65 .5 .6 .7))'
+FAIL Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / none), color(xyz-d65 .5 .6 .7 / 0.5))' assert_equals: expected "color(xyz-d65 0.3 0.4 0.5 / 0.5)" but got "color(xyz-d65 0.233333 0.333333 0.433333 / 0.75)"
+PASS Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / none), color(xyz-d65 .5 .6 .7 / none))'
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/navigation-timing-sizes.https.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/navigation-timing-sizes.https.html
new file mode 100644
index 0000000..19c254c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/navigation-timing-sizes.https.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/utils.sub.js"></script>
+
+<meta name="variant" content="">
+<meta name="variant" content="?bypass_cache=true">
+<meta name="variant" content="?prefetch=true">
+<meta name="variant" content="?prefetch=true&bypass_cache=true">
+
+<script>
+const searchParams = new URLSearchParams(location.search);
+const prefetchEnabled = searchParams.has('prefetch');
+const bypassCache = searchParams.has('bypass_cache');
+
+// Header size: https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-transfersize
+const headerSize = 300;
+
+promise_test(async t => {
+    assert_implements(HTMLScriptElement.supports('speculationrules'),
+      "Speculation Rules not supported");
+
+    const agent = await spawnWindow(t);
+    // Some meaningless query param to avoid cached response.
+    const prefetchUrl =
+      bypassCache ? agent.getExecutorURL({ a: "b" }) : agent.getExecutorURL();
+
+    if (prefetchEnabled)
+      await agent.forceSinglePrefetch(prefetchUrl);
+
+    await agent.navigate(prefetchUrl);
+
+    if (prefetchEnabled)
+      assert_prefetched(await agent.getRequestHeaders(),
+        `Prefetch ${prefetchUrl.href} should work.`);
+    else
+      assert_not_prefetched(await agent.getRequestHeaders(),
+        `${prefetchUrl.href} should not be prefetched.`);
+
+    await agent.execute_script(
+      () => window.entries = performance.getEntriesByType('navigation'));
+
+    // TODO(crbug/1317756): Currently the initial prefetch request bypasses the
+    // HTTP cache. Expand test coverage for cache and cache+revalidation cases.
+    //
+    // We do not assert the exact size of `resources/executor.sub.html` since it
+    // would be a headache to update this test everytime executor.sub.html
+    // changes.
+    assert_equals(await agent.execute_script(() => window.entries.length), 1,
+      'Wrong number of entries');
+    const entry =
+      await agent.execute_script(() => window.entries[0]);
+    const bodySize = entry.encodedBodySize;
+    assert_greater_than(bodySize, 0);
+    assert_equals(entry.transferSize, headerSize + bodySize);
+    assert_equals(entry.decodedBodySize, bodySize);
+  }, `PerformanceNavigationTiming.transferSize/encodedBodySize/decodedBodySize test, same origin prefetch.`);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/referrer-policy-from-rules.https.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/referrer-policy-from-rules.https.html
new file mode 100644
index 0000000..01e32ba
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/referrer-policy-from-rules.https.html
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+<title>Prefetch with the referrer policy specified in speculation rules</title>
+
+<!--Split test cases due to the use of timeouts in speculation rules test utilities.-->
+<meta name="variant" content="?1-1">
+<meta name="variant" content="?2-2">
+<meta name="variant" content="?3-3">
+<meta name="variant" content="?4-4">
+<meta name="variant" content="?5-last">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/subset-tests.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/utils.sub.js"></script>
+
+<script>
+"use strict";
+
+subsetTest(promise_test, async t => {
+  assert_implements(HTMLScriptElement.supports('speculationrules'), "Speculation Rules not supported");
+
+  const agent = await spawnWindow(t);
+  await agent.setReferrerPolicy("strict-origin-when-cross-origin");
+  const expectedReferrer = agent.getExecutorURL().origin + "/";
+
+  const nextURL = agent.getExecutorURL({ page: 2 });
+  await agent.forceSinglePrefetch(nextURL, { referrer_policy: "strict-origin" });
+  await agent.navigate(nextURL);
+
+  const headers = await agent.getRequestHeaders();
+  assert_prefetched(headers, "must be prefetched");
+  assert_equals(headers.referer, expectedReferrer, "must send the origin as the referrer");
+}, 'with "strict-origin" referrer policy in rule set overriding "strict-origin-when-cross-origin" of referring page');
+
+subsetTest(promise_test, async t => {
+  assert_implements(HTMLScriptElement.supports('speculationrules'), "Speculation Rules not supported");
+
+  const agent = await spawnWindow(t);
+  await agent.setReferrerPolicy("unsafe-url");
+
+  const nextURL = agent.getExecutorURL({ page: 2 });
+  await agent.forceSinglePrefetch(nextURL, { referrer_policy: "no-referrer" });
+  await agent.navigate(nextURL);
+
+  // This referring page's referrer policy would not be eligible for
+  // prefetching, but setting a sufficiently strict policy in the rule allows
+  // for prefetching.
+  // TODO(crbug.com/1379846): This test will be trivialized once the prefetch
+  // referrer policy requirements are relaxed for same-site.
+  const headers = await agent.getRequestHeaders();
+  assert_prefetched(headers, "must be prefetched");
+  assert_equals(headers.referer, '', "must send no referrer");
+}, 'with "no-referrer" referrer policy in rule set overriding "unsafe-url" of referring page');
+
+subsetTest(promise_test, async t => {
+  assert_implements(HTMLScriptElement.supports('speculationrules'), "Speculation Rules not supported");
+
+  const agent = await spawnWindow(t);
+  await agent.setReferrerPolicy("strict-origin-when-cross-origin");
+
+  const nextURL = agent.getExecutorURL({ page: 2 });
+  await agent.forceSinglePrefetch(nextURL, { referrer_policy: "no-referrrrrrrer" });
+  await agent.navigate(nextURL);
+
+  const headers = await agent.getRequestHeaders();
+  assert_not_prefetched(headers, "must not be prefetched");
+}, 'unrecognized policies invalidate the rule');
+
+subsetTest(promise_test, async t => {
+  assert_implements(HTMLScriptElement.supports('speculationrules'), "Speculation Rules not supported");
+
+  const agent = await spawnWindow(t);
+  await agent.setReferrerPolicy("strict-origin-when-cross-origin");
+
+  const nextURL = agent.getExecutorURL({ page: 2 });
+  await agent.forceSinglePrefetch(nextURL, { referrer_policy: "never" });
+  await agent.navigate(nextURL);
+
+  const headers = await agent.getRequestHeaders();
+  assert_not_prefetched(headers, "must not be prefetched");
+}, 'treat legacy referrer policy values as invalid');
+
+subsetTest(promise_test, async t => {
+  assert_implements(HTMLScriptElement.supports('speculationrules'), "Speculation Rules not supported");
+
+  const agent = await spawnWindow(t);
+  await agent.setReferrerPolicy("strict-origin");
+  const expectedReferrer = agent.getExecutorURL().origin + "/";
+
+  const nextURL = agent.getExecutorURL({ page: 2 });
+  await agent.forceSinglePrefetch(nextURL, { referrer_policy: "unsafe-url" });
+  await agent.navigate(nextURL);
+
+  // This referring page's referrer policy would normally make it eligible for
+  // prefetching, but setting an unacceptable policy in the rule makes it ineligible.
+  // TODO(crbug.com/1379846): This test will be invalidated once the prefetch
+  // referrer policy requirements are relaxed for same-site.
+  const headers = await agent.getRequestHeaders();
+  assert_not_prefetched(headers, "must not be prefetched");
+  assert_equals(headers.referer, expectedReferrer, "must send the origin as the referrer");
+}, 'with "unsafe-url" referrer policy in rule set overriding "strict-origin" of referring page');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/referrer-policy-from-rules.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/referrer-policy-from-rules.html
new file mode 100644
index 0000000..b19c5ddd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/referrer-policy-from-rules.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta name="timeout" content="long">
+<title>Prerender with the referrer policy specified in speculation rules</title>
+
+<!--Split test cases due to the use of timeouts in test utilities.-->
+<meta name="variant" content="?1-1">
+<meta name="variant" content="?2-last">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/subset-tests.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"></script>
+<script src="resources/utils.js"></script>
+
+<script>
+setup(() => assertSpeculationRulesIsSupported());
+
+subsetTest(promise_test, async t => {
+  const {exec, tryToActivate} = await create_prerendered_page(
+      t, {},
+      {referrer_policy: 'strict-origin-when-cross-origin'},
+      {referrer_policy: 'strict-origin'});
+
+  const actualReferrer = await exec(() => { return document.referrer; });
+  const expectedReferrer = location.origin + "/";
+  assert_equals(actualReferrer, expectedReferrer, 'must send the origin as the referrer');
+
+  const result = await tryToActivate();
+  assert_equals(result, 'activated');
+}, 'with "strict-origin" referrer policy in rule set overriding "strict-origin-when-cross-origin" of referring page');
+
+subsetTest(promise_test, async t => {
+  const {exec, tryToActivate} = await create_prerendered_page(
+      t, {},
+      {referrer_policy: 'strict-origin-when-cross-origin'},
+      {referrer_policy: 'no-referrrrrrrer'});
+  const result = await tryToActivate();
+  assert_equals(result, 'discarded');
+}, 'unrecognized policies invalidate the rule');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/utils.js b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/utils.js
index baaf6cf..4ad5150 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/utils.js
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/resources/utils.js
@@ -183,7 +183,12 @@
     });
 }
 
-async function create_prerendered_page(t, opt = {}, init_opt = {}) {
+// `opt` provides additional query params for the prerendered URL.
+// `init_opt` provides additional query params for the page that triggers
+// the prerender.
+// `rule_extras` provides additional parameters for the speculation rule used
+// to trigger prerendering.
+async function create_prerendered_page(t, opt = {}, init_opt = {}, rule_extras = {}) {
   const baseUrl = '/speculation-rules/prerender/resources/exec.py';
   const init_uuid = token();
   const prerender_uuid = token();
@@ -205,16 +210,16 @@
     params.set(p, opt[p]);
   const url = `${baseUrl}?${params.toString()}`;
 
-  await init_remote.execute_script(url => {
+  await init_remote.execute_script((url, rule_extras) => {
       const a = document.createElement('a');
       a.href = url;
       a.innerText = 'Activate';
       document.body.appendChild(a);
       const rules = document.createElement('script');
       rules.type = "speculationrules";
-      rules.text = JSON.stringify({prerender: [{source: 'list', urls: [url]}]});
+      rules.text = JSON.stringify({prerender: [{source: 'list', urls: [url], ...rule_extras}]});
       document.head.appendChild(rules);
-  }, [url]);
+  }, [url, rule_extras]);
 
   await Promise.any([
     prerender_remote.execute_script(() => {
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/utils.js b/third_party/blink/web_tests/external/wpt/webcodecs/utils.js
index 2dcfefa..3637646d6 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/utils.js
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/utils.js
@@ -200,3 +200,9 @@
   new MessageChannel().port1.postMessage(buffer, [buffer]);
   return view;
 }
+
+function isFrameClosed(frame) {
+  return frame.format == null && frame.codedWidth == 0 &&
+         frame.codedHeight == 0 && frame.displayWidth == 0 &&
+         frame.displayHeight == 0;
+}
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/video-frame-serialization.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/video-frame-serialization.any.js
index bec6dfe..9ce6b88e 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/video-frame-serialization.any.js
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/video-frame-serialization.any.js
@@ -37,8 +37,11 @@
 
   frame.close();
 
-  assert_not_equals(copy.timestamp, defaultInit.timestamp);
+  assert_equals(copy.timestamp, defaultInit.timestamp);
+  assert_equals(copy.duration, defaultInit.duration);
+  assert_true(isFrameClosed(copy));
   assert_equals(clone.timestamp, defaultInit.timestamp);
+  assert_false(isFrameClosed(clone));
 
   clone.close();
 }, 'Verify closing a frame doesn\'t affect its clones.');
@@ -88,7 +91,7 @@
   })
 
   localPort.postMessage(localFrame, [localFrame]);
-  assert_not_equals(localFrame.timestamp, defaultInit.timestamp);
+  assert_true(isFrameClosed(localFrame));
 }, 'Verify transferring frames closes them.');
 
 async_test(t => {
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/videoFrame-construction.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/videoFrame-construction.any.js
index bc909ea..8f71a95 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/videoFrame-construction.any.js
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/videoFrame-construction.any.js
@@ -18,12 +18,12 @@
 
 test(t => {
   let image = makeImageBitmap(32, 16);
-  let frame = new VideoFrame(image, {timestamp: 10});
+  let frame = new VideoFrame(image, {timestamp: 10, duration: 15});
   frame.close();
 
   assert_equals(frame.format, null, 'format')
-  assert_equals(frame.timestamp, null, 'timestamp');
-  assert_equals(frame.duration, null, 'duration');
+  assert_equals(frame.timestamp, 10, 'timestamp');
+  assert_equals(frame.duration, 15, 'duration');
   assert_equals(frame.codedWidth, 0, 'codedWidth');
   assert_equals(frame.codedHeight, 0, 'codedHeight');
   assert_equals(frame.visibleRect, null, 'visibleRect');
@@ -33,6 +33,7 @@
   assert_equals(frame.colorSpace.transfer, null, 'colorSpace.transfer');
   assert_equals(frame.colorSpace.matrix, null, 'colorSpace.matrix');
   assert_equals(frame.colorSpace.fullRange, null, 'colorSpace.fullRange');
+  assert_true(isFrameClosed(frame));
 
   assert_throws_dom('InvalidStateError', () => frame.clone());
 }, 'Test closed VideoFrame.');
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/back/back.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/back/back.py
index 86ad2278..21e8498 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/back/back.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/back/back.py
@@ -33,6 +33,23 @@
     assert_success(response)
 
 
+def test_basic(session, inline):
+    url = inline("<div id=foo>")
+
+    session.url = url
+    session.url = inline("<div id=bar>")
+    element = session.find.css("#bar", all=False)
+
+    response = back(session)
+    assert_success(response)
+
+    with pytest.raises(error.StaleElementReferenceException):
+        element.property("id")
+
+    assert session.url == url
+    assert session.find.css("#foo", all=False)
+
+
 def test_data_urls(session, inline):
     test_pages = [
         inline("<p id=1>"),
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/element_click/click.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/element_click/click.py
index 8abcfe7..bd4e2c68 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/element_click/click.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/element_click/click.py
@@ -41,6 +41,36 @@
     assert_error(response, "no such window")
 
 
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    response = element_click(session, element)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<div id='parent'><p/>")
+    element = session.find.css("#parent", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    response = element_click(session, element)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<div id='parent'><p/>"))
+
+    frame = session.find.css("iframe", all=False)
+    session.switch_frame(frame)
+
+    element = session.find.css("#parent", all=False)
+    session.switch_frame("parent")
+
+    response = element_click(session, element)
+    assert_error(response, "no such element")
+
 @pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
 def test_stale_element_reference(session, stale_element, as_frame):
     element = stale_element("<div>", "div", as_frame=as_frame)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/element_send_keys/send_keys.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/element_send_keys/send_keys.py
index 3cb1c74..5f8d3f01 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/element_send_keys/send_keys.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/element_send_keys/send_keys.py
@@ -54,6 +54,37 @@
     assert_error(response, "no such window")
 
 
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    response = element_send_keys(session, element, "foo")
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<div id='parent'><p/>")
+    element = session.find.css("#parent", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    response = element_send_keys(session, element, "foo")
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<div id='parent'><p/>"))
+
+    frame = session.find.css("iframe", all=False)
+    session.switch_frame(frame)
+
+    element = session.find.css("#parent", all=False)
+    session.switch_frame("parent")
+
+    response = element_send_keys(session, element, "foo")
+    assert_error(response, "no such element")
+
+
 @pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
 def test_stale_element_reference(session, stale_element, as_frame):
     element = stale_element("<input>", "input", as_frame=as_frame)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/execute_async_script/execute_async.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/execute_async_script/execute_async.py
index 8953305..697df72b 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/execute_async_script/execute_async.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/execute_async_script/execute_async.py
@@ -1,5 +1,6 @@
 import pytest
 
+from webdriver import Element
 from webdriver.error import NoSuchAlertException
 from webdriver.transport import Response
 
@@ -24,6 +25,70 @@
     assert_error(response, "no such window")
 
 
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    result = execute_async_script(session, """
+        arguments[1](true);
+        """, args=[element])
+    assert_error(result, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<div id='parent'><p/>")
+    element = session.find.css("#parent", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    result = execute_async_script(session, """
+        arguments[1](true);
+        """, args=[element])
+    assert_error(result, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<div id='parent'><p/>"))
+
+    frame = session.find.css("iframe", all=False)
+    session.switch_frame(frame)
+
+    element = session.find.css("#parent", all=False)
+    session.switch_frame("parent")
+
+    result = execute_async_script(session, """
+        arguments[1](true);
+        """, args=[element])
+    assert_error(result, "no such element")
+
+
+@pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
+def test_stale_element_reference_as_argument(session, stale_element, as_frame):
+    element = stale_element("<div>", "div", as_frame=as_frame)
+
+    result = execute_async_script(session, "arguments[0](1);", args=[element])
+    assert_error(result, "stale element reference")
+
+
+@pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
+def test_stale_element_reference_as_returned_value(session, iframe, inline, as_frame):
+    if as_frame:
+        session.url = inline(iframe("<div>"))
+        frame = session.find.css("iframe", all=False)
+        session.switch_frame(frame)
+    else:
+        session.url = inline("<div>")
+
+    element = session.find.css("div", all=False)
+
+    result = execute_async_script(session, """
+        const [elem, resolve] = arguments;
+        elem.remove();
+        resolve(elem);
+        """, args=[element])
+    assert_error(result, "stale element reference")
+
+
 @pytest.mark.parametrize("dialog_type", ["alert", "confirm", "prompt"])
 def test_abort_by_user_prompt(session, dialog_type):
     response = execute_async_script(
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/execute_script/execute.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/execute_script/execute.py
index a12080c..d1f1f13 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/execute_script/execute.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/execute_script/execute.py
@@ -1,4 +1,6 @@
 import pytest
+
+from webdriver import Element
 from webdriver.error import NoSuchAlertException
 from webdriver.transport import Response
 
@@ -23,6 +25,64 @@
     assert_error(response, "no such window")
 
 
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    result = execute_script(session, "return true;", args=[element])
+    assert_error(result, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<div id='parent'><p/>")
+    element = session.find.css("#parent", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    result = execute_script(session, "return true;", args=[element])
+    assert_error(result, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<div id='parent'><p/>"))
+
+    frame = session.find.css("iframe", all=False)
+    session.switch_frame(frame)
+
+    element = session.find.css("#parent", all=False)
+    session.switch_frame("parent")
+
+    result = execute_script(session, "return true;", args=[element])
+    assert_error(result, "no such element")
+
+
+@pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
+def test_stale_element_reference_as_argument(session, stale_element, as_frame):
+    element = stale_element("<div>", "div", as_frame=as_frame)
+
+    result = execute_script(session, "return 1;", args=[element])
+    assert_error(result, "stale element reference")
+
+
+@pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
+def test_stale_element_reference_as_returned_value(session, iframe, inline, as_frame):
+    if as_frame:
+        session.url = inline(iframe("<div>"))
+        frame = session.find.css("iframe", all=False)
+        session.switch_frame(frame)
+    else:
+        session.url = inline("<div>")
+
+    element = session.find.css("div", all=False)
+
+    result = execute_script(session, """
+        const elem = arguments[0];
+        elem.remove();
+        return elem;
+        """, args=[element])
+    assert_error(result, "stale element reference")
+
+
 def test_opening_new_window_keeps_current_window_handle(session, inline):
     original_handle = session.window_handle
     original_handles = session.handles
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/forward/forward.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/forward/forward.py
index 97a935c8..61c66e2 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/forward/forward.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/forward/forward.py
@@ -29,6 +29,25 @@
     assert_success(response)
 
 
+def test_basic(session, inline):
+    url = inline("<div id=foo>")
+
+    session.url = inline("<div id=bar>")
+    session.url = url
+    session.back()
+
+    element = session.find.css("#bar", all=False)
+
+    response = forward(session)
+    assert_success(response)
+
+    with pytest.raises(error.StaleElementReferenceException):
+        element.property("id")
+
+    assert session.url == url
+    assert session.find.css("#foo", all=False)
+
+
 def test_no_browsing_history(session, inline):
     url = inline("<div id=foo>")
 
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_active_element/get.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_active_element/get.py
index 14544008..2b79ebd 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_active_element/get.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_active_element/get.py
@@ -20,6 +20,19 @@
     assert_error(response, "no such window")
 
 
+def test_no_such_element(session, inline):
+    session.url = inline("<body></body>")
+    session.execute_script("""
+        if (document.body.remove) {
+          document.body.remove();
+        } else {
+          document.body.removeNode(true);
+        }""")
+
+    response = get_active_element(session)
+    assert_error(response, "no such element")
+
+
 def test_success_document(session, inline):
     session.url = inline("""
         <body>
@@ -117,16 +130,3 @@
     response = get_active_element(session)
     element = assert_success(response)
     assert_is_active_element(session, element)
-
-
-def test_missing_document_element(session, inline):
-    session.url = inline("<body></body>")
-    session.execute_script("""
-        if (document.body.remove) {
-          document.body.remove();
-        } else {
-          document.body.removeNode(true);
-        }""")
-
-    response = get_active_element(session)
-    assert_error(response, "no such element")
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_label/get.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_label/get.py
index 12aa82e..901b9809 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_label/get.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_label/get.py
@@ -1,5 +1,6 @@
 import pytest
 
+from webdriver import Element
 from webdriver.error import NoSuchAlertException
 
 from tests.support.asserts import assert_error, assert_success
@@ -17,6 +18,37 @@
     assert_error(response, "no such window")
 
 
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    result = get_computed_label(session, element.id)
+    assert_error(result, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<div id='parent'><p/>")
+    element = session.find.css("#parent", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    result = get_computed_label(session, element.id)
+    assert_error(result, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<div id='parent'><p/>"))
+
+    frame = session.find.css("iframe", all=False)
+    session.switch_frame(frame)
+
+    element = session.find.css("#parent", all=False)
+    session.switch_frame("parent")
+
+    result = get_computed_label(session, element.id)
+    assert_error(result, "no such element")
+
+
 @pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
 def test_stale_element_reference(session, stale_element, as_frame):
     element = stale_element("<input>", "input", as_frame=as_frame)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_role/get.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_role/get.py
index d242f738..84e7ab2 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_role/get.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_role/get.py
@@ -1,5 +1,6 @@
 import pytest
 
+from webdriver import Element
 from webdriver.error import NoSuchAlertException
 
 from tests.support.asserts import assert_error, assert_success
@@ -17,6 +18,37 @@
     assert_error(response, "no such window")
 
 
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    result = get_computed_role(session, element.id)
+    assert_error(result, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<div id='parent'><p/>")
+    element = session.find.css("#parent", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    result = get_computed_role(session, element.id)
+    assert_error(result, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<div id='parent'><p/>"))
+
+    frame = session.find.css("iframe", all=False)
+    session.switch_frame(frame)
+
+    element = session.find.css("#parent", all=False)
+    session.switch_frame("parent")
+
+    result = get_computed_role(session, element.id)
+    assert_error(result, "no such element")
+
+
 @pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
 def test_stale_element_reference(session, stale_element, as_frame):
     element = stale_element("<input>", "input", as_frame=as_frame)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_attribute/get.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_attribute/get.py
index 5119949..1d00307 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_attribute/get.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_attribute/get.py
@@ -1,5 +1,7 @@
 import pytest
 
+from webdriver import Element
+
 from tests.support.asserts import assert_error, assert_success
 
 
@@ -27,10 +29,35 @@
     assert_error(response, "no such window")
 
 
-def test_element_not_found(session):
-    # 13.2 Step 3
-    result = get_element_attribute(session, "foo", "id")
-    assert_error(result, "no such element")
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    response = get_element_attribute(session, element.id, "id")
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<div id='parent'><p/>")
+    element = session.find.css("#parent", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    response = get_element_attribute(session, element.id, "id")
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<div id='parent'><p/>"))
+
+    frame = session.find.css("iframe", all=False)
+    session.switch_frame(frame)
+
+    element = session.find.css("#parent", all=False)
+    session.switch_frame("parent")
+
+    response = get_element_attribute(session, element.id, "id")
+    assert_error(response, "no such element")
 
 
 @pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_css_value/get.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_css_value/get.py
index 5ab0b3b..3d4a3101 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_css_value/get.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_css_value/get.py
@@ -1,5 +1,7 @@
 import pytest
 
+from webdriver import Element
+
 from tests.support.asserts import assert_error, assert_success
 
 
@@ -31,9 +33,35 @@
     assert_error(response, "no such window")
 
 
-def test_element_not_found(session):
-    result = get_element_css_value(session, "foo", "display")
-    assert_error(result, "no such element")
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    response = get_element_css_value(session, element.id, "display")
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<div id='parent'><p/>")
+    element = session.find.css("#parent", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    response = get_element_css_value(session, element.id, "display")
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<div id='parent'><p/>"))
+
+    frame = session.find.css("iframe", all=False)
+    session.switch_frame(frame)
+
+    element = session.find.css("#parent", all=False)
+    session.switch_frame("parent")
+
+    response = get_element_css_value(session, element.id, "display")
+    assert_error(response, "no such element")
 
 
 @pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_property/get.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_property/get.py
index 46755ffc..9223f1df 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_property/get.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_property/get.py
@@ -1,4 +1,5 @@
 import pytest
+
 from webdriver import Element, Frame, ShadowRoot, Window
 
 from tests.support.asserts import assert_error, assert_same_element, assert_success
@@ -29,8 +30,34 @@
     assert_error(response, "no such window")
 
 
-def test_element_not_found(session):
-    response = get_element_property(session, "foo", "id")
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    response = get_element_property(session, element.id, "id")
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<div id='parent'><p/>")
+    element = session.find.css("#parent", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    response = get_element_property(session, element.id, "id")
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<div id='parent'><p/>"))
+
+    frame = session.find.css("iframe", all=False)
+    session.switch_frame(frame)
+
+    element = session.find.css("#parent", all=False)
+    session.switch_frame("parent")
+
+    response = get_element_property(session, element.id, "id")
     assert_error(response, "no such element")
 
 
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_rect/get.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_rect/get.py
index a12b8d4e..97dc7a9 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_rect/get.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_rect/get.py
@@ -1,5 +1,7 @@
 import pytest
 
+from webdriver import Element
+
 from tests.support.asserts import assert_error, assert_success
 from tests.support.helpers import element_rect
 
@@ -31,9 +33,35 @@
     assert_error(response, "no such window")
 
 
-def test_element_not_found(session):
-    result = get_element_rect(session, "foo")
-    assert_error(result, "no such element")
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    response = get_element_rect(session, element.id)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<div id='parent'><p/>")
+    element = session.find.css("#parent", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    response = get_element_rect(session, element.id)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<div id='parent'><p/>"))
+
+    frame = session.find.css("iframe", all=False)
+    session.switch_frame(frame)
+
+    element = session.find.css("#parent", all=False)
+    session.switch_frame("parent")
+
+    response = get_element_rect(session, element.id)
+    assert_error(response, "no such element")
 
 
 @pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_shadow_root/get.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_shadow_root/get.py
index 3cf3c65..70168bb8 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_shadow_root/get.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_shadow_root/get.py
@@ -1,5 +1,7 @@
 import pytest
 
+from webdriver import Element
+
 from tests.support.asserts import assert_error, assert_same_element, assert_success
 
 
@@ -27,9 +29,33 @@
     assert_error(response, "no such window")
 
 
-def test_element_not_found(session):
-    result = get_shadow_root(session, "foo")
-    assert_error(result, "no such element")
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    response = get_shadow_root(session, element.id)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline, checkbox_dom):
+    session.url = inline(checkbox_dom)
+    element = session.find.css("custom-checkbox-element", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    response = get_shadow_root(session, element.id)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline, checkbox_dom):
+    session.url = inline(iframe(checkbox_dom))
+
+    session.switch_frame(0)
+    element = session.find.css("custom-checkbox-element", all=False)
+    session.switch_frame("parent")
+
+    response = get_shadow_root(session, element.id)
+    assert_error(response, "no such element")
 
 
 @pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_tag_name/get.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_tag_name/get.py
index b163a58..1e4d5d9 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_tag_name/get.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_tag_name/get.py
@@ -1,5 +1,7 @@
 import pytest
 
+from webdriver import Element
+
 from tests.support.asserts import assert_error, assert_success
 
 
@@ -27,9 +29,35 @@
     assert_error(response, "no such window")
 
 
-def test_element_not_found(session):
-    result = get_element_tag_name(session, "foo")
-    assert_error(result, "no such element")
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    response = get_element_tag_name(session, element.id)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<div id='parent'><p/>")
+    element = session.find.css("#parent", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    response = get_element_tag_name(session, element.id)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<div id='parent'><p/>"))
+
+    frame = session.find.css("iframe", all=False)
+    session.switch_frame(frame)
+
+    element = session.find.css("#parent", all=False)
+    session.switch_frame("parent")
+
+    response = get_element_tag_name(session, element.id)
+    assert_error(response, "no such element")
 
 
 @pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_text/get.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_text/get.py
index d2a1f18..6f03da9 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_text/get.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_element_text/get.py
@@ -1,5 +1,7 @@
 import pytest
 
+from webdriver import Element
+
 from tests.support.asserts import assert_error, assert_success
 
 
@@ -27,6 +29,37 @@
     assert_error(response, "no such window")
 
 
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    response = get_element_text(session, element.id)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<div id='parent'><p/>")
+    element = session.find.css("#parent", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    response = get_element_text(session, element.id)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<div id='parent'><p/>"))
+
+    frame = session.find.css("iframe", all=False)
+    session.switch_frame(frame)
+
+    element = session.find.css("#parent", all=False)
+    session.switch_frame("parent")
+
+    response = get_element_text(session, element.id)
+    assert_error(response, "no such element")
+
+
 @pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
 def test_stale_element_reference(session, stale_element, as_frame):
     element = stale_element("<input>", "input", as_frame=as_frame)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/is_element_enabled/enabled.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/is_element_enabled/enabled.py
index 5e8548b..fa472a6 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/is_element_enabled/enabled.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/is_element_enabled/enabled.py
@@ -1,5 +1,7 @@
 import pytest
 
+from webdriver import Element
+
 from tests.support.asserts import assert_error, assert_success
 
 
@@ -30,6 +32,37 @@
     assert_error(response, "no such window")
 
 
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    response = is_element_enabled(session, element.id)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<div id='parent'><p/>")
+    element = session.find.css("#parent", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    response = is_element_enabled(session, element.id)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<div id='parent'><p/>"))
+
+    frame = session.find.css("iframe", all=False)
+    session.switch_frame(frame)
+
+    element = session.find.css("#parent", all=False)
+    session.switch_frame("parent")
+
+    response = is_element_enabled(session, element.id)
+    assert_error(response, "no such element")
+
+
 @pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
 def test_stale_element_reference(session, stale_element, as_frame):
     element = stale_element("<input>", "input", as_frame=as_frame)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/is_element_selected/selected.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/is_element_selected/selected.py
index 4dbc7974d..79a4927 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/is_element_selected/selected.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/is_element_selected/selected.py
@@ -1,5 +1,7 @@
 import pytest
 
+from webdriver import Element
+
 from tests.support.asserts import assert_error, assert_success
 
 
@@ -46,6 +48,35 @@
     assert_error(response, "no such window")
 
 
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    response = is_element_selected(session, element.id)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<input>")
+    element = session.find.css("input", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    response = is_element_selected(session, element.id)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<input>"))
+
+    session.switch_frame(0)
+    element = session.find.css("input", all=False)
+    session.switch_frame("parent")
+
+    response = is_element_selected(session, element.id)
+    assert_error(response, "no such element")
+
+
 @pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
 def test_stale_element_reference(session, stale_element, check_doc, as_frame):
     element = stale_element(check_doc, "#checked", as_frame=as_frame)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/navigate_to/navigate.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/navigate_to/navigate.py
index 74ac33c..a367fc10 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/navigate_to/navigate.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/navigate_to/navigate.py
@@ -39,6 +39,22 @@
     assert session.url == doc
 
 
+def test_basic(session, inline):
+    url = inline("<div id=foo>")
+
+    session.url = inline("<div id=bar>")
+    element = session.find.css("#bar", all=False)
+
+    response = navigate_to(session, url)
+    assert_success(response)
+
+    with pytest.raises(error.StaleElementReferenceException):
+        element.property("id")
+
+    assert session.url == url
+    assert session.find.css("#foo", all=False)
+
+
 # Capability needed as long as no valid certificate is available:
 #   https://github.com/web-platform-tests/wpt/issues/28847
 @pytest.mark.capabilities({"acceptInsecureCerts": True})
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/refresh/refresh.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/refresh/refresh.py
index 33595d0..b364713 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/refresh/refresh.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/refresh/refresh.py
@@ -1,6 +1,6 @@
 import pytest
 
-from webdriver.error import NoSuchElementException, StaleElementReferenceException
+from webdriver import error
 
 from tests.support.asserts import assert_error, assert_success
 
@@ -32,7 +32,7 @@
     response = refresh(session)
     assert_success(response)
 
-    with pytest.raises(StaleElementReferenceException):
+    with pytest.raises(error.StaleElementReferenceException):
         element.property("id")
 
     assert session.url == url
@@ -48,7 +48,7 @@
     response = refresh(session)
     assert_success(response)
 
-    with pytest.raises(StaleElementReferenceException):
+    with pytest.raises(error.StaleElementReferenceException):
         element.property("id")
 
     assert session.url == url
@@ -72,7 +72,7 @@
     response = refresh(session)
     assert_success(response)
 
-    with pytest.raises(StaleElementReferenceException):
+    with pytest.raises(error.StaleElementReferenceException):
         element.property("id")
 
     session.find.css("input", all=False)
@@ -106,7 +106,7 @@
     assert session.url == "{}#pushstate".format(pushstate_page)
     assert session.execute_script("return history.state;") == {"foo": "bar"}
 
-    with pytest.raises(StaleElementReferenceException):
+    with pytest.raises(error.StaleElementReferenceException):
         element.property("id")
 
 
@@ -114,7 +114,7 @@
     session.url = inline("<div id=foo>")
 
     session.switch_frame(create_frame())
-    with pytest.raises(NoSuchElementException):
+    with pytest.raises(error.NoSuchElementException):
         session.find.css("#foo", all=False)
 
     response = refresh(session)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/switch_to_frame/switch_webelement.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/switch_to_frame/switch_webelement.py
index 465efb7..5b57186 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/switch_to_frame/switch_webelement.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/switch_to_frame/switch_webelement.py
@@ -18,7 +18,7 @@
     return "<frameset rows='{}'>\n{}</frameset>".format(len(frames) * "*,", "\n".join(frames))
 
 
-def test_frame_id_webelement_no_element_reference(session, inline, iframe):
+def test_frame_id_webelement_no_such_element(session, iframe, inline):
     session.url = inline(iframe("<p>foo"))
     frame = session.find.css("iframe", all=False)
     frame.id = "bar"
@@ -28,7 +28,7 @@
 
 
 @pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
-def test_stale_element_reference(session, iframe, stale_element, as_frame):
+def test_frame_id_webelement_stale_element_reference(session, iframe, stale_element, as_frame):
     frame = stale_element(iframe("<div>"), "iframe", as_frame=as_frame)
 
     result = switch_to_frame(session, frame)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/screenshot.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/screenshot.py
index 79ffa15b..4a248b1 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/screenshot.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/screenshot.py
@@ -1,5 +1,7 @@
 import pytest
 
+from webdriver import Element
+
 from tests.support.asserts import assert_error, assert_success
 from tests.support.image import png_dimensions
 from . import element_dimensions
@@ -30,6 +32,37 @@
     assert png_dimensions(screenshot) == element_dimensions(session, element)
 
 
+def test_no_such_element_with_invalid_value(session):
+    element = Element("foo", session)
+
+    response = take_element_screenshot(session, element.id)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_window_handle(session, inline):
+    session.url = inline("<div id='parent'><p/>")
+    element = session.find.css("#parent", all=False)
+
+    new_handle = session.new_window()
+    session.window_handle = new_handle
+
+    response = take_element_screenshot(session, element.id)
+    assert_error(response, "no such element")
+
+
+def test_no_such_element_from_other_frame(session, iframe, inline):
+    session.url = inline(iframe("<div id='parent'><p/>"))
+
+    frame = session.find.css("iframe", all=False)
+    session.switch_frame(frame)
+
+    element = session.find.css("#parent", all=False)
+    session.switch_frame("parent")
+
+    response = take_element_screenshot(session, element.id)
+    assert_error(response, "no such element")
+
+
 @pytest.mark.parametrize("as_frame", [False, True], ids=["top_context", "child_context"])
 def test_stale_element_reference(session, stale_element, as_frame):
     element = stale_element("<input>", "input", as_frame=as_frame)
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
index 762ec05..50d3ca0 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
@@ -261,6 +261,9 @@
 markerStart
 mask
 maskType
+mathDepth
+mathShift
+mathStyle
 maxBlockSize
 maxHeight
 maxInlineSize
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt
index 67bf409d..24d15c0 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt
@@ -243,6 +243,9 @@
     marker-start
     mask
     mask-type
+    math-depth
+    math-shift
+    math-style
     max-block-size
     max-height
     max-inline-size
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 eb3325e..345b2d5 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
@@ -4571,6 +4571,221 @@
     method constructor
     method open
     setter onstatechange
+interface MathMLElement : Element
+    attribute @@toStringTag
+    getter attributeStyleMap
+    getter autofocus
+    getter dataset
+    getter nonce
+    getter onabort
+    getter onanimationend
+    getter onanimationiteration
+    getter onanimationstart
+    getter onauxclick
+    getter onbeforeinput
+    getter onbeforematch
+    getter onbeforexrselect
+    getter onblur
+    getter oncancel
+    getter oncanplay
+    getter oncanplaythrough
+    getter onchange
+    getter onclick
+    getter onclose
+    getter oncontentvisibilityautostatechanged
+    getter oncontextlost
+    getter oncontextmenu
+    getter oncontextrestored
+    getter oncopy
+    getter oncuechange
+    getter oncut
+    getter ondblclick
+    getter ondrag
+    getter ondragend
+    getter ondragenter
+    getter ondragleave
+    getter ondragover
+    getter ondragstart
+    getter ondrop
+    getter ondurationchange
+    getter onemptied
+    getter onended
+    getter onerror
+    getter onfocus
+    getter onformdata
+    getter ongotpointercapture
+    getter oninput
+    getter oninvalid
+    getter onkeydown
+    getter onkeypress
+    getter onkeyup
+    getter onload
+    getter onloadeddata
+    getter onloadedmetadata
+    getter onloadstart
+    getter onlostpointercapture
+    getter onmousedown
+    getter onmouseenter
+    getter onmouseleave
+    getter onmousemove
+    getter onmouseout
+    getter onmouseover
+    getter onmouseup
+    getter onmousewheel
+    getter onpaste
+    getter onpause
+    getter onplay
+    getter onplaying
+    getter onpointercancel
+    getter onpointerdown
+    getter onpointerenter
+    getter onpointerleave
+    getter onpointermove
+    getter onpointerout
+    getter onpointerover
+    getter onpointerrawupdate
+    getter onpointerup
+    getter onprogress
+    getter onratechange
+    getter onreset
+    getter onresize
+    getter onscroll
+    getter onsecuritypolicyviolation
+    getter onseeked
+    getter onseeking
+    getter onselect
+    getter onselectionchange
+    getter onselectstart
+    getter onslotchange
+    getter onstalled
+    getter onsubmit
+    getter onsuspend
+    getter ontimeupdate
+    getter ontoggle
+    getter ontouchcancel
+    getter ontouchend
+    getter ontouchmove
+    getter ontouchstart
+    getter ontransitioncancel
+    getter ontransitionend
+    getter ontransitionrun
+    getter ontransitionstart
+    getter onvolumechange
+    getter onwaiting
+    getter onwebkitanimationend
+    getter onwebkitanimationiteration
+    getter onwebkitanimationstart
+    getter onwebkittransitionend
+    getter onwheel
+    getter style
+    getter tabIndex
+    method blur
+    method constructor
+    method focus
+    setter autofocus
+    setter nonce
+    setter onabort
+    setter onanimationend
+    setter onanimationiteration
+    setter onanimationstart
+    setter onauxclick
+    setter onbeforeinput
+    setter onbeforematch
+    setter onbeforexrselect
+    setter onblur
+    setter oncancel
+    setter oncanplay
+    setter oncanplaythrough
+    setter onchange
+    setter onclick
+    setter onclose
+    setter oncontentvisibilityautostatechanged
+    setter oncontextlost
+    setter oncontextmenu
+    setter oncontextrestored
+    setter oncopy
+    setter oncuechange
+    setter oncut
+    setter ondblclick
+    setter ondrag
+    setter ondragend
+    setter ondragenter
+    setter ondragleave
+    setter ondragover
+    setter ondragstart
+    setter ondrop
+    setter ondurationchange
+    setter onemptied
+    setter onended
+    setter onerror
+    setter onfocus
+    setter onformdata
+    setter ongotpointercapture
+    setter oninput
+    setter oninvalid
+    setter onkeydown
+    setter onkeypress
+    setter onkeyup
+    setter onload
+    setter onloadeddata
+    setter onloadedmetadata
+    setter onloadstart
+    setter onlostpointercapture
+    setter onmousedown
+    setter onmouseenter
+    setter onmouseleave
+    setter onmousemove
+    setter onmouseout
+    setter onmouseover
+    setter onmouseup
+    setter onmousewheel
+    setter onpaste
+    setter onpause
+    setter onplay
+    setter onplaying
+    setter onpointercancel
+    setter onpointerdown
+    setter onpointerenter
+    setter onpointerleave
+    setter onpointermove
+    setter onpointerout
+    setter onpointerover
+    setter onpointerrawupdate
+    setter onpointerup
+    setter onprogress
+    setter onratechange
+    setter onreset
+    setter onresize
+    setter onscroll
+    setter onsecuritypolicyviolation
+    setter onseeked
+    setter onseeking
+    setter onselect
+    setter onselectionchange
+    setter onselectstart
+    setter onslotchange
+    setter onstalled
+    setter onsubmit
+    setter onsuspend
+    setter ontimeupdate
+    setter ontoggle
+    setter ontouchcancel
+    setter ontouchend
+    setter ontouchmove
+    setter ontouchstart
+    setter ontransitioncancel
+    setter ontransitionend
+    setter ontransitionrun
+    setter ontransitionstart
+    setter onvolumechange
+    setter onwaiting
+    setter onwebkitanimationend
+    setter onwebkitanimationiteration
+    setter onwebkitanimationstart
+    setter onwebkittransitionend
+    setter onwheel
+    setter style
+    setter tabIndex
 interface MediaCapabilities
     attribute @@toStringTag
     method constructor
diff --git a/third_party/ipcz/src/util/safe_math.h b/third_party/ipcz/src/util/safe_math.h
index 3c4c7b39..db32ee3 100644
--- a/third_party/ipcz/src/util/safe_math.h
+++ b/third_party/ipcz/src/util/safe_math.h
@@ -38,16 +38,16 @@
 template <typename T>
 constexpr T CheckAdd(T a, T b) {
   T result;
-  ABSL_HARDENING_ASSERT(
-      !ABSL_PREDICT_FALSE(__builtin_add_overflow(a, b, &result)));
+  const bool did_overflow = ABSL_PREDICT_FALSE(__builtin_add_overflow(a, b, &result));
+  ABSL_HARDENING_ASSERT(!did_overflow);
   return result;
 }
 
 template <typename T>
 constexpr T CheckMul(T a, T b) {
   T result;
-  ABSL_HARDENING_ASSERT(
-      !ABSL_PREDICT_FALSE(__builtin_mul_overflow(a, b, &result)));
+  const bool did_overflow = ABSL_PREDICT_FALSE(__builtin_mul_overflow(a, b, &result));
+  ABSL_HARDENING_ASSERT(!did_overflow);
   return result;
 }
 
diff --git a/third_party/nearby/README.chromium b/third_party/nearby/README.chromium
index b702471..f6f8b99 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: 33c5f44ea1f39cf435ee711a59d73cb946c0ae2a
+Version: 4935a2ffdb17da23a2b21957b8e61a493786d703
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/camera-access-marker.html b/third_party/webxr_test_pages/webxr-samples/proposals/camera-access-marker.html
index 963575a..c702ab3e 100644
--- a/third_party/webxr_test_pages/webxr-samples/proposals/camera-access-marker.html
+++ b/third_party/webxr_test_pages/webxr-samples/proposals/camera-access-marker.html
@@ -134,7 +134,9 @@
         let quadCopyTexIdx = 0;
         let backgroundTexture = null;
         let readPixelBuf = null;
-        let alreadyDoingMarkerDetection = false;
+        let readPixelsStarted = false;
+        let readPixelsDone = false;
+        let pendingMarkerDetection = null;
         let frameNum = 0;
 
         let arc = null;
@@ -144,6 +146,10 @@
         // If requested, use DOM overlay to provide information about the read color:
         const use_dom_overlay = QueryArgs.getBool('useDomOverlay', true);
 
+        // Optionally, enable time traces for use with chrome://tracing . This
+        // is off by default since it spams the console log with timing data.
+        const use_timers = QueryArgs.getBool('useTimers', false);
+
         const textOverlayElement = document.querySelector("#text-overlay");
         if (!textOverlayElement) {
           console.error("#text-overlay element not found!");
@@ -286,7 +292,9 @@
             lastTime = 0;
             readback_pixels = null;
             backgroundTexture = null;
-            alreadyDoingMarkerDetection = false;
+            readPixelsStarted = false;
+            readPixelsDone = false;
+            pendingMarkerDetection = null;
             markersInFrame = [];
         }
 
@@ -335,14 +343,24 @@
 
                 if (frameTimes.length >= frameTimeMax - 1) {
                     const fps = Math.round(1000 * frameTimeMax / frameTimes.reduce((sum, v) => sum + v));
-                   const msg = '' + fps + ' fps';
+                    const msg = '' + fps + ' fps';
                     console.debug(msg);
-                    document.getElementById('text-info').innerText = msg;
+                    if (use_dom_overlay) {
+                      document.getElementById('text-info').innerText = msg;
+                    }
                 }
             }
             lastTime = timestamp;
         }
 
+        function consoleTimeStart(name) {
+          if (use_timers) console.time(name);
+        }
+
+        function consoleTimeEnd(name) {
+          if (use_timers) console.timeEnd(name);
+        }
+
         function onXRFrame(frameTimestamp, frame) {
             ++frameNum;
             let session = frame.session;
@@ -353,59 +371,84 @@
             for (let view of pose.views) {
                 let viewport = session.renderState.baseLayer.getViewport(view);
 
-                let asyncRead = document.getElementById('asyncRead').checked; if (asyncRead) {
+                let asyncRead = document.getElementById('asyncRead').checked;
+                if (asyncRead) {
                     // Use asynchronous readPixels and draw the augmented scene based on the
                     // last-detected marker poses, effectively adding a frame of delay to the
                     // session. If the scene also had elements that are based on poses from the XR
                     // session, such as hit test results, those poses would also need to be saved
                     // to match the marker poses.
 
-                    // Draw the scene based on the last-available camera image (if available) and
-                    // marker poses.
+                    // WebXR rAF for frame N
+                    //   start async readPixels for frame N
+                    //   run marker detection for frame N-1
+                    //   draw scene for frame N-1
+                    //
+                    // WebXR rAF for frame N+1
+                    //   start async readPixels for frame N+1
+                    //   run marker detection for frame N
+                    //   draw scene for frame N
+                    //
+                    // ... etc. There may be be rAF calls where the async readPixels for the
+                    // previous frame hasn't completed yet. In that case, just re-draw the
+                    // scene based on the last-available marker poses, reusing the corresponding
+                    // old camera image as background as appropriate.
+
+                    if (pendingMarkerDetection) {
+                       // We have read the pixels for a previous frame, but haven't started marker
+                       // detection on it yet. It's now time to grab a fresh frame. Marker processing
+                       // will start below.
+                      readPixelsDone = false;
+                    }
+
+                    if (!readPixelsStarted && !readPixelsDone) {
+                      // We're not currently running marker detection. Copy the camera frame and kick
+                      // off the asynchronous readPixels + marker detection for it.
+                      console.debug('start frame ' + frameNum + ' readPixels');
+                      if (!view.camera) return;
+                      if (!copyCameraTexture(view)) return;
+                      readPixelsAndScheduleMarkerDetectionAsync(view);
+                      readPixelsStarted = true;
+
+                      // Update the FPS counter whenever we start processing a fresh image.
+                      showFps(frameTimestamp);
+                    }
+
+                    if (pendingMarkerDetection) {
+                       // Run marker detection on the previous frame synchronously, while the
+                       // asynchronous readPixels for the current frame is happening in the
+                       // background.
+                       pendingMarkerDetection();
+                       pendingMarkerDetection = null;
+                    }
+
+                    // Draw the scene based on the last-available camera image and marker poses.
                     console.debug('draw frame ' + frameNum);
                     const delayImage = document.getElementById('delayImage').checked;
                     drawMarkerScene(session, viewport, view, delayImage);
-
-                    // If we're already running marker detection, ignore this camera frame. The
-                    // marker poses will be updated once the currently-running detection finishes.
-                    if (alreadyDoingMarkerDetection) {
-                        console.debug('skip frame ' + frameNum + ', already doing marker detection');
-                        // Exit early so that we don't update the FPS counter.
-                        // That's supposed to only count frames with updated
-                        // marker pose data.
-                        return;
-                    }
-
-                    // We're not currently running marker detection. Copy the camera frame and run
-                    // asynchronous marker detection on it.
-                    console.debug('start frame ' + frameNum + ' new marker detection');
-                    if (!view.camera) return;
-
-                    if (!copyCameraTexture(view)) return;
-
-                    readPixelsAndDetectMarkersAsync(view);
-                    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
                 } else {
                     // Simple version - copy the camera texture, readPixels, detect markers, draw them.
 
                     if (!view.camera) return;
                     if (!copyCameraTexture(view)) return;
 
+                    consoleTimeStart("readPixelsSync");
                     gl.readPixels(0, 0, scaledWidth, scaledHeight,
                                   gl.RGBA, gl.UNSIGNED_BYTE, readback_pixels);
                     gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+                    consoleTimeEnd("readPixelsSync");
 
                     detectMarkers(new Uint8ClampedArray(readback_pixels.buffer),
                                   scaledWidth, scaledHeight, view);
 
                     drawMarkerScene(session, viewport, view, false);
+
+                    // Update the FPS counter. This is the simple case, there's one processed camera frame
+                    // per rAF callback.
+                    showFps(frameTimestamp);
                 }
             }
 
-            // If we get here, update the FPS counter. This is skipped in case the drawing loop
-            // above did an early return, so we're only counting frames where the image was actually
-            // processed.
-            showFps(frameTimestamp);
 
             // Once per session, check for GL errors and report them. Then turn
             // off this expensive check.
@@ -433,7 +476,6 @@
                                   0, gl.RGBA, gl.UNSIGNED_BYTE, null);
                 }
             }
-            readback_pixels.fill(0);
 
             console.debug('frame ' + frameNum + ' update texture ' + quadCopyTexIdx);
             gl.bindTexture(gl.TEXTURE_2D, quadCopyTextures[quadCopyTexIdx]);
@@ -455,27 +497,39 @@
             return true;
         }
 
-        function readPixelsAndDetectMarkersAsync(view) {
-            alreadyDoingMarkerDetection = true;
+        function readPixelsAndScheduleMarkerDetectionAsync(view) {
             const subtaskFrameNum = frameNum;
+            const subtaskQuadCopyTexIdx = quadCopyTexIdx;
 
+            consoleTimeStart("readPixelsAsync");
+            //readback_pixels.fill(0);
             readPixelsAsync(gl, 0, 0, scaledWidth, scaledHeight, gl.RGBA, gl.UNSIGNED_BYTE,
                             readback_pixels, readPixelBuf).then(() => {
+                consoleTimeEnd("readPixelsAsync");
+                console.debug('finished frame ' + subtaskFrameNum + ' readPixels');
+
+                readPixelsStarted = false;
+                readPixelsDone = true;
+
                 // session ended?
                 if (!readback_pixels) return;
 
-                console.debug('async do frame ' + subtaskFrameNum + ' marker detection, texture ' + quadCopyTexIdx);
-                detectMarkers(new Uint8ClampedArray(readback_pixels.buffer),
-                              scaledWidth, scaledHeight, view);
+                pendingMarkerDetection = () => {
+                  console.debug('async do frame ' + subtaskFrameNum + ' marker detection, texture ' + subtaskQuadCopyTexIdx);
+                  detectMarkers(new Uint8ClampedArray(readback_pixels.buffer),
+                                scaledWidth, scaledHeight, view);
 
-                alreadyDoingMarkerDetection = false;
-                backgroundTexture = quadCopyTextures[quadCopyTexIdx];
-                console.debug('finished frame ' + subtaskFrameNum + ' marker detection, new background texture ' + quadCopyTexIdx);
-                quadCopyTexIdx = (quadCopyTexIdx + 1) % quadCopyTextures.length;
+                  backgroundTexture = quadCopyTextures[subtaskQuadCopyTexIdx];
+                  console.debug('finished frame ' + subtaskFrameNum + ' marker detection, new background texture ' + subtaskQuadCopyTexIdx);
+                };
             });
+            quadCopyTexIdx = (quadCopyTexIdx + 1) % quadCopyTextures.length;
+            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
         }
 
         function drawMarkerScene(session, viewport, view, drawBackground) {
+            consoleTimeStart("drawMarkerScene");
+
             gl.bindFramebuffer(gl.FRAMEBUFFER, session.renderState.baseLayer.framebuffer);
             gl.clearColor(0, 0, 0, 0);
             gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
@@ -509,6 +563,7 @@
                 drawCube(pose, gl, programInfo, buffers, view, markerColor);
             }
             gl.disable(gl.BLEND);
+            consoleTimeEnd("drawMarkerScene");
         }
 
         let markerImage = null;
@@ -530,6 +585,7 @@
         }
 
         function detectMarkers(rawData, scaledWidth, scaledHeight, view) {
+            consoleTimeStart("detectMarkers");
             const debugOpenCV = document.getElementById('debugOpenCV').checked;
 
             const img = new cv.Mat(scaledHeight, scaledWidth, cv.CV_8UC4);
@@ -592,7 +648,7 @@
                 // Apply the tvec translation.
                 mat4.translate(m, m, tvecs.doublePtr(0, i));
                 // Rotate the cube based on rvec's length and axis.
-                mat4.rotate(m, m, vec3.length(rvecs.doublePtr(0, i)), rvecs.doublePtr(0, 0))
+                mat4.rotate(m, m, vec3.length(rvecs.doublePtr(0, i)), rvecs.doublePtr(0, i))
                 // Resize the cube to match the marker size.
                 mat4.scale(m, m, [markerSize / 2, markerSize / 2, markerSize / 2]);
                 // The drawn cube has a 2 unit (meter) edge length. Translate it so that
@@ -607,6 +663,7 @@
                 cv.imshow('out-canvas', img);
             }
             img.delete();
+            consoleTimeEnd("detectMarkers");
         }
 
 // This GL rendering code is adapted from a publicly provided code sample found at
diff --git a/tools/accessibility/inspect/ax_dump_events.cc b/tools/accessibility/inspect/ax_dump_events.cc
index 03bba85..5c5d6f1 100644
--- a/tools/accessibility/inspect/ax_dump_events.cc
+++ b/tools/accessibility/inspect/ax_dump_events.cc
@@ -77,7 +77,7 @@
   // A future patch will update mac and linux to use selector->widget and remove
   // the `pid` argument.
   unsigned int pid = 0;
-#if defined(USE_OZONE) || BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_MAC)
   pid = selector->widget;
 #endif
   const auto server =
diff --git a/tools/accessibility/inspect/ax_utils.cc b/tools/accessibility/inspect/ax_utils.cc
index 1634906..7143f3e5 100644
--- a/tools/accessibility/inspect/ax_utils.cc
+++ b/tools/accessibility/inspect/ax_utils.cc
@@ -21,7 +21,7 @@
 
 char kFiltersSwitch[] = "filters";
 
-#if defined(USE_OZONE) || BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_MAC)
 char kIdSwitch[] = "pid";
 #else
 char kIdSwitch[] = "window";
@@ -30,7 +30,7 @@
 using ui::AXTreeSelector;
 
 gfx::AcceleratedWidget CastToAcceleratedWidget(unsigned int window_id) {
-#if defined(USE_OZONE) || BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_MAC)
   return static_cast<gfx::AcceleratedWidget>(window_id);
 #else
   return reinterpret_cast<gfx::AcceleratedWidget>(window_id);
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index 592f519..ee77cc0b 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -345,7 +345,7 @@
     "includes": [2160],
   },
   "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/bluetooth_internals/resources.grd": {
-    "META": {"sizes": {"includes": [30],}},
+    "META": {"sizes": {"includes": [50],}},
     "includes": [2180],
   },
   "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/chromeos/audio/resources.grd": {
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index 876cb2c..3e0111f 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -381,6 +381,10 @@
                       help=('Run under the internal swarming server '
                             '(chrome-swarming) instead of the public server '
                             '(chromium-swarm).'))
+    subp.add_argument('--realm',
+                      default=None,
+                      help=('Optional realm used when triggering swarming '
+                            'tasks.'))
     subp.add_argument('--tags', default=[], action='append', metavar='FOO:BAR',
                       help='Tags to assign to the swarming task')
     subp.add_argument('--no-default-dimensions', action='store_false',
@@ -677,9 +681,11 @@
     if internal:
       cas_instance = 'chrome-swarming'
       swarming_server = 'chrome-swarming.appspot.com'
+      realm = 'chrome:try' if not self.args.realm else self.args.realm
     else:
       cas_instance = 'chromium-swarm'
       swarming_server = 'chromium-swarm.appspot.com'
+      realm = self.args.realm
     # TODO(dpranke): Look up the information for the target in
     # the //testing/buildbot.json file, if possible, so that we
     # can determine the isolate target, command line, and additional
@@ -751,11 +757,8 @@
           '-dump-json',
           json_file,
       ]
-      if internal:
-        cmd += [
-            '--realm',
-            'chrome:try',
-        ]
+      if realm:
+        cmd += ['--realm', realm]
       cmd += tags + dimensions + ['--'] + list(isolate_cmd)
       if self.args.extra_args:
         cmd += self.args.extra_args
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 3a3e9ce..0f0f2d14 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -495,7 +495,6 @@
       'Chromium Win Goma RBE Staging (clobber)': 'release_bot_x86_minimal_symbols',
       'Chromium Win Goma RBE ToT': 'release_bot_x86_minimal_symbols',
 
-
       'Chromium iOS Goma RBE ToT': 'ios_device_release_compile_only',
 
       'chromeos-amd64-generic-rel-goma-rbe-staging': 'chromeos_amd64-generic_use_fake_dbus_clients_vm_optimized',
@@ -1165,17 +1164,17 @@
       'linux-perfetto-rel': 'perfetto_release_trybot_reclient',
       'linux-rel': 'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange_code_coverage_do_typecheck_reclient',
       'linux-rel-inverse-fyi': 'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange_code_coverage_do_typecheck',
-      'linux-rel-ml': 'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange_do_typecheck',
+      'linux-rel-ml': 'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange_do_typecheck_reclient',
       'linux-rel-warmed': 'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange_code_coverage',
       'linux-viz-rel': 'release_trybot',
       'linux-wayland-rel': 'gpu_tests_wayland_release_trybot_no_symbols_use_dummy_lastchange_code_coverage_reclient',
       # TODO (crbug.com/1287228): Remove when orchestrator is verified
       'linux-wayland-rel-orchestrator': 'gpu_tests_wayland_release_trybot_no_symbols_use_dummy_lastchange_code_coverage_reclient',
-      'linux-webkit-msan-rel': 'msan_release_bot',
+      'linux-webkit-msan-rel': 'msan_release_bot_reclient',
       'linux-wpt-content-shell-fyi-rel': 'release_trybot',
-      'linux-wpt-fyi-rel': 'release_trybot',
-      'linux-wpt-identity-fyi-rel': 'release_trybot',
-      'linux-wpt-input-fyi-rel': 'release_trybot',
+      'linux-wpt-fyi-rel': 'release_trybot_reclient',
+      'linux-wpt-identity-fyi-rel': 'release_trybot_reclient',
+      'linux-wpt-input-fyi-rel': 'release_trybot_reclient',
       'linux-x64-castos': 'cast_release_trybot',
       'linux-x64-castos-audio': 'cast_audio_release_trybot',
       'linux-x64-castos-dbg': 'cast_debug_bot',
@@ -2847,10 +2846,6 @@
       'use_clang_coverage', 'partial_code_coverage_instrumentation', 'devtools_do_typecheck',
     ],
 
-    'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange_do_typecheck': [
-      'gpu_tests', 'release_trybot', 'no_symbols', 'use_dummy_lastchange',
-      'devtools_do_typecheck',
-    ],
     'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange_do_typecheck_reclient': [
       'gpu_tests', 'release_trybot_reclient', 'no_symbols', 'use_dummy_lastchange',
       'devtools_do_typecheck',
@@ -3464,7 +3459,6 @@
       'release_bot_reclient', 'fuchsia', 'arm64', 'arm64_host', 'fuchsia_cfv2_script',
     ],
 
-
     'release_bot_fuchsia_cfv2_script_chrome_reclient': [
       'release_bot_reclient', 'fuchsia',  'fuchsia_cfv2_script', 'fuchsia_chrome',
     ],
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
index 1dcd5b2..efa55358 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
@@ -445,7 +445,7 @@
       "proprietary_codecs": true,
       "symbol_level": 0,
       "use_dummy_lastchange": true,
-      "use_goma": true
+      "use_remoteexec": true
     }
   },
   "linux-rel-warmed": {
@@ -514,7 +514,7 @@
       "is_debug": false,
       "is_msan": true,
       "msan_track_origins": 2,
-      "use_goma": true
+      "use_remoteexec": true
     }
   },
   "linux-wpt-content-shell-fyi-rel": {
@@ -532,7 +532,7 @@
       "is_component_build": false,
       "is_debug": false,
       "symbol_level": 0,
-      "use_goma": true
+      "use_remoteexec": true
     }
   },
   "linux-wpt-identity-fyi-rel": {
@@ -541,7 +541,7 @@
       "is_component_build": false,
       "is_debug": false,
       "symbol_level": 0,
-      "use_goma": true
+      "use_remoteexec": true
     }
   },
   "linux-wpt-input-fyi-rel": {
@@ -550,7 +550,7 @@
       "is_component_build": false,
       "is_debug": false,
       "symbol_level": 0,
-      "use_goma": true
+      "use_remoteexec": true
     }
   },
   "linux-x64-castos": {
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 32b676f..ca6bc05 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -14855,6 +14855,7 @@
   <int value="158774237" label="WebAuthn.removeCredential"/>
   <int value="159066111" label="WebAudio.getRealtimeData"/>
   <int value="169232265" label="Storage.clearTrustTokens"/>
+  <int value="171091294" label="InputDeviceSettingsSplit:disabled"/>
   <int value="174855927" label="DOMStorage.removeDOMStorageItem"/>
   <int value="177653395" label="Browser.setPermission"/>
   <int value="187560850" label="Emulation.setEmulatedMedia"/>
@@ -57357,6 +57358,7 @@
       label="AutofillUseImprovedLabelDisambiguation:enabled"/>
   <int value="-2130982324" label="DesktopPWAsWebBundles:disabled"/>
   <int value="-2129940395" label="WebAssemblySimd:disabled"/>
+  <int value="-2129781123" label="RequestDesktopSiteZoom:enabled"/>
   <int value="-2129013032" label="DocumentTransition:enabled"/>
   <int value="-2128705444" label="AssistantAppSupport:enabled"/>
   <int value="-2128535212" label="GuestOsFiles:enabled"/>
@@ -60408,6 +60410,7 @@
   <int value="-360453785" label="LeftToRightUrls:disabled"/>
   <int value="-360038744" label="invert-viewport-scroll-order"/>
   <int value="-359886807" label="AppPreloadService:disabled"/>
+  <int value="-359331504" label="InputDeviceSettingsSplit:enabled"/>
   <int value="-357464687" label="SharingPreferVapid:enabled"/>
   <int value="-357250869"
       label="ActivateMetricsReportingEnabledPolicyAndroid:enabled"/>
@@ -64475,6 +64478,7 @@
   <int value="2095740699" label="OmniboxPedalsBatch3:disabled"/>
   <int value="2096736155" label="BrowsingDataLifetimeManager:enabled"/>
   <int value="2097048479" label="disable-auto-hiding-toolbar-threshold"/>
+  <int value="2097465503" label="RequestDesktopSiteZoom:disabled"/>
   <int value="2097585272" label="ProductivityLauncherImageSearch:enabled"/>
   <int value="2098059607" label="WifiSyncAllowDeletes:disabled"/>
   <int value="2098114768" label="DocumentPictureInPictureAPI:disabled"/>
@@ -78950,15 +78954,6 @@
   <int value="2" label="Other Aborted"/>
 </enum>
 
-<enum name="PaymentRequestMissingContactFields">
-  <summary>
-    A bit field value that shows the missing fields of contact info section in
-    payment sheet. The cardinality is not too high since the total number of
-    used bits is less than 4.
-  </summary>
-  <int value="0" label="Enum placerholder. See crbug/1179826"/>
-</enum>
-
 <enum name="PaymentRequestMissingPaymentFields">
   <summary>
     A bit field value that shows the missing fields of payment section in
@@ -78968,15 +78963,6 @@
   <int value="0" label="Enum placerholder. See crbug/1179826"/>
 </enum>
 
-<enum name="PaymentRequestMissingShippingFields">
-  <summary>
-    A bit field value that shows the missing fields of shipping section in
-    payment sheet. The cardinality is not too high since the total number of
-    used bits is less than 4.
-  </summary>
-  <int value="0" label="Enum placerholder. See crbug/1179826"/>
-</enum>
-
 <enum name="PaymentRequestNoShowReason">
   <int value="0" label="NoMatchingPaymentMethod"/>
   <int value="1" label="NoSupportedPaymentMethod"/>
@@ -82458,6 +82444,14 @@
   <int value="4" label="Failed to initialize schema"/>
 </enum>
 
+<enum name="PrivacySandboxPrivateAggregationHostSendHistogramReportResult">
+  <int value="0" label="Success"/>
+  <int value="1" label="API disabled in settings"/>
+  <int value="2" label="Too many contributions"/>
+  <int value="3" label="Debug key present but debug mode not enabled"/>
+  <int value="4" label="Report request creation failed"/>
+</enum>
+
 <enum name="PrivacySandboxReferrer">
   <int value="0" label="Privacy Settings"/>
   <int value="1" label="Cookie Settings Snackbar"/>
@@ -96493,6 +96487,7 @@
   <int value="53" label="Contact info"/>
   <int value="54" label="Autofill Wallet Usage"/>
   <int value="55" label="Segmentation"/>
+  <int value="56" label="Saved Tab Groups"/>
 </enum>
 
 <enum name="SyncModelTypeStoreInitResult">
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml
index e072f0a4..135929b 100644
--- a/tools/metrics/histograms/metadata/arc/histograms.xml
+++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -1989,6 +1989,17 @@
   </summary>
 </histogram>
 
+<histogram name="Arc.Session.HasWebViewUsage" enum="Boolean"
+    expires_after="2022-12-20">
+  <owner>hungmn@google.com</owner>
+  <owner>khmel@google.com</owner>
+  <owner>arc-performance@google.com</owner>
+  <summary>
+    True if there has been a WebView usage in an ARC session. This is recorded
+    when an ARC session is stopped.
+  </summary>
+</histogram>
+
 <histogram name="Arc.Session.MojoDisconnection{ArcUserTypes}"
     enum="ArcMojoConnectionType" expires_after="2022-11-11">
   <owner>shaochuan@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 2004d23..35590fb 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -248,6 +248,7 @@
   <variant name=".CreditCardSave" summary="Credit card save"/>
   <variant name=".FidoAuthentication"
       summary="Fido authentication (Better Auth Project)"/>
+  <variant name=".IBANSave" summary="IBAN save"/>
   <variant name=".LocalCardMigration" summary="Local card migration"/>
   <variant name=".VirtualCardEnrollment" summary="virtual card enrollment"/>
 </variants>
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index a2ed6997..650cc5f 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -105,6 +105,21 @@
   <token key="Process" variants="ProfiledProcess"/>
 </histogram>
 
+<histogram name="HeapProfiling.InProcess.TotalSampledMemory{Process}"
+    units="samples" expires_after="2023-04-30">
+  <owner>joenotcharles@chromium.org</owner>
+  <owner>etienneb@chromium.org</owner>
+  <owner>chrome-memory@google.com</owner>
+  <summary>
+    The total amount of memory in a heap snapshot taken in {Process}. This is an
+    estimate of total memory allocations in the code being sampled. Because it
+    is collected through random sampling it is only statistically accurate when
+    aggregated over many reports. Emitted once per snapshot when the in-process
+    heap profiler is enabled.
+  </summary>
+  <token key="Process" variants="ProfiledProcess"/>
+</histogram>
+
 <histogram name="HeapProfiling.ProfiledProcess.Type"
     enum="HeapProfilingProcessType" expires_after="2021-12-12">
   <owner>erikchen@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/mobile/histograms.xml b/tools/metrics/histograms/metadata/mobile/histograms.xml
index edccfa63..be376ee 100644
--- a/tools/metrics/histograms/metadata/mobile/histograms.xml
+++ b/tools/metrics/histograms/metadata/mobile/histograms.xml
@@ -563,7 +563,7 @@
 </histogram>
 
 <histogram name="Mobile.SystemNotification.Permission.Change"
-    enum="BooleanEnabled" expires_after="2023-03-05">
+    enum="BooleanEnabled" expires_after="2023-11-01">
   <owner>salg@google.com</owner>
   <owner>shaktisahu@chromium.org</owner>
   <summary>
@@ -574,7 +574,7 @@
 </histogram>
 
 <histogram name="Mobile.SystemNotification.Permission.OSPromptResult"
-    enum="BooleanEnabled" expires_after="2023-03-05">
+    enum="BooleanEnabled" expires_after="2023-11-01">
   <owner>salg@google.com</owner>
   <owner>shaktisahu@chromium.org</owner>
   <summary>
@@ -584,7 +584,7 @@
 </histogram>
 
 <histogram name="Mobile.SystemNotification.Permission.RationaleResult"
-    enum="NotificationRationaleResult" expires_after="2023-03-05">
+    enum="NotificationRationaleResult" expires_after="2023-11-01">
   <owner>salg@google.com</owner>
   <owner>shaktisahu@chromium.org</owner>
   <summary>
@@ -594,7 +594,7 @@
 </histogram>
 
 <histogram name="Mobile.SystemNotification.Permission.StartupRequestCount"
-    units="attempts" expires_after="2023-03-05">
+    units="attempts" expires_after="2023-11-01">
   <owner>salg@google.com</owner>
   <owner>shaktisahu@chromium.org</owner>
   <summary>
@@ -605,7 +605,7 @@
 </histogram>
 
 <histogram name="Mobile.SystemNotification.Permission.StartupState"
-    enum="NotificationPermissionState" expires_after="2022-12-31">
+    enum="NotificationPermissionState" expires_after="2023-11-01">
   <owner>salg@google.com</owner>
   <owner>shaktisahu@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/payment/histograms.xml b/tools/metrics/histograms/metadata/payment/histograms.xml
index a7397ae6a..ba8ee6d 100644
--- a/tools/metrics/histograms/metadata/payment/histograms.xml
+++ b/tools/metrics/histograms/metadata/payment/histograms.xml
@@ -136,19 +136,6 @@
   </summary>
 </histogram>
 
-<histogram name="PaymentRequest.MissingContactFields"
-    enum="PaymentRequestMissingContactFields" expires_after="2022-10-30">
-  <owner>rouslan@chromium.org</owner>
-  <owner>web-payments-team@google.com</owner>
-  <summary>
-    A bitfield representing different missing fields of the contact section in
-    payment sheet. This only gets recorded when no complete contact profile is
-    available. In case of multiple partially complete profiles, this is only
-    recorded for the most complete one which is also the first profile in the
-    suggestion list.
-  </summary>
-</histogram>
-
 <histogram name="PaymentRequest.MissingPaymentFields"
     enum="PaymentRequestMissingPaymentFields" expires_after="2022-06-26">
   <owner>rouslan@chromium.org</owner>
@@ -162,19 +149,6 @@
   </summary>
 </histogram>
 
-<histogram name="PaymentRequest.MissingShippingFields"
-    enum="PaymentRequestMissingShippingFields" expires_after="2022-10-30">
-  <owner>rouslan@chromium.org</owner>
-  <owner>web-payments-team@google.com</owner>
-  <summary>
-    A bitfield representing different missing fields of the shipping section in
-    the payment sheet. This only gets recorded when no complete shipping profile
-    is available. In case of multiple partially complete profiles, this is only
-    recorded for the most complete one which is also the first profile in the
-    suggestion list.
-  </summary>
-</histogram>
-
 <histogram name="PaymentRequest.NumberOfSuggestionsShown" units="units"
     expires_after="2022-11-22">
   <owner>rouslan@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/privacy/histograms.xml b/tools/metrics/histograms/metadata/privacy/histograms.xml
index f9d26d4e..4392f43 100644
--- a/tools/metrics/histograms/metadata/privacy/histograms.xml
+++ b/tools/metrics/histograms/metadata/privacy/histograms.xml
@@ -428,6 +428,21 @@
   </summary>
 </histogram>
 
+<histogram
+    name="PrivacySandbox.PrivateAggregation.Host.SendHistogramReportResult"
+    enum="PrivacySandboxPrivateAggregationHostSendHistogramReportResult"
+    expires_after="M117">
+  <owner>alexmt@chromium.org</owner>
+  <owner>linnan@chromium.org</owner>
+  <summary>
+    Records whether a call to PrivateAggregationHost::SendHistogramReport()
+    successfully resulted in a report request being forwarded to the manager
+    and, if not, the reason for failure. Note that, even if successfully
+    forwarded, the report may still be rejected by the budgeter or in the
+    aggregation_service layer. Recorded for every call.
+  </summary>
+</histogram>
+
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index 3da2d3f2..30c4579 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -52,6 +52,7 @@
   <variant name=".PRIORITY_PREFERENCE" summary="PRIORITY_PREFERENCE"/>
   <variant name=".PROXY_TABS" summary="PROXY_TABS"/>
   <variant name=".READING_LIST" summary="READING_LIST"/>
+  <variant name=".SAVED_TAB_GROUPS" summary="SAVED_TAB_GROUPS"/>
   <variant name=".SEARCH_ENGINE" summary="SEARCH_ENGINE"/>
   <variant name=".SECURITY_EVENT" summary="SECURITY_EVENT"/>
   <variant name=".SEGMENTATION" summary="SEGMENTATION"/>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 73f2cb2..047c7c9 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "1aba577a3550db8ad57a3eb7efebb5c20493054c",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/21523f457bf0af5ba8e3d33a687e9643e797f607/trace_processor_shell.exe"
+            "hash": "81b38750b54f1ea39e7c27bbb4d5d0d932ec240c",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/06c0150ef04cb2b72c8df56acd8d22a185d81cb7/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "6f2840b72c0f8cab5e2f26ad39fe4a9aaebf955b",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/21523f457bf0af5ba8e3d33a687e9643e797f607/trace_processor_shell"
+            "hash": "f0e10eab066befabf088ca0251c364ffe71f6c07",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/06c0150ef04cb2b72c8df56acd8d22a185d81cb7/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "92318bea34f5c9beec69d2d826a9a92ec9d3cdcd",
             "full_remote_path": "perfetto-luci-artifacts/v30.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "2ef6d0c4d75184e5139cfab6a4485ddfd488a1ae",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/06c0150ef04cb2b72c8df56acd8d22a185d81cb7/trace_processor_shell"
+            "hash": "b4264cc85b1e5791349072665411bff029e53082",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/6232b55ef794877510be9435549503968813d0f6/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 696f0e53..6498f17 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -78,6 +78,7 @@
 crbug.com/1337486 [ mac ] desktop_ui/tab_search:top50:loading:2020 [ Skip ]
 crbug.com/1267328 [ chromeos ] desktop_ui/side_search:navigation [ Skip ]
 crbug.com/1267328 [ chromeos ] desktop_ui/tab_search:top10:2020 [ Skip ]
+crbug.com/1380407 [ chromeos ] desktop_ui/tab_search:top10:loading:2020 [ Skip ]
 
 # Benchmark: blink_perf.webcodecs
 crbug.com/1300680 [ android ] blink_perf.webcodecs/software-video-encoding.html [ Skip ]
@@ -212,6 +213,7 @@
 crbug.com/1341890 [ chromeos ] rendering.desktop/aquarium_20k [ Skip ]
 crbug.com/1341890 [ chromeos ] rendering.desktop/aquarium_20k_fast_call [ Skip ]
 crbug.com/1341890 [ chromeos ] rendering.desktop/blob [ Skip ]
+crbug.com/1380407 [ chromeos ] rendering.desktop/css_value_type_transform_simple [ Skip ]
 crbug.com/1360321 [ linux ] rendering.desktop/overlay_background_color_css_transitions_page [ Skip ]
 crbug.com/1325314 [ win ] rendering.desktop/cnn_pinch_2018 [ Skip ]
 
@@ -347,6 +349,7 @@
 crbug.com/1044682 [ desktop ] system_health.common_desktop/browse:tools:gmail-search:2020 [ Skip ]
 crbug.com/1044682 [ desktop ] system_health.common_desktop/browse:tools:gmail-compose:2020 [ Skip ]
 crbug.com/1156571 [ mac ] system_health.common_desktop/browse:news:nytimes:2020 [ Skip ]
+crbug.com/1380407 [ chromeos ] system_health.common_desktop/browse:news:nytimes:2020 [ Skip ]
 crbug.com/1190750 [ linux ] system_health.common_desktop/long_running:tools:gmail-foreground [ Skip ]
 crbug.com/1190750 [ linux ] system_health.common_desktop/long_running:tools:gmail-background [ Skip ]
 crbug.com/1190750 [ win ] system_health.common_desktop/long_running:tools:gmail-background [ Skip ]
@@ -651,6 +654,7 @@
 crbug.com/1211795 [ mac ] v8.browsing_desktop-future/browse:social:twitter_infinite_scroll:2018 [ Skip ]
 crbug.com/1211795 [ mac ] v8.browsing_desktop-future/browse:social:facebook_infinite_scroll:2018 [ Skip ]
 crbug.com/1302694 [ mac ] v8.browsing_desktop-future/browse:tools:photoshop:2021 [ Skip ]
+crbug.com/1380407 [ chromeos ] v8.browsing_desktop-future/browse:tools:photoshop:2021 [ Skip ]
 crbug.com/1302694 [ mac ] v8.browsing_desktop-future/browse:tools:photoshop_warm:2021 [ Skip ]
 crbug.com/1360312 [ win-laptop ] v8.browsing_desktop-future/browse:tools:photoshop:2021 [ Skip ]
 crbug.com/1360314 [ win ] v8.browsing_desktop-future/browse:tools:photoshop_warm:2021 [ Skip ]
@@ -705,6 +709,7 @@
 # Benchmark: webrtc
 crbug.com/1051644 [ win ] webrtc/pause_play_peerconnections [ Skip ]
 crbug.com/1129497 [ win-laptop ] webrtc/multiple_peerconnections [ Skip ]
+crbug.com/1380407 [ chromeos ] webrtc/multiple_peerconnections [ Skip ]
 crbug.com/1319408 [ android ] webrtc/insertable_streams_audio_processing [ Skip ]
 
 # Benchmark: system_health_smoke_test
diff --git a/tools/rust/update_rust.py b/tools/rust/update_rust.py
index 9746e7f..bc8c836 100755
--- a/tools/rust/update_rust.py
+++ b/tools/rust/update_rust.py
@@ -45,7 +45,7 @@
 # This should almost always be None. When a breakage happens the fallback should
 # be temporary. Once fixed, the applicable revision(s) above should be updated
 # and FALLBACK_CLANG_VERSION should be reset to None.
-FALLBACK_CLANG_VERSION = None
+FALLBACK_CLANG_VERSION = 'llvmorg-16-init-8697-g60809cd2-1'
 
 # Hash of src/stage0.json, which itself contains the stage0 toolchain hashes.
 # We trust the Rust build system checks, but to ensure it is not tampered with
@@ -91,7 +91,10 @@
         with open(VERSION_STAMP_PATH) as version_file:
             existing_stamp = version_file.readline().rstrip()
         version_re = re.compile(r'rustc [0-9.]+-dev \((.+?) chromium\)')
-        return version_re.fullmatch(existing_stamp).group(1)
+        match = version_re.fullmatch(existing_stamp)
+        if match is None:
+            return None
+        return match.group(1)
 
     return None
 
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 5163a37..9161a84 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -305,7 +305,7 @@
  <item id="url_icon_source_fetch" added_in_milestone="98" content_hash_code="0297f30b" os_list="chromeos" file_path="chrome/browser/ui/app_list/search/common/url_icon_source.cc" />
  <item id="launcher_item_suggest" added_in_milestone="98" content_hash_code="04a4041e" os_list="chromeos" file_path="chrome/browser/ui/app_list/search/files/item_suggest_cache.cc" />
  <item id="ambient_client" added_in_milestone="98" content_hash_code="062d821f" os_list="chromeos" file_path="chrome/browser/ui/ash/ambient/ambient_client_impl.cc" />
- <item id="calendar_get_events" added_in_milestone="98" content_hash_code="0108842a" os_list="chromeos" file_path="chrome/browser/ui/ash/calendar/calendar_keyed_service.cc" />
+ <item id="calendar_get_events" added_in_milestone="98" content_hash_code="0603f52a" os_list="chromeos" file_path="chrome/browser/ui/ash/calendar/calendar_keyed_service.cc" />
  <item id="side_search_availability_test" added_in_milestone="98" content_hash_code="04b1ffa5" os_list="chromeos,linux,windows" file_path="chrome/browser/ui/side_search/side_search_tab_contents_helper.cc" />
  <item id="edu_account_login_profile_image_fetcher" added_in_milestone="98" content_hash_code="0689301c" os_list="chromeos" file_path="chrome/browser/ui/webui/ash/edu_account_login_handler.cc" />
  <item id="management_ui_customer_logo" added_in_milestone="98" content_hash_code="01a17a58" os_list="chromeos" file_path="chrome/browser/ui/webui/management/management_ui_handler_chromeos.cc" />
diff --git a/ui/aura/demo/demo_main.cc b/ui/aura/demo/demo_main.cc
index bfb5b82..74d23f3 100644
--- a/ui/aura/demo/demo_main.cc
+++ b/ui/aura/demo/demo_main.cc
@@ -46,7 +46,7 @@
 #include "ui/display/win/dpi.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
@@ -163,7 +163,7 @@
 }
 
 int DemoMain() {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   ui::OzonePlatform::InitParams params;
   params.single_process = true;
   ui::OzonePlatform::InitializeForUI(params);
diff --git a/ui/aura/env.cc b/ui/aura/env.cc
index 1d08bb50..481d7a00 100644
--- a/ui/aura/env.cc
+++ b/ui/aura/env.cc
@@ -31,7 +31,7 @@
 #include "ui/base/win/win_cursor_factory.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
@@ -242,7 +242,7 @@
 }
 
 bool Env::Init() {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   // The ozone platform can provide its own event source. So initialize the
   // platform before creating the default event source
   ui::OzonePlatform::InitParams params;
diff --git a/ui/aura/test/aura_test_helper.cc b/ui/aura/test/aura_test_helper.cc
index d9b73d0..17b0882 100644
--- a/ui/aura/test/aura_test_helper.cc
+++ b/ui/aura/test/aura_test_helper.cc
@@ -43,7 +43,7 @@
 #include "ui/aura/native_window_occlusion_tracker_win.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/events/ozone/events_ozone.h"
 #endif
 
@@ -67,7 +67,7 @@
   ui::test::EnableTestConfigForPlatformWindows();
 #endif
 
-#if defined(USE_OZONE) && BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_OZONE) && BUILDFLAG(IS_CHROMEOS_ASH)
   ui::DisableNativeUiEventDispatchForTest();
 #endif
 
diff --git a/ui/aura/test/ui_controls_factory_aura.h b/ui/aura/test/ui_controls_factory_aura.h
index 5a9f80c..6a4dfc8 100644
--- a/ui/aura/test/ui_controls_factory_aura.h
+++ b/ui/aura/test/ui_controls_factory_aura.h
@@ -5,6 +5,7 @@
 #ifndef UI_AURA_TEST_UI_CONTROLS_FACTORY_AURA_H_
 #define UI_AURA_TEST_UI_CONTROLS_FACTORY_AURA_H_
 
+#include "build/build_config.h"
 #include "ui/base/test/ui_controls_aura.h"
 
 namespace aura {
@@ -14,7 +15,7 @@
 
 ui_controls::UIControlsAura* CreateUIControlsAura(WindowTreeHost* host);
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 // Callback from Window Service with the result of posting an event. |result|
 // is true if event successfully processed and |closure| is an optional closure
 // to run when done (used in client code to wait for ack).
diff --git a/ui/aura/window_tree_host.h b/ui/aura/window_tree_host.h
index 0bdb86b..c85e519e 100644
--- a/ui/aura/window_tree_host.h
+++ b/ui/aura/window_tree_host.h
@@ -299,8 +299,6 @@
   // See VideoCaptureLock for details. This may return null.
   std::unique_ptr<VideoCaptureLock> CreateVideoCaptureLock();
 
-  bool holding_pointer_moves() const { return holding_pointer_moves_; }
-
 #if BUILDFLAG(IS_WIN)
   // Returns whether a host's window is on the current workspace or not,
   // absl::nullopt if the state is not known.
diff --git a/ui/aura/window_tree_host_platform.cc b/ui/aura/window_tree_host_platform.cc
index 4c31e785..c226395 100644
--- a/ui/aura/window_tree_host_platform.cc
+++ b/ui/aura/window_tree_host_platform.cc
@@ -29,7 +29,7 @@
 #include "ui/platform_window/platform_window.h"
 #include "ui/platform_window/platform_window_init_properties.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/events/keycodes/dom/dom_keyboard_layout_map.h"
 #include "ui/ozone/public/ozone_platform.h"
 #endif
@@ -69,7 +69,7 @@
   // end up propagating unneeded bounds change event when it is first notified
   // through OnBoundsChanged, which may lead to unneeded re-layouts, etc.
   size_in_pixels_ = properties.bounds.size();
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   platform_window_ = ui::OzonePlatform::GetInstance()->CreatePlatformWindow(
       this, std::move(properties));
 #elif BUILDFLAG(IS_WIN)
@@ -156,7 +156,7 @@
 
 base::flat_map<std::string, std::string>
 WindowTreeHostPlatform::GetKeyboardLayoutMap() {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   return ui::GenerateDomKeyboardLayoutMap();
 #else
   NOTIMPLEMENTED();
diff --git a/ui/base/clipboard/clipboard.cc b/ui/base/clipboard/clipboard.cc
index 2782c65..722cde3c 100644
--- a/ui/base/clipboard/clipboard.cc
+++ b/ui/base/clipboard/clipboard.cc
@@ -31,7 +31,7 @@
   // Use lambda instead of local helper function in order to access private
   // member IsSelectionBufferAvailable().
   static auto IsSupportedSelectionClipboard = []() -> bool {
-#if defined(USE_OZONE) && !BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_OZONE) && !BUILDFLAG(IS_CHROMEOS)
     ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
     CHECK(clipboard);
     return clipboard->IsSelectionBufferAvailable();
diff --git a/ui/base/clipboard/clipboard.h b/ui/base/clipboard/clipboard.h
index b48e4a2..b37842a 100644
--- a/ui/base/clipboard/clipboard.h
+++ b/ui/base/clipboard/clipboard.h
@@ -407,11 +407,11 @@
 
   static base::PlatformThreadId GetAndValidateThreadID();
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   // Returns whether the selection buffer is available.  This is true for some
   // Linux platforms.
   virtual bool IsSelectionBufferAvailable() const = 0;
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
 
   // A list of allowed threads. By default, this is empty and no thread checking
   // is done (in the unit test case), but a user (like content) can set which
diff --git a/ui/base/clipboard/clipboard_non_backed.cc b/ui/base/clipboard/clipboard_non_backed.cc
index 36dfe64..558ad7d9 100644
--- a/ui/base/clipboard/clipboard_non_backed.cc
+++ b/ui/base/clipboard/clipboard_non_backed.cc
@@ -23,6 +23,7 @@
 #include "base/synchronization/lock.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -807,7 +808,7 @@
 #endif
 }
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 bool ClipboardNonBacked::IsSelectionBufferAvailable() const {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   return false;
@@ -815,7 +816,7 @@
   return true;
 #endif
 }
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
 
 void ClipboardNonBacked::WritePortableAndPlatformRepresentations(
     ClipboardBuffer buffer,
diff --git a/ui/base/clipboard/clipboard_non_backed.h b/ui/base/clipboard/clipboard_non_backed.h
index 498647d2..bb9001d 100644
--- a/ui/base/clipboard/clipboard_non_backed.h
+++ b/ui/base/clipboard/clipboard_non_backed.h
@@ -10,6 +10,7 @@
 
 #include "base/component_export.h"
 #include "base/gtest_prod_util.h"
+#include "build/build_config.h"
 #include "ui/base/clipboard/clipboard.h"
 
 namespace ui {
@@ -107,9 +108,9 @@
   void ReadData(const ClipboardFormatType& format,
                 const DataTransferEndpoint* data_dst,
                 std::string* result) const override;
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   bool IsSelectionBufferAvailable() const override;
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
   void WritePortableAndPlatformRepresentations(
       ClipboardBuffer buffer,
       const ObjectMap& objects,
diff --git a/ui/base/clipboard/clipboard_test_template.h b/ui/base/clipboard/clipboard_test_template.h
index 682f3d1f..870d118 100644
--- a/ui/base/clipboard/clipboard_test_template.h
+++ b/ui/base/clipboard/clipboard_test_template.h
@@ -523,7 +523,7 @@
 // TODO(tonikitoo, msisov): enable back for ClipboardOzone implements
 // selection support. https://crbug.com/911992
 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID) && \
-    !BUILDFLAG(IS_CHROMEOS_ASH) && !defined(USE_OZONE)
+    !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_OZONE)
   ascii_text.clear();
   this->clipboard().ReadAsciiText(ClipboardBuffer::kSelection,
                                   /* data_dst = */ nullptr, &ascii_text);
diff --git a/ui/base/clipboard/test/test_clipboard.cc b/ui/base/clipboard/test/test_clipboard.cc
index 1bccd24..f9da5dbb9 100644
--- a/ui/base/clipboard/test/test_clipboard.cc
+++ b/ui/base/clipboard/test/test_clipboard.cc
@@ -286,11 +286,11 @@
   last_modified_time_ = base::Time();
 }
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 bool TestClipboard::IsSelectionBufferAvailable() const {
   return true;
 }
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
 
 void TestClipboard::WritePortableAndPlatformRepresentations(
     ClipboardBuffer buffer,
diff --git a/ui/base/clipboard/test/test_clipboard.h b/ui/base/clipboard/test/test_clipboard.h
index 3450036..f12c4df 100644
--- a/ui/base/clipboard/test/test_clipboard.h
+++ b/ui/base/clipboard/test/test_clipboard.h
@@ -14,6 +14,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/clipboard/clipboard.h"
@@ -88,9 +89,9 @@
                 std::string* result) const override;
   base::Time GetLastModifiedTime() const override;
   void ClearLastModifiedTime() override;
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   bool IsSelectionBufferAvailable() const override;
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
   void WritePortableAndPlatformRepresentations(
       ClipboardBuffer buffer,
       const ObjectMap& objects,
diff --git a/ui/base/ime/init/input_method_factory.cc b/ui/base/ime/init/input_method_factory.cc
index d5cfdda..31ad0c6 100644
--- a/ui/base/ime/init/input_method_factory.cc
+++ b/ui/base/ime/init/input_method_factory.cc
@@ -18,7 +18,7 @@
 #include "ui/base/ime/win/input_method_win_tsf.h"
 #elif BUILDFLAG(IS_APPLE)
 #include "ui/base/ime/mac/input_method_mac.h"
-#elif defined(USE_OZONE)
+#elif BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #else
 #include "ui/base/ime/input_method_minimal.h"
@@ -64,7 +64,7 @@
                                                widget);
 #elif BUILDFLAG(IS_APPLE)
   return std::make_unique<InputMethodMac>(ime_key_event_dispatcher);
-#elif defined(USE_OZONE)
+#elif BUILDFLAG(IS_OZONE)
   return ui::OzonePlatform::GetInstance()->CreateInputMethod(
       ime_key_event_dispatcher, widget);
 #else
diff --git a/ui/base/x/x11_util.cc b/ui/base/x/x11_util.cc
index c48000bc..06f8847 100644
--- a/ui/base/x/x11_util.cc
+++ b/ui/base/x/x11_util.cc
@@ -747,7 +747,7 @@
 // builds as long as our EGL impl for Ozone/X11 is not mature enough and we do
 // not receive swap completions on time, which results in weird resize behaviour
 // as X Server waits for the XSyncCounter changes.
-#if BUILDFLAG(IS_CHROMEOS_ASH) || defined(USE_OZONE)
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_OZONE)
   return false;
 #else
   static bool result =
diff --git a/ui/display/manager/display_manager.cc b/ui/display/manager/display_manager.cc
index 4e28329..8db36e7f 100644
--- a/ui/display/manager/display_manager.cc
+++ b/ui/display/manager/display_manager.cc
@@ -1004,9 +1004,20 @@
       ++new_info_iter;
     }
   }
+
   Display old_primary;
-  if (delegate_)
-    old_primary = screen_->GetPrimaryDisplay();
+  if (delegate_) {
+    // Get old primary from current resolved layout, because we could be in the
+    // middle of updating the primary display, so screen_->GetPrimaryDisplay()
+    // may already point to the new primary.
+    if (current_resolved_layout_) {
+      Display* primary = FindDisplayForId(current_resolved_layout_->primary_id);
+      if (primary)
+        old_primary = *primary;
+    }
+    if (!old_primary.is_valid())
+      old_primary = screen_->GetPrimaryDisplay();
+  }
 
   // Clear focus if the display has been removed, but don't clear focus if
   // the destkop has been moved from one display to another
diff --git a/ui/events/event.cc b/ui/events/event.cc
index 3a757179..569c2b65 100644
--- a/ui/events/event.cc
+++ b/ui/events/event.cc
@@ -32,7 +32,7 @@
 #include "ui/gfx/geometry/transform.h"
 #include "ui/gfx/geometry/transform_util.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/base/ui_base_features.h"                               // nogncheck
 #include "ui/events/ozone/layout/keyboard_layout_engine.h"          // nogncheck
 #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"  // nogncheck
@@ -145,13 +145,13 @@
   }
 }
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 uint32_t ScanCodeFromNative(const PlatformEvent& native_event) {
   const KeyEvent* event = static_cast<const KeyEvent*>(native_event);
   DCHECK(event->IsKeyEvent());
   return event->scan_code();
 }
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
 
 bool IsNearZero(const float num) {
   // Epsilon of 1e-10 at 0.
@@ -309,7 +309,7 @@
     latency()->set_source_event_type(EventTypeToLatencySourceEventType(type));
   ComputeEventLatencyOS(native_event);
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   source_device_id_ = native_event->source_device_id();
   if (auto* properties = native_event->properties())
     properties_ = std::make_unique<Properties>(*properties);
@@ -812,7 +812,7 @@
 
 // static
 KeyEvent* KeyEvent::last_key_event_ = nullptr;
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 KeyEvent* KeyEvent::last_ibus_key_event_ = nullptr;
 #endif
 
@@ -822,12 +822,12 @@
 KeyEvent::KeyEvent(const PlatformEvent& native_event, int event_flags)
     : Event(native_event, EventTypeFromNative(native_event), event_flags),
       key_code_(KeyboardCodeFromNative(native_event)),
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
       scan_code_(ScanCodeFromNative(native_event)),
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
       code_(CodeFromNative(native_event)),
       is_char_(IsCharFromNative(native_event)) {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   DCHECK(native_event->IsKeyEvent());
   key_ = native_event->AsKeyEvent()->key_;
 #endif
@@ -875,9 +875,9 @@
 KeyEvent::KeyEvent(const KeyEvent& rhs)
     : Event(rhs),
       key_code_(rhs.key_code_),
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
       scan_code_(rhs.scan_code_),
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
       code_(rhs.code_),
       is_char_(rhs.is_char_),
       key_(rhs.key_) {
@@ -887,9 +887,9 @@
   if (this != &rhs) {
     Event::operator=(rhs);
     key_code_ = rhs.key_code_;
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
     scan_code_ = rhs.scan_code_;
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
     code_ = rhs.code_;
     key_ = rhs.key_;
     is_char_ = rhs.is_char_;
@@ -956,7 +956,7 @@
     return;
 
   KeyboardCode dummy_key_code;
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   if (KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()->Lookup(
           code, flags(), &key_, &dummy_key_code)) {
     return;
@@ -1036,7 +1036,7 @@
 }
 
 KeyEvent** KeyEvent::GetLastKeyEvent() {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   // Use a different static variable for key events that have non standard
   // state masks as it may be reposted by an IME. IBUS-GTK and fcitx-GTK uses
   // this field to detect the re-posted event for example. crbug.com/385873.
diff --git a/ui/events/event.h b/ui/events/event.h
index afda88ef1..6e1efdbd 100644
--- a/ui/events/event.h
+++ b/ui/events/event.h
@@ -14,6 +14,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/gesture_event_details.h"
 #include "ui/events/gestures/gesture_types.h"
@@ -869,13 +870,13 @@
   // events in an EventRewriter.
   void set_key_code(KeyboardCode key_code) { key_code_ = key_code; }
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   // The scan code of the physical key. This is used to perform the mapping
   // of top row keys from Actions back to F-Keys on new Chrome OS keyboards
   // that supply the mapping via the kernel.
   uint32_t scan_code() const { return scan_code_; }
   void set_scan_code(uint32_t scan_code) { scan_code_ = scan_code; }
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
 
   // Returns the same value as key_code(), except that located codes are
   // returned in place of non-located ones (e.g. VKEY_LSHIFT or VKEY_RSHIFT
@@ -926,10 +927,10 @@
 
   KeyboardCode key_code_;
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   // The scan code of the physical key on Chrome OS.
   uint32_t scan_code_ = 0;
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
 
   // DOM KeyboardEvent |code| (e.g. DomCode::US_A, DomCode::SPACE).
   // http://www.w3.org/TR/DOM-Level-3-Events-code/
@@ -956,7 +957,7 @@
   mutable DomKey key_ = DomKey::NONE;
 
   static KeyEvent* last_key_event_;
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   static KeyEvent* last_ibus_key_event_;
 #endif
 
diff --git a/ui/events/event_switches.cc b/ui/events/event_switches.cc
index 4b9ec92..326ba6b 100644
--- a/ui/events/event_switches.cc
+++ b/ui/events/event_switches.cc
@@ -33,7 +33,7 @@
 const char kPenDevices[] = "pen-devices";
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 // Tells Chrome to do edge touch filtering. Useful for convertible tablet.
 const char kEdgeTouchFiltering[] = "edge-touch-filtering";
 
diff --git a/ui/events/event_switches.h b/ui/events/event_switches.h
index a59ed75..6c33d89 100644
--- a/ui/events/event_switches.h
+++ b/ui/events/event_switches.h
@@ -18,7 +18,7 @@
 EVENTS_BASE_EXPORT extern const char kPenDevices[];
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 EVENTS_BASE_EXPORT extern const char kEdgeTouchFiltering[];
 EVENTS_BASE_EXPORT extern const char kDisableCancelAllTouches[];
 EVENTS_BASE_EXPORT
diff --git a/ui/events/mojom/mojom_traits_unittest.cc b/ui/events/mojom/mojom_traits_unittest.cc
index 127481f..99d32724 100644
--- a/ui/events/mojom/mojom_traits_unittest.cc
+++ b/ui/events/mojom/mojom_traits_unittest.cc
@@ -4,6 +4,7 @@
 
 #include <utility>
 
+#include "build/build_config.h"
 #include "mojo/public/cpp/base/time_mojom_traits.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -16,7 +17,7 @@
 #include "ui/gfx/geometry/mojom/geometry_mojom_traits.h"
 #include "ui/latency/mojom/latency_info_mojom_traits.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/events/ozone/layout/scoped_keyboard_layout_engine.h"  // nogncheck
 #include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h"  // nogncheck
 #endif
@@ -389,7 +390,7 @@
             output->AsTouchEvent()->unique_event_id());
 }
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 
 // Test KeyboardLayoutEngine implementation that always returns 'x'.
 class FixedKeyboardLayoutEngine : public StubKeyboardLayoutEngine {
diff --git a/ui/events/ozone/evdev/event_factory_evdev.cc b/ui/events/ozone/evdev/event_factory_evdev.cc
index a9dfcccc..3f9b191 100644
--- a/ui/events/ozone/evdev/event_factory_evdev.cc
+++ b/ui/events/ozone/evdev/event_factory_evdev.cc
@@ -219,19 +219,25 @@
 
 void EventFactoryEvdev::Init() {
   DCHECK(!initialized_);
+  DCHECK(user_input_task_runner_);
 
   StartThread();
 
   initialized_ = true;
 }
 
+void EventFactoryEvdev::SetUserInputTaskRunner(
+    scoped_refptr<base::SingleThreadTaskRunner> user_input_task_runner) {
+  user_input_task_runner_ = std::move(user_input_task_runner);
+}
+
 std::unique_ptr<SystemInputInjector>
 EventFactoryEvdev::CreateSystemInputInjector() {
   // Use forwarding dispatcher for the injector rather than dispatching
   // directly. We cannot assume it is safe to (re-)enter ui::Event dispatch
   // synchronously from the injection point.
   std::unique_ptr<DeviceEventDispatcherEvdev> proxy_dispatcher(
-      new ProxyDeviceEventDispatcher(base::ThreadTaskRunnerHandle::Get(),
+      new ProxyDeviceEventDispatcher(user_input_task_runner_,
                                      weak_ptr_factory_.GetWeakPtr()));
   return std::make_unique<InputInjectorEvdev>(std::move(proxy_dispatcher),
                                               cursor_);
@@ -523,7 +529,7 @@
 void EventFactoryEvdev::StartThread() {
   // Set up device factory.
   std::unique_ptr<DeviceEventDispatcherEvdev> proxy_dispatcher(
-      new ProxyDeviceEventDispatcher(base::ThreadTaskRunnerHandle::Get(),
+      new ProxyDeviceEventDispatcher(user_input_task_runner_,
                                      weak_ptr_factory_.GetWeakPtr()));
   thread_.Start(std::move(proxy_dispatcher), cursor_,
                 base::BindOnce(&EventFactoryEvdev::OnThreadStarted,
diff --git a/ui/events/ozone/evdev/event_factory_evdev.h b/ui/events/ozone/evdev/event_factory_evdev.h
index 5b8e482a..fef4bbd8 100644
--- a/ui/events/ozone/evdev/event_factory_evdev.h
+++ b/ui/events/ozone/evdev/event_factory_evdev.h
@@ -59,6 +59,10 @@
 
   ~EventFactoryEvdev() override;
 
+  // Must be called before Init().
+  void SetUserInputTaskRunner(
+      scoped_refptr<base::SingleThreadTaskRunner> user_input_task_runner);
+
   // Initialize. Must be called with a valid message loop.
   void Init();
 
@@ -164,6 +168,8 @@
   // Touch event id generator.
   SequentialIDGenerator touch_id_generator_;
 
+  scoped_refptr<base::SingleThreadTaskRunner> user_input_task_runner_;
+
   // Support weak pointers for attach & detach callbacks.
   base::WeakPtrFactory<EventFactoryEvdev> weak_ptr_factory_{this};
 };
diff --git a/ui/events/ozone/evdev/event_factory_evdev_unittest.cc b/ui/events/ozone/evdev/event_factory_evdev_unittest.cc
index 38100be1..64f2584 100644
--- a/ui/events/ozone/evdev/event_factory_evdev_unittest.cc
+++ b/ui/events/ozone/evdev/event_factory_evdev_unittest.cc
@@ -38,6 +38,7 @@
  protected:
   EventFactoryEvdevTest() : event_factory_(nullptr, &device_manager_, nullptr) {
     scoped_feature_list_.InitAndEnableFeature(kEnableOrdinalMotion);
+    event_factory_.SetUserInputTaskRunner(base::ThreadTaskRunnerHandle::Get());
     event_factory_.Init();
     event_factory_.AddPlatformEventObserver(&event_observer_);
   }
diff --git a/ui/events/platform_event.h b/ui/events/platform_event.h
index 9c8c58db7..4aa6d6b7 100644
--- a/ui/events/platform_event.h
+++ b/ui/events/platform_event.h
@@ -24,7 +24,7 @@
 namespace ui {
 
 // Cross platform typedefs for native event types.
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 using PlatformEvent = ui::Event*;
 #elif BUILDFLAG(IS_WIN)
 using PlatformEvent = CHROME_MSG;
diff --git a/ui/events/test/keyboard_layout.cc b/ui/events/test/keyboard_layout.cc
index 9f266962..2a7e407a 100644
--- a/ui/events/test/keyboard_layout.cc
+++ b/ui/events/test/keyboard_layout.cc
@@ -8,14 +8,14 @@
 #include "base/notreached.h"
 #include "build/build_config.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h"  // nogncheck
 #endif
 
 namespace ui {
 
 ScopedKeyboardLayout::ScopedKeyboardLayout(KeyboardLayout layout) {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   CHECK_EQ(layout, KEYBOARD_LAYOUT_ENGLISH_US);
   auto keyboard_layout_engine = std::make_unique<StubKeyboardLayoutEngine>();
   scoped_keyboard_layout_engine_ = std::make_unique<ScopedKeyboardLayoutEngine>(
diff --git a/ui/events/test/keyboard_layout.h b/ui/events/test/keyboard_layout.h
index 518dfdb..2580dc2 100644
--- a/ui/events/test/keyboard_layout.h
+++ b/ui/events/test/keyboard_layout.h
@@ -12,7 +12,7 @@
 #elif BUILDFLAG(IS_MAC)
 #include <Carbon/Carbon.h>
 #include "base/mac/scoped_cftyperef.h"
-#elif defined(USE_OZONE)
+#elif BUILDFLAG(IS_OZONE)
 #include "ui/events/ozone/layout/scoped_keyboard_layout_engine.h"  // nogncheck
 #endif
 
@@ -51,7 +51,7 @@
   ~ScopedKeyboardLayout();
 
  private:
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   std::unique_ptr<ScopedKeyboardLayoutEngine> scoped_keyboard_layout_engine_;
 #endif
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
diff --git a/ui/events/x/x11_event_translation.cc b/ui/events/x/x11_event_translation.cc
index 91f5aee4..0f0ce74 100644
--- a/ui/events/x/x11_event_translation.cc
+++ b/ui/events/x/x11_event_translation.cc
@@ -9,6 +9,7 @@
 #include "base/check.h"
 #include "base/notreached.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "ui/events/devices/x11/touch_factory_x11.h"
 #include "ui/events/event.h"
@@ -172,11 +173,11 @@
   auto event = std::make_unique<TouchEventX11>(
       type, EventLocationFromXEvent(xev), EventTimeFromXEvent(xev),
       GetTouchPointerDetailsFromXEvent(xev));
-#if defined(USE_OZONE)
-    // Touch events don't usually have |root_location| set differently than
-    // |location|, since there is a touch device to display association, but
-    // this doesn't happen in Ozone X11.
-    event->set_root_location(EventSystemLocationFromXEvent(xev));
+#if BUILDFLAG(IS_OZONE)
+  // Touch events don't usually have |root_location| set differently than
+  // |location|, since there is a touch device to display association, but
+  // this doesn't happen in Ozone X11.
+  event->set_root_location(EventSystemLocationFromXEvent(xev));
 #endif
   return event;
 }
diff --git a/ui/file_manager/base/js/test_harness.js b/ui/file_manager/base/js/test_harness.js
index 6df92c0..6f3f3d7 100644
--- a/ui/file_manager/base/js/test_harness.js
+++ b/ui/file_manager/base/js/test_harness.js
@@ -5,6 +5,8 @@
 // Mark the test as fully loaded. The Browser Test reads this.
 window.__TEST_LOADED__ = false;
 
+import 'chrome://webui-test/strings.m.js';
+
 async function run() {
   // Grab the JS module to test from the GET params.
   const params = new URLSearchParams(window.location.search);
diff --git a/ui/file_manager/file_manager/background/js/BUILD.gn b/ui/file_manager/file_manager/background/js/BUILD.gn
index 7f9bdec..541907a4 100644
--- a/ui/file_manager/file_manager/background/js/BUILD.gn
+++ b/ui/file_manager/file_manager/background/js/BUILD.gn
@@ -221,7 +221,6 @@
     ":mock_file_operation_manager",
     ":mock_progress_center",
     "//chrome/test/data/webui:chai_assert",
-    "//ui/file_manager/file_manager/common/js:mock_chrome",
   ]
 }
 
diff --git a/ui/file_manager/file_manager/background/js/file_operation_handler_unittest.js b/ui/file_manager/file_manager/background/js/file_operation_handler_unittest.js
index 38059f34..15b50c54 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_handler_unittest.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_handler_unittest.js
@@ -4,8 +4,6 @@
 
 import {assertEquals, assertGT, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
-import {installMockChrome} from '../../common/js/mock_chrome.js';
-
 import {FileOperationHandler} from './file_operation_handler.js';
 import {fileOperationUtil} from './file_operation_util.js';
 import {MockFileOperationManager} from './mock_file_operation_manager.js';
@@ -21,18 +19,6 @@
 let fileOperationHandler;
 
 /**
- * Mock chrome APIs.
- * @type {Object}
- */
-const mockChrome = {};
-
-mockChrome.fileManagerPrivate = {
-  onIOTaskProgressStatus: {
-    addListener: function(callback) {},
-  },
-};
-
-/**
  * Mock JS Date.
  *
  * The stop() method should be called to restore original Date.
@@ -70,18 +56,6 @@
 
 // Set up the test components.
 export function setUp() {
-  // Mock LoadTimeData strings.
-  window.loadTimeData.resetForTesting({
-    COPY_FILE_NAME: 'Copying $1...',
-    COPY_TARGET_EXISTS_ERROR: '$1 is already exists.',
-    COPY_FILESYSTEM_ERROR: 'Copy filesystem error: $1',
-    FILE_ERROR_GENERIC: 'File error generic.',
-    COPY_UNEXPECTED_ERROR: 'Copy unexpected error: $1',
-  });
-
-  // Install mock chrome APIs.
-  installMockChrome(mockChrome);
-
   // Create mock items needed for FileOperationHandler.
   fileOperationManager = new MockFileOperationManager();
   progressCenter = new MockProgressCenter();
diff --git a/ui/file_manager/file_manager/foreground/elements/files_xf_elements_unittest.js b/ui/file_manager/file_manager/foreground/elements/files_xf_elements_unittest.js
index d2db664..4c406fbf 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_xf_elements_unittest.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_xf_elements_unittest.js
@@ -6,7 +6,7 @@
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {waitUntil} from '../../common/js/test_error_reporting.js';
-import {util} from '../../common/js/util.js';
+import {str, util} from '../../common/js/util.js';
 
 import {DisplayPanel} from './xf_display_panel.js';
 
@@ -218,7 +218,7 @@
 
   // Check the default primary text displays a generic error message.
   // Note, the i18n message gets smooshed into 'An error occurred.' in the app.
-  assertEquals('FILE_ERROR_GENERIC', panelItem.primaryText);
+  assertEquals(str('FILE_ERROR_GENERIC'), panelItem.primaryText);
 
   // Check the secondary text is empty.
   assertEquals('', panelItem.secondaryText);
diff --git a/ui/file_manager/file_manager/foreground/js/file_type_filters_controller_unittest.js b/ui/file_manager/file_manager/foreground/js/file_type_filters_controller_unittest.js
index 91994ec..a98ecb5 100644
--- a/ui/file_manager/file_manager/foreground/js/file_type_filters_controller_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/file_type_filters_controller_unittest.js
@@ -3,12 +3,10 @@
 // found in the LICENSE file.
 
 import {NativeEventTarget as EventTarget} from 'chrome://resources/js/cr/event_target.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {EntryList, FakeEntryImpl} from '../../common/js/files_app_entry_types.js';
 import {metrics} from '../../common/js/metrics.js';
-import {installMockChrome} from '../../common/js/mock_chrome.js';
 import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js';
 import {DirectoryChangeEvent} from '../../externs/directory_change_event.js';
 import {FakeEntry} from '../../externs/files_app_entry_interfaces.js';
@@ -53,41 +51,6 @@
 const TOTAL_FILTER_BUTTON_COUNT = 5;
 
 export function setUp() {
-  // Mock loadTimeData strings.
-  loadTimeData.resetForTesting({
-    MEDIA_VIEW_ALL_ROOT_LABEL: 'All',
-    MEDIA_VIEW_AUDIO_ROOT_LABEL: 'Audio',
-    MEDIA_VIEW_IMAGES_ROOT_LABEL: 'Images',
-    MEDIA_VIEW_VIDEOS_ROOT_LABEL: 'Videos',
-    MEDIA_VIEW_DOCUMENTS_ROOT_LABEL: 'Documents',
-    RECENT_VIEW_FILTER_ON: 'on',
-    RECENT_VIEW_FILTER_OFF: 'off',
-    RECENT_VIEW_FILTER_RESET: 'reset',
-    FILTERS_IN_RECENTS_V2_ENABLED: true,
-  });
-
-  /**
-   * Mock chrome APIs.
-   * @type {!Object}
-   */
-  const mockChrome = {
-    fileManagerPrivate: {
-      SourceRestriction: {
-        ANY_SOURCE: 'any_source',
-        NATIVE_SOURCE: 'native_source',
-      },
-      RecentFileType: {
-        ALL: 'all',
-        AUDIO: 'audio',
-        IMAGE: 'image',
-        VIDEO: 'video',
-        DOCUMENT: 'document',
-      },
-    },
-  };
-
-  installMockChrome(mockChrome);
-
   class MockDirectoryModel extends EventTarget {
     constructor() {
       super();
diff --git a/ui/file_manager/file_manager/foreground/js/path_component_unittest.js b/ui/file_manager/file_manager/foreground/js/path_component_unittest.js
index a50dbc52..775b03d4 100644
--- a/ui/file_manager/file_manager/foreground/js/path_component_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/path_component_unittest.js
@@ -6,6 +6,7 @@
 
 import {MockVolumeManager} from '../../background/js/mock_volume_manager.js';
 import {MockFileSystem} from '../../common/js/mock_entry.js';
+import {str} from '../../common/js/util.js';
 import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js';
 
 import {PathComponent} from './path_component.js';
@@ -36,14 +37,14 @@
   // .files-by-id.
   await validate('/.files-by-id/1234/file', [
     [
-      'DRIVE_SHARED_WITH_ME_COLLECTION_LABEL',
+      str('DRIVE_SHARED_WITH_ME_COLLECTION_LABEL'),
       'fake-entry://drive_shared_with_me',
     ],
     ['file', 'filesystem:drive/.files-by-id/1234/file'],
   ]);
   await validate('/.files-by-id/1234/a/file', [
     [
-      'DRIVE_SHARED_WITH_ME_COLLECTION_LABEL',
+      str('DRIVE_SHARED_WITH_ME_COLLECTION_LABEL'),
       'fake-entry://drive_shared_with_me',
     ],
     ['a', 'filesystem:drive/.files-by-id/1234/a'],
@@ -52,14 +53,14 @@
   // .shortcut-targets-by-id.
   await validate('/.shortcut-targets-by-id/1-abc-xyz/file', [
     [
-      'DRIVE_SHARED_WITH_ME_COLLECTION_LABEL',
+      str('DRIVE_SHARED_WITH_ME_COLLECTION_LABEL'),
       'fake-entry://drive_shared_with_me',
     ],
     ['file', 'filesystem:drive/.shortcut-targets-by-id/1-abc-xyz/file'],
   ]);
   await validate('/.shortcut-targets-by-id/1-abc-xyz/a/file', [
     [
-      'DRIVE_SHARED_WITH_ME_COLLECTION_LABEL',
+      str('DRIVE_SHARED_WITH_ME_COLLECTION_LABEL'),
       'fake-entry://drive_shared_with_me',
     ],
     ['a', 'filesystem:drive/.shortcut-targets-by-id/1-abc-xyz/a'],
@@ -67,34 +68,34 @@
   ]);
   // Computers.
   await validate('/Computers/C1/file', [
-    ['DRIVE_COMPUTERS_LABEL', 'filesystem:drive/Computers'],
+    [str('DRIVE_COMPUTERS_LABEL'), 'filesystem:drive/Computers'],
     ['C1', 'filesystem:drive/Computers/C1'],
     ['file', 'filesystem:drive/Computers/C1/file'],
   ]);
   await validate('/Computers/C1/a/file', [
-    ['DRIVE_COMPUTERS_LABEL', 'filesystem:drive/Computers'],
+    [str('DRIVE_COMPUTERS_LABEL'), 'filesystem:drive/Computers'],
     ['C1', 'filesystem:drive/Computers/C1'],
     ['a', 'filesystem:drive/Computers/C1/a'],
     ['file', 'filesystem:drive/Computers/C1/a/file'],
   ]);
   // root.
   await validate('/root/file', [
-    ['DRIVE_MY_DRIVE_LABEL', 'filesystem:drive/root'],
+    [str('DRIVE_MY_DRIVE_LABEL'), 'filesystem:drive/root'],
     ['file', 'filesystem:drive/root/file'],
   ]);
   await validate('/root/a/file', [
-    ['DRIVE_MY_DRIVE_LABEL', 'filesystem:drive/root'],
+    [str('DRIVE_MY_DRIVE_LABEL'), 'filesystem:drive/root'],
     ['a', 'filesystem:drive/root/a'],
     ['file', 'filesystem:drive/root/a/file'],
   ]);
   // team_drives.
   await validate('/team_drives/S1/file', [
-    ['DRIVE_SHARED_DRIVES_LABEL', 'filesystem:drive/team_drives'],
+    [str('DRIVE_SHARED_DRIVES_LABEL'), 'filesystem:drive/team_drives'],
     ['S1', 'filesystem:drive/team_drives/S1'],
     ['file', 'filesystem:drive/team_drives/S1/file'],
   ]);
   await validate('/team_drives/S1/a/file', [
-    ['DRIVE_SHARED_DRIVES_LABEL', 'filesystem:drive/team_drives'],
+    [str('DRIVE_SHARED_DRIVES_LABEL'), 'filesystem:drive/team_drives'],
     ['S1', 'filesystem:drive/team_drives/S1'],
     ['a', 'filesystem:drive/team_drives/S1/a'],
     ['file', 'filesystem:drive/team_drives/S1/a/file'],
@@ -106,29 +107,29 @@
 
   // Downloads.
   await validate('/file', [
-    ['DOWNLOADS_DIRECTORY_LABEL', 'filesystem:downloads/'],
+    [str('DOWNLOADS_DIRECTORY_LABEL'), 'filesystem:downloads/'],
     ['file', 'filesystem:downloads/file'],
   ]);
   await validate('/a/file', [
-    ['DOWNLOADS_DIRECTORY_LABEL', 'filesystem:downloads/'],
+    [str('DOWNLOADS_DIRECTORY_LABEL'), 'filesystem:downloads/'],
     ['a', 'filesystem:downloads/a'],
     ['file', 'filesystem:downloads/a/file'],
   ]);
 
   // Special labels for '/Downloads', '/PvmDefault', '/Camera'.
   await validate('/Downloads/file', [
-    ['DOWNLOADS_DIRECTORY_LABEL', 'filesystem:downloads/'],
-    ['DOWNLOADS_DIRECTORY_LABEL', 'filesystem:downloads/Downloads'],
+    [str('DOWNLOADS_DIRECTORY_LABEL'), 'filesystem:downloads/'],
+    [str('DOWNLOADS_DIRECTORY_LABEL'), 'filesystem:downloads/Downloads'],
     ['file', 'filesystem:downloads/Downloads/file'],
   ]);
   await validate('/PvmDefault/file', [
-    ['DOWNLOADS_DIRECTORY_LABEL', 'filesystem:downloads/'],
-    ['PLUGIN_VM_DIRECTORY_LABEL', 'filesystem:downloads/PvmDefault'],
+    [str('DOWNLOADS_DIRECTORY_LABEL'), 'filesystem:downloads/'],
+    [str('PLUGIN_VM_DIRECTORY_LABEL'), 'filesystem:downloads/PvmDefault'],
     ['file', 'filesystem:downloads/PvmDefault/file'],
   ]);
   await validate('/Camera/file', [
-    ['DOWNLOADS_DIRECTORY_LABEL', 'filesystem:downloads/'],
-    ['CAMERA_DIRECTORY_LABEL', 'filesystem:downloads/Camera'],
+    [str('DOWNLOADS_DIRECTORY_LABEL'), 'filesystem:downloads/'],
+    [str('CAMERA_DIRECTORY_LABEL'), 'filesystem:downloads/Camera'],
     ['file', 'filesystem:downloads/Camera/file'],
   ]);
 }
diff --git a/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog_unittest.js b/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog_unittest.js
index c399683..a85c4017 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog_unittest.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import {assertInstanceof} from 'chrome://resources/js/assert.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {InstallLinuxPackageDialog} from './install_linux_package_dialog.js';
@@ -13,10 +12,6 @@
   /** @suppress {checkTypes,const} */
   chrome.app = {window: {current: () => null}};
 
-  // Mock loadTimeData.
-  loadTimeData.getString = id => id;
-  loadTimeData.data = {};
-
   let getInfoCallback;
   /** @suppress {checkTypes,const} */
   chrome.fileManagerPrivate = {
diff --git a/ui/file_manager/file_manager/widgets/README.md b/ui/file_manager/file_manager/widgets/README.md
index 7a062f5..2f3071b 100644
--- a/ui/file_manager/file_manager/widgets/README.md
+++ b/ui/file_manager/file_manager/widgets/README.md
@@ -2,30 +2,175 @@
 
 ## Overview
 
-Widgets are [Web
-Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components) that
-have the following responsibility:
+Widgets are
+[Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components)
+that have the following responsibility:
 
-1. Manage user input and events (Keyboard, Mouse, Touch, etc).
-2. Layout and style, the look & feel.
-3. Accessibility (a11y).
-4. Translation/Internationalization (i18n) & Localization (l10n), LTR & RTL
-   (Left To Right & Right To Left).
+1.  Manage user input and events (Keyboard, Mouse, Touch, etc).
+2.  Layout and style, the look & feel.
+3.  Accessibility (a11y).
+4.  Translation/Internationalization (i18n) & Localization (l10n), LTR & RTL
+    (Left To Right & Right To Left).
 
 Widgets should NOT:
 
-1. Handle business logic. Instead it should report the user actions or change of
-   local state by emitting events.
-2. Call any private API or storage API.
-3. Use Polymer. It should use native web component, `extends HTMLElement`.
+1.  Handle business logic. Instead it should report the user actions or change
+    of local state by emitting events.
+2.  Call any private API or storage API.
+3.  Use Polymer. Now [LitElement](https://lit.dev/) is preferred.
 
 ## How to create a new Widget?
 
-Internal documentation: http://go/xf-site/howtos/add-new-ts-file
+```typescript
+import { XfBase, property, customElement, state, css, query } from './xf_base.js';
 
-Example CL: crrev.com/c/3790650
+// Use @customElement decorator to link the component name with the component
+// class, no `customElements.define` is required.
+@customElement('xf-widget')
+// XfBase is our base widget class, all the widgets should extend from it.
+export class XfWidget extends XfBase {
+    // Section: API contract.
 
-## Native Web Components Features
+    // Exposed property/attributes.
+    // reflect: true is the key to connect attribute with this property.
+    @property({type: String, reflect: true}) label = '';
+    // We can specify a custom attribute name (otherwise the default
+    // attribute name would be "haschildren").
+    @property({type: Boolean, reflect: true, attribute: 'has-children'})
+    hasChildren = false;
+
+    // Emitted custom events.
+    // All the events emitted by this widget should be defined here. Since it's
+    // static, the consumer of the widget can also reference the event name by
+    // `XfWidget.events.BUTTON_CLICKED`.
+    static get events() {
+      return {
+        BUTTON_CLICKED: 'button_clicked',
+      } as const;
+    }
+
+    // Exposed public methods.
+    children() {
+      // Return the slotted element.
+      return this.items;
+    }
+
+    // Section: Styles.
+    static get styles() {
+      return getCSS();
+    }
+
+    // Section: Internal states.
+    // Define internal variables here which will only be used inside the
+    // render() (e.g. referred in the template).
+    @state() private buttonClicked_ = false;
+
+    // Section: Internal variables.
+    // Define internal variables here which won't affect render()
+    private boundOnWindowScrolled_ = this.onWindowScrolled_.bind(this);
+
+    // Section: Child DOM elements.
+    // We can query the child elements defined in the render() here.
+    @query('button') $button!: HTMLButtonElement;
+    // We can even query the slotted elements.
+    @queryAssignedElements() items!: Array<HTMLSpanElement>;
+
+    // Section: Constructor.
+    constructor() {
+      // Usually not needed, because the template defined in the render()
+      // will be attached to shadow DOM automatically.
+    }
+
+    // Section: Render method.
+    override render() {
+      // * We can pass property/state or any other valid variables to the
+      //   template here.
+      // * We can event bind event directly on the element, no bind(this) is
+      //   required, for example, the <button> click below.
+      // * We can do conditional render like this with nested html tag, for
+      //   example, only render "Clicked" when the state is true.
+      return html`
+        <span>${this.label}</span>
+        <slot></slot>
+        <button @click=${this.onButtonClicked_}>Update label</button>
+        ${ this.buttonClicked_ ? html`<p>Clicked</p>` : '' }
+      `;
+    }
+
+    // Section: Lifecycle methods.
+    connectedCallback() {
+      // Widget is being added to the document, add event listener for
+      // global events.
+      document.addEventListener('scroll', this.boundOnWindowScrolled_);
+    }
+
+    disconnectedCallback() {
+      // Widget is being removed from the document, remove event listener
+      // for global events to prevent memory leak.
+      document.removeEventListener('scroll', this.boundOnWindowScrolled_);
+    }
+
+    // Section: Internal event handlers.
+    private onButtonClicked_(e: MouseEvent) {
+      this.label = 'something else';
+      this.buttonClicked_ = true;
+      // Once the property/state has been changed, no need to call render()
+      // manually, it will be invoked automatically.
+
+      // Dispatch custom event to the consumer.
+      // IMPORTANT: use `bubbles: true, composed: true` to be able to traverse
+      // across all Shadow DOMs.
+      this.dispatchEvent(new CustomEvent(XfWidget.events.BUTTON_CLICKED, {
+        bubbles: true,
+        composed: true,
+        detail: { label: this.label },
+      }));
+    }
+
+    private onWindowScrolled_(e: Event) {
+      // logic to respond window scroll
+    }
+
+    // Section: Other private helper methods.
+    private myHelper_() {
+      // random helper function
+    }
+}
+
+// We are define all CSS outside the class so it's easier for
+// Developer/Reviewer to focus on other logic in the class.
+function getCSS() {
+  return css`
+    button {
+      padding: 2px;
+    }
+  `;
+}
+
+// Section: Type definitions.
+// Export the type for the event so caller code can use it in their listener.
+export type WidgetButtonClickedEvent = CustomEvent<{label: string}>;
+// TS way to tell the compiler about the types of our custom element.
+declare global {
+  // When dispatching `button_clicked` event that's the type of the event, aka:
+  // event.detail.label exists and is a string.
+  interface HTMLElementEventMap {
+    [XfWidget.events.BUTTON_CLICKED]: WidgetButtonClickedEvent;
+  }
+
+  // When fetching the element from the DOM via:
+  // document.querySelector('xf-widget');
+  // this is the type returned (or null).
+  interface HTMLElementTagNameMap {
+    'xf-widget': XfWidget;
+  }
+}
+```
+
+## LitElement Features
+
+A lot of features from Native Web Component are also available in the context of
+LitElement.
 
 ### Slots
 
@@ -33,36 +178,39 @@
 are used to populate parts of the template with content managed outside of the
 widget.
 
-For example, for a generic button `<my-button>`, the label of the button should
-be managed outside of the button code, `<my-button>The Label</mybutton>`.
+For example, with the above example `xf-widget` we can pass additional element
+as children to it.
 
 ```html
-<template id="my-button">
-  <button class="really-different-button">
-    <slot></slot>
-  </button>
-</template>
+<xf-widget>
+  <span>child label</span>
+</xf-widget>
 ```
 
-Slots can be used to named parts:
+Here the `<span>child label</span>` can replace the `<slot></slot>` in the above
+render() function.
 
-```html
-<template id="example-dialog">
-  <h1><slot name="title">Dialog</slot></h1>
-  <p> <slot name="message"></slot></p>
+Slots can also be used to named parts:
 
-  <div>
-    <slot name="buttons">
-      <button id="ok">Ok</button>
-    </slot>
-  </div>
-</template>
+```typescript
+// render() function for `example-dialog` widget.
+render() {
+  return html`
+    <h1><slot name="title">Dialog</slot></h1>
+    <p><slot name="message"></slot></p>
+
+    <div>
+      <slot name="buttons">
+        <button id="ok">Ok</button>
+      </slot>
+    </div>
+  `;
+}
 ```
 
 The user of the `<example-dialog>` can fill in the slots like:
 
 ```html
-
 <example-dialog>
   <span slot="title">Delete?</span>
   <span slot="message">Do you want to delete the file "abc.txt"?</span>
@@ -84,56 +232,41 @@
 [change](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event)
 event whenever the user selects an option either via keyboard or mouse.
 
-The event listeners should be added on the `connectedCallback()` and removed in
-the `disconnectedCallback()`, see section [Lifecycle
-Methods](#lifecycle-methods).
+The event listeners should be added by `@<event>` binding in the render()
+function to bind event handler to the corresponding event, which is illustrated
+in the above code snippet. Alternatively, we can also bind the event in the
+`connectedCallback()` and removed it in the `disconnectedCallback()`, see
+section [Lifecycle Methods](#lifecycle-methods).
 
-For TypeScript we should declare the types used in the emitted events.
-
-```typescript
-// A const to avoid special strings around the code base.
-export const DARK_ROOM_CHANGED = 'dark-room-changed';
-
-// The type of the emitted event. The `event.detail` has an boolean attribute
-// `enabled`.
-export type DarkRoomChangedEvent = CustomEvent<{enabled: boolean}>;
-
-// TS type mapping.
-declare global {
-  interface HTMLElementEventMap {
-    // Enforces strong typing for listeners of "dark-room-changed" event.
-    [DARK_ROOM_CHANGED]: DarkRoomChangedEvent;
-  }
-
-  // With this, TS knows that <xf-dark-room> is an instance of `XfDarkRoom`.
-  interface HTMLElementTagNameMap {
-    'xf-dark-room': XfDarkRoom;
-  }
-}
-```
+For TypeScript we should declare the types used in the emitted events, which is
+illustrated in the above code snippet.
 
 ### Expose style
 
 A widget can allow customization of its style using the following features:
 
-*  [CSS
-   variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties)
-*  [part & ::part()](https://developer.mozilla.org/en-US/docs/Web/CSS/::part)
-   and
-   [exportparts](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/exportparts)
+*   [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties)
+*   [part & ::part()](https://developer.mozilla.org/en-US/docs/Web/CSS/::part)
+    and
+    [exportparts](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/exportparts)
 
-```html
-<template id="my-element">
-  <style>
+```typescript
+render() {
+  return html`
+    <div part="tab active">Tab 1</div>
+    <div part="tab">Tab 2</div>
+    <div part="tab">Tab 3</div>
+  `;
+}
+
+function getCSS() {
+  return css`
     :host {
       color: var(--my-element-color, #f4f4f4);
       border-radius: var(--my-element-border-radius, 2px);
     }
-  </style>
-  <div part="tab active">Tab 1</div>
-  <div part="tab">Tab 2</div>
-  <div part="tab">Tab 3</div>
-</template>
+  `;
+}
 ```
 
 ```html
@@ -169,40 +302,20 @@
 [MDN](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks)
 for more information.
 
-*  `connectedCallback()`: Invoked each time the custom element is appended into
-   a document-connected element.
-*  `disconnectedCallback()`: Invoked each time the custom element is
-   disconnected from the document's DOM.
-*  `attributeChangedCallback()`: Invoked each time one of the custom element's
-   attributes is added, removed, or changed. Which attributes to notice change
-   for is specified in a `static get observedAttributes()` method.
-*  `adoptedCallback()`: Invoked each time the custom element is moved to a new
-   document.
-
-### Attributes
-
-Widget can receive data/state via its attributes, for example `<my-element
-type="text"></my-element>`.
-
-In this example `type` is the attribute name.
-```typescript
-class MyElement extends HTMLElement {
-  static get observedAttributes() {
-    return ['text'];
-  }
-
-  attributeChangedCallback(name: string, oldValue: string, newValue: string) {
-    if (name === 'type') {
-      this.type = newValue;
-    }
-  }
-}
-```
+*   `connectedCallback()`: Invoked each time the custom element is appended into
+    a document-connected element.
+*   `disconnectedCallback()`: Invoked each time the custom element is
+    disconnected from the document's DOM.
+*   `attributeChangedCallback()`: Invoked each time one of the custom element's
+    attributes is added, removed, or changed. Which attributes to notice change
+    for is specified in a `static get observedAttributes()` method. With
+    LitElement we barely need this callback.
+*   `adoptedCallback()`: Invoked each time the custom element is moved to a new
+    document.
 
 ### A11y
 
-[`tabindex`](
-https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex)
+[`tabindex`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex)
 and ARIA attributes, especially `aria-label` and `role` are used to enhance the
 content for users.
 
@@ -213,9 +326,35 @@
 
 Every widget should have its unittest checking the its exposed API. It should
 test:
-1. The events the widget emits, e.g.: Show/hide or open/close, content updated,
-   user has selected something, etc.
-2. The behavior exposed, parts showing/hiding, the different status it can have,
-   etc.
-3. Any internally calculated layout/style/dimensions, e.g.: positioning
-   top/left, height/width, etc.
+
+1.  The events the widget emits, e.g.: Show/hide or open/close, content updated,
+    user has selected something, etc.
+2.  The behavior exposed, parts showing/hiding, the different status it can
+    have, etc.
+3.  Any internally calculated layout/style/dimensions, e.g.: positioning
+    top/left, height/width, etc.
+
+Since the render process is asynchronous and controlled by the Lit library, we
+need to explicitly wait for the render to be finished before we can do any
+assertion in the unit test. This is usually happening for:
+
+*   the initial render
+*   the re-render triggered by property/state change
+
+In LitElement, we can rely on the
+[await element.updateComplete](https://lit.dev/docs/components/lifecycle/#updatecomplete)
+to explicitly wait for the render to be completed.
+
+```typescript
+// For initial render.
+document.body.innerHTML = '<xf-widget></xf-widget>';
+const element = document.querySelector('xf-widget')!;
+element.label = 'abcd';
+await element.updateComplete;
+
+// For re-render.
+const button = element.shadowRoot!.querySelector('button');
+button.click();
+await element.updateComplete;
+// Assert label change.
+```
diff --git a/ui/file_manager/file_manager/widgets/xf_breadcrumb.ts b/ui/file_manager/file_manager/widgets/xf_breadcrumb.ts
index 7a0be00c..9996f0a 100644
--- a/ui/file_manager/file_manager/widgets/xf_breadcrumb.ts
+++ b/ui/file_manager/file_manager/widgets/xf_breadcrumb.ts
@@ -40,7 +40,6 @@
   @state() private isMenuOpen_ = false;
 
   static override get styles() {
-    console.log('aaa');
     return getCSS();
   }
 
diff --git a/ui/file_manager/image_loader/image_loader.js b/ui/file_manager/image_loader/image_loader.js
index aad077a..5a7d0d2 100644
--- a/ui/file_manager/image_loader/image_loader.js
+++ b/ui/file_manager/image_loader/image_loader.js
@@ -97,7 +97,6 @@
  * @type {Array<string>}
  */
 ImageLoader.ALLOWED_CLIENT_ORIGINS = [
-  'chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj',  // File Manager
   'chrome://file-manager',  // File Manager SWA
 ];
 
diff --git a/ui/file_manager/image_loader/manifest.json b/ui/file_manager/image_loader/manifest.json
index c60fc4b9..85648db1 100644
--- a/ui/file_manager/image_loader/manifest.json
+++ b/ui/file_manager/image_loader/manifest.json
@@ -21,9 +21,6 @@
     "persistent": false
   },
   "externally_connectable": {
-    "ids": [
-      "hhaomjibdihmijegdhdafkllkbggdgoj"  // File Manager
-    ],
     "matches": [
       "chrome://file-manager/*"  // File Manager SWA
     ]
diff --git a/ui/gfx/color_space.cc b/ui/gfx/color_space.cc
index a67f02c..3a053d4 100644
--- a/ui/gfx/color_space.cc
+++ b/ui/gfx/color_space.cc
@@ -154,6 +154,13 @@
 }
 
 // static
+ColorSpace ColorSpace::CreateExtendedSRGB10Bit() {
+  return ColorSpace(PrimaryID::P3, TransferID::CUSTOM_HDR, MatrixID::RGB,
+                    RangeID::FULL, nullptr,
+                    &SkNamedTransferFnExt::kSRGBExtended1023Over510, true);
+}
+
+// static
 ColorSpace ColorSpace::CreatePiecewiseHDR(
     PrimaryID primaries,
     float sdr_joint,
@@ -718,6 +725,16 @@
   return matrix_ == MatrixID::RGB;
 }
 
+bool ColorSpace::IsTransferFunctionEqualTo(
+    const skcms_TransferFunction& fn) const {
+  if (fn.a == transfer_params_[0] && fn.b == transfer_params_[1] &&
+      fn.c == transfer_params_[2] && fn.d == transfer_params_[3] &&
+      fn.e == transfer_params_[4] && fn.f == transfer_params_[5] &&
+      fn.g == transfer_params_[6])
+    return true;
+  return false;
+}
+
 bool ColorSpace::Contains(const ColorSpace& other) const {
   if (primaries_ == PrimaryID::INVALID ||
       other.primaries_ == PrimaryID::INVALID)
diff --git a/ui/gfx/color_space.h b/ui/gfx/color_space.h
index 9084b272..6ed38775 100644
--- a/ui/gfx/color_space.h
+++ b/ui/gfx/color_space.h
@@ -12,6 +12,7 @@
 
 #include "base/gtest_prod_util.h"
 #include "build/build_config.h"
+#include "skia/ext/skcolorspace_trfn.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "ui/gfx/color_space_export.h"
@@ -231,6 +232,11 @@
                       RangeID::FULL);
   }
 
+  // An extended sRGB ColorSpace that matches the sRGB EOTF but extends to
+  // 4.99x the headroom of SDR brightness. Designed for a 10 bpc buffer format.
+  // Uses P3 primaries. An HDR ColorSpace suitable for blending and compositing.
+  static ColorSpace CreateExtendedSRGB10Bit();
+
   // Create a piecewise-HDR color space.
   // - If |primaries| is CUSTOM, then |custom_primary_matrix| must be
   //   non-nullptr.
@@ -383,6 +389,10 @@
   // unless the color space has a non-RGB matrix.
   bool HasExtendedSkTransferFn() const;
 
+  // Returns true if the transfer function values of this color space match
+  // those of the passed in skcms_TransferFunction.
+  bool IsTransferFunctionEqualTo(const skcms_TransferFunction& fn) const;
+
   // Returns true if each color in |other| can be expressed in this color space.
   bool Contains(const ColorSpace& other) const;
 
diff --git a/ui/gfx/gpu_extra_info.h b/ui/gfx/gpu_extra_info.h
index d63194da..e8fa594 100644
--- a/ui/gfx/gpu_extra_info.h
+++ b/ui/gfx/gpu_extra_info.h
@@ -8,10 +8,11 @@
 #include <string>
 #include <vector>
 
+#include "build/build_config.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/gfx_export.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/buildflags.h"
 #if BUILDFLAG(OZONE_PLATFORM_X11)
 #define USE_OZONE_PLATFORM_X11
diff --git a/ui/gfx/gpu_memory_buffer.h b/ui/gfx/gpu_memory_buffer.h
index 8ad8af9..cca6f3e4 100644
--- a/ui/gfx/gpu_memory_buffer.h
+++ b/ui/gfx/gpu_memory_buffer.h
@@ -15,7 +15,7 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/gfx_export.h"
 
-#if defined(USE_OZONE) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "ui/gfx/native_pixmap_handle.h"
 #elif BUILDFLAG(IS_MAC)
 #include "ui/gfx/mac/io_surface.h"
diff --git a/ui/gfx/native_widget_types.h b/ui/gfx/native_widget_types.h
index 73d9dce..d6eafd4 100644
--- a/ui/gfx/native_widget_types.h
+++ b/ui/gfx/native_widget_types.h
@@ -247,7 +247,7 @@
 #elif BUILDFLAG(IS_ANDROID)
 typedef ANativeWindow* AcceleratedWidget;
 constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
-#elif defined(USE_OZONE)
+#elif BUILDFLAG(IS_OZONE)
 typedef uint32_t AcceleratedWidget;
 constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
 #else
diff --git a/ui/gl/gl_display.cc b/ui/gl/gl_display.cc
index e6fdcbd5..2ba942a 100644
--- a/ui/gl/gl_display.cc
+++ b/ui/gl/gl_display.cc
@@ -31,9 +31,9 @@
 #include "ui/gl/glx_util.h"
 #endif  // defined(USE_GLX)
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/buildflags.h"
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
 
 #if BUILDFLAG(IS_ANDROID)
 #include <android/native_window_jni.h>
@@ -359,14 +359,14 @@
       extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
       extra_display_attribs.push_back(
           EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE);
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #if BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(OZONE_PLATFORM_X11)
       extra_display_attribs.push_back(
           EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE);
       extra_display_attribs.push_back(
           EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE);
 #endif  // BUILDFLAG(OZONE_PLATFORM_X11)
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
       return GetPlatformANGLEDisplay(
           display, EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE, enabled_angle_features,
           disabled_angle_features, extra_display_attribs);
diff --git a/ui/gl/gl_surface.h b/ui/gl/gl_surface.h
index 69aeddf..7f5911b 100644
--- a/ui/gl/gl_surface.h
+++ b/ui/gl/gl_surface.h
@@ -30,7 +30,7 @@
 #include "ui/gl/gl_surface_format.h"
 #include "ui/gl/gpu_preference.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/gfx/native_pixmap.h"
 #endif
 
@@ -64,7 +64,7 @@
 class EGLTimestampClient;
 
 // OverlayImage is a platform specific type for overlay plane image data.
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 using OverlayImage = scoped_refptr<gfx::NativePixmap>;
 #elif BUILDFLAG(IS_MAC)
 using OverlayImage = gfx::ScopedIOSurface;
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 6e56cf7b..78c190c4 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -33,9 +33,9 @@
 #include "ui/gl/scoped_make_current.h"
 #include "ui/gl/sync_control_vsync_provider.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/buildflags.h"
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
 
 #if BUILDFLAG(IS_ANDROID)
 #include <android/native_window_jni.h>
diff --git a/ui/gl/init/gl_factory.cc b/ui/gl/init/gl_factory.cc
index 603eaa4..797ee45 100644
--- a/ui/gl/init/gl_factory.cc
+++ b/ui/gl/init/gl_factory.cc
@@ -21,7 +21,7 @@
 #include "ui/gl/gl_version_info.h"
 #include "ui/gl/init/gl_initializer.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/base/ui_base_features.h"
 #include "ui/ozone/public/ozone_platform.h"
 #endif
diff --git a/ui/gl/init/gl_factory.h b/ui/gl/init/gl_factory.h
index b4c8f7a4..0ba334b 100644
--- a/ui/gl/init/gl_factory.h
+++ b/ui/gl/init/gl_factory.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/memory/ref_counted.h"
+#include "build/build_config.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gl/gl_display.h"
@@ -94,7 +95,7 @@
     GLDisplay* display,
     gfx::AcceleratedWidget window);
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 // Creates a GL surface that renders directly into a window with surfaceless
 // semantics - there is no default framebuffer and the primary surface must
 // be presented as an overlay. If surfaceless mode is not supported or
@@ -102,7 +103,7 @@
 GL_INIT_EXPORT scoped_refptr<GLSurface> CreateSurfacelessViewGLSurface(
     GLDisplay* display,
     gfx::AcceleratedWidget window);
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
 
 // Creates a GL surface used for offscreen rendering.
 GL_INIT_EXPORT scoped_refptr<GLSurface> CreateOffscreenGLSurface(
diff --git a/ui/gl/init/gl_initializer_ozone.cc b/ui/gl/init/gl_initializer_ozone.cc
index 345f94e..0d1c500 100644
--- a/ui/gl/init/gl_initializer_ozone.cc
+++ b/ui/gl/init/gl_initializer_ozone.cc
@@ -10,12 +10,9 @@
 #include "ui/gl/gl_gl_api_implementation.h"
 #include "ui/gl/gl_surface.h"
 #include "ui/gl/gl_utils.h"
-
-#if defined(USE_OZONE)
 #include "ui/gl/init/gl_display_egl_util_ozone.h"
 #include "ui/gl/init/ozone_util.h"
 #include "ui/ozone/public/ozone_platform.h"
-#endif
 
 namespace gl {
 namespace init {
diff --git a/ui/gl/test/gl_image_test_support.cc b/ui/gl/test/gl_image_test_support.cc
index 277c29de..744dd82 100644
--- a/ui/gl/test/gl_image_test_support.cc
+++ b/ui/gl/test/gl_image_test_support.cc
@@ -8,12 +8,13 @@
 
 #include "base/check_op.h"
 #include "base/notreached.h"
+#include "build/build_config.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/half_float.h"
 #include "ui/gl/init/gl_factory.h"
 #include "ui/gl/test/gl_surface_test_support.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "base/run_loop.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/ozone/public/ozone_platform.h"
@@ -36,7 +37,7 @@
 // static
 GLDisplay* GLImageTestSupport::InitializeGL(
     absl::optional<GLImplementationParts> prefered_impl) {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   ui::OzonePlatform::InitParams params;
   params.single_process = true;
   ui::OzonePlatform::InitializeForGPU(params);
@@ -52,7 +53,7 @@
 
   GLDisplay* display =
       GLSurfaceTestSupport::InitializeOneOffImplementation(impl, true);
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   // Make sure all the tasks posted to the current task runner by the
   // initialization functions are run before running the tests.
   base::RunLoop().RunUntilIdle();
diff --git a/ui/gl/test/gl_surface_test_support.cc b/ui/gl/test/gl_surface_test_support.cc
index 58fd3a0a..43dd9e6 100644
--- a/ui/gl/test/gl_surface_test_support.cc
+++ b/ui/gl/test/gl_surface_test_support.cc
@@ -18,7 +18,7 @@
 #include "ui/platform_window/common/platform_window_defaults.h"  // nogncheck
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
@@ -29,7 +29,7 @@
 GLDisplay* InitializeOneOffHelper(bool init_extensions) {
   DCHECK_EQ(kGLImplementationNone, GetGLImplementation());
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   ui::OzonePlatform::InitParams params;
   params.single_process = true;
   ui::OzonePlatform::InitializeForGPU(params);
@@ -109,7 +109,7 @@
 
 // static
 GLDisplay* GLSurfaceTestSupport::InitializeOneOffWithMockBindings() {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   ui::OzonePlatform::InitParams params;
   params.single_process = true;
   ui::OzonePlatform::InitializeForGPU(params);
@@ -121,7 +121,7 @@
 
 // static
 GLDisplay* GLSurfaceTestSupport::InitializeOneOffWithStubBindings() {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   ui::OzonePlatform::InitParams params;
   params.single_process = true;
   ui::OzonePlatform::InitializeForGPU(params);
diff --git a/ui/gl/test/run_all_unittests.cc b/ui/gl/test/run_all_unittests.cc
index 044531b..fc9d3050 100644
--- a/ui/gl/test/run_all_unittests.cc
+++ b/ui/gl/test/run_all_unittests.cc
@@ -14,7 +14,7 @@
 #include "base/test/mock_chrome_application_mac.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "base/command_line.h"
 #include "mojo/core/embedder/embedder.h"  // nogncheck
 #include "ui/base/ui_base_features.h"
@@ -43,7 +43,7 @@
     task_environment_ = std::make_unique<base::test::TaskEnvironment>(
         base::test::TaskEnvironment::MainThreadType::UI);
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
     // Make Ozone run in single-process mode, where it doesn't expect a GPU
     // process and it spawns and starts its own DRM thread. Note that this mode
     // still requires a mojo pipe for in-process communication between the host
@@ -68,7 +68,7 @@
 }  // namespace
 
 int main(int argc, char** argv) {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   mojo::core::Init();
 #endif
 
diff --git a/ui/message_center/views/notification_view_base_unittest.cc b/ui/message_center/views/notification_view_base_unittest.cc
index d7287f1..90fde41 100644
--- a/ui/message_center/views/notification_view_base_unittest.cc
+++ b/ui/message_center/views/notification_view_base_unittest.cc
@@ -503,7 +503,7 @@
 }
 
 // TODO(crbug.com/1232197): Test failing on linux-lacros-tester-rel and ozone.
-#if BUILDFLAG(IS_CHROMEOS_LACROS) || defined(USE_OZONE)
+#if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_OZONE)
 #define MAYBE_TestInlineReply DISABLED_TestInlineReply
 #else
 #define MAYBE_TestInlineReply TestInlineReply
diff --git a/ui/ozone/platform/drm/ozone_platform_drm.cc b/ui/ozone/platform/drm/ozone_platform_drm.cc
index 12826112..a86cbf4d 100644
--- a/ui/ozone/platform/drm/ozone_platform_drm.cc
+++ b/ui/ozone/platform/drm/ozone_platform_drm.cc
@@ -279,6 +279,13 @@
     }
   }
 
+  void PostCreateMainMessageLoop(base::OnceCallback<void()> shutdown_cb,
+                                 scoped_refptr<base::SingleThreadTaskRunner>
+                                     user_input_task_runner) override {
+    event_factory_ozone_->SetUserInputTaskRunner(
+        std::move(user_input_task_runner));
+  }
+
   const PlatformRuntimeProperties& GetPlatformRuntimeProperties() override {
     DCHECK(has_initialized_ui() || has_initialized_gpu());
     return host_properties_;
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index 6a6ab207..0228a265d 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -383,7 +383,8 @@
   }
 
   void PostCreateMainMessageLoop(
-      base::OnceCallback<void()> shutdown_cb) override {
+      base::OnceCallback<void()> shutdown_cb,
+      scoped_refptr<base::SingleThreadTaskRunner>) override {
     DCHECK(connection_);
     connection_->SetShutdownCb(std::move(shutdown_cb));
   }
diff --git a/ui/ozone/platform/x11/ozone_platform_x11.cc b/ui/ozone/platform/x11/ozone_platform_x11.cc
index 8743c12e..7e93d25 100644
--- a/ui/ozone/platform/x11/ozone_platform_x11.cc
+++ b/ui/ozone/platform/x11/ozone_platform_x11.cc
@@ -289,7 +289,8 @@
   }
 
   void PostCreateMainMessageLoop(
-      base::OnceCallback<void()> shutdown_cb) override {
+      base::OnceCallback<void()> shutdown_cb,
+      scoped_refptr<base::SingleThreadTaskRunner>) override {
     // Installs the X11 error handlers for the UI process after the
     // main message loop has started. This will allow us to exit cleanly
     // if X exits before we do.
diff --git a/ui/ozone/public/ozone_platform.cc b/ui/ozone/public/ozone_platform.cc
index 0f6c3b50..2450476 100644
--- a/ui/ozone/public/ozone_platform.cc
+++ b/ui/ozone/public/ozone_platform.cc
@@ -182,7 +182,8 @@
 }
 
 void OzonePlatform::PostCreateMainMessageLoop(
-    base::OnceCallback<void()> shutdown_cb) {}
+    base::OnceCallback<void()> shutdown_cb,
+    scoped_refptr<base::SingleThreadTaskRunner> input_event_task_runner) {}
 
 void OzonePlatform::PostMainMessageLoopRun() {}
 
diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h
index b250b8e..8850a11 100644
--- a/ui/ozone/public/ozone_platform.h
+++ b/ui/ozone/public/ozone_platform.h
@@ -218,11 +218,13 @@
   // error handlers if supported so that we can print errors during the browser
   // process' start up).
   static void PreEarlyInitialization();
-  // Sets error handlers if supported for the browser process after the message
-  // loop started. It's required to call this so that we can exit cleanly if the
+  // Sets error handlers if supported for the browser process, and provides a
+  // task_runner suitable for handling user input after the message loop
+  // started. It's required to call this so that we can exit cleanly if the
   // server can exit before we do.
   virtual void PostCreateMainMessageLoop(
-      base::OnceCallback<void()> shutdown_cb);
+      base::OnceCallback<void()> shutdown_cb,
+      scoped_refptr<base::SingleThreadTaskRunner> user_input_task_runner);
   // Resets the error handlers if set.
   virtual void PostMainMessageLoopRun();
 
diff --git a/ui/platform_window/platform_window_init_properties.h b/ui/platform_window/platform_window_init_properties.h
index 23e24eb6c..832e0b2 100644
--- a/ui/platform_window/platform_window_init_properties.h
+++ b/ui/platform_window/platform_window_init_properties.h
@@ -144,7 +144,7 @@
   absl::optional<std::string> restore_window_id_source;
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   // Specifies whether the current window requests key-events that matches
   // system shortcuts.
   bool inhibit_keyboard_shortcuts = false;
diff --git a/ui/views/controls/editable_combobox/editable_combobox_unittest.cc b/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
index e8ded6a..4fa85d1 100644
--- a/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
+++ b/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
@@ -38,7 +38,7 @@
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_utils.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/events/ozone/layout/keyboard_layout_engine_test_utils.h"
 #endif
 
@@ -141,7 +141,7 @@
 void EditableComboboxTest::SetUp() {
   ViewsTestBase::SetUp();
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   // TODO(crbug.com/1209477): Wayland bots use Weston with Headless backend that
   // sets up XkbKeyboardLayoutEngine differently. When that is fixed, remove the
   // workaround below.
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index eedf593e..bbc0dc1 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -72,7 +72,7 @@
 #include "ui/aura/window_delegate.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
@@ -115,7 +115,7 @@
 #endif
 
 bool ShouldIgnoreScreenBoundsForMenus() {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   // Some platforms, such as Wayland, disallow client applications to manipulate
   // global screen coordinates, requiring menus to be positioned relative to
   // their parent windows. See comment in ozone_platform_wayland.cc.
diff --git a/ui/views/controls/menu/menu_controller_unittest.cc b/ui/views/controls/menu/menu_controller_unittest.cc
index d7d76a6..c93717b 100644
--- a/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/ui/views/controls/menu/menu_controller_unittest.cc
@@ -63,7 +63,7 @@
 #include "ui/views/controls/menu/menu_pre_target_handler.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/buildflags.h"
 #include "ui/ozone/public/ozone_platform.h"
 #if BUILDFLAG(OZONE_PLATFORM_X11)
@@ -86,7 +86,7 @@
 using ::ui::mojom::DragOperation;
 
 bool ShouldIgnoreScreenBoundsForMenus() {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   // Some platforms, such as Wayland, disallow client applications to manipulate
   // global screen coordinates, requiring menus to be positioned relative to
   // their parent windows. See comment in ozone_platform_wayland.cc.
diff --git a/ui/views/controls/menu/menu_host.cc b/ui/views/controls/menu/menu_host.cc
index cd3b086..f83822c 100644
--- a/ui/views/controls/menu/menu_host.cc
+++ b/ui/views/controls/menu/menu_host.cc
@@ -31,7 +31,7 @@
 #include "ui/aura/window.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/base/ui_base_features.h"
 #include "ui/ozone/public/ozone_platform.h"
 #endif
diff --git a/ui/views/controls/menu/menu_runner_impl.cc b/ui/views/controls/menu/menu_runner_impl.cc
index bed24dc..711d6c4 100644
--- a/ui/views/controls/menu/menu_runner_impl.cc
+++ b/ui/views/controls/menu/menu_runner_impl.cc
@@ -22,7 +22,7 @@
 #include "ui/events/win/system_event_state_lookup.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/base/ui_base_features.h"
 #include "ui/events/event_constants.h"
 #include "ui/ozone/public/ozone_platform.h"
@@ -46,7 +46,7 @@
   }
 }
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 bool IsAltPressed() {
   if (const auto* const platorm_menu_utils =
           ui::OzonePlatform::GetInstance()->GetPlatformMenuUtils()) {
@@ -55,7 +55,7 @@
   }
   return false;
 }
-#endif  // defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_OZONE)
 
 }  // namespace
 
@@ -259,7 +259,7 @@
   // Show mnemonics if the button has focus or alt is pressed.
 #if BUILDFLAG(IS_WIN)
   show_mnemonics |= ui::win::IsAltPressed();
-#elif defined(USE_OZONE)
+#elif BUILDFLAG(IS_OZONE)
   show_mnemonics |= IsAltPressed();
 #elif BUILDFLAG(IS_MAC)
   show_mnemonics = false;
diff --git a/ui/views/controls/menu/menu_runner_unittest.cc b/ui/views/controls/menu/menu_runner_unittest.cc
index bc766f7..dd8fa63 100644
--- a/ui/views/controls/menu/menu_runner_unittest.cc
+++ b/ui/views/controls/menu/menu_runner_unittest.cc
@@ -158,7 +158,7 @@
 // Tests that a key press on a US keyboard layout activates the correct menu
 // item.
 // This test is flaky on ozone (https://crbug.com/1197217).
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #define MAYBE_LatinMnemonic DISABLED_LatinMnemonic
 #else
 #define MAYBE_LatinMnemonic LatinMnemonic
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index f5f69ff..c3ac2ae 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -86,7 +86,7 @@
 #include "ui/base/cocoa/secure_password_input.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/base/ui_base_features.h"
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/ozone/public/platform_gl_egl_utility.h"
@@ -177,11 +177,10 @@
 }
 
 bool CanUseTransparentBackgroundForDragImage() {
-#if defined(USE_OZONE)
-    const auto* const egl_utility =
-        ui::OzonePlatform::GetInstance()->GetPlatformGLEGLUtility();
-    return egl_utility ? egl_utility->IsTransparentBackgroundSupported()
-                       : false;
+#if BUILDFLAG(IS_OZONE)
+  const auto* const egl_utility =
+      ui::OzonePlatform::GetInstance()->GetPlatformGLEGLUtility();
+  return egl_utility ? egl_utility->IsTransparentBackgroundSupported() : false;
 #else
   // Other platforms allow this.
   return true;
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index 541ea02..cbf4ab1 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -77,7 +77,7 @@
 #include "ui/base/cocoa/text_services_context_menu.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/events/ozone/layout/keyboard_layout_engine_test_utils.h"
 #endif
 
@@ -414,7 +414,7 @@
       std::make_unique<ui::TestClipboard>());
   ViewsTestBase::SetUp();
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   // TODO(crbug.com/1209477): Wayland bots use Weston with Headless backend that
   // sets up XkbKeyboardLayoutEngine differently. When that is fixed, remove the
   // workaround below.
diff --git a/ui/views/examples/examples_main_proc.cc b/ui/views/examples/examples_main_proc.cc
index bf12e17..64727ab 100644
--- a/ui/views/examples/examples_main_proc.cc
+++ b/ui/views/examples/examples_main_proc.cc
@@ -67,7 +67,7 @@
 #include "ui/views/examples/examples_skia_gold_pixel_diff.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
@@ -100,7 +100,7 @@
 
   mojo::core::Init();
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   ui::OzonePlatform::InitParams params;
   params.single_process = true;
   ui::OzonePlatform::InitializeForGPU(params);
diff --git a/ui/views/test/views_test_base.cc b/ui/views/test/views_test_base.cc
index 3bace74..3d02e8b 100644
--- a/ui/views/test/views_test_base.cc
+++ b/ui/views/test/views_test_base.cc
@@ -30,7 +30,7 @@
 #include "ui/views/widget/native_widget_mac.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/ozone/public/platform_gl_egl_utility.h"
 #endif
@@ -40,7 +40,7 @@
 namespace {
 
 bool DoesVisualHaveAlphaForTest() {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   const auto* const egl_utility =
       ui::OzonePlatform::GetInstance()->GetPlatformGLEGLUtility();
   return egl_utility ? egl_utility->X11DoesVisualHaveAlphaForTest() : false;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
index 4ed33f3..3fcef7e55 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -143,7 +143,7 @@
   if (params.parent && params.parent->GetHost())
     properties.parent_widget = params.parent->GetHost()->GetAcceleratedWidget();
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   if (ui::OzonePlatform::GetInstance()
           ->GetPlatformProperties()
           .set_parent_for_non_top_level_windows) {
@@ -366,7 +366,7 @@
   if (!platform_window())
     return;
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   SetWmDropHandler(platform_window(), nullptr);
 #endif
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
index 5e44211..8bf5ce5 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/command_line.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
+#include "build/build_config.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/aura/window_tree_host_observer.h"
 #include "ui/base/ui_base_features.h"
@@ -21,7 +22,7 @@
 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
 #include "ui/views/widget/widget_observer.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
@@ -311,7 +312,7 @@
 
 TEST_F(DesktopWindowTreeHostPlatformTest, MakesParentChildRelationship) {
   bool context_is_also_parent = false;
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   if (ui::OzonePlatform::GetInstance()
           ->GetPlatformProperties()
           .set_parent_for_non_top_level_windows) {
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc
index b6cc28b5a..be86dde 100644
--- a/ui/views/widget/native_widget_aura.cc
+++ b/ui/views/widget/native_widget_aura.cc
@@ -65,7 +65,7 @@
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h"
 #endif
 
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) && defined(USE_OZONE)
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) && BUILDFLAG(IS_OZONE)
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h"
 #endif
 
@@ -335,13 +335,11 @@
 }
 
 Widget* NativeWidgetAura::GetWidget() {
-  DCHECK(delegate_);
-  return delegate_->AsWidget();
+  return delegate_ ? delegate_->AsWidget() : nullptr;
 }
 
 const Widget* NativeWidgetAura::GetWidget() const {
-  DCHECK(delegate_);
-  return delegate_->AsWidget();
+  return delegate_ ? delegate_->AsWidget() : nullptr;
 }
 
 gfx::NativeView NativeWidgetAura::GetNativeView() const {
@@ -957,28 +955,26 @@
 // NativeWidgetAura, aura::WindowDelegate implementation:
 
 gfx::Size NativeWidgetAura::GetMinimumSize() const {
-  DCHECK(delegate_);
-  return delegate_->GetMinimumSize();
+  return delegate_ ? delegate_->GetMinimumSize() : gfx::Size();
 }
 
 gfx::Size NativeWidgetAura::GetMaximumSize() const {
   // Do no check maximizability as EXO clients can have maximum size and be
   // maximizable at the same time.
-  DCHECK(delegate_);
-  return delegate_->GetMaximumSize();
+  return delegate_ ? delegate_->GetMaximumSize() : gfx::Size();
 }
 
 void NativeWidgetAura::OnBoundsChanged(const gfx::Rect& old_bounds,
                                        const gfx::Rect& new_bounds) {
-  DCHECK(delegate_);
   // Assume that if the old bounds was completely empty a move happened. This
   // handles the case of a maximize animation acquiring the layer (acquiring a
   // layer results in clearing the bounds).
-  if (old_bounds.origin() != new_bounds.origin() ||
-      (old_bounds == gfx::Rect(0, 0, 0, 0) && !new_bounds.IsEmpty())) {
+  if (delegate_ &&
+      (old_bounds.origin() != new_bounds.origin() ||
+       (old_bounds == gfx::Rect(0, 0, 0, 0) && !new_bounds.IsEmpty()))) {
     delegate_->OnNativeWidgetMove();
   }
-  if (old_bounds.size() != new_bounds.size())
+  if (delegate_ && (old_bounds.size() != new_bounds.size()))
     delegate_->OnNativeWidgetSizeChanged(new_bounds.size());
 }
 
@@ -987,16 +983,15 @@
 }
 
 int NativeWidgetAura::GetNonClientComponent(const gfx::Point& point) const {
-  DCHECK(delegate_);
-  return delegate_->GetNonClientComponent(point);
+  return delegate_ ? delegate_->GetNonClientComponent(point) : 0;
 }
 
 bool NativeWidgetAura::ShouldDescendIntoChildForEventHandling(
     aura::Window* child,
     const gfx::Point& location) {
-  DCHECK(delegate_);
-  return delegate_->ShouldDescendIntoChildForEventHandling(
-      window_->layer(), child, child->layer(), location);
+  return delegate_ ? delegate_->ShouldDescendIntoChildForEventHandling(
+                         window_->layer(), child, child->layer(), location)
+                   : false;
 }
 
 bool NativeWidgetAura::CanFocus() {
@@ -1009,8 +1004,8 @@
 }
 
 void NativeWidgetAura::OnPaint(const ui::PaintContext& context) {
-  DCHECK(delegate_);
-  delegate_->OnNativeWidgetPaint(context);
+  if (delegate_)
+    delegate_->OnNativeWidgetPaint(context);
 }
 
 void NativeWidgetAura::OnDeviceScaleFactorChanged(
@@ -1048,23 +1043,23 @@
 }
 
 void NativeWidgetAura::OnWindowTargetVisibilityChanged(bool visible) {
-  DCHECK(delegate_);
-  delegate_->OnNativeWidgetVisibilityChanged(visible);
+  if (delegate_)
+    delegate_->OnNativeWidgetVisibilityChanged(visible);
 }
 
 bool NativeWidgetAura::HasHitTestMask() const {
-  DCHECK(delegate_);
-  return delegate_->HasHitTestMask();
+  return delegate_ ? delegate_->HasHitTestMask() : false;
 }
 
 void NativeWidgetAura::GetHitTestMask(SkPath* mask) const {
-  DCHECK(mask && delegate_);
-  delegate_->GetHitTestMask(mask);
+  DCHECK(mask);
+  if (delegate_)
+    delegate_->GetHitTestMask(mask);
 }
 
 void NativeWidgetAura::UpdateVisualState() {
-  DCHECK(delegate_);
-  delegate_->LayoutRootViewIfNecessary();
+  if (delegate_)
+    delegate_->LayoutRootViewIfNecessary();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1073,52 +1068,52 @@
 void NativeWidgetAura::OnWindowPropertyChanged(aura::Window* window,
                                                const void* key,
                                                intptr_t old) {
-  DCHECK(delegate_);
-  if (key == aura::client::kShowStateKey)
+  if (delegate_ && key == aura::client::kShowStateKey)
     delegate_->OnNativeWidgetWindowShowStateChanged();
 
-  if (key == aura::client::kWindowWorkspaceKey)
+  if (delegate_ && key == aura::client::kWindowWorkspaceKey)
     delegate_->OnNativeWidgetWorkspaceChanged();
 }
 
 void NativeWidgetAura::OnResizeLoopStarted(aura::Window* window) {
-  DCHECK(delegate_);
-  delegate_->OnNativeWidgetBeginUserBoundsChange();
+  if (delegate_)
+    delegate_->OnNativeWidgetBeginUserBoundsChange();
 }
 
 void NativeWidgetAura::OnResizeLoopEnded(aura::Window* window) {
-  DCHECK(delegate_);
-  delegate_->OnNativeWidgetEndUserBoundsChange();
+  if (delegate_)
+    delegate_->OnNativeWidgetEndUserBoundsChange();
 }
 
 void NativeWidgetAura::OnWindowAddedToRootWindow(aura::Window* window) {
-  DCHECK(delegate_);
-  delegate_->OnNativeWidgetAddedToCompositor();
+  if (delegate_)
+    delegate_->OnNativeWidgetAddedToCompositor();
 }
 
 void NativeWidgetAura::OnWindowRemovingFromRootWindow(aura::Window* window,
                                                       aura::Window* new_root) {
-  DCHECK(delegate_);
-  delegate_->OnNativeWidgetRemovingFromCompositor();
+  if (delegate_)
+    delegate_->OnNativeWidgetRemovingFromCompositor();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // NativeWidgetAura, ui::EventHandler implementation:
 
 void NativeWidgetAura::OnKeyEvent(ui::KeyEvent* event) {
-  DCHECK(window_ && delegate_);
+  DCHECK(window_);
   // Renderer may send a key event back to us if the key event wasn't handled,
   // and the window may be invisible by that time.
   if (!window_->IsVisible())
     return;
 
-  delegate_->OnKeyEvent(event);
+  if (delegate_)
+    delegate_->OnKeyEvent(event);
 }
 
 void NativeWidgetAura::OnMouseEvent(ui::MouseEvent* event) {
-  DCHECK(window_ && delegate_);
+  DCHECK(window_);
   DCHECK(window_->IsVisible());
-  if (event->type() == ui::ET_MOUSEWHEEL) {
+  if (delegate_ && event->type() == ui::ET_MOUSEWHEEL) {
     delegate_->OnMouseEvent(event);
     return;
   }
@@ -1126,26 +1121,28 @@
   if (tooltip_manager_.get())
     tooltip_manager_->UpdateTooltip();
   TooltipManagerAura::UpdateTooltipManagerForCapture(this);
-  delegate_->OnMouseEvent(event);
+
+  if (delegate_)
+    delegate_->OnMouseEvent(event);
 }
 
 void NativeWidgetAura::OnScrollEvent(ui::ScrollEvent* event) {
-  DCHECK(delegate_);
-  delegate_->OnScrollEvent(event);
+  if (delegate_)
+    delegate_->OnScrollEvent(event);
 }
 
 void NativeWidgetAura::OnGestureEvent(ui::GestureEvent* event) {
-  DCHECK(window_ && delegate_);
+  DCHECK(window_);
   DCHECK(window_->IsVisible() || event->IsEndingEvent());
-  delegate_->OnGestureEvent(event);
+  if (delegate_)
+    delegate_->OnGestureEvent(event);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // NativeWidgetAura, wm::ActivationDelegate implementation:
 
 bool NativeWidgetAura::ShouldActivate() const {
-  DCHECK(delegate_);
-  return delegate_->CanActivate();
+  return delegate_ ? delegate_->CanActivate() : false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1155,15 +1152,15 @@
     wm::ActivationChangeObserver::ActivationReason,
     aura::Window* gained_active,
     aura::Window* lost_active) {
-  DCHECK(delegate_);
   DCHECK(window_ == gained_active || window_ == lost_active);
-  if (GetWidget()->GetFocusManager()) {
+  if (GetWidget() && GetWidget()->GetFocusManager()) {
     if (window_ == gained_active)
       GetWidget()->GetFocusManager()->RestoreFocusedView();
     else if (window_ == lost_active)
       GetWidget()->GetFocusManager()->StoreFocusedView(true);
   }
-  delegate_->OnNativeWidgetActivationChanged(window_ == gained_active);
+  if (delegate_)
+    delegate_->OnNativeWidgetActivationChanged(window_ == gained_active);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1171,10 +1168,9 @@
 
 void NativeWidgetAura::OnWindowFocused(aura::Window* gained_focus,
                                        aura::Window* lost_focus) {
-  DCHECK(delegate_);
-  if (window_ == gained_focus)
+  if (delegate_ && window_ == gained_focus)
     delegate_->OnNativeFocus();
-  else if (window_ == lost_focus)
+  else if (delegate_ && window_ == lost_focus)
     delegate_->OnNativeBlur();
 }
 
@@ -1213,8 +1209,8 @@
 // NativeWidgetAura, wm::TransientWindowObserver implementation:
 
 void NativeWidgetAura::OnTransientParentChanged(aura::Window* new_parent) {
-  DCHECK(delegate_);
-  delegate_->OnNativeWidgetParentChanged(new_parent);
+  if (delegate_)
+    delegate_->OnNativeWidgetParentChanged(new_parent);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1246,7 +1242,7 @@
 // Widget, public:
 
 namespace {
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) && (BUILDFLAG(IS_WIN) || defined(USE_OZONE))
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) && (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_OZONE))
 void CloseWindow(aura::Window* window) {
   if (window) {
     Widget* widget = Widget::GetWidgetForNativeView(window);
@@ -1276,7 +1272,7 @@
   EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, 0);
 #endif
 
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) && defined(USE_OZONE)
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) && BUILDFLAG(IS_OZONE)
   DesktopWindowTreeHostPlatform::CleanUpWindowList(CloseWindow);
 #endif
 }
diff --git a/ui/views/widget/native_widget_aura_unittest.cc b/ui/views/widget/native_widget_aura_unittest.cc
index 0835fea6..355e5cd 100644
--- a/ui/views/widget/native_widget_aura_unittest.cc
+++ b/ui/views/widget/native_widget_aura_unittest.cc
@@ -926,9 +926,105 @@
   raw_ptr<TestNativeWidgetAura> native_widget;
 };
 
-TEST_F(NativeWidgetAuraWithNoDelegateTest, OnCaptureLostTest) {
+TEST_F(NativeWidgetAuraWithNoDelegateTest, GetHitTestMaskTest) {
+  SkPath mask;
+  native_widget->GetHitTestMask(&mask);
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, GetMaximumSizeTest) {
+  native_widget->GetMaximumSize();
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, GetMinimumSizeTest) {
+  native_widget->GetMinimumSize();
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, GetNonClientComponentTest) {
+  native_widget->GetNonClientComponent(gfx::Point());
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, GetWidgetTest) {
+  native_widget->GetWidget();
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, HasHitTestMaskTest) {
+  native_widget->HasHitTestMask();
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, OnBoundsChangedTest) {
   native_widget->OnCaptureLost();
 }
 
+TEST_F(NativeWidgetAuraWithNoDelegateTest, OnCaptureLostTest) {
+  native_widget->OnBoundsChanged(gfx::Rect(), gfx::Rect());
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, OnKeyEventTest) {
+  ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_0, ui::EF_NONE);
+  native_widget->OnKeyEvent(&key);
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, OnPaintTest) {
+  native_widget->OnPaint(ui::PaintContext(nullptr, 0, gfx::Rect(), false));
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, OnResizeLoopEndedTest) {
+  native_widget->OnResizeLoopEnded(nullptr);
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, OnResizeLoopStartedTest) {
+  native_widget->OnResizeLoopStarted(nullptr);
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, OnScrollEventTest) {
+  ui::ScrollEvent scroll(ui::ET_SCROLL, gfx::Point(), ui::EventTimeForNow(), 0,
+                         0, 0, 0, 0, 0);
+  native_widget->OnScrollEvent(&scroll);
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, OnTransientParentChangedTest) {
+  native_widget->OnTransientParentChanged(nullptr);
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, OnWindowAddedToRootWindowTest) {
+  native_widget->OnWindowAddedToRootWindow(nullptr);
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, OnWindowPropertyChangedTest) {
+  native_widget->OnWindowPropertyChanged(nullptr, nullptr, 0);
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, OnWindowRemovingFromRootWindowTest) {
+  native_widget->OnWindowRemovingFromRootWindow(nullptr, nullptr);
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest,
+       OnWindowTargetVisibilityChangedTest) {
+  native_widget->OnWindowTargetVisibilityChanged(false);
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, OnWindowActivatedTest) {
+  native_widget->OnWindowActivated(
+      wm::ActivationChangeObserver::ActivationReason::ACTIVATION_CLIENT,
+      native_widget->GetNativeView(), nullptr);
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, OnWindowFocusedTest) {
+  native_widget->OnWindowFocused(nullptr, nullptr);
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, ShouldActivateTest) {
+  native_widget->ShouldActivate();
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest,
+       ShouldDescendIntoChildForEventHandlingTest) {
+  native_widget->ShouldDescendIntoChildForEventHandling(nullptr, gfx::Point());
+}
+
+TEST_F(NativeWidgetAuraWithNoDelegateTest, UpdateVisualStateTest) {
+  native_widget->UpdateVisualState();
+}
+
 }  // namespace
 }  // namespace views
diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h
index c7495ec..37b5615 100644
--- a/ui/views/widget/widget.h
+++ b/ui/views/widget/widget.h
@@ -434,7 +434,7 @@
     // widget.
     ui::PropertyHandler init_properties_container;
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
     // Only used by Wayland for root level windows. Specifies whether this
     // window should request the wayland compositor to send key events,
     // even if it matches with the compositor's keyboard shortcuts.
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index 427aa55..0b570b52 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -84,7 +84,7 @@
 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
 #endif
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/ozone/public/platform_gl_egl_utility.h"
 #endif
@@ -5410,7 +5410,7 @@
 namespace {
 
 bool CanHaveCompositingManager() {
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
   auto* const egl_utility =
       ui::OzonePlatform::GetInstance()->GetPlatformGLEGLUtility();
   return (egl_utility != nullptr) && egl_utility->HasVisualManager();
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn
index 85b1406..4223b192 100644
--- a/ui/webui/resources/BUILD.gn
+++ b/ui/webui/resources/BUILD.gn
@@ -185,6 +185,7 @@
                "js/icon.ts",
                "js/keyboard_shortcut_list.ts",
                "js/parse_html_subset.ts",
+               "js/platform.ts",
                "js/plural_string_proxy.ts",
                "js/static_types.ts",
                "js/store_ts.ts",
diff --git a/ui/webui/resources/cr_components/help_bubble/help_bubble.ts b/ui/webui/resources/cr_components/help_bubble/help_bubble.ts
index 853690b..68350ca8 100644
--- a/ui/webui/resources/cr_components/help_bubble/help_bubble.ts
+++ b/ui/webui/resources/cr_components/help_bubble/help_bubble.ts
@@ -19,7 +19,7 @@
 import {CrButtonElement} from '//resources/cr_elements/cr_button/cr_button.js';
 import {CrIconButtonElement} from '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import {assert, assertNotReached} from '//resources/js/assert_ts.js';
-import {isWindows} from '//resources/js/cr.m.js';
+import {isWindows} from '//resources/js/platform.js';
 import {DomRepeatEvent, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './help_bubble.html.js';
diff --git a/ui/webui/resources/cr_components/most_visited/most_visited.ts b/ui/webui/resources/cr_components/most_visited/most_visited.ts
index 11f9049..906aa91 100644
--- a/ui/webui/resources/cr_components/most_visited/most_visited.ts
+++ b/ui/webui/resources/cr_components/most_visited/most_visited.ts
@@ -14,13 +14,13 @@
 import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
 import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {skColorToRgba} from 'chrome://resources/js/color_utils.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
-import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.js';
-import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {isMac} from 'chrome://resources/js/platform.js';
 import {hasKeyModifiers} from 'chrome://resources/js/util.js';
 import {TextDirection} from 'chrome://resources/mojo/mojo/public/mojom/base/text_direction.mojom-webui.js';
 import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
diff --git a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.ts b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.ts
index a38dfdb..6ff5625f 100644
--- a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.ts
+++ b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.ts
@@ -7,10 +7,10 @@
 import {FlattenedNodesObserver, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {assert} from '../../js/assert.js';
-import {isMac, isWindows} from '../../js/cr.m.js';
 import {FocusOutlineManager} from '../../js/focus_outline_manager.js';
 import {FocusRow} from '../../js/focus_row.js';
 import {focusWithoutInk} from '../../js/focus_without_ink.js';
+import {isMac, isWindows} from '../../js/platform.js';
 import {getDeepActiveElement} from '../../js/util.js';
 
 import {getTemplate} from './cr_action_menu.html.js';
diff --git a/ui/webui/resources/cr_elements/cr_tree/cr_tree.ts b/ui/webui/resources/cr_elements/cr_tree/cr_tree.ts
index d0bbad5f..3eedcf262 100644
--- a/ui/webui/resources/cr_elements/cr_tree/cr_tree.ts
+++ b/ui/webui/resources/cr_elements/cr_tree/cr_tree.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {assert, assertNotReached} from '../../js/assert_ts.js';
-import {isMac} from '../../js/cr.m.js';
+import {isMac} from '../../js/platform.js';
 
 import {getTemplate} from './cr_tree.html.js';
 import {CrTreeBaseElement} from './cr_tree_base.js';
diff --git a/ui/webui/resources/cr_elements/find_shortcut_mixin.ts b/ui/webui/resources/cr_elements/find_shortcut_mixin.ts
index 6c874dd..8e1da343 100644
--- a/ui/webui/resources/cr_elements/find_shortcut_mixin.ts
+++ b/ui/webui/resources/cr_elements/find_shortcut_mixin.ts
@@ -5,8 +5,8 @@
 import {dedupingMixin, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {assert, assertNotReached} from '../js/assert.js';
-import {isMac} from '../js/cr.m.js';
 import {KeyboardShortcutList} from '../js/keyboard_shortcut_list.js';
+import {isMac} from '../js/platform.js';
 import {isTextInputElement} from '../js/util.js';
 
 /**
diff --git a/ui/webui/resources/js/BUILD.gn b/ui/webui/resources/js/BUILD.gn
index 2978e2e..7c56ccdd 100644
--- a/ui/webui/resources/js/BUILD.gn
+++ b/ui/webui/resources/js/BUILD.gn
@@ -51,6 +51,7 @@
     "icon.ts",
     "keyboard_shortcut_list.ts",
     "parse_html_subset.ts",
+    "platform.ts",
     "plural_string_proxy.ts",
     "search_highlight_utils.ts",
     "static_types.ts",
diff --git a/ui/webui/resources/js/cr.m.js b/ui/webui/resources/js/cr.m.js
index b79bce92..9783e814 100644
--- a/ui/webui/resources/js/cr.m.js
+++ b/ui/webui/resources/js/cr.m.js
@@ -140,36 +140,3 @@
 assert(!window.cr.webUIListenerCallback);
 window.cr.webUIResponse = webUIResponse;
 window.cr.webUIListenerCallback = webUIListenerCallback;
-
-/** Whether we are using a Mac or not. */
-export const isMac = /Mac/.test(navigator.platform);
-
-/** Whether this is on the Windows platform or not. */
-export const isWindows = /Win/.test(navigator.platform);
-
-/** Whether this is the ChromeOS/ash web browser. */
-export const isChromeOS = (() => {
-  let returnValue = false;
-  // <if expr="chromeos_ash">
-  returnValue = true;
-  // </if>
-  return returnValue;
-})();
-
-/** Whether this is the ChromeOS/Lacros web browser. */
-export const isLacros = (() => {
-  let returnValue = false;
-  // <if expr="chromeos_lacros">
-  returnValue = true;
-  // </if>
-  return returnValue;
-})();
-
-/** Whether this is on vanilla Linux (not chromeOS). */
-export const isLinux = /Linux/.test(navigator.userAgent);
-
-/** Whether this is on Android. */
-export const isAndroid = /Android/.test(navigator.userAgent);
-
-/** Whether this is on iOS. */
-export const isIOS = /CriOS/.test(navigator.userAgent);
diff --git a/ui/webui/resources/js/focus_without_ink.ts b/ui/webui/resources/js/focus_without_ink.ts
index 76196b181..888f1b3 100644
--- a/ui/webui/resources/js/focus_without_ink.ts
+++ b/ui/webui/resources/js/focus_without_ink.ts
@@ -4,7 +4,7 @@
 
 // clang-format off
 import {assert} from './assert_ts.js';
-import {isIOS} from './cr.m.js';
+import {isIOS} from './platform.js';
 // clang-format on
 
 
diff --git a/ui/webui/resources/js/icon.ts b/ui/webui/resources/js/icon.ts
index a19ce7b..9758d6b 100644
--- a/ui/webui/resources/js/icon.ts
+++ b/ui/webui/resources/js/icon.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {isAndroid, isIOS} from './cr.m.js';
+import {isAndroid, isIOS} from './platform.js';
 
 /**
  * @return The scale factors supported by this platform for webui resources.
diff --git a/ui/webui/resources/js/platform.ts b/ui/webui/resources/js/platform.ts
new file mode 100644
index 0000000..6948af1
--- /dev/null
+++ b/ui/webui/resources/js/platform.ts
@@ -0,0 +1,38 @@
+// 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.
+
+/* @fileoverview Utilities for determining the current platform. */
+
+/** Whether we are using a Mac or not. */
+export const isMac = /Mac/.test(navigator.platform);
+
+/** Whether this is on the Windows platform or not. */
+export const isWindows = /Win/.test(navigator.platform);
+
+/** Whether this is the ChromeOS/ash web browser. */
+export const isChromeOS = (() => {
+  let returnValue = false;
+  // <if expr="chromeos_ash">
+  returnValue = true;
+  // </if>
+  return returnValue;
+})();
+
+/** Whether this is the ChromeOS/Lacros web browser. */
+export const isLacros = (() => {
+  let returnValue = false;
+  // <if expr="chromeos_lacros">
+  returnValue = true;
+  // </if>
+  return returnValue;
+})();
+
+/** Whether this is on vanilla Linux (not chromeOS). */
+export const isLinux = /Linux/.test(navigator.userAgent);
+
+/** Whether this is on Android. */
+export const isAndroid = /Android/.test(navigator.userAgent);
+
+/** Whether this is on iOS. */
+export const isIOS = /CriOS/.test(navigator.userAgent);